56 lines
2.7 KiB
PHP
56 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\InvestmentLegalSource;
|
|
use App\Models\User;
|
|
use App\Support\IsolatedQaDatabase;
|
|
use App\Support\StructuredData;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/** Exact operation-level IVA calculation against an explicit QA draft; never a payment/return total. */
|
|
final class SimulationFiscalQaPreview
|
|
{
|
|
/**
|
|
* @param array<array-key, mixed> $input
|
|
* @return array<array-key, mixed>
|
|
*/
|
|
public function create(User $actor, string $ruleId, array $input, string $key): array
|
|
{
|
|
abort_unless(IsolatedQaDatabase::allowsSchemaChanges(), 404);
|
|
|
|
return DB::transaction(function () use ($actor, $ruleId, $input, $key): array {
|
|
$rule = app(SimulationTaxRuleGovernance::class)->show($actor, $ruleId);
|
|
abort_unless($rule->status === 'draft' && $rule->rule_code === 'IVA-GERAL-BASE-LIQUIDA', 422,
|
|
'Seleccione o rascunho do primeiro lote fiscal.');
|
|
abort_if(collect(StructuredData::collectionItems($rule->rule_definition['nodes'] ?? []))->contains(fn (array $node): bool => ($node['op'] ?? null) === 'round'), 422,
|
|
'Este percurso preserva o valor exacto e não aplica arredondamento automático.');
|
|
$source = InvestmentLegalSource::findOrFail($rule->legal_source_id);
|
|
$version = $rule->attributesToArray();
|
|
$version['source'] = $source->attributesToArray();
|
|
$date = $input['reference_date'];
|
|
$context = ['operation' => $input['operation'], 'currency' => $input['currency']];
|
|
$missing = [];
|
|
$resolution = [];
|
|
$evaluation = null;
|
|
if ($date < '2026-01-01' || $date > '2026-12-31') {
|
|
$missing[] = 'demonstrated_coverage';
|
|
} else {
|
|
$resolution = app(SimulationTaxParameterResolver::class)->resolveDraftForQa([$version], 'IVA', $date, $date, $context);
|
|
$rate = collect($resolution)->firstWhere('parameter_code', 'IVA_GENERAL_RATE');
|
|
if (($rate['status'] ?? null) !== 'resolved' || ($rate['unit'] ?? null) !== 'percent') {
|
|
$missing[] = 'eligible_parameter';
|
|
} else {
|
|
$evaluation = app(SimulationTaxRuleExpression::class)->evaluate($rule->rule_definition,
|
|
['base_tributavel' => $input['base']], ['iva_general_rate' => $rate['value']]);
|
|
}
|
|
}
|
|
$snapshot = app(SimulationFiscalSnapshot::class)->build($version, $input, $resolution, $evaluation, $missing);
|
|
|
|
return app(SimulationExecution::class)->createFiscalQaPreview($snapshot, $actor->id, $key);
|
|
});
|
|
}
|
|
}
|