# 03 · Workflows, Rules & Formulas

The prototype implements every flow below; use it to see the exact screens and buttons.

## End-to-end stages

| # | Stage | Gate to enter (enforced) |
|---|---|---|
| 1 | Applicant Registration | — |
| 2 | Operation Initial Review | Registration status `new_application` |
| 3 | Submit Location to ZUS | Registration status `ready_for_location_screening` |
| 4 | Wait for ZUS Decision | Submission recorded |
| 4a | If rejected → record reason → new location → resubmit | Rejection reason mandatory |
| 5 | Contact Applicant | Location `approved` |
| 6–7 | Collect & Upload Documents | Location `approved` |
| 8 | Verification (Syafiqah / Asyraff) | Document `under_verification` |
| 8a | If rejected → resubmission (new version) | Reason mandatory |
| 9 | Onboarding (7 steps) | **All mandatory documents verified** (DB trigger) |
| 10 | Activation → ACTIVE | **Location approved + documents complete + 7 steps completed** (DB trigger) |
| 11 | Daily Operations | Merchant `active` |
| 12 | Daily Finance Reporting | Every business day |
| 13 | Monthly Closing | All checks pass (DB trigger) |
| 14 | Management Reporting | — |

Blocked transitions (from the requirement): Location Rejected → Onboarding; Documents Incomplete → Activation; Restock > Available Fund; Unverified Settlement → Month Locked; self-approval.

### "Current stage / Next action" logic (merchant page)

Evaluate in order; the first match wins:

| Condition | Stage | Next action |
|---|---|---|
| Registration `new_application` | Registration | Start initial review (or assign PIC if none) |
| Registration `under_initial_review` | Registration | Complete review, mark ready |
| Location `pending_review` | Location Screening | Submit proposed location to ZUS |
| Location `pending_zus_decision` | Location Screening | Record ZUS decision |
| Location `rejected` / `resubmission_required` | Location Screening | Propose a new location and resubmit |
| Any mandatory doc rejected | Document Collection | Re-upload `<first rejected doc>` |
| Any mandatory doc missing | Document Collection | Upload `<first missing doc>` |
| Docs under verification | Document Collection | Verify `<first doc>` |
| Onboarding step not completed | Onboarding | Complete `<first incomplete step>` |
| All steps completed, not active | Activation | Activate merchant |
| Active | Daily Operations | — |

