Integrate monthly IVA with projected sales in QA

This commit is contained in:
2026-10-21 14:05:46 +02:00
parent 0d7b70459d
commit 3f5430c3ce
18 changed files with 690 additions and 12 deletions
+55 -1
View File
@@ -6,6 +6,7 @@ namespace App\Services;
use App\Models\InvestmentLegalSource;
use App\Models\SimulationTaxRuleVersion;
use Brick\Math\BigRational;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
@@ -35,8 +36,11 @@ final class SimulationPublishedFiscal
->where('scope_key', 'mz.internal-general-net.mzn');
}
public function admit(array $input): array
public function admit(array $input, bool $missingBase = false): array
{
if (isset($input['monthly_sales'])) {
return $this->admitMonthly($input);
}
// Called inside createPayload, after idempotent replay, never by the worker.
abort_unless(DB::transactionLevel() > 0, 500);
abort_unless(config('simulation-fiscal-consumer.enabled'), 503,
@@ -63,6 +67,9 @@ final class SimulationPublishedFiscal
$input['reference_date'], $input['reference_date'], ['currency' => $input['currency'], 'operation' => $input['operation']]);
$rate = collect($resolution)->firstWhere('parameter_code', 'IVA_GENERAL_RATE');
$missing = [];
if ($missingBase) {
$missing[] = 'taxable_sales';
}
$evaluation = null;
if (! ($input['scope_declared'] ?? false)) {
$missing[] = 'operation_scope';
@@ -79,4 +86,51 @@ final class SimulationPublishedFiscal
return app(SimulationFiscalSnapshot::class)->build($version, $input, $resolution, $evaluation, $missing, true);
}
private function admitMonthly(array $input): array
{
$rows = [];
$baseTotal = BigRational::zero();
$vatTotal = BigRational::zero();
$complete = true;
$previous = null;
foreach ($input['monthly_sales'] as $sale) {
abort_unless($previous === null || $sale['date'] === date('Y-m-d', strtotime($previous.' +1 month')), 422, 'A projecção deve conter meses consecutivos, sem duplicações.');
$previous = $sale['date'];
$known = $sale['amount'] !== null && $sale['amount'] !== '';
$net = $input['sales_vat_basis'] === 'net';
$month = $this->admit(['base' => $known && $net ? $sale['amount'] : null,
'currency' => $input['currency'], 'reference_date' => $sale['date'],
'operation' => 'internal_general_net', 'scope_declared' => $input['scope_declared']], ! $known || ! $net);
$snapshot ??= $month;
$rate = collect($month['fiscal_provenance']['resolution'])->firstWhere('parameter_code', 'IVA_GENERAL_RATE');
$vat = $month['result']['value'];
$status = ! $known ? 'missing_sales' : (! $net ? 'unsupported_basis' : ($vat === null ? 'indeterminate' : 'calculated'));
$rows[] = ['date' => $sale['date'], 'sales_declared' => $sale['amount'],
'net_sales' => $known && $net ? $sale['amount'] : null,
'rate' => $vat !== null && ($rate['status'] ?? null) === 'resolved' ? $rate['value'] : null,
'vat' => $vat, 'gross_sales' => $vat === null ? null : (string) BigRational::of($sale['amount'])->plus($vat)->toBigDecimal(),
'status' => $status, 'rule_id' => $month['fiscal_provenance']['rule']['id'],
'rule_version' => $month['fiscal_provenance']['rule']['rule_version']];
if ($vat === null) {
$complete = false;
} else {
$baseTotal = $baseTotal->plus($sale['amount']);
$vatTotal = $vatTotal->plus($vat);
}
}
$value = $complete ? (string) $vatTotal->toBigDecimal() : null;
$snapshot['base'] = $complete ? (string) $baseTotal->toBigDecimal() : null;
$snapshot['indicative_total'] = $value;
$snapshot['result']['value'] = $value;
$snapshot['result']['status'] = $complete ? 'calculated' : 'indeterminate';
$snapshot['result']['missing_inputs'] = $complete ? [] : ['monthly_sales_or_net_basis_or_scope'];
$snapshot['result']['explanation'] = 'IVA mensal sobre vendas declaradas sem IVA, pela versão publicada congelada na admissão. Valores com IVA incluído ou sem informação permanecem indeterminados. Não é imposto final a pagar.';
$snapshot['presentation']['value'] = $value;
$snapshot['intermediate'] = null;
$snapshot['monthly_projection'] = ['input' => $input, 'rows' => $rows,
'cash_treatment' => 'Informação separada da tesouraria: os recebimentos declarados não são aumentados pelo IVA nem o IVA é deduzido como custo ou imposto a pagar. Calendário de cobrança e liquidação fiscal não é inferido.'];
return $snapshot;
}
}