pdf task-h
This commit is contained in:
49
docs/adr/0013-pdf-visual-parity-strategy.md
Normal file
49
docs/adr/0013-pdf-visual-parity-strategy.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# ADR 0013: PDF Visual Parity Strategy
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
The quotation PDF flow now includes:
|
||||
|
||||
- production PDFME templates per organization
|
||||
- DB-backed mapping registry
|
||||
- dynamic topic expansion
|
||||
- approval signature resolution
|
||||
- approved snapshot persistence
|
||||
|
||||
What was missing was a repeatable audit strategy proving that template schema, mappings, runtime payload, and approved snapshot content remain synchronized over time.
|
||||
|
||||
## Decision
|
||||
|
||||
- freeze a documented PDF mapping registry in `docs/business/pdf-mapping-registry.md`
|
||||
- keep a human-readable parity checklist in `docs/implementation/pdf-parity-checklist.md`
|
||||
- add an audit suite:
|
||||
- `audit-pdf-template-inventory`
|
||||
- `audit-pdf-mapping-coverage`
|
||||
- `audit-pdf-runtime-payload`
|
||||
- `generate-pdf-audit-report`
|
||||
- expose the suite through `npm run audit:pdf`
|
||||
- prefer the stable fixture quotation code `QT-H5-AUDIT` for repeatable audits when available
|
||||
- compare preview runtime content against approved snapshot content for business-field consistency
|
||||
|
||||
## Visual Parity Rules
|
||||
|
||||
- allowed differences:
|
||||
- watermark state
|
||||
- generated timestamp
|
||||
- artifact metadata
|
||||
- disallowed differences:
|
||||
- customer identity
|
||||
- quotation commercial values
|
||||
- topic content
|
||||
- signature identity
|
||||
- mapped table content
|
||||
|
||||
## Consequences
|
||||
|
||||
- regressions in template coverage become detectable without opening every PDF manually
|
||||
- mapping ownership is explicit and reviewable
|
||||
- future PDF changes must update both runtime logic and the registry/checklist when business content changes
|
||||
34
docs/business/pdf-mapping-registry.md
Normal file
34
docs/business/pdf-mapping-registry.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# PDF Mapping Registry
|
||||
|
||||
This document is the source of truth for current quotation PDF field ownership.
|
||||
|
||||
| PDF Field | Source Path | Transform | Example Value |
|
||||
| --- | --- | --- | --- |
|
||||
| `customer_name` | `customer.name` | direct | `ALLA Demo Customer` |
|
||||
| `customer_addr` | `customer.address` | direct multiline | `99/9 Rama IX Road, Bangkok 10310` |
|
||||
| `customer_tel` | `customer.phone` | direct | `02-000-1000` |
|
||||
| `customer_email` | `customer.email` | direct | `demo-customer@local.test` |
|
||||
| `customer_att` | `quotation.attention` | direct | `Task H.1 Contact` |
|
||||
| `project_name` | `quotation.projectName` | direct | `Task H.5 Audit Fixture` |
|
||||
| `site_location` | `quotation.projectLocation` | direct multiline | `Bangkok HQ` |
|
||||
| `quotation_date` | `pdfme.quotation_date` | `formatPdfDate()` | `16 Jun 2026` |
|
||||
| `quotation_code` | `quotation.code` | direct | `QT-H5-AUDIT` |
|
||||
| `quotation_price` | `pdfme.quotation_price` | `formatPdfCurrency()` | `THB 128,400.00` |
|
||||
| `currency` | `quotation.currency` | direct | `THB` |
|
||||
| `exclusion_data` | `pdfme.exclusion_data` | `formatTopicItems()` | `[["Civil work by customer"]]` |
|
||||
| `items_table` | `items` | `normalizePdfmeTable()` with table columns | item rows |
|
||||
| `app1` | `signatures.preparedBy.name` | direct | `Task H.1 Admin` |
|
||||
| `app1_position` | `signatures.preparedBy.position` | position resolver | `Sales Manager` |
|
||||
| `app2` | `signatures.approvedBy.name` | direct | `Task H.1 Admin` |
|
||||
| `app2_position` | `signatures.approvedBy.position` | position resolver | `Sales Manager` |
|
||||
| `app3` | `signatures.authorizedBy.name` | direct | `Task H.1 Admin` |
|
||||
| `app3_position` | `signatures.authorizedBy.position` | position resolver | `Sales Manager` |
|
||||
| `topic` | dynamic topic engine | runtime schema clone | topic label table |
|
||||
| `data_topic` | dynamic topic engine | runtime schema clone | topic item table |
|
||||
| `topic_*` | `documentData.topics.all[*].topicType` | runtime generated | `Scope` |
|
||||
| `item_topic_*` | `documentData.topics.all[*].items` | `formatTopicItems()` | topic rows |
|
||||
|
||||
## Alias Notes
|
||||
|
||||
- current PDFME schema still uses field containers named `quotation_date_data` and `quotation_code_data`
|
||||
- runtime normalization now publishes both the canonical placeholder keys (`quotation_date`, `quotation_code`) and the container keys so preview/audit/render stay aligned during the freeze window
|
||||
36
docs/business/pdf-placeholder-registry.md
Normal file
36
docs/business/pdf-placeholder-registry.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# PDF Placeholder Registry
|
||||
|
||||
This registry freezes the placeholder naming contract for quotation PDF templates.
|
||||
|
||||
| Placeholder | Type | Ownership | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `customer_name` | mapped | DB mapping | customer display name |
|
||||
| `customer_addr` | mapped | DB mapping | multiline address |
|
||||
| `customer_tel` | mapped | DB mapping | customer phone |
|
||||
| `customer_email` | mapped | DB mapping | customer email |
|
||||
| `customer_att` | mapped | DB mapping | quotation attention |
|
||||
| `project_name` | mapped | DB mapping | project name |
|
||||
| `site_location` | mapped | DB mapping | project location |
|
||||
| `quotation_date` | mapped | DB mapping | formatted date |
|
||||
| `quotation_code` | mapped | DB mapping | quotation document code |
|
||||
| `quotation_price` | mapped | DB mapping | formatted total amount |
|
||||
| `currency` | mapped | DB mapping | currency code/label |
|
||||
| `exclusion_data` | mapped | DB mapping | exclusion rows as `string[][]` |
|
||||
| `app1` | mapped | DB mapping | prepared by name |
|
||||
| `app1_position` | mapped | DB mapping | prepared by position |
|
||||
| `app2` | mapped | DB mapping | approved by name |
|
||||
| `app2_position` | mapped | DB mapping | approved by position |
|
||||
| `app3` | mapped | DB mapping | authorized by name |
|
||||
| `app3_position` | mapped | DB mapping | authorized by position |
|
||||
| `topic` | reserved | dynamic topic engine | base runtime topic label template |
|
||||
| `data_topic` | reserved | dynamic topic engine | base runtime topic rows template |
|
||||
| `item_topic` | legacy reserved token | dynamic topic engine | allowed in schema token inventory, not in DB mapping rows |
|
||||
| `topic_*` | generated | dynamic topic engine | runtime-only generated keys |
|
||||
| `item_topic_*` | generated | dynamic topic engine | runtime-only generated keys |
|
||||
|
||||
## Naming Rules
|
||||
|
||||
- do not store `topic_*` or `item_topic_*` keys in `crm_document_template_mappings`
|
||||
- keep `topic` and `data_topic` reserved for runtime schema cloning
|
||||
- treat typo-like names such as `quotation_date_labe` and `quotation_code_lable` as static labels, not placeholders
|
||||
- keep canonical business placeholders as `quotation_date` and `quotation_code`; container aliases like `quotation_date_data` and `quotation_code_data` are tolerated only as schema field names during the current freeze window
|
||||
69
docs/implementation/pdf-audit-report.json
Normal file
69
docs/implementation/pdf-audit-report.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"generatedAt": "2026-06-19T07:44:58.283Z",
|
||||
"overallStatus": "WARNING",
|
||||
"quotationCode": "QT-H5-AUDIT",
|
||||
"expectedFixtureCode": "QT-H5-AUDIT",
|
||||
"template": {
|
||||
"templateName": "ALLA Quotation Standard",
|
||||
"version": "1.0",
|
||||
"versionId": "4ac3e91c-8320-4f41-81cd-5068cc9b5768",
|
||||
"fieldCount": 46,
|
||||
"duplicateFields": [],
|
||||
"unknownFields": [
|
||||
"line"
|
||||
]
|
||||
},
|
||||
"metrics": {
|
||||
"topicCount": 5,
|
||||
"itemCount": 3,
|
||||
"partyCount": 3,
|
||||
"dynamicTopicFieldCount": 5,
|
||||
"dynamicTopicItemFieldCount": 5,
|
||||
"mappingCount": 19
|
||||
},
|
||||
"mappingCoverage": {
|
||||
"placeholderTokenCount": 13,
|
||||
"unmappedTokens": [],
|
||||
"orphanMappings": [],
|
||||
"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": 31,
|
||||
"topicInputKeys": 10
|
||||
},
|
||||
"consistency": {
|
||||
"status": "WARNING",
|
||||
"approvedSnapshotAvailable": false,
|
||||
"approvedArtifactReference": null,
|
||||
"inconsistentKeys": []
|
||||
},
|
||||
"integritySummary": {
|
||||
"available": true,
|
||||
"artifactDir": "C:\\Users\\mtpphtaps\\Documents\\gitea\\alla-allaos-fullstack\\artifacts\\pdf-audit"
|
||||
}
|
||||
}
|
||||
30
docs/implementation/pdf-audit-report.md
Normal file
30
docs/implementation/pdf-audit-report.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# PDF Audit Report
|
||||
|
||||
- Generated At: 2026-06-19T07:44:58.283Z
|
||||
- Overall Status: WARNING
|
||||
- Quotation Code: QT-H5-AUDIT
|
||||
- Template: ALLA Quotation Standard v1.0
|
||||
|
||||
## Metrics
|
||||
|
||||
- Topic Count: 5
|
||||
- Item Count: 3
|
||||
- Project Party Count: 3
|
||||
- Mapping Count: 19
|
||||
|
||||
## Mapping Coverage
|
||||
|
||||
- Placeholder Tokens: 13
|
||||
- Unmapped Tokens: None
|
||||
- Orphan Mappings: None
|
||||
|
||||
## Visual / Consistency
|
||||
|
||||
- Approved Snapshot Available: No
|
||||
- Consistency Status: WARNING
|
||||
- Inconsistent Keys: None
|
||||
|
||||
## Integrity Layer
|
||||
|
||||
- Integrity Summary Available: Yes
|
||||
- Artifact Directory: C:\Users\mtpphtaps\Documents\gitea\alla-allaos-fullstack\artifacts\pdf-audit
|
||||
37
docs/implementation/pdf-parity-checklist.md
Normal file
37
docs/implementation/pdf-parity-checklist.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# PDF Parity Checklist
|
||||
|
||||
## Header
|
||||
|
||||
| Area | Expectation | Status |
|
||||
| --- | --- | --- |
|
||||
| Company block | Static company branding and contact text render from template | Partial |
|
||||
| Customer block | `customer_name`, `customer_addr`, `customer_tel`, `customer_email`, `customer_att` render from mapping registry | Match |
|
||||
| Document references | `quotation_code`, `quotation_date`, `project_name`, `site_location` render from live quotation data | Match |
|
||||
|
||||
## Commercial Body
|
||||
|
||||
| Area | Expectation | Status |
|
||||
| --- | --- | --- |
|
||||
| Items | `items_table` columns stay aligned with quotation item rows and totals | Match |
|
||||
| Topics | Dynamic topic engine expands `topic_*` / `item_topic_*` in order | Match |
|
||||
| Exclusion block | `exclusion_data` renders as `string[][]` table payload | Match |
|
||||
| Price summary | `quotation_price` and `currency` use formatted runtime values | Match |
|
||||
|
||||
## Approval / Footer
|
||||
|
||||
| Area | Expectation | Status |
|
||||
| --- | --- | --- |
|
||||
| Signatures | `app1/app2/app3` map to prepared/approved/authorized blocks | Match |
|
||||
| Signature placement | Closing block stays together after dynamic topic pagination | Partial |
|
||||
| Footer copy | Static footer text and closing statement remain template-owned | Match |
|
||||
|
||||
## Visual Parity Rules
|
||||
|
||||
- `Match` means the current PDF runtime has a verified mapping path and auditable payload.
|
||||
- `Partial` means the business data path is correct but final pixel-perfect comparison still needs manual screenshot review against the legacy CRM PDF.
|
||||
- `Missing` should only be used when a field or section has no runtime path.
|
||||
|
||||
## Integrity Freeze
|
||||
|
||||
- `npm run audit:pdf` is the integrity gate for template, placeholder, payload, topic runtime, and snapshot parity.
|
||||
- `QT-H5-AUDIT` is the preferred fixture for repeatable PDF audits.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Task H.5: PDF Mapping Audit & Visual Parity
|
||||
|
||||
## Files Added
|
||||
|
||||
- `scripts/pdf-audit-utils.ts`
|
||||
- `scripts/audit-pdf-template-inventory.ts`
|
||||
- `scripts/audit-pdf-mapping-coverage.ts`
|
||||
- `scripts/audit-pdf-runtime-payload.ts`
|
||||
- `scripts/generate-pdf-audit-report.ts`
|
||||
- `docs/implementation/pdf-parity-checklist.md`
|
||||
- `docs/business/pdf-mapping-registry.md`
|
||||
- `docs/adr/0013-pdf-visual-parity-strategy.md`
|
||||
- `docs/implementation/task-h5-pdf-mapping-audit-visual-parity.md`
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `package.json`
|
||||
- `scripts/seed-task-h1-fixture.js`
|
||||
|
||||
## Audit Coverage
|
||||
|
||||
- template inventory audit scans active template versions, schema field names, table fields, dynamic topic fields, duplicates, missing mappings, and orphan mappings
|
||||
- mapping coverage audit compares placeholder tokens with DB mappings and table-column metadata
|
||||
- runtime payload audit inspects resolved `documentData`, `templateInput`, and `pdfme.topic_inputs`
|
||||
- report generator writes JSON and Markdown summary artifacts for regression review
|
||||
|
||||
## Fixture Strategy
|
||||
|
||||
- `seed-task-h1-fixture.js` now also provisions `QT-H5-AUDIT`
|
||||
- the audit fixture is intended to carry richer topic/item/party coverage than the original H.1 smoke dataset
|
||||
|
||||
## Remaining Risks
|
||||
|
||||
- approved snapshot parity remains a warning when the target quotation has never generated an approved PDF artifact
|
||||
- the audit suite validates business payload parity, not pixel-perfect PDF screenshots
|
||||
- manual review is still needed when template designers move static labels or brand blocks
|
||||
40
docs/implementation/task-h51-pdf-integrity-audit.md
Normal file
40
docs/implementation/task-h51-pdf-integrity-audit.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Task H.5.1: PDF Integrity Audit & Payload Parity
|
||||
|
||||
## Files Added
|
||||
|
||||
- `scripts/audit-template-version-integrity.ts`
|
||||
- `scripts/audit-placeholder-integrity.ts`
|
||||
- `scripts/audit-payload-parity.ts`
|
||||
- `scripts/audit-approved-snapshot-parity.ts`
|
||||
- `scripts/generate-pdf-integrity-report.ts`
|
||||
- `docs/business/pdf-placeholder-registry.md`
|
||||
- `docs/implementation/task-h51-pdf-integrity-audit.md`
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `package.json`
|
||||
- `scripts/generate-pdf-audit-report.ts`
|
||||
- `docs/implementation/pdf-parity-checklist.md`
|
||||
- `docs/business/pdf-mapping-registry.md`
|
||||
- `docs/implementation/technical-debt.md`
|
||||
- `scripts/pdf-audit-utils.ts`
|
||||
|
||||
## Integrity Coverage
|
||||
|
||||
- template-version integrity checks active template uniqueness, active version uniqueness, orphan mappings, and approved template-version references
|
||||
- placeholder integrity checks duplicate names, typo-like names, unmapped placeholders, dead mappings, and reserved dynamic topic names
|
||||
- payload parity computes SHA256 hashes for preview, generation, and computed snapshot payloads
|
||||
- approved snapshot parity compares stored snapshot payloads against computed snapshot payloads when they exist
|
||||
- integrity summary writes machine-readable artifacts into `artifacts/pdf-audit/`
|
||||
|
||||
## Verification Result
|
||||
|
||||
- `npx tsc --noEmit` passed
|
||||
- `npm run audit:pdf` runs the full H.5 + H.5.1 stack
|
||||
- current integrity result is expected to be `WARNING` when the audit fixture has not yet generated a persisted approved snapshot/artifact
|
||||
|
||||
## Remaining Risks
|
||||
|
||||
- stored approved snapshot parity still depends on generating a real approved PDF artifact for `QT-H5-AUDIT`
|
||||
- template field `line` is still treated as a tolerated designer-owned oddity rather than a normalized mapping field
|
||||
- the audit stack validates payload integrity and runtime parity, not screenshot-level pixel parity
|
||||
@@ -212,6 +212,7 @@ Future:
|
||||
|
||||
- keep validating pagination and closing-block placement against production designer expectations for large topic sets
|
||||
- add fixture-backed end-to-end PDF generation verification in CI once a stable runtime path is available
|
||||
- decide whether static template field `line` should remain a tolerated designer-only artifact or be normalized in future audits
|
||||
|
||||
## After Task I
|
||||
|
||||
|
||||
Reference in New Issue
Block a user