# Task H.3: Approval Signature Strategy ## Goal ทำให้ PDF ใบเสนอราคาสามารถแสดงข้อมูลผู้จัดทำ ผู้อนุมัติ และผู้มีอำนาจอนุมัติได้อย่างถูกต้อง รองรับทั้ง: ```txt Preview PDF Approved PDF Approved Snapshot Document Artifact ``` โดยข้อมูลลายเซ็นต้องมาจาก Approval Workflow จริง ไม่ใช่ค่าคงที่ใน Template --- # Background Task H.2 เพิ่ม: ```txt Dynamic Topic Engine PDFME Runtime Mapping Template Mapping Resolver ``` Task H.1 เพิ่ม: ```txt Approved Snapshot Artifact Storage ``` Task H.3 จะเติมส่วนที่ยังขาด: ```txt Prepared By Approved By Authorized By Position Approval Timeline Resolution ``` --- # Current Problem Template ปัจจุบันมี placeholder เช่น: ```txt app1 app1_position app2 app2_position app3 app3_position ``` แต่ยังไม่มี strategy กลางสำหรับดึงข้อมูลจริงจาก: ```txt quotation approval workflow approval actions membership business role ``` --- # Scope H3.1 Signature Data Contract Create: ```txt src/features/crm/quotations/document/signature.types.ts ``` Define: ```ts type SignatureBlock = { preparedBy: { name: string; position: string; }; approvedBy: { name: string; position: string; }; authorizedBy: { name: string; position: string; }; }; ``` --- # Scope H3.2 Position Resolution Strategy Create: ```txt src/features/crm/quotations/document/signature-position.ts ``` Resolve position from: ```txt membership.businessRole ``` Recommended mapping: ```txt sales → Sales Engineer sales_support → Sales Support sales_manager → Sales Manager department_manager → Department Manager top_manager → General Manager ``` Rules: Do NOT hardcode display strings inside PDF generator. Source from: ```txt crm_job_title ``` master option when available. Fallback: ```txt businessRole label ``` --- # Scope H3.3 Prepared By Resolution Prepared By source: Priority: ```txt quotation.salesmanId ``` Fallback: ```txt quotation.createdBy ``` Output: ```txt preparedBy.name preparedBy.position ``` Rules: Must resolve display name. Never use raw user id. --- # Scope H3.4 Approved By Resolution Resolve from approval workflow history. Source: ```txt crm_approval_actions ``` Rules: Find latest approved action from: ```txt sales_manager ``` or ```txt department_manager ``` depending on workflow configuration. Output: ```txt approvedBy.name approvedBy.position ``` --- # Scope H3.5 Authorized By Resolution Resolve from final approval step. Source: ```txt crm_approval_actions ``` Rules: Find final approved action. Expected role: ```txt top_manager ``` Output: ```txt authorizedBy.name authorizedBy.position ``` --- # Scope H3.6 Signature Resolver Create: ```txt src/features/crm/quotations/document/server/signature-resolver.ts ``` Expose: ```ts resolveQuotationSignatures( quotationId, organizationId ) ``` Returns: ```ts SignatureBlock ``` Responsibilities: ```txt load quotation load approval history resolve users resolve positions build final signature object ``` --- # Scope H3.7 PDF Mapping Integration Update: ```txt src/features/crm/quotations/document/server/service.ts ``` Inject: ```txt signatures.preparedBy.name signatures.preparedBy.position signatures.approvedBy.name signatures.approvedBy.position signatures.authorizedBy.name signatures.authorizedBy.position ``` Into: ```txt documentData templateInput approvedSnapshot ``` --- # Scope H3.8 PDFME Placeholder Mapping Update seed mappings. Expected: ```txt 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 ``` Rules: Idempotent seed. Update existing mappings if incorrect. --- # Scope H3.9 Approved Snapshot Integration Update: ```txt prepareApprovedQuotationSnapshot() ``` Snapshot must store: ```txt resolved signatures ``` Purpose: Future PDF regeneration must preserve: ```txt who approved who authorized ``` even if users later change. --- # Scope H3.10 Preview Behavior Preview PDF: ```txt resolve live signatures ``` Approved PDF: ```txt prefer approved snapshot signatures ``` Rules: Approved artifact must remain immutable. --- # Scope H3.11 UI Visibility Update quotation detail. Add section: ```txt Approval Signatures ``` Display: ```txt Prepared By Approved By Authorized By ``` with: ```txt name position approval timestamp ``` if available. --- # Scope H3.12 Verification Create: ```txt scripts/verify-task-h3.js ``` Verify: ```txt preparedBy exists approvedBy exists authorizedBy exists app1 mapped app2 mapped app3 mapped snapshot contains signatures approved PDF contains names ``` --- # ADR Create: ```txt docs/adr/0012-approval-signature-strategy.md ``` Freeze: ```txt Prepared By rule Approved By rule Authorized By rule Position resolution rule Snapshot immutability rule ``` --- # Explicit Non-Scope Do NOT implement: ```txt Digital signature Image signature Certificate signature PKI e-Signature QR verification Stamp image upload ``` Only text-based approval identity. --- # Output 1. Files Added 2. Files Modified 3. Signature Resolution Strategy 4. Position Resolution Strategy 5. PDF Mapping Changes 6. Snapshot Changes 7. Verification Result 8. ADR Added 9. Remaining Risks --- # Definition of Done ✔ Prepared By resolves correctly ✔ Approved By resolves correctly ✔ Authorized By resolves correctly ✔ Position resolves correctly ✔ PDF Preview displays signatures ✔ Approved PDF displays signatures ✔ Approved Snapshot stores signatures ✔ app1/app2/app3 mappings work ✔ Artifact remains immutable ✔ ADR created