458 lines
16 KiB
Markdown
458 lines
16 KiB
Markdown
# Task P.4.1 Verification Report
|
||
|
||
## Scope
|
||
|
||
This report completes the runtime verification requested by [task-p.4.1.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/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-data` calls `buildQuotationDocumentData`
|
||
- [src/app/api/crm/quotations/[id]/document-data/route.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/quotations/[id]/document-data/route.ts:39)
|
||
- `document-preview` calls `getQuotationDocumentPreviewData`
|
||
- [src/app/api/crm/quotations/[id]/document-preview/route.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/quotations/[id]/document-preview/route.ts:39)
|
||
- `pdf-preview` calls `generateQuotationPreviewPdf`
|
||
- [src/app/api/crm/quotations/[id]/pdf-preview/route.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/quotations/[id]/pdf-preview/route.ts:39)
|
||
- `pdf-download` calls `generateQuotationPdf`
|
||
- [src/app/api/crm/quotations/[id]/pdf-download/route.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/quotations/[id]/pdf-download/route.ts:39)
|
||
- `approved-pdf` reads stored artifact first and falls back to generation path when needed
|
||
- [src/app/api/crm/quotations/[id]/approved-pdf/route.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/app/api/crm/quotations/[id]/approved-pdf/route.ts:44)
|
||
|
||
Verified shared pipeline:
|
||
|
||
- `generateQuotationPdf()` calls `getQuotationDocumentPreviewData()`
|
||
- [src/features/foundation/pdf-generator/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/foundation/pdf-generator/server/service.ts:304)
|
||
- `generateQuotationPreviewPdf()` simply delegates to `generateQuotationPdf()`
|
||
- [src/features/foundation/pdf-generator/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/foundation/pdf-generator/server/service.ts:317)
|
||
- `generateApprovedQuotationPdf()` also calls `getQuotationDocumentPreviewData()`
|
||
- [src/features/foundation/pdf-generator/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/foundation/pdf-generator/server/service.ts:326)
|
||
|
||
Verified preview builder chain:
|
||
|
||
- `getQuotationDocumentPreviewData()` builds `documentData`
|
||
- resolves active template and active version
|
||
- resolves version mappings
|
||
- maps `documentData` to `templateInput`
|
||
- applies topic page expansion
|
||
|
||
Reference:
|
||
|
||
- [src/features/crm/quotations/document/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/service.ts:661)
|
||
|
||
### 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()`
|
||
- [src/features/crm/quotations/document/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/service.ts:669)
|
||
- `resolveTemplateMappings()`
|
||
- [src/features/crm/quotations/document/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/service.ts:674)
|
||
- `mapDocumentDataToTemplateInput()`
|
||
- [src/features/crm/quotations/document/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/service.ts:688)
|
||
|
||
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:
|
||
|
||
```json
|
||
[
|
||
{
|
||
"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 `filePath` pointing to the original JSON file source
|
||
- mapping count is currently `20` for 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:
|
||
|
||
```json
|
||
{
|
||
"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_json` stored 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:
|
||
|
||
- [src/db/seeds/foundation.seed.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/db/seeds/foundation.seed.ts:854)
|
||
- [scripts/pdf-audit-utils.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/scripts/pdf-audit-utils.ts:667)
|
||
|
||
---
|
||
|
||
## 3. Current Template Page Structure
|
||
|
||
### 3.1 Active page count
|
||
|
||
Verified from active DB runtime schema:
|
||
|
||
- `ALLA` active template version has `2` pages
|
||
- `ONVALLA` active template version has `2` pages
|
||
|
||
### 3.2 Schema names per page
|
||
|
||
Verified runtime schema dump for `ALLA`:
|
||
|
||
```text
|
||
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`:
|
||
|
||
```text
|
||
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 `1` in zero-based runtime terms
|
||
- this matches the hardcoded topic engine default
|
||
|
||
Reference:
|
||
|
||
- [src/features/crm/quotations/document/server/pdf-topic-engine.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/pdf-topic-engine.ts:14)
|
||
|
||
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/schemas` table plugin
|
||
- `@pdfme/pdf-lib` for page counting
|
||
|
||
No production template was modified.
|
||
|
||
A temporary verification PDF was generated at:
|
||
|
||
- [rows_30_short.pdf](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/artifacts/task-p4-verification/rows_30_short.pdf)
|
||
|
||
### 4.2 Runtime source-code evidence for repeat header
|
||
|
||
The installed table plugin contains explicit repeat-header logic:
|
||
|
||
```text
|
||
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:
|
||
|
||
- `repeatHead` is 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:
|
||
|
||
```json
|
||
{"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 rows` fits on `1` page
|
||
- `30 rows` breaks to `2` pages
|
||
- `100 rows` breaks to `4` pages
|
||
- `30` long-description rows consume `3` pages instead of `2`
|
||
|
||
### 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`:
|
||
|
||
```text
|
||
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:
|
||
|
||
- `itemNumber`
|
||
- `productTypeCode`
|
||
- `productTypeLabel`
|
||
- `description`
|
||
- `quantity`
|
||
- `unitCode`
|
||
- `unitLabel`
|
||
- `unitPrice`
|
||
- `discount`
|
||
- `discountTypeCode`
|
||
- `taxRate`
|
||
- `totalPrice`
|
||
- `notes`
|
||
|
||
Reference:
|
||
|
||
- [src/features/crm/quotations/document/server/service.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/server/service.ts:564)
|
||
- [src/features/crm/quotations/document/types.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/src/features/crm/quotations/document/types.ts:83)
|
||
|
||
### 5.3 Actual mapping coverage for `items_table`
|
||
|
||
Verified active mapping rows already include `items_table` for both organizations.
|
||
|
||
Current columns:
|
||
|
||
- `Item` → `itemNumber`
|
||
- `Description` → `description`
|
||
- `Qty` → `quantity`
|
||
- `Unit` → `unitLabel`
|
||
- `Unit Price` → `unitPrice`
|
||
- `Total` → `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 code` does not exist as a standalone quotation item field in the current model
|
||
- `specification` does not exist as a standalone quotation item field in the current model
|
||
- `remark` likely maps to `notes`, but this should be treated as a business decision, not assumed silently
|
||
|
||
---
|
||
|
||
## 6. Final Implementation Strategy
|
||
|
||
Recommended implementation order:
|
||
|
||
1. generalize topic/signature runtime targeting so it no longer depends on hardcoded page index `1`
|
||
2. create new template JSON files
|
||
- `ALLA_template_pdfme_product_v1.json`
|
||
- `ONVALLA_template_pdfme_product_v1.json`
|
||
3. place the Product Item page between customer detail and topic/signature pages
|
||
4. configure the Product Item page with official PDFMe `table` schema using:
|
||
- `showHead: true`
|
||
- `repeatHead: true`
|
||
- blank/object-style `basePdf`
|
||
5. keep old versions untouched and inactive/active-switchable
|
||
6. update reseed/audit source selectors so the new JSON files can be published into DB
|
||
7. 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:
|
||
|
||
1. do not delete the current active `1.0` versions
|
||
2. publish the new Product Item design as a separate version only
|
||
3. if regression occurs, deactivate the new version
|
||
4. reactivate the current `1.0` version in `/dashboard/crm/settings/templates`
|
||
5. 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-data`
|
||
- `GET /api/crm/quotations/[id]/document-preview`
|
||
- `GET /api/crm/quotations/[id]/pdf-preview`
|
||
- `GET /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.json` structure
|
||
- 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 code` and `specification` fields
|
||
- the biggest implementation blocker is still the topic engine’s hardcoded page targeting, not the table plugin itself
|