task-4.3.1
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
|
||||
@@ -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": []
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
413
plans/task-p.4.3.1.md
Normal file
413
plans/task-p.4.3.1.md
Normal file
@@ -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.
|
||||
@@ -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<AuditPayload> {
|
||||
|
||||
const templateInput = {
|
||||
...mapDocumentDataToTemplateInput(documentData, mappings, columns),
|
||||
...resolveQuotationStaticTemplateInputs(
|
||||
documentData as unknown as Parameters<
|
||||
typeof resolveQuotationStaticTemplateInputs
|
||||
>[0],
|
||||
),
|
||||
...topicInputs
|
||||
};
|
||||
const normalizedTemplateInput = normalizeTemplateInputAliases(
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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<string, unknown> {
|
||||
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,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -9,9 +9,29 @@ function cloneTemplate<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
|
||||
function mergeTemplateInputPatch(
|
||||
target: Record<string, unknown>,
|
||||
patch: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
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<string, unknown>;
|
||||
staticInputPatch?: Record<string, unknown>;
|
||||
sections: BuiltSection[];
|
||||
}): AssembledTemplate {
|
||||
const baseTemplate = cloneTemplate(args.template);
|
||||
@@ -62,13 +82,15 @@ export function assembleTemplate(args: {
|
||||
|
||||
baseTemplate.schemas = nextPages;
|
||||
|
||||
const templateInput = args.sections.reduce<Record<string, unknown>>(
|
||||
(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,
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user