21 KiB
ERP Backend — Development Tasks (Phase 1)
Reference: BACKEND_SETUP.md
Use this checklist when building or extending the backend. Follow the build order in Section 1.
1. Project Scaffold
- Create
package.jsonwith dependencies and scripts from the spec - Install dependencies (
npm install) - Add
.env.example(never commit.env) - Configure ESLint, Prettier, Husky, lint-staged
- Add
jest.config.jswithdotenv/configsetup file
2. Database & Prisma
- Define
prisma/schema.prisma:- Auth:
User,Role,Module,Permission,RolePermission,RefreshToken,PasswordResetToken - System:
AuditLog,DocumentSeries - Business: Vendor (+ sub-tables), PurchaseOrder (+ items), GRN (+ items), Asset (+ transfers), all masters
- Auth:
- Use
PascalCasemodels,snake_casefields,@@mapto plural table names - Add common columns on every business table:
is_active,created_by,updated_by,created_at,updated_at,deleted_at - Run
npx prisma migrate dev --name init - Run
npx prisma generate - Implement
prisma/seed.js— bootstrap Super Admin user (modules/permissions/roles from DDL) - Full idempotent seed (modules → permissions → roles → role_permissions) if not using DDL
Seed requirements
| Step | Content |
|---|---|
| Modules | USERS, ROLES, MASTERS, VENDOR, PURCHASE_ORDER, GRN, ASSET, SETTINGS, AUDIT_LOGS |
| Permissions | Each module × view, create, edit, delete, approve, export (skip where N/A) |
| Roles | Super Admin, Admin, Purchase Manager, Store Manager, Accounts, Asset Manager |
| Bootstrap user | Super Admin, bcrypt-hashed password, status = active |
3. Core Infrastructure
src/config/env.js— Joi validation; app must refuse to boot on invalid/missing envsrc/config/logger.js— Winston + daily rotate; redact sensitive keyssrc/config/morgan.js— HTTP logs into Winston with request ID + usersrc/config/prisma.js— singleton client with query/error loggingsrc/config/swagger.js— OpenAPI 3.0 fromsrc/docs/completed-routes.yamlsrc/utils/— ApiError, ApiResponse, asyncHandler, encryption, auditLog, pagination, generateCodesrc/middlewares/— requestId, error, validate, rateLimiter, auth, rbac, uploadsrc/app.js— Helmet, CORS, HPP, compression, rate limit, routes, Swagger, 404, error handlersrc/server.js— graceful shutdown, BigInt JSON serializer, unhandled rejection/exception handlers- Verify
GET /healthresponds
4. Modules (HMVC Pattern)
Build in this order. Each module: *.routes.js → *.controller.js → *.service.js → *.validation.js (+ *.repository.js for complex modules).
| # | Module | Status | Notes |
|---|---|---|---|
| 8 | auth | [x] Done | login, refresh, logout, me; rotating hashed refresh tokens; account lockout |
| 9 | masters | [x] Done | 15 sub-masters under masters/index.js (UOM template replicated) |
| — | users | [x] Done | CRUD + summary/filters/export; RBAC: USERS |
| — | roles | [x] Done | CRUD + permission assignment + matrix; RBAC: ROLES |
| 10 | vendors | [x] Done | CRUD + addresses/contacts/bank-details; encrypt bank accounts |
| 11 | purchase-orders | [x] Done | Header + line items; submit/approve/reject/amend/cancel; PDF export |
| 12 | grn | [x] Done | Receipt + PO status recalc + asset auto-creation |
| 13 | assets | [x] Done | CRUD + transfer + AMC/service visits/insurance + expiry alerts |
| 14 | settings | [x] Done | Company profile + SMTP email settings; RBAC: SETTINGS |
Current progress:
authmodule (login,refresh,logout)usersmodule (screen APIs: summary, filters, export, CRUD)rolesmodule (cards, permission catalog, matrix, CRUD)masters/uomtemplate + 13 replicated sub-masters- Swagger docs for all completed routes (
src/docs/completed-routes.yaml) vendorsmodule (CRUD, status, addresses, contacts, bank-details)purchase-ordersmodule (CRUD, workflow, PDF)grnmodule (transactional receipt, PO status recalc, asset auto-creation)assetsmodule (CRUD, transfer, AMC, service visits, insurance, alerts)GET /auth/me(current user + permissions for FE)
Layer rules
| Layer | Responsibility |
|---|---|
| Routes | HTTP mapping + middleware chain (authenticate → authorize → validate → controller) |
| Controller | Parse request, call service, return ApiResponse — no business logic, no Prisma |
| Service | Business logic, Prisma (or repository), auditLog on every mutation |
| Repository | Multi-table prisma.$transaction only (PO, GRN, Assets) |
| Validation | Joi schemas for body/query |
5. Module API List (Phase 1)
Base path: /api/v1 · Auth: Authorization: Bearer <accessToken> (except public auth routes)
System
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /health |
— | App health (root, not under v1) |
| [x] | GET | /healthz |
— | API v1 health |
| [x] | GET | /api-docs |
— | Swagger UI |
| [x] | GET | /api-docs.json |
— | OpenAPI spec |
Auth (/auth)
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | POST | /auth/login |
Public | Returns accessToken, refreshToken |
| [x] | POST | /auth/refresh |
Public | Rotating refresh token |
| [x] | POST | /auth/logout |
Public | Revoke refresh token |
| [ ] | POST | /auth/forgot-password |
Public | Not implemented |
| [x] | GET | /auth/me |
Authenticated | Current user + permissions (for FE) |
Users (/users) — module: USERS
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /users/summary |
view | Dashboard cards (total/active/inactive/locked, roles count) |
| [x] | GET | /users/filters |
view | Role, department, status dropdowns |
| [x] | GET | /users/export |
export | CSV export (same filters as list) |
| [x] | GET | /users |
view | Paginated list (search, status, role_id, department_id) |
| [x] | GET | /users/:id |
view | User detail |
| [x] | POST | /users |
create | Create user with role_ids[] (bcrypt password, mobile encrypted) |
| [x] | PUT | /users/:id |
edit | Update user; optional role_ids[] replaces assigned roles |
| [x] | DELETE | /users/:id |
delete | Soft delete |
Roles (/roles) — module: ROLES
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /roles/permissions |
view | Permission catalog (modules × actions) |
| [x] | GET | /roles/cards |
view | Role cards (name, description, user/permission counts) |
| [x] | GET | /roles |
view | Paginated role list |
| [x] | GET | /roles/:id |
view | Role detail with permissions |
| [x] | POST | /roles |
create | Create role with optional initial permissions (permission_ids[] and/or view_modules[]) |
| [x] | PUT | /roles/:id |
edit | Update role |
| [x] | DELETE | /roles/:id |
delete | Soft delete (blocked if users assigned) |
| [x] | PUT | /roles/:id/permissions |
edit | Assign by permission_ids[] (replaces all) |
| [x] | GET | /roles/:id/permission-matrix |
view | Matrix UI (view/create/edit/delete/approve/export) |
| [x] | PUT | /roles/:id/permission-matrix |
edit | Save matrix checkboxes |
Masters (/masters/*) — module: MASTERS
Each sub-master supports: GET / (list), GET /:id, POST /, PUT /:id, DELETE /:id with RBAC view|create|edit|delete.
| Status | Sub-master | Base path |
|---|---|---|
| [x] | UOM | /masters/uom |
| [x] | Item Categories | /masters/item-categories |
| [x] | Item Subcategories | /masters/item-subcategories |
| [x] | Items | /masters/items |
| [x] | Brands | /masters/brands |
| [x] | GST Rates | /masters/gst-rates |
| [x] | HSN Codes | /masters/hsn-codes |
| [x] | Payment Terms | /masters/payment-terms |
| [x] | Delivery Terms | /masters/delivery-terms |
| [~] | Asset Categories | removed — use Item Categories |
| [~] | Asset Subcategories | removed — use Item Subcategories |
| [x] | Departments | /masters/departments |
| [x] | Designations | /masters/designations |
| [x] | Locations | /masters/locations |
| [x] | Plants | /masters/plants |
| [x] | Warehouses | /masters/warehouses |
| [x] | Document Series | /masters/document-series |
Masters total: 13 modules × 5 endpoints (+ plants/warehouses aliases)
Vendors (/vendors) — module: VENDOR
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /vendors |
view | List (search, status, vendor_type) |
| [x] | GET | /vendors/gst-treatments |
view | GST treatment dropdown options |
| [x] | GET | /vendors/source-of-supply |
view | Indian states/UTs dropdown options |
| [x] | POST | /vendors |
create | Auto vendor_code via VENDOR series |
| [x] | GET | /vendors/:id |
view | Detail + addresses, contacts, bank details, item mappings |
| [x] | PUT | /vendors/:id |
edit | Update vendor |
| [x] | PATCH | /vendors/:id/status |
edit | active / inactive / blacklisted |
| [x] | DELETE | /vendors/:id |
delete | Soft delete |
| [x] | GET | /vendors/:vendorId/addresses |
view | List addresses |
| [x] | POST | /vendors/:vendorId/addresses |
create | Add address |
| [x] | GET | /vendors/:vendorId/addresses/:addressId |
view | Get address |
| [x] | PUT | /vendors/:vendorId/addresses/:addressId |
edit | Update address |
| [x] | DELETE | /vendors/:vendorId/addresses/:addressId |
delete | Deactivate address |
| [x] | GET | /vendors/:vendorId/contacts |
view | List contacts |
| [x] | POST | /vendors/:vendorId/contacts |
create | Add contact |
| [x] | GET | /vendors/:vendorId/contacts/:contactId |
view | Get contact |
| [x] | PUT | /vendors/:vendorId/contacts/:contactId |
edit | Update contact |
| [x] | DELETE | /vendors/:vendorId/contacts/:contactId |
delete | Deactivate contact |
| [x] | GET | /vendors/:vendorId/bank-details |
view | List bank details (decrypted) |
| [x] | POST | /vendors/:vendorId/bank-details |
create | Add bank detail (encrypted) |
| [x] | GET | /vendors/:vendorId/bank-details/:bankDetailId |
view | Get bank detail |
| [x] | PUT | /vendors/:vendorId/bank-details/:bankDetailId |
edit | Update bank detail |
| [x] | DELETE | /vendors/:vendorId/bank-details/:bankDetailId |
delete | Deactivate bank detail |
| [x] | GET | /vendors/:vendorId/items |
view | List vendor–item mappings |
| [x] | POST | /vendors/:vendorId/items |
create | Map item to vendor |
| [x] | GET | /vendors/:vendorId/items/:mappingId |
view | Get item mapping |
| [x] | PUT | /vendors/:vendorId/items/:mappingId |
edit | Update rate / preferred flag |
| [x] | DELETE | /vendors/:vendorId/items/:mappingId |
delete | Deactivate item mapping |
Purchase Orders (/purchase-orders) — module: PURCHASE_ORDER
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /purchase-orders |
view | List POs |
| [x] | GET | /purchase-orders/:id |
view | PO detail + line items |
| [x] | POST | /purchase-orders |
create | Create PO |
| [x] | PUT | /purchase-orders/:id |
edit | Update PO |
| [x] | DELETE | /purchase-orders/:id |
delete | Soft delete |
| [x] | POST | /purchase-orders/:id/submit |
edit | Submit for approval |
| [x] | POST | /purchase-orders/:id/approve |
approve | Approve PO |
| [x] | POST | /purchase-orders/:id/reject |
approve | Reject PO |
| [x] | POST | /purchase-orders/:id/amend |
edit | Amend PO |
| [x] | POST | /purchase-orders/:id/cancel |
edit | Cancel PO |
| [x] | GET | /purchase-orders/:id/pdf |
view | PDF export |
GRN (/grn) — module: GRN
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /grn |
view | List GRNs |
| [x] | GET | /grn/:id |
view | GRN detail + line items |
| [x] | POST | /grn |
create | Create GRN (transactional) |
| [x] | PUT | /grn/:id |
edit | Update GRN |
| [x] | POST | /grn/:id/cancel |
edit | Cancel GRN |
| [x] | GET | /grn/:id/pdf |
view | PDF export |
| [x] | GET | /grn/:grnId/attachments |
view | List GRN attachments |
| [x] | POST | /grn/:grnId/attachments |
edit | Upload file (multipart file) |
| [x] | GET | /grn/:grnId/attachments/:attachmentId |
view | Attachment metadata |
| [x] | GET | /grn/:grnId/attachments/:attachmentId/download |
view | Download file (authenticated) |
| [x] | DELETE | /grn/:grnId/attachments/:attachmentId |
delete | Delete attachment + file |
Assets (/assets) — module: ASSET
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /assets |
view | List assets |
| [x] | GET | /assets/:id |
view | Asset detail |
| [x] | POST | /assets |
create | Create asset |
| [x] | PUT | /assets/:id |
edit | Update asset |
| [x] | DELETE | /assets/:id |
delete | Soft delete |
| [x] | POST | /assets/:id/transfer |
edit | Transfer asset |
| [x] | GET | /assets/:id/transfer-history |
view | Transfer history |
AMC contracts (/assets/:id/amc)
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /assets/:id/amc |
view | List AMC contracts |
| [x] | POST | /assets/:id/amc |
create | Add AMC contract |
| [x] | GET | /assets/:id/amc/:contractId |
view | AMC detail |
| [x] | PUT | /assets/:id/amc/:contractId |
edit | Update AMC |
| [x] | PATCH | /assets/:id/amc/:contractId/renew |
edit | Renew AMC (deactivate old, create new) |
Service visits (/assets/:id/service-visits)
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /assets/:id/service-visits |
view | List visits |
| [x] | POST | /assets/:id/service-visits |
create | Log visit |
| [x] | GET | /assets/:id/service-visits/:visitId |
view | Visit detail |
| [x] | PUT | /assets/:id/service-visits/:visitId |
edit | Update visit |
| [x] | PATCH | /assets/:id/service-visits/:visitId/status |
edit | Update visit status |
Insurance (/assets/:id/insurance)
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /assets/:id/insurance |
view | List policies |
| [x] | POST | /assets/:id/insurance |
create | Add policy |
| [x] | GET | /assets/:id/insurance/:policyId |
view | Policy detail |
| [x] | PUT | /assets/:id/insurance/:policyId |
edit | Update policy |
| [x] | PATCH | /assets/:id/insurance/:policyId/renew |
edit | Renew policy |
Alerts (cross-asset)
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /assets/alerts/expiry |
view | AMC/Insurance/Warranty expiry (?days=30/60/90&type=) |
| [x] | GET | /assets/alerts/service |
view | Overdue/upcoming service (?status=OVERDUE) |
Settings (/settings) — module: SETTINGS
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /settings/company |
view | Company profile (org name, GSTIN, contact, address, logo URL) |
| [x] | PUT | /settings/company |
edit | Update company profile |
| [x] | POST | /settings/company/logo |
edit | Upload logo (multipart/form-data, field logo) |
| [x] | GET | /settings/email |
view | SMTP settings (has_smtp_password flag; password never returned) |
| [x] | PUT | /settings/email |
edit | Update SMTP settings (password encrypted at rest) |
DB patch: scripts/patch-company-email-settings.sql — creates company + email_settings singleton tables and SETTINGS module permissions for Super Admin.
DB patch: scripts/patch-company-gstin.sql — adds company.gstin for PDF document headers.
Audit Logs (/audit-logs) — module: AUDIT_LOGS
| Status | Method | Endpoint | RBAC | Notes |
|---|---|---|---|---|
| [x] | GET | /audit-logs/filters |
view | Distinct table_names, actions, performers for FE dropdowns |
| [x] | GET | /audit-logs |
view | Filtered list; empty by default until at least one filter is applied |
| [x] | GET | /audit-logs/:id |
view | Full detail with old_value / new_value JSON |
| [x] | GET | /audit-logs/export |
export | CSV export (filters required) |
List filters: table_name, record_id, action, performed_by, request_id, date_from, date_to, search (+ page, limit).
DB patch: scripts/patch-audit-logs-module.sql — AUDIT_LOGS module with view + export for Super Admin.
DB patch: run scripts/patch-assets-amc-insurance.sql then scripts/patch-assets-views.sql on deployed DB.
API progress summary
| Module | Endpoints done | Endpoints total | Status |
|---|---|---|---|
| System | 4 | 4 | [x] Done |
| Auth | 3 | 5 | [ ] Partial |
| Users | 8 | 8 | [x] Done |
| Roles | 10 | 10 | [x] Done |
| Masters | 70 | 70 | [x] Done |
| Vendors | 27 | 27 | [x] Done |
| Purchase Orders | 11 | 11 | [x] Done |
| GRN | 11 | 11 | [x] Done |
| Assets | 24 | 24 | [x] Done |
| Settings | 5 | 5 | [x] Done |
| Audit Logs | 4 | 4 | [x] Done |
| Total | 172 | 172 | [x] Phase 1 APIs + Asset extensions |
6. Security Tasks
- JWT access tokens (15m) in
Authorization: Bearerheader - Refresh tokens: opaque, SHA-256 hashed in DB, rotated on each refresh
- bcrypt password hashing (
BCRYPT_SALT_ROUNDS=12) - Account lockout after
MAX_LOGIN_ATTEMPTSfailures authenticate+authorize(module, action)on every protected route- AES-256-GCM field encryption + HMAC blind index for searchable PII
- Rate limiting: global on
/api, stricter on/auth/loginand/auth/forgot-password - Helmet, CORS (no
*in production), HPP, compression - File uploads: MIME allow-list, random filenames, RBAC-protected download (not direct web serve)
auditLog()on every create/update/status-change/approve/reject (completed modules)X-Request-Idon every request for log correlation
RBAC permission actions
view, create, edit, delete, approve, export
7. API Conventions
- Versioning:
/api/v1/... - Pagination:
?page=1&limit=20(cap limit at 100); responsemeta: { page, limit, total } - Filtering:
?status=...&search=...per module - Sorting:
?sort=-created_at(-= descending) - IDs:
BigIntserialized as strings in JSON - Dates: ISO 8601 UTC
- Soft delete: set
deleted_at, never hard-delete transactional records - Response envelope:
{ success, message, data?, meta?, errors? }
8. Testing
- Jest + Supertest against
app.js(no live server) - Separate test DB via
.env.test+prisma migrate deploy - Mirror structure:
tests/modules/<module>/<module>.test.js
Minimum Phase 1 coverage
- Auth: login, refresh, lockout
- RBAC: allowed vs forbidden
- Vendor: CRUD + status transitions
- PO: status lifecycle
- GRN: partial receipt → PO status recalculation
9. Documentation & DevOps
- OpenAPI spec for completed routes (
src/docs/completed-routes.yaml+/api-docs) - Add
@swaggerJSDoc blocks to route files (optional; YAML spec in use) Dockerfile(Node 20 Alpine)docker-compose.yml(API + Postgres 15)- Verify
docker-compose upboots API + Postgres - Run
npm auditin CI
10. Future Development Guidelines
When adding a new module or endpoint:
- Add Prisma model with common columns + soft delete
- Add module/permissions to seed (if new domain)
- Create the four-file HMVC module (five if multi-table transactions)
- Register routes in
src/routes/v1/index.js - Apply middleware chain:
authenticate→authorize(MODULE, action)→validate(schema)→ controller - Call
auditLog()in service for all mutations - Use
nextDocumentNumber()for auto-generated codes - Encrypt PII at service layer with blind index for search
- Add Swagger JSDoc + Jest tests
- Breaking API changes → new version (
/api/v2)
Complex transaction modules
Use *.repository.js with prisma.$transaction when a single operation touches multiple tables (e.g. PO header + items, GRN + PO qty update + asset creation).
Masters replication
Copy the UOM module pattern for new masters: simple name/code fields, RBAC code MASTERS, no document numbering.