# Technical Debt

## After Task D

### Database foreign keys

Customer, contact, and enquiry relationships are still enforced mainly in application logic.

Future:

- add FK constraints after the CRM schema settles
- review cascade behavior before enabling hard relational enforcement

### Date input UX

Several forms still use plain `YYYY-MM-DD` text/date inputs.

Future:

- move to a shared date picker pattern
- standardize timezone handling for CRM forms

### Related enquiry pagination

Customer detail can show related enquiries, but that view still has no dedicated pagination strategy.

Future:

- add paging once real data volume grows

### Follow-up activity timeline

Activity history still relies heavily on audit payloads instead of a curated domain timeline.

Future:

- build a combined activity view/service for CRM interactions

## After Task D.1

### Enquiry Own / Assigned Scope

Current:
sales role ยังอ่าน enquiry ในระดับ organization scope

Future:
เพิ่ม server-side policy:

- sales เห็นเฉพาะ enquiry ที่ตัวเองสร้าง
- หรือ enquiry ที่ assigned ให้ตัวเอง
- sales_manager เห็นทั้งทีม

Priority:
High

### Legacy Business Role Backfill

Current:
ระบบ runtime ใช้ CRM businessRole แล้ว แต่ DB เก่าอาจยังมี role จาก IT-CENTER

Future:
ทำ one-time migration/backfill script สำหรับ memberships.businessRole

Priority:
Medium

## After Task E

### Mixed tax rollup

Quotation items support per-line tax input, but summary calculation still assumes a header-centric tax model.

Future:

- support mixed-tax aggregation from actual item rows
- align document totals and reporting with per-line tax logic

### Attachment storage abstraction

Quotation attachments started as metadata-only and approved PDFs now write to local disk, but there is still no real storage abstraction.

Future:

- introduce a provider abstraction
- support upload/download lifecycle consistently across manual attachments and generated artifacts
- define migration strategy for existing local/public paths

### Revision snapshot coverage

Revision copy currently focuses on quotation header, items, customers, and topics.

Future:

- decide whether follow-ups and attachments belong in revision snapshots
- define immutable vs mutable child-data behavior across revisions

### CRM foreign keys

Core CRM tables still depend on organization-scoped validation in service code.

Future:

- add FK constraints after schema churn slows down
- review delete and soft-delete interactions first

## After Task F

### Permission sync for existing memberships

New permissions are defined in code, but old membership rows may still hold stale permission arrays.

Future:

- add a permission sync/backfill command
- provide admin tooling for role and permission maintenance

### Approval workflow configuration UI

Approval workflows are seeded and usable, but still not configurable from the dashboard.

Future:

- build workflow setup screens
- add step editing and role mapping management

### Approval notifications

Approval flow has no queue-backed notification channel yet.

Future:

- add notification jobs
- support reminders and escalation policy

## After Task G

### Document mapping editor

Template mappings are seeded and readable, but not manageable from UI.

Future:

- add mapping CRUD for admins
- support table column editing
- add draft/publish template workflow

### Product-specific template resolution

Quotation document template resolution still defaults to a coarse product selection strategy.

Future:

- define clearer rules for crane, dock door, solar, and service-specific templates

## After Task H / H.1

### Font compatibility fallback

PDF generation now uses vendored Cordia TTF files from `public/fonts`, which fixed the TTC runtime issue, but generator-side fallback normalization still remains as a safety net.

Future:

- keep validating Thai text metrics against designer output
- remove fallback normalization only after render parity is proven stable

### Local filesystem storage

Approved PDFs currently persist under `public/generated/quotations/<organizationId>/`.

Future:

- replace local public storage for production with object storage
- add signed/private delivery where required
- define backup and cleanup expectations

### Approved artifact immutability

Approved PDF persistence exists, but the lifecycle is not immutable by policy yet.

Future:

- define whether regeneration is allowed after approval
- version approved artifacts explicitly if regeneration must remain possible
- document which snapshot fields are contractual records

### Post-approval automation

Approved PDF generation is still manual by design.

Future:

- add a non-blocking auto-generation hook after final approval
- move heavy generation/retry behavior to a background job when needed

### PDF pipeline test coverage

Task H.1 added dev fixture and HTTP verification scripts, but there is still no CI-backed E2E coverage for the PDF pipeline.

Future:

- promote fixture verification into automated test coverage
- add regression checks for template mappings, storage persistence, and permission enforcement

### Current PDFME template coverage

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 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
- add fixture-backed end-to-end PDF generation verification in CI once a stable runtime path is available

## After Task I

### Storage provider production rollout

Task I introduced a storage provider abstraction with local and S3/MinIO-compatible implementations, but the production rollout still depends on environment configuration and infrastructure ownership outside the app.

