# 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
