diff --git a/artifacts/pdf-audit/payload-parity-report.json b/artifacts/pdf-audit/payload-parity-report.json index aa8cce5..ba7f89a 100644 --- a/artifacts/pdf-audit/payload-parity-report.json +++ b/artifacts/pdf-audit/payload-parity-report.json @@ -1,13 +1,13 @@ { "audit": "payload-parity", "overallStatus": "PASS", - "quotationCode": "QT-H5-AUDIT", - "previewHash": "1914aaab07b562d967c63d9f3bdea6d44b583e4aac39ea590d08e49975391675", - "generationHash": "1914aaab07b562d967c63d9f3bdea6d44b583e4aac39ea590d08e49975391675", - "snapshotHash": "1914aaab07b562d967c63d9f3bdea6d44b583e4aac39ea590d08e49975391675", + "quotationCode": "QT2606-1122", + "previewHash": "8ab32cb3cf38b76f6bbc4c5704f89fca6a3d1bbaeed0b1d0488cf33217f0f39a", + "generationHash": "8ab32cb3cf38b76f6bbc4c5704f89fca6a3d1bbaeed0b1d0488cf33217f0f39a", + "snapshotHash": "8ab32cb3cf38b76f6bbc4c5704f89fca6a3d1bbaeed0b1d0488cf33217f0f39a", "mismatches": [], - "comparableFieldCount": 35, - "topicInputKeyCount": 10, + "comparableFieldCount": 33, + "topicInputKeyCount": 6, "signatureParity": true, "priceTableParity": true, "priceTableRowCount": 1, diff --git a/artifacts/pdf-audit/placeholder-integrity-report.json b/artifacts/pdf-audit/placeholder-integrity-report.json index 5453ecd..7f5ac25 100644 --- a/artifacts/pdf-audit/placeholder-integrity-report.json +++ b/artifacts/pdf-audit/placeholder-integrity-report.json @@ -1,30 +1,34 @@ { "audit": "placeholder-integrity", - "overallStatus": "PASS", + "overallStatus": "FAIL", "results": [ { - "templateName": "ALLA Quotation Standard", - "organizationId": "2330c067-2737-4443-bd6e-ccb8a6cf856b", - "version": "1.1", + "templateName": "ONVALLA Quotation Standard", + "organizationId": "6bf90327-2ce9-4831-93bb-e8d026699ff2", + "version": "1.0", "duplicateFieldNames": [], "typoFields": [], "unmappedPlaceholders": [], - "deadMappings": [], + "deadMappings": [ + "exclusion_data" + ], "invalidReservedMappings": [], "missingReservedFields": [], - "status": "PASS" + "status": "FAIL" }, { - "templateName": "ONVALLA Quotation Standard", - "organizationId": "8cd23975-e199-4ed0-85a9-8cbd01c810d2", - "version": "1.1", + "templateName": "ALLA Quotation Standard", + "organizationId": "d5c46139-9af4-421b-9395-d83620c62f9b", + "version": "1.0", "duplicateFieldNames": [], "typoFields": [], "unmappedPlaceholders": [], - "deadMappings": [], + "deadMappings": [ + "exclusion_data" + ], "invalidReservedMappings": [], "missingReservedFields": [], - "status": "PASS" + "status": "FAIL" } ] } diff --git a/artifacts/pdf-audit/runtime-payload-report.json b/artifacts/pdf-audit/runtime-payload-report.json index ac849ce..d4d7dcb 100644 --- a/artifacts/pdf-audit/runtime-payload-report.json +++ b/artifacts/pdf-audit/runtime-payload-report.json @@ -1,11 +1,11 @@ { "audit": "runtime-payload", - "overallStatus": "FAIL", - "staticLabelsStatus": "FAIL", - "quotationCode": "QT2606-1126", + "overallStatus": "PASS", + "staticLabelsStatus": "PASS", + "quotationCode": "QT2606-1122", "templateName": "ALLA Quotation Standard", "templateVersion": "1.0", - "findingCount": 165, + "findingCount": 170, "findings": [ { "field": "documentData.company.id", @@ -25,28 +25,28 @@ "field": "documentData.customer.id", "source": "documentData", "status": "PASS", - "value": "ba10e844-997e-45ed-8696-7ca96730a859", + "value": "56f1f9a3-6204-4ccc-af0f-5dbf866d5a56", "message": "String is populated" }, { "field": "documentData.customer.name", "source": "documentData", "status": "PASS", - "value": "มหาวิทยาลัยเทคโนโลยีพระจอมเกล้าพระนครเหนือ", + "value": "บริษัท นอร์ธเทิร์น แมชชีนเนอรี่ ดีลเลอร์ จำกัด", "message": "String is populated" }, { "field": "documentData.customer.address", "source": "documentData", "status": "PASS", - "value": "Bang Sue, Bangkok", + "value": "Mueang Chiang Mai, Chiang Mai", "message": "String is populated" }, { "field": "documentData.customer.phone", "source": "documentData", "status": "PASS", - "value": "02-400827", + "value": "02-400824", "message": "String is populated" }, { @@ -60,14 +60,14 @@ "field": "documentData.contact.id", "source": "documentData", "status": "PASS", - "value": "2591dec2-b7c9-486a-9810-8ab9bf0f4926", + "value": "93843e06-65db-492f-9229-066e6dfdec5d", "message": "String is populated" }, { "field": "documentData.contact.name", "source": "documentData", "status": "PASS", - "value": "คุณวราภรณ์ เกษมศรี", + "value": "คุณกิตติพงศ์ สุวรรณ", "message": "String is populated" }, { @@ -81,56 +81,56 @@ "field": "documentData.contact.mobile", "source": "documentData", "status": "PASS", - "value": "0810000250", + "value": "0810000220", "message": "String is populated" }, { "field": "documentData.contact.position", "source": "documentData", "status": "PASS", - "value": "Procurement Manager", + "value": "Plant Manager", "message": "String is populated" }, { "field": "documentData.quotation.id", "source": "documentData", "status": "PASS", - "value": "c964ec57-c036-404b-93c5-6826d6539d1e", + "value": "7dc0630b-e27d-4696-803a-d93f51bb9804", "message": "String is populated" }, { "field": "documentData.quotation.code", "source": "documentData", "status": "PASS", - "value": "QT2606-1126", + "value": "QT2606-1122", "message": "String is populated" }, { "field": "documentData.quotation.projectName", "source": "documentData", "status": "PASS", - "value": "Material Handling Lab Crane", + "value": "Dealer Stock Crane Package", "message": "String is populated" }, { "field": "documentData.quotation.projectLocation", "source": "documentData", "status": "PASS", - "value": "Bang Sue Engineering Faculty", + "value": "Chiang Mai Service Warehouse", "message": "String is populated" }, { "field": "documentData.quotation.attention", "source": "documentData", "status": "PASS", - "value": "คุณวราภรณ์ เกษมศรี", + "value": "คุณกิตติพงศ์ สุวรรณ", "message": "String is populated" }, { "field": "documentData.quotation.reference", "source": "documentData", "status": "PASS", - "value": "crm-uat-seed-v1:university-lab-crane-approved", + "value": "crm-uat-seed-v1:dealer-channel-approved", "message": "String is populated" }, { @@ -151,14 +151,14 @@ "field": "documentData.quotation.subtotal", "source": "documentData", "status": "PASS", - "value": 3008000, + "value": 4418000, "message": "Scalar value is populated" }, { "field": "documentData.quotation.totalAmount", "source": "documentData", "status": "PASS", - "value": 3122003, + "value": 4585442, "message": "Scalar value is populated" }, { @@ -172,7 +172,7 @@ "field": "documentData.items[0].id", "source": "documentData", "status": "PASS", - "value": "4b151f4b-ff48-4dcc-b092-d891ff6f5a29", + "value": "7b2a99ab-5106-4ab1-b611-cd63957aa38f", "message": "String is populated" }, { @@ -186,7 +186,7 @@ "field": "documentData.items[0].description", "source": "documentData", "status": "PASS", - "value": "Main equipment supply for Material Handling Lab Crane", + "value": "Main equipment supply for Dealer Stock Crane Package", "message": "String is populated" }, { @@ -207,7 +207,7 @@ "field": "documentData.items[0].unitPrice", "source": "documentData", "status": "PASS", - "value": 1717102, + "value": 2521993, "message": "Scalar value is populated" }, { @@ -221,14 +221,14 @@ "field": "documentData.items[0].totalPrice", "source": "documentData", "status": "PASS", - "value": 1717102, + "value": 2521993, "message": "Scalar value is populated" }, { "field": "documentData.items[1].id", "source": "documentData", "status": "PASS", - "value": "43c6d8c7-4af3-4e35-80af-5bb1beeffdc7", + "value": "cc058b8e-2ce1-4e66-a72c-6c40474cf962", "message": "String is populated" }, { @@ -242,7 +242,7 @@ "field": "documentData.items[1].description", "source": "documentData", "status": "PASS", - "value": "Installation and commissioning for Material Handling Lab Crane", + "value": "Installation and commissioning for Dealer Stock Crane Package", "message": "String is populated" }, { @@ -263,7 +263,7 @@ "field": "documentData.items[1].unitPrice", "source": "documentData", "status": "PASS", - "value": 874161, + "value": 1283924, "message": "Scalar value is populated" }, { @@ -277,14 +277,14 @@ "field": "documentData.items[1].totalPrice", "source": "documentData", "status": "PASS", - "value": 874161, + "value": 1283924, "message": "Scalar value is populated" }, { "field": "documentData.items[2].id", "source": "documentData", "status": "PASS", - "value": "b2aca42a-0592-4725-9ecd-89c8287b4b44", + "value": "9436e5e6-6dc7-4fa6-bf8a-da2727992680", "message": "String is populated" }, { @@ -298,7 +298,7 @@ "field": "documentData.items[2].description", "source": "documentData", "status": "PASS", - "value": "Operator training and documentation for Material Handling Lab Crane", + "value": "Operator training and documentation for Dealer Stock Crane Package", "message": "String is populated" }, { @@ -319,7 +319,7 @@ "field": "documentData.items[2].unitPrice", "source": "documentData", "status": "PASS", - "value": 312200, + "value": 458544, "message": "Scalar value is populated" }, { @@ -333,14 +333,14 @@ "field": "documentData.items[2].totalPrice", "source": "documentData", "status": "PASS", - "value": 312200, + "value": 458544, "message": "Scalar value is populated" }, { "field": "documentData.items[3].id", "source": "documentData", "status": "PASS", - "value": "bb721763-f87f-4a92-8899-36e690eede66", + "value": "63d82897-1660-4f49-b0d1-fddb9712922f", "message": "String is populated" }, { @@ -354,7 +354,7 @@ "field": "documentData.items[3].description", "source": "documentData", "status": "PASS", - "value": "Project management and testing for Material Handling Lab Crane", + "value": "Project management and testing for Dealer Stock Crane Package", "message": "String is populated" }, { @@ -375,7 +375,7 @@ "field": "documentData.items[3].unitPrice", "source": "documentData", "status": "PASS", - "value": 218540, + "value": 320981, "message": "Scalar value is populated" }, { @@ -389,7 +389,7 @@ "field": "documentData.items[3].totalPrice", "source": "documentData", "status": "PASS", - "value": 218540, + "value": 320981, "message": "Scalar value is populated" }, { @@ -403,14 +403,14 @@ "field": "documentData.quotationCustomers[0].id", "source": "documentData", "status": "PASS", - "value": "8de4be97-45d0-48f7-b6a6-962077b63389", + "value": "7a20818a-48a1-4eed-a779-ac5ec9d6159b", "message": "String is populated" }, { "field": "documentData.quotationCustomers[0].customerId", "source": "documentData", "status": "PASS", - "value": "ba10e844-997e-45ed-8696-7ca96730a859", + "value": "56f1f9a3-6204-4ccc-af0f-5dbf866d5a56", "message": "String is populated" }, { @@ -438,7 +438,7 @@ "field": "documentData.topics.all[0].id", "source": "documentData", "status": "PASS", - "value": "1c97eb84-131e-49ad-97ca-a59547223c8d", + "value": "ef076be2-fbef-444a-b7cf-cc2523e8b7dc", "message": "String is populated" }, { @@ -473,7 +473,7 @@ "field": "documentData.topics.all[0].items[0].content", "source": "documentData", "status": "PASS", - "value": "Supply and execute material handling lab crane according to approved drawing.", + "value": "Supply and execute dealer stock crane package according to approved drawing.", "message": "String is populated" }, { @@ -501,7 +501,7 @@ "field": "documentData.topics.all[1].id", "source": "documentData", "status": "PASS", - "value": "49935121-792a-405c-8a6f-fbdbc8bdda65", + "value": "14f41b98-f9d8-463b-9880-8e96d258a6ba", "message": "String is populated" }, { @@ -564,7 +564,7 @@ "field": "documentData.topics.all[2].id", "source": "documentData", "status": "PASS", - "value": "5685dff7-add1-407e-9e6a-e16d6ea8d1be", + "value": "54500c11-5f51-49d6-947e-db2f4d689959", "message": "String is populated" }, { @@ -634,7 +634,7 @@ "field": "documentData.topics.exclusion[0].id", "source": "documentData", "status": "PASS", - "value": "49935121-792a-405c-8a6f-fbdbc8bdda65", + "value": "14f41b98-f9d8-463b-9880-8e96d258a6ba", "message": "String is populated" }, { @@ -732,14 +732,14 @@ "field": "documentData.pdfme.quotation_date", "source": "documentData", "status": "PASS", - "value": "Jun 15, 2026", + "value": "Jun 3, 2026", "message": "String is populated" }, { "field": "documentData.pdfme.quotation_price", "source": "documentData", "status": "PASS", - "value": "THB 3,008,000.00", + "value": "THB 4,418,000.00", "message": "String is populated" }, { @@ -816,29 +816,29 @@ "field": "documentData.signatures.preparedBy.actedAt", "source": "documentData", "status": "PASS", - "value": "2026-06-15T09:00:00.000Z", + "value": "2026-06-03T09:00:00.000Z", "message": "String is populated" }, { "field": "documentData.signatures.approvedBy.name", "source": "documentData", "status": "PASS", - "value": "-", + "value": "Super Admin", "message": "String is populated" }, { "field": "documentData.signatures.approvedBy.position", "source": "documentData", "status": "PASS", - "value": "-", + "value": "Sales Manager", "message": "String is populated" }, { "field": "documentData.signatures.approvedBy.actedAt", "source": "documentData", - "status": "WARNING", - "value": null, - "message": "Value is null" + "status": "PASS", + "value": "2026-06-06T10:00:00.000Z", + "message": "String is populated" }, { "field": "documentData.signatures.authorizedBy.name", @@ -858,28 +858,28 @@ "field": "documentData.signatures.authorizedBy.actedAt", "source": "documentData", "status": "PASS", - "value": "2026-06-17T11:00:00.000Z", + "value": "2026-06-07T09:00:00.000Z", "message": "String is populated" }, { "field": "templateInput.customer_name", "source": "templateInput", "status": "PASS", - "value": "มหาวิทยาลัยเทคโนโลยีพระจอมเกล้าพระนครเหนือ", + "value": "บริษัท นอร์ธเทิร์น แมชชีนเนอรี่ ดีลเลอร์ จำกัด", "message": "String is populated" }, { "field": "templateInput.customer_addr", "source": "templateInput", "status": "PASS", - "value": "Bang Sue, Bangkok", + "value": "Mueang Chiang Mai, Chiang Mai", "message": "String is populated" }, { "field": "templateInput.customer_tel", "source": "templateInput", "status": "PASS", - "value": "02-400827", + "value": "02-400824", "message": "String is populated" }, { @@ -893,35 +893,35 @@ "field": "templateInput.customer_att", "source": "templateInput", "status": "PASS", - "value": "คุณวราภรณ์ เกษมศรี", + "value": "คุณกิตติพงศ์ สุวรรณ", "message": "String is populated" }, { "field": "templateInput.project_name", "source": "templateInput", "status": "PASS", - "value": "Material Handling Lab Crane", + "value": "Dealer Stock Crane Package", "message": "String is populated" }, { "field": "templateInput.site_location", "source": "templateInput", "status": "PASS", - "value": "Bang Sue Engineering Faculty", + "value": "Chiang Mai Service Warehouse", "message": "String is populated" }, { "field": "templateInput.quotation_date", "source": "templateInput", "status": "PASS", - "value": "Jun 15, 2026", + "value": "Jun 3, 2026", "message": "String is populated" }, { "field": "templateInput.quotation_code", "source": "templateInput", "status": "PASS", - "value": "QT2606-1126", + "value": "QT2606-1122", "message": "String is populated" }, { @@ -935,7 +935,7 @@ "field": "templateInput.quotation_price", "source": "templateInput", "status": "PASS", - "value": "THB 3,008,000.00", + "value": "THB 4,418,000.00", "message": "String is populated" }, { @@ -970,14 +970,14 @@ "field": "templateInput.app2", "source": "templateInput", "status": "PASS", - "value": "-", + "value": "Super Admin", "message": "String is populated" }, { "field": "templateInput.app2_position", "source": "templateInput", "status": "PASS", - "value": "-", + "value": "Sales Manager", "message": "String is populated" }, { @@ -1001,6 +1001,41 @@ "value": "rows:4", "message": "Array shape is valid" }, + { + "field": "templateInput.tel_label", + "source": "templateInput", + "status": "PASS", + "value": "Tel", + "message": "String is populated" + }, + { + "field": "templateInput.email_label", + "source": "templateInput", + "status": "PASS", + "value": "Email", + "message": "String is populated" + }, + { + "field": "templateInput.att_label", + "source": "templateInput", + "status": "PASS", + "value": "Att", + "message": "String is populated" + }, + { + "field": "templateInput.project_label", + "source": "templateInput", + "status": "PASS", + "value": "Project", + "message": "String is populated" + }, + { + "field": "templateInput.site_label", + "source": "templateInput", + "status": "PASS", + "value": "Location", + "message": "String is populated" + }, { "field": "templateInput.topic_1_0", "source": "templateInput", @@ -1047,14 +1082,14 @@ "field": "templateInput.quotation_date_data", "source": "templateInput", "status": "PASS", - "value": "Jun 15, 2026", + "value": "Jun 3, 2026", "message": "String is populated" }, { "field": "templateInput.quotation_code_data", "source": "templateInput", "status": "PASS", - "value": "QT2606-1126", + "value": "QT2606-1122", "message": "String is populated" }, { @@ -1117,7 +1152,7 @@ "field": "templateInput.quotation_price_data[0][1]", "source": "templateInput", "status": "PASS", - "value": "฿3,008,000.00", + "value": "฿4,418,000.00", "message": "Price table amount cell is formatted" }, { @@ -1130,37 +1165,37 @@ { "field": "templateInput.tel_label", "source": "templateInput", - "status": "FAIL", - "value": null, - "message": "Static label is empty" + "status": "PASS", + "value": "Tel", + "message": "Static label is populated" }, { "field": "templateInput.email_label", "source": "templateInput", - "status": "FAIL", - "value": null, - "message": "Static label is empty" + "status": "PASS", + "value": "Email", + "message": "Static label is populated" }, { "field": "templateInput.att_label", "source": "templateInput", - "status": "FAIL", - "value": null, - "message": "Static label is empty" + "status": "PASS", + "value": "Att", + "message": "Static label is populated" }, { "field": "templateInput.project_label", "source": "templateInput", - "status": "FAIL", - "value": null, - "message": "Static label is empty" + "status": "PASS", + "value": "Project", + "message": "Static label is populated" }, { "field": "templateInput.site_label", "source": "templateInput", - "status": "FAIL", - "value": null, - "message": "Static label is empty" + "status": "PASS", + "value": "Location", + "message": "Static label is populated" } ] } diff --git a/artifacts/pdf-audit/signature-audit-report.json b/artifacts/pdf-audit/signature-audit-report.json index 21dddf9..c906d28 100644 --- a/artifacts/pdf-audit/signature-audit-report.json +++ b/artifacts/pdf-audit/signature-audit-report.json @@ -1,13 +1,22 @@ { "audit": "approved-snapshot-parity", - "overallStatus": "PASS", - "quotationCode": "QT-H5-AUDIT", + "overallStatus": "FAIL", + "quotationCode": "QT2606-1122", "approvedSnapshotAvailable": true, - "approvedTemplateVersionId": "bd6713d5-a731-482f-a5bb-0dea9d766301", - "computedTemplateVersionId": "bd6713d5-a731-482f-a5bb-0dea9d766301", - "inconsistentTemplateKeys": [], - "computedSignatureHash": "39e2c42458c8b08b4299dc3136c9812c311ae7886df91078f4c9ae6e87bdcc00", - "storedSignatureHash": "39e2c42458c8b08b4299dc3136c9812c311ae7886df91078f4c9ae6e87bdcc00", - "approvedArtifactReference": "artifact:a1fd951d-f0b4-4e5b-be88-073596b3829d", + "approvedTemplateVersionId": null, + "computedTemplateVersionId": "6c375f16-3f89-4987-88b0-c8ebe48aeb89", + "inconsistentTemplateKeys": [ + "quotation_date", + "topic_1_0", + "item_topic_1_0", + "topic_1_1", + "item_topic_1_1", + "topic_1_2", + "item_topic_1_2", + "quotation_date_data" + ], + "computedSignatureHash": "72c05979b9a0b0666a06cce5b80dc67fe9c5493eced4366a4cf63785dc415269", + "storedSignatureHash": "933007a4f0e782b88e3d3049527a25b5bcd744d4a483282050fbfc1b7f06a362", + "approvedArtifactReference": "artifact:ec300b87-3710-43a4-a63c-a72168fe3933", "refreshedFixtureSnapshot": false } diff --git a/artifacts/pdf-audit/summary.json b/artifacts/pdf-audit/summary.json index 55c5645..3838430 100644 --- a/artifacts/pdf-audit/summary.json +++ b/artifacts/pdf-audit/summary.json @@ -1,7 +1,7 @@ { - "generatedAt": "2026-06-19T10:12:54.977Z", - "overallStatus": "PASS", - "quotationCode": "QT-H5-AUDIT", + "generatedAt": "2026-06-29T04:38:30.133Z", + "overallStatus": "FAIL", + "quotationCode": "QT2606-1122", "artifacts": { "templateVersionReport": "template-version-report.json", "placeholderIntegrityReport": "placeholder-integrity-report.json", @@ -12,11 +12,11 @@ }, "statuses": { "templateVersion": "PASS", - "placeholderIntegrity": "PASS", + "placeholderIntegrity": "FAIL", "runtimePayload": "PASS", "payloadParity": "PASS", "topicRuntime": "PASS", - "approvedSnapshotParity": "PASS", + "approvedSnapshotParity": "FAIL", "staticLabels": "PASS" }, "priceTable": { diff --git a/artifacts/pdf-audit/summary.md b/artifacts/pdf-audit/summary.md index feb3146..a974e64 100644 --- a/artifacts/pdf-audit/summary.md +++ b/artifacts/pdf-audit/summary.md @@ -1,17 +1,17 @@ # PDF Integrity Summary -- Generated At: 2026-06-19T10:12:54.977Z -- Overall Status: PASS -- Quotation Code: QT-H5-AUDIT +- Generated At: 2026-06-29T04:38:30.133Z +- Overall Status: FAIL +- Quotation Code: QT2606-1122 ## Statuses - Template Version Integrity: PASS -- Placeholder Integrity: PASS +- Placeholder Integrity: FAIL - Runtime Payload: PASS - Payload Parity: PASS - Topic Runtime: PASS -- Approved Snapshot Parity: PASS +- Approved Snapshot Parity: FAIL - Static Labels: PASS - Price Table Present: Yes - Price Table Status: PASS diff --git a/artifacts/pdf-audit/template-version-report.json b/artifacts/pdf-audit/template-version-report.json index 3d0ca97..1d9813e 100644 --- a/artifacts/pdf-audit/template-version-report.json +++ b/artifacts/pdf-audit/template-version-report.json @@ -2,8 +2,8 @@ "audit": "template-version-integrity", "overallStatus": "PASS", "activeTemplateCount": 2, - "quotationCode": "QT-H5-AUDIT", - "approvedTemplateVersionId": "bd6713d5-a731-482f-a5bb-0dea9d766301", - "approvedTemplateVersionIsActive": true, + "quotationCode": "QT2606-1122", + "approvedTemplateVersionId": null, + "approvedTemplateVersionIsActive": false, "issues": [] } diff --git a/artifacts/pdf-audit/topic-runtime-report.json b/artifacts/pdf-audit/topic-runtime-report.json index 2ea3357..76630bb 100644 --- a/artifacts/pdf-audit/topic-runtime-report.json +++ b/artifacts/pdf-audit/topic-runtime-report.json @@ -1,21 +1,17 @@ { "audit": "topic-runtime", "overallStatus": "PASS", - "quotationCode": "QT-H5-AUDIT", - "topicCount": 5, + "quotationCode": "QT2606-1122", + "topicCount": 3, "topicInputKeys": [ "topic_1_0", "topic_1_1", - "topic_1_2", - "topic_1_3", - "topic_1_4" + "topic_1_2" ], "itemTopicInputKeys": [ "item_topic_1_0", "item_topic_1_1", - "item_topic_1_2", - "item_topic_1_3", - "item_topic_1_4" + "item_topic_1_2" ], "duplicateKeys": [], "emptySections": [] diff --git a/docs/implementation/pdf-audit-report.json b/docs/implementation/pdf-audit-report.json index f94f56c..7bf2d7a 100644 --- a/docs/implementation/pdf-audit-report.json +++ b/docs/implementation/pdf-audit-report.json @@ -1,19 +1,61 @@ { - "generatedAt": "2026-06-19T10:12:54.188Z", - "overallStatus": "PASS", - "quotationCode": "QT-H5-AUDIT", + "generatedAt": "2026-06-29T04:38:25.895Z", + "overallStatus": "FAIL", + "quotationCode": "QT2606-1122", "expectedFixtureCode": "QT-H5-AUDIT", "template": { "templateName": "ALLA Quotation Standard", - "version": "1.1", - "versionId": "bd6713d5-a731-482f-a5bb-0dea9d766301", + "version": "1.0", + "versionId": "6c375f16-3f89-4987-88b0-c8ebe48aeb89", "fieldCount": 44, - "previousVersion": "1.0", - "deletedFields": [ - "exclusion_data", - "exclusion_label" + "previousVersion": null, + "deletedFields": [], + "newFields": [ + "Please_do_not", + "app1", + "app1_position", + "app2", + "app2_position", + "app3", + "app3_position", + "att_label", + "colon", + "colon copy", + "colon_att", + "colon_email", + "colon_fax", + "colon_project", + "colon_site", + "colon_tel", + "company1", + "company2", + "company_addr1", + "company_addr2", + "company_email", + "company_tax", + "company_tel", + "customer_addr", + "customer_att", + "customer_email", + "customer_name", + "customer_tel", + "data_topic", + "dear_sirs", + "email_label", + "line", + "project_label", + "project_name", + "quotation_code_data", + "quotation_code_lable", + "quotation_date_data", + "quotation_date_labe", + "quotation_price_data", + "site_label", + "site_location", + "tel_label", + "topic", + "yours_faithfuly" ], - "newFields": [], "duplicateFields": [], "unknownFields": [], "classifications": [ @@ -135,23 +177,23 @@ }, { "name": "tel_label", - "classification": "static input field" + "classification": "readonly/static visual field" }, { "name": "email_label", - "classification": "static input field" + "classification": "readonly/static visual field" }, { "name": "att_label", - "classification": "static input field" + "classification": "readonly/static visual field" }, { "name": "project_label", - "classification": "static input field" + "classification": "readonly/static visual field" }, { "name": "site_label", - "classification": "static input field" + "classification": "readonly/static visual field" }, { "name": "topic", @@ -196,33 +238,63 @@ ] }, "metrics": { - "topicCount": 5, - "itemCount": 3, - "partyCount": 3, - "dynamicTopicFieldCount": 5, - "dynamicTopicItemFieldCount": 5, - "mappingCount": 23 + "topicCount": 3, + "itemCount": 4, + "partyCount": 1, + "dynamicTopicFieldCount": 3, + "dynamicTopicItemFieldCount": 3, + "mappingCount": 20 }, "mappingCoverage": { "placeholderTokenCount": 12, "unmappedTokens": [], - "orphanMappings": [], + "orphanMappings": [ + "exclusion_data" + ], "priceTableMapping": { "sourcePath": "pdfme.quotation_price_data", "dataType": "table" }, - "itemTableColumns": [] + "itemTableColumns": [ + { + "columnName": "Item", + "sourceField": "itemNumber" + }, + { + "columnName": "Description", + "sourceField": "description" + }, + { + "columnName": "Qty", + "sourceField": "quantity" + }, + { + "columnName": "Unit", + "sourceField": "unitLabel" + }, + { + "columnName": "Unit Price", + "sourceField": "unitPrice" + }, + { + "columnName": "Total", + "sourceField": "totalPrice" + } + ] }, "runtimePayload": { - "templateInputKeys": 35, - "topicInputKeys": 10, + "templateInputKeys": 33, + "topicInputKeys": 6, "priceTableRows": 1 }, "consistency": { - "status": "PASS", + "status": "FAIL", "approvedSnapshotAvailable": true, - "approvedArtifactReference": "artifact:a1fd951d-f0b4-4e5b-be88-073596b3829d", - "inconsistentKeys": [] + "approvedArtifactReference": "artifact:ec300b87-3710-43a4-a63c-a72168fe3933", + "inconsistentKeys": [ + "quotation_date", + "quotation_date_data" + ] }, "integritySummary": { "available": true, diff --git a/docs/implementation/pdf-audit-report.md b/docs/implementation/pdf-audit-report.md index 14ccb8b..ebdff25 100644 --- a/docs/implementation/pdf-audit-report.md +++ b/docs/implementation/pdf-audit-report.md @@ -1,29 +1,29 @@ # PDF Audit Report -- Generated At: 2026-06-19T10:12:54.188Z -- Overall Status: PASS -- Quotation Code: QT-H5-AUDIT -- Template: ALLA Quotation Standard v1.1 -- Previous Version: 1.0 +- Generated At: 2026-06-29T04:38:25.895Z +- Overall Status: FAIL +- Quotation Code: QT2606-1122 +- Template: ALLA Quotation Standard v1.0 +- Previous Version: None ## Metrics -- Topic Count: 5 -- Item Count: 3 -- Project Party Count: 3 -- Mapping Count: 23 +- Topic Count: 3 +- Item Count: 4 +- Project Party Count: 1 +- Mapping Count: 20 ## Mapping Coverage - Placeholder Tokens: 12 - Unmapped Tokens: None -- Orphan Mappings: None +- Orphan Mappings: exclusion_data ## Visual / Consistency - Approved Snapshot Available: Yes -- Consistency Status: PASS -- Inconsistent Keys: None +- Consistency Status: FAIL +- Inconsistent Keys: quotation_date, quotation_date_data ## Integrity Layer diff --git a/plans/task-p.4.3.1.md b/plans/task-p.4.3.1.md new file mode 100644 index 0000000..538e0ba --- /dev/null +++ b/plans/task-p.4.3.1.md @@ -0,0 +1,413 @@ +# Task P.4.3.1 - PDF Runtime Regression Stabilization + +## Objective + +Stabilize the Task P.4.3 section-based PDF runtime refactor before moving to Product Item implementation. + +This task fixes regression only. + +Do NOT add new Product Item pages. +Do NOT create new template JSON files. +Do NOT change database schema. +Do NOT change CRM Template UI. +Do NOT introduce new user-facing behavior. + +--- + +# Background + +After Task P.4.3, runtime audit still fails. + +Observed audit result: + +```text +overallStatus = FAIL +staticLabelsStatus = FAIL +quotationCode = QT2606-1126 +templateName = ALLA Quotation Standard +templateVersion = 1.0 +``` + +Most runtime payloads pass, including: + +* documentData +* quotation data +* customer data +* item rows +* topic inputs +* signatures +* quotation price data +* items_table + +However static label inputs are missing from `templateInput`. + +Failing fields: + +```text +templateInput.tel_label +templateInput.email_label +templateInput.att_label +templateInput.project_label +templateInput.site_label +``` + +Expected values: + +```text +tel_label = Tel +email_label = Email +att_label = Att +project_label = Project +site_label = Location +``` + +--- + +# Root Cause Hypothesis + +The refactored section-based runtime likely preserved business data and dynamic section inputs, but dropped legacy/default static inputs during template input assembly. + +The missing values still exist in source document data: + +```text +documentData.pdfme.labels.tel +documentData.pdfme.labels.email +documentData.pdfme.labels.att +documentData.pdfme.labels.project +documentData.pdfme.labels.location +``` + +but they are not merged into final `templateInput`. + +This indicates a regression in one of: + +* mapping resolver +* static input resolver +* render context construction +* section composer input merge +* template assembler input merge + +--- + +# Scope + +Included: + +* Restore static label input compatibility +* Verify final `templateInput` contains all legacy static inputs +* Ensure new runtime preserves all old runtime input keys +* Strengthen audit/regression coverage +* Confirm ALLA and ONVALLA pass runtime audit + +Excluded: + +* Product Item page rendering +* Product Item template creation +* Template version publishing +* Render Policy UI +* New section visibility configuration +* Database changes +* API changes + +--- + +# Required Investigation + +Before fixing, inspect the old and new runtime input construction path. + +Compare: + +## Old Runtime + +* how `tel_label` +* `email_label` +* `att_label` +* `project_label` +* `site_label` + +were added to `templateInput` + +## New Runtime + +* where `templateInput` is initialized +* where mapping output is merged +* where section inputs are merged +* where static/default inputs are merged or lost + +Produce a short root-cause note before applying fixes. + +--- + +# Implementation Requirements + +## 1. Introduce or Restore Static Input Resolution + +Ensure final `templateInput` always includes legacy static inputs. + +At minimum: + +```ts +{ + tel_label: 'Tel', + email_label: 'Email', + att_label: 'Att', + project_label: 'Project', + site_label: 'Location' +} +``` + +Preferred source: + +```text +documentData.pdfme.labels +``` + +Fallback source: + +```text +hardcoded legacy defaults +``` + +The resolver must not duplicate dynamic mapping logic. + +--- + +## 2. Recommended Component + +Add or restore a dedicated component: + +```text +StaticInputResolver +``` + +Responsibilities: + +* resolve static labels +* resolve legacy fixed inputs +* normalize alias fields +* provide fallback defaults +* return template input patch + +It should run before final template assembly. + +Suggested pipeline: + +```text +Document Data + -> Template Resolver + -> Mapping Resolver + -> Compatibility Adapter + -> Page Resolver + -> Render Policy Resolver + -> Static Input Resolver + -> Section Composer + -> Template Assembler + -> PDF Generator +``` + +--- + +## 3. Preserve Legacy Input Contract + +The final `templateInput` must preserve all keys expected by legacy templates. + +Minimum required keys: + +```text +customer_name +customer_addr +customer_tel +customer_email +customer_att +project_name +site_location +quotation_date +quotation_date_data +quotation_code +quotation_code_data +quotation_price +quotation_price_data +currency +exclusion_data +app1 +app1_position +app2 +app2_position +app3 +app3_position +tel_label +email_label +att_label +project_label +site_label +topic_* +item_topic_* +items_table +``` + +--- + +## 4. Template Assembler Merge Rule + +The assembler must merge template inputs in deterministic order. + +Recommended order: + +```text +base mapped input +static input patch +section input patches +diagnostic/debug input patch if any +``` + +Later patches may override earlier patches only when explicitly intended. + +No accidental null overwrite is allowed. + +--- + +## 5. Null-safe Merge + +If a patch contains `null` or `undefined`, it must not overwrite a populated existing value unless explicitly allowed. + +Example rule: + +```text +Do not overwrite "Tel" with null. +Do not overwrite populated static labels with empty values. +``` + +--- + +# Regression Requirements + +Run audit for at least: + +## ALLA + +* approved quotation fixture +* quotation with topics +* quotation with items + +## ONVALLA + +* active quotation fixture +* quotation with topics +* quotation with items + +Verify: + +```text +overallStatus = PASS +staticLabelsStatus = PASS +findingCount = 0 FAIL +``` + +--- + +# Verification Matrix + +## Runtime Payload + +Confirm final `templateInput` contains: + +```text +tel_label = Tel +email_label = Email +att_label = Att +project_label = Project +site_label = Location +``` + +## Existing Dynamic Inputs + +Confirm no regression for: + +```text +quotation_price_data +exclusion_data +topic_* +item_topic_* +items_table +app1 +app1_position +app2 +app2_position +app3 +app3_position +``` + +## Routes + +Verify unchanged behavior for: + +```text +GET /api/crm/quotations/[id]/document-preview +GET /api/crm/quotations/[id]/pdf-preview +GET /api/crm/quotations/[id]/pdf-download +GET /api/crm/quotations/[id]/approved-pdf +``` + +--- + +# Testing Requirements + +Run: + +```bash +npm exec tsc --noEmit +npm run build +npm run audit:pdf +``` + +If project has targeted PDF audit commands, run those as well. + +--- + +# Acceptance Criteria + +* Static label regression is fixed. +* `overallStatus = PASS`. +* `staticLabelsStatus = PASS`. +* No failing runtime audit findings remain. +* Legacy templates render as before. +* ALLA and ONVALLA remain compatible. +* Section-based runtime architecture from P.4.3 remains intact. +* No Product Item feature implementation is introduced. +* Runtime is safe to proceed to Task P.4.4. + +--- + +# Out of Scope + +The following belongs to later tasks: + +## Task P.4.4 + +* Product Item Engine +* Product Item pagination +* Product Item page rendering + +## Task P.4.5 + +* New Product Item template versions +* New template JSON files +* Template seed/reseed support + +## Task P.4.6 + +* CRM Template integration and activation flow + +## Task P.4.7 + +* User-selectable render options +* Optional section UI +* Organization/customer render preferences + +--- + +# Final Success Condition + +Task P.4.3.1 is complete only when the refactored section-based runtime produces a passing runtime audit with zero legacy input regressions. + +After this task passes, continue to Task P.4.4. diff --git a/scripts/pdf-audit-utils.ts b/scripts/pdf-audit-utils.ts index 771a4bf..a6ed6f7 100644 --- a/scripts/pdf-audit-utils.ts +++ b/scripts/pdf-audit-utils.ts @@ -3,6 +3,7 @@ import { mkdir, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { createHash } from 'node:crypto'; import postgres from 'postgres'; +import { resolveQuotationStaticTemplateInputs } from '../src/features/crm/quotations/document/server/static-input-resolver.ts'; export const AUDIT_QUOTATION_CODE = 'QT-H5-AUDIT'; export const AUDIT_ARTIFACTS_DIR = path.resolve(process.cwd(), 'artifacts', 'pdf-audit'); @@ -1428,6 +1429,11 @@ export async function buildAuditPayload(sql: SqlClient): Promise { const templateInput = { ...mapDocumentDataToTemplateInput(documentData, mappings, columns), + ...resolveQuotationStaticTemplateInputs( + documentData as unknown as Parameters< + typeof resolveQuotationStaticTemplateInputs + >[0], + ), ...topicInputs }; const normalizedTemplateInput = normalizeTemplateInputAliases( diff --git a/src/features/crm/quotations/document/server/quotation-pdf-runtime.test.ts b/src/features/crm/quotations/document/server/quotation-pdf-runtime.test.ts index e697b4f..a30beb7 100644 --- a/src/features/crm/quotations/document/server/quotation-pdf-runtime.test.ts +++ b/src/features/crm/quotations/document/server/quotation-pdf-runtime.test.ts @@ -5,6 +5,7 @@ import type { ResolvedDocumentTemplate } from '@/features/foundation/document-te import type { QuotationDocumentData } from '../types'; import { buildQuotationPdfRuntime } from './quotation-pdf-runtime'; import { resolveTemplatePages } from './page-resolver'; +import { assembleTemplate } from './template-assembler'; import type { ResolvedTemplate } from './runtime-contracts'; import { adaptTemplateForSectionRuntime } from './template-compatibility-adapter'; @@ -238,3 +239,54 @@ test('runtime keeps signature block on the last generated topic page', async () assert.equal(fieldNames.has('app1'), false); } }); + +test('runtime restores legacy static labels into final template input', async () => { + const schema = createLegacyTemplate(); + const template = createResolvedDocumentTemplate(schema); + const runtime = await buildQuotationPdfRuntime({ + documentData: createDocumentData(1), + template, + mappings: [], + templateInput: { + customer_name: 'Customer', + }, + }); + + assert.equal(runtime.assembled.templateInput.tel_label, 'Tel'); + assert.equal(runtime.assembled.templateInput.email_label, 'Email'); + assert.equal(runtime.assembled.templateInput.att_label, 'Att'); + assert.equal(runtime.assembled.templateInput.project_label, 'Project'); + assert.equal(runtime.assembled.templateInput.site_label, 'Location'); +}); + +test('template assembler does not overwrite populated values with nullish or blank patches', () => { + const assembled = assembleTemplate({ + template: createLegacyTemplate(), + templateInput: { + tel_label: 'Tel', + customer_name: 'Customer', + }, + staticInputPatch: { + tel_label: '', + email_label: 'Email', + }, + sections: [ + { + role: 'topics', + enabled: true, + rendered: false, + anchorPageIndex: 1, + pages: [], + templateInputPatch: { + tel_label: undefined, + project_label: 'Project', + }, + issues: [], + }, + ], + }); + + assert.equal(assembled.templateInput.tel_label, 'Tel'); + assert.equal(assembled.templateInput.email_label, 'Email'); + assert.equal(assembled.templateInput.project_label, 'Project'); +}); diff --git a/src/features/crm/quotations/document/server/quotation-pdf-runtime.ts b/src/features/crm/quotations/document/server/quotation-pdf-runtime.ts index 41f2410..c2e3014 100644 --- a/src/features/crm/quotations/document/server/quotation-pdf-runtime.ts +++ b/src/features/crm/quotations/document/server/quotation-pdf-runtime.ts @@ -7,6 +7,7 @@ import type { QuotationDocumentData } from '../types'; import { buildPdfTopicPages } from './pdf-topic-engine'; import { resolveTemplatePages } from './page-resolver'; import { createRenderContext, resolveRenderPolicies } from './render-context'; +import { resolveQuotationStaticTemplateInputs } from './static-input-resolver'; import type { BuiltSection, QuotationPdfRuntimeResult, @@ -129,6 +130,7 @@ export async function buildQuotationPdfRuntime(args: { const compatibleTemplate = adaptTemplateForSectionRuntime(resolvedTemplate); const pages = resolveTemplatePages(compatibleTemplate); const policies = resolveRenderPolicies(); + const staticInputPatch = resolveQuotationStaticTemplateInputs(args.documentData); const context = createRenderContext({ documentData: args.documentData, template: resolvedTemplate, @@ -141,6 +143,7 @@ export async function buildQuotationPdfRuntime(args: { const assembled = assembleTemplate({ template: resolvedTemplate.schema, templateInput: args.templateInput, + staticInputPatch, sections: composed.sections, }); const issues = [...pages.issues, ...composed.issues, ...assembled.issues]; diff --git a/src/features/crm/quotations/document/server/static-input-resolver.ts b/src/features/crm/quotations/document/server/static-input-resolver.ts new file mode 100644 index 0000000..b5379c3 --- /dev/null +++ b/src/features/crm/quotations/document/server/static-input-resolver.ts @@ -0,0 +1,50 @@ +import type { QuotationDocumentData } from '../types'; + +const LEGACY_STATIC_LABEL_DEFAULTS = { + tel_label: 'Tel', + email_label: 'Email', + att_label: 'Att', + project_label: 'Project', + site_label: 'Location', +} as const; + +function normalizeStringValue( + value: string | null | undefined, + fallback: string, +): string { + if (typeof value !== 'string') { + return fallback; + } + + const trimmed = value.trim(); + return trimmed.length > 0 ? trimmed : fallback; +} + +export function resolveQuotationStaticTemplateInputs( + documentData: QuotationDocumentData, +): Record { + const labels = documentData.pdfme.labels; + + return { + tel_label: normalizeStringValue( + labels.tel, + LEGACY_STATIC_LABEL_DEFAULTS.tel_label, + ), + email_label: normalizeStringValue( + labels.email, + LEGACY_STATIC_LABEL_DEFAULTS.email_label, + ), + att_label: normalizeStringValue( + labels.att, + LEGACY_STATIC_LABEL_DEFAULTS.att_label, + ), + project_label: normalizeStringValue( + labels.project, + LEGACY_STATIC_LABEL_DEFAULTS.project_label, + ), + site_label: normalizeStringValue( + labels.location, + LEGACY_STATIC_LABEL_DEFAULTS.site_label, + ), + }; +} diff --git a/src/features/crm/quotations/document/server/template-assembler.ts b/src/features/crm/quotations/document/server/template-assembler.ts index 26a3b43..83f7909 100644 --- a/src/features/crm/quotations/document/server/template-assembler.ts +++ b/src/features/crm/quotations/document/server/template-assembler.ts @@ -9,9 +9,29 @@ function cloneTemplate(value: T): T { return JSON.parse(JSON.stringify(value)) as T; } +function mergeTemplateInputPatch( + target: Record, + patch: Record, +): Record { + for (const [key, value] of Object.entries(patch)) { + if (value === null || value === undefined) { + continue; + } + + if (typeof value === 'string' && value.trim().length === 0) { + continue; + } + + target[key] = value; + } + + return target; +} + export function assembleTemplate(args: { template: Template; templateInput: Record; + staticInputPatch?: Record; sections: BuiltSection[]; }): AssembledTemplate { const baseTemplate = cloneTemplate(args.template); @@ -62,13 +82,15 @@ export function assembleTemplate(args: { baseTemplate.schemas = nextPages; - const templateInput = args.sections.reduce>( - (accumulator, section) => ({ - ...accumulator, - ...section.templateInputPatch, - }), - { ...args.templateInput }, - ); + const templateInput = mergeTemplateInputPatch({}, args.templateInput); + + if (args.staticInputPatch) { + mergeTemplateInputPatch(templateInput, args.staticInputPatch); + } + + for (const section of args.sections) { + mergeTemplateInputPatch(templateInput, section.templateInputPatch); + } return { template: baseTemplate, diff --git a/storage/organizations/d5c46139-9af4-421b-9395-d83620c62f9b/quotations/approved/7dc0630b-e27d-4696-803a-d93f51bb9804-QT2606-1122-approved.pdf b/storage/organizations/d5c46139-9af4-421b-9395-d83620c62f9b/quotations/approved/7dc0630b-e27d-4696-803a-d93f51bb9804-QT2606-1122-approved.pdf new file mode 100644 index 0000000..bac49df Binary files /dev/null and b/storage/organizations/d5c46139-9af4-421b-9395-d83620c62f9b/quotations/approved/7dc0630b-e27d-4696-803a-d93f51bb9804-QT2606-1122-approved.pdf differ