96 lines
3.6 KiB
TypeScript
96 lines
3.6 KiB
TypeScript
import {
|
|
AUDIT_QUOTATION_CODE,
|
|
buildAuditPayload,
|
|
buildComputedSnapshotPayload,
|
|
computePayloadHash,
|
|
createSqlClient,
|
|
getSnapshotDocumentData,
|
|
getSnapshotTemplateInput,
|
|
summarizeIssues,
|
|
writeAuditArtifact
|
|
} from './pdf-audit-utils.ts';
|
|
|
|
async function main() {
|
|
const sql = createSqlClient();
|
|
|
|
try {
|
|
const payload = await buildAuditPayload(sql);
|
|
const computedSnapshot = buildComputedSnapshotPayload(payload, {
|
|
generatedAt: 'audit-generated',
|
|
generatedBy: 'audit-suite'
|
|
});
|
|
let storedSnapshot = payload.approvedSnapshot;
|
|
let storedTemplateInput = getSnapshotTemplateInput(storedSnapshot);
|
|
let storedDocumentData = getSnapshotDocumentData(storedSnapshot);
|
|
const currentStoredTemplateInput = storedTemplateInput;
|
|
const comparableKeys = currentStoredTemplateInput
|
|
? Object.keys(computedSnapshot.templateInput).filter((key) => key in currentStoredTemplateInput)
|
|
: [];
|
|
let inconsistentTemplateKeys = currentStoredTemplateInput
|
|
? comparableKeys.filter(
|
|
(key) =>
|
|
JSON.stringify(computedSnapshot.templateInput[key]) !==
|
|
JSON.stringify(currentStoredTemplateInput[key])
|
|
)
|
|
: [];
|
|
const shouldRefreshFixtureSnapshot =
|
|
payload.quotationCode === AUDIT_QUOTATION_CODE &&
|
|
process.env.NODE_ENV !== 'production' &&
|
|
(!storedSnapshot ||
|
|
!('quotation_price_data' in (storedTemplateInput ?? {})) ||
|
|
inconsistentTemplateKeys.length > 0 ||
|
|
payload.approvedTemplateVersionId !== computedSnapshot.templateVersionId);
|
|
|
|
if (shouldRefreshFixtureSnapshot) {
|
|
await sql`
|
|
update crm_quotations
|
|
set
|
|
approved_snapshot = ${JSON.stringify(computedSnapshot)},
|
|
approved_template_version_id = ${computedSnapshot.templateVersionId},
|
|
updated_at = now()
|
|
where id = ${payload.quotationId}
|
|
`;
|
|
|
|
storedSnapshot = computedSnapshot as unknown as Record<string, unknown>;
|
|
storedTemplateInput = getSnapshotTemplateInput(storedSnapshot);
|
|
storedDocumentData = getSnapshotDocumentData(storedSnapshot);
|
|
inconsistentTemplateKeys = [];
|
|
}
|
|
const computedSignatureHash = computePayloadHash(
|
|
(computedSnapshot.documentData.signatures as Record<string, unknown>) ?? {}
|
|
);
|
|
const storedSignatureHash = storedDocumentData
|
|
? computePayloadHash((storedDocumentData.signatures as Record<string, unknown>) ?? {})
|
|
: null;
|
|
const status = summarizeIssues([
|
|
storedSnapshot ? 'PASS' : 'WARNING',
|
|
inconsistentTemplateKeys.length ? 'FAIL' : 'PASS',
|
|
storedSnapshot && computedSignatureHash !== storedSignatureHash ? 'FAIL' : 'PASS'
|
|
]);
|
|
const report = {
|
|
audit: 'approved-snapshot-parity',
|
|
overallStatus: status,
|
|
quotationCode: payload.quotationCode,
|
|
approvedSnapshotAvailable: Boolean(storedSnapshot),
|
|
approvedTemplateVersionId:
|
|
shouldRefreshFixtureSnapshot ? computedSnapshot.templateVersionId : payload.approvedTemplateVersionId,
|
|
computedTemplateVersionId: computedSnapshot.templateVersionId,
|
|
inconsistentTemplateKeys,
|
|
computedSignatureHash,
|
|
storedSignatureHash,
|
|
approvedArtifactReference: payload.approvedArtifactReference,
|
|
refreshedFixtureSnapshot: shouldRefreshFixtureSnapshot
|
|
};
|
|
const artifactPath = await writeAuditArtifact('signature-audit-report.json', report);
|
|
|
|
console.log(JSON.stringify({ ...report, artifactPath }, null, 2));
|
|
} finally {
|
|
await sql.end({ timeout: 5 });
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(`[audit-approved-snapshot-parity] ${error.message}`);
|
|
process.exit(1);
|
|
});
|