task-h.5.3
This commit is contained in:
@@ -2,6 +2,7 @@ import {
|
||||
buildAuditPayload,
|
||||
createSqlClient,
|
||||
getActiveTemplateVersions,
|
||||
loadTemplateSourceByOrganizationName,
|
||||
summarizeIssues,
|
||||
writeAuditArtifact
|
||||
} from './pdf-audit-utils.ts';
|
||||
@@ -12,7 +13,9 @@ type IntegrityIssue = {
|
||||
| 'DUPLICATE_ACTIVE_VERSION'
|
||||
| 'ACTIVE_VERSION_WITHOUT_TEMPLATE'
|
||||
| 'MAPPING_WITHOUT_VERSION'
|
||||
| 'INVALID_APPROVED_TEMPLATE_VERSION';
|
||||
| 'INVALID_APPROVED_TEMPLATE_VERSION'
|
||||
| 'ACTIVE_VERSION_SCHEMA_DRIFT'
|
||||
| 'MISSING_RETAINED_INACTIVE_VERSION';
|
||||
message: string;
|
||||
context: Record<string, unknown>;
|
||||
};
|
||||
@@ -21,19 +24,29 @@ async function main() {
|
||||
const sql = createSqlClient();
|
||||
|
||||
try {
|
||||
const [activeTemplateVersions, versionRows, orphanMappingRows, payload] = await Promise.all([
|
||||
const [
|
||||
activeTemplateVersions,
|
||||
versionRows,
|
||||
orphanMappingRows,
|
||||
payload,
|
||||
templateRows
|
||||
] = await Promise.all([
|
||||
getActiveTemplateVersions(sql),
|
||||
sql`
|
||||
select
|
||||
v.id,
|
||||
v.template_id as "templateId",
|
||||
v.organization_id as "organizationId",
|
||||
o.name as "organizationName",
|
||||
v.version,
|
||||
v.is_active as "isActive",
|
||||
v.schema_json as "schemaJson",
|
||||
t.id as "resolvedTemplateId"
|
||||
from crm_document_template_versions v
|
||||
left join crm_document_templates t
|
||||
on t.id = v.template_id
|
||||
left join organizations o
|
||||
on o.id = v.organization_id
|
||||
where v.deleted_at is null
|
||||
`,
|
||||
sql`
|
||||
@@ -47,7 +60,20 @@ async function main() {
|
||||
where m.deleted_at is null
|
||||
and (v.id is null or v.deleted_at is not null)
|
||||
`,
|
||||
buildAuditPayload(sql)
|
||||
buildAuditPayload(sql),
|
||||
sql`
|
||||
select
|
||||
t.id,
|
||||
t.organization_id as "organizationId",
|
||||
o.name as "organizationName",
|
||||
t.template_name as "templateName"
|
||||
from crm_document_templates t
|
||||
inner join organizations o
|
||||
on o.id = t.organization_id
|
||||
where t.deleted_at is null
|
||||
and t.document_type = 'quotation'
|
||||
and t.file_type = 'pdfme'
|
||||
`
|
||||
]);
|
||||
const issues: IntegrityIssue[] = [];
|
||||
const templateBuckets = new Map<string, typeof activeTemplateVersions>();
|
||||
@@ -121,6 +147,48 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
for (const templateRow of templateRows as Array<Record<string, unknown>>) {
|
||||
const templateId = String(templateRow.id);
|
||||
const rows = (versionRows as Array<Record<string, unknown>>).filter(
|
||||
(row) => String(row.templateId) === templateId
|
||||
);
|
||||
const activeRows = rows.filter((row) => Boolean(row.isActive));
|
||||
const inactiveRows = rows.filter((row) => !row.isActive);
|
||||
const templateSource = loadTemplateSourceByOrganizationName(String(templateRow.organizationName));
|
||||
|
||||
if (templateSource && activeRows.length === 1) {
|
||||
const activeSchema =
|
||||
typeof activeRows[0].schemaJson === 'string'
|
||||
? JSON.parse(String(activeRows[0].schemaJson))
|
||||
: activeRows[0].schemaJson;
|
||||
|
||||
if (JSON.stringify(activeSchema) !== JSON.stringify(templateSource.schemaJson)) {
|
||||
issues.push({
|
||||
kind: 'ACTIVE_VERSION_SCHEMA_DRIFT',
|
||||
message: `Active template version does not match source JSON for ${String(templateRow.templateName)}`,
|
||||
context: {
|
||||
templateId,
|
||||
organizationName: templateRow.organizationName,
|
||||
activeVersionId: activeRows[0].id,
|
||||
activeVersion: activeRows[0].version,
|
||||
sourceFile: templateSource.relativePath
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (rows.length > 1 && inactiveRows.length === 0) {
|
||||
issues.push({
|
||||
kind: 'MISSING_RETAINED_INACTIVE_VERSION',
|
||||
message: `Template ${String(templateRow.templateName)} has no retained inactive historical version`,
|
||||
context: {
|
||||
templateId,
|
||||
organizationName: templateRow.organizationName
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const row of orphanMappingRows as Array<Record<string, unknown>>) {
|
||||
issues.push({
|
||||
kind: 'MAPPING_WITHOUT_VERSION',
|
||||
@@ -129,13 +197,16 @@ async function main() {
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
payload.approvedTemplateVersionId &&
|
||||
!activeTemplateVersions.some((record) => record.versionId === payload.approvedTemplateVersionId)
|
||||
) {
|
||||
const approvedTemplateVersionExists = payload.approvedTemplateVersionId
|
||||
? (versionRows as Array<Record<string, unknown>>).some(
|
||||
(row) => String(row.id) === payload.approvedTemplateVersionId
|
||||
)
|
||||
: true;
|
||||
|
||||
if (payload.approvedTemplateVersionId && !approvedTemplateVersionExists) {
|
||||
issues.push({
|
||||
kind: 'INVALID_APPROVED_TEMPLATE_VERSION',
|
||||
message: 'Audit quotation approvedTemplateVersionId does not resolve to an active template version',
|
||||
message: 'Audit quotation approvedTemplateVersionId does not resolve to a retained template version',
|
||||
context: {
|
||||
quotationCode: payload.quotationCode,
|
||||
approvedTemplateVersionId: payload.approvedTemplateVersionId
|
||||
@@ -150,6 +221,9 @@ async function main() {
|
||||
activeTemplateCount: activeTemplateVersions.length,
|
||||
quotationCode: payload.quotationCode,
|
||||
approvedTemplateVersionId: payload.approvedTemplateVersionId,
|
||||
approvedTemplateVersionIsActive: activeTemplateVersions.some(
|
||||
(record) => record.versionId === payload.approvedTemplateVersionId
|
||||
),
|
||||
issues
|
||||
};
|
||||
const artifactPath = await writeAuditArtifact('template-version-report.json', report);
|
||||
|
||||
Reference in New Issue
Block a user