Due date = earliest open task due date for that merchant (preferring the merchant PIC's tasks). PIC = merchant's assigned PIC.

## Status lists

| Module | Statuses |
|---|---|
| Registration | New Application → Under Initial Review → Ready for Location Screening |
| Location | Pending Review → Submitted to ZUS → Pending ZUS Decision → Approved / Rejected → Resubmission Required |
| Document | Missing → Submitted → Under Verification → Verified / Rejected / Resubmission Required |
| Document set | Pending → Under Verification → **Complete** (all mandatory verified) |
| Onboarding step | Not Started → In Progress → Completed |
| Merchant | Applicant → **Active** → Inactive / Suspended |
| Task | Not Started → In Progress → Submitted → Pending Verification → Completed / Rejected · *Overdue is derived* |
| Settlement | Matched · Partial · Unmatched · Missing · Adjustment Required |
| Bank transaction | Unmatched → Investigation Required → Matched |
| Restock (balance) | Not Ready · Ready for Restock |
| Restock order | Restock Requested → Order Submitted → Awaiting Delivery → Received → Completed (or Cancelled) |
| Stock | Normal · Low Stock · Out of Stock |
| Repayment | Paid · Partial · Outstanding · Overdue |
| Issue | Open → Assigned → In Progress → Resolved → Verified → Closed · *Overdue is derived* |
| Finance report | Not Prepared → Draft → Verified → Sent to Finance |
| Month | Open → Locked (cannot reopen) |
| Adjustment | Pending Approval → Approved / Rejected |

Status colours: green = completed/approved/active/matched; yellow = pending/in progress/partial; red = rejected/overdue/unmatched/missing/low stock; blue = submitted/under review; grey = draft/inactive/not started.

## Onboarding step fields (`onboarding_steps.details` JSON)

| Step | Fields |
|---|---|
| Agreement | agreement_status, agreement_date, signed_date, file_key |
| Payment | package_amount, payment_amount, payment_date, payment_proof_key, payment_status |
| Fasspay | registration_status, form_key, submission_date, terminal_status, bank_settlement_details |
| Heydoc | registration_status, verification_status, required_documents[], completion_date |
| Allianz | insurance_status, policy_no, document_key, completion_date |
| Training | training_date, training_status, attendance, trainer, remarks |
| Starter Kit | status, dispatch_date, tracking_number, received_date, received_by |
| Activation | activation_date, zus_branch, merchant_status (stored on `merchants`) |

## Formulas

**Settlement (per merchant per day)**
- Expected settlement = POS sales for that day *(confirm whether Fasspay MDR/fees are deducted — see open questions)*
- Difference = Actual received − Expected
- Status: no actual → Missing; actual = expected → Matched; 0 < actual < expected → Partial; otherwise → Unmatched; user flag → Adjustment Required

**Fund allocation (per settled amount)**
- For each bucket in the allocation rule effective on the settlement date: amount = settled amount × bucket % (stored with the % used)
- Merchant payout = settled amount − sum of bucket amounts
- Example with 40:20 on RM5,000 → Restock Fund RM2,000 · Repayment Fund RM1,000 · Payout RM2,000

**Restock balance (per merchant)**
- Available = Opening balance + Restock Fund allocations − Completed orders − Open (reserved) orders
- Ready for Restock when Available ≥ threshold (configurable; prototype uses RM300)
- New order is blocked when order total > Available (DB trigger)

**Stock (per merchant per item)**
- Current = Opening + Received − Used (± count adjustments)
- Low Stock when Current < Minimum; Out of Stock when Current ≤ 0

**Repayment (per merchant)**
- Package RM16,200 = RM675 × 24 months; first due date = 10th of the month after activation *(confirm)*
- Instalments due = number of due dates up to today (max 24)
- Expected to date = instalments due × RM675; Arrears = Expected − Paid; Outstanding = RM16,200 − Paid
- Status: arrears ≤ 0 → Paid; arrears ≥ one instalment → Overdue; payment made this month but short → Partial; else Outstanding

## Daily Finance report

- One report per business day (weekdays excluding `public_holidays`), covering the previous day's settlement batch.
- Content: total POS sales, expected settlement, actual bank receipts, difference, merchant breakdown, bank account breakdown, unmatched amount, adjustments, remarks.
- Flow: Draft (anyone in Ops) → Verified (Syafiqah/Asyraff, not the preparer unless Full Access) → Sent to Finance (email + timestamp).
- On verification the totals are frozen in `finance_reports.snapshot`.
- `v_missing_finance_reports` lists business days without a sent report → red alert on the dashboard; reminder email at 10:30 if today's is not sent.

## Monthly closing checklist

Lock is allowed only when (DB-enforced items marked •):
- Sales uploaded for every active merchant
- • All settlements in the month are Matched
- • All bank transactions in the month are matched to a merchant
- Fund allocation done for all settled amounts
- Restock deliveries verified; stock movements recorded
- Repayment records entered
- Every business day has a Finance report Sent
- • No adjustment requests pending
- Outstanding issues reviewed (warning, not a blocker)

After lock, rows in `sales`, `settlements`, `bank_transactions`, `fund_allocations`, `repayments` for that month cannot be inserted, changed or deleted, except inside a transaction that sets `app.adjustment_id` to an **approved** adjustment request.

## Validation rules (imports and forms)

| Rule | Where |
|---|---|
| Duplicate Merchant ID / IC | `merchants` PK + unique `ic_or_company_no` |
| Duplicate sales | unique `pos_reference`; unique `(merchant_id, sales_date)` |
| Duplicate settlement | unique `(merchant_id, settlement_date)` |
| Duplicate bank transaction | unique `(bank_account_id, reference, txn_date, amount)` |
| Duplicate upload (same file) | unique `(kind, file_sha256)` on `import_batches`; unique `(merchant_id, sha256)` on `documents` |
| Invalid merchant ID | FK to `merchants` |
| Missing mandatory fields | NOT NULL + import validator |
| Invalid dates, future sales dates | type + `CHECK sales_date <= current_date` |
| Invalid / negative amounts | `CHECK amount > 0` (or `>= 0` where zero is valid) |
| Restock over available balance | trigger `restock_guard` |
| Missing required documents before onboarding/activation | triggers |
| File type and size | `documents` CHECKs + content sniffing in app |

## Email notifications

Each event writes to `notifications`; the worker sends and records `sent_at`.

| Event | To |
|---|---|
| New task assigned / reassigned | Task PIC |
| Document submitted / verification requested | Syafiqah, Asyraff |
| Document rejected / resubmission required | Uploader + merchant PIC |
| Task overdue (daily 09:00) | Task PIC, cc Full Access |
| Location approved / rejected | Merchant PIC |
| Merchant ready for onboarding (documents complete) | Merchant PIC |
| Low stock | Merchant PIC |
| Restock ready (balance crosses threshold) | Merchant PIC |
| Settlement mismatch / unmatched bank transaction | Full Access + Finance |
| Finance report reminder (10:30 if not sent) | Full Access + preparer |
| Finance report sent | Finance team |
| Issue assigned / issue overdue | Issue PIC |

Template example:

```
Subject: New ZUSahabat Task Assigned

Merchant: ZS-00123
Task: Verify SSM
Assigned To: Asyraff
Due Date: 14 Nov 2026

Please login to the ZUSahabat Operation Portal to complete the task.
https://ops.<domain>/tasks/T-00045
```

## Search & filters

- Global search: Merchant ID, name, phone, email, AIM branch, ZUS branch, location, state, PIC, status (Postgres `pg_trgm` index recommended on name/address)
- Date presets on every dated list: Today, This Week, This Month, Custom range
- Management dashboard filters: date, state, AIM branch, ZUS branch, PIC, merchant status

## Reports (export Excel/CSV; PDF for Finance and Management)

Registration · Location Screening · Document Status · Onboarding · Merchant · Sales · Settlement · Fund Allocation · Restock · Stock · Repayment · Issue · Daily Finance · Monthly Finance · Audit. Every export is audited (who, what, filters).
