task-d.7.2

This commit is contained in:
phaichayon
2026-07-02 13:42:09 +07:00
parent bca8d3f7e0
commit 0ae4ae3e38
22 changed files with 4423 additions and 0 deletions

View File

@@ -0,0 +1,557 @@
# CSV Template Specification
Source task: `plans/task-d.7.1.md`
All supported CSV imports must run in two phases:
1. Preview: parse, validate, resolve references, detect duplicates, and produce an import report.
2. Commit: upsert in a transaction where database-only writes are involved.
CSV imports must not bypass feature/foundation services once those services exist. Where current seed logic only exists in scripts, D.7.2+ should extract reusable server services first.
## Supported Templates
### `organizations.csv`
Purpose: create tenant organizations.
Target: `organizations`.
Seed type: `organization`.
Required columns: `organization_code`, `organization_name`, `slug`, `created_by_email`.
Optional columns: `tax_id`, `default_currency`, `phone`, `email`, `address`, `logo_url`, `plan`, `status`.
Generated columns: `id`, `created_at`, `updated_at`.
Validation rules: `slug` lowercase and unique; `organization_code` uppercase; `created_by_email` must resolve to a user.
Relationship resolution: `created_by_email -> users.id`.
Duplicate detection: existing organization by `slug`.
Upsert key: `slug`.
Import order: `02`.
```csv
organization_code,organization_name,slug,created_by_email,tax_id,default_currency,status
ALLA,ALLA Public Company Limited,alla,admin@example.com,0107558000393,THB,active
```
Error examples: duplicate slug with conflicting name; missing creator user; invalid lowercase/uppercase format.
### `users.csv`
Purpose: create setup-managed users.
Target: `users` through auth/user service.
Seed type: `organization`.
Required columns: `email`, `name`, `system_role`.
Optional columns: `password`, `active_organization_code`, `image_url`, `reset_password`.
Generated columns: `id`, `password_hash`, `created_at`, `updated_at`.
Validation rules: email format; `system_role` in `super_admin,user`; password required for new credential users unless invitation flow exists.
Relationship resolution: `active_organization_code -> organizations.slug or setup organization_code`.
Duplicate detection: existing user by email.
Upsert key: `email`.
Import order: `01`.
```csv
email,name,system_role,password,active_organization_code,reset_password
admin@example.com,System Administrator,super_admin,ChangeMe123!,ALLA,true
```
Error examples: invalid email; unsupported system role; existing user without reset permission.
### `memberships.csv`
Purpose: grant app-level organization access.
Target: `memberships`.
Seed type: `organization`.
Required columns: `organization_code`, `user_email`, `membership_role`, `business_role`.
Optional columns: `permissions`, `branch_scope_codes`, `product_type_scope_codes`.
Generated columns: `id`, timestamps.
Validation rules: `membership_role` in `admin,user`; `business_role` should be a known CRM role fallback code; scope codes must resolve when provided.
Relationship resolution: organization, user, branch options, product type options.
Duplicate detection: existing membership by `organization_id + user_id`.
Upsert key: `organization_code + user_email`.
Import order: `03`.
```csv
organization_code,user_email,membership_role,business_role,permissions,branch_scope_codes,product_type_scope_codes
ALLA,admin@example.com,admin,crm_admin,crm.dashboard.read|crm.role.read,head_office,crane|dockdoor
```
Error examples: unknown user; unknown organization; invalid branch/product type code.
### `crm-role-assignments.csv`
Purpose: assign CRM role profiles and CRM scopes.
Target: `crm_user_role_assignments`.
Seed type: `organization`.
Required columns: `organization_code`, `user_email`, `crm_role_code`, `is_primary`, `branch_scope_mode`, `product_type_scope_mode`.
Optional columns: `branch_codes`, `product_type_codes`, `is_active`.
Generated columns: `id`, `assigned_at`, timestamps.
Validation rules: role profile must exist; scope mode in `all,selected,none,inherit`; selected modes require matching codes.
Relationship resolution: user, organization, role profile, branch options, product type options.
Duplicate detection: existing assignment by `organization_id + user_id + role_profile_id`.
Upsert key: `organization_code + user_email + crm_role_code`.
Import order: `05`.
```csv
organization_code,user_email,crm_role_code,is_primary,branch_scope_mode,branch_codes,product_type_scope_mode,product_type_codes,is_active
ALLA,admin@example.com,crm_admin,true,all,,all,,true
```
Error examples: selected scope without codes; role profile missing; multiple primary rows for one user unless explicit replacement mode.
### `master-options.csv`
Purpose: import governed option rows.
Target: `ms_options`.
Seed type: `foundation`.
Required columns: `organization_code`, `category`, `code`, `label`.
Optional columns: `value`, `parent_category`, `parent_code`, `sort_order`, `is_active`, `metadata_json`.
Generated columns: `id`, timestamps.
Validation rules: category/code non-empty; parent resolves in same organization when provided; metadata must be valid JSON.
Relationship resolution: organization, optional parent option.
Duplicate detection: existing option by `organization_id + category + code`.
Upsert key: `organization_code + category + code`.
Import order: `06`.
```csv
organization_code,category,code,label,value,parent_category,parent_code,sort_order,is_active,metadata_json
ALLA,crm_customer_group,industrial,Industrial,industrial,,,1,true,{}
```
Error examples: invalid JSON; missing parent option; duplicate category/code in same CSV with conflicting labels.
### `branches.csv`
Purpose: typed convenience template for branch options.
Target: `ms_options` with `category=crm_branch`.
Seed type: `organization`.
Required columns: `organization_code`, `branch_code`, `branch_name`.
Optional columns: `prefix`, `address`, `tax_id`, `phone`, `email`, `sort_order`, `is_active`.
Generated columns: `id`, `category`, timestamps.
Validation rules: branch code unique per organization; code lowercase snake_case recommended; metadata fields serialized into option metadata.
Relationship resolution: organization.
Duplicate detection: existing `crm_branch` option by code.
Upsert key: `organization_code + branch_code`.
Import order: `07`.
```csv
organization_code,branch_code,branch_name,prefix,address,tax_id,sort_order,is_active
ALLA,head_office,Head Office,HO,99/9 Rama IX Road,0107558000393,1,true
```
Error examples: duplicate branch code; invalid organization; malformed metadata.
### `product-types.csv`
Purpose: typed convenience template for product type options.
Target: `ms_options` with `category=crm_product_type`.
Seed type: `organization`.
Required columns: `organization_code`, `product_type_code`, `product_type_name`.
Optional columns: `document_prefix`, `sort_order`, `is_active`, `metadata_json`.
Generated columns: `id`, `category`, timestamps.
Validation rules: product type code unique per organization; `document_prefix` uppercase recommended.
Relationship resolution: organization.
Duplicate detection: existing `crm_product_type` option by code.
Upsert key: `organization_code + product_type_code`.
Import order: `08`.
```csv
organization_code,product_type_code,product_type_name,document_prefix,sort_order,is_active
ALLA,crane,Crane,CR,1,true
```
Error examples: duplicate product type code; invalid JSON metadata; unsupported inactive default product type for document sequence.
### `document-sequences.csv`
Purpose: configure document numbering.
Target: `document_sequences`.
Seed type: `foundation`.
Required columns: `organization_code`, `document_type`, `prefix`, `period`.
Optional columns: `branch_code`, `product_type_code`, `current_number`, `padding_length`, `format`, `reset_policy`, `is_active`.
Generated columns: `id`, timestamps.
Validation rules: document type supported by sequence foundation; branch/product type resolve or default to generic scope; current number non-negative integer.
Relationship resolution: organization, branch option, product type option.
Duplicate detection: `organization_id + branch_id + product_type + document_type + period`.
Upsert key: same as duplicate detection.
Import order: `14`.
```csv
organization_code,branch_code,product_type_code,document_type,prefix,period,current_number,padding_length,format,reset_policy,is_active
ALLA,head_office,crane,quotation,QT-HO-CR-,2607,0,3,{prefix}{period}-{running},period,true
```
Error examples: unknown branch; unknown product type; malformed format missing `{running}`.
### `approval-workflows.csv`
Purpose: configure approval workflow headers.
Target: `crm_approval_workflows`.
Seed type: `foundation`.
Required columns: `organization_code`, `workflow_code`, `workflow_name`, `entity_type`.
Optional columns: `description`, `is_system`, `is_active`.
Generated columns: `id`, timestamps.
Validation rules: workflow code unique per organization; entity type supported, currently `quotation`.
Relationship resolution: organization.
Duplicate detection: `organization_id + workflow_code`.
Upsert key: `organization_code + workflow_code`.
Import order: `15`.
```csv
organization_code,workflow_code,workflow_name,entity_type,description,is_system,is_active
ALLA,quotation_standard,Quotation Standard Approval,quotation,Default quotation approval,false,true
```
Error examples: duplicate workflow code; unsupported entity type.
### `approval-steps.csv`
Purpose: configure workflow steps.
Target: `crm_approval_steps`.
Seed type: `foundation`.
Required columns: `organization_code`, `workflow_code`, `step_number`, `role_code`, `role_name`.
Optional columns: `approval_mode`, `is_required`, `sla_hours`, `calendar_mode`, `timeout_action`.
Generated columns: `id`, timestamps.
Validation rules: workflow resolves; step number positive integer; role code known to CRM role profiles or approved workflow roles.
Relationship resolution: organization, approval workflow.
Duplicate detection: `workflow_id + step_number`.
Upsert key: `organization_code + workflow_code + step_number`.
Import order: `16`.
```csv
organization_code,workflow_code,step_number,role_code,role_name,approval_mode,is_required,sla_hours,calendar_mode,timeout_action
ALLA,quotation_standard,1,sales_manager,Sales Manager,sequential,true,24,calendar_days,none
```
Error examples: missing workflow; duplicate step number; invalid SLA value.
### `approval-matrix.csv`
Purpose: map approval workflows to entity/scope/amount.
Target: `crm_approval_matrices`.
Seed type: `foundation`.
Required columns: `organization_code`, `entity_type`, `workflow_code`, `priority`, `is_default`.
Optional columns: `branch_code`, `product_type_code`, `min_amount`, `max_amount`, `currency`, `is_active`, `description`.
Generated columns: `id`, timestamps.
Validation rules: workflow resolves; branch/product optional; amount range valid; at most one default per target policy unless replacement mode.
Relationship resolution: organization, workflow, branch option, product type option.
Duplicate detection: default matrix by `organization + entity_type + is_default` or scoped matrix by full scope/amount tuple.
Upsert key: `organization_code + entity_type + workflow_code + priority + scope`.
Import order: `17`.
```csv
organization_code,entity_type,workflow_code,branch_code,product_type_code,min_amount,max_amount,currency,priority,is_default,is_active,description
ALLA,quotation,quotation_standard,head_office,crane,0,5000000,THB,100,true,true,Default crane approval
```
Error examples: max less than min; missing workflow; unresolved currency option.
### `customers.csv`
Purpose: import CRM customers.
Target: `crm_customers`.
Seed type: `business`.
Required columns: `organization_code`, `customer_code`, `customer_name`, `customer_type`, `customer_status`, `created_by_email`.
Optional columns: `branch_code`, `abbr`, `tax_id`, `address`, `province`, `district`, `sub_district`, `postal_code`, `country`, `phone`, `fax`, `email`, `website`, `lead_channel`, `customer_group`, `customer_sub_group`, `owner_user_email`, `notes`, `is_active`.
Generated columns: `id`, timestamps, owner assignment timestamps if owner provided.
Validation rules: code unique per organization; option references resolve; email format when provided.
Relationship resolution: organization, branch, owner user, creator user, master options.
Duplicate detection: `organization_id + customer_code`.
Upsert key: `organization_code + customer_code`.
Import order: `30`.
```csv
organization_code,customer_code,customer_name,customer_type,customer_status,owner_user_email,created_by_email,branch_code,customer_group,email,phone
ALLA,CUS-001,Siam Manufacturing,company,active,sales.crane.uat@alla.local,admin@example.com,head_office,industrial,contact@siam.example,02-000-1000
```
Error examples: unresolved owner; invalid customer group/sub-group relationship; duplicate customer code.
### `contacts.csv`
Purpose: import customer contacts.
Target: `crm_customer_contacts`.
Seed type: `business`.
Required columns: `organization_code`, `customer_code`, `contact_name`, `created_by_email`.
Optional columns: `position`, `department`, `phone`, `mobile`, `email`, `is_primary`, `notes`, `is_active`.
Generated columns: `id`, timestamps.
Validation rules: customer resolves; email format; one primary contact warning when multiple rows set primary.
Relationship resolution: organization, customer, creator user.
Duplicate detection: `organization + customer + email` when email exists, otherwise customer + contact name.
Upsert key: `organization_code + customer_code + email/contact_name`.
Import order: `32`.
```csv
organization_code,customer_code,contact_name,email,mobile,position,is_primary,created_by_email
ALLA,CUS-001,Anan Sales,anan@siam.example,081-000-0001,Procurement Manager,true,admin@example.com
```
Error examples: missing customer; duplicate primary contact warning; invalid email.
### `contact-shares.csv`
Purpose: import contact sharing grants.
Target: `crm_contact_shares`.
Seed type: `business`.
Required columns: `organization_code`, `customer_code`, `contact_email`, `shared_to_user_email`, `shared_by_user_email`.
Optional columns: `remark`, `is_active`.
Generated columns: `id`, `shared_at`, timestamps.
Validation rules: contact resolves; users resolve; cannot share to user outside organization membership.
Relationship resolution: organization, customer, contact, users.
Duplicate detection: `organization_id + contact_id + shared_to_user_id`.
Upsert key: same as duplicate detection.
Import order: `33`.
```csv
organization_code,customer_code,contact_email,shared_to_user_email,shared_by_user_email,remark,is_active
ALLA,CUS-001,anan@siam.example,sales.dockdoor.uat@alla.local,admin@example.com,Shared for joint account coverage,true
```
Error examples: contact not found; target user has no membership; self-share warning.
### `leads.csv`
Purpose: import marketing-owned leads.
Target: `crm_leads`.
Seed type: `business`.
Required columns: `organization_code`, `lead_code`, `status`, `created_by_email`.
Optional columns: `branch_code`, `customer_code`, `contact_email`, `description`, `project_name`, `project_location`, `lead_channel`, `product_type_code`, `priority`, `estimated_value`, `awareness_code`, `followup_status`, `lost_reason`, `outcome`, `owner_marketing_user_email`, `assigned_sales_owner_email`, `assignment_remark`.
Generated columns: `id`, assignment timestamps when assigned, timestamps.
Validation rules: status/priority/product type resolve; customer/contact relationship valid; estimated value numeric.
Relationship resolution: organization, branch, customer, contact, users, master options.
Duplicate detection: `organization_id + lead_code`.
Upsert key: `organization_code + lead_code`.
Import order: `34`.
```csv
organization_code,lead_code,status,created_by_email,customer_code,contact_email,branch_code,product_type_code,priority,project_name,owner_marketing_user_email,assigned_sales_owner_email
ALLA,LD-001,assigned,mk.uat@alla.local,CUS-001,anan@siam.example,head_office,crane,high,Warehouse Crane,mk.uat@alla.local,sales.crane.uat@alla.local
```
Error examples: contact does not belong to customer; unresolved status option; assignee outside organization.
### `opportunities.csv`
Purpose: import sales opportunities.
Target: `crm_opportunities`.
Seed type: `business`.
Required columns: `organization_code`, `opportunity_code`, `customer_code`, `title`, `product_type_code`, `status`, `priority`, `created_by_email`, `updated_by_email`.
Optional columns: `lead_code`, `contact_email`, `branch_code`, `description`, `requirement`, `project_name`, `project_location`, `outcome_status`, `lead_channel`, `estimated_value`, `chance_percent`, `expected_close_date`, `competitor`, `source`, `notes`, `is_hot_project`, `pipeline_stage`, `assigned_to_user_email`.
Generated columns: `id`, assignment timestamps when assigned, lifecycle timestamps where appropriate.
Validation rules: product/status/priority resolve; percent 0-100; dates ISO `YYYY-MM-DD`; lead optional.
Relationship resolution: organization, lead, customer, contact, users, options.
Duplicate detection: `organization_id + opportunity_code`.
Upsert key: `organization_code + opportunity_code`.
Import order: `36`.
```csv
organization_code,opportunity_code,customer_code,title,product_type_code,status,priority,created_by_email,updated_by_email,branch_code,contact_email,lead_code,chance_percent,assigned_to_user_email
ALLA,OP-001,CUS-001,Warehouse Crane Expansion,crane,negotiation,high,sales.crane.uat@alla.local,sales.crane.uat@alla.local,head_office,anan@siam.example,LD-001,70,sales.crane.uat@alla.local
```
Error examples: chance percent outside range; unresolved lead; contact/customer mismatch.
### `opportunity-parties.csv`
Purpose: import opportunity project parties.
Target: `crm_opportunity_customers`.
Seed type: `business`.
Required columns: `organization_code`, `opportunity_code`, `customer_code`, `role_code`.
Optional columns: `remark`.
Generated columns: `id`, timestamps.
Validation rules: opportunity/customer resolve; project party role resolves.
Relationship resolution: opportunity, customer, role option.
Duplicate detection: `opportunity_id + customer_id + role`.
Upsert key: same as duplicate detection.
Import order: `37`.
```csv
organization_code,opportunity_code,customer_code,role_code,remark
ALLA,OP-001,CUS-001,end_customer,Primary project owner
```
Error examples: unknown role; opportunity not found; duplicate role/customer row.
### `quotations.csv`
Purpose: import quotation headers.
Target: `crm_quotations`.
Seed type: `business`.
Required columns: `organization_code`, `quotation_code`, `customer_code`, `quotation_date`, `quotation_type`, `status`, `currency`, `subtotal`, `total_amount`, `created_by_email`, `updated_by_email`.
Optional columns: `opportunity_code`, `contact_email`, `branch_code`, `valid_until`, `project_name`, `project_location`, `attention`, `reference`, `notes`, `revision`, `parent_quotation_code`, `revision_remark`, `exchange_rate`, `discount`, `discount_type`, `tax_rate`, `tax_amount`, `chance_percent`, `is_hot_project`, `competitor`, `salesman_email`, `is_sent`, `sent_at`, `sent_via`.
Generated columns: `id`, timestamps, approval/snapshot/artifact fields remain runtime generated.
Validation rules: status/currency/type resolve; amounts numeric; parent quotation resolves for revision imports.
Relationship resolution: organization, opportunity, customer, contact, branch, options, users.
Duplicate detection: `organization_id + quotation_code`.
Upsert key: `organization_code + quotation_code`.
Import order: `40`.
```csv
organization_code,quotation_code,customer_code,quotation_date,quotation_type,status,currency,subtotal,total_amount,created_by_email,updated_by_email,opportunity_code,branch_code,salesman_email
ALLA,QT-HO-CR-2607-001,CUS-001,2026-07-02,crane,draft,THB,1000000,1070000,sales.crane.uat@alla.local,sales.crane.uat@alla.local,OP-001,head_office,sales.crane.uat@alla.local
```
Error examples: parent revision missing; total mismatch warning; approved/runtime fields supplied.
### `quotation-items.csv`
Purpose: import quotation line items.
Target: `crm_quotation_items`.
Seed type: `business`.
Required columns: `organization_code`, `quotation_code`, `item_number`, `product_type_code`, `description`, `quantity`, `unit_price`, `total_price`.
Optional columns: `unit`, `discount`, `discount_type`, `tax_rate`, `notes`, `sort_order`.
Generated columns: `id`, timestamps.
Validation rules: numeric amount fields; product type resolves; quotation resolves.
Relationship resolution: quotation, product type, unit, discount type.
Duplicate detection: `quotation_id + item_number`.
Upsert key: `organization_code + quotation_code + item_number`.
Import order: `41`.
```csv
organization_code,quotation_code,item_number,product_type_code,description,quantity,unit,unit_price,discount,discount_type,tax_rate,total_price,sort_order
ALLA,QT-HO-CR-2607-001,1,crane,Crane installation package,1,set,1000000,0,,7,1000000,1
```
Error examples: missing quotation; invalid item number; total calculation warning.
### `quotation-parties.csv`
Purpose: import quotation project parties.
Target: `crm_quotation_customers`.
Seed type: `business`.
Required columns: `organization_code`, `quotation_code`, `customer_code`, `role_code`.
Optional columns: `is_primary`, `remark`.
Generated columns: `id`, timestamps.
Validation rules: quotation/customer resolve; role resolves; one primary warning per role as applicable.
Relationship resolution: quotation, customer, project party role option.
Duplicate detection: `quotation_id + customer_id + role`.
Upsert key: same as duplicate detection.
Import order: `42`.
```csv
organization_code,quotation_code,customer_code,role_code,is_primary,remark
ALLA,QT-HO-CR-2607-001,CUS-001,end_customer,true,Primary quotation customer
```
Error examples: missing quotation; unknown project party role; duplicate primary warning.
### `quotation-topics.csv`
Purpose: import quotation topic headers.
Target: `crm_quotation_topics`.
Seed type: `business`.
Required columns: `organization_code`, `quotation_code`, `topic_key`, `topic_type`, `title`.
Optional columns: `sort_order`.
Generated columns: `id`, timestamps.
Validation rules: quotation resolves; topic type resolves; topic key unique per quotation CSV batch.
Relationship resolution: quotation, topic type option.
Duplicate detection: `quotation_id + topic_key`.
Upsert key: `organization_code + quotation_code + topic_key`.
Import order: `43`.
```csv
organization_code,quotation_code,topic_key,topic_type,title,sort_order
ALLA,QT-HO-CR-2607-001,scope-main,scope,Scope of Work,1
```
Error examples: topic type missing; duplicate topic key.
### `quotation-topic-items.csv`
Purpose: import quotation topic rows.
Target: `crm_quotation_topic_items`.
Seed type: `business`.
Required columns: `organization_code`, `quotation_code`, `topic_key`, `content`.
Optional columns: `sort_order`.
Generated columns: `id`, timestamps.
Validation rules: topic key resolves from imported or existing topic; content required.
Relationship resolution: quotation topic.
Duplicate detection: `topic_id + sort_order + content`.
Upsert key: `organization_code + quotation_code + topic_key + sort_order`.
Import order: `44`.
```csv
organization_code,quotation_code,topic_key,content,sort_order
ALLA,QT-HO-CR-2607-001,scope-main,Supply and install one overhead crane system,1
```
Error examples: missing topic; empty content; duplicate sort order warning.
## Unsupported or Deferred Templates
### `departments.csv`
Status: unsupported pending schema. No first-class department table was identified. Do not map this to ad hoc options without an ADR.
```csv
department_code,department_name
SALES,Sales
```
### `teams.csv`
Status: unsupported pending schema. Team scope limitations are documented; no first-class team graph exists.
```csv
team_code,team_name
SALES-CRANE,Crane Sales Team
```
### `warehouses.csv`
Status: unsupported pending schema. No warehouse table was identified.
```csv
warehouse_code,warehouse_name
MAIN,Main Warehouse
```
### `sites.csv`
Status: unsupported pending schema. Project/customer site is currently represented by text fields such as `project_location`, not a reusable site entity.
```csv
site_code,site_name,address
BKK-HQ,Bangkok HQ,99/9 Rama IX Road
```
### `services.csv`
Status: unsupported pending schema. Service catalog does not currently exist as first-class CRM data.
```csv
service_code,service_name
INSTALL,Installation Service
```
### `price-books.csv`
Status: unsupported pending schema. Current quotation items are not backed by a price-book foundation.
```csv
price_book_code,product_or_service_code,currency,unit_price
STD-THB,INSTALL,THB,10000
```
### `tax-rates.csv`
Status: unsupported pending schema. Tax rate currently lives as numeric fields on quotation/item rows.
```csv
tax_code,tax_name,tax_rate
VAT7,VAT 7%,7
```
### `attachments.csv`
Status: deferred. Attachment metadata exists, but binary upload/storage import is not safe as a generic CSV-only flow.
```csv
entity_type,entity_code,file_path,description
quotation,QT-HO-CR-2607-001,./files/spec.pdf,Customer specification
```
### `email-settings.csv`
Status: unsupported pending schema. Email configuration appears environment/provider driven; verification can check readiness but CSV import should not persist secrets without a secure configuration design.
```csv
provider,host,port,from_address
smtp,smtp.example.com,587,no-reply@example.com
```

