Adapt preparation presentation to declared applicability

This commit is contained in:
2026-10-23 13:53:02 +02:00
parent e1f010f941
commit 7ca94967f3
8 changed files with 88 additions and 10 deletions
@@ -30,6 +30,28 @@ class InvestmentCorridorContextTest extends TestCase
$entry=collect(app(CorridorAgribusinessOpportunities::class)->entries())->firstWhere('corridor',config('investment_corridors.'.$corridor.'.name'));
return Opportunity::where('management_creation_key',$entry['key'])->firstOrFail();
}
public function test_nonapplicable_land_hides_the_action_and_reopening_recovers_saved_evidence(): void
{
$id = $this->postJson('/investimentos', ['title' => 'QA adaptação sem perda', 'client_key' => (string) Str::uuid(), 'reuse_consent' => true])->assertCreated()->json('id');
$view = $this->getJson('/investimentos/'.$id)->assertOk()->json();
$this->assertNotNull($view['tools']['land']);
$this->assertSame('undetermined', collect($view['steps'])->firstWhere('code', 'S10')['applicability']);
$this->postJson('/investimentos/'.$id.'/etapas/S10', ['version' => $view['case']['version'], 'state' => 'preparing', 'action' => 'prepare', 'reason' => 'Informação territorial já guardada'])->assertOk();
$view = $this->getJson('/investimentos/'.$id)->json();
$facts = ['land_needed' => 'no', 'external_funding' => 'no', 'viability' => 'unknown', 'reason' => 'Investimento sem necessidade de terra nem financiamento externo'];
$this->postJson('/investimentos/'.$id.'/aplicabilidade', ['version' => $view['case']['version'], ...$facts])->assertOk();
$view = $this->getJson('/investimentos/'.$id)->assertJsonPath('tools.land', null)->json();
$step = collect($view['steps'])->firstWhere('code', 'S10');
$this->assertFalse($step['preparation_allowed']);
$this->assertSame('preparing', $step['saved_state']);
$this->assertSame('Informação territorial já guardada', $step['detail']['reason']);
$this->postJson('/investimentos/'.$id.'/etapas/S10', ['version' => $view['case']['version'], 'state' => 'preparing', 'action' => 'prepare', 'reason' => 'Não deve executar etapa sem aplicação'])->assertUnprocessable();
$this->postJson('/investimentos/'.$id.'/aplicabilidade', ['version' => $view['case']['version'], ...$facts, 'land_needed' => 'yes'])->assertOk();
$view = $this->getJson('/investimentos/'.$id)->assertOk()->json();
$this->assertNotNull($view['tools']['land']);
$this->assertSame('preparing', collect($view['steps'])->firstWhere('code', 'S10')['state']);
$this->assertCount(19, $view['steps']);
}
public function test_tempo_configuration_uses_existing_ids_and_maputo_includes_both_units(): void
{
foreach(config('investment_corridors') as $key=>$entry){
@@ -12,6 +12,29 @@ import {
validateDossierAttachments,
} from '../../resources/js/lib/project-dossier-report.ts';
test('report separates inapplicable documents from missing and unknown requirements without changing the saved snapshot', async () => {
const requirements = [
{ label: 'Título DUAT', state: 'not_applicable', reason: 'Arrendamento declarado' },
{ label: 'Identificação', state: 'missing', reason: 'Pedido em preparação' },
{ label: 'Capital externo', state: 'indeterminate', reason: 'Origem por determinar' },
];
const snapshot = JSON.stringify({ version: 1, context_revision: 2, preparation: {
enabled: true, mode: 'registration', version: 'QA', next: 'Completar identificação',
calculation_notice: 'Cálculo separado', notice: 'Preparação', requirements,
} });
const draft = { context_applicability_snapshot: snapshot };
const report = createProjectDossierReport(draft, { selectedSections: new Set(['sources']) });
const section = report.sections.find((section) => section.id === 'sources');
assert.ok(section);
assert.equal(section.rows.some((row) => row.field === 'Título DUAT'), false);
assert.match(section.paragraphs.join(' '), /Não exigido neste contexto: Título DUAT \(Arrendamento declarado\)/);
assert.equal(section.rows.find((row) => row.field === 'Identificação').value, 'Documento por associar');
assert.equal(section.rows.find((row) => row.field === 'Capital externo').value, 'Informação em falta para decidir');
const print = await createProjectDossierPrintHtml(report, [], false);
assert.match(print, /Arrendamento declarado/);
assert.equal(draft.context_applicability_snapshot, snapshot);
});
test('executive summary exposes the same financial result and period without replacing missing data by zero', () => {
const draft = { currency:'MZN', financial_tools_snapshot:serializeFinancialToolsSnapshot({budgetPeriod:'Mensal',loan:{principal:'1200',rate:'0',months:'12',rateType:'nominal',periodMonths:'1',gracePeriods:'0',upfrontFee:'0',recurringFee:'0'},breakEven:{fixed:'100.01',price:'30',variable:'20'},funding:{total:'1000',secured:'800'},incomes:[{label:'Receita QA',amount:'1000'}],expenses:[{label:'Despesa QA',amount:'1100'}]}) };
const report = createProjectDossierReport(draft,{selectedSections:new Set(['summary','finance'])});