467 lines
23 KiB
Markdown
467 lines
23 KiB
Markdown
# ERP Backend — Development Tasks (Phase 1)
|
||
|
||
Reference: [BACKEND_SETUP.md](./BACKEND_SETUP.md)
|
||
|
||
Use this checklist when building or extending the backend. Follow the build order in Section 1.
|
||
|
||
---
|
||
|
||
## 1. Project Scaffold
|
||
|
||
- [x] Create `package.json` with dependencies and scripts from the spec
|
||
- [x] Install dependencies (`npm install`)
|
||
- [x] Add `.env.example` (never commit `.env`)
|
||
- [x] Configure ESLint, Prettier, Husky, lint-staged
|
||
- [x] Add `jest.config.js` with `dotenv/config` setup file
|
||
|
||
---
|
||
|
||
## 2. Database & Prisma
|
||
|
||
- [x] 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
|
||
- [x] Use `PascalCase` models, `snake_case` fields, `@@map` to 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`
|
||
- [x] Run `npx prisma generate`
|
||
- [x] 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
|
||
|
||
- [x] `src/config/env.js` — Joi validation; app must refuse to boot on invalid/missing env
|
||
- [x] `src/config/logger.js` — Winston + daily rotate; redact sensitive keys
|
||
- [x] `src/config/morgan.js` — HTTP logs into Winston with request ID + user
|
||
- [x] `src/config/prisma.js` — singleton client with query/error logging
|
||
- [x] `src/config/swagger.js` — OpenAPI 3.0 from `src/docs/completed-routes.yaml`
|
||
- [x] `src/utils/` — ApiError, ApiResponse, asyncHandler, encryption, auditLog, pagination, generateCode
|
||
- [x] `src/middlewares/` — requestId, error, validate, rateLimiter, auth, rbac, upload
|
||
- [x] `src/app.js` — Helmet, CORS, HPP, compression, rate limit, routes, Swagger, 404, error handler
|
||
- [x] `src/server.js` — graceful shutdown, BigInt JSON serializer, unhandled rejection/exception handlers
|
||
- [x] Verify `GET /health` responds
|
||
|
||
---
|
||
|
||
## 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, profile, avatar, change/forgot/reset password |
|
||
| 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:
|
||
|
||
- [x] `auth` module (`login`, `refresh`, `logout`)
|
||
- [x] `users` module (screen APIs: summary, filters, export, CRUD)
|
||
- [x] `roles` module (cards, permission catalog, matrix, CRUD)
|
||
- [x] `masters/uom` template + 13 replicated sub-masters
|
||
- [x] Swagger docs for all completed routes (`src/docs/completed-routes.yaml`)
|
||
- [x] `vendors` module (CRUD, status, addresses, contacts, bank-details)
|
||
- [x] `purchase-orders` module (CRUD, workflow, PDF)
|
||
- [x] `grn` module (transactional receipt, PO status recalc, asset auto-creation)
|
||
- [x] `assets` module (CRUD, transfer, AMC, service visits, insurance, alerts)
|
||
- [x] `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 |
|
||
| [x] | POST | `/auth/forgot-password` | Public | Sends reset link via SMTP (`email_settings`) |
|
||
| [x] | POST | `/auth/reset-password` | Public | Reset password with token from email |
|
||
| [x] | GET | `/auth/me` | Authenticated | Current user + permissions (for FE) |
|
||
| [x] | PUT | `/auth/profile` | Authenticated | Update `full_name` / `email` / `mobile` |
|
||
| [x] | POST | `/auth/profile/avatar` | Authenticated | Upload avatar (`multipart`, field `avatar`) |
|
||
| [x] | POST | `/auth/change-password` | Authenticated | Change password (revokes refresh tokens) |
|
||
|
||
**DB patch:** `scripts/patch-users-avatar.sql` — adds `users.avatar_path`.
|
||
|
||
**Env:** `FRONTEND_URL` (reset link base), `PASSWORD_RESET_EXPIRY_MINUTES` (default 60).
|
||
|
||
---
|
||
|
||
### 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` | Optional asset defaults: `code_prefix`, `default_useful_life_years`, `default_depreciation_method` |
|
||
| [x] | Item Subcategories | `/masters/item-subcategories` | Filter: `item_category_id` — shared by items + assets |
|
||
| [x] | Items | `/masters/items` | `item_code` auto via `ITEM` document series; HSN via `hsn_code_id` |
|
||
| [x] | Brands | `/masters/brands` |
|
||
| [x] | GST Rates | `/masters/gst-rates` |
|
||
| [x] | HSN Codes | `/masters/hsn-codes` | Search by `code` or `description` |
|
||
| [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` | Unified plants + warehouses (`type`: `plant` \| `warehouse`) |
|
||
| [x] | Plants | `/masters/plants` | Alias — plant locations only |
|
||
| [x] | Warehouses | `/masters/warehouses` | Alias — warehouse locations only |
|
||
| [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 |
|
||
| [x] | GET | `/purchase-orders/:poId/attachments` | view | List PO attachments |
|
||
| [x] | POST | `/purchase-orders/:poId/attachments` | edit | Upload file (multipart `file`) |
|
||
| [x] | GET | `/purchase-orders/:poId/attachments/:attachmentId` | view | Attachment metadata |
|
||
| [x] | GET | `/purchase-orders/:poId/attachments/:attachmentId/download` | view | Download file (authenticated) |
|
||
| [x] | DELETE | `/purchase-orders/:poId/attachments/:attachmentId` | delete | Delete attachment + file |
|
||
|
||
**Totals:** `taxable_amount = sub_total + freight + other − discount`; GST (`tax_total` / CGST+SGST / IGST) is calculated on `taxable_amount` (not on `sub_total`). Column persisted via `scripts/patch-po-taxable-amount.sql`.
|
||
|
||
---
|
||
|
||
### 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 |
|
||
| [x] | GET | `/assets/:assetId/attachments` | view | List asset attachments |
|
||
| [x] | POST | `/assets/:assetId/attachments` | edit | Upload file (multipart `file`; optional `attachment_type`, AMC/visit/insurance link) |
|
||
| [x] | GET | `/assets/:assetId/attachments/:attachmentId` | view | Attachment metadata |
|
||
| [x] | GET | `/assets/:assetId/attachments/:attachmentId/download` | view | Download file (authenticated) |
|
||
| [x] | DELETE | `/assets/:assetId/attachments/:attachmentId` | delete | Delete attachment + file |
|
||
|
||
**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/favicon URLs) |
|
||
| [x] | PUT | `/settings/company` | edit | Update company profile |
|
||
| [x] | POST | `/settings/company/logo` | edit | Upload logo (`multipart/form-data`, field `logo`) |
|
||
| [x] | POST | `/settings/company/favicon` | edit | Upload favicon (`multipart/form-data`, field `favicon`) |
|
||
| [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.
|
||
|
||
**DB patch:** `scripts/patch-company-favicon.sql` — adds `company.favicon_path`.
|
||
|
||
---
|
||
|
||
### 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 | 9 | 9 | [x] Done |
|
||
| 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
|
||
|
||
- [x] JWT access tokens (15m) in `Authorization: Bearer` header
|
||
- [x] Refresh tokens: opaque, SHA-256 hashed in DB, rotated on each refresh
|
||
- [x] bcrypt password hashing (`BCRYPT_SALT_ROUNDS=12`)
|
||
- [x] Account lockout after `MAX_LOGIN_ATTEMPTS` failures
|
||
- [x] `authenticate` + `authorize(module, action)` on every protected route
|
||
- [x] AES-256-GCM field encryption + HMAC blind index for searchable PII
|
||
- [x] Rate limiting: global on `/api`, stricter on `/auth/login` and `/auth/forgot-password`
|
||
- [x] Helmet, CORS (no `*` in production), HPP, compression
|
||
- [x] File uploads: MIME allow-list, random filenames, RBAC-protected download (not direct web serve)
|
||
- [x] `auditLog()` on every create/update/status-change/approve/reject (completed modules)
|
||
- [x] `X-Request-Id` on 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); response `meta: { page, limit, total }`
|
||
- Filtering: `?status=...&search=...` per module
|
||
- Sorting: `?sort=-created_at` (`-` = descending)
|
||
- IDs: `BigInt` serialized 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
|
||
- [x] PO: status lifecycle
|
||
- [x] GRN: partial receipt → PO status recalculation
|
||
|
||
---
|
||
|
||
## 9. Documentation & DevOps
|
||
|
||
- [x] OpenAPI spec for completed routes (`src/docs/completed-routes.yaml` + `/api-docs`)
|
||
- [ ] Add `@swagger` JSDoc blocks to route files (optional; YAML spec in use)
|
||
- [ ] `Dockerfile` (Node 20 Alpine)
|
||
- [ ] `docker-compose.yml` (API + Postgres 15)
|
||
- [ ] Verify `docker-compose up` boots API + Postgres
|
||
- [ ] Run `npm audit` in CI
|
||
|
||
---
|
||
|
||
## 10. Future Development Guidelines
|
||
|
||
When adding a new module or endpoint:
|
||
|
||
1. Add Prisma model with common columns + soft delete
|
||
2. Add module/permissions to seed (if new domain)
|
||
3. Create the four-file HMVC module (five if multi-table transactions)
|
||
4. Register routes in `src/routes/v1/index.js`
|
||
5. Apply middleware chain: `authenticate` → `authorize(MODULE, action)` → `validate(schema)` → controller
|
||
6. Call `auditLog()` in service for all mutations
|
||
7. Use `nextDocumentNumber()` for auto-generated codes
|
||
8. Encrypt PII at service layer with blind index for search
|
||
9. Add Swagger JSDoc + Jest tests
|
||
10. 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.
|