16 KiB
Task P.4.1 Verification Report
Scope
This report completes the runtime verification requested by task-p.4.1.md.
No production template files were modified. No new rendering pipeline was created. No implementation for Task P.4 has started yet.
1. Verified Runtime Findings
1.1 Current quotation PDF runtime chain
The current quotation document routes do not use separate template pipelines.
Verified route chain:
document-datacallsbuildQuotationDocumentDatadocument-previewcallsgetQuotationDocumentPreviewDatapdf-previewcallsgenerateQuotationPreviewPdfpdf-downloadcallsgenerateQuotationPdfapproved-pdfreads stored artifact first and falls back to generation path when needed
Verified shared pipeline:
generateQuotationPdf()callsgetQuotationDocumentPreviewData()generateQuotationPreviewPdf()simply delegates togenerateQuotationPdf()generateApprovedQuotationPdf()also callsgetQuotationDocumentPreviewData()
Verified preview builder chain:
getQuotationDocumentPreviewData()buildsdocumentData- resolves active template and active version
- resolves version mappings
- maps
documentDatatotemplateInput - applies topic page expansion
Reference:
1.2 Same active template/version/mapping pipeline
Confirmed: document-preview, pdf-preview, pdf-download, and approved PDF generation all depend on the same active template/version/mapping pipeline.
Shared resolution calls:
resolveTemplateForDocument()resolveTemplateMappings()mapDocumentDataToTemplateInput()
Conclusion:
- there is one active runtime pipeline for quotation PDF generation
- there is no separate preview-only template source
- there is no separate download-only template source
- approved PDF generation reuses the same active version before persistence
2. Active Template Runtime Source Verification
2.1 Database active versions
Verified against live database:
[
{
"organizationName": "ALLA",
"templateName": "ALLA Quotation Standard",
"version": "1.0",
"filePath": "src/pdfme_template/ALLA_template_pdfme_fainal3.json",
"mappingCount": "20"
},
{
"organizationName": "ONVALLA",
"templateName": "ONVALLA Quotation Standard",
"version": "1.0",
"filePath": "src/pdfme_template/ONVALLA_template_pdfme_fainal3.json",
"mappingCount": "20"
}
]
Interpretation:
- runtime selects active version rows from DB
- each active version still keeps
filePathpointing to the original JSON file source - mapping count is currently
20for both active versions
2.2 DB schema vs file source
Verified that the active version schema_json in DB matches the JSON file on disk exactly for both organizations.
Evidence:
{
"organization": "ALLA",
"filePath": "src/pdfme_template/ALLA_template_pdfme_fainal3.json",
"matches": true
}
{
"organization": "ONVALLA",
"filePath": "src/pdfme_template/ONVALLA_template_pdfme_fainal3.json",
"matches": true
}
Conclusion:
- runtime renders from
schema_jsonstored in DB - the current DB value is seeded from the JSON file and currently matches it
- changing only the JSON file will not change runtime behavior unless DB version rows are reseeded or updated
2.3 Seed/runtime source conclusion
Runtime uses both, but with different roles:
- DB active template/version/mapping is the actual runtime source
- JSON file is the seed/reseed source of truth for version payload creation
This means P.4 must update:
- new template JSON files
- seed or reseed logic that publishes those JSON files into DB versions
Relevant current source selectors:
3. Current Template Page Structure
3.1 Active page count
Verified from active DB runtime schema:
ALLAactive template version has2pagesONVALLAactive template version has2pages
3.2 Schema names per page
Verified runtime schema dump for ALLA:
PAGE 1 FIELDS:
company1, company2, company_addr1, company_addr2, company_tel, company_email,
company_tax, line, customer_name, customer_addr, customer_tel, customer_email,
dear_sirs, quotation_price_data, quotation_date_labe, quotation_date_data,
quotation_code_lable, quotation_code_data, customer_att, project_name,
site_location, colon, colon copy, colon_tel, colon_fax, colon_email,
colon_att, colon_project, colon_site, tel_label, email_label,
att_label, project_label, site_label
PAGE 2 FIELDS:
topic, data_topic, Please_do_not, yours_faithfuly,
app1, app1_position, app2, app2_position, app3, app3_position
Verified runtime schema dump for ONVALLA:
PAGE 1 FIELDS:
company, company_addr1, company_addr2, company_tel, company_email,
company_tax, line, customer_name, customer_addr, customer_tel, customer_email,
dear_sirs, quotation_price_data, quotation_date_labe, quotation_date_data,
quotation_code_lable, quotation_code_data, customer_att, project_name,
site_location, colon, colon copy, colon_tel, colon_fax, colon_email,
colon_att, colon_project, colon_site, tel_label, email_label,
att_label, project_label, site_label
PAGE 2 FIELDS:
topic, data_topic, Please_do_not, yours_faithfuly,
app1, app1_position, app2, app2_position, app3, app3_position
3.3 Topic/signature page index
Confirmed:
- topic/signature page is currently page index
1in zero-based runtime terms - this matches the hardcoded topic engine default
Reference:
This is the key verification that explains why inserting a new page in the middle will break the current engine if no runtime change is made first.
4. PDFMe Table Capability Verification
4.1 Verification method
Temporary verification was performed using:
@pdfme/generator@pdfme/schemastable plugin@pdfme/pdf-libfor page counting
No production template was modified.
A temporary verification PDF was generated at:
4.2 Runtime source-code evidence for repeat header
The installed table plugin contains explicit repeat-header logic:
node_modules/@pdfme/schemas/dist/dynamicTemplate-CXBCt5wk.js:488
schema.showHead = schema.showHead === false ? false : !schema.__isSplit || schema.repeatHead === true;
node_modules/@pdfme/schemas/dist/dynamicTemplate-CXBCt5wk.js:704
if (!(schema.repeatHead && isBlankPdf(args.basePdf) && headerHeight > 0)) return baseHeights;
Interpretation:
repeatHeadis a real runtime behavior in the installed table plugin- repeated header behavior is only applied when using blank/object-style
basePdf - this matches the P.4 requirement and also matches the project’s current template style
4.3 Temporary generation results
Verified generation results:
{"label":"rows_5_short","rowCount":5,"pageCount":1,"pdfBytes":8869}
{"label":"rows_30_short","rowCount":30,"pageCount":2,"pdfBytes":21737}
{"label":"rows_100_short","rowCount":100,"pageCount":4,"pdfBytes":55297}
{"label":"rows_30_long","rowCount":30,"pageCount":3,"pdfBytes":26739}
Verified conclusions:
5 rowsfits on1page30 rowsbreaks to2pages100 rowsbreaks to4pages30long-description rows consume3pages instead of2
4.4 Table behavior conclusions
Confirmed:
- auto page break works
- multi-page table generation works
- row height behavior changes pagination when descriptions become longer
- repeat header is implemented in the installed runtime and tied to
repeatHead: true
Important nuance:
- visual image-based confirmation was not performed because this environment does not have PDF raster/text inspection tools installed
- however the runtime code path plus successful multi-page generation confirms the feature support exists in the actual installed library
5. Product Item Mapping Verification
5.1 Actual source fields found
Verified live DB columns for crm_quotation_items:
id
organization_id
quotation_id
item_number
product_type
description
quantity
unit
unit_price
discount
discount_type
tax_rate
total_price
notes
sort_order
created_at
updated_at
deleted_at
5.2 Actual document runtime fields exposed
Verified quotation document runtime exposes:
itemNumberproductTypeCodeproductTypeLabeldescriptionquantityunitCodeunitLabelunitPricediscountdiscountTypeCodetaxRatetotalPricenotes
Reference:
- src/features/crm/quotations/document/server/service.ts
- src/features/crm/quotations/document/types.ts
5.3 Actual mapping coverage for items_table
Verified active mapping rows already include items_table for both organizations.
Current columns:
Item→itemNumberDescription→descriptionQty→quantityUnit→unitLabelUnit Price→unitPriceTotal→totalPrice
5.4 Gap analysis against Task P.4.1 required fields
| Required field | Verified source | Status |
|---|---|---|
| product code | no dedicated field found | gap |
| description | description |
available |
| specification | no dedicated field found | gap |
| quantity | quantity |
available |
| unit | unit / unitLabel |
available |
| unit price | unitPrice |
available |
| discount | discount |
available |
| amount | totalPrice |
available |
| remark | closest existing field is notes |
gap / mapping decision needed |
Conclusion:
product codedoes not exist as a standalone quotation item field in the current modelspecificationdoes not exist as a standalone quotation item field in the current modelremarklikely maps tonotes, but this should be treated as a business decision, not assumed silently
6. Final Implementation Strategy
Recommended implementation order:
- generalize topic/signature runtime targeting so it no longer depends on hardcoded page index
1 - create new template JSON files
ALLA_template_pdfme_product_v1.jsonONVALLA_template_pdfme_product_v1.json
- place the Product Item page between customer detail and topic/signature pages
- configure the Product Item page with official PDFMe
tableschema using:showHead: truerepeatHead: true- blank/object-style
basePdf
- keep old versions untouched and inactive/active-switchable
- update reseed/audit source selectors so the new JSON files can be published into DB
- verify runtime behavior for both old and new versions through the existing routes
Important implementation constraint:
- because runtime currently renders from DB
schema_json, adding only new JSON files is not sufficient - reseed or explicit version creation in template admin is required for runtime adoption
7. Rollback Plan
Recommended rollback:
- do not delete the current active
1.0versions - publish the new Product Item design as a separate version only
- if regression occurs, deactivate the new version
- reactivate the current
1.0version in/dashboard/crm/settings/templates - if needed, revert reseed script changes independently from runtime code changes
Why rollback is low-risk:
- versioned template storage already exists
- runtime resolves active version dynamically
- current production version remains intact
8. Regression Test Matrix
8.1 Runtime routes
Verify for both ALLA and ONVALLA:
GET /api/crm/quotations/[id]/document-dataGET /api/crm/quotations/[id]/document-previewGET /api/crm/quotations/[id]/pdf-previewGET /api/crm/quotations/[id]/pdf-download- approved PDF generation flow
- approved PDF retrieval flow after artifact exists
8.2 Template versions
Verify both:
- old version
1.0 - new product-item version
8.3 Row-volume cases
Verify with quotation items around:
- 5 rows
- 30 rows
- 100 rows
- long wrapped descriptions
8.4 Output expectations
Verify:
- old template still renders exactly as before
- new template inserts Product Item page between page 1 and topic/signature page
- topic/signature block still lands on the correct page
- repeated table header appears when multiple product pages exist
- long descriptions affect row height without layout corruption
- preview and download use the same selected version
- approved PDF persists the expected template version ID
9. Summary
Task P.4.1 verification confirms:
- quotation PDF runtime uses one shared active template/version/mapping pipeline
- current active runtime template is still the 2-page
*_fainal3.jsonstructure - runtime source is DB
schema_json, while JSON files remain the seed/reseed source - official PDFMe table support is sufficient for multi-page product item tables
- current quotation item model is missing dedicated
product codeandspecificationfields - the biggest implementation blocker is still the topic engine’s hardcoded page targeting, not the table plugin itself