IDSPL is a FastAPI-based ERP application for managing scheme-driven sales, claims, and role-based operations through a simple web dashboard.
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m uvicorn main:app --reload
Open:
Optional dependencies for file-based analytics uploads:
pip install openpyxl pypdf pillow pytesseract
main.py - application entry point, routes, schema checks, and startup setupmodels.py - database modelsschemas.py - request and response schemasauth.py - password hashing and JWT helpersdatabase.py - database configuration and session handlingscheme_engine.py - scheme evaluation logicschemes.py - scheme-related helpersstatic/ - login, signup, dashboard, and PWA assetsSECRET_KEY in the environment before using this in production.Use PostgreSQL in production. SQLite is fine locally, but Render’s web-service filesystem is temporary. Data written to SQLite can disappear whenever the service is restarted or redeployed.
The included render.yaml creates both the idspl web service and the idspl-postgres managed PostgreSQL database. Render automatically passes the PostgreSQL connection string to the web service as DATABASE_URL. The application creates its tables on first startup, and every user’s sales, schemes, accounts, and uploads are then stored in PostgreSQL rather than on one laptop or one temporary Render instance.
To deploy:
render.yaml and create both services.basic-256mb PostgreSQL plan shown by Render, then apply the blueprint and wait for the first deploy to finish. This paid tier is required because Render’s free PostgreSQL databases expire after 30 days.For an existing Render web service, create a Render PostgreSQL database, copy its Internal Database URL into the web service’s DATABASE_URL environment variable, add psycopg2-binary through this repository change, and redeploy.
Existing data in scheme_erp.db is local SQLite data. It is not automatically copied to PostgreSQL; export/import or migrate it before removing the old database file.
pip install -r requirements.txtuvicorn main:app --host 0.0.0.0 --port $PORTSECRET_KEY in the project variables.Under Scheme Maintenance, an Admin/BrandManager/BrandPartner can attach the scheme circular they received from a brand - an image, PDF, or Excel file - via Attach Scheme Document. This:
Draft.If extraction fails, or the document type isn’t supported, or ANTHROPIC_API_KEY isn’t set, the document is still saved and the scheme still appears in the Draft queue - it just needs to be filled in by hand before activating.
To enable extraction, set ANTHROPIC_API_KEY on the web service (Render dashboard -> idspl -> Environment). Without it, document upload still works, but scheme fields must be entered manually.
Under IDS Price System, Admin/Accounts/MISExecutive can bulk-upload the price list via Upload Price List, in either of two ways:
.xlsx/.xls) - the same brand-section-header layout as the existing price sheets (a brand name row followed by an Item Details / Total Stock / Purchase Price / MSP / ISP table). Parsed directly, no AI involved..jpg/.jpeg/.png/.webp) or PDF - an AI vision model reads the brand sections and item rows directly from the image/PDF and returns the same row shape as the Excel parser.Either path updates existing items (matched by brand + item name), adds new items, and creates any brand not yet in the system - exactly the same insert/update logic either way.
Any one of these three keys enables the image/PDF path - whichever is set on the web service is used automatically, checked in this order (override with VISION_PROVIDER=anthropic|xai|openai if more than one happens to be set):
| Env var | Provider | Images | PDFs |
|---|---|---|---|
ANTHROPIC_API_KEY |
Claude | Yes | Yes |
XAI_API_KEY |
Grok (xAI) | Yes | No - use an image or Excel instead |
OPENAI_API_KEY |
OpenAI | Yes | No - use an image or Excel instead |
If none of these three keys are set, the Excel upload still works exactly as before - only the image/PDF option is disabled, with a message pointing the user at Excel or an Admin.
AI-read values can occasionally misread a digit or a column, especially from a blurry photo - review the upload summary (new/updated/skipped counts and which provider read it) and spot-check a few items after an image/PDF upload.
After login, the AI Analysis tile on the home page opens /analytics - a profitability dashboard built from an uploaded sales export.
.xlsx/.xls) or CSV file. Every sheet in a workbook is read, so one file with a tab per financial year works in a single upload. Column names are matched loosely and in any order - it looks for Date, Item, Sales Amt, Cost Amt, Profit/Loss (Division and Qty are optional extras that unlock the division breakdown and unit counts). Each new upload replaces the previous dataset - this is a current-snapshot dashboard, not a history of every file ever uploaded.In Sales in your scope, the Profitability Report - Scheme Matched Sales panel filters your uploaded profitability report (Interval Sales Analytics Upload - the Date/Vch No/Account/Item/Qty/Unit/Sales Amt/Cost/Profit-Loss/Profit% format exported from Busy) down to only the rows that:
Each matched row shows the computed backend claim amount, using the same Fixed/Percentage/Slab reward math as the automatic claim engine (scheme_engine.py), so Admin can see at a glance which Busy sales are scheme-eligible without manual cross-checking.
After login, users can choose Schemes or Purchase Orders. Any logged-in user can submit a stock requisition with branch, division, supplier, delivery address, product/model/serial details, stock balance, sales rate, quantity, and estimated price. Admin and MIS Executive users see every request and can update its status, Busy PO number, order date, and processing notes.
To send an alert to MIS Executive through WhatsApp, configure a WhatsApp Cloud API app and add these Render environment variables to the web service:
WHATSAPP_ACCESS_TOKEN - permanent or system-user access token from MetaWHATSAPP_PHONE_NUMBER_ID - the sending WhatsApp Business phone number IDWHATSAPP_MIS_RECIPIENTS - comma-separated recipient numbers in international format, for example 919876543210,919812345678WHATSAPP_API_VERSION - optional; defaults to v23.0The PO is saved even when WhatsApp is not configured or its provider rejects a message. WhatsApp Business may require an approved message template for business-initiated alerts outside the customer service window.
There is no “forgot password” email flow. A locked-out user is told on the login page to ask their Admin. Instead:
POST /api/users/{user_id}/reset-password, Admin-only, which hashes and saves the new password and invalidates any old reset token that may still exist from before this flow was introduced.The old email-based /auth/forgot-password and /auth/reset-password endpoints, and the forgot_password.html / reset_password.html pages, no longer exist in this codebase.
The “Final Order” / “Send PO Email” action on a Purchase Order sends a real email through SMTP. In code, the host, username, port, and from-address already default to the company Gmail mailbox:
SMTP_HOST defaults to smtp.gmail.comSMTP_USER / SMTP_FROM default to initiative.lucknow@gmail.comSMTP_PORT defaults to 587So on Render, the only thing you must set yourself is SMTP_PASSWORD - a Gmail App Password, not the normal account password (Gmail blocks plain-password SMTP login).
initiative.lucknow@gmail.com.myaccount.google.com/apppasswords while signed in as that account).In the Render dashboard, open the idspl web service -> Environment, and add:
| Key | Value |
|---|---|
SMTP_PASSWORD |
the 16-character app password from step 1 |
render.yaml already declares this key with sync: false, so if you deploy via Blueprint, Render will prompt you for it instead of trying to read it from the repo (and it’s never committed).
Redeploy (or just save the environment changes - Render restarts the service automatically) and test:
Emailed to N recipient(s). instead of Not sent: ....SMTP_HOST, SMTP_USER, SMTP_FROM, and SMTP_PASSWORD with that provider’s values; nothing else in the code needs to change.After login, the Ageing Stock Analysis tile opens /ageing-stock - an Admin-uploaded, everyone-viewable report of slow-moving stock, broken out by ageing bucket, Category, Brand, and physical location.
systemd and nginx.ORACLE_CLOUD_FREE_TIER_SETUP.md.