task-h.4
This commit is contained in:
63
docs/adr/0012-approval-signature-strategy.md
Normal file
63
docs/adr/0012-approval-signature-strategy.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# ADR 0012: Approval Signature Strategy
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Quotation PDF templates already contain the signature placeholders `app1`, `app2`, `app3` and their position fields, but earlier tasks only seeded safe fallback values. The production document flow now needs a deterministic rule for:
|
||||
|
||||
- who is shown as `Prepared By`
|
||||
- who is shown as `Approved By`
|
||||
- who is shown as `Authorized By`
|
||||
- how each person’s position is resolved
|
||||
- how approved documents stay immutable after approval
|
||||
|
||||
## Decision
|
||||
|
||||
### Prepared By
|
||||
|
||||
- resolve from `crm_quotations.salesman_id`
|
||||
- fallback to `crm_quotations.created_by`
|
||||
- always render the user display name, never a raw id
|
||||
- use quotation `created_at` as the available timestamp for the prepared block
|
||||
|
||||
### Approved By
|
||||
|
||||
- resolve from live approval history in `crm_approval_actions`
|
||||
- consider only `approve` actions
|
||||
- choose the latest approved action whose workflow role is one of:
|
||||
- `sales_manager`
|
||||
- `department_manager`
|
||||
- exclude the workflow’s final approval role from this slot
|
||||
|
||||
### Authorized By
|
||||
|
||||
- resolve from the latest approved action for the workflow’s final step
|
||||
- in the standard workflow this is expected to be `top_manager`
|
||||
|
||||
### Position Resolution
|
||||
|
||||
- primary source is `memberships.business_role`
|
||||
- display label should come from active `ms_options` in category `crm_job_title` when available
|
||||
- if the master option is missing, fallback labels are:
|
||||
- `sales` -> `Sales Engineer`
|
||||
- `sales_support` -> `Sales Support`
|
||||
- `sales_manager` -> `Sales Manager`
|
||||
- `department_manager` -> `Department Manager`
|
||||
- `top_manager` -> `General Manager`
|
||||
|
||||
### Snapshot Immutability
|
||||
|
||||
- preview PDF resolves signatures from live quotation and approval data
|
||||
- approved snapshot stores the resolved signature block and resolved template input
|
||||
- approved PDF generation prefers snapshot signature input when a snapshot already exists
|
||||
- locked approved artifacts remain the immutable historical source
|
||||
|
||||
## Consequences
|
||||
|
||||
- signature placeholder mapping is now explicit and organization-aware
|
||||
- template output no longer depends on hardcoded placeholder text in the PDFME template
|
||||
- position labels can be governed centrally through master options without changing generator code
|
||||
- approved documents preserve approval identity even if user names, memberships, or master options change later
|
||||
78
docs/implementation/task-h3-approval-signature-strategy.md
Normal file
78
docs/implementation/task-h3-approval-signature-strategy.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Task H.3: Approval Signature Strategy
|
||||
|
||||
## Files Added
|
||||
|
||||
- `src/features/crm/quotations/document/signature.types.ts`
|
||||
- `src/features/crm/quotations/document/signature-position.ts`
|
||||
- `src/features/crm/quotations/document/server/signature-resolver.ts`
|
||||
- `scripts/verify-task-h3.js`
|
||||
- `docs/adr/0012-approval-signature-strategy.md`
|
||||
- `docs/implementation/task-h3-approval-signature-strategy.md`
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `src/features/crm/quotations/document/types.ts`
|
||||
- `src/features/crm/quotations/document/server/service.ts`
|
||||
- `src/features/crm/quotations/components/quotation-document-preview.tsx`
|
||||
- `src/features/foundation/pdf-generator/server/service.ts`
|
||||
- `src/db/seeds/foundation.seed.ts`
|
||||
- `docs/implementation/technical-debt.md`
|
||||
- `package.json`
|
||||
|
||||
## Signature Resolution Strategy
|
||||
|
||||
- `Prepared By` resolves from `quotation.salesmanId`, then falls back to `quotation.createdBy`
|
||||
- `Approved By` resolves from the latest approved workflow action for `sales_manager` or `department_manager`, excluding the final authorization step
|
||||
- `Authorized By` resolves from the latest approved workflow action for the final workflow step, which is `top_manager` in the standard flow
|
||||
- each signature block now carries:
|
||||
- `name`
|
||||
- `position`
|
||||
- `actedAt`
|
||||
|
||||
## Position Resolution Strategy
|
||||
|
||||
- primary source is `memberships.businessRole`
|
||||
- display label first checks active `crm_job_title` master options
|
||||
- fallback labels are:
|
||||
- `sales` -> `Sales Engineer`
|
||||
- `sales_support` -> `Sales Support`
|
||||
- `sales_manager` -> `Sales Manager`
|
||||
- `department_manager` -> `Department Manager`
|
||||
- `top_manager` -> `General Manager`
|
||||
|
||||
## PDF Mapping Changes
|
||||
|
||||
- `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`
|
||||
|
||||
## Snapshot Changes
|
||||
|
||||
- `prepareApprovedQuotationSnapshot()` now stores the resolved signature block through `documentData`
|
||||
- persisted `templateInput` now preserves the text values used for `app1/app2/app3`
|
||||
- approved PDF generation prefers snapshot input when a snapshot already exists
|
||||
|
||||
## UI Changes
|
||||
|
||||
- quotation document preview now shows an `Approval Signatures` section
|
||||
- each block displays:
|
||||
- name
|
||||
- position
|
||||
- timestamp when available
|
||||
|
||||
## Verification Result
|
||||
|
||||
- `scripts/verify-task-h3.js` validates:
|
||||
- DB mappings for `app1/app2/app3`
|
||||
- `crm_job_title` master options
|
||||
- approved snapshot signature payload
|
||||
- approved snapshot template input alignment
|
||||
|
||||
## Remaining Risks
|
||||
|
||||
- organizations with custom approval roles outside the current sales-manager / department-manager / final-step model may need an extended resolver rule later
|
||||
- `Prepared By` currently uses quotation `createdAt` as the available timestamp because the system does not yet store a separate “prepared at” event
|
||||
- end-to-end PDF binary text extraction is still not automated; verification currently proves snapshot and template-input integrity
|
||||
@@ -206,13 +206,11 @@ Future:
|
||||
Current:
|
||||
|
||||
- payment, warranty, delivery, and extra topic sections now flow through the dynamic `topic` / `data_topic` engine on page 2 instead of fixed placeholder fields
|
||||
- approval signature fields are not mapped yet
|
||||
- signature placeholders `app1/app2/app3` currently rely on safe fallback mapping and still need Task H.3 strategy for real names and positions
|
||||
- approval signature placeholders now resolve through the H.3 signature strategy and `crm_job_title` position lookup
|
||||
|
||||
Future:
|
||||
|
||||
- keep validating pagination and closing-block placement against production designer expectations for large topic sets
|
||||
- map approval signature fields when approval-to-document ownership is formalized
|
||||
- add fixture-backed end-to-end PDF generation verification in CI once a stable runtime path is available
|
||||
|
||||
## After Task I
|
||||
|
||||
Reference in New Issue
Block a user