View File

@@ -0,0 +1,215 @@
# CSV Import Mapping
This document maps supported setup CSV columns to target fields and reference resolvers.
Common resolver rules:
- `organization_code` resolves to the setup organization by wizard code, organization slug, or import context alias.
- `*_email` resolves to `users.email`.
- `branch_code` resolves to `ms_options` where `category = crm_branch`.
- `product_type_code` resolves to `ms_options` where `category = crm_product_type`.
- Master option values resolve by category and code unless a target field explicitly stores plain text.
- All imported rows are organization-scoped unless noted otherwise.
## `organizations.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | setup alias / metadata | trim uppercase | unique in import batch | yes | none | none | Not currently a DB column; used by setup import context. |
| `organization_name` | `organizations.name` | trim | non-empty | yes | none | none | |
| `slug` | `organizations.slug` | trim lowercase | unique | yes | `organizations.slug` duplicate check | generated from name only in wizard mode | |
| `created_by_email` | `organizations.created_by` | lowercase email | user exists | yes | `users.email -> id` | current setup actor | |
| `plan` | `organizations.plan` | trim lowercase | known plan string | no | none | `enterprise` for setup-created org | |
| `logo_url` | `organizations.image_url` | trim | URL or relative path | no | none | null | |
## `users.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `email` | `users.email` | trim lowercase | email format, unique | yes | duplicate by email | none | Upsert key. |
| `name` | `users.name` | trim | non-empty | yes | none | email local part | |
| `system_role` | `users.system_role` | trim lowercase | `super_admin,user` | yes | none | `user` | |
| `password` | `users.password_hash` | bcrypt hash | required for new credentials user | no | none | generated invite flow later | Do not store raw password. |
| `active_organization_code` | `users.active_organization_id` | resolve | org exists | no | organization resolver | null | |
| `image_url` | `users.image` | trim | URL optional | no | none | null | |
| `reset_password` | import behavior | boolean | true/false | no | none | false | Allows password update for existing users. |
## `memberships.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `memberships.organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `user_email` | `memberships.user_id` | lowercase | user exists | yes | `users.email` | none | |
| `membership_role` | `memberships.role` | lowercase | `admin,user` | yes | none | `user` | |
| `business_role` | `memberships.business_role` | lowercase | known CRM fallback role | yes | role definition code | `sales_support` | Compatibility fallback only. |
| `permissions` | `memberships.permissions` | pipe split | known permission codes | no | `src/lib/auth/rbac.ts` | default permissions | |
| `branch_scope_codes` | `memberships.branch_scope_ids` | pipe split resolve | branch options exist | no | `crm_branch` options | empty array | Compatibility scope only. |
| `product_type_scope_codes` | `memberships.product_type_scope_ids` | pipe split resolve | product type options exist | no | `crm_product_type` options | empty array | Compatibility scope only. |
## `crm-role-assignments.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `user_email` | `user_id` | lowercase | user + membership exist | yes | user resolver | none | |
| `crm_role_code` | `role_profile_id` | lowercase | role profile exists | yes | `crm_role_profiles.code` | none | |
| `is_primary` | `is_primary` | boolean | true/false | yes | none | false | One primary per user warning/enforcement. |
| `branch_scope_mode` | `branch_scope_mode` | lowercase | `all,selected,none,inherit` | yes | none | `inherit` | |
| `branch_codes` | `branch_scope_ids` | pipe split resolve | required when mode selected | no | `crm_branch` options | empty array | |
| `product_type_scope_mode` | `product_type_scope_mode` | lowercase | `all,selected,none,inherit` | yes | none | `inherit` | |
| `product_type_codes` | `product_type_scope_ids` | pipe split resolve | required when mode selected | no | `crm_product_type` options | empty array | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
## `master-options.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `category` | `category` | trim lowercase | non-empty | yes | none | none | |
| `code` | `code` | trim | non-empty unique per category | yes | duplicate check | none | |
| `label` | `label` | trim | non-empty | yes | none | none | |
| `value` | `value` | trim | optional | no | none | same as code | |
| `parent_category` | parent lookup | trim | parent requires parent_code | no | `ms_options.category` | null | |
| `parent_code` | `parent_id` | resolve | parent exists | no | `ms_options.category+code` | null | |
| `sort_order` | `sort_order` | integer | >= 0 | no | none | 0 | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
| `metadata_json` | `metadata` | JSON parse | valid object | no | none | null | |
## `branches.csv`
Maps to `ms_options` with fixed `category = crm_branch`.
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `branch_code` | `code`, `value` | trim lowercase snake_case | unique per org | yes | duplicate check | none | |
| `branch_name` | `label` | trim | non-empty | yes | none | none | |
| `prefix` | `metadata.prefix` | trim uppercase | optional | no | none | first letters of code | Used by sequence suggestions only. |
| `address` | `metadata.address` | trim | optional | no | none | null | |
| `tax_id` | `metadata.taxId` | trim | optional | no | none | null | |
| `phone` | `metadata.phone` | trim | optional | no | none | null | |
| `email` | `metadata.email` | lowercase | email optional | no | none | null | |
| `sort_order` | `sort_order` | integer | >= 0 | no | none | 0 | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
## `product-types.csv`
Maps to `ms_options` with fixed `category = crm_product_type`.
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `product_type_code` | `code`, `value` | lowercase snake_case | unique per org | yes | duplicate check | none | |
| `product_type_name` | `label` | trim | non-empty | yes | none | none | |
| `document_prefix` | `metadata.documentPrefix` | uppercase | optional | no | none | derived from code | Used for sequence suggestion. |
| `sort_order` | `sort_order` | integer | >= 0 | no | none | 0 | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
| `metadata_json` | `metadata` merge | JSON parse | object | no | none | null | Merged after `documentPrefix`. |
## `document-sequences.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `branch_code` | `branch_id` | resolve | option exists when provided | no | `crm_branch` option | empty string | Empty string means generic branch scope. |
| `product_type_code` | `product_type` | normalize | option exists or `all` | no | `crm_product_type` option/code | `all` | Store normalized code, not option id. |
| `document_type` | `document_type` | lowercase | supported by sequence foundation | yes | none | none | |
| `prefix` | `prefix` | trim | non-empty | yes | none | none | |
| `period` | `period` | trim | matches active period format | yes | none | generated current YYMM later | |
| `current_number` | `current_number` | integer | >= 0 | no | none | 0 | |
| `padding_length` | `padding_length` | integer | 1-12 | no | none | 3 | |
| `format` | `format` | trim | contains `{running}` | no | none | `{prefix}{period}-{running}` | |
| `reset_policy` | `reset_policy` | lowercase | known reset policy | no | none | `period` | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
## `approval-workflows.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `workflow_code` | `code` | lowercase snake_case | unique per org | yes | duplicate check | none | |
| `workflow_name` | `name` | trim | non-empty | yes | none | none | |
| `entity_type` | `entity_type` | lowercase | `quotation` initially | yes | none | `quotation` | |
| `description` | `description` | trim | optional | no | none | null | |
| `is_system` | `is_system` | boolean | true/false | no | none | false | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
## `approval-steps.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `workflow_code` | `workflow_id` | resolve | workflow exists | yes | approval workflow | none | |
| `step_number` | `step_number` | integer | > 0 | yes | none | none | |
| `role_code` | `role_code` | lowercase | role code known | yes | role profile optional check | none | |
| `role_name` | `role_name` | trim | non-empty | yes | none | label from role profile | |
| `approval_mode` | `approval_mode` | lowercase | known mode | no | none | `sequential` | |
| `is_required` | `is_required` | boolean | true/false | no | none | true | |
| `sla_hours` | `sla_hours` | integer | >= 0 | no | none | 24 | |
| `calendar_mode` | `calendar_mode` | lowercase | known mode | no | none | `calendar_days` | |
| `timeout_action` | `timeout_action` | lowercase | known action | no | none | `none` | |
## `approval-matrix.csv`
| CSV Column | Target Field | Transform | Validation | Required | Reference Resolver | Fallback | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `organization_code` | `organization_id` | resolve | org exists | yes | organization resolver | setup org | |
| `entity_type` | `entity_type` | lowercase | `quotation` initially | yes | none | quotation | |
| `workflow_code` | `workflow_id` | resolve | workflow exists | yes | approval workflow | none | |
| `branch_code` | `branch_id` | resolve | optional branch exists | no | branch option | null | Null means any branch. |
| `product_type_code` | `product_type` | normalize | optional product type exists | no | product type option/code | null | |
| `min_amount` | `min_amount` | decimal | <= max when both exist | no | none | null | |
| `max_amount` | `max_amount` | decimal | >= min when both exist | no | none | null | |
| `currency` | `currency` | uppercase | currency option exists | no | `crm_currency` | null | |
| `priority` | `priority` | integer | >= 0 | yes | none | 100 | |
| `is_default` | `is_default` | boolean | true/false | yes | none | false | |
| `is_active` | `is_active` | boolean | true/false | no | none | true | |
| `description` | `description` | trim | optional | no | none | null | |
## CRM Business CSVs
The following mappings use the same resolver pattern:
| CSV File | Business Key | Required Reference Resolvers | Important Option Resolvers | Generated Fields | Notes |
| --- | --- | --- | --- | --- | --- |
| `customers.csv` | `organization_code + customer_code` | organization, branch, owner user, created_by user | `crm_customer_type`, `crm_customer_status`, `crm_customer_group`, `crm_customer_sub_group`, `crm_lead_channel` | `id`, timestamps, owner assignment timestamps | Owner history should be generated by service behavior. |
| `contacts.csv` | `organization_code + customer_code + email/contact_name` | organization, customer, created_by user | none | `id`, timestamps | Contact must belong to resolved customer. |
| `contact-shares.csv` | `organization + contact + shared_to_user` | organization, customer, contact, shared users | none | `id`, `shared_at`, timestamps | Target user must have organization membership. |
| `leads.csv` | `organization_code + lead_code` | organization, branch, customer, contact, creator, marketing owner, sales assignee | `crm_lead_status`, `crm_lead_followup_status`, `crm_lead_lost_reason`, `crm_product_type`, `crm_priority`, `crm_lead_awareness`, `crm_lead_channel` | `id`, timestamps, assignment timestamps | Lead outcome defaults to `open`. |
| `opportunities.csv` | `organization_code + opportunity_code` | organization, lead, customer, contact, creator, updater, assignee | `crm_product_type`, `crm_opportunity_status`, `crm_priority`, `crm_lead_channel`, outcome/cancel/no-quotation reasons | `id`, timestamps, assignment/lifecycle timestamps | Validate contact/customer and optional lead lineage. |
| `opportunity-parties.csv` | `opportunity + customer + role_code` | organization, opportunity, customer | `crm_project_party_role` | `id`, timestamps | Used by revenue attribution. |
| `quotations.csv` | `organization_code + quotation_code` | organization, opportunity, customer, contact, branch, users, parent quotation | `crm_quotation_type`, `crm_quotation_status`, `crm_currency`, `crm_discount_type`, `crm_sent_via` | `id`, timestamps | Runtime approval/PDF fields must not be accepted from CSV. |
| `quotation-items.csv` | `quotation + item_number` | organization, quotation | `crm_product_type`, `crm_unit`, `crm_discount_type` | `id`, timestamps | Validate numeric price fields. |
| `quotation-parties.csv` | `quotation + customer + role_code` | organization, quotation, customer | `crm_project_party_role` | `id`, timestamps | Used by quotation reporting/PDF context. |
| `quotation-topics.csv` | `quotation + topic_key` | organization, quotation | `crm_quotation_topic_type` | `id`, timestamps | `topic_key` is import-only alias. |
| `quotation-topic-items.csv` | `quotation + topic_key + sort_order` | organization, quotation topic by import alias or existing lookup | none | `id`, timestamps | Topic must be preview-resolved before commit. |
Business CSV field transforms:
- Dates: accept `YYYY-MM-DD`; API responses remain ISO UTC.
- Booleans: accept `true,false,1,0,yes,no`; normalize to boolean.
- Amounts: parse decimal with `.`; reject thousands separators in strict mode.
- Pipe lists: split on `|`, trim, reject empty tokens in strict mode.
- Empty string: normalize to `null` for nullable DB fields.
Business CSV fallback rules:
- `created_by_email` may fall back to setup actor only when the CSV file explicitly omits it and the import run is wizard-owned.
- `updated_by_email` may fall back to `created_by_email`.
- `branch_code` may fall back to the wizard default branch.
- `currency` may fall back to organization default currency once that is persisted in setup state or organization metadata.
- Option fields should not silently create new options unless import mode is `allowOptionCreate`.
## Unsupported Mapping Decisions
| CSV | Mapping Decision |
| --- | --- |
| `departments.csv` | Block. No first-class schema. |
| `teams.csv` | Block. No first-class team graph; security docs note team-scope limitations. |
| `warehouses.csv` | Block. No first-class schema. |
| `sites.csv` | Block. Current project location is text on lead/opportunity/quotation. |
| `services.csv` | Block. No first-class service catalog. |
| `price-books.csv` | Block. No price book foundation. |
| `tax-rates.csv` | Block. No tax rate table; quotation/item rows carry tax numbers. |
| `attachments.csv` | Defer. Needs binary upload/storage import design, not pure CSV. |
| `email-settings.csv` | Block. No secure first-class email settings table. |

