# Task H.5.3: PDFME Template Version Remap + Static Label Mapping Fix ## Goal รองรับการแก้ไข PDFME template version ใหม่ทั้งหมด หลังจากมีการลบ/เพิ่ม/เปลี่ยน field ใน template พร้อมแก้ปัญหา static label เช่น: ```txt Tel Email Att Project Location ``` ไม่แสดงใน PDF --- # Background Current issue: Template แสดงค่าฝั่งข้อมูล เช่น: ```txt 02-000-1000 demo-customer@local.test Task H.1 Contact Task H.5 Audit Fixture Bangkok HQ ``` แต่ label ฝั่งซ้ายไม่แสดง: ```txt Tel Email Att Project Location ``` Cause: Field label เช่น: ```txt tel_label email_label att_label project_label site_label ``` ยังไม่มี runtime input / mapping ที่ตรงกับ template version ใหม่ อีกทั้ง template มีการลบ field แล้ว จึงต้อง remap ใหม่ทั้ง version --- # Important Rule Do NOT patch the old template version in-place. Create a new template version. Example: ```txt ALLA Quotation Standard v1.0 → ALLA Quotation Standard v1.1 ``` Approved PDF เดิมต้องยังอ้าง version เดิมได้ --- # Must Read ```txt docs/business/pdf-mapping-registry.md docs/business/pdf-placeholder-registry.md docs/adr/0013-pdf-visual-parity-strategy.md docs/implementation/task-h5-pdf-mapping-audit-visual-parity.md docs/implementation/task-h51-pdf-integrity-audit.md src/features/foundation/document-template/** src/features/foundation/pdf-generator/** src/features/crm/quotations/document/** src/db/seeds/foundation.seed.ts scripts/audit-pdf-template-inventory.ts scripts/audit-pdf-mapping-coverage.ts scripts/audit-pdf-runtime-payload.ts scripts/generate-pdf-integrity-report.ts ``` --- # Scope H5.3.1 Template Version Ingestion Add support to ingest/register new PDFME template JSON as a new version. Required behavior: * read latest ALLA / ONVALLA template JSON * create new `crm_document_template_versions` * mark new version active * deactivate previous active version for same template * do not delete old version * preserve approved snapshots that reference old version Rules: ```txt one active version per template approved snapshots remain immutable ``` --- # Scope H5.3.2 Full Field Inventory From New Template For the new template version, scan all schema names. Classify fields into: ```txt static input field dynamic topic template readonly/static visual field system field unknown field deleted field from previous version new field ``` Special handling: Dynamic topic template fields: ```txt topic data_topic ``` must not become static mappings. Runtime generated fields: ```txt topic_* item_topic_* ``` must not be stored in DB mapping. --- # Scope H5.3.3 Full Mapping Rebuild For New Version For the new template version: 1. create mappings only for fields that exist in the new template 2. do not copy orphan mappings from old version 3. do not map deleted fields 4. use `pdf-placeholder-registry.md` and actual field inventory 5. create missing mappings for valid fields 6. mark unsupported fields as warning, not silent pass Mapping categories: ## Static Label Fields ```txt tel_label -> pdfme.labels.tel email_label -> pdfme.labels.email att_label -> pdfme.labels.att project_label -> pdfme.labels.project site_label -> pdfme.labels.location ``` Expected values: ```txt Tel Email Att Project Location ``` or Thai labels if configured: ```txt โทร อีเมล เรียน โครงการ สถานที่ ``` ## Customer Fields ```txt customer_name -> customer.name customer_addr -> customer.address customer_tel -> customer.phone customer_email -> customer.email customer_att -> quotation.attention ``` ## Project / Quotation Fields ```txt project_name -> quotation.projectName site_location -> quotation.projectLocation quotation_code -> quotation.code quotation_code_data -> quotation.code quotation_date -> pdfme.quotation_date quotation_date_data -> pdfme.quotation_date ``` ## Price Table Canonical: ```txt quotation_price_data -> pdfme.quotation_price_data ``` Keep compatibility if template still has scalar placeholders: ```txt quotation_price -> pdfme.quotation_price currency -> pdfme.currency ``` ## Exclusion ```txt exclusion_data -> pdfme.exclusion_data ``` ## Signature ```txt app1 -> signatures.preparedBy.name app1_position -> signatures.preparedBy.position app2 -> signatures.approvedBy.name app2_position -> signatures.approvedBy.position app3 -> signatures.authorizedBy.name app3_position -> signatures.authorizedBy.position ``` --- # Scope H5.3.4 Runtime Document Data Update Update: ```txt src/features/crm/quotations/document/server/service.ts ``` Ensure `documentData.pdfme.labels` exists: ```ts pdfme: { labels: { tel: "Tel"; email: "Email"; att: "Att"; project: "Project"; location: "Location"; } } ``` Optional Thai mode: ```ts pdfme: { labels: { tel: "โทร"; email: "อีเมล"; att: "เรียน"; project: "โครงการ"; location: "สถานที่"; } } ``` Rules: * never return undefined * label values should be controlled by terminology/config later * for now default to current PDF language * do not hardcode in template only --- # Scope H5.3.5 Resolver Safety Update mapping resolver to: * resolve nested paths such as `pdfme.labels.tel` * fallback to mapping.defaultValue * fallback to `"-"` if still empty * preserve table values as `string[][]` * not stringify table input --- # Scope H5.3.6 Audit Update Update audit scripts to support version remap. Checks: ```txt new version active old version inactive but retained deleted fields removed from mapping new fields mapped or classified label fields resolved price table present dynamic topics still valid signature fields still valid approved snapshot parity still pass for newly generated artifact ``` Special check: ```txt tel_label email_label att_label project_label site_label ``` must be non-empty. --- # Scope H5.3.7 Seed / Migration Strategy Update: ```txt src/db/seeds/foundation.seed.ts ``` or create a dedicated script: ```txt scripts/reseed-pdf-template-version.ts ``` Recommended: Use dedicated script for remap: ```txt npm run seed:pdf-template-version ``` because template version remap is operationally different from foundation seed. Rules: * no hardcoded organizationId * handles every organization * handles ALLA / ONVALLA branch template * idempotent per template version * no duplicate active versions --- # Scope H5.3.8 Approved Snapshot Rule Existing approved artifacts: * keep using old template version * do not regenerate automatically * do not update old snapshot mapping New approved artifacts: * use new active template version * use new mappings * pass parity audit --- # Scope H5.3.9 Verification Run: ```txt npm run seed:pdf-template-version npm run audit:pdf ``` Expected: ```txt Overall Status: PASS Template Version: PASS Placeholder Integrity: PASS Payload Parity: PASS Topic Runtime: PASS Approved Snapshot Parity: PASS Price Table: PASS Static Labels: PASS ``` Visual check: ```txt Tel : 02-000-1000 Email : demo-customer@local.test Att : Task H.1 Contact Project : Task H.5 Audit Fixture Location : Bangkok HQ ``` --- # Scope H5.3.10 Documentation Update: ```txt docs/business/pdf-mapping-registry.md docs/business/pdf-placeholder-registry.md docs/implementation/task-h5-pdf-mapping-audit-visual-parity.md docs/implementation/technical-debt.md ``` Add: ```txt Template version remap process Static label mapping strategy Deleted field handling Dynamic topic reserved fields Approved snapshot immutability rule ``` --- # Explicit Non-Scope Do NOT implement: ```txt visual template designer signature image / draw signature digital signature currency exchange calculation report center permission center ``` --- # Output After completion, summarize: 1. Files Added 2. Files Modified 3. New Template Version Created 4. Deleted Fields Detected 5. New Field Inventory 6. Mappings Rebuilt 7. Static Labels Fixed 8. Audit Result 9. Remaining Risks --- # Definition of Done Task H.5.3 passes when: * new template version is created * old template version is retained * old mappings are not blindly copied * deleted fields do not remain as active mappings * new fields are mapped or classified * `tel_label` renders * `email_label` renders * `att_label` renders * `project_label` renders * `site_label` renders * price table still renders * dynamic topics still render * signatures still render * approved snapshot parity passes for newly generated approved PDF * `npm run audit:pdf` returns Overall Status PASS