# ERP Backend — Module & Data Flow Diagram > **Stack:** Express · PostgreSQL · Prisma · JWT + RBAC > **Base API:** `/api/v1` > **Phase 1 modules:** Auth, Users, Roles, Masters (14), Vendors, Purchase Orders, GRN, Assets (+ AMC / Insurance / Service / Alerts / Depreciation), Settings Reference: [BACKEND_TASKS.md](../BACKEND_TASKS.md) · [BACKEND_SETUP.md](../BACKEND_SETUP.md) --- ## 1. High-level system architecture ```mermaid flowchart TB subgraph Client["Frontend / API Client"] FE[React App / Postman] end subgraph Gateway["Express App (src/app.js)"] HELMET[Helmet + CORS + HPP] RATE[Rate Limiter /api] REQID[Request ID] ROUTES["/api/v1 Routes"] SWAGGER[Swagger /api-docs] ERR[Error Middleware] end subgraph Middleware["Per-route chain"] AUTH[authenticate JWT] RBAC[authorize module + action] VAL[validate Joi schema] CTRL[Controller] end subgraph Services["Business Layer"] SVC["*.service.js"] REPO["*.repository.js PO/GRN/Assets"] end subgraph Data["PostgreSQL"] PRISMA[(Prisma ORM)] VIEWS[(SQL Views v_asset_*)] AUDIT[(audit_logs)] end FE --> HELMET --> RATE --> REQID --> ROUTES ROUTES --> AUTH --> RBAC --> VAL --> CTRL --> SVC SVC --> PRISMA SVC --> REPO --> PRISMA SVC --> AUDIT SVC --> VIEWS CTRL --> ERR ``` --- ## 2. Every API module map ```mermaid flowchart LR subgraph System HEALTH[/health /healthz] DOCS[/api-docs] end subgraph Security AUTH_MOD["/auth login|refresh|logout|me"] USERS["/users CRUD + export"] ROLES["/roles CRUD + permissions matrix"] end subgraph Masters["/masters/* (item categories shared with assets)"] M1[uom] M2[item-categories / subcategories / items] M3[brands / gst-rates] M4[payment-terms / delivery-terms] M5[departments / designations] M6[locations / plants / warehouses] M7[document-series] end subgraph Procurement VEND["/vendors + addresses|contacts|bank|items"] PO["/purchase-orders workflow + PDF"] GRN["/grn receipt + cancel + PDF"] end subgraph Assets["/assets"] A_CORE[CRUD + transfer] A_AMC[AMC contracts + renew] A_SVC[service visits + status] A_INS[insurance + renew] A_DEPR[depreciation preview] A_ALERT[expiry + service alerts] end subgraph Config SET["/settings company + email SMTP"] end System --> Security --> Masters --> Procurement --> Assets --> Config ``` --- ## 3. Standard request data flow (all protected routes) ```mermaid sequenceDiagram participant FE as Frontend participant API as Express Route participant AUTH as authenticate participant RBAC as authorize participant VAL as validate participant SVC as Service participant DB as PostgreSQL participant AUD as audit_logs FE->>API: HTTP + Bearer JWT API->>AUTH: Verify token, load user + roles AUTH->>RBAC: Check module permission (view/create/edit/delete/approve/export) RBAC->>VAL: Joi validate body/query VAL->>SVC: Business logic SVC->>DB: Prisma read/write (deleted_at: null) SVC->>AUD: auditLog on mutations SVC-->>FE: ApiResponse { success, message, data, meta } ``` **RBAC modules:** `USERS`, `ROLES`, `MASTERS`, `VENDOR`, `PURCHASE_ORDER`, `GRN`, `ASSET`, `SETTINGS`, `AUDIT_LOGS` ### Audit logs (read-only) | Endpoint | Purpose | |---|---| | `GET /audit-logs/filters` | Dropdown values: tables, actions, performers | | `GET /audit-logs` | Filtered list — **returns empty until a filter is set** | | `GET /audit-logs/:id` | Full row with `old_value` / `new_value` | | `GET /audit-logs/export` | CSV of filtered rows | Typical FE flow: load filters → user picks table + record (or date range) → list → click row for detail drawer. --- ## 4. Auth & session flow ```mermaid stateDiagram-v2 [*] --> Login: POST /auth/login Login --> Active: Valid credentials Login --> Locked: MAX_LOGIN_ATTEMPTS exceeded Login --> RateLimited: Too many failed logins (IP) Active --> TokenIssued: accessToken 15m + refreshToken TokenIssued --> APIAccess: Bearer on /api/v1/* APIAccess --> Refresh: access expired Refresh --> TokenIssued: POST /auth/refresh (rotate hash in DB) APIAccess --> Logout: POST /auth/logout (revoke refresh) Locked --> Login: After LOCKOUT_DURATION_MINUTES ``` | Layer | Storage | Reset | |---|---|---| | Global API rate limit | In-memory per IP | Restart server or wait 15 min | | Login rate limit | In-memory per IP | Restart or wait 15 min | | Account lockout | `users.locked_until` | Wait 30 min or DB reset | --- ## 5. Master data dependency graph Masters must exist **before** transactional modules use them. ```mermaid flowchart TD DS[document_series] --> VCODE[vendor_code / po_number / grn_number / asset_code] LOC[locations plants + warehouses] --> PO LOC --> GRN LOC --> ASSET DEPT[departments] --> USERS DEPT --> ASSET DES[designations] --> USERS UOM[uom] --> ITEMS IC[item_categories] --> ISUB[item_subcategories] --> ITEMS GST[gst_rates] --> ITEMS BRANDS[brands] --> ITEMS BRANDS --> PO PT[payment_terms] --> VEND PT --> PO DT[delivery_terms] --> PO ICAT[item_categories] --> ISUB[item_subcategories] ISUB --> ITEMS[items] ICAT --> ASSET ICAT --> GRN_AUTO[GRN auto-asset creation] ISUB --> ASSET ISUB --> GRN_AUTO ITEMS --> PO_ITEMS ITEMS --> GRN_ITEMS VEND --> PO VEND --> GRN VEND --> AMC VEND --> SVC_VISIT ``` --- ## 6. Procurement → Asset end-to-end flow (core business chain) ```mermaid flowchart TD START([Setup Masters + Vendor]) --> PO_CREATE PO_CREATE["POST /purchase-orders
status: DRAFT"] --> PO_EDIT["PUT /purchase-orders/:id
add line items"] PO_EDIT --> PO_SUBMIT["POST .../submit
→ PENDING_APPROVAL"] PO_SUBMIT --> PO_APPROVE{"Approve?"} PO_APPROVE -->|Yes| PO_APPROVED["status: APPROVED"] PO_APPROVE -->|No| PO_REJECTED["status: REJECTED → edit & resubmit"] PO_REJECTED --> PO_EDIT PO_APPROVED --> GRN_CREATE["POST /grn
receive against PO lines"] subgraph GRN_TXN["GRN Transaction (repository)"] G1[Create grn POSTED] G2[Create grn_items per line] G3[Increment PO item received_qty] G4{is_asset_item?} G5[Auto-create assets 1 per accepted qty] G6[Recalculate PO status] G1 --> G2 --> G3 --> G4 G4 -->|Yes| G5 --> G6 G4 -->|No| G6 end GRN_CREATE --> GRN_TXN G6 --> PO_STATUS{Receipt status} PO_STATUS -->|partial| PARTIAL[PARTIALLY_RECEIVED] PO_STATUS -->|full| FULL[FULLY_RECEIVED] G5 --> MANUAL_ASSET["POST /assets manual create also allowed"] PARTIAL --> GRN_CREATE FULL --> ASSET_OPS[Asset lifecycle ops] GRN_CANCEL["POST /grn/:id/cancel"] --> REV1[Decrement received_qty] REV1 --> REV2[Soft-delete linked assets] REV2 --> REV3[GRN status CANCELLED] ``` ### PO status lifecycle ```mermaid stateDiagram-v2 [*] --> DRAFT DRAFT --> PENDING_APPROVAL: submit REJECTED --> PENDING_APPROVAL: resubmit PENDING_APPROVAL --> APPROVED: approve PENDING_APPROVAL --> REJECTED: reject APPROVED --> PARTIALLY_RECEIVED: GRN partial APPROVED --> FULLY_RECEIVED: GRN full PARTIALLY_RECEIVED --> FULLY_RECEIVED: more GRN APPROVED --> CLOSED: amend closes old PO DRAFT --> CANCELLED: cancel APPROVED --> CANCELLED: cancel if no receipts ``` ### GRN goods receiving concept flow End-to-end warehouse receipt: PO-backed GRN posting, optional supporting documents, PO qty update, and asset auto-creation. ```mermaid flowchart TD subgraph Physical["Physical receipt at warehouse"] P1[Vendor delivery arrives] P2[Verify PO / invoice / LR / vehicle] P3[Inspect qty quality batch] end subgraph System["System — GRN posting"] S1["GET open PO
status APPROVED / PARTIALLY_RECEIVED"] S2["POST /grn
header + line items"] S3["GRN txn: grn POSTED"] S4["grn_items per accepted/rejected qty"] S5["PO received_qty += accepted_qty"] S6{Item is_asset_item?} S7["Auto-create assets
1 per accepted unit"] S8["Recalculate PO status"] end subgraph Docs["Supporting documents (after or alongside GRN)"] D1["POST /grn/:grnId/attachments
multipart file"] D2["grn_attachments row"] D3["File stored under uploads/grn/{grnId}/"] D4["GET .../download — authenticated"] end P1 --> P2 --> P3 --> S1 --> S2 --> S3 --> S4 --> S5 --> S6 S6 -->|Yes| S7 --> S8 S6 -->|No| S8 S3 --> D1 --> D2 --> D3 D3 --> D4 S8 --> OUT1{More qty pending?} OUT1 -->|Yes| PARTIAL[PO PARTIALLY_RECEIVED] OUT1 -->|No| FULL[PO FULLY_RECEIVED] CANCEL["POST /grn/:id/cancel"] --> REV1[Reverse PO received_qty] REV1 --> REV2[Soft-delete linked assets] REV2 --> REV3[GRN CANCELLED — attachments remain on record] ``` | Step | Actor | API / table | Notes | |------|-------|-------------|-------| | 1 | Store / procurement | `POST /grn` | Requires APPROVED PO with pending qty | | 2 | System | `grn`, `grn_items` | `accepted_qty + rejected_qty = current_qty` per line | | 3 | System | `purchase_order_items.received_qty` | Incremented in same transaction | | 4 | System | `assets` | Created when `items.is_asset_item = true` | | 5 | Store user | `POST /grn/:grnId/attachments` | Invoice PDF, LR copy, photos (PDF/JPEG/PNG/WebP) | | 6 | Any authorized user | `GET .../attachments/:id/download` | RBAC-protected download — not public `/uploads` | | 7 | GRN detail | `GET /grn/:id` | Includes `attachments[]` metadata | **Typical attachment types:** vendor tax invoice, LR/eway bill, packing list, QC rejection photos, delivery challan. ### GRN auto-asset creation (when `items.is_asset_item = true`) | Field on Asset | Source | |---|---| | `asset_code` | `document_series` via `ASSET_{category.code}` | | `asset_name` | Item name (+ `#N` if qty > 1) | | `item_category_id` / `item_subcategory_id` | From item master (optional GRN line override) | | `plant_id` | From PO | | `warehouse_id`, `vendor_id`, `po_id`, `grn_id`, `grn_item_id` | From GRN | | `purchase_date` | `grn.grn_date` | | `purchase_cost` | `grn_items.rate` | | `useful_life_years`, `depreciation_method` | From `item_categories` defaults | | `condition` | `NEW` | | `status` | `IN_USE` | --- ## 7. Asset module — full lifecycle ```mermaid flowchart TB subgraph Create["Asset Creation Paths"] P1[Manual POST /assets] P2[Auto from GRN receipt] end subgraph Validate["normalizeAssetPayload validations"] V1[item_category + subcategory match] V2[plant / dept / warehouse / user refs] V3[vendor / PO / GRN / grn_item refs] V4[disposal_date required if DISPOSED/SCRAPPED] V5[depreciation_rate required if method=OTHER] V6[resolve rate from category defaults] end Create --> Validate --> SAVE[(assets table)] SAVE --> CODE[asset_code from document_series] SAVE --> READ["GET /assets/:id
includes depreciation summary"] SAVE --> TRANSFER["POST /assets/:id/transfer"] TRANSFER --> TH[(asset_transfers)] TRANSFER --> UPDATE_LOC[Update plant/dept/user/warehouse on asset] SAVE --> UPDATE["PUT /assets/:id"] SAVE --> DELETE["DELETE /assets/:id soft delete"] UPDATE --> DISPOSE{status DISPOSED/SCRAPPED?} DISPOSE -->|Yes| NEED_DATE[disposal_date required] ``` ### Asset status & condition enums | Field | Values | |---|---| | `status` | `IN_USE`, `IDLE`, `UNDER_MAINTENANCE`, `DISPOSED`, `SCRAPPED` | | `condition` | `NEW`, `GOOD`, `FAIR`, `POOR` | --- ## 8. AMC contracts flow + renewal concept ```mermaid flowchart TD A[(assets)] --> AMC_LIST["GET /assets/:id/amc"] A --> AMC_CREATE["POST /assets/:id/amc"] AMC_CREATE --> CHECK1{is_active=true?} CHECK1 -->|Yes| DEACT[Deactivate other active AMC for same asset] CHECK1 --> AMC_ROW[(asset_amc_contracts)] DEACT --> AMC_ROW AMC_ROW --> FIELDS["vendor_id, contract_type, start/end dates,
renewal_date, annual_cost, visits_per_year, etc."] AMC_ROW --> AMC_UPDATE["PUT /assets/:id/amc/:contractId"] AMC_ROW --> AMC_RENEW["PATCH .../renew"] subgraph RENEW_FLOW["Renewal Flow (AMC)"] R1[Load existing contract] R2[Set old contract is_active=false] R3[Create NEW contract row with new dates] R4[New contract is_active=true] R5[auditLog action=RENEW] R1 --> R2 --> R3 --> R4 --> R5 end AMC_RENEW --> RENEW_FLOW AMC_ROW --> SVC_LINK["Service visits can link amc_contract_id"] ``` **Key rule:** Only **one active AMC** per asset (`deactivateOtherActive`). **Renewal ≠ update dates on same row** — renewal creates a **new contract record** and deactivates the old one (history preserved). --- ## 9. Insurance policies flow + renewal concept ```mermaid flowchart TD A[(assets)] --> INS_LIST["GET /assets/:id/insurance"] A --> INS_CREATE["POST /assets/:id/insurance"] INS_CREATE --> CHECK{is_active=true?} CHECK -->|Yes| DEACT[Deactivate other active policies] CHECK --> POL[(asset_insurance_policies)] DEACT --> POL POL --> FIELDS["policy_no, insurer, sum_insured,
annual_premium, start/end dates,
renewal_date, is_auto_renewal, premium_paid"] POL --> INS_UPDATE["PUT /assets/:id/insurance/:policyId"] POL --> INS_RENEW["PATCH .../renew"] subgraph RENEW_INS["Renewal Flow (Insurance)"] I1[Deactivate old policy] I2[Create new policy with new dates] I3[premium_paid defaults false on renew] I4[auditLog action=RENEW] I1 --> I2 --> I3 --> I4 end INS_RENEW --> RENEW_INS ``` Same pattern as AMC: **one active policy**, renewal = **new row + deactivate old**. --- ## 10. Service visits flow ```mermaid flowchart TD A[(assets)] --> SV_LIST["GET /assets/:id/service-visits"] A --> SV_CREATE["POST /assets/:id/service-visits"] SV_CREATE --> VAL_SV["Validate amc_contract_id belongs to asset
optional vendor_id ref"] VAL_SV --> SV_ROW[(asset_service_visits)] SV_ROW --> FIELDS["visit_type, visit_date, complaint details,
engineer, work_done, parts_replaced,
next_service_date, downtime_hours,
service_cost, is_under_amc,
asset_condition_after"] SV_ROW --> SV_UPDATE["PUT .../service-visits/:visitId"] SV_ROW --> SV_STATUS["PATCH .../status
SCHEDULED|IN_PROGRESS|COMPLETED|etc."] SV_ROW --> ALERT_VIEW["Feeds v_asset_next_service view"] ``` ### Visit types `PREVENTIVE`, `BREAKDOWN`, `INSPECTION`, `INSTALLATION`, `CALIBRATION`, `OTHER` ### Link to AMC - `amc_contract_id` optional but validated against the asset - `is_under_amc` flag for cost tracking - `next_service_date` drives **service alerts** --- ## 11. Depreciation flow ```mermaid flowchart TD subgraph Inputs CAT[item_categories defaults] FORM[User form fields] end CAT --> |default_useful_life_years
default_depreciation_method| CREATE FORM --> CREATE["POST /assets or PUT /assets/:id"] FORM --> PREVIEW["POST /assets/depreciation/calculate"] subgraph Methods["depreciation_method"] SLM[SLM Straight Line] WDV[WDV Written Down Value] OTHER[OTHER manual rate required] end PREVIEW --> CALC[calculateDepreciation] CREATE --> STORE[(assets: method, rate, cost, salvage, life, purchase_date)] STORE --> READ["GET /assets/:id"] READ --> CALC CALC --> OUT["annual_depreciation
accumulated_depreciation
book_value
years_elapsed
resolved depreciation_rate"] SLM --> CALC WDV --> CALC OTHER --> CALC ``` | Method | Rate auto-calc? | Formula concept | |---|---|---| | **SLM** | Yes (if rate omitted) | Equal yearly depreciation on original cost | | **WDV** | Yes (if rate omitted) | Depreciation on reducing book value year-by-year | | **OTHER** | No — user must send `depreciation_rate` | Custom % on cost | **FE usage:** - **Live preview** → `POST /assets/depreciation/calculate` (no save) - **Saved asset view** → `GET /assets/:id` → `data.depreciation` object - **Method dropdown** → `GET /assets/depreciation-methods` --- ## 12. Alerts flow (cross-asset dashboards) ```mermaid flowchart TD subgraph Sources AMC[(asset_amc_contracts end_date)] INS[(asset_insurance_policies policy_end_date)] WAR[(assets warranty_expiry_date)] SVC[(asset_service_visits next_service_date)] end AMC --> V1[v_asset_expiry_alerts] INS --> V1 WAR --> V1 SVC --> V2[v_asset_next_service] V1 --> API1["GET /assets/alerts/expiry
?days=30&type=AMC|INSURANCE|WARRANTY"] V2 --> API2["GET /assets/alerts/service
?status=OVERDUE|DUE_THIS_WEEK|..."] V1 --> LEVELS["EXPIRED / CRITICAL / WARNING / INFO"] V2 --> SSTAT["OVERDUE / DUE_THIS_WEEK / DUE_THIS_MONTH / UPCOMING"] ``` **Requires DB views:** run `scripts/patch-assets-amc-insurance.sql` + `scripts/patch-assets-views.sql` --- ## 13. Vendor module data flow ```mermaid flowchart LR V[(vendors)] --> ADDR[vendor_addresses] V --> CONT[vendor_contacts] V --> BANK[vendor_bank_details AES encrypted] V --> MAP[vendor_item_mapping] V --> PO[purchase_orders] V --> GRN[grn] V --> AMC[asset_amc_contracts] V --> SVC[asset_service_visits] MAP --> ITEMS[items preferred vendor rates] ``` --- ## 14. Users & roles permission flow ```mermaid flowchart TD U[(users)] --> UR[user_roles] UR --> R[(roles)] R --> RP[role_permissions] RP --> P[(permissions)] P --> M[(modules)] U --> PO_APPROVE[PO approve/reject] U --> ASSET_OPS[Asset CRUD/transfer] U --> AUDIT[performed_by in audit_logs] LOGIN["GET /auth/me"] --> PERM[Returns user + flat permissions list for FE menu/RBAC] ``` --- ## 15. Settings module ```mermaid flowchart LR CO[(company singleton)] --> PDF[PO/GRN/Asset PDF headers] EM[(email_settings SMTP encrypted)] --> MAIL[Future email notifications] LOGO["POST /settings/company/logo"] --> UPLOADS[(uploads/)] ``` --- ## 16. Cross-cutting concerns (every module) ```mermaid flowchart TD M[Every mutation] --> AUDIT[auditLog table_name, record_id, action, old/new JSON] M --> SOFT[Soft delete deleted_at = now] M --> USER[created_by / updated_by from req.user.id] M --> REQ[request_id from middleware] DOC[Document numbers] --> SERIES[document_series atomic increment] SERIES --> VC[vendor_code] SERIES --> PO_NUM[po_number] SERIES --> GRN_NUM[grn_number] SERIES --> ASSET_CODE[asset_code per category prefix] PII[mobile, bank accounts] --> ENC[AES-256-GCM + HMAC blind index] ``` --- ## 17. Complete entity relationship (simplified) ```mermaid erDiagram vendors ||--o{ purchase_orders : supplies vendors ||--o{ grn : delivers vendors ||--o{ asset_amc_contracts : maintains vendors ||--o{ asset_service_visits : services purchase_orders ||--o{ purchase_order_items : contains purchase_orders ||--o{ grn : received_via purchase_orders ||--o{ assets : sourced_from grn ||--o{ grn_items : lines grn ||--o{ grn_attachments : documents grn_items ||--o{ assets : auto_created items ||--o{ purchase_order_items : ordered items ||--o{ grn_items : received item_categories ||--o{ item_subcategories : has item_categories ||--o{ items : classifies item_categories ||--o{ assets : classifies item_subcategories ||--o{ items : sub_classifies item_subcategories ||--o{ assets : sub_classifies assets ||--o{ asset_amc_contracts : has assets ||--o{ asset_insurance_policies : insured_by assets ||--o{ asset_service_visits : serviced assets ||--o{ asset_transfers : moved assets ||--o{ asset_attachments : files asset_amc_contracts ||--o{ asset_service_visits : covers locations ||--o{ assets : plant_warehouse departments ||--o{ assets : assigned_dept users ||--o{ assets : assigned_user users ||--o{ user_roles : has roles ||--o{ role_permissions : grants permissions }o--|| modules : belongs_to ``` --- ## 18. Asset sub-module API quick reference | Area | Endpoints | Notes | |---|---|---| | Core | `GET/POST /assets`, `GET/PUT/DELETE /assets/:id` | Includes computed `depreciation` on read | | Transfer | `POST /assets/:id/transfer`, `GET .../transfer-history` | Blocked if DISPOSED/SCRAPPED | | AMC | `GET/POST /assets/:id/amc`, `GET/PUT .../:contractId`, `PATCH .../renew` | One active contract | | Service | `GET/POST /assets/:id/service-visits`, `PUT`, `PATCH .../status` | Links optional AMC | | Insurance | `GET/POST /assets/:id/insurance`, `GET/PUT`, `PATCH .../renew` | One active policy | | Depreciation | `GET /depreciation-methods`, `POST /depreciation/calculate` | Preview only | | Alerts | `GET /alerts/expiry`, `GET /alerts/service` | SQL views | --- ## 19. Typical FE screen → API mapping | Screen | APIs used | |---|---| | Login | `POST /auth/login`, `GET /auth/me` | | Master setup | `/masters/*` CRUD | | Vendor management | `/vendors/*` | | Create PO | Masters dropdowns + `POST /purchase-orders` | | Approve PO | `POST /purchase-orders/:id/approve` | | GRN receipt | `POST /grn` (auto assets for asset items) | | GRN attachments | `POST /grn/:grnId/attachments`, `GET .../download` | | Asset list/detail | `GET /assets`, `GET /assets/:id` | | Asset create/edit form | Masters + `POST /depreciation/calculate` (preview) + `POST/PUT /assets` | | AMC tab | `/assets/:id/amc/*` + renew | | Insurance tab | `/assets/:id/insurance/*` + renew | | Service log | `/assets/:id/service-visits/*` | | Dashboard alerts | `/assets/alerts/expiry`, `/assets/alerts/service` | | Transfer asset | `POST /assets/:id/transfer` | --- ## 20. Renewal concept summary (for FE) | Entity | What "Renew" does | Old record | New record | |---|---|---|---| | **AMC** | `PATCH /assets/:id/amc/:contractId/renew` | `is_active = false` | New row, `is_active = true`, new dates | | **Insurance** | `PATCH /assets/:id/insurance/:policyId/renew` | `is_active = false` | New row, `is_active = true`, new policy period | Both preserve **full history** — never overwrite the old contract/policy row. --- ## Viewing diagrams - **GitHub** — renders Mermaid natively in markdown - **VS Code** — install a Mermaid preview extension - **Online** — paste into [mermaid.live](https://mermaid.live)