View File

@@ -0,0 +1,32 @@
# Reset and Reseed Matrix
All reset operations must be blocked in production unless an explicit product decision introduces a safe production-specific reset path. Existing reset guards in `scripts/db/reset-database.ts` and `scripts/seed-reset.ts` must be preserved.
| Scope | Tables Affected | Data Retained | Data Deleted | Safety Guard | Requires Confirmation | Allowed Environment | Transaction Behavior | Storage Cleanup Behavior | Audit Behavior | Recommended Command/API |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `foundation_reset` | `ms_options`, `document_sequences`, `crm_approval_workflows`, `crm_approval_steps`, `crm_approval_matrices`, document template/library/report/notification foundation rows | Users, organizations, memberships, business data where compatible | Foundation rows for target organization only | Block production destructive mode; require target org | yes | local, test, staging with flag | Single DB transaction where possible | Document library storage cleanup must be planned and reported; do not delete approved artifacts | Audit setup reset action | `POST /api/setup/reset` with scope |
| `organization_reset` | `organizations`, `memberships`, CRM role assignments/profiles for target org, organization-scoped foundation rows | Global users unless explicitly selected | Target organization and tenant-scoped rows | Require exact organization code/name confirmation | yes | local/test/staging with flag | Transaction for DB rows | Cleanup only setup-owned storage prefix; report leftovers | Audit setup reset action | future setup reset service |
| `business_reset` | customers, contacts, contact shares, leads, opportunities, quotations, related parties/items/topics/followups, business attachments metadata | Organization, users, memberships, foundation rows | Business records for target organization | Require target org and business reset phrase | yes | local/test/staging with flag | Transaction for DB rows | Delete only non-approved setup/business import files when explicitly selected | Audit reset summary | future setup reset service |
| `demo_uat_reset` | UAT users, UAT memberships, UAT CRM role assignments, UAT-seeded CRM business/runtime fixture rows | Production foundation rows and non-UAT business data | Rows tagged/deterministically known from `uat-users.seed.ts` and `crm-uat.seed.ts` | Require demo/UAT mode and seed tag | yes | local/test/staging | Transaction for DB rows | Remove demo-generated storage objects under demo prefix only | Audit reset summary | extend `scripts/seed-uat.ts`/future API |
| `full_reset` | Entire public/drizzle schemas through existing reset script | Nothing in target database | All DB data | Existing `--allow-db-reset`; production blocked; unsafe host/db blocked | yes | local/test only by default | Drop/recreate schema | Storage cleanup is out-of-band and must be explicit | Console/report only before setup audit exists | `npm run db:reset -- --allow-db-reset` |
| `csv_import_rollback` | Rows from one import manifest | Data outside import manifest | Imported/updated rows when reversible metadata exists | Requires seed manifest item checksums | yes | local/test/staging; production only if designed later | Transaction if rollback immediately after failed commit; compensating actions otherwise | Cleanup files imported by same manifest only | Audit rollback action | future setup import rollback service |
| `setup_run_reset` | proposed `setup_runs`, `setup_run_steps`, `seed_manifests`, `seed_manifest_items` | Actual business/foundation data unless separate scope selected | Setup run metadata | Require setup run id | yes | all non-production; production only metadata reset by super admin later | Transaction | No storage cleanup | Audit metadata reset | future setup state service |
## General Reset Rules
- Never rely on client-side confirmation alone.
- Require exact typed confirmation for destructive scopes.
- Refuse reset when `NODE_ENV=production` unless a future ADR defines an allowed production maintenance workflow.
- Use organization-scoped deletes whenever possible.
- Runtime generated approved artifacts must not be deleted by `business_reset` unless they are demo/UAT artifacts and the scope explicitly includes them.
- Storage cleanup must be best-effort with a compensation report because storage providers do not participate in DB transactions.
- Reset reports must list retained data, deleted data, skipped data, warnings, and errors.
## Reseed Behavior
- Foundation reseed must be idempotent.
- Demo/UAT reseed must be deterministic and isolated from production seed.
- Same seed version and checksum should skip unless `force` is explicitly set.
- Same seed version with changed checksum should warn or block.
- Newer compatible seed versions may apply.
- Older versions should block unless rollback mode exists.

