This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\InvestmentLegalSource;
|
||||
use App\Models\SimulationTaxRuleVersion;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/** Admission freezes eligibility without granting the public consumer governance write access. */
|
||||
final class SimulationPublishedFiscal
|
||||
{
|
||||
public function availability(): array
|
||||
{
|
||||
$enabled = (bool) config('simulation-fiscal-consumer.enabled');
|
||||
$eligible = $enabled && $this->boundRule()->where('status', 'published')->exists();
|
||||
|
||||
return ['enabled' => $eligible, 'coverage_start' => '2026-01-01', 'coverage_end' => '2026-12-31',
|
||||
'message' => $eligible ? 'IVA geral sobre base líquida disponível; enquadramento declarado.'
|
||||
: 'O cálculo de IVA geral sobre base líquida está indisponível para novas execuções. Os históricos mantêm-se acessíveis.'];
|
||||
}
|
||||
|
||||
private function boundRule(): Builder
|
||||
{
|
||||
if (! Str::isUuid((string) config('simulation-fiscal-consumer.rule_id')) || config('simulation-fiscal-consumer.rule_version') < 1) {
|
||||
return SimulationTaxRuleVersion::query()->whereRaw('false');
|
||||
}
|
||||
|
||||
return SimulationTaxRuleVersion::query()->whereKey(config('simulation-fiscal-consumer.rule_id'))
|
||||
->where('rule_version', config('simulation-fiscal-consumer.rule_version'))
|
||||
->where('rule_code', 'IVA-GERAL-BASE-LIQUIDA')->where('component', 'IVA')
|
||||
->where('scope_key', 'mz.internal-general-net.mzn');
|
||||
}
|
||||
|
||||
public function admit(array $input): array
|
||||
{
|
||||
// Called inside createPayload, after idempotent replay, never by the worker.
|
||||
abort_unless(DB::transactionLevel() > 0, 500);
|
||||
abort_unless(config('simulation-fiscal-consumer.enabled'), 503,
|
||||
'O cálculo de IVA está temporariamente indisponível. Os dados e históricos foram preservados.');
|
||||
DB::select('select pg_advisory_xact_lock_shared(261021, hashtext(?))', [(string) config('simulation-fiscal-consumer.rule_id')]);
|
||||
$rule = $this->boundRule()->first();
|
||||
abort_unless($rule && $rule->status === 'published', 503,
|
||||
'A versão autorizada de IVA não está disponível para novas execuções. Os históricos foram preservados.');
|
||||
abort_if($input['reference_date'] < '2026-01-01' || $input['reference_date'] > '2026-12-31', 422,
|
||||
'Este cálculo abrange apenas exigibilidade em 2026. As entradas foram preservadas.');
|
||||
abort_if(collect($rule->rule_definition['nodes'] ?? [])->contains(fn (array $node): bool => ($node['op'] ?? null) === 'round'), 422,
|
||||
'A versão seleccionada não é compatível com o cálculo exacto deste consumidor.');
|
||||
DB::select('select pg_advisory_xact_lock_shared(261022, hashtext(?))', [(string) $rule->legal_source_id]);
|
||||
$source = InvestmentLegalSource::whereKey($rule->legal_source_id)->firstOrFail();
|
||||
// Publish calculation provenance, not private governance notes or staff identifiers.
|
||||
$version = array_intersect_key($rule->attributesToArray(), array_flip([
|
||||
'id', 'rule_code', 'rule_version', 'component', 'scope_key', 'status', 'valid_from', 'valid_until',
|
||||
'legal_source_id', 'source_version_no', 'source_sha256', 'rule_definition', 'parameters', 'rounding_policy',
|
||||
]));
|
||||
$version['source'] = array_intersect_key($source->attributesToArray(), array_flip([
|
||||
'id', 'title', 'url', 'diploma', 'article', 'competent_entity', 'valid_from', 'valid_until', 'validation_status', 'version_no',
|
||||
]));
|
||||
$resolution = app(SimulationTaxParameterResolver::class)->resolvePublishedForPeriod('IVA',
|
||||
$input['reference_date'], $input['reference_date'], ['currency' => $input['currency'], 'operation' => $input['operation']]);
|
||||
$rate = collect($resolution)->firstWhere('parameter_code', 'IVA_GENERAL_RATE');
|
||||
$missing = [];
|
||||
$evaluation = null;
|
||||
if (! ($input['scope_declared'] ?? false)) {
|
||||
$missing[] = 'operation_scope';
|
||||
}
|
||||
if (($rate['status'] ?? null) !== 'resolved' || ($rate['unit'] ?? null) !== 'percent'
|
||||
|| ($rate['rule_id'] ?? null) !== (string) $rule->id
|
||||
|| (int) ($rate['rule_version'] ?? 0) !== (int) $rule->rule_version) {
|
||||
$missing[] = 'eligible_parameter';
|
||||
}
|
||||
if ($missing === []) {
|
||||
$evaluation = app(SimulationTaxRuleExpression::class)->evaluate($rule->rule_definition,
|
||||
['base_tributavel' => $input['base']], ['iva_general_rate' => $rate['value']]);
|
||||
}
|
||||
|
||||
return app(SimulationFiscalSnapshot::class)->build($version, $input, $resolution, $evaluation, $missing, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user