task-h.5.3

This commit is contained in:
phaichayon
2026-06-19 17:15:28 +07:00
parent fd01ebf7c7
commit 51d67ef7c2
35 changed files with 4301 additions and 633 deletions

398
plans/task-h.5.2.md Normal file
View File

@@ -0,0 +1,398 @@
# Task H.5.2: PDFME Price Table Input Normalization
## Goal
แก้ปัญหา PDFME ไม่แสดงข้อความในตารางราคา
Current template field:
```json
{
"name": "quotation_price_data",
"type": "table",
"content": "[[\"Price (Exclude VAT)\",\"{quotation_price}\",\" \",\"{currency}\"]]"
}
```
Expected output:
```txt
Price (Exclude VAT) ฿500,000.00 THB
```
Root cause:
PDFME table field นี้ควรได้รับ input ตาม schema name:
```txt
quotation_price_data
```
ในรูปแบบ:
```ts
[
["Price (Exclude VAT)", "฿500,000.00", " ", "THB"]
]
```
ไม่ควร rely กับการ map cell ย่อย `{quotation_price}` และ `{currency}` ภายใน table content
---
# Must Read
```txt
docs/implementation/task-h5-pdf-mapping-audit-visual-parity.md
docs/business/pdf-mapping-registry.md
docs/business/pdf-placeholder-registry.md
docs/implementation/task-h51-pdf-integrity-audit.md
src/features/crm/quotations/document/server/pdfme-transforms.ts
src/features/crm/quotations/document/server/service.ts
src/features/foundation/document-template/server/service.ts
src/features/foundation/pdf-generator/server/service.ts
src/db/seeds/foundation.seed.ts
scripts/audit-pdf-runtime-payload.ts
scripts/audit-payload-parity.ts
scripts/generate-pdf-integrity-report.ts
```
---
# Scope H5.2.1: Add PDFME Price Table Field
Update quotation document builder:
```txt
src/features/crm/quotations/document/server/service.ts
```
Add to `documentData.pdfme`:
```ts
quotation_price_data: string[][];
```
Expected shape:
```ts
[
[
"Price (Exclude VAT)",
formatPdfCurrency(quotation.subtotal, quotation.currency),
" ",
quotation.currency || "THB"
]
]
```
Rules:
* first column must be `"Price (Exclude VAT)"`
* second column must be formatted currency string such as `฿500,000.00`
* third column is `" "` to match template spacing
* fourth column must be currency code such as `THB`
* never return `undefined`
* if price is missing, use `"-"`
* if currency is missing, fallback to `"THB"`
---
# Scope H5.2.2: Keep Scalar Fields For Compatibility
Keep existing fields:
```ts
pdfme.quotation_price
pdfme.currency
```
But table rendering must use:
```txt
pdfme.quotation_price_data
```
Do not remove existing fields because other templates or audits may still reference them.
---
# Scope H5.2.3: Template Mapping Seed Update
Update:
```txt
src/db/seeds/foundation.seed.ts
```
Ensure mapping exists:
```txt
quotation_price_data -> pdfme.quotation_price_data
```
With:
```txt
dataType = table
defaultValue = [["Price (Exclude VAT)", "-", " ", "THB"]]
sortOrder appropriate
```
Retain mappings if used elsewhere:
```txt
quotation_price -> pdfme.quotation_price
currency -> pdfme.currency
```
But do not depend on them for `quotation_price_data`.
Rules:
* idempotent seed
* update existing incorrect mapping
* no duplicate mapping
* no hardcoded organizationId
* apply to active ALLA / ONVALLA template versions
---
# Scope H5.2.4: Template Mapping Resolver
Update:
```txt
src/features/foundation/document-template/server/service.ts
```
Ensure `mapDocumentDataToTemplateInput()` preserves table input when source value is already:
```ts
string[][]
```
For mapping:
```txt
placeholderKey = quotation_price_data
dataType = table
sourcePath = pdfme.quotation_price_data
```
Expected template input:
```ts
{
quotation_price_data: [
["Price (Exclude VAT)", "฿500,000.00", " ", "THB"]
]
}
```
Do not stringify this table.
Do not flatten it.
Do not attempt nested placeholder interpolation for this field.
---
# Scope H5.2.5: Template Placeholder Audit Update
Update:
```txt
scripts/audit-pdf-template-inventory.ts
scripts/audit-pdf-mapping-coverage.ts
scripts/audit-pdf-runtime-payload.ts
scripts/audit-payload-parity.ts
scripts/generate-pdf-integrity-report.ts
```
Audit must recognize:
```txt
quotation_price_data
```
as the table field to verify.
Expected checks:
```txt
quotation_price_data exists in template schema
quotation_price_data mapping exists
quotation_price_data sourcePath = pdfme.quotation_price_data
quotation_price_data resolved type = string[][]
quotation_price_data row count >= 1
quotation_price_data[0][1] contains formatted currency
quotation_price_data[0][3] contains currency code
```
---
# Scope H5.2.6: Approved Snapshot Parity Fix
Approved snapshot must store:
```ts
templateInput.quotation_price_data
```
And it must match current runtime payload unless quotation data changed after approval.
For fixture `QT-H5-AUDIT`, after regenerating approved PDF:
Expected:
```txt
Approved Snapshot Parity = PASS
inconsistentKeys = []
```
Rules:
* if approved snapshot exists with old data, regenerate artifact only in fixture/dev environment
* do not silently overwrite locked production artifacts
* production regeneration must require explicit void/regenerate flow later
---
# Scope H5.2.7: PDFME Input Compatibility
Because current template content still has nested tokens:
```txt
{quotation_price}
{currency}
```
inside:
```txt
quotation_price_data
```
Runtime should still prioritize input key:
```txt
quotation_price_data
```
over nested tokens.
Optional safe improvement:
If needed, update the active template schema content from:
```json
"[[\"Price (Exclude VAT)\",\"{quotation_price}\",\" \",\"{currency}\"]]"
```
to:
```json
"[[\"Price (Exclude VAT)\",\"-\",\" \",\"THB\"]]"
```
But only if PDFME requires removing nested placeholders for table input to render correctly.
Do not make layout changes.
---
# Scope H5.2.8: Verification
Run:
```txt
npm run audit:pdf
```
Expected:
```txt
Template Version Integrity: PASS
Placeholder Integrity: PASS
Payload Parity: PASS
Topic Runtime: PASS
Approved Snapshot Parity: PASS
Overall Status: PASS
```
Also verify actual preview / PDF visually shows:
```txt
Price (Exclude VAT) ฿500,000.00 THB
```
---
# Scope H5.2.9: Documentation
Update:
```txt
docs/business/pdf-mapping-registry.md
docs/business/pdf-placeholder-registry.md
docs/implementation/task-h51-pdf-integrity-audit.md
docs/implementation/technical-debt.md
```
Document:
```txt
quotation_price_data is the canonical PDFME table field for price display.
quotation_price and currency remain scalar compatibility fields.
Do not use nested table placeholders for canonical price table rendering.
```
---
# Explicit Non-Scope
Do NOT implement:
```txt
signature image
draw signature
new PDF template design
visual designer
approved artifact void/regenerate UI
report center
currency exchange calculation
multi-currency tax logic
```
---
# Output
After completion, summarize:
1. Files Modified
2. PDFME Price Table Field Added
3. Mapping Seed Updated
4. Resolver Behavior Confirmed
5. Audit Updated
6. Approved Snapshot Parity Result
7. Remaining Risks
---
# Definition of Done
Task H.5.2 passes when:
* `pdfme.quotation_price_data` exists
* `quotation_price_data` maps to `pdfme.quotation_price_data`
* `quotation_price_data` is `string[][]`
* PDF renders `Price (Exclude VAT) ฿500,000.00 THB`
* scalar `quotation_price` and `currency` remain available
* approved snapshot stores `templateInput.quotation_price_data`
* `npm run audit:pdf` returns Overall Status PASS
* `inconsistentKeys` is empty

505
plans/task-h.5.3.md Normal file
View File

@@ -0,0 +1,505 @@
# 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