1229 lines
22 KiB
Markdown
1229 lines
22 KiB
Markdown
# Task P.4.2 Runtime Design
|
|
|
|
## Scope
|
|
|
|
This document defines the revised architecture design for the next-generation CRM PDF runtime requested by [task-p.4.2.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4.2.md).
|
|
|
|
This is architecture work only.
|
|
|
|
- no production code changes
|
|
- no template JSON changes
|
|
- no database changes
|
|
- no API changes
|
|
|
|
This design reuses verified findings from:
|
|
|
|
- [task-p.4-discovery-report.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4-discovery-report.md)
|
|
- [task-p.4.1-verification-report.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4.1-verification-report.md)
|
|
|
|
---
|
|
|
|
## 1. Current Architecture Summary
|
|
|
|
### 1.1 Current runtime shape
|
|
|
|
The current quotation PDF runtime is effectively:
|
|
|
|
```text
|
|
Quotation Document Data
|
|
-> Active Template Resolution
|
|
-> Mapping Resolution
|
|
-> Mutate Topic Page
|
|
-> PDF Generator
|
|
```
|
|
|
|
The same underlying pipeline is used by:
|
|
|
|
- document preview
|
|
- PDF preview
|
|
- PDF download
|
|
- approved PDF generation
|
|
|
|
### 1.2 Current strengths
|
|
|
|
The existing foundation already gives us:
|
|
|
|
- DB-backed active template versioning
|
|
- DB-backed placeholder mappings
|
|
- shared quotation document data builder
|
|
- shared PDF generation gateway
|
|
- working topic pagination
|
|
- working PDFMe table support
|
|
- safe rollback through template version activation
|
|
|
|
### 1.3 Current architectural problem
|
|
|
|
The main problem is not PDF generation itself.
|
|
The problem is that rendering behavior depends on page order.
|
|
|
|
Today the topic runtime assumes:
|
|
|
|
```ts
|
|
schemas[1]
|
|
```
|
|
|
|
is always the topic/signature page.
|
|
|
|
That means business logic currently depends on:
|
|
|
|
- physical page indexes
|
|
|
|
instead of:
|
|
|
|
- logical document roles
|
|
- logical sections
|
|
|
|
This makes page insertion fragile and blocks optional section composition.
|
|
|
|
---
|
|
|
|
## 2. Proposed Architecture
|
|
|
|
### 2.1 Architecture principles
|
|
|
|
The new runtime must be:
|
|
|
|
- page-role driven
|
|
- section-based
|
|
- version compatible
|
|
- renderer independent
|
|
- extensible
|
|
|
|
### 2.2 Architectural shift
|
|
|
|
The runtime should move from:
|
|
|
|
```text
|
|
Template
|
|
-> Mutate Page 2
|
|
-> Generate PDF
|
|
```
|
|
|
|
to:
|
|
|
|
```text
|
|
Document Data
|
|
-> Template Resolver
|
|
-> Mapping Resolver
|
|
-> Compatibility Adapter
|
|
-> Page Resolver
|
|
-> Render Policy Resolver
|
|
-> Section Composer
|
|
-> Template Assembler
|
|
-> PDF Generator
|
|
```
|
|
|
|
### 2.3 Main design idea
|
|
|
|
The core design change is:
|
|
|
|
- treat document content as logical sections
|
|
- treat pages as render targets discovered by role
|
|
- let render policy decide whether each section should render
|
|
- let the assembler compose the final template from section outputs
|
|
|
|
This means future changes are made by adding:
|
|
|
|
- a new section role
|
|
- a new section builder
|
|
- a new marker rule
|
|
- a new assembly rule
|
|
|
|
instead of rewriting the whole runtime.
|
|
|
|
---
|
|
|
|
## 3. Section-Based Rendering Model
|
|
|
|
### 3.1 Section concept
|
|
|
|
A `Document Section` represents logical document content, not fixed physical pages.
|
|
|
|
Example sections:
|
|
|
|
- `customer`
|
|
- `product_items`
|
|
- `topics`
|
|
- `conditions`
|
|
- `signature`
|
|
- `attachments`
|
|
- `appendix`
|
|
- `warranty`
|
|
- `cover`
|
|
|
|
### 3.2 Section output behavior
|
|
|
|
A section may generate:
|
|
|
|
- zero pages
|
|
- one page
|
|
- multiple pages
|
|
|
|
This is important because:
|
|
|
|
- product items may paginate to many pages
|
|
- topics may paginate to many pages
|
|
- optional sections may render nothing
|
|
|
|
### 3.3 Separation rule
|
|
|
|
Each section builder must own only its own content domain.
|
|
|
|
Examples:
|
|
|
|
- Product Item rendering must not know topic rendering
|
|
- Topic rendering must not know product rendering
|
|
- Signature placement must not know product table layout internals
|
|
|
|
---
|
|
|
|
## 4. Optional Sections and Render Policy
|
|
|
|
### 4.1 Motivation
|
|
|
|
The new requirement adds optional section support.
|
|
|
|
That means the runtime must support skipping logical sections without requiring:
|
|
|
|
- another template version
|
|
- another runtime pipeline
|
|
|
|
### 4.2 Section requirement model
|
|
|
|
Each section should be evaluated through an explicit render policy.
|
|
|
|
Proposed contract:
|
|
|
|
```ts
|
|
type PageRole =
|
|
| 'customer'
|
|
| 'product_items'
|
|
| 'topics'
|
|
| 'conditions'
|
|
| 'signature'
|
|
| 'attachments'
|
|
| 'appendix'
|
|
| 'warranty'
|
|
| 'cover'
|
|
| 'unknown';
|
|
|
|
interface RenderPolicy {
|
|
section: PageRole;
|
|
enabled: boolean;
|
|
required: boolean;
|
|
visibleWhenEmpty: boolean;
|
|
}
|
|
```
|
|
|
|
### 4.3 Policy meaning
|
|
|
|
- `enabled`
|
|
- whether the runtime should attempt to render the section
|
|
- `required`
|
|
- whether missing output should be treated as error
|
|
- `visibleWhenEmpty`
|
|
- whether an enabled section should still render a shell/placeholder when it has no content
|
|
|
|
### 4.4 Architectural examples
|
|
|
|
Example policy set:
|
|
|
|
```text
|
|
Customer: required
|
|
Product Items: optional
|
|
Topics: optional
|
|
Conditions: optional
|
|
Signature: required
|
|
```
|
|
|
|
### 4.5 Policy source
|
|
|
|
Policy source is intentionally undefined in this task.
|
|
|
|
Future tasks may supply it from:
|
|
|
|
- user selection
|
|
- customer preference
|
|
- template defaults
|
|
- organization defaults
|
|
|
|
The architecture only needs a clean slot for the policy, not a persistence/UI decision yet.
|
|
|
|
### 4.6 Important constraint
|
|
|
|
Template versions should be used for:
|
|
|
|
- structural differences
|
|
- layout differences
|
|
|
|
Template versions should not be used solely for:
|
|
|
|
- enabling or disabling logical sections
|
|
|
|
That responsibility belongs to render policy.
|
|
|
|
---
|
|
|
|
## 5. Runtime Pipeline
|
|
|
|
### 5.1 Proposed pipeline
|
|
|
|
```text
|
|
Document Data
|
|
-> Template Resolver
|
|
-> Mapping Resolver
|
|
-> Compatibility Adapter
|
|
-> Page Resolver
|
|
-> Render Policy Resolver
|
|
-> Section Composer
|
|
-> Customer Section Builder
|
|
-> Product Item Engine
|
|
-> Topic Engine
|
|
-> Condition Engine
|
|
-> Signature Resolver
|
|
-> Template Assembler
|
|
-> PDF Generator
|
|
```
|
|
|
|
### 5.2 Stage descriptions
|
|
|
|
#### Stage 1: Document Data
|
|
|
|
Builds normalized business data:
|
|
|
|
- quotation fields
|
|
- customer fields
|
|
- product items
|
|
- topics
|
|
- signatures
|
|
- approval metadata
|
|
|
|
Does not know:
|
|
|
|
- page structure
|
|
- template markers
|
|
- physical page ordering
|
|
|
|
#### Stage 2: Template Resolver
|
|
|
|
Finds:
|
|
|
|
- active template
|
|
- active version
|
|
- runtime `schemaJson`
|
|
- template metadata
|
|
|
|
Does not know:
|
|
|
|
- business sections
|
|
- page roles
|
|
- render policy
|
|
|
|
#### Stage 3: Mapping Resolver
|
|
|
|
Finds:
|
|
|
|
- placeholder mappings
|
|
- table column mappings
|
|
- defaults
|
|
- format masks
|
|
|
|
Does not know:
|
|
|
|
- page placement
|
|
- optionality rules
|
|
|
|
#### Stage 4: Compatibility Adapter
|
|
|
|
Normalizes legacy and future template models into one page-role-aware runtime model.
|
|
|
|
Owns:
|
|
|
|
- legacy page-role inference
|
|
- explicit marker interpretation
|
|
- marker validation
|
|
- compatibility warnings
|
|
|
|
#### Stage 5: Page Resolver
|
|
|
|
Builds a page inventory from the normalized template.
|
|
|
|
Owns:
|
|
|
|
- page lookup by role
|
|
- duplicate detection
|
|
- fallback ordering
|
|
- insertion anchors
|
|
|
|
#### Stage 6: Render Policy Resolver
|
|
|
|
Produces the effective policy for each section.
|
|
|
|
Owns:
|
|
|
|
- section enablement state
|
|
- required/optional state
|
|
- visible-when-empty behavior
|
|
|
|
Does not own:
|
|
|
|
- section rendering itself
|
|
|
|
#### Stage 7: Section Composer
|
|
|
|
Coordinates section builders in policy order.
|
|
|
|
Owns:
|
|
|
|
- builder orchestration
|
|
- passing section inputs
|
|
- collecting built sections
|
|
- skipping disabled sections
|
|
|
|
Does not own:
|
|
|
|
- detailed rendering logic of each section
|
|
|
|
#### Stage 8+: Section Builders
|
|
|
|
Each builder produces section output only for its own section.
|
|
|
|
Builders include:
|
|
|
|
- Customer Section Builder
|
|
- Product Item Engine
|
|
- Topic Engine
|
|
- Condition Engine
|
|
- Signature Resolver
|
|
|
|
#### Final Stage: Template Assembler
|
|
|
|
Combines:
|
|
|
|
- preserved template pages
|
|
- generated section pages
|
|
- section input patches
|
|
|
|
into one final assembled template.
|
|
|
|
#### Final Stage: PDF Generator
|
|
|
|
Renders the assembled result through the existing PDFMe gateway.
|
|
|
|
---
|
|
|
|
## 6. Runtime Components
|
|
|
|
### 6.1 Template Resolver
|
|
|
|
Responsibility:
|
|
|
|
- resolve active template/version
|
|
- return raw template payload
|
|
|
|
Input:
|
|
|
|
- organization id
|
|
- document type
|
|
- product type
|
|
- file type
|
|
|
|
Output:
|
|
|
|
- `ResolvedTemplate`
|
|
|
|
### 6.2 Mapping Resolver
|
|
|
|
Responsibility:
|
|
|
|
- resolve version mappings and table columns
|
|
|
|
Input:
|
|
|
|
- `ResolvedTemplate`
|
|
|
|
Output:
|
|
|
|
- `ResolvedTemplateMappings`
|
|
|
|
### 6.3 Compatibility Adapter
|
|
|
|
Responsibility:
|
|
|
|
- convert raw runtime template into normalized page-role-aware model
|
|
- handle legacy inference
|
|
- expose compatibility issues
|
|
|
|
Input:
|
|
|
|
- `ResolvedTemplate`
|
|
- `ResolvedTemplateMappings`
|
|
|
|
Output:
|
|
|
|
- `CompatibleTemplate`
|
|
|
|
### 6.4 Page Resolver
|
|
|
|
Responsibility:
|
|
|
|
- resolve page identities
|
|
- build role-based lookup
|
|
- determine insertion anchors
|
|
|
|
Input:
|
|
|
|
- `CompatibleTemplate`
|
|
|
|
Output:
|
|
|
|
- `ResolvedPages`
|
|
|
|
### 6.5 Render Policy Resolver
|
|
|
|
Responsibility:
|
|
|
|
- determine which sections should render
|
|
- determine required/optional behavior
|
|
- determine empty-state display rules
|
|
|
|
Input:
|
|
|
|
- document data
|
|
- template metadata
|
|
- default runtime policy
|
|
|
|
Output:
|
|
|
|
- `ResolvedRenderPolicies`
|
|
|
|
### 6.6 Section Composer
|
|
|
|
Responsibility:
|
|
|
|
- orchestrate section builders
|
|
- pass shared context
|
|
- collect built section outputs
|
|
|
|
Input:
|
|
|
|
- document data
|
|
- resolved pages
|
|
- resolved render policies
|
|
|
|
Output:
|
|
|
|
- `BuiltSections`
|
|
|
|
### 6.7 Customer Section Builder
|
|
|
|
Responsibility:
|
|
|
|
- build customer section only
|
|
- preserve customer-page behavior
|
|
|
|
Input:
|
|
|
|
- customer data
|
|
- target customer page
|
|
|
|
Output:
|
|
|
|
- `BuiltSection<'customer'>`
|
|
|
|
### 6.8 Product Item Engine
|
|
|
|
Responsibility:
|
|
|
|
- build product item pages only
|
|
- paginate table
|
|
- repeat headers
|
|
- handle dynamic row height
|
|
|
|
Input:
|
|
|
|
- items
|
|
- product-items base page
|
|
- product section render policy
|
|
|
|
Output:
|
|
|
|
- `BuiltSection<'product_items'>`
|
|
|
|
### 6.9 Topic Engine
|
|
|
|
Responsibility:
|
|
|
|
- build topic pages only
|
|
- paginate topics
|
|
|
|
Input:
|
|
|
|
- topics
|
|
- topic base page
|
|
- topic section render policy
|
|
|
|
Output:
|
|
|
|
- `BuiltSection<'topics'>`
|
|
|
|
### 6.10 Condition Engine
|
|
|
|
Responsibility:
|
|
|
|
- build condition/terms section only
|
|
|
|
Input:
|
|
|
|
- condition data or condition template markers
|
|
- condition policy
|
|
|
|
Output:
|
|
|
|
- `BuiltSection<'conditions'>`
|
|
|
|
### 6.11 Signature Resolver
|
|
|
|
Responsibility:
|
|
|
|
- build signature section only
|
|
- handle keep-together logic
|
|
- last-page placement policy
|
|
|
|
Input:
|
|
|
|
- signatures
|
|
- signature page
|
|
- prior assembled content
|
|
- signature policy
|
|
|
|
Output:
|
|
|
|
- `BuiltSection<'signature'>`
|
|
|
|
### 6.12 Template Assembler
|
|
|
|
Responsibility:
|
|
|
|
- combine preserved and generated pages
|
|
- apply assembly order
|
|
- merge input patches
|
|
|
|
Input:
|
|
|
|
- compatible template
|
|
- built sections
|
|
|
|
Output:
|
|
|
|
- `AssembledTemplate`
|
|
|
|
### 6.13 PDF Render Gateway
|
|
|
|
Responsibility:
|
|
|
|
- font loading
|
|
- plugin registration
|
|
- final PDFMe rendering
|
|
|
|
Input:
|
|
|
|
- `AssembledTemplate`
|
|
|
|
Output:
|
|
|
|
- rendered PDF bytes
|
|
|
|
---
|
|
|
|
## 7. Page Marker Strategy
|
|
|
|
### 7.1 Goal
|
|
|
|
The runtime must never depend on:
|
|
|
|
```ts
|
|
schemas[1]
|
|
```
|
|
|
|
The runtime must resolve pages by logical markers and fallbacks.
|
|
|
|
### 7.2 Explicit marker strategy
|
|
|
|
Future templates should include explicit role markers.
|
|
|
|
Example:
|
|
|
|
```ts
|
|
{
|
|
name: '__page_role__product_items',
|
|
type: 'text',
|
|
content: 'product_items'
|
|
}
|
|
```
|
|
|
|
or
|
|
|
|
```ts
|
|
{
|
|
name: '__page_role__topics',
|
|
type: 'text',
|
|
content: 'topics'
|
|
}
|
|
```
|
|
|
|
### 7.3 Legacy inference strategy
|
|
|
|
Existing templates do not have explicit markers.
|
|
|
|
Legacy inference rules should be centralized in the compatibility adapter.
|
|
|
|
Examples:
|
|
|
|
- page containing customer identity fields => `customer`
|
|
- page containing `topic` + `data_topic` => `topics`
|
|
- page containing `app1`/`app2`/`app3` and closing signature text => `signature`
|
|
- future page containing `items_table` => `product_items`
|
|
- future page containing dedicated condition markers => `conditions`
|
|
|
|
### 7.4 Fallback detection
|
|
|
|
If no explicit marker or legacy inference succeeds:
|
|
|
|
- mark page as `unknown`
|
|
- record a runtime issue
|
|
- optionally continue if page is non-required
|
|
|
|
### 7.5 Marker precedence
|
|
|
|
Priority order:
|
|
|
|
1. explicit marker
|
|
2. legacy inference
|
|
3. fallback guess
|
|
4. unknown
|
|
|
|
---
|
|
|
|
## 8. Runtime Contracts
|
|
|
|
### 8.1 Core contracts
|
|
|
|
```ts
|
|
type PageRole =
|
|
| 'customer'
|
|
| 'product_items'
|
|
| 'topics'
|
|
| 'conditions'
|
|
| 'signature'
|
|
| 'attachments'
|
|
| 'appendix'
|
|
| 'warranty'
|
|
| 'cover'
|
|
| 'unknown';
|
|
|
|
interface ResolvedTemplate {
|
|
templateId: string;
|
|
templateVersionId: string;
|
|
templateName: string;
|
|
version: string;
|
|
schemaJson: Template;
|
|
}
|
|
|
|
interface ResolvedTemplateMappings {
|
|
mappings: DocumentTemplateMappingWithColumns[];
|
|
}
|
|
|
|
interface ResolvedPage {
|
|
pageIndex: number;
|
|
role: PageRole;
|
|
strategy: 'explicit_marker' | 'legacy_inference' | 'fallback';
|
|
schema: Template['schemas'][number];
|
|
}
|
|
|
|
interface ResolvedPages {
|
|
all: ResolvedPage[];
|
|
byRole: Partial<Record<PageRole, ResolvedPage[]>>;
|
|
issues: RuntimeIssue[];
|
|
}
|
|
|
|
interface RenderPolicy {
|
|
section: PageRole;
|
|
enabled: boolean;
|
|
required: boolean;
|
|
visibleWhenEmpty: boolean;
|
|
}
|
|
|
|
interface BuiltSection<TRole extends PageRole = PageRole> {
|
|
role: TRole;
|
|
enabled: boolean;
|
|
rendered: boolean;
|
|
pages: Template['schemas'];
|
|
templateInputPatch: Record<string, unknown>;
|
|
issues: RuntimeIssue[];
|
|
}
|
|
|
|
interface RuntimeIssue {
|
|
code:
|
|
| 'MISSING_MARKER'
|
|
| 'DUPLICATE_MARKER'
|
|
| 'INVALID_TEMPLATE'
|
|
| 'MISSING_MAPPING'
|
|
| 'EMPTY_OPTIONAL_SECTION'
|
|
| 'EMPTY_REQUIRED_SECTION'
|
|
| 'LEGACY_COMPAT_MODE';
|
|
severity: 'warning' | 'error';
|
|
message: string;
|
|
details?: Record<string, unknown>;
|
|
}
|
|
|
|
interface AssembledTemplate {
|
|
template: Template;
|
|
templateInput: Record<string, unknown>;
|
|
issues: RuntimeIssue[];
|
|
}
|
|
```
|
|
|
|
### 8.2 Ownership boundaries
|
|
|
|
- `ResolvedTemplate` belongs to template resolution
|
|
- `ResolvedTemplateMappings` belongs to mapping resolution
|
|
- `ResolvedPages` belongs to page resolution
|
|
- `RenderPolicy` belongs to policy resolution
|
|
- `BuiltSection` belongs to section builders
|
|
- `AssembledTemplate` belongs to assembly only
|
|
- `RuntimeIssue` is shared, accumulative diagnostic output
|
|
|
|
---
|
|
|
|
## 9. Section Composer Design
|
|
|
|
### 9.1 Purpose
|
|
|
|
The section composer is a new first-class component introduced by the updated requirement.
|
|
|
|
Its job is to coordinate section rendering independently from page index assumptions.
|
|
|
|
### 9.2 Responsibilities
|
|
|
|
- evaluate effective render policy
|
|
- call only enabled section builders
|
|
- preserve required section order
|
|
- collect built outputs
|
|
- collect runtime issues
|
|
|
|
### 9.3 Example composition order
|
|
|
|
Suggested default composition order:
|
|
|
|
1. `customer`
|
|
2. `product_items`
|
|
3. `topics`
|
|
4. `conditions`
|
|
5. `signature`
|
|
6. `attachments`
|
|
7. `appendix`
|
|
|
|
This order is logical, not tied to page indexes.
|
|
|
|
### 9.4 Composer interface
|
|
|
|
```ts
|
|
interface SectionComposerInput {
|
|
documentData: QuotationDocumentData;
|
|
resolvedPages: ResolvedPages;
|
|
policies: RenderPolicy[];
|
|
}
|
|
|
|
interface SectionComposerOutput {
|
|
sections: BuiltSection[];
|
|
issues: RuntimeIssue[];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Compatibility Strategy
|
|
|
|
### 10.1 Objective
|
|
|
|
Support:
|
|
|
|
- legacy templates
|
|
- future marker-based templates
|
|
|
|
without duplicating runtime logic.
|
|
|
|
### 10.2 Design rule
|
|
|
|
Legacy support must be isolated in:
|
|
|
|
- `Compatibility Adapter`
|
|
|
|
Every component after that should consume normalized contracts only.
|
|
|
|
### 10.3 Why this matters
|
|
|
|
Without this layer, optional sections and page-role logic would leak branching across:
|
|
|
|
- topic engine
|
|
- product item engine
|
|
- assembler
|
|
- signature logic
|
|
|
|
That would make the runtime harder to extend and harder to verify.
|
|
|
|
### 10.4 Compatibility outcome
|
|
|
|
Legacy templates stay valid because:
|
|
|
|
- their pages can still be inferred by field patterns
|
|
- their mappings remain unchanged
|
|
- their active versions remain selectable
|
|
|
|
Future templates become simpler because:
|
|
|
|
- markers are explicit
|
|
- sections are additive
|
|
- optionality is policy-driven, not template-version-driven
|
|
|
|
---
|
|
|
|
## 11. Future Extensibility
|
|
|
|
### 11.1 Additive extension model
|
|
|
|
A new document section should require only:
|
|
|
|
1. a `PageRole`
|
|
2. a section builder
|
|
3. a marker rule
|
|
4. an assembly rule
|
|
|
|
### 11.2 Supported future examples
|
|
|
|
- drawing
|
|
- specification
|
|
- gallery
|
|
- warranty
|
|
- appendix
|
|
- inspection report
|
|
|
|
### 11.3 Builder isolation rule
|
|
|
|
Existing section builders must not need modification when a new section is added, unless a shared contract truly changes.
|
|
|
|
That keeps the runtime additive instead of fragile.
|
|
|
|
---
|
|
|
|
## 12. Error Handling Strategy
|
|
|
|
### 12.1 Missing marker
|
|
|
|
Behavior:
|
|
|
|
- try explicit marker
|
|
- try legacy inference
|
|
- try fallback guess
|
|
- otherwise emit `MISSING_MARKER`
|
|
|
|
### 12.2 Duplicate marker
|
|
|
|
Behavior:
|
|
|
|
- duplicate singleton section like `customer` or `signature` => error
|
|
- duplicate repeatable section like `attachments` or `appendix` => warning only if ambiguous
|
|
|
|
### 12.3 Missing mappings
|
|
|
|
Behavior:
|
|
|
|
- if required mapped section cannot render => error
|
|
- if optional section cannot render => warning and skip if policy allows
|
|
|
|
### 12.4 Empty optional section
|
|
|
|
Behavior:
|
|
|
|
- emit `EMPTY_OPTIONAL_SECTION`
|
|
- skip rendering when `visibleWhenEmpty = false`
|
|
- render placeholder shell when `visibleWhenEmpty = true`
|
|
|
|
### 12.5 Empty required section
|
|
|
|
Behavior:
|
|
|
|
- emit `EMPTY_REQUIRED_SECTION`
|
|
- fail gracefully before final PDF generation if policy requires output
|
|
|
|
### 12.6 Invalid template
|
|
|
|
Behavior:
|
|
|
|
- stop assembly
|
|
- return `INVALID_TEMPLATE`
|
|
|
|
### 12.7 Issue accumulation
|
|
|
|
All stages append to `RuntimeIssue[]`.
|
|
|
|
This supports:
|
|
|
|
- preview diagnostics
|
|
- audit tooling
|
|
- rollout safety
|
|
|
|
This remains consistent with ADR-0013 parity/audit discipline.
|
|
|
|
---
|
|
|
|
## 13. Migration Strategy
|
|
|
|
### 13.1 Migration goal
|
|
|
|
Move from the current runtime to a section-based runtime without breaking:
|
|
|
|
- preview
|
|
- download
|
|
- approved PDF
|
|
- existing template versions
|
|
|
|
### 13.2 Constraints
|
|
|
|
- no database migration
|
|
- no API changes
|
|
- no approved artifact rewrite
|
|
|
|
### 13.3 Safe migration path
|
|
|
|
1. introduce runtime contracts
|
|
2. introduce compatibility adapter
|
|
3. introduce page resolver
|
|
4. introduce render policy resolver
|
|
5. introduce section composer
|
|
6. refactor topic engine to consume resolved topic page
|
|
7. add product item engine
|
|
8. add assembler-driven final composition
|
|
9. publish new structural template versions only when needed
|
|
|
|
### 13.4 Approved PDF safety
|
|
|
|
Already stored approved PDFs remain safe because retrieval uses stored artifact binaries, not live re-rendering.
|
|
|
|
Only future generated documents will use the refactored runtime.
|
|
|
|
---
|
|
|
|
## 14. Sequence Diagrams
|
|
|
|
### 14.1 Current Runtime
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant U as User/Route
|
|
participant DS as Document Service
|
|
participant TR as Template Resolver
|
|
participant MR as Mapping Resolver
|
|
participant TE as Topic Engine
|
|
participant PG as PDF Generator
|
|
|
|
U->>DS: Request preview/download
|
|
DS->>DS: buildQuotationDocumentData()
|
|
DS->>TR: resolveTemplateForDocument()
|
|
DS->>MR: resolveTemplateMappings()
|
|
DS->>DS: mapDocumentDataToTemplateInput()
|
|
DS->>TE: buildPdfTopicTemplate()
|
|
TE-->>DS: mutated template + topic inputs
|
|
DS-->>PG: final template + templateInput
|
|
PG-->>U: PDF bytes
|
|
```
|
|
|
|
### 14.2 Proposed Runtime
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant U as User/Route
|
|
participant DD as Document Data
|
|
participant TR as Template Resolver
|
|
participant MR as Mapping Resolver
|
|
participant CA as Compatibility Adapter
|
|
participant PR as Page Resolver
|
|
participant RP as Render Policy Resolver
|
|
participant SC as Section Composer
|
|
participant CB as Customer Builder
|
|
participant PI as Product Item Engine
|
|
participant TO as Topic Engine
|
|
participant CO as Condition Engine
|
|
participant SG as Signature Resolver
|
|
participant TA as Template Assembler
|
|
participant PG as PDF Generator
|
|
|
|
U->>DD: Request preview/download
|
|
DD-->>TR: documentData
|
|
TR-->>MR: resolvedTemplate
|
|
MR-->>CA: mappings + template
|
|
CA-->>PR: compatibleTemplate
|
|
PR-->>RP: resolvedPages
|
|
RP-->>SC: render policies
|
|
SC->>CB: build customer section
|
|
SC->>PI: build product section
|
|
SC->>TO: build topic section
|
|
SC->>CO: build condition section
|
|
SC->>SG: build signature section
|
|
CB-->>SC: built customer section
|
|
PI-->>SC: built product section
|
|
TO-->>SC: built topic section
|
|
CO-->>SC: built condition section
|
|
SG-->>SC: built signature section
|
|
SC-->>TA: built sections
|
|
TA-->>PG: assembled template + input
|
|
PG-->>U: PDF bytes
|
|
```
|
|
|
|
---
|
|
|
|
## 15. Component Diagram
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class TemplateResolver
|
|
class MappingResolver
|
|
class CompatibilityAdapter
|
|
class PageResolver
|
|
class RenderPolicyResolver
|
|
class SectionComposer
|
|
class CustomerSectionBuilder
|
|
class ProductItemEngine
|
|
class TopicEngine
|
|
class ConditionEngine
|
|
class SignatureResolver
|
|
class TemplateAssembler
|
|
class PdfRenderGateway
|
|
|
|
TemplateResolver --> MappingResolver
|
|
MappingResolver --> CompatibilityAdapter
|
|
CompatibilityAdapter --> PageResolver
|
|
PageResolver --> RenderPolicyResolver
|
|
RenderPolicyResolver --> SectionComposer
|
|
SectionComposer --> CustomerSectionBuilder
|
|
SectionComposer --> ProductItemEngine
|
|
SectionComposer --> TopicEngine
|
|
SectionComposer --> ConditionEngine
|
|
SectionComposer --> SignatureResolver
|
|
SectionComposer --> TemplateAssembler
|
|
TemplateAssembler --> PdfRenderGateway
|
|
```
|
|
|
|
---
|
|
|
|
## 16. Risk Assessment
|
|
|
|
### 16.1 Main risks
|
|
|
|
- legacy inference becomes too magical if not centralized
|
|
- optional section policy becomes entangled with template versioning
|
|
- section builders may start leaking logic into each other
|
|
- assembler may become implicit if order rules are not declared clearly
|
|
|
|
### 16.2 Mitigations
|
|
|
|
- keep all legacy branching inside compatibility adapter
|
|
- keep all enable/disable logic inside render policy resolver
|
|
- keep all orchestration inside section composer
|
|
- keep all page order rules inside assembler
|
|
- keep template versions for layout/structure, not section toggles
|
|
|
|
---
|
|
|
|
## 17. Implementation Roadmap
|
|
|
|
### Phase 1: Runtime contracts
|
|
|
|
Deliverables:
|
|
|
|
- `PageRole`
|
|
- `RenderPolicy`
|
|
- `BuiltSection`
|
|
- `RuntimeIssue`
|
|
- `AssembledTemplate`
|
|
|
|
### Phase 2: Compatibility adapter
|
|
|
|
Deliverables:
|
|
|
|
- explicit marker support
|
|
- legacy inference support
|
|
- normalized compatible template output
|
|
|
|
### Phase 3: Page resolver
|
|
|
|
Deliverables:
|
|
|
|
- role-based page inventory
|
|
- duplicate/missing marker diagnostics
|
|
|
|
### Phase 4: Render policy resolver
|
|
|
|
Deliverables:
|
|
|
|
- effective section policy resolution
|
|
- required/optional rules
|
|
- visible-when-empty rules
|
|
|
|
### Phase 5: Section composer
|
|
|
|
Deliverables:
|
|
|
|
- builder orchestration
|
|
- section collection
|
|
- policy-driven skipping
|
|
|
|
### Phase 6: Product item engine
|
|
|
|
Deliverables:
|
|
|
|
- item table rendering
|
|
- pagination
|
|
- repeated headers
|
|
|
|
### Phase 7: Template upgrade
|
|
|
|
Deliverables:
|
|
|
|
- structural template versions for new layouts only
|
|
|
|
### Phase 8: Regression verification
|
|
|
|
Deliverables:
|
|
|
|
- preview/download/approved regression checks
|
|
- old/new version parity checks
|
|
|
|
Each phase must be independently testable.
|
|
|
|
---
|
|
|
|
## 18. Final Recommendation
|
|
|
|
Task P.4.3 should implement a `section-based runtime` with `render policy` and `page-role resolution` as first-class architecture concepts.
|
|
|
|
The most important architectural order is:
|
|
|
|
1. contracts
|
|
2. compatibility adapter
|
|
3. page resolver
|
|
4. render policy resolver
|
|
5. section composer
|
|
6. section builders
|
|
7. assembler
|
|
|
|
This keeps the runtime:
|
|
|
|
- backward compatible
|
|
- optional-section capable
|
|
- future-extensible
|
|
- independent from page indexes
|
|
|
|
and leaves minimal architectural ambiguity for implementation.
|