Future:

- validate real bucket policies, credentials rotation, and environment separation
- add operational runbooks for provider misconfiguration and storage outage handling
- add smoke checks for startup-time storage provider health

### Legacy approved PDF migration

Task I keeps compatibility for legacy `public/generated/...` approved PDFs, but those legacy files are still outside the new artifact source-of-truth model.

Future:

- add a one-time migration script from legacy local paths into `crm_document_artifacts`
- backfill `approvedArtifactId` where possible
- remove legacy compatibility once migration is complete and verified

### Artifact lifecycle policy depth

Approved artifacts are now immutable-first and locked after generation, but void/regeneration policy is still service-level rather than a full operator workflow.

Future:

- add explicit admin tooling for artifact void and replacement flows
- define whether artifact voiding also clears or supersedes quotation-facing references automatically
- document retention expectations for voided objects in object storage

### Secure artifact delivery model

Approved-PDF viewing now goes through secure server routes and storage-backed reads, but signed-url/offload behavior is not yet used.

Future:

- decide when direct signed URLs are acceptable versus mandatory server streaming
- add response caching strategy for large artifact downloads
- evaluate optional download-audit sampling if volume grows

## After Task J.0

### Missing lifecycle timestamps for KPI precision

Task J.0 freezes KPI rules around `pipelineStage`, but the schema still lacks explicit lifecycle timestamps for stage conversion and closure events.

Future:

- add `convertedToOpportunityAt` for precise lead-to-opportunity timing
- add `closedWonAt` for won-date reporting
- add `closedLostAt` for lost-date reporting
- backfill reporting logic to prefer explicit lifecycle timestamps over inference when available

### Lost reason analytics

Closed-lost semantics are frozen, but there is still no structured `lostReason` field for dashboard grouping and sales analysis.

Future:

- add `lostReason` to the CRM enquiry lifecycle model
- define a controlled vocabulary or master-option category for loss analysis
- expose lost-reason reporting only after the field is reliably captured

### Default customer party role behavior

Customer party definitions are frozen, but there is still no `defaultPartyRole` behavior to help normalize customer selection and reduce operator ambiguity.

Future:

- decide whether customers should carry a suggested default project-party role
- add `defaultPartyRole` only if it improves data-entry quality without creating hidden automation
- keep dashboard logic independent from this field until the model is formally introduced

## After Task D.2.1

### Unified project-party reporting projection

Revenue attribution is now frozen, but the schema still stores enquiry and quotation parties in separate tables rather than a dedicated reporting projection.

Future:

- add a unified reporting view or materialized projection if dashboard/report queries become heavy
- keep quotation-party precedence and enquiry fallback centralized in one service until that projection exists
- avoid duplicating attribution logic in dashboard-specific code

### Historical party snapshot fidelity

Current revenue attribution uses live project-party relationships with quotation-first fallback behavior, but it does not yet provide effective-dated historical party snapshots for reporting.

Future:

- decide whether approved quotation snapshots or another immutable reporting snapshot should become the historical source of truth
- define how reporting should behave if project parties change after quotation approval or after a won/lost outcome
- add backfill or reconciliation tooling if historical party drift becomes a business issue

## After Task J

### Closed-won lifecycle fidelity

Task J ships a live CRM dashboard, but the current schema still lacks a first-class enquiry `closed_won` lifecycle field and dedicated close timestamps.

Current:

- dashboard won-stage metrics currently use quotation status `accepted` as the practical proxy for `closed_won`

Future:

- add explicit enquiry lifecycle support for `closed_won`
- add `closedWonAt` to support precise won-date analytics
- remove dashboard proxy logic once the lifecycle model is formalized

### Follow-up completion semantics

Task J adds follow-up analytics, but the follow-up model still has no explicit completion flag.

Current:

- dashboard `Completed` follow-up metrics infer completion from `outcome` presence

Future:

- add an explicit completion state or activity outcome model for follow-ups
- separate scheduled, completed, cancelled, and skipped follow-up reporting cleanly

### Dashboard own-scope policy

Task J introduces `crm.dashboard.read`, but sales users still view organization-wide KPI data for now.

Future:

- add own-scope dashboard filtering for sales roles
- define whether mixed-role users see own, team, or organization scope by default
- keep exports aligned with the same scope policy once enforced

### Global project-party filter coverage

Task J adds a global `Project Party Role` filter in the dashboard UI, but the strongest enforcement today is in revenue analytics and export sections.

Future:

- decide which non-revenue widgets should fully honor project-party role filtering
- add a unified filtered reporting projection if cross-widget party-role filtering becomes a hard requirement
