fix: expose document association changes in preparation impact
This commit is contained in:
@@ -2130,3 +2130,14 @@ No editor, os documentos ainda por associar mantêm-se visíveis; os já associa
|
||||
Passaram oito testes frontend dirigidos: seis existentes e dois novos para encaminhamento conforme contexto, distinção entre associação/aprovação/indeterminação, controlos recuperados editáveis, retirada explícita, recuperação sem mutação do original e estado desactivado dos controlos. Tipos, lint, formatação e compilação QA passaram. Recibo e hashes no campo `adaptive_documents_increment` de `local-validation-20261029.json`; provas integrais aceites não foram repetidas.
|
||||
|
||||
**Visual continua pendente, execução pela equipa:** na candidata, conferir contagens e destino com/sem dados em falta; abrir documentos já associados, alterar/retirar uma associação, comparar impacto antes de confirmar e verificar recuperação. Incluir também âmbito/período não cobertos e exigências indeterminadas, sem interpretar associação como aprovação. Registar revisão, perfil, percurso, esperado/observado e evidência nesta matriz. Sem instalação ou avanço de main; requisitos transversais continuam parciais.
|
||||
|
||||
|
||||
### 29/10/2026 — comparação de impacto: alterações das associações documentais
|
||||
|
||||
Continuação sobre `181a9883`, nos requisitos existentes V2-07/V2-10 e na decisão adaptativa de 23/10: explicar divergências e impacto antes da confirmação. Lacuna reproduzida na revisão anterior: a comparação mostrava apenas alterações de estado, omitindo a troca de documento quando ambos mantinham «associado». O texto de vínculo preservado também não distinguia substituição de conservação.
|
||||
|
||||
Corrigido no componente existente: comparação por estado e identificador documental, com substituição, associação e retirada explícitas; nomes anterior/novo quando disponíveis, nome indisponível identificado sem inventar o ficheiro, e vínculo preservado apenas quando o identificador permanece. A ausência de alterações é expressamente limitada às exigências/associações documentais. O plano anterior/posterior já fornecido pelo backend e a confirmação de impacto mantêm-se; não há escrita em dados, mudança de schema, permissões ou cálculo.
|
||||
|
||||
Dois novos casos falharam contra o código anterior e passaram na candidata. Dez testes frontend dirigidos passaram, cobrindo também troca com o mesmo nome, retirada, nova associação, nome indisponível, histórico sem aplicação, ausência de alterações e preservação da origem. Tipos, lint, formatação e compilação QA passaram. Provas históricas aceites reutilizadas, sem repetição integral. Recibo e hashes no campo `adaptive_document_impact_increment` de `local-validation-20261029.json`.
|
||||
|
||||
**Visual pendente pela equipa:** na candidata e num dossier autorizado, preparar troca/retirada de documento, comparar antes de confirmar e conferir nomes/estado anterior e posterior; confirmar que associação mantida não surge como substituição e que o histórico permanece. Não é necessário confirmar uma alteração real para observar a prévia. Registar revisão, percurso, perfil, esperado/observado e evidência nesta matriz. Nenhuma instalação ou avanço de main; o incremento não encerra o percurso adaptativo transversal.
|
||||
|
||||
@@ -1254,5 +1254,41 @@
|
||||
"preservation": "Presentation and navigation only. Existing impact comparison, document data/history, backend, schema, permissions and fiscal rules unchanged. Historical accepted suites reused, not rerun in full.",
|
||||
"visual_acceptance": "Pending team execution and recording on candidate; component tests do not constitute visual acceptance.",
|
||||
"installation": "Not triggered; main and active release unchanged."
|
||||
},
|
||||
"adaptive_document_impact_increment": {
|
||||
"recorded_at": "2026-10-29T20:58:29.863158+00:00",
|
||||
"base_commit": "181a9883b383d11226a84ea0a7ff64844543982d",
|
||||
"requirements": [
|
||||
"V2-07",
|
||||
"V2-10",
|
||||
"Tempo adaptive journey decision 2026-10-23: explain divergences and impacts before confirmation"
|
||||
],
|
||||
"defect": "Impact comparison filtered only by requirement state, hiding a changed document association when both old and new states were associated; preservation text could misdescribe a replacement.",
|
||||
"behavior": "Compare both state and document identifier. Expose replacement, attachment and detachment with prior/new filenames when available, explicit fallback when a linked name is unavailable, and preserved-link text only for unchanged identifiers. Explicitly report no requirement/association changes without implying no other context impact.",
|
||||
"checks": {
|
||||
"previous_revision_reproduced": true,
|
||||
"new_frontend_cases": 2,
|
||||
"directed_frontend_passed": 10,
|
||||
"types": "passed",
|
||||
"lint": "passed",
|
||||
"format": "passed",
|
||||
"qa_build": "passed"
|
||||
},
|
||||
"source_sha256": {
|
||||
"resources/js/components/private-investment-preparation.tsx": "ec387317c8a4b0c82bf6339d3e3d898e9fdae3682419fbbc4b6a09c1b248a9f9",
|
||||
"tests/frontend/investor-usability-components.test.mjs": "e6f4dcc4727f8ad8650cf50223c8d0aed1ac9f6b195568da3490e65cf93ecc50"
|
||||
},
|
||||
"evidence": {
|
||||
"outputs/validation-20261029/adaptive-impact-red.log": "879cffff2d7b197b3e25caa80d41c203263c017823bb54ed1ff10e31dbf01d58",
|
||||
"outputs/validation-20261029/adaptive-impact-tests.log": "e62231b8f3087a30781d59ebfab3255264a842ca205c5e627a448eb2000df566",
|
||||
"outputs/validation-20261029/adaptive-impact-types.log": "82962651e47f564de653ad2883f8fd34e7dae4b4468e4ac459cb3331962a3879",
|
||||
"outputs/validation-20261029/adaptive-impact-lint.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"outputs/validation-20261029/adaptive-impact-format-check.log": "69ac262099e4710ddfdef82edaac4b6cc0548322385ae66c10852453cee79eb3",
|
||||
"outputs/validation-20261029/adaptive-impact-build.log": "21247f3e6ebce90ebd4c68d5877dbe9606eaaa4f26eb86c4e439755ca26cf61d",
|
||||
"outputs/validation-20261029/adaptive-impact-build-exit.json": "124fd50cb6d223cdd18fedc166d00af7254199b6e6dedf9be82f71db87af6d01"
|
||||
},
|
||||
"preservation": "Existing before/after backend preview and confirmation flow retained; no data writes, schema, permissions, tax rules or backend changes. Historical accepted suites reused.",
|
||||
"visual_acceptance": "Pending team execution and recording against candidate.",
|
||||
"installation": "Not triggered; main and active release unchanged."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7667,3 +7667,14 @@ No editor, os documentos ainda por associar mantêm-se visíveis; os já associa
|
||||
Passaram oito testes frontend dirigidos: seis existentes e dois novos para encaminhamento conforme contexto, distinção entre associação/aprovação/indeterminação, controlos recuperados editáveis, retirada explícita, recuperação sem mutação do original e estado desactivado dos controlos. Tipos, lint, formatação e compilação QA passaram. Recibo e hashes no campo `adaptive_documents_increment` de `local-validation-20261029.json`; provas integrais aceites não foram repetidas.
|
||||
|
||||
**Visual continua pendente, execução pela equipa:** na candidata, conferir contagens e destino com/sem dados em falta; abrir documentos já associados, alterar/retirar uma associação, comparar impacto antes de confirmar e verificar recuperação. Incluir também âmbito/período não cobertos e exigências indeterminadas, sem interpretar associação como aprovação. Registar revisão, perfil, percurso, esperado/observado e evidência nesta matriz. Sem instalação ou avanço de main; requisitos transversais continuam parciais.
|
||||
|
||||
|
||||
### 29/10/2026 — comparação de impacto: alterações das associações documentais
|
||||
|
||||
Continuação sobre `181a9883`, nos requisitos existentes V2-07/V2-10 e na decisão adaptativa de 23/10: explicar divergências e impacto antes da confirmação. Lacuna reproduzida na revisão anterior: a comparação mostrava apenas alterações de estado, omitindo a troca de documento quando ambos mantinham «associado». O texto de vínculo preservado também não distinguia substituição de conservação.
|
||||
|
||||
Corrigido no componente existente: comparação por estado e identificador documental, com substituição, associação e retirada explícitas; nomes anterior/novo quando disponíveis, nome indisponível identificado sem inventar o ficheiro, e vínculo preservado apenas quando o identificador permanece. A ausência de alterações é expressamente limitada às exigências/associações documentais. O plano anterior/posterior já fornecido pelo backend e a confirmação de impacto mantêm-se; não há escrita em dados, mudança de schema, permissões ou cálculo.
|
||||
|
||||
Dois novos casos falharam contra o código anterior e passaram na candidata. Dez testes frontend dirigidos passaram, cobrindo também troca com o mesmo nome, retirada, nova associação, nome indisponível, histórico sem aplicação, ausência de alterações e preservação da origem. Tipos, lint, formatação e compilação QA passaram. Provas históricas aceites reutilizadas, sem repetição integral. Recibo e hashes no campo `adaptive_document_impact_increment` de `local-validation-20261029.json`.
|
||||
|
||||
**Visual pendente pela equipa:** na candidata e num dossier autorizado, preparar troca/retirada de documento, comparar antes de confirmar e conferir nomes/estado anterior e posterior; confirmar que associação mantida não surge como substituição e que o histórico permanece. Não é necessário confirmar uma alteração real para observar a prévia. Registar revisão, percurso, perfil, esperado/observado e evidência nesta matriz. Nenhuma instalação ou avanço de main; o incremento não encerra o percurso adaptativo transversal.
|
||||
|
||||
@@ -494,6 +494,22 @@ export function PreparationImpact({
|
||||
before: Preparation;
|
||||
after: Preparation;
|
||||
}) {
|
||||
const changedRequirements = after.requirements.filter((requirement) => {
|
||||
const previous = before.requirements.find(
|
||||
(item) => item.key === requirement.key,
|
||||
);
|
||||
return (
|
||||
previous?.state !== requirement.state ||
|
||||
(previous?.document_id ?? null) !== requirement.document_id
|
||||
);
|
||||
});
|
||||
const documentName = (
|
||||
requirement: Preparation['requirements'][number] | undefined,
|
||||
) =>
|
||||
requirement?.document_id
|
||||
? (requirement.document?.file_name ??
|
||||
'Documento associado — nome indisponível')
|
||||
: 'Sem documento associado';
|
||||
return (
|
||||
<div
|
||||
className="space-y-2 rounded border border-amber-400 p-3"
|
||||
@@ -504,27 +520,36 @@ export function PreparationImpact({
|
||||
{preparationMode(before.mode)} → {preparationMode(after.mode)}.
|
||||
</p>
|
||||
<p>Próximo passo: {after.next}.</p>
|
||||
{changedRequirements.length === 0 && (
|
||||
<p>Sem alterações às exigências ou associações documentais.</p>
|
||||
)}
|
||||
<ul className="list-disc pl-5">
|
||||
{after.requirements
|
||||
.filter(
|
||||
(r) =>
|
||||
before.requirements.find((b) => b.key === r.key)
|
||||
?.state !== r.state,
|
||||
)
|
||||
.map((r) => (
|
||||
<li key={r.key}>
|
||||
{changedRequirements.map((r) => {
|
||||
const previous = before.requirements.find(
|
||||
(item) => item.key === r.key,
|
||||
);
|
||||
const documentChanged =
|
||||
(previous?.document_id ?? null) !== r.document_id;
|
||||
return (
|
||||
<li key={r.key} className="space-y-1">
|
||||
{r.label}:{' '}
|
||||
{
|
||||
states[
|
||||
before.requirements.find(
|
||||
(b) => b.key === r.key,
|
||||
)?.state ?? 'indeterminate'
|
||||
]
|
||||
}{' '}
|
||||
→ {states[r.state]}.{' '}
|
||||
{r.document_id && 'Vínculo documental preservado.'}
|
||||
{states[previous?.state ?? 'indeterminate']} →{' '}
|
||||
{states[r.state]}.{' '}
|
||||
{documentChanged ? (
|
||||
<p>
|
||||
{previous?.document_id && r.document_id
|
||||
? 'Substituição de documento: '
|
||||
: 'Documento: '}
|
||||
{documentName(previous)} → {documentName(r)}
|
||||
.
|
||||
</p>
|
||||
) : (
|
||||
r.document_id &&
|
||||
'Vínculo documental preservado.'
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<p>{after.calculation_notice}</p>
|
||||
<PreparationSummary data={after} />
|
||||
|
||||
@@ -396,6 +396,79 @@ function preparationFixture() {
|
||||
],
|
||||
};
|
||||
}
|
||||
void test('impact exposes document replacement even when its state and filename stay unchanged', () => {
|
||||
const Impact = load(
|
||||
'../../resources/js/components/private-investment-preparation.tsx',
|
||||
preparationMocks,
|
||||
'PreparationImpact',
|
||||
);
|
||||
const before = preparationFixture();
|
||||
before.requirements = [before.requirements[1]];
|
||||
before.requirements[0].document = { file_name: 'ORIGINAL.pdf' };
|
||||
const after = structuredClone(before);
|
||||
after.requirements[0].document_id = 'replacement-document';
|
||||
const original = JSON.stringify({ before, after });
|
||||
const html = renderToStaticMarkup(
|
||||
React.createElement(Impact, { before, after }),
|
||||
);
|
||||
const impactList = html.slice(html.indexOf('<ul'), html.indexOf('</ul>'));
|
||||
assert.match(impactList, /ASSOCIATED/);
|
||||
assert.match(
|
||||
impactList,
|
||||
/Substituição de documento: ORIGINAL.pdf → ORIGINAL.pdf/,
|
||||
);
|
||||
assert.doesNotMatch(impactList, /Vínculo documental preservado/);
|
||||
assert.doesNotMatch(html, /Sem alterações às exigências/);
|
||||
assert.equal(JSON.stringify({ before, after }), original);
|
||||
});
|
||||
void test('impact distinguishes detachment, unavailable names, preserved history and unchanged associations', () => {
|
||||
const Impact = load(
|
||||
'../../resources/js/components/private-investment-preparation.tsx',
|
||||
preparationMocks,
|
||||
'PreparationImpact',
|
||||
);
|
||||
const before = preparationFixture();
|
||||
before.requirements = [before.requirements[1]];
|
||||
before.requirements[0].document = { file_name: 'ORIGINAL.pdf' };
|
||||
const render = (after) =>
|
||||
renderToStaticMarkup(React.createElement(Impact, { before, after }));
|
||||
const detached = structuredClone(before);
|
||||
detached.requirements[0] = {
|
||||
...detached.requirements[0],
|
||||
document_id: null,
|
||||
document: null,
|
||||
state: 'missing',
|
||||
};
|
||||
assert.match(
|
||||
render(detached),
|
||||
/Documento: ORIGINAL.pdf → Sem documento associado/,
|
||||
);
|
||||
const replaced = structuredClone(before);
|
||||
replaced.requirements[0] = {
|
||||
...replaced.requirements[0],
|
||||
document_id: 'new',
|
||||
document: null,
|
||||
};
|
||||
assert.match(
|
||||
render(replaced),
|
||||
/Substituição de documento: ORIGINAL.pdf → Documento associado — nome indisponível/,
|
||||
);
|
||||
const historical = structuredClone(before);
|
||||
historical.requirements[0].state = 'not_applicable';
|
||||
const historicalList = render(historical).split('</ul>')[0];
|
||||
assert.match(historicalList, /Vínculo documental preservado/);
|
||||
assert.doesNotMatch(historicalList, /Substituição de documento/);
|
||||
assert.match(
|
||||
render(structuredClone(before)),
|
||||
/Sem alterações às exigências ou associações documentais/,
|
||||
);
|
||||
const associated = structuredClone(detached);
|
||||
const added = renderToStaticMarkup(
|
||||
React.createElement(Impact, { before: associated, after: before }),
|
||||
);
|
||||
assert.match(added, /Documento: Sem documento associado → ORIGINAL.pdf/);
|
||||
assert.equal(before.requirements[0].document_id, 'saved-document');
|
||||
});
|
||||
const preparationMocks = {
|
||||
'@inertiajs/react': {
|
||||
Link: ({ href, children }) =>
|
||||
|
||||
Reference in New Issue
Block a user