# 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
```
