task f.3
This commit is contained in:
501
plans/task-f.2.md
Normal file
501
plans/task-f.2.md
Normal file
@@ -0,0 +1,501 @@
|
||||
# Task F.2: Approval Matrix Foundation
|
||||
|
||||
## Status
|
||||
|
||||
Planned
|
||||
|
||||
## Objective
|
||||
|
||||
Build the Approval Matrix foundation so the system can select the correct approval workflow automatically when a quotation is submitted for approval.
|
||||
|
||||
This task upgrades approval from a single static workflow to rule-based workflow resolution.
|
||||
|
||||
The goal is to support approval flow selection by:
|
||||
|
||||
* entity type
|
||||
* product type
|
||||
* branch
|
||||
* quotation amount
|
||||
* default fallback
|
||||
* priority when multiple rules match
|
||||
|
||||
This task does not build the full Workflow Builder UI or Notification system yet.
|
||||
|
||||
---
|
||||
|
||||
## Mandatory Review
|
||||
|
||||
Review before implementation:
|
||||
|
||||
* `AGENTS.md`
|
||||
* `docs/standards/project-foundations.md`
|
||||
* `docs/standards/task-catalog.md`
|
||||
* existing Approval implementation
|
||||
* existing Quotation submit approval flow
|
||||
* `src/db/schema.ts`
|
||||
* `src/features/crm/approval/**`
|
||||
* `src/features/crm/quotations/**`
|
||||
* `src/features/crm/opportunities/**`
|
||||
* `src/features/foundation/audit-log/**`
|
||||
* `src/features/foundation/master-options/**`
|
||||
* `docs/security/crm-authorization-boundaries.md`
|
||||
|
||||
---
|
||||
|
||||
## Current Foundation
|
||||
|
||||
Already available:
|
||||
|
||||
```txt
|
||||
crm_approval_workflows
|
||||
crm_approval_steps
|
||||
crm_approval_requests
|
||||
crm_approval_actions
|
||||
|
||||
quotation_standard_approval
|
||||
quotation submit approval flow
|
||||
quotation approval actions
|
||||
audit foundation
|
||||
```
|
||||
|
||||
Current limitation:
|
||||
|
||||
```txt
|
||||
Quotation approval uses a fixed or first active workflow.
|
||||
```
|
||||
|
||||
This task introduces matrix-based workflow resolution.
|
||||
|
||||
---
|
||||
|
||||
## Business Requirement
|
||||
|
||||
When a quotation is submitted for approval, the system must determine:
|
||||
|
||||
```txt
|
||||
Which approval workflow should this quotation use?
|
||||
```
|
||||
|
||||
The workflow should be selected based on business conditions.
|
||||
|
||||
Example:
|
||||
|
||||
```txt
|
||||
Crane quotation under 500,000
|
||||
→ Crane Standard Approval
|
||||
|
||||
Crane quotation over 5,000,000
|
||||
→ Crane Executive Approval
|
||||
|
||||
Dock Door quotation
|
||||
→ Dock Door Approval
|
||||
|
||||
No specific match
|
||||
→ Default Quotation Approval
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.1 Approval Matrix Schema
|
||||
|
||||
Add table:
|
||||
|
||||
```txt
|
||||
crm_approval_matrices
|
||||
```
|
||||
|
||||
Recommended columns:
|
||||
|
||||
```txt
|
||||
id
|
||||
organization_id
|
||||
entity_type
|
||||
workflow_id
|
||||
branch_id
|
||||
product_type
|
||||
min_amount
|
||||
max_amount
|
||||
currency
|
||||
priority
|
||||
is_default
|
||||
is_active
|
||||
description
|
||||
created_at
|
||||
updated_at
|
||||
deleted_at
|
||||
created_by
|
||||
updated_by
|
||||
```
|
||||
|
||||
Recommended types:
|
||||
|
||||
```txt
|
||||
entity_type text not null
|
||||
workflow_id text not null
|
||||
branch_id text null
|
||||
product_type text null
|
||||
min_amount numeric(14,2) null
|
||||
max_amount numeric(14,2) null
|
||||
currency text null
|
||||
priority integer not null default 100
|
||||
is_default boolean not null default false
|
||||
is_active boolean not null default true
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* `entity_type = quotation` for quotation approval
|
||||
* `product_type = null` means all product types
|
||||
* `branch_id = null` means all branches
|
||||
* `min_amount = null` means no lower bound
|
||||
* `max_amount = null` means no upper bound
|
||||
* `currency = null` means all currencies
|
||||
* lower priority number wins, or document project convention if already defined
|
||||
* only active rules are considered
|
||||
* soft-deleted rules are ignored
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.2 Matrix Matching Rules
|
||||
|
||||
Implement resolver:
|
||||
|
||||
```txt
|
||||
src/features/crm/approval/server/matrix-resolver.ts
|
||||
```
|
||||
|
||||
Function:
|
||||
|
||||
```ts
|
||||
resolveApprovalWorkflowForEntity(input)
|
||||
```
|
||||
|
||||
Input:
|
||||
|
||||
```ts
|
||||
{
|
||||
organizationId: string;
|
||||
entityType: 'quotation';
|
||||
branchId?: string | null;
|
||||
productType?: string | null;
|
||||
amount?: number | null;
|
||||
currency?: string | null;
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```ts
|
||||
{
|
||||
matrixId: string;
|
||||
workflowId: string;
|
||||
matchReason: string;
|
||||
}
|
||||
```
|
||||
|
||||
Matching rules:
|
||||
|
||||
1. Match same organization
|
||||
2. Match same entity type
|
||||
3. Ignore inactive or deleted matrix rows
|
||||
4. Product type must match exact or be null
|
||||
5. Branch must match exact or be null
|
||||
6. Currency must match exact or be null
|
||||
7. Amount must be within range:
|
||||
|
||||
* `minAmount <= amount` if min exists
|
||||
* `amount <= maxAmount` if max exists
|
||||
8. Choose most specific rule
|
||||
9. If specificity ties, choose highest priority
|
||||
10. If still tied, choose newest active rule or throw deterministic conflict error
|
||||
11. If no specific rule matches, use default rule
|
||||
12. If no default rule exists, throw clear configuration error
|
||||
|
||||
Specificity scoring suggestion:
|
||||
|
||||
```txt
|
||||
productType match = +10
|
||||
branch match = +10
|
||||
currency match = +5
|
||||
amount range match = +5
|
||||
default rule = -100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.3 Submit Approval Integration
|
||||
|
||||
Update quotation submit approval flow.
|
||||
|
||||
Current behavior:
|
||||
|
||||
```txt
|
||||
use static quotation_standard_approval
|
||||
```
|
||||
|
||||
New behavior:
|
||||
|
||||
```txt
|
||||
quotation submit
|
||||
↓
|
||||
build approval matrix input from quotation
|
||||
↓
|
||||
resolve workflow
|
||||
↓
|
||||
create approval request with resolved workflow
|
||||
```
|
||||
|
||||
Quotation fields used:
|
||||
|
||||
```txt
|
||||
organizationId
|
||||
branchId
|
||||
productType / quotationType
|
||||
totalAmount
|
||||
currency
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* do not select workflow in route handler
|
||||
* service must call matrix resolver
|
||||
* approval request must store selected workflowId
|
||||
* audit must include selected matrixId and workflowId
|
||||
* existing approval action flow should continue unchanged after request is created
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.4 Default Matrix Seed
|
||||
|
||||
Update seed to create at least one default quotation approval matrix.
|
||||
|
||||
Example:
|
||||
|
||||
```txt
|
||||
entity_type = quotation
|
||||
workflow = quotation_standard_approval
|
||||
product_type = null
|
||||
branch_id = null
|
||||
min_amount = null
|
||||
max_amount = null
|
||||
currency = null
|
||||
priority = 999
|
||||
is_default = true
|
||||
is_active = true
|
||||
```
|
||||
|
||||
This preserves current behavior while enabling future product-specific rules.
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.5 Product Type Matrix Examples
|
||||
|
||||
Seed optional sample rules only if project seed policy allows.
|
||||
|
||||
Examples:
|
||||
|
||||
```txt
|
||||
quotation_crane_standard
|
||||
product_type = crane
|
||||
amount <= 500000
|
||||
workflow = quotation_standard_approval
|
||||
|
||||
quotation_crane_executive
|
||||
product_type = crane
|
||||
amount > 5000000
|
||||
workflow = quotation_executive_approval
|
||||
```
|
||||
|
||||
If required workflows do not exist, do not seed these sample rules.
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.6 Matrix Admin API Foundation
|
||||
|
||||
Create API foundation for managing matrix rows.
|
||||
|
||||
Routes:
|
||||
|
||||
```txt
|
||||
GET /api/crm/approval/matrices
|
||||
POST /api/crm/approval/matrices
|
||||
GET /api/crm/approval/matrices/[id]
|
||||
PATCH /api/crm/approval/matrices/[id]
|
||||
DELETE /api/crm/approval/matrices/[id]
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* no UI required in F.2
|
||||
* enforce approval admin permission
|
||||
* validate workflow exists
|
||||
* validate no invalid range
|
||||
* soft delete only
|
||||
* prevent deleting default matrix if it is the only active default for entity type
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.7 Validation Rules
|
||||
|
||||
Create schemas:
|
||||
|
||||
```txt
|
||||
src/features/crm/approval/schemas/approval-matrix.schema.ts
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
* entityType required
|
||||
* workflowId required
|
||||
* minAmount must be <= maxAmount when both exist
|
||||
* priority must be integer
|
||||
* productType optional
|
||||
* branchId optional
|
||||
* currency optional
|
||||
* isDefault boolean
|
||||
* isActive boolean
|
||||
|
||||
Conflict warning:
|
||||
|
||||
* multiple active matrix rows can overlap
|
||||
* resolver must handle deterministic selection
|
||||
* API may warn but does not need to fully block overlap in F.2
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.8 Audit Logging
|
||||
|
||||
Audit actions:
|
||||
|
||||
```txt
|
||||
create_approval_matrix
|
||||
update_approval_matrix
|
||||
delete_approval_matrix
|
||||
resolve_approval_matrix
|
||||
```
|
||||
|
||||
For submit approval, audit must capture:
|
||||
|
||||
```txt
|
||||
quotationId
|
||||
matrixId
|
||||
workflowId
|
||||
matchReason
|
||||
productType
|
||||
branchId
|
||||
amount
|
||||
currency
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.9 Permission Rules
|
||||
|
||||
Add or verify permissions:
|
||||
|
||||
```txt
|
||||
crm.approval.matrix.read
|
||||
crm.approval.matrix.create
|
||||
crm.approval.matrix.update
|
||||
crm.approval.matrix.delete
|
||||
```
|
||||
|
||||
Submit approval should still use existing quotation approval submit permission.
|
||||
|
||||
Rules:
|
||||
|
||||
* route handlers must not check role strings directly
|
||||
* use existing CRM authorization foundation
|
||||
* admin-only API for matrix maintenance
|
||||
|
||||
---
|
||||
|
||||
## Scope F.2.10 Documentation
|
||||
|
||||
Create:
|
||||
|
||||
```txt
|
||||
docs/implementation/task-f2-approval-matrix-foundation.md
|
||||
```
|
||||
|
||||
Document:
|
||||
|
||||
```txt
|
||||
schema
|
||||
matching rules
|
||||
priority rules
|
||||
default fallback
|
||||
submit approval integration
|
||||
seed behavior
|
||||
known limitations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Explicit Non-Scope
|
||||
|
||||
Do not implement:
|
||||
|
||||
* full Workflow Builder UI
|
||||
* drag-and-drop step editor
|
||||
* Notification Center
|
||||
* Email sending
|
||||
* LINE / webhook sending
|
||||
* reminder / escalation
|
||||
* approval dashboard
|
||||
* approval delegation
|
||||
* parallel approval
|
||||
* any-one approval
|
||||
* dynamic approver lookup by manager hierarchy
|
||||
|
||||
These belong to later F tasks.
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
Run:
|
||||
|
||||
```txt
|
||||
npm exec tsc --noEmit
|
||||
npm run build
|
||||
```
|
||||
|
||||
Manual verification:
|
||||
|
||||
```txt
|
||||
Default matrix exists after seed
|
||||
Submit quotation approval still works
|
||||
Approval request stores resolved workflowId
|
||||
Matrix resolver chooses product-specific rule over default
|
||||
Matrix resolver chooses branch-specific rule over all-branch rule
|
||||
Matrix resolver chooses amount-range rule correctly
|
||||
Matrix resolver handles no-match with clear error
|
||||
Matrix CRUD APIs work
|
||||
Soft delete matrix works
|
||||
Audit logs include matrix resolution
|
||||
Existing approval approve/reject actions still work
|
||||
Existing quotation flow still works
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
|
||||
Task is complete when:
|
||||
|
||||
* `crm_approval_matrices` exists
|
||||
* default quotation approval matrix is seeded
|
||||
* quotation submit approval uses matrix resolver
|
||||
* workflow selection supports product type, branch, amount, currency, default fallback, and priority
|
||||
* matrix CRUD APIs exist
|
||||
* audit logs capture matrix changes and resolution
|
||||
* existing approval actions continue working
|
||||
|
||||
Result:
|
||||
|
||||
```txt
|
||||
Approval Matrix Foundation = Established
|
||||
Workflow Resolution = Dynamic
|
||||
Ready for F.3 Approval Workflow Builder
|
||||
```
|
||||
Reference in New Issue
Block a user