0. Two-line summary
FactoryTracking is a multi-tenant garment shop-floor app: each factory is a companies row, and every lot is a production_batches row that moves CUTTING → DISPATCH with QR scans and qty handshakes. It also covers thin inventory/BOM, orders, buyer portal, defects, AI assist, and soft Stripe/Paytm billing — strong as a pilot MES, not yet production-SaaS.
1. What the product does (detailed)
1.1 Industry context
Indian garment / fabric factories run work as lots or batches through departments (cutting, stitching, washing, QC, packing, dispatch). Visibility gaps cause missing pieces, late deliveries, and phone-call chaos between floor and office.
1.2 What this code solves today
- Track every batch through six fixed stages with who moved what quantity.
- QR labels so a phone can prove a batch was seen on the floor (
scan_logs). - Quantity integrity: sending dept enters
sent_qty; receiving dept entersreceived_qty; mismatch →ALERT. - Multi-tenant SaaS shell: signup creates a company + admin; data isolated by
company_id. - Materials: inventory ledger + style BOM + WhatsApp reorder draft links (
wa.me). - Commercial thin layer: customer orders, buyer login, defects, reports, optional AI chat/ML delay/qty models.
- Billing UI: 14-day trial + Stripe/Paytm hooks (soft gate — expired trial still gets full app access).
1.3 What it does not do (preview — full gaps in Doc 2)
No live confirmed cloud deploy, no real WhatsApp bot, no GST invoices, no formal POs/MRP, no fabric roll/shade tracking, no hard subscription lockout.
2. Tech stack & file map
Two-line summary
Almost all behavior lives in one Flask file (app.py); SQLite is default; Postgres is an optional shim via db.py that has not been proven in production.
Stack
| Layer | Choice | Notes |
|---|---|---|
| Language | Python 3.12 (pinned deps) | Avoid 3.14 for sklearn pins |
| Web | Flask 3 + Jinja templates | Server-rendered HTML |
| DB default | SQLite factory_inventory.db | File in repo; demo has 200 batches |
| DB optional | Postgres via DATABASE_URL | ? placeholders rewritten to %s in db.py |
| Auth | Session cookie + werkzeug password hashes | Username globally unique |
| QR | qrcode → static/qr_codes/ | Payload = batch_id string |
| ML | joblib + ai_model.pkl / delay_model.pkl | Version skew warning possible |
| AI chat | OpenRouter / Groq (OpenAI-compatible) | Needs API key in .env |
| Payments | Stripe Checkout + Paytm | Explicitly marked untested in code comments |
| Deploy recipes | Render / OCI Docker | Misnamed files; not verified live |
Key files
| File | Two-line summary | Role |
|---|---|---|
app.py | Monolith of ~7.4k lines / ~103 routes. | Auth, MES, inventory, billing, AI, imports |
db.py | Compatibility layer SQLite↔Postgres. | get_raw_connection, ?→%s |
create_database.py | Destructive wipe + recreate + demo seed. | Do not run on real customer DB |
create_database_postgres.sql | Postgres DDL for core tables. | Schema incomplete vs all ensure_* tables |
templates/*.html | 55 Jinja pages. | UI for every feature |
static/ | CSS/JS + QR images. | Front assets |
train_ai_model.py / train_delay_model.py | Offline training → pickle. | Retrain when data grows |
deployment/ | Docker Compose + Caddy. | OCI-style template |
DEPLOY.md | Render free-tier instructions. | Template; example URL not verified |
requirements .txt | Deps (note space in filename). | Breaks standard pip -r requirements.txt |
orders_routes.py / suppliers_routes.py | Modular extracts. | Logic largely inlined in app.py already |
Runtime config (env)
| Variable | Two-line summary |
|---|---|
DATABASE_URL | Empty = SQLite. Set to postgres://… to switch backends. |
FACTORY_SECRET_KEY | Signs session cookies; required for stable logins across restarts. |
FACTORY_DEMO_MODE | 1 shows admin/admin123 hint on login page. |
FACTORY_DEBUG | Flask debug. |
PORT | Local uses 5050 (macOS AirPlay owns 5000). |
FACTORY_BASE_URL | Public URL for links/emails. |
STRIPE_* / PAYTM_* | SaaS payment (soft). |
OPENROUTER_API_KEY / GROQ_API_KEY | AI chat. |
SMTP_* | Password-reset / daily notification emails. |
INTERNAL_TASK_TOKEN | Protects cron-style internal notify route. |
3. Multi-tenancy & security model
Two-line summary
Every business row carries company_id; the logged-in session’s company_id is applied to queries so factories cannot see each other. Usernames are globally unique so login never asks “which factory?”
Detailed rules
- **Tenant =
companiesrow.** Signup inserts company (plan=trial,subscription_status=trialing,trial_ends_at=+14d) + first user (Administrator, deptPRODUCTION). - Session keys (staff):
user,name,department,role,company_id. - Session keys (buyer portal — separate):
buyer_id,buyer_company_id,buyer_name,buyer_username. - Roles:
Administrator(full),Supervisor(move/receive any stage),Operator/Inspector(only ifsession.department == batch.current_stage). - Subscription soft gate:
subscription_active()is used on billing UX / emails — not a globalbefore_requestlock. Expired trials still use MES. - Login rate limit: ~5 fails / 60s in-memory.
- Password reset: token in
password_resets; requires SMTP or admin reset from Users page.
flowchart TD signup["POST signup"] --> co["Insert companies"] co --> u["Insert admin user"] u --> sess["Set session company_id"] sess --> onboard["Onboarding checklist"] login["POST login"] --> lookup["Find user by username"] lookup --> check["Check password hash"] check --> sess2["Session with company_id"] sess2 --> home["Home dashboard"]
4. Complete database schema
Two-line summary
Live demo DB has 29 user tables (plus sqlite_sequence). Core MES tables are created in create_database.py; many later features are created at runtime by ensure_* in app.py.
4.1 Entity relationship (logical)
erDiagram
companies ||--o{ users : has
companies ||--o{ production_batches : has
companies ||--o{ factory_stages : has
companies ||--o{ inventory_items : has
companies ||--o{ customer_orders : has
companies ||--o{ buyers : has
production_batches ||--o{ movement_history : moves
production_batches ||--o{ scan_logs : scans
production_batches ||--o{ defect_records : defects
production_batches ||--o{ batch_sizes : sizes
production_batches }o--o{ customer_orders : linked_by_order_id
inventory_items ||--o{ inventory_transactions : ledger
inventory_items ||--o{ bill_of_materials : bom
suppliers ||--o{ purchase_requests : requests
4.2 Identity & access tables
companies — tenant / factory
Two-line: One row per paying (or trial) factory. Holds plan, Stripe IDs, trial end, and active flag.
| Column | Type | Notes |
|---|---|---|
company_id | INTEGER PK | Auto |
company_name | TEXT NOT NULL | Display name |
slug | TEXT UNIQUE NOT NULL | URL-safe id (e.g. demo-factory) |
plan | TEXT default trial | Commercial plan label |
is_active | INTEGER default 1 | Soft disable |
stripe_customer_id | TEXT | Stripe customer |
stripe_subscription_id | TEXT | Stripe sub |
subscription_status | TEXT default trialing | trialing / active / … |
trial_ends_at | TIMESTAMP | Trial expiry |
created_at | TIMESTAMP | |
trial_reminder_sent | INTEGER default 0 | Daily job flag |
Demo rows: 1 (Demo Factory).
users — factory staff
Two-line: Login accounts scoped to one company; username unique globally; department drives stage permissions.
| Column | Type | Notes |
|---|---|---|
user_id | INTEGER PK | |
company_id | INTEGER NOT NULL | FK → companies |
username | TEXT UNIQUE NOT NULL | Login id |
password | TEXT NOT NULL | scrypt hash |
full_name | TEXT | |
department | TEXT | CUTTING…DISPATCH or PRODUCTION |
role | TEXT | Administrator / Supervisor / Operator / Inspector |
is_active | INTEGER default 1 | |
created_at | TIMESTAMP | |
email | TEXT | Added later via ensure |
Demo rows: 10.
buyers — brand / customer portal users
Two-line: Separate login for external buyers; visibility filtered by matching customer_name on orders.
| Column | Type | Notes |
|---|---|---|
buyer_id | INTEGER PK | |
company_id | INTEGER NOT NULL | Factory that owns the relationship |
customer_name | TEXT NOT NULL | Must match order.customer_name |
username | TEXT NOT NULL | Portal login |
password | TEXT NOT NULL | Hash |
contact_email | TEXT | |
is_active | INTEGER default 1 | |
created_at | TIMESTAMP |
Demo rows: 1.
password_resets
Two-line: One-time tokens for forgot-password; expire and single-use.
| Column | Type | Notes |
|---|---|---|
token | TEXT PK | |
username | TEXT NOT NULL | |
company_id | INTEGER NOT NULL | |
expires_at | TIMESTAMP NOT NULL | |
used | INTEGER default 0 |
4.3 Production / MES tables
factory_stages — per-tenant pipeline
Two-line: Ordered list of stage keys for this factory; defaults to the six garment stages.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
stage_key | TEXT NOT NULL | e.g. CUTTING |
stage_name | TEXT NOT NULL | Display |
stage_order | INTEGER NOT NULL | 0…n |
created_at | TIMESTAMP |
Default keys: CUTTING, STITCHING, WASHING, QC, PACKING, DISPATCH.
production_batches — core lot (most important table)
Two-line: One garment lot’s identity, per-stage quantities, current stage, qty handshake fields, AI fields, QR path, optional order link.
| Column | Type | Notes |
|---|---|---|
batch_id | TEXT PK | e.g. BATCH-00001 |
company_id | INTEGER NOT NULL | Tenant |
lot_number | INTEGER | |
sort_number | TEXT | |
fit | TEXT | Style / fit name (BOM key) |
cut_qty … dispatch_qty | INTEGER | Legacy per-stage qty columns |
size_28 … size_38 | INTEGER | Legacy fixed size buckets |
total | INTEGER | Total pieces |
current_stage | TEXT default CUTTING | Where the lot sits now |
sent_qty | INTEGER | Last send quantity |
received_qty | INTEGER | Last receive quantity |
difference_qty | INTEGER | sent − received |
movement_status | TEXT default OK | OK or ALERT |
ai_prediction / ai_reason / ai_recommendation | TEXT | ML outputs |
anomaly_score | REAL | |
qr_path | TEXT | Path to PNG |
created_at / updated_at | TIMESTAMP | |
line_number | TEXT | Sewing line |
order_id | TEXT | Link to customer_orders.order_id |
Demo rows: 200.
batch_sizes
Two-line: Flexible size breakdown (S/M/L/…) instead of only size_28…38 columns.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT NOT NULL | |
size | TEXT NOT NULL | Label |
quantity | INTEGER default 0 |
batch_stage_quantities
Two-line: Flexible stage qty mirror keyed by stage_key (future-proof vs fixed cut_qty columns).
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT NOT NULL | |
stage_key | TEXT NOT NULL | |
quantity | REAL NOT NULL | |
updated_at | TIMESTAMP |
movement_history
Two-line: Audit of every stage transition with quantity and who moved it.
| Column | Type | Notes |
|---|---|---|
movement_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT | |
from_stage / to_stage | TEXT | |
quantity | INTEGER | |
moved_by | TEXT | username |
remarks | TEXT | |
movement_time | TIMESTAMP |
scan_logs
Two-line: QR presence events; does not by itself advance stage.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT | |
username | TEXT | Who scanned |
department | TEXT | Their dept |
stage | TEXT | Batch stage at scan time |
source | TEXT | Device/source |
scanner_location | TEXT | Optional |
created_at | TIMESTAMP |
activity_logs
Two-line: General audit trail (MOVE_BATCH, login-ish actions, etc.).
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
username | TEXT | |
action | TEXT | |
detail | TEXT | |
created_at | TIMESTAMP |
daily_targets
Two-line: Per-day, per-stage target quantity for ops dashboards.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
target_date | TEXT NOT NULL | |
stage | TEXT NOT NULL | |
target_qty | REAL NOT NULL | |
created_by | TEXT | |
created_at | TIMESTAMP |
department_performance
Two-line: Cached KPIs per department (efficiency, rejects).
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
department | TEXT | |
total_batches / total_quantity / completed_batches | INTEGER | |
rejected_quantity | INTEGER | |
efficiency | REAL | |
updated_at | TIMESTAMP |
dashboard_summary
Two-line: One cached home-dashboard snapshot per company (stage totals).
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
total_batches / running_batches / completed_batches | INTEGER | |
total_cutting … total_dispatch | INTEGER | |
updated_at | TIMESTAMP |
test_logs
Two-line: Ad-hoc factory test records attached to a batch/department.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT | |
department | TEXT | |
test_name / status / remarks | TEXT | |
tested_by | TEXT | |
created_at | TIMESTAMP |
4.4 Inventory & materials tables
inventory_items
Two-line: SKU catalogue (fabric, trims, etc.) with reorder level and optional supplier link.
| Column | Type | Notes |
|---|---|---|
item_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
name | TEXT NOT NULL | |
sku | TEXT | |
unit | TEXT default pcs | mtr, pcs, cone… |
category | TEXT default Other | Fabric / Trims… |
reorder_level | REAL | Low-stock threshold |
supplier_name / supplier_contact | TEXT | Denormalized |
unit_cost | REAL | For cost report |
active | INTEGER | |
created_at | TIMESTAMP | |
low_stock_alerted_at | TIMESTAMP | Dedupes email alerts |
supplier_id | INTEGER | FK-ish → suppliers |
inventory_transactions
Two-line: Ledger of stock movements; on-hand is sum of IN − OUT (− RESERVED logic in app).
| Column | Type | Notes |
|---|---|---|
transaction_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
item_id | INTEGER NOT NULL | |
transaction_type | TEXT NOT NULL | IN / OUT / RESERVED |
quantity | REAL NOT NULL | |
transaction_date | TEXT NOT NULL | |
reference / notes | TEXT | |
created_by | TEXT | |
created_at | TIMESTAMP |
bill_of_materials
Two-line: Per-style material recipe: how much of each item per finished piece.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
style | TEXT NOT NULL | Matches batch fit |
item_id | INTEGER NOT NULL | |
quantity_per_piece | REAL NOT NULL | |
created_at | TIMESTAMP |
suppliers
Two-line: Supplier directory for reorders.
| Column | Type | Notes |
|---|---|---|
supplier_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
name | TEXT NOT NULL | |
contact | TEXT | Often phone for wa.me |
category / notes | TEXT | |
active | INTEGER | |
created_at | TIMESTAMP |
purchase_requests
Two-line: Log of reorder messages sent/drafted (channel default whatsapp).
| Column | Type | Notes |
|---|---|---|
request_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
item_id / supplier_id | INTEGER | |
item_name | TEXT NOT NULL | |
quantity / unit | ||
channel | TEXT default whatsapp | |
message | TEXT | Body drafted |
requested_by | TEXT | |
created_at | TIMESTAMP |
packing_lists
Two-line: Material “production orders” — planned reservation of inventory against a style/qty (status Planned / Reserved / Short).
| Column | Type | Notes |
|---|---|---|
packing_list_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
list_name | TEXT NOT NULL | |
brand | TEXT | |
planned_date | TEXT | |
status | TEXT default Planned | Planned / Reserved / Short |
notes | TEXT | |
created_by | TEXT | |
created_at | TIMESTAMP | |
product_name | TEXT | Style/product |
quantity | REAL | Planned piece count |
packing_list_items
Two-line: Line items of materials required/reserved for a packing_list.
| Column | Type | Notes |
|---|---|---|
packing_list_item_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
packing_list_id | INTEGER NOT NULL | |
item_id | INTEGER NOT NULL | → inventory_items |
required_quantity | REAL NOT NULL | |
reserved_quantity | REAL default 0 |
packing_sheets
Two-line: Header for style×size packing grids (carton/size export) — naming overlaps “packing” but is separate from material lists.
| Column | Type | Notes |
|---|---|---|
packing_list_id | INTEGER PK | Shared id space with sheets feature |
company_id | INTEGER NOT NULL | |
style_number | TEXT NOT NULL | |
list_date | TEXT | |
created_by | TEXT | |
created_at | TIMESTAMP |
packing_sheet_items
Two-line: Grid cells: row label × size label × quantity.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
packing_list_id | INTEGER NOT NULL | |
company_id | INTEGER NOT NULL | |
row_label | TEXT NOT NULL | e.g. color/carton |
size_label | TEXT NOT NULL | S/M/L… |
quantity | INTEGER default 0 | |
row_position / size_position | INTEGER | Display order |
stage_labor_rates
Two-line: Cost-per-piece by stage for cost reports.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
stage | TEXT NOT NULL | |
cost_per_piece | REAL | |
updated_at | TIMESTAMP |
4.5 Orders, quality, billing tables
customer_orders
Two-line: Buyer/brand order header; status drives commercial visibility; can link to batches via order_id.
| Column | Type | Notes |
|---|---|---|
id | INTEGER PK | Surrogate |
order_id | TEXT NOT NULL | Business id (unique per company in app logic) |
company_id | INTEGER NOT NULL | |
customer_name | TEXT NOT NULL | |
style | TEXT | |
quantity | INTEGER | |
delivery_date | TEXT | |
status | TEXT default Pending | Pending / In Production / Ready / Dispatched |
notes | TEXT | |
created_by | TEXT | |
created_at / updated_at | TIMESTAMP |
defect_records
Two-line: Typed QC (or any-stage) defects — why pieces failed, separate from qty mismatch ALERT.
| Column | Type | Notes |
|---|---|---|
defect_id | INTEGER PK | |
company_id | INTEGER NOT NULL | |
batch_id | TEXT NOT NULL | |
stage | TEXT NOT NULL | |
defect_type | TEXT NOT NULL | From DEFECT_TYPES list |
quantity | INTEGER | |
notes | TEXT | |
recorded_by | TEXT | |
created_at | TIMESTAMP |
Defect types: Stitch Issue, Wrong Size, Broken Zip, Loose Button, Fabric Defect, Color Mismatch, Print Defect, Measurement Issue, Other.
pending_orders
Two-line: Paytm checkout pending rows for SaaS subscription payment (not garment customer orders).
| Column | Type | Notes |
|---|---|---|
order_id | TEXT PK | Paytm order id |
company_id | INTEGER NOT NULL | |
amount | TEXT NOT NULL | |
status | TEXT default created | |
created_at | TIMESTAMP |
5. End-to-end flows (detailed)
5.1 Shop-floor production (the heart)
Two-line summary
Create a batch at CUTTING → print/scan QR → move to next stage with quantity → receiving department confirms → repeat until DISPATCH; QC can log typed defects along the way.
Detailed steps
- Admin/supervisor opens
/create-batch, enters style (fit), quantities, optional sizes / line / order link. - App inserts
production_batcheswithcurrent_stage=CUTTING, setscut_qty, may writebatch_sizes. - Optional BOM consume may reduce inventory when cutting starts (style-matched
bill_of_materials). - Open
/batch/<id>→/qr/<id>generates PNG understatic/qr_codes/{company_id}_{batch_id}.pngencoding the batch id string. - Floor phone camera (html5-qrcode) hits
/log-scan?batch=BATCH-…→scan_logs(duplicate window ~30s). Scan ≠ move. - Sending operator
POST /move/<batch_id>with quantity → validates against previous stage qty, updatescurrent_stage, stage qty column,sent_qty, insertsmovement_history, activity log, may refresh AI fields. - Receiving operator
POST /receive/<batch_id>with received qty →received_qty,difference_qty,movement_statusOK/ALERT. - Permissions: Admin/Supervisor any stage; others only if department matches current stage.
- At QC (or any stage),
POST /batch/<id>/defectwritesdefect_records. - Packing sheets optional via
/packing-list/*. - Final stage DISPATCH; dashboards count it completed; reports/TV/command-center read aggregates.
flowchart TD
A["Create batch at CUTTING"] --> B["Generate QR"]
B --> C["Scan log presence"]
C --> D["Move with sent qty"]
D --> E["Receive with received qty"]
E --> F{"Qty matches?"}
F -->|yes| G[OK]
F -->|no| H[ALERT]
G --> I{"More stages?"}
H --> I
I -->|yes| D
I -->|QC| J["Log defects"]
J --> I
I -->|DISPATCH| K["Reports and TV"]
Tables touched
production_batches, batch_sizes, batch_stage_quantities, movement_history, scan_logs, activity_logs, defect_records, optionally inventory_transactions, bill_of_materials.
Routes
/create-batch, /production, /move/<id>, /receive/<id>, /batch/<id>, /qr/<id>, /log-scan, /scan-history, /timeline/<id>, /quantity-alerts, /department/<dept>.
5.2 Signup → trial → first users
Two-line summary
Factory owner signs up → company + admin created with 14-day trial → onboarding checklist → admin adds floor users by department.
Detailed steps
GET/POST /signup(company name, full name, username, password ≥8).- Insert
companies+usersAdministrator. - Session set; redirect
/onboarding. ensure_factory_stages_tableseeds six stages for company when used.- Admin
/users/addcreates cutting1, stitch1, etc. - Billing page shows trial countdown; payment optional until you enforce hard gate in Fabric IQ.
5.3 Customer order → batch → buyer portal
Two-line summary
Staff create an order, link one or more batches, update status through production; buyer logs in and only sees orders matching their customer_name.
flowchart LR O["Add order Pending"] --> L["Link batch"] L --> P["Floor MES"] P --> S["Ready or Dispatched"] B["Create buyer login"] --> V["Buyer portal"] S --> V
Statuses: Pending → In Production → Ready → Dispatched.
Routes: /orders*, /buyers*, /buyer/login, /buyer/, /buyer/order/<id>.
5.4 Inventory → low stock → WhatsApp draft
Two-line summary
Stock is ledger-based; when on-hand ≤ reorder level, admin drafts a WhatsApp message via wa.me/91… and may log purchase_requests. There is no automated Meta Cloud send in this codebase.
flowchart TD
item["Inventory item"] --> txn["IN OUT RESERVED ledger"]
item --> bom["Style BOM"]
bom --> reserve["Production order reserve"]
txn --> low{"On hand at or below reorder?"}
low -->|yes| draft["Open wa.me draft"]
draft --> log["Log purchase_requests"]
Detailed: add item → IN/OUT movements → BOM for style → production-orders reserve materials → low stock UI + /inventory/reorder-message/<id> → optional daily email job via /internal/tasks/daily-notifications.
5.5 Defects vs quantity ALERT
Two-line summary
ALERT = pieces went missing between send and receive. Defects = pieces failed quality with a typed reason. Both can exist on the same batch.
5.6 Billing (SaaS) as coded today
Two-line summary
Trial starts at signup; /billing shows Stripe Checkout and/or Paytm; webhooks/callbacks set subscription_status=active — but MES routes are not blocked when trial expires.
sequenceDiagram participant U as Admin participant App as FlaskApp participant Pay as StripeOrPaytm U->>App: Open billing page U->>App: Start checkout App->>Pay: Create payment session Pay->>App: Webhook or callback App->>App: Set subscription active Note over App: Soft gate only MES still open if expired
5.7 AI flows
Two-line summary
Pickled sklearn models score quantity anomalies and delay risk on move/receive; optional OpenRouter chat answers questions with tool calls over tenant data (rate-limited).
Routes: /ai-report, /ai-briefing, /ai-chat, /ai-chat/ask, /api/prediction/<batch_id>.
6. Route map (grouped)
Two-line summary
~103 routes in app.py: auth, MES, inventory, orders/buyers, packing, defects, reports, AI, billing, import, internal cron.
| Group | Routes (representative) |
|---|---|
| Auth | /login, /logout, /signup, /forgot-password, /reset-password/<token>, /onboarding |
| Home / ops | /, /dashboard, /tv, /overview, /bottlenecks, /live-status, /command-center, /departments, /analytics |
| MES | /production, /create-batch, /move/<id>, /receive/<id>, /batch/<id>, /targets, /logs, /movement-history |
| QR | /qr/<id>, /log-scan, /scan-history |
| Inventory | /inventory*, /bom*, /production-orders*, /cost-settings, /cost-report |
| Orders | /orders, /orders/add, /orders/<id>/status, /orders/<id>/link-batch |
| Buyers | /buyers*, /buyer/login, /buyer/, /buyer/order/<id> |
| Suppliers | /suppliers* |
| Packing | /packing-list* |
| Quality | /batch/<id>/defect, /defects, /defects/export/<fmt> |
| Reports | /reports* + exports |
| Users | /users* |
| Billing | /billing, /billing/checkout, /billing/checkout-paytm, /billing/paytm-callback, /billing/webhook |
| AI | /ai-report, /ai-briefing, /ai-chat* |
| Import | /import* |
| Internal | POST /internal/tasks/daily-notifications, /healthz |
| APIs | /api/dashboard, /api/department-performance, /api/line-performance, /api/stages, /api/prediction/<id> |
7. Demo data snapshot (local DB)
| Entity | Count | Notes |
|---|---|---|
| companies | 1 | Demo Factory |
| users | 10 | admin + floor roles |
| production_batches | 200 | ~33–34 per stage |
| factory_stages | 6 | Full pipeline |
| inventory_items | 4 | Fabric + trims |
| movement_history | 7 | Sample moves |
| scan_logs | 6 | Sample scans |
| defect_records | 1 | Sample |
| customer_orders | 0 | Empty until created |
| buyers | 1 | Portal sample |
Floor seed passwords: operators use 123; admin uses admin123.
8. Deployment status (today)
Two-line summary
No confirmed live public deployment from this tree. Render/OCI docs exist but filenames (render .yaml, requirements .txt) and missing .github/workflows/ placement mean templates would fail as-is.
Treat FactoryTracking as local/demo reference for Fabric IQ domain logic.
9. How to run locally
cd FactoryTracking
source .venv/bin/activate # Python 3.12 venv
# .env: PORT=5050, FACTORY_DEMO_MODE=1, FACTORY_DEBUG=1, empty DATABASE_URL
python app.py
# open http://localhost:5050 → admin / admin123
Warning: python create_database.py wipes the SQLite file and reseeds demo data.
10. What to remember before Fabric IQ
- Domain logic (stages, move/receive, QR, company_id) is the valuable part — keep it.
- Monolith + SQLite + soft billing + unwired WhatsApp are why we rebuild.
- Every table/column above must have a Postgres equivalent (or intentional redesign) in Fabric IQ schema.
- Next: 02 Gaps & additions · 03 Fabric IQ plan