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_SETUP.md

1. High-level system architecture

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

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/* (14 sub-masters)"]
        M1[uom]
        M2[item-categories / subcategories / items]
        M3[brands / gst-rates]
        M4[payment-terms / delivery-terms]
        M5[item-categories / subcategories]
        M6[departments / designations]
        M7[locations / plants / warehouses]
        M8[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)

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

4. Auth & session flow

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
        
LayerStorageReset
Global API rate limitIn-memory per IPRestart server or wait 15 min
Login rate limitIn-memory per IPRestart or wait 15 min
Account lockoutusers.locked_untilWait 30 min or DB reset

5. Master data dependency graph

Masters must exist before transactional modules use them.

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 --> ASSETS[assets]
    ISUB --> ASSETS
    ACAT --> ASSET
    ACAT --> GRN_AUTO[GRN auto-asset creation]
    ASUB --> ASSET
    ASUB --> 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)

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

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.

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"] D1["POST /grn/:grnId/attachments"] D2["grn_attachments row"] D3["File in 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 --> D4 S8 --> OUT1{More qty pending?} OUT1 -->|Yes| PARTIAL[PO PARTIALLY_RECEIVED] OUT1 -->|No| FULL[PO FULLY_RECEIVED]
StepAPI / tableNotes
1POST /grnRequires APPROVED PO with pending qty
2grn, grn_itemsaccepted_qty + rejected_qty = current_qty
3purchase_order_items.received_qtyIncremented in same transaction
4assetsWhen items.is_asset_item = true
5POST /grn/:grnId/attachmentsInvoice PDF, LR copy, photos
6GET .../attachments/:id/downloadRBAC download — not public /uploads

GRN auto-asset creation (when items.is_asset_item = true)

Field on AssetSource
asset_codedocument_series via ASSET_{category.code}
asset_nameItem name (+ #N if qty > 1)
item_category_id / item_subcategory_idFrom item master (optional GRN line override)
plant_idFrom PO
warehouse_id, vendor_id, po_id, grn_id, grn_item_idFrom GRN
purchase_dategrn.grn_date
purchase_costgrn_items.rate
useful_life_years, depreciation_methodFrom item_categories defaults
conditionNEW
statusIN_USE

7. Asset module — full lifecycle

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

FieldValues
statusIN_USE, IDLE, UNDER_MAINTENANCE, DISPOSED, SCRAPPED
conditionNEW, GOOD, FAIR, POOR

8. AMC contracts flow + renewal concept

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

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

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

11. Depreciation flow

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
MethodRate auto-calc?Formula concept
SLMYes (if rate omitted)Equal yearly depreciation on original cost
WDVYes (if rate omitted)Depreciation on reducing book value year-by-year
OTHERNo — user must send depreciation_rateCustom % on cost

FE usage:

12. Alerts flow (cross-asset dashboards)

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

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

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

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)

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)

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

AreaEndpointsNotes
CoreGET/POST /assets, GET/PUT/DELETE /assets/:idIncludes computed depreciation on read
TransferPOST /assets/:id/transfer, GET .../transfer-historyBlocked if DISPOSED/SCRAPPED
AMCGET/POST /assets/:id/amc, GET/PUT .../:contractId, PATCH .../renewOne active contract
ServiceGET/POST /assets/:id/service-visits, PUT, PATCH .../statusLinks optional AMC
InsuranceGET/POST /assets/:id/insurance, GET/PUT, PATCH .../renewOne active policy
DepreciationGET /depreciation-methods, POST /depreciation/calculatePreview only
AlertsGET /alerts/expiry, GET /alerts/serviceSQL views

19. Typical FE screen → API mapping

ScreenAPIs used
LoginPOST /auth/login, GET /auth/me
Master setup/masters/* CRUD
Vendor management/vendors/*
Create POMasters dropdowns + POST /purchase-orders
Approve POPOST /purchase-orders/:id/approve
GRN receiptPOST /grn (auto assets for asset items)
Asset list/detailGET /assets, GET /assets/:id
Asset create/edit formMasters + 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 assetPOST /assets/:id/transfer

20. Renewal concept summary (for FE)

EntityWhat "Renew" doesOld recordNew 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.


Generated from MODULE_DATA_FLOW.md · Open in any browser · Mermaid diagrams require network for CDN