← All projects
Case study · Jun 2026 · Odoo x KAHE Hackathon finalist · team of 2
Mini ERP: 14 business modules in one Django codebase
- Problem
- Small manufacturers juggle sales, stock, purchasing and production in spreadsheets. One app should cover the whole order-to-delivery flow without a heavy front-end framework.
- Team
- Two people: me and Jarshin J S, built for the Odoo x KAHE Coimbatore Hackathon, where we were finalists.
- My part
- Inventory, purchasing and procurement (including the Celery task that creates procurement documents), the audit log via Django signals, and the voice assistant API. Jarshin built accounts, manufacturing, blockchain, foreign trade and delivery.
- Stack
- Django 5.2, Django REST Framework, HTMX, Alpine.js, Tailwind CSS, Celery + Redis, SQLite (Postgres-ready), Ollama.
- Links
- Repo
14Django apps, one per business domain
102pytest test functions
8voice intents mapped to ERP actions
Systems lens
Architecture
- Modular apps: accounts, products, sales, purchase, inventory, manufacturing, procurement, delivery, foreign trade, reports, dashboard, notifications, audit logs and blockchain, each with its own models, views and tests.
- Fat models, skinny views: business rules live in models and
services.py, so views only handle HTTP. - HTMX instead of a SPA: server-rendered templates swap fragments on user actions, so most screens need no JSON API at all.
- Background work: a Celery task creates the procurement document asynchronously when a procurement trigger is raised for a product.
- Audit trail: an audit-log app records creates, edits and deletes through Django signals, and trade documents can be anchored by hash to a blockchain transaction for later verification.
Voice assistant API
The one place a JSON API earns its keep. A browser transcript goes to POST /api/voice/assistant/; a local LLM (llama3.1 through Ollama) returns a structured intent as JSON, and the server runs it only if it matches one of a fixed set of handlers.
POST /api/voice/assistant/
{ "transcript": "Create a new sales order for John Doe with 5 teak chairs" }
→ { "action": "create_sales_order", "success": true,
"reply": "Sales Order SO-A1B2C3D4 created for John Doe.\n • teak chair × 5 @ ₹1500.00" }
Supported intents: create product, create sales order, create purchase order, look up product / sales order / purchase order, update stock, and a fallback for general questions. Running the model locally keeps business data on the machine.
ML lensWhy a local LLM, and how it is kept safe
- The model only proposes an intent and its fields. A fixed table of Django handlers performs the action, as the logged-in user (the endpoint requires authentication).
- Any intent outside that table is never executed; the server just returns the model's text. A malformed model response returns an error instead of guessing.
What I'd do next
- Switch to PostgreSQL and add load tests for the order and stock endpoints.
- Add an evaluation set of transcripts to measure intent accuracy, and refuse writes below a confidence threshold (the model already reports one).