# Bhagirath ERP: Technical Handover

This document is for the technical project manager taking over the system. It explains what the
software does, how it is built, which file does what, how the database is organised, where each value
lives, and how the business flows connect, so you can change things safely.

| Companion document | Use it for |
|---|---|
| [DATABASE_REFERENCE.md](DATABASE_REFERENCE.md) | Every table and column, with keys and references (generated from the live schema) |
| [SETUP_GUIDE_WINDOWS.md](SETUP_GUIDE_WINDOWS.md) | First-time local setup on Windows |
| [SETUP_GUIDE_MAC.md](SETUP_GUIDE_MAC.md) | First-time local setup on macOS |
| [backend/scripts/README.md](../backend/scripts/README.md) | The data-loading and maintenance scripts |

---

## Contents

1. [The system at a glance](#1-the-system-at-a-glance)
2. [Repository layout](#2-repository-layout)
3. [How a request flows](#3-how-a-request-flows)
4. [Configuration](#4-configuration)
5. [Sign-in, permissions and station access](#5-sign-in-permissions-and-station-access)
6. [Backend reference](#6-backend-reference)
7. [Frontend reference](#7-frontend-reference)
8. [Database guide](#8-database-guide)
9. [Business flows end to end](#9-business-flows-end-to-end)
10. [What affects what](#10-what-affects-what)
11. [How to make common changes](#11-how-to-make-common-changes)
12. [Operations: run, back up, restore, upgrade](#12-operations-run-back-up-restore-upgrade)
13. [Testing and checks](#13-testing-and-checks)
14. [Known limitations](#14-known-limitations)
15. [Glossary](#15-glossary)

---

## 1. The system at a glance

The ERP runs a bus body-building plant: a customer chassis arrives at the yard, moves through the
production stations, passes quality gates and leaves through dispatch. Around that core it handles
sales and dealer orders, bills of material, purchasing and stock, a vendor portal, a dealer portal and
a maintenance portal.

| Portal | URL path | Who uses it | Signs in with |
|---|---|---|---|
| Staff ERP | `/` | Office staff, supervisors, operators, QA inspectors | Staff account (`login_users`) |
| Station screens | `/my-station`, `/qa/inspect` | Operators and QA on phones at the station | Staff account |
| Dealer portal | `/dealer-portal` | Dealers placing and tracking orders | Dealer login (`customer_master.portal_username`) |
| Vendor portal | `/vendor-portal` | Suppliers: POs, shipping notices, rejections | Vendor login (`vendor_master.portal_username`) |
| Maintenance portal | `/maintenance-portal` | Maintenance team | Staff account with MAINTENANCE permission |
| Public tracking | `/track/<chassis no>` | Anyone with the QR code | No sign-in |

### Technology

| Layer | Technology | Version |
|---|---|---|
| Database | PostgreSQL | 18 (the dump is a PostgreSQL 18 custom-format archive) |
| Backend | Python, FastAPI, SQLAlchemy 2 (async, asyncpg), Alembic, Pydantic 2 | Python 3.12 |
| Frontend | React 18, Vite, React Router, TanStack Query, Tailwind CSS, axios | Node 20 or newer |
| Scanning | html5-qrcode (phone camera) | |

### Ports

| Service | Port | Address |
|---|---|---|
| Frontend (Vite dev server) | 5173 | http://localhost:5173 |
| Backend API | 8000 | http://localhost:8000 (interactive API docs at `/docs`) |
| PostgreSQL | 5432 | localhost |

---

## 2. Repository layout

```
Audi_new/
├── backend/                      FastAPI application
│   ├── app/
│   │   ├── main.py               Creates the app, CORS, mounts every router under /api/v1, serves /uploads
│   │   ├── core/                 Settings, database session, security, logging, document numbering
│   │   ├── models/               SQLAlchemy models, one file per business area (+ table_catalog.py)
│   │   └── modules/              One folder per business area: routes.py, schemas.py, service.py
│   ├── migrations/               Alembic environment and versions/ (database schema history)
│   ├── scripts/                  Seed and Excel ingest scripts, table comments, DB reference generator
│   ├── tests/                    pytest tests (run against the configured database)
│   ├── uploads/                  Uploaded photos and documents (not in git; back this up)
│   ├── logs/                     error.log (unhandled exceptions; not in git)
│   ├── requirements.txt          Python dependencies
│   ├── alembic.ini, pytest.ini
│   └── .env.example              Template for backend/.env
├── frontend/                     React application
│   ├── src/
│   │   ├── main.jsx              Entry point: React Query client, router, toaster
│   │   ├── App.jsx               Every route and which page renders it
│   │   ├── api/                  One file per backend area: axios calls wrapped in React Query hooks
│   │   ├── components/           layout/ (menus), ui/ (shared widgets), qa/, station/, sales/
│   │   ├── pages/                One folder per area; one file per screen
│   │   ├── hooks/                useAuth, usePermissions, useDebouncedValue
│   │   ├── store/authStore.js    Signed-in user state
│   │   └── utils/                Date and duration formatting, confirm dialog, image compression
│   ├── package.json, vite.config.js, tailwind.config.js
│   └── .env.example
├── scripts/                      setup_mac.sh and run_mac.sh (one-command Mac setup and start)
├── docs/                         This handover, setup guides, database reference, change summary
└── README.md
```

---

## 3. How a request flows

Take "operator checks a chassis in at a station" as the example.

```
Phone browser
  └─ pages/station/MyStation.jsx            button "Check in"
       └─ api/production.js → useScan()     axios POST /api/v1/production/scan   (token attached by api/client.js)
            └─ backend app/main.py          router mounted at /api/v1/production
                 └─ modules/production/routes.py  scan()           Depends(require_permission('PRODUCTION','create'))
                      ├─ core/security.py   get_current_user → user_has_permission (role_module_permission)
                      ├─ station_work/routes.ensure_station_access  (station_user_assignment)
                      └─ modules/production/service.py  scan_chassis()
                           ├─ station_work/flow.py  check_arrival()  ← blocks if previous station's QA has not released it
                           ├─ writes chassis_station_log, updates chassis_master
                           └─ returns a message; React Query refreshes the station board
```

Rules that hold everywhere:

- **Transactions.** `core/database.get_db` opens one session per request, commits when the handler
  returns and rolls back on any exception. Services may also commit explicitly.
- **Errors.** Handlers raise `HTTPException(status, detail)`. The frontend shows `detail` in a toast.
  Anything unhandled is caught in `main.py`, written with the full traceback to `backend/logs/error.log`
  and returned as a generic 500.
- **Layering.** `routes.py` declares endpoints, permissions and request/response schemas;
  `service.py` holds the business logic; `schemas.py` holds Pydantic models. Small modules keep the
  logic in `routes.py`.
- **Frontend data.** Pages never call axios directly; they use hooks from `src/api/*.js`. After a
  change the hook invalidates the related React Query keys, so screens refresh themselves.

---

## 4. Configuration

### backend/.env (copy from `backend/.env.example`)

| Variable | Purpose |
|---|---|
| `DATABASE_URL` | Async connection used by the app (`postgresql+asyncpg://user:pass@host:5432/erp_db`) |
| `DATABASE_URL_SYNC` | Sync connection used by Alembic migrations (`postgresql://...`) |
| `ALLOWED_ORIGINS` | JSON list of browser origins allowed to call the API (the frontend URL) |
| `ALLOWED_ORIGIN_REGEX` | Optional pattern for further origins; the template allows private network addresses on port 5173 so phones on the plant Wi-Fi can use the app |
| `SECRET_KEY` | Signs login tokens. Changing it signs everyone out. Keep it secret. |
| `ALGORITHM` | JWT algorithm, `HS256` |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | Staff token lifetime (43200 = 30 days) |
| `REFRESH_TOKEN_EXPIRE_DAYS` | Refresh lifetime |
| `DEBUG` | `True` prints every SQL statement; keep `False` outside development |

Read by `backend/app/core/config.py` (`settings`), and by `migrations/env.py` for `DATABASE_URL_SYNC`.

### frontend/.env (optional, from `frontend/.env.example`)

| Variable | Purpose |
|---|---|
| `VITE_API_URL` | API base URL. Leave unset: the app then calls port 8000 on whatever host served the page, which is what lets phones on the plant network reach the API. |

### Values deliberately kept in code

Status codes that drive logic (for example `WIP`, `RELEASED`, `AWAITING_QA`) and permission module
codes are part of the program, not configuration. Everything a user would want to edit lives in the
database: dropdown lists, stations and their order, QA checklists, roles and permissions, station
staff, document types, gate-pass and thermal checklist items.

---

## 5. Sign-in, permissions and station access

### Staff sign-in

1. `POST /api/v1/auth/login` with employee code (or email) and password, checked against
   `login_users.password_hash` (bcrypt).
2. The API returns a JWT signed with `SECRET_KEY`. The frontend stores it in `localStorage`
   (`access_token`) and `api/client.js` sends it as `Authorization: Bearer ...` on every call.
3. Each protected endpoint depends on `get_current_user` (`core/security.py`), which decodes the token
   and loads the user. Inactive users are refused.

The dealer and vendor portals issue their own tokens (`dealer_token`, `vendor_token`) tied to a
session row (`dealer_portal_log`, `vendor_portal_log`) that is closed on logout. The maintenance portal
uses a staff sign-in stored as `maintenance_token`.

### Permissions: all data, no code

| Table | Holds |
|---|---|
| `role_master` | Roles |
| `login_users.role_code` | The one role each user has |
| `module_master` | Modules permissions are granted on (PRODUCTION, QA, YARD, SALES, ...). `is_active = false` blocks a module for everyone. |
| `role_module_permission` | Per role and module: `can_view`, `can_create`, `can_edit`, `can_delete`, `can_approve` |
| `user_permission_override` | Per-user exception to one flag (a non-null value wins over the role) |

Backend: `Depends(require_permission('MODULE', 'action'))` on an endpoint. Frontend: `usePermissions()`
(`hooks/usePermissions.js`) reads the same resolved permissions from `GET /auth/me/permissions` and
hides menu entries and buttons accordingly. There is no hidden admin bypass: ADMIN works because its
rows grant everything. Change permissions in **Admin > Users & Access**; no redeploy is needed.

Modules in use: BOM, DEALER_PORTAL_ADMIN, INVENTORY, LOCATIONS, MAINTENANCE, PROCUREMENT, PRODUCTION,
PRODUCTION_MASTERS, QA, RM_MASTER, ROLE_MANAGEMENT, SALES, STORE, SYSTEM_LOGS, USER_MANAGEMENT, VENDOR,
VENDOR_PORTAL_ADMIN, YARD.

### Station access (operators and QA)

`station_user_assignment` links a user to a station with a role, `OPERATOR` or `QA`. It is managed on
**Setup > Station Staff**. The rule, in `station_work/routes.ensure_station_access`:

- A user assigned to stations in that role may act **only** at those stations.
- A user with no assignment in that role falls back to the module permission (PRODUCTION view for
  operators, QA view for inspectors), so supervisors and admins can act everywhere.
- A user assigned to several stations gets one tab per station on **My Station** and **QA Inspection**.

### Public endpoints

Only `GET /api/v1/yard/track/{chassis_no}` (QR tracking page), the login endpoints and `/health`
work without signing in.

---

## 6. Backend reference

### 6.1 Core (`backend/app/core`)

| File | Responsibility |
|---|---|
| `config.py` | Loads `backend/.env` into `settings` |
| `database.py` | Async engine, `AsyncSessionLocal`, `Base`, `get_db` (commit or rollback per request) |
| `security.py` | Password hashing, JWT create/decode, `get_current_user`, `user_has_permission`, `require_permission`, `require_permission_any` |
| `logging.py` | `app_logger` writing errors to `backend/logs/error.log` (5 MB x 5 files) |
| `numbering.py` | `next_document_number()`: sequential `PREFIX-YEAR-00001` numbers for PO, GRN and debit notes, serialised with a database lock |

### 6.2 Models (`backend/app/models`)

One file per business area. `__init__.py` imports them all, so importing any model registers every
model, and attaches the table descriptions from `table_catalog.py`.

| File | Tables |
|---|---|
| `core_models.py` | login_users |
| `access_models.py` | role_master, module_master, role_module_permission, user_permission_override |
| `master_list_models.py` | master_list_registry and the 17 dropdown tables (`*_master`, generated from `MASTER_LIST_TABLES`) |
| `location_models.py` | chassis_location_master, station_qa_checklist_item_master, chassis_station_qa_result |
| `rm_models.py` | station_master, store_master, vendor_master, rm_master and stock, purchase requests and orders, GRN, monthly plans |
| `yard_models.py` | chassis_master and its logs, arrival documents, yard checklists, store hand-over, customer-supplied parts, vehicle_model_master |
| `production_models.py` | chassis_station_log, production_hold, delay_reason_master |
| `station_work_models.py` | station_user_assignment, chassis_station_handoff, qa_response_photo, qa_section_remark |
| `qa_models.py` | QA templates, sections, checkpoints, station mapping, instances, responses, sign-offs, shortages |
| `dispatch_models.py` | gate pass and thermal checksheet with items |
| `bom_models.py` | product_master, bom_master, bom_detail, special_feature_bom |
| `procurement_models.py` | chassis_bom_snapshot, rm_purchase_request(_detail) |
| `dealer_models.py` | dealer orders and vehicles, dealer clients and requests, body types, segments, special features, seating layouts, seat rules, RTO |
| `sales_models.py` | customers, contacts, clients, transporters, enquiries, payments, goodwill, service and spare-parts orders, sales templates |
| `vendor_portal.py` | ASN, rejections, NCR, debit notes, disputes, scorecards, vendor portal logs |
| `maintenance_models.py` | assets, records, PM schedule, consumables, stock, cost, diesel, LPG, solar, rentals |
| `production_report_models.py` | production_report_manual_entry |
| `table_catalog.py` | Plain-language description of every table, grouped by area |

### 6.3 Modules (`backend/app/modules`)

All routes are under `/api/v1`. The full, live list with request and response shapes is at
http://localhost:8000/docs.

| Module folder | Prefix | Responsibility | Key files |
|---|---|---|---|
| `auth` | `/auth` | Staff login, current user and permissions, admin "view as vendor/dealer" | routes.py |
| `access_control` | `/access-control` | Users, roles, permission matrix, audit logs | routes.py |
| `lookups` | `/lookups` | Admin-managed dropdown lists (registry + 17 master tables) | routes.py |
| `station` | `/stations` | Station master CRUD and dropdown | routes.py, service.py |
| `station_work` | `/station-work` | Station staff assignments; operator and QA station boards; QA release | routes.py, **flow.py** (hand-over state machine) |
| `yard` | `/yard` | Chassis inward, arrival documents, DA-lite, yard QA checklist, movements, store hand-over, hand-over to production, reports, public tracking, locations and stations admin, production report | service.py, production_dashboard.py, production_report_service.py |
| `production` | `/production` | Station scan (check in / complete), floor dashboard, timeline, overdue, delays, holds, delay reasons, bottleneck analytics | service.py |
| `qa` | `/qa` | Checksheet templates, station mapping, inspections, responses, photos, remarks, sign-off, shortages, QA dashboard, chassis QA history | service.py |
| `dispatch` | `/dispatch` | Dispatch queue, thermal checksheet, gate pass | service.py |
| `sales` | `/sales` | Sales dashboard, customers, clients, transporters, enquiries, induction-ready queue and induction, payments, goodwill, spare parts, service orders, dealer requests, sales templates | routes.py, dashboard.py |
| `dealer_portal` | `/dealer-portal` | Everything a dealer does in their portal | service.py |
| `dealer_portal_admin` | `/admin/dealer-portal` | Staff review, confirmation and chassis allocation for dealer orders | service.py |
| `production_masters` | `/production-masters` | Vehicle models, body types, segments, RTOs, special features and options, model/segment/feature links, seating layouts, seat rules | service.py |
| `production_masters` (bom_analysis_routes.py) | `/bom-analysis` | BOM coverage dashboard, feature BOM summary | bom_analysis_routes.py |
| `bom` | `/bom` | Products, BOMs and BOM lines, feature BOMs | routes.py |
| `rm_master` | `/rm-master` | Raw material master | routes.py |
| `vendor` | `/vendors` | Vendor master and vendor-material links | routes.py |
| `store` | `/stores` | Store master | routes.py |
| `inventory` | `/inventory` | Stock balances, consume, transfer, ledger | service.py |
| `procurement` | `/procurement` | Procurement engine per chassis, bulk engine, purchase requests, POs, GRN, debit notes, monthly plans | engine_service.py, bulk_engine_service.py, routes.py |
| `vendor_portal` | `/portal` | Vendor login, POs, ASN, rejections, NCR, debit notes, monthly plans | service.py |
| `vendor_portal_admin` | `/admin/vendor-portal` | Enable vendor portal access | service.py |
| `maintenance` | `/maintenance` | Assets, records, checklists, PM schedule, consumables, stock, costs, diesel, LPG, solar, rentals, purchases | routes.py |

### 6.4 Key functions to know

| Function | File | What it does |
|---|---|---|
| `scan_chassis` | production/service.py | Check in (ARRIVAL) or complete (COMPLETION) a chassis at a station; enforces the QA gate; sets `tracking_status = WIP` on first check-in; computes cycle time |
| `check_arrival`, `on_completion`, `set_qa_outcome`, `release`, `next_station` | station_work/flow.py | The station-to-station hand-over state machine (section 9.4) |
| `start_inspection`, `submit_responses_bulk`, `signoff_instance` | qa/service.py | QA inspection lifecycle; sign-off tells the hand-over whether QA passed |
| `_deduct_inventory_on_qa_pass` | qa/service.py | Consumes the chassis BOM parts for that station from stock when QA passes |
| `create_chassis`, `update_da_lite`, `upsert_qa_checklist`, `handover_to_production`, `station_scan` | yard/service.py | Chassis inward to hand-over; `station_scan` moves yard tracking statuses (FVI, dispatch) |
| `induct_chassis_to_production` | sales/routes.py | Inducts scheduled chassis: `HANDED_TO_PRODUCTION`, procurement check pending |
| `ProcurementEngineService` | procurement/engine_service.py | Builds the chassis BOM snapshot (base BOM + feature BOMs), checks stock, raises shortages |
| `build_dashboard` | sales/dashboard.py | Sales dashboard figures |
| `next_document_number` | core/numbering.py | Sequential document numbers |

### 6.5 Migrations (`backend/migrations`)

Alembic tracks the schema. The current head is `g3b4c5d6e7f8` (table descriptions). The latest
revisions, newest last:

| Revision | Change |
|---|---|
| `b2c3d4e5f6a8` | The revision the original database dump is at |
| `c7d8e9f0a1b2` | Admin-managed dropdown values |
| `e1f2a3b4c5d6` | 17 dropdown master tables and registry, station_user_assignment, qa_response_photo |
| `f2a3b4c5d6e7` | chassis_station_handoff (QA gate), qa_section_remark |
| `g3b4c5d6e7f8` | COMMENT ON TABLE for every table |

`alembic upgrade head` applies whatever a database is missing; it is safe to run repeatedly.

---

## 7. Frontend reference

### 7.1 Entry, routing and menus

| File | Role |
|---|---|
| `src/main.jsx` | Mounts the app with the React Query client and router |
| `src/App.jsx` | Every route: path → page component, grouped by portal. Old paths redirect with `<Navigate>`. |
| `components/layout/Layout.jsx` | Staff shell: side menu grouped by task, top bar, phone bottom tabs. Each menu entry names the permission module that shows it. |
| `components/layout/DealerLayout.jsx`, `VendorLayout.jsx`, `MaintenanceLayout.jsx` | Shells for the three portals |
| `api/client.js` | axios instance: picks the API host, attaches the right token per portal, redirects to login on 401 |

### 7.2 API hooks (`src/api`)

One file per backend area. Each exports `useXxx` query hooks and `useCreateXxx` / `useUpdateXxx`
mutation hooks.

| File | Backend area |
|---|---|
| `accessControl.js` | users, roles, permission matrix, `useMe`, `useMyPermissions` |
| `lookups.js` | dropdown lists (`useLookupOptions(category)`) |
| `station.js`, `stationWork.js` | stations; station staff, station boards, QA release, QA photos and remarks |
| `yard.js` | chassis inward, documents, DA-lite, checklists, hand-over, reports |
| `production.js` | scan, floor dashboard, holds, delays |
| `qa.js` | templates, mapping, inspections, history |
| `dispatch.js` | dispatch queue, thermal checksheet, gate pass |
| `sales.js`, `dealerOrdersAdmin.js`, `dealerPortal.js` | sales, staff side of dealer orders, dealer portal |
| `bom.js`, `productionMasters.js` | BOMs and feature BOMs; vehicle masters and features |
| `rm.js`, `vendor.js`, `store.js`, `inventory.js`, `procurement.js` | materials, vendors, stores, stock, purchasing |
| `maintenance.js` | maintenance portal |

### 7.3 Shared components (`src/components`)

| Component | Use |
|---|---|
| `ui/DataTable.jsx` | Sortable table used across list screens |
| `ui/Modal.jsx`, `ui/Drawer.jsx`, `ui/ConfirmModal.jsx`, `ui/PromptModal.jsx` | Dialogs; `utils/confirm.jsx` gives `await customConfirm(...)` |
| `ui/PageHeader.jsx`, `ui/StatCard.jsx` | Page title bar, KPI cards |
| `ui/SmartSelect.jsx`, `ui/LookupOptions.jsx` | Searchable select; options from a dropdown list in the database |
| `ui/ChassisScanner.jsx`, `ui/CameraCaptureModal.jsx` | Phone camera QR/barcode scan, photo capture |
| `station/StationTabs.jsx` | One tab per assigned station |
| `qa/QaSetupTabs.jsx` | Tabs across the QA setup screens |
| `sales/VehicleConfigurator.jsx` | Model, body, segment and feature selection |

### 7.4 Screens by area (`src/pages`)

| Area | Route(s) | Files |
|---|---|---|
| Sign-in | `/login`, `/choose-portal` | login/Login.jsx, ChoosePortal.jsx |
| Home | `/dashboard` | dashboard/Dashboard.jsx |
| Yard | `/yard`, `/yard/:id`, `/yard/reports`, `/station-scanner`, `/track/:chassisNo` | yard/YardList, YardChassisDetail, YardReports, StationScanner, ChassisTrackingPage, QaChecklistEditorModal |
| Production floor | `/production/dashboard`, `/production/report` | production/ProductionDashboard (floor, holds, delays tabs), ProductionReport |
| Operator station | `/my-station` | station/MyStation.jsx (scan or pick chassis, check in, complete) |
| QA | `/qa/dashboard`, `/qa/inspect`, `/qa/templates`, `/qa/station-config`, `/qa/chassis-history` | qa/QADashboard, QaInspect (phone inspection), TemplateManager, StationChecklistConfig, ChassisQAHistory |
| Stations | `/stations`, `/stations/staff` | locations/StationManager, StationStaff |
| Dispatch | `/dispatch`, `/dispatch/:id/thermal-checksheet`, `/dispatch/:id/gate-pass` | dispatch/DispatchQueue, ThermalChecksheet, GatePass |
| Sales | `/sales/...` | sales/SalesDashboard, CustomerList/Form/Profile, ClientMaster/Form/DetailDashboard, DealerOrders, DealerOrderReview, InductionReady, PreProductionQueue, DALiteHoldQueue, PaymentLog, GoodwillQueue, SparePartsOrder, ServiceOrderList, TransportList/Form, DealerRequests |
| BOM | `/bom`, `/bom/:id`, `/bom/features` | bom/BomControlCenter, BomForm, FeatureBomManager |
| Purchasing | `/procurement-engine`, `/monthly-planning`, `/purchase-orders`, `/grn`, `/debit-notes` | procurement/ProcurementEngine, MonthlyPlanning, PoList/PoForm, GrnList/GrnForm, DebitNoteList |
| Stock | `/stock-balance`, `/ledger`, `/consumption`, `/store-requests`, `/rm-master`, `/vendors`, `/stores` | inventory/*, rm/*, vendor/*, store/* |
| Setup and admin | `/production-masters`, `/admin/access-control`, `/admin/system-logs` | admin/ProductionMasters (incl. DropdownListsTab, SeatConfigsTab), AccessControl, SystemLogs |
| Dealer portal | `/dealer-portal/...` | dealer-portal/* |
| Vendor portal | `/vendor-portal/...` | vendor-portal/* |
| Maintenance portal | `/maintenance-portal/...` | maintenance-portal/* |

---

## 8. Database guide

Database `erp_db`, schema `public`, about 157 tables. Every table has a description stored in the
database: in pgAdmin select a table and open **Properties > General > Comment**, or run
`\dt+` in psql. The full column list is in [DATABASE_REFERENCE.md](DATABASE_REFERENCE.md).

### 8.1 Naming conventions

| Pattern | Meaning | Example |
|---|---|---|
| `*_master` | Reference data maintained by admins | `station_master`, `ac_make_master` |
| `*_log` | Append-only history; never edited | `chassis_status_log`, `chassis_station_log` |
| `*_map`, `*_mapping` | Link between two masters | `qa_station_template_map`, `rm_vendor_mapping` |
| `*_detail`, `*_item`, `*_line` | Lines of a header document | `rm_purchase_order_detail` |
| `<table>_id` | Primary key (UUID); the same name is used as the foreign key elsewhere | `chassis_id` |
| `is_active` | Soft delete: inactive rows stay for history but disappear from pickers | |
| `created_at`, `updated_at` | Timestamps in UTC | |

### 8.2 Business areas

| Area | Main tables | Hub table |
|---|---|---|
| Users and access | login_users, role_master, module_master, role_module_permission, user_permission_override, station_user_assignment | login_users |
| Dropdown lists | master_list_registry + 17 `*_master` tables | master_list_registry |
| Plant layout | chassis_location_master, station_master, store_master, yard_checkpoint_master, yard_slot_master | station_master |
| Chassis and yard | chassis_master + status, movement, document, checklist and hand-over tables | **chassis_master** |
| Production | chassis_station_log, chassis_station_handoff, production_hold, delay_reason_master, chassis_bom_snapshot | chassis_station_log |
| Quality | qa_checksheet_template → section → checkpoint_item; qa_station_template_map; qa_checksheet_instance → response → photo; qa_section_remark; signoff; shortage | qa_checksheet_instance |
| Dispatch | dispatch_thermal_checksheet(+item), dispatch_gate_pass(+item) | |
| Products and BOM | product_master, bom_master, bom_detail, special_feature_*, body_type_master, customer_segment_master, vehicle_model_master, seating_layout_master | bom_master |
| Sales and dealers | customer_master (dealers), dealer_client_master, dealer_order → vehicle → feature/chassis | dealer_order |
| Purchasing and stock | rm_master, rm_inventory, rm_inventory_log, rm_purchase_request, rm_purchase_order, rm_receiving_log (GRN), vendor_master | rm_master |
| Vendor portal | asn_log, rejection_log, ncr_log, debit_note, vendor_scorecard | |
| Maintenance | maintenance_* | maintenance_asset |

### 8.3 Where to find a value

| You want | Look in |
|---|---|
| A chassis and its current state | `chassis_master` (search `chassis_no`); `tracking_status`, `current_status`, `procurement_status`, `da_lite_status`, `production_hold_status` |
| Where a chassis is on the line now | `chassis_station_log` rows with `event_type = 'ARRIVAL'` and `status = 'AT_STATION'` |
| Its full station history and cycle times | `chassis_station_log` ordered by `scanned_at` |
| Whether it may move to the next station | latest `chassis_station_handoff` row for the chassis |
| Every status change with who and when | `chassis_status_log` |
| Its parts list | `chassis_bom_snapshot` (part, quantity, station, reserved and consumed quantity) |
| QA results | `qa_checksheet_instance` (per chassis and station) → `qa_checkpoint_response` → `qa_response_photo`; `qa_section_remark`; `qa_checksheet_signoff` |
| The order behind a chassis | `chassis_master.dealer_order_vehicle_id` → `dealer_order_vehicle` → `dealer_order` |
| Dealer of a chassis | `chassis_master.dealer_id` → `customer_master.customer_id` |
| Values offered in a dropdown | `master_list_registry` names the table; that `*_master` table holds the values (`code`, `label`, `sort_order`, `is_active`) |
| Station order on the line | `station_master.sequence_no` (active stations only) |
| Who works at which station | `station_user_assignment` (role OPERATOR or QA) |
| Which checklists apply at a station | `qa_station_template_map` |
| What a role may do | `role_module_permission`; per-user exceptions in `user_permission_override` |
| Stock of a part | `rm_inventory` (per store); movements in `rm_inventory_log` |
| Approved vendors and prices for a part | `rm_vendor_mapping` |
| Last PO / GRN / debit note numbers | `rm_purchase_order.po_number`, `rm_receiving_log.grn_number`, `debit_note.dn_number` |
| Uploaded files | The table stores a path (for example `/uploads/qa/...`); the file is in `backend/uploads/...` |

### 8.4 Status values

| Column | Values and meaning |
|---|---|
| `chassis_master.tracking_status` | `CHASSIS_IN` (in yard) → `WIP` (checked in at a station) → `READY_FOR_DISPATCH` (passed FVI) → `DISPATCHED` |
| `chassis_master.current_status` | `YARD_PENDING_CHECK` → `YARD_CHECKED` (yard checklist done) → `HANDED_TO_PRODUCTION` |
| `chassis_master.procurement_status` | `PENDING` → `PROCUREMENT_PENDING` (inducted) → `PROCUREMENT_CHECKED` or `SHORTAGE_HOLD` |
| `chassis_master.da_lite_status` | `PENDING`, `OK`, `HOLD` (payment or document hold) |
| `chassis_master.production_hold_status` | `NONE`, `ON_HOLD`, `RELEASED` |
| `chassis_station_log.event_type` / `status` | `ARRIVAL` / `AT_STATION` while there; `COMPLETION` when work is done |
| `chassis_station_handoff.status` | `AWAITING_QA` → `QA_PASSED` → `RELEASED` → `RECEIVED`; `SUPERSEDED` when the station is re-completed after rework |
| `qa_checksheet_instance.station_qa_status` | `IN_PROGRESS`, then the sign-off result (`PASSED` / `FAILED`) |
| `dealer_order.status` | `DRAFT` → `SUBMITTED` → `PENDING_DEALER_ACK` / `DEALER_EDITED` → `PENDING_FINAL_CONFIRMATION` → `CONFIRMED` |
| `station_user_assignment.role` | `OPERATOR`, `QA` |

### 8.5 Dropdown lists

`master_list_registry` has one row per list: `list_code` (used by the screens), `table_name`, label,
description, display order. Each list is its own table with `code`, `label`, `sort_order`,
`is_active` (`gate_pass_item_master` also has `default_qty` and `print_row`). Admins edit them in
**Setup > Production Masters > Dropdown Lists**. The API is `GET /api/v1/lookups?category=AC_MAKE`.

The 17 lists: production lines, AC makes, interior work, yard locations, machine types, incoming
material categories, rental vendors, rental equipment types, QA verification methods, production hold
types, payment terms, regions, enquiry types, enquiry sources, chassis document types, gate pass items,
thermal check items.

---

## 9. Business flows end to end

### 9.1 Order to chassis

1. A dealer places an order in the dealer portal (`dealer_order`, `dealer_order_vehicle`,
   `dealer_order_vehicle_feature`), or sales enters one.
2. Sales reviews and confirms it (**Sales & Orders > Dealer Orders**; `dealer_portal_admin/service.py`). The
   dealer acknowledges changes; sales gives final confirmation (`CONFIRMED`).
3. Chassis are allocated to the order lines (`dealer_order_vehicle_chassis`, and
   `chassis_master.dealer_order_vehicle_id`).

### 9.2 Chassis inward (yard)

Screens: **Chassis & Yard > Chassis Inward** (`YardList`, `YardChassisDetail`). Service: `yard/service.py`.

1. **Create** the chassis (`create_chassis`): number, model, dealer, fuel, arrival date, driver and
   vehicle details. `tracking_status = CHASSIS_IN`, `current_status = YARD_PENDING_CHECK`.
2. **Arrival documents** (e-way bill, insurance, challan) uploaded and verified
   (`chassis_arrival_document`; types from `chassis_document_type_master`).
3. **DA-lite** check with receiving photo (`da_lite_status`, photo in `uploads/da_lite`). A `HOLD`
   blocks the chassis commercially and shows on the sales DA-lite queue.
4. **Yard QA checklist** (`chassis_qa_checklist_result`, questions in `yard_qa_checklist_item_master`).
   When complete, `current_status = YARD_CHECKED`.
5. **Movements** between yard checkpoints (`chassis_movement_log`, with a checklist).
6. **Store hand-over** sheet of loose parts with approvals (`chassis_store_handover`, `..._part`).
7. **Hand over to production** (`handover_to_production`): needs the yard checklist and DA-lite photo.
   Sets `HANDED_TO_PRODUCTION`.

### 9.3 Induction and procurement check

1. **Production > Production Schedule** schedules chassis (`scheduled_induction_date`) and inducts them
   (`induct_chassis_to_production`): `current_status = HANDED_TO_PRODUCTION`,
   `procurement_status = PROCUREMENT_PENDING`.
2. **Purchasing > Material Requirements** (`procurement/engine_service.py`) explodes the BOM for each
   chassis: base BOM of its product (`bom_master` / `bom_detail`) plus the feature BOMs of the options
   chosen on the order (`special_feature_bom`). The result is frozen in `chassis_bom_snapshot`, with the
   station that consumes each part (taken from `rm_master.station_id`).
3. Stock is checked and reserved; shortages raise purchase requests and set `SHORTAGE_HOLD`, otherwise
   `PROCUREMENT_CHECKED`.
4. Purchasing: purchase request → PO (`rm_purchase_order`, number assigned on save) → vendor sends an
   ASN in the vendor portal → GRN at stores (`rm_receiving_log`, stock posted to `rm_inventory`,
   movement in `rm_inventory_log`) → rejected quantity can raise a debit note (value = rejected quantity
   × PO unit price).

### 9.4 Production floor and the QA gate

Screens: operator **Production > My Station**, QA **Quality > QA Inspection**, supervisor
**Production > Production Floor**.
Logic: `production/service.scan_chassis` and `station_work/flow.py`.

```
Station A: operator checks in ──► works ──► marks complete
                                                │
                       station A has QA checklists?
                        ├─ no  → hand-over RELEASED to station B immediately
                        └─ yes → hand-over AWAITING_QA
                                   QA inspects at station A (checklists, photos, remarks) and signs off
                                   ├─ all mandatory checklists pass → QA_PASSED → QA presses Release → RELEASED
                                   └─ a checklist fails → stays at A for rework; completing again SUPERSEDES the old hand-over
Station B: operator can check the chassis in only when the hand-over is RELEASED → RECEIVED
```

- **Next station** (`flow.next_station`): the next active station by `sequence_no` that has parts for
  this chassis in `chassis_bom_snapshot`; if none, simply the next station by sequence.
- **Chassis with no hand-over history** (already on the floor before the gate existed) are not blocked.
- **First check-in** sets `tracking_status = WIP`. Each event is a `chassis_station_log` row; cycle time
  is compared with `station_master.standard_cycle_time_min` and `alert_threshold_min` to flag warnings
  and overdue chassis; overdue chassis can carry a delay reason (`delay_reason_master`).
- **Holds** (`production_hold`, types from `production_hold_type_master`) stop a chassis and are
  resolved from the Production Floor holds tab.
- **Stock consumption**: when QA passes at a station, the parts of `chassis_bom_snapshot` for that
  station are consumed from stock (`_deduct_inventory_on_qa_pass`).
- **Age**: chassis age is shown in days from arrival.

### 9.5 QA set-up and inspection

1. **Quality > QA Checksheets** (`TemplateManager`): templates (format number, revision) → sections →
   checkpoints (specification, unit, verification method, mandatory flag).
2. **Quality > QA Checksheets > Station assignments** (`StationChecklistConfig`): which templates run
   at which station (`qa_station_template_map`), whether each is mandatory, and optionally only for some
   vehicle models or body types.
3. **Inspection** (`QaInspect` on a phone): pick the station tab, pick or scan a chassis →
   `start_inspection` creates (or reopens) an instance per applicable template → answer each checkpoint
   (OK / NOT OK / NA, value, remark, photos) → section and checksheet remarks → sign off.
4. Sign-off reports whether every mandatory checksheet has passed; if so the hand-over moves to
   `QA_PASSED` and QA can release the chassis.

### 9.6 Final inspection and dispatch

1. The last station is **FVI** (final vehicle inspection). A scan through `yard.station_scan` with an
   FVI/RFD target sets `READY_FOR_DISPATCH`.
2. **Dispatch** queue (`dispatch/service.py`): the **thermal (AC) checksheet**
   (`dispatch_thermal_checksheet`, items from `thermal_check_item_master`), then the **gate pass**
   (`dispatch_gate_pass`, items from `gate_pass_item_master`) → `DISPATCHED`, `dispatched_at` set.
3. Anyone with the chassis QR code can follow it on `/track/<chassis no>`.

### 9.7 Maintenance portal

Assets and breakdown records with actions and photos, preventive maintenance schedule and checklists,
daily consumables, monthly stock, machine cost and cost versus production, diesel, LPG and solar
registers, equipment purchases and rentals. Tables `maintenance_*`; module `maintenance`; dropdowns from
the machine type, material category, rental vendor and equipment type lists.

### 9.8 Vendor and dealer portals

Vendor: sees its POs, acknowledges them, sends ASNs, sees rejections, NCRs, debit notes and monthly
plans. Dealer: profile and contacts, its own clients, orders and order edits, templates, requests,
and (if `allow_chassis_tracking`) the live status of its chassis. Staff grant access from the
vendor profile (**Setup > Vendors**) and the dealer profile (**Setup > Dealers**).

---

## 10. What affects what

| If you change | It affects |
|---|---|
| `station_master` order (`sequence_no`) or `is_active` | The route every chassis takes; the next-station rule; production floor columns; sales dashboard pipeline |
| `qa_station_template_map` | Whether completing a station waits for QA; which checklists QA sees there |
| A checkpoint's mandatory flag | Whether a checksheet can pass |
| `station_user_assignment` | Which stations an operator or inspector can act at and sees as tabs |
| `rm_master.station_id` | Which station consumes a part (copied into `chassis_bom_snapshot` at the procurement run); which station is "next" for a chassis |
| `special_feature_bom` | Parts added to a chassis BOM when that option is ordered (at the next procurement run) |
| `role_module_permission` | Menus, buttons and API access for every user with that role, immediately |
| A dropdown table | The choices offered on the related screens; old records keep their saved value |
| `da_lite_status = HOLD` | Sales DA-lite queue and dashboard hold counts |
| `SECRET_KEY` | Signs out every user |
| `ALLOWED_ORIGINS` | Whether a browser at a given address can call the API |

---

## 11. How to make common changes

### Add or change a dropdown value
**Setup > Production Masters > Dropdown Lists**, pick the list, add or edit. Deactivate rather than
delete values already used. In SQL: insert into the list's `*_master` table.

### Add a new dropdown list
1. Add `'LIST_CODE': 'new_thing_master'` to `MASTER_LIST_TABLES` in `app/models/master_list_models.py`.
2. Add a description to `app/models/table_catalog.py`.
3. `alembic revision -m "new_thing dropdown"`, and in it create the table with the standard columns
   and insert its `master_list_registry` row (copy the pattern from `e1f2a3b4c5d6`).
4. `alembic upgrade head`. On screens use `<LookupOptions category="LIST_CODE" />` or
   `useLookupOptions('LIST_CODE')`.

### Add a station or change the line order
**Setup > Stations**: create the station and set its sequence number; map QA checklists in
**Quality > QA Checksheets > Station assignments**; assign people in **Setup > Station Staff**.

### Give someone a station
**Setup > Station Staff**: user, station, role (Operator or QA). Several rows make several tabs.

### Add a QA checkpoint
**Quality > QA Checksheets**, open the template, add it to a section. New inspections pick it up; existing
instances keep the checkpoints they started with.

### Grant or remove a permission
**Admin > Users & Access**: tick the role's flags for the module, or set a per-user override.

### Add a role
**Admin > Users & Access > Roles**, then set its permissions. Assign users to it.

### Reset a user's password
**Admin > Users & Access > Users**, open the user and set a new password.

### Add an API endpoint
1. Add the handler to the module's `routes.py` with `Depends(require_permission('MODULE', 'action'))`.
2. Put logic in `service.py`, request/response models in `schemas.py`.
3. Add a hook to the matching `frontend/src/api/*.js` file and use it from the page.

### Add a screen
Create the page under `src/pages/<area>/`, add a `<Route>` in `App.jsx`, and add a menu entry in
`components/layout/Layout.jsx` with the permission module that should show it.

### Add a table or column
1. Add the column or model in `app/models/<area>_models.py`, and a description in
   `table_catalog.py` for a new table.
2. `cd backend && alembic revision --autogenerate -m "describe the change"`; read the generated file and
   remove anything unrelated.
3. `alembic upgrade head`, then `alembic check` should report no differences.
4. `python scripts/apply_table_comments.py` and `python scripts/generate_db_reference.py`.

### Change a status flow
Status transitions live in code: tracking statuses in `yard/service.py` and
`production/service.py`, the station hand-over in `station_work/flow.py`, QA in `qa/service.py`.
Search for the status string to find every place that sets or reads it.

---

## 12. Operations: run, back up, restore, upgrade

### Run

```bash
# backend
cd backend && source venv/bin/activate        # Windows: venv\Scripts\activate
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# frontend (second terminal)
cd frontend && npm run dev -- --host
```

On a Mac, `./scripts/run_mac.sh` does both (`--lan` makes it reachable from phones). `--host 0.0.0.0`
on the API makes it reachable from phones on the same network at `http://<computer IP>:5173`; the
frontend already listens on the network.

### Back up

```bash
pg_dump -h localhost -U erp_user -d erp_db -Fc -f erp_db_YYYYMMDD.dump
```

Back up `backend/uploads/` at the same time: it holds every photo and document; the database stores
only their paths.

### Restore into a new machine

Create the role and an empty database, then:

```bash
pg_restore -h localhost -U erp_user -d erp_db --no-owner --no-privileges erp_db_YYYYMMDD.dump
cd backend && alembic upgrade head
```

The restoring `pg_restore` must be the same major version as the dump or newer (the current dump is
PostgreSQL 18). See the setup guides.

### Upgrade to a newer version of the code

Replace the code (keep `backend/.env`, `backend/venv` and `backend/uploads`), then:

```bash
cd backend && pip install -r requirements.txt && alembic upgrade head
cd ../frontend && npm install
```

### Logs

Unhandled server errors with full tracebacks: `backend/logs/error.log`. User actions in the portals
and access control: **Admin > System Logs** (tables `*_action_log`, `*_portal_log`).

---

## 13. Testing and checks

| Check | Command |
|---|---|
| Backend tests | `cd backend && python -m pytest` (uses the database in `.env`; tests create and remove their own rows and run as the first active ADMIN user) |
| Models match the database | `cd backend && alembic check` → "No new upgrade operations detected." |
| Frontend builds | `cd frontend && npx vite build` |
| API contract | http://localhost:8000/docs |

---

## 14. Known limitations

| Item | Detail |
|---|---|
| Build databases from the dump | Running every migration on an empty database completes, but the result differs from the models in minor details (column types, some foreign keys). New environments should be restored from a dump and then upgraded with `alembic upgrade head`. |
| Uploaded files live on disk | `backend/uploads` must be copied and backed up separately from the database. |
| QA shortages have no screen | The `/qa/shortage` API and `qa_shortage_log` table exist; the old screen was retired before this handover. |
| Some production report columns are not captured | `dispatch_transporter` and `pdi_inspector_name` in the production report are always blank. |
| Older QA tables kept for history | `station_qa_checklist_item_master` and `chassis_station_qa_result` predate the checksheet system and are no longer written. |
| Phone camera needs HTTPS | Browsers allow the camera only on HTTPS pages (or localhost). On the plain-HTTP local setup, phones pick or type the chassis number; scanning works once the system is served over HTTPS. |
| Development server only | The run commands use the development servers. A production deployment would serve the built frontend (`npx vite build` → `frontend/dist`) behind a web server and run uvicorn without `--reload`, over HTTPS. |

---

## 15. Glossary

| Term | Meaning |
|---|---|
| Chassis | The bare vehicle supplied by the customer or OEM on which the bus body is built |
| DA-lite | Dispatch-authorisation check at receipt; `HOLD` blocks the chassis for payment or documents |
| Yard | The receiving area where chassis wait before production |
| Induction | Releasing a scheduled chassis into production |
| BOM | Bill of materials: parts and quantities needed to build a product |
| Feature BOM | Extra parts an ordered option (for example AC) adds to the BOM |
| WIP | Work in progress: the chassis is on the production line |
| QA gate | QA must pass and release a chassis before the next station can take it |
| FVI | Final vehicle inspection, the last production station |
| RFD | Ready for dispatch |
| PDI | Pre-delivery inspection |
| Thermal checksheet | AC performance check before dispatch |
| Gate pass | Document that lets the bus leave the plant |
| PR / PO | Purchase request / purchase order |
| ASN | Advance shipping notice sent by a vendor |
| GRN | Goods receipt note, recorded when material arrives |
| NCR | Non-conformance report on a vendor |
| Debit note | Amount claimed back from a vendor for rejected or short material |
| FAPS | Fire alarm and protection system fitted to buses |
| CSP | Customer-supplied part |
| RTO | Regional transport office (vehicle registration) |