82
docs/setup/seed-matrix.md Normal file
View File

@@ -0,0 +1,82 @@
# Setup Seed Matrix
Source task: `plans/task-d.7.1.md`
Source audit: `docs/implementation/task-d7-initial-system-setup-audit.md`
Seed type values:
- `foundation`: shared system or CRM foundation data required before normal app use.
- `organization`: tenant-specific setup data.
- `business`: customer, sales, quotation, and related master/business data.
- `demo_uat`: deterministic demo or UAT fixture data.
- `runtime_generated`: data generated by approved workflows or system actions.
- `runtime_only`: operational data that should not be seeded directly.
- `unsupported_pending_schema`: requested concept that does not currently have a first-class schema.
| Module | Entity | Table / Storage | Seed Type | Required For First Login | Required For CRM | Required For Quotation | CSV Importable | Wizard Editable | Auto Generated | Source Truth | Dependencies | Import Order | Reset Scope | Notes |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Core | User | `users` | organization | yes | yes | yes | yes | yes | no | CSV/Wizard/Auth seed | none | 01 | organization_reset | First administrator must exist before organization-owned foundation setup can complete. |
| Core | Organization | `organizations` | organization | yes | yes | yes | yes | yes | no | CSV/Wizard | created_by_user | 02 | organization_reset | Current system seed creates `ALLA Demo`; wizard must accept real organization input. |
| Core | Membership | `memberships` | organization | yes | yes | yes | yes | yes | no | CSV/Wizard | user, organization | 03 | organization_reset | Grants app-level organization access. |
| Authorization | CRM role profile | `crm_role_profiles` | foundation | no | yes | yes | no | limited | no | `src/lib/auth/rbac.ts` + role foundation | organization, created_by_user | 04 | foundation_reset | Seed from code-defined defaults; import custom profiles later only after governance. |
| Authorization | CRM user role assignment | `crm_user_role_assignments` | organization | no | yes | yes | yes | yes | no | CSV/Wizard | user, organization, role profile, branch/product options | 05 | organization_reset | Primary CRM authorization source per ADR-0014. |
| Foundation | Master option | `ms_options` | foundation | no | yes | yes | yes | yes | no | Foundation seed/CSV | organization, created_by_user | 06 | foundation_reset | Shared option table for many governed CRM dropdowns. |
| Foundation | Branch | `ms_options(category=crm_branch)` | organization | no | yes | yes | yes | yes | no | CSV/Wizard | organization, created_by_user | 07 | organization_reset | Branch is currently modeled as a master option, not a branch table. |
| Foundation | Product type | `ms_options(category=crm_product_type)` | organization | no | yes | yes | yes | yes | no | CSV/Wizard | organization, created_by_user | 08 | organization_reset | Used by CRM scope and document sequence resolution. |
| Foundation | Currency | `ms_options(category=crm_currency)` | foundation | no | yes | yes | yes | yes | no | Foundation seed/CSV | organization | 09 | foundation_reset | Default should include THB. |
| Foundation | Unit | `ms_options(category=crm_unit)` | foundation | no | no | yes | yes | yes | no | Foundation seed/CSV | organization | 10 | foundation_reset | Used by quotation items. |
| Foundation | Status and reason options | `ms_options` status/reason categories | foundation | no | yes | yes | yes | limited | no | Foundation seed/CSV | organization | 11 | foundation_reset | Includes customer, lead, opportunity, quotation, lost/cancel/no-quotation statuses. |
| Foundation | Job title | `ms_options(category=crm_job_title)` | foundation | no | yes | yes | yes | limited | no | Foundation seed/CSV | organization | 12 | foundation_reset | Used by PDF signature position fallback rules. |
| Foundation | Project party role | `ms_options(category=crm_project_party_role)` | foundation | no | yes | yes | yes | limited | no | Foundation seed/CSV | organization | 13 | foundation_reset | Required by revenue attribution and quotation parties. |
| Document sequence | Document sequence | `document_sequences` | foundation | no | yes | yes | yes | yes | no | Foundation seed/Wizard | organization, branch, product type | 14 | foundation_reset | Scoped by organization, branch, product type, document type, period. |
| Approval | Approval workflow | `crm_approval_workflows` | foundation | no | no | yes | yes | yes | no | Foundation seed/Wizard | organization, created_by_user | 15 | foundation_reset | Quotation approval depends on this. |
| Approval | Approval step | `crm_approval_steps` | foundation | no | no | yes | yes | yes | no | Foundation seed/Wizard | approval workflow | 16 | foundation_reset | Defines sequential actors and SLA metadata. |
| Approval | Approval matrix | `crm_approval_matrices` | foundation | no | no | yes | yes | yes | no | Foundation seed/Wizard | approval workflow, branch, product type | 17 | foundation_reset | Resolves workflow by amount/scope. |
| Document template | Template | `crm_document_templates` | foundation | no | no | yes | no | limited | no | Foundation seed/document-template foundation | organization, created_by_user | 18 | foundation_reset | Approved path for template metadata. |
| Document template | Template version | `crm_document_template_versions` | foundation | no | no | yes | no | limited | no | Foundation seed/document-template foundation | template | 19 | foundation_reset | Must have one active PDFME version for quotation. |
| Document template | Template mapping | `crm_document_template_mappings` | foundation | no | no | yes | no | limited | no | Foundation seed/PDF registry | template version | 20 | foundation_reset | DB mapping for PDF placeholders. |
| Document template | Template table column | `crm_document_template_table_columns` | foundation | no | no | yes | no | limited | no | Foundation seed/PDF registry | template mapping | 21 | foundation_reset | Required by table placeholders. |
| Document library | Document library | `crm_document_libraries` | foundation | no | no | yes | no | limited | no | Foundation seed/document-library foundation | organization, storage provider | 22 | foundation_reset | Metadata for reusable document library files. |
| Document library | Document library version | `crm_document_library_versions` + storage object | foundation | no | no | yes | no | limited | partly | Foundation seed/storage provider | document library, storage provider | 23 | foundation_reset | Storage write cannot be DB-transactional; setup report must capture compensation needs. |
| Notifications | Notification template | `app_notification_templates` | foundation | no | yes | yes | no | limited | no | Foundation seed/notification foundation | organization, created_by_user | 24 | foundation_reset | Required for approval in-app notifications. |
| Reports | Report definition | `crm_report_definitions` | foundation | no | yes | no | no | limited | no | Foundation seed/report foundation | organization | 25 | foundation_reset | Required by report catalog. |
| CRM | Customer | `crm_customers` | business | no | yes | yes | yes | yes | no | CSV/CRM service | organization, branch, owner user, master options | 30 | business_reset | Must use customer service/audit rules in implementation. |
| CRM | Customer owner history | `crm_customer_owner_history` | runtime_generated | no | yes | yes | no | no | yes | Customer service | customer, owner user | 31 | business_reset | Generated when owner changes; UAT seed currently writes initial history. |
| CRM | Contact | `crm_customer_contacts` | business | no | yes | yes | yes | yes | no | CSV/CRM service | organization, customer | 32 | business_reset | Contact visibility is governed by customer/contact sharing rules. |
| CRM | Contact share | `crm_contact_shares` | business | no | yes | optional | yes | yes | no | CSV/CRM service | organization, contact, shared user | 33 | business_reset | Must preserve ADR-0015 visibility model. |
| CRM | Lead | `crm_leads` | business | no | yes | optional | yes | yes | no | CSV/CRM service | organization, branch, customer/contact optional, owner/assignee users, options | 34 | business_reset | Dedicated marketing-owned lead table per ADR-0018 direction. |
| CRM | Lead followup | audit payload in `tr_audit_logs` today | runtime_generated | no | yes | no | no | no | yes | Lead service | lead, user | 35 | business_reset | No first-class lead followup table currently identified. |
| CRM | Opportunity | `crm_opportunities` | business | no | yes | yes | yes | yes | no | CSV/CRM service | organization, customer, contact, lead optional, options, assignee | 36 | business_reset | Active sales opportunity domain. |
| CRM | Opportunity party | `crm_opportunity_customers` | business | no | yes | yes | yes | yes | no | CSV/CRM service | opportunity, customer, project party role | 37 | business_reset | Used by revenue attribution. |
| CRM | Opportunity followup | `crm_opportunity_followups` | business | no | yes | optional | no | yes | no | Opportunity service | opportunity, contact, followup option | 38 | business_reset | Could be CSV-supported later; D.7.1 does not require a template. |
| CRM | Opportunity attachment | `crm_opportunity_attachments` + storage/file path | business | no | yes | optional | no | yes | partly | Opportunity service/storage foundation | opportunity, storage | 39 | business_reset | File import deferred until upload transport rules are specified. |
| Quotation | Quotation | `crm_quotations` | business | no | yes | yes | yes | yes | no | CSV/Quotation service | organization, opportunity optional, customer, contact, branch, options, salesman | 40 | business_reset | Must preserve revision/approval/document behavior. |
| Quotation | Quotation item | `crm_quotation_items` | business | no | no | yes | yes | yes | no | CSV/Quotation service | quotation, product type, unit, discount type | 41 | business_reset | Not a price-book row; item text/pricing belong to quotation. |
| Quotation | Quotation party | `crm_quotation_customers` | business | no | yes | yes | yes | yes | no | CSV/Quotation service | quotation, customer, project party role | 42 | business_reset | Billing/end customer rules affect reports. |
| Quotation | Quotation topic | `crm_quotation_topics` | business | no | no | yes | yes | yes | no | CSV/Quotation service | quotation, topic type | 43 | business_reset | Parent for topic items. |
| Quotation | Quotation topic item | `crm_quotation_topic_items` | business | no | no | yes | yes | yes | no | CSV/Quotation service | quotation topic | 44 | business_reset | Ordered terms/scope rows. |
| Quotation | Quotation followup | `crm_quotation_followups` | business | no | yes | yes | no | yes | no | Quotation service | quotation, contact, followup option | 45 | business_reset | Could be CSV-supported later; D.7.1 does not require a template. |
| Quotation | Quotation attachment | `crm_quotation_attachments` + storage/file path | business | no | optional | optional | no | yes | partly | Quotation service/storage foundation | quotation, storage | 46 | business_reset | `attachments.csv` deferred because binary transport is not generic CSV. |
| Approval runtime | Approval request | `crm_approval_requests` | runtime_generated | no | no | yes | no | no | yes | Approval service | quotation, approval workflow | 50 | demo_uat_reset | Production requests are runtime data; demo seed may generate fixtures. |
| Approval runtime | Approval action | `crm_approval_actions` | runtime_generated | no | no | yes | no | no | yes | Approval service | approval request, actor | 51 | demo_uat_reset | Production actions are runtime audit/workflow records. |
| Audit | Audit log | `tr_audit_logs` | runtime_only | no | no | no | no | no | yes | Audit foundation | actor, entity | 60 | full_reset | Should be created by services, not CSV import. |
| Notifications | Notification event | `app_notification_events` | runtime_only | no | no | no | no | no | yes | Notification foundation | entity/action | 61 | full_reset | Runtime queue/event state. |
| Notifications | User notification | `app_notifications` | runtime_only | no | no | no | no | no | yes | Notification foundation | notification event, recipient | 62 | full_reset | User-facing runtime inbox state. |
| Notifications | Dispatch | `app_notification_dispatches` | runtime_only | no | no | no | no | no | yes | Notification foundation | notification event/template | 63 | full_reset | Provider dispatch runtime state. |
| Notifications | Delivery | `app_notification_deliveries` | runtime_only | no | no | no | no | no | yes | Notification foundation | notification | 64 | full_reset | Delivery runtime state. |
| PDF/storage | Document artifact | `crm_document_artifacts` + storage object | runtime_generated | no | no | yes | no | no | yes | Document artifact/PDF/storage foundations | entity, template, storage | 65 | explicit_artifact_reset | Approved documents are immutable-first; do not delete casually. |
| Storage | Uploaded file | storage provider + attachment metadata | runtime_generated | no | optional | optional | no | no | partly | Storage foundation | entity, uploader | 66 | explicit_storage_reset | Storage cleanup needs compensation/reporting. |
| Setup | Setup run | proposed `setup_runs` | foundation | no | no | no | no | no | yes | Setup foundation | actor | 70 | setup_run_reset | Proposed for resume/reporting; not implemented yet. |
| Setup | Setup run step | proposed `setup_run_steps` | foundation | no | no | no | no | no | yes | Setup foundation | setup run | 71 | setup_run_reset | Proposed for step state and error history. |
| Setup | Seed manifest | proposed `seed_manifests` | foundation | no | yes | yes | no | no | yes | Setup foundation | organization optional | 72 | setup_run_reset | Proposed for seed lifecycle tracking. |
| Setup | Seed manifest item | proposed `seed_manifest_items` | foundation | no | yes | yes | no | no | yes | Setup foundation | seed manifest | 73 | setup_run_reset | Proposed for checksum and item-level report. |
| Demo | UAT users | `users`, `memberships`, `crm_user_role_assignments` | demo_uat | no | no | no | no | no | no | `src/db/seeds/uat-users.seed.ts` | foundation seed | 80 | demo_uat_reset | Separate from production foundation install. |
| Demo | UAT CRM data | CRM business/runtime tables | demo_uat | no | no | no | no | no | no | `src/db/seeds/crm-uat.seed.ts` | UAT users, foundation seed | 81 | demo_uat_reset | Deterministic demo stories. |
| Unsupported | Department | none identified | unsupported_pending_schema | no | no | no | no | no | no | pending ADR/schema | organization | 90 | none | Do not implement as first-class setup data without schema decision. |
| Unsupported | Team | none identified | unsupported_pending_schema | no | no | no | no | no | no | pending ADR/schema | organization | 91 | none | Team-scope limitations are documented in security docs. |
| Unsupported | Warehouse | none identified | unsupported_pending_schema | no | no | no | no | no | no | pending ADR/schema | organization | 92 | none | No warehouse table reviewed. |
| Unsupported | Site | none identified | unsupported_pending_schema | no | optional | optional | no | no | no | pending ADR/schema | customer/opportunity | 93 | none | Project location is currently stored on lead/opportunity/quotation text fields. |
| Unsupported | Service catalog | none identified | unsupported_pending_schema | no | no | optional | no | no | no | pending ADR/schema | organization | 94 | none | Current quotation item descriptions are free-form. |
| Unsupported | Price book | none identified | unsupported_pending_schema | no | no | optional | no | no | no | pending ADR/schema | product/service catalog | 95 | none | Do not conflate with generic starter `products` table. |
| Unsupported | Tax rate table | none identified | unsupported_pending_schema | no | no | optional | no | no | no | pending ADR/schema | organization/currency | 96 | none | Quotation tax rate is currently stored on quotation/item rows. |
| Configuration | Email provider config | environment/provider settings | unsupported_pending_schema | no | optional | optional | no | limited | no | environment | deployment config | 97 | none | Verification can check env readiness; no first-class table identified. |

View File

@@ -0,0 +1,136 @@
# Seed Version and Manifest Strategy
Seed manifests make setup repeatable, auditable, and safe to resume.
## Seed Package Model
| Field | Meaning |
| --- | --- |
| `name` | Stable package name, such as `system-core`, `crm-foundation`, `crm-demo-uat`. |
| `version` | Semantic version for the seed package. |
| `type` | `foundation`, `organization`, `business`, `demo_uat`, or `setup_state`. |
| `dependencies` | Package names and compatible versions required before this package. |
| `checksum` | Checksum over manifest content and referenced item checksums. |
| `appliedAt` | Timestamp when applied. |
| `appliedBy` | User id or setup actor id. |
| `targetOrganization` | Organization id/code when organization-scoped. |
| `source` | `script`, `wizard`, `csv`, or `plugin`. |
| `status` | `pending`, `applied`, `skipped`, `warning`, `failed`, `rolled_back`. |
| `report` | Import/seed report with counts, warnings, errors, and storage compensation notes. |
## Manifest Format
```json
{
"name": "crm-foundation",
"version": "1.0.0",
"type": "foundation",
"dependencies": ["system-core@1.0.0"],
"targetOrganization": "ALLA",
"source": "wizard",
"items": [
{
"file": "master-options.csv",
"checksum": "sha256:...",
"required": true,
"importOrder": "06"
},
{
"file": "document-sequences.csv",
"checksum": "sha256:...",
"required": true,
"importOrder": "14"
}
]
}
```
## Package Naming
| Package | Type | Owner |
| --- | --- | --- |
| `system-core` | organization | setup foundation |
| `crm-foundation` | foundation | foundation seed services |
| `crm-organization` | organization | setup wizard/import |
| `crm-business-import` | business | CSV import engine |
| `crm-demo-uat` | demo_uat | UAT seed services |
| `document-template-foundation` | foundation | document-template/PDF foundations |
| `notification-foundation` | foundation | notification foundation |
| `report-foundation` | foundation | CRM report foundation |
## Version Rules
| Existing Manifest | Incoming Manifest | Behavior |
| --- | --- | --- |
| none | any valid version | Apply when dependencies pass. |
| same version + same checksum | same content | Skip and report already applied. |
| same version + changed checksum | changed content | Warn/block unless `force` is true and package allows mutable same-version seed. |
| older version | newer compatible version | Apply upgrade path after compatibility check. |
| newer version | older version | Block unless explicit rollback mode exists. |
| failed | same checksum | Allow retry from failed item when service is idempotent. |
| rolled back | newer version | Apply only after dependency and cleanup verification. |
## Checksum Rules
- Item checksum uses canonical CSV bytes after line-ending normalization.
- Manifest checksum includes sorted item checksums and manifest metadata except `appliedAt`, `appliedBy`, and runtime report.
- Generated wizard inputs should be serialized into canonical JSON and hashed as an item, for example `wizard-organization.json`.
- Storage object checksums should be recorded separately because storage writes cannot be DB-transactional.
## Dependency Rules
- Dependencies use package names and semver ranges, such as `crm-foundation@^1.0.0`.
- A dependency is satisfied when a successful manifest with compatible version/checksum exists for the same target organization or global scope.
- Missing required dependencies block apply.
- Optional dependencies produce warnings.
## Report Contract
Every seed package apply should produce:
```json
{
"imported": 0,
"updated": 0,
"skipped": 0,
"errors": [],
"warnings": [],
"storage": {
"createdObjects": [],
"cleanupWarnings": []
}
}
```
## Persistence Strategy
Use proposed tables:
- `seed_manifests`
- `seed_manifest_items`
D.7.1 does not add migrations. D.7.8 should implement persistence after setup APIs and CSV import behavior are stable.
## Upgrade Behavior
1. Resolve target organization.
2. Load existing manifests for target scope.
3. Validate package dependencies.
4. Compute incoming checksums.
5. Compare version/checksum behavior table.
6. Dry-run item operations.
7. Apply in import order.
8. Store manifest and item reports in the same DB transaction where possible.
9. Store storage compensation report after non-transactional storage operations.
## Rollback Behavior
Rollback is not a default seed feature. It requires:
- previous manifest item reports
- reversible upsert strategy
- explicit reset scope
- storage cleanup strategy
- audit trail
Until implemented, use scoped reset/reseed rather than pretending arbitrary rollback is safe.

View File

@@ -0,0 +1,161 @@
# Setup Plugin Architecture
Goal: future modules can register setup behavior without editing the core wizard for every feature.
This is a design proposal only. No plugin runtime is implemented in D.7.1.
## Plugin Contract
```ts
interface SetupPlugin {
moduleId: string;
displayName: string;
dependencies: string[];
enabledCondition: SetupEnabledCondition;
setupSteps: SetupStepDefinition[];
csvTemplates: CsvTemplateDefinition[];
seedTasks: SeedTaskDefinition[];
verificationChecks: VerificationCheckDefinition[];
resetScopes: ResetScopeDefinition[];
}
```
## Fields
| Field | Purpose |
| --- | --- |
| `moduleId` | Stable identifier, for example `crm.quotation`. |
| `displayName` | Human-readable module name in setup UI. |
| `dependencies` | Other module ids or seed package versions required first. |
| `enabledCondition` | Function/config deciding if module applies to this installation. |
| `setupSteps` | Optional wizard steps or sections contributed by the module. |
| `csvTemplates` | CSV templates owned by the module. |
| `seedTasks` | Idempotent seed operations. |
| `verificationChecks` | Checks added to the verification matrix. |
| `resetScopes` | Reset/reseed behavior owned by the module. |
## Example Plugin Manifest
```json
{
"moduleId": "crm.quotation",
"displayName": "CRM Quotation",
"dependencies": ["core.foundation", "crm.foundation", "crm.approval"],
"setupSteps": ["document-sequences", "approval-workflow", "csv-import"],
"csvTemplates": ["quotations.csv", "quotation-items.csv", "quotation-parties.csv"],
"seedTasks": ["quotation-document-sequences", "quotation-template-mappings"],
"verificationChecks": ["SEQUENCE_QUOTATION", "APPROVAL_WORKFLOW", "PDF_TEMPLATE_ASSET"],
"resetScopes": ["business_reset", "demo_uat_reset"]
}
```
## Core Modules
| Module | Owns |
| --- | --- |
| `core.foundation` | Readiness, users, organizations, memberships, setup state, seed manifests. |
| `crm.foundation` | Master options, branch/product type scopes, CRM role profiles, CRM assignments. |
| `crm.customer` | Customers, contacts, contact sharing, customer ownership verification. |
| `crm.lead` | Lead import, lead sequence verification, lead master options. |
| `crm.opportunity` | Opportunity import, parties, followups, outcome lifecycle checks. |
| `crm.quotation` | Quotations, items, parties, topics, quotation sequences. |
| `crm.approval` | Workflows, steps, matrices, approval verification. |
| `document` | Templates, template versions, mappings, libraries. |
| `notification` | Notification templates and provider readiness checks. |
| `reporting` | Report definitions and report catalog verification. |
| `future_asset` | Placeholder for asset/warehouse/catalog setup after schema decisions. |
| `future_hr` | Placeholder for teams/departments after schema decisions. |
## Execution Order
1. Load core plugins.
2. Resolve enabled modules.
3. Build dependency graph.
4. Detect cycles.
5. Sort setup steps by dependency and declared order.
6. Merge CSV templates and reject duplicate file ownership.
7. Merge verification checks and reset scopes.
8. Render wizard steps by module and step group.
## Cycle Rules
- A plugin cannot depend on itself.
- Cycles block setup startup with a developer-facing error.
- Optional dependencies must not introduce ordering ambiguity.
## Extension Points
### Setup Step
```ts
interface SetupStepDefinition {
key: string;
title: string;
required: boolean;
inputsSchema: unknown;
previewAction?: string;
commitAction?: string;
produces: string[];
invalidates: string[];
}
```
### CSV Template
```ts
interface CsvTemplateDefinition {
fileName: string;
requiredColumns: string[];
optionalColumns: string[];
importOrder: string;
targetService: string;
unsupportedReason?: string;
}
```
### Seed Task
```ts
interface SeedTaskDefinition {
key: string;
packageName: string;
version: string;
idempotent: boolean;
dependencies: string[];
dryRunAction: string;
commitAction: string;
}
```
### Verification Check
```ts
interface VerificationCheckDefinition {
checkId: string;
area: string;
requiredFor: string[];
runAction: string;
suggestedFix: string;
}
```
### Reset Scope
```ts
interface ResetScopeDefinition {
key: string;
destructive: boolean;
allowedEnvironments: string[];
requiresTypedConfirmation: boolean;
affectedTables: string[];
storageCleanup: "none" | "report_only" | "best_effort";
}
```
## Safety Rules
- Plugins can register behavior but cannot bypass setup core authorization.
- Plugin seed tasks must be idempotent or marked unsafe.
- Plugin reset scopes must declare affected tables and storage behavior.
- Plugin CSV templates must declare ownership to avoid two modules writing the same file contract.
- Setup core owns persistence of run state, manifests, audit events, and final reports.

View File

@@ -0,0 +1,155 @@
# Setup State Machine
D.7.1 does not implement persistence. This document proposes the model and transitions for D.7.8.
## States
```text
not_started
readiness_checked
foundation_installed
organization_created
administrator_created
scope_configured
sequences_configured
approval_configured
csv_validated
csv_imported
verified
completed
failed
cancelled
```
## Events
```text
CHECK_READINESS
INSTALL_FOUNDATION
CREATE_ORGANIZATION
CREATE_ADMIN
CONFIGURE_SCOPE
CONFIGURE_SEQUENCES
CONFIGURE_APPROVAL
VALIDATE_CSV
IMPORT_CSV
RUN_VERIFICATION
COMPLETE_SETUP
FAIL_STEP
CANCEL_SETUP
RESUME_SETUP
RESET_STEP
```
## Transition Rules
| From | Event | To | Guard |
| --- | --- | --- | --- |
| `not_started` | `CHECK_READINESS` | `readiness_checked` | Required readiness checks pass or warnings are acknowledged. |
| `readiness_checked` | `INSTALL_FOUNDATION` | `foundation_installed` | Migrations valid and seed transaction succeeds. |
| `foundation_installed` | `CREATE_ORGANIZATION` | `organization_created` | Organization is created or resolved. |
| `organization_created` | `CREATE_ADMIN` | `administrator_created` | User, membership, and CRM role assignment resolve. |
| `administrator_created` | `CONFIGURE_SCOPE` | `scope_configured` | At least one branch and product type exists. |
| `scope_configured` | `CONFIGURE_SEQUENCES` | `sequences_configured` | Required sequence previews succeed. |
| `sequences_configured` | `CONFIGURE_APPROVAL` | `approval_configured` | Quotation workflow, steps, and matrix resolve. |
| `approval_configured` | `VALIDATE_CSV` | `csv_validated` | Preview has no blocking errors, or no CSV files were selected. |
| `csv_validated` | `IMPORT_CSV` | `csv_imported` | Commit succeeds or CSV import skipped. |
| `csv_imported` | `RUN_VERIFICATION` | `verified` | Required verification checks pass. |
| `verified` | `COMPLETE_SETUP` | `completed` | No blocking failures remain. |
| any non-completed state | `FAIL_STEP` | `failed` | Step returns blocking error. |
| any non-completed state | `CANCEL_SETUP` | `cancelled` | User confirms cancellation. |
| `failed` | `RESUME_SETUP` | prior recoverable state | Failure source still recoverable. |
| completed step state | `RESET_STEP` | earliest affected state | Explicit confirmation and reset scope guard pass. |
## Proposed Persistence
### `setup_runs`
Purpose: one row per setup attempt.
Fields:
- `id`
- `status`
- `mode`: `production | demo_uat | dry_run`
- `target_organization_id`
- `started_by`
- `completed_by`
- `started_at`
- `completed_at`
- `last_error`
- `metadata`
- timestamps
### `setup_run_steps`
Purpose: step-level status, resume, and report storage.
Fields:
- `id`
- `setup_run_id`
- `step_key`
- `status`
- `input_hash`
- `output_json`
- `errors_json`
- `warnings_json`
- `started_at`
- `completed_at`
- timestamps
### `seed_manifests`
Purpose: seed package lifecycle tracking.
Fields:
- `id`
- `setup_run_id`
- `organization_id`
- `name`
- `version`
- `type`
- `checksum`
- `status`
- `applied_by`
- `applied_at`
- `report_json`
- timestamps
### `seed_manifest_items`
Purpose: item-level seed/import report and checksum tracking.
Fields:
- `id`
- `seed_manifest_id`
- `item_key`
- `source_file`
- `checksum`
- `status`
- `imported_count`
- `updated_count`
- `skipped_count`
- `error_count`
- `warning_count`
- `report_json`
- timestamps
## Staleness Rules
- Changing organization setup makes administrator, scope, sequence, approval, CSV, and verification stale.
- Changing administrator makes verification stale.
- Changing scope makes sequences, approval, CSV, and verification stale.
- Changing sequences makes verification stale.
- Changing approval makes CSV quotations and verification stale.
- Changing CSV preview invalidates committed import unless the source file checksum is unchanged.
- Any committed import makes verification stale.
## Cancellation Rules
- `cancelled` keeps report history.
- Cancellation does not rollback already committed successful steps.
- A cancelled setup can resume only through `RESUME_SETUP` with explicit acknowledgement of committed state.

View File

@@ -0,0 +1,57 @@
# Setup Wizard Blueprint
Target route proposal: `src/app/dashboard/administration/setup/page.tsx`
Target feature proposal: `src/features/setup/**`
UI rules:
- Use dashboard shell and `PageContainer`.
- Use shadcn/ui primitives and app icons through `@/components/icons`.
- Use server-side enforcement for all setup actions.
- Treat client-side step visibility as UX only.
- Do not reuse the existing `/setup` template onboarding component as-is; it is a historical prototype.
| Step Number | Step Name | Purpose | Inputs | Validation | Actions | APIs | Can Skip | Can Go Back | Resume Behavior | Error Handling | Output |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | System Readiness | Prove the environment can safely initialize. | none, optional setup mode | Env variables, DB connectivity, migration status, storage read/write/delete, upload directory, PDF template asset availability, optional email config. | Run read-only checks and record result. | `GET /api/setup/readiness` | no | no | Resume from latest readiness result but allow re-run. | PASS/WARNING/FAIL with suggested fix per check. | Readiness report. |
| 2 | Foundation Install | Install or verify foundation data. | seed mode, target organization context if already created | Foundation seed dependencies present; no pending migration failures. | Run extracted foundation seed services or dry-run. | `POST /api/setup/foundation` | no | yes, before commit only | If already installed, show manifest and allow verify/reapply compatible seed. | Roll back DB transaction on failure; report storage compensation if library write fails. | Foundation seed report. |
| 3 | Organization Setup | Create or update tenant organization. | `organization_name`, `organization_code`, `slug`, `tax_id`, `phone`, `email`, `address`, `default_currency`, `timezone`, `logo` | Name/code/slug required; code uppercase; slug lowercase unique; email format; timezone defaults `Asia/Bangkok`; currency resolves. | Create organization and store setup alias. | `POST /api/setup/organization` | no | yes | Existing run loads created organization and locks destructive fields unless reset. | Field errors; duplicate slug/code error; retry safe. | Organization summary. |
| 4 | Branch & Product Type Setup | Configure current scope foundations. | Branch rows, product type rows, default branch, default product type | At least one active branch and product type; codes unique; prefixes valid. | Upsert `crm_branch` and `crm_product_type` options. | `POST /api/setup/scope` | no | yes | Resume loads imported/created options. | Row-level validation table. | Scope summary and sequence suggestions. |
| 5 | Administrator Setup | Create first administrator and CRM authorization. | Admin name, email, password or existing user, membership role, CRM role, branch/product scope | Email valid; password policy for new user; user membership exists or can be created; role profile resolves. | Create/update user, membership, CRM role assignment. | `POST /api/setup/administrator` | no | yes | Resume masks password and shows resolved user/membership/assignment. | Avoid logging password; show user/role validation errors. | Administrator access summary. |
| 6 | Document Sequence Setup | Configure numbering by organization/branch/product/document type. | Document type, branch, product type, prefix, period, current number, padding, format, reset policy | Required sequence coverage for customer, lead, opportunity, quotation; format contains `{running}`; scope resolves. | Preview and upsert sequences. | `POST /api/setup/document-sequences/preview`, `POST /api/setup/document-sequences` | no | yes | Resume loads existing sequence rows and preview samples. | Per-row validation and conflict warnings. | Sequence preview and seed report. |
| 7 | Approval Workflow Setup | Configure quotation approval. | Workflow header, steps, matrix rows, SLA fields | Workflow active; steps sequential; matrix resolves to workflow; default quotation matrix exists. | Upsert workflow, steps, matrix. | `POST /api/setup/approval/preview`, `POST /api/setup/approval` | no for quotation install | yes | Resume shows active workflow and matrix status. | Matrix conflict/error report. | Approval configuration report. |
| 8 | CSV Import | Import optional master/business data. | CSV files, import mode, upsert mode, partial import toggle | Parseable CSV; required columns; references resolve; duplicates classified; unsupported templates blocked. | Preview, validate, then commit selected files in order. | `POST /api/setup/import/preview`, `POST /api/setup/import/commit` | yes | yes | Resume stores last preview hash and committed manifest. | No commit if preview has blocking errors; partial import only for selected valid files. | Import report. |
| 9 | Verification | Confirm install can operate. | check selection, optional email dry-run recipient | Required checks pass; warnings acknowledged. | Run verification matrix. | `POST /api/setup/verify` | no | yes | Resume shows last verification but requires re-run after mutations. | PASS/WARNING/FAIL with suggested fixes. | Verification report. |
| 10 | Finish & Report | Close setup and generate summary. | acknowledgement, optional export report | Required setup states completed; blocking failures absent. | Mark setup completed, write manifest/report, optionally lock setup. | `POST /api/setup/complete`, `GET /api/setup/report` | no | yes until complete | Completed run becomes read-only except explicit reset. | Completion blocked when required checks fail. | Setup summary, seed summary, import summary, verification report. |
## API Behavior
All setup write APIs should:
- require authenticated administrator or bootstrap setup token depending on system state
- validate request body with Zod or equivalent schema
- call `src/features/setup/server/**` services
- use existing auth, organization, role, seed, storage, approval, sequence, PDF, and audit foundations
- return structured step reports, not raw SQL/provider errors
## Resume Rules
- Setup run state is stored in proposed `setup_runs` and `setup_run_steps`.
- A completed step can be reviewed.
- A completed step can be re-run only when its service supports idempotent upsert and the user confirms conflict behavior.
- Later steps become stale if an earlier dependency step changes.
- Verification must be re-run after foundation, organization, scope, sequence, approval, or CSV commit changes.
## Step Outputs
Every step output should include:
- `status`: `not_started | running | passed | warning | failed | skipped`
- `startedAt`
- `completedAt`
- `summary`
- `items`
- `errors`
- `warnings`
- `nextRecommendedAction`

View File

@@ -0,0 +1,39 @@
# Setup Verification Matrix
Verification checks return `PASS`, `WARNING`, or `FAIL`.
| Check ID | Area | Check Name | Purpose | Required For | Method | Pass Condition | Warning Condition | Fail Condition | Suggested Fix | API / Service |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `ENV_AUTH_SECRET` | Environment | Auth secret present | Ensure Auth.js can sign sessions. | first login | Read server env. | `AUTH_SECRET` exists and length is acceptable. | Weak-looking local/dev secret. | Missing secret. | Set `AUTH_SECRET` in `.env.local` or deployment secrets. | `setup/readiness.service.ts` |
| `ENV_DATABASE_URL` | Environment | Database URL present | Ensure app can connect to PostgreSQL. | all setup | Read server env. | `DATABASE_URL` exists. | Host appears local in non-local environment. | Missing value. | Set `DATABASE_URL`. | `setup/readiness.service.ts` |
| `DB_CONNECTIVITY` | Database | Database connectivity | Prove DB is reachable. | all setup | Open DB connection and `select 1`. | Query succeeds. | High latency. | Connection/query fails. | Verify database host, network, credentials. | `src/lib/db.ts` |
| `DB_MIGRATIONS` | Database | Migration status | Prevent setup against stale schema. | all setup | Check Drizzle migration metadata/schema tables where available. | Schema matches expected tables. | Migration metadata unavailable but required tables exist. | Required tables missing. | Run `npm run db:migrate`. | setup readiness + Drizzle |
| `STORAGE_PROVIDER` | Storage | Storage provider smoke test | Prove seed/document storage can write. | document library/PDF | Write/read/delete test object under setup prefix. | All operations succeed. | Delete cleanup fails but object tracked. | Write/read fails. | Fix local directory/S3 credentials. | `src/features/foundation/storage/**` |
| `UPLOAD_DIRECTORY` | Storage | Upload directory readiness | Prove local provider path is writable. | local storage | Check directory exists/create/write/delete. | Directory writable. | Directory created during check. | Not writable. | Create/fix permissions. | storage provider |
| `PDF_TEMPLATE_ASSET` | PDF | PDF template assets exist | Ensure quotation PDF can render. | quotation | Check active template version/file/schema. | Active version and schema exist. | Template exists but preview asset missing. | No active template version. | Run foundation/PDF template seed. | document-template/pdf foundations |
| `PDF_MAPPING_COVERAGE` | PDF | PDF mappings exist | Ensure placeholders can resolve. | quotation | Count required mappings for active version. | Required mappings present. | Optional mappings missing. | Required mapping missing. | Run PDF mapping seed/audit. | PDF audit services/scripts |
| `EMAIL_CONFIG` | Email | Email configuration | Confirm optional email readiness. | optional notifications | Read env/provider config; optional dry-run. | Provider configured and dry-run succeeds. | Email not configured but in-app notifications are available. | Provider configured but dry-run fails. | Fix SMTP/provider settings. | future notification/email service |
| `ADMIN_USER` | Auth | First administrator exists | Ensure first login actor exists. | first login | Query `users` by setup admin email. | User exists. | User exists but inactive semantics added later. | Missing user. | Create administrator step. | user/auth service |
| `ADMIN_MEMBERSHIP` | Auth | Administrator membership exists | Ensure admin can enter organization. | first login | Query `memberships`. | Membership exists with admin role. | Membership role is user but CRM assignment admin. | Missing membership. | Create membership. | organization/session helpers |
| `ADMIN_CRM_ASSIGNMENT` | Authorization | Administrator CRM assignment resolves | Ensure CRM admin permissions resolve. | CRM | Resolve CRM access. | `crm_admin` or equivalent permissions resolve. | Compatibility business role only, no assignment. | No CRM access. | Seed role profiles and assignment. | `resolveCrmAccess()` |
| `ORG_ACTIVE` | Organization | Organization active/resolvable | Ensure active org context works. | all setup | Resolve organization and active org for admin. | Organization exists and admin active org set. | Organization exists but active org missing. | Organization missing. | Update `users.active_organization_id`. | `requireOrganizationAccess()` |
| `BRANCH_OPTION` | Scope | Branch option exists | Ensure branch scope can resolve. | CRM/quotation | Query `ms_options` category `crm_branch`. | At least one active branch. | Branch exists but no default convention. | No active branch. | Configure branch step. | master-options foundation |
| `PRODUCT_TYPE_OPTION` | Scope | Product type option exists | Ensure product scope can resolve. | CRM/quotation | Query `ms_options` category `crm_product_type`. | At least one active product type. | Product type exists but no document prefix metadata. | No active product type. | Configure product types. | master-options foundation |
| `SEQUENCE_CUSTOMER` | Document sequence | Customer sequence preview | Ensure customer codes can generate. | CRM | Call sequence preview for customer. | Preview returns code. | Generic branch/product scope only. | Preview fails. | Configure document sequence. | document-sequence foundation |
| `SEQUENCE_LEAD` | Document sequence | Lead sequence preview | Ensure lead codes can generate. | CRM | Call sequence preview for `crm_lead`. | Preview returns code. | Generic scope only. | Preview fails. | Configure lead sequence. | document-sequence foundation |
| `SEQUENCE_OPPORTUNITY` | Document sequence | Opportunity sequence preview | Ensure opportunity codes can generate. | CRM | Call sequence preview for `crm_opportunity`. | Preview returns code. | Generic scope only. | Preview fails. | Configure opportunity sequence. | document-sequence foundation |
| `SEQUENCE_QUOTATION` | Document sequence | Quotation sequence preview | Ensure quotation codes can generate. | quotation | Preview by branch/product type. | Preview returns code for each configured product type. | Missing some branch/product combinations. | No quotation sequence works. | Configure quotation sequences. | document-sequence foundation |
| `APPROVAL_WORKFLOW` | Approval | Quotation workflow resolves | Ensure quotation approval can start. | quotation | Query active workflow for quotation. | Active workflow exists. | Multiple active candidates. | Missing workflow. | Configure approval workflow. | approval foundation |
| `APPROVAL_MATRIX` | Approval | Approval matrix resolves | Ensure quotation can select workflow by scope/amount. | quotation | Resolve matrix for sample quotation amount. | Matrix resolves. | Default-only fallback. | No matrix resolves. | Configure approval matrix. | approval foundation |
| `NOTIFICATION_TEMPLATES` | Notifications | Approval notification templates exist | Ensure in-app approval notifications can render. | CRM/quotation | Query `app_notification_templates`. | Required approval templates active. | Some optional templates missing. | Required templates missing. | Run foundation seed. | notification foundation |
| `REPORT_DEFINITIONS` | Reports | Report definitions exist | Ensure report catalog works. | CRM reports | Query `crm_report_definitions`. | Required definitions active. | Definitions exist but categories missing. | No definitions. | Run report foundation seed. | report foundation |
| `CSV_PREVIEW` | Import | CSV preview integrity | Ensure uploaded files were validated before commit. | CSV import | Check latest preview hash/status. | Preview exists and matches commit input hash. | Preview stale after dependency change. | Missing preview for commit. | Re-run CSV preview. | setup import service |
| `SETUP_MANIFEST` | Setup | Seed manifest recorded | Ensure lifecycle tracking exists. | setup completion | Query proposed manifest tables once implemented. | Manifest recorded with checksums. | Manifest not implemented in early slice. | Manifest required but missing. | Implement D.7.8. | setup manifest service |
## Required Completion Rule
Setup can complete only when all required checks for selected setup mode pass:
- Production foundation mode: all first-login, CRM, quotation, storage/PDF required checks must pass except optional email may warn.
- Demo/UAT mode: production required checks plus UAT user/data checks must pass.
- Dry-run mode: failures do not write setup completion, but report can be exported.