A multi-currency ERP for small wholesale/retail businesses — inventory, sales, purchasing, clients/suppliers, a cash & bank ledger with partial and cross-currency payments, and live reporting dashboards. Built as a production-shaped Django + Angular monorepo that runs end-to-end with a single docker compose up.
Why it exists: built to run a real multi-currency (EUR / USD / LEK) trading business where invoices are paid in installments and in whichever currency the customer has on hand — the accounting has to stay correct anyway.
flowchart LR
User([User]) -->|HTTPS| NG[Angular 18 SPA<br/>Material · Tailwind · ECharts]
NG -->|/api · HttpOnly JWT cookie| NGINX[nginx]
NGINX -->|reverse proxy| API[Django 5.1 + DRF<br/>cookie JWT auth]
API -->|psycopg2| DB[(PostgreSQL)]
API -.->|weekly sync| FX[(ExchangeRate-API)]
subgraph Docker Compose
NG
NGINX
API
DB
end
Request flow: the SPA calls the API with an HttpOnly, SameSite JWT cookie (no token in JS). DRF authenticates via a cookie-aware JWTAuthentication. Exchange rates are fetched from an external API and cached in Postgres, refreshed weekly.
See the full entity-relationship diagram: db/ERdatabaseSchema.svg.
| Layer | Tech |
|---|---|
| Frontend | Angular 16, Angular Material, TailwindCSS, ngx-echarts, xlsx |
| Backend | Django 5.1, Django REST Framework, SimpleJWT (cookie-based), gunicorn |
| Database | PostgreSQL |
| Infra | Docker Compose, nginx |
| Quality | pytest, ruff, GitHub Actions, drf-spectacular (OpenAPI) |
Comprehensive analytics with live data visualization, multi-currency support, and at-a-glance KPIs.
Sales & Purchase Analytics
Track sales and purchase trends over time with detailed revenue breakdown.
Top Products & Customers
Identify your best-performing products by inventory moved.
Monitor top clients and suppliers with transaction volumes.
Handle multi-currency payments, installments, and cross-currency settlements with auditable ledgers.
Payment Status Overview
Visualize pending, partial, and completed payments at a glance.
Revenue Breakdown by Category
Track revenue, costs, and profit margins across product categories.
Create, track, and manage sales and purchases with full audit trails.
Transaction Details
View complete transaction history with line items, payment status, and client information.
Maintain detailed profiles with transaction history and inventory metrics.
Client Information
Access client details, outstanding balances, transaction history, and purchase patterns.
Product Analytics
Monitor product pricing trends, inventory levels, sales velocity, and supplier activity.
Real-time stock monitoring with low-stock alerts and supplier management.
Track current stock levels, manage suppliers, and monitor active account balances.
Requires Docker + Docker Compose.
git clone https://github.com/LedjoLleshaj/Full-stack-ERP.git
cd Full-stack-ERP
# Configure backend env (generates secrets locally; never commit .env)
cp backend/.env.example backend/.env
python3 -c "import secrets; print('SECRET_KEY=' + secrets.token_urlsafe(64))" # paste into backend/.env
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:4200 |
| API (v1) | http://localhost:8080/api/v1/ |
| API docs (Swagger) | http://localhost:8080/api/docs |
| API health | http://localhost:8080/erp/health/ |
| Django admin | http://localhost:8080/admin |
Migrations run automatically on backend startup; a demo admin (admin / adminpass) is seeded by backend/entrypoint.sh — change these before any non-local use.
The project includes a Makefile with shortcuts for common operations. All commands run from the repo root.
| Command | What it does | When to use |
|---|---|---|
make up |
Builds and starts all Docker containers in detached mode (docker compose up --build -d) |
Quick run / first launch. Spin up the full stack (nginx, API, DB, frontend) to test the app end-to-end. Also use after pulling new changes to rebuild images. |
make down |
Stops and removes all containers (docker compose down) |
When you're done working and want to free up resources. Preserves database volumes. |
make build |
Builds Docker images without starting containers (docker compose build) |
When you only need to verify that images build successfully (e.g., after changing a Dockerfile or dependencies) without running the stack. |
make test |
Runs the backend pytest suite (pytest -v) using the local .venv |
Development. Run frequently while writing code. Uses SQLite in-memory (via settings_test), so no Docker needed. |
make lint |
Runs ruff linter on the backend (ruff check erp/) |
Development. Check for code style issues and errors before committing. |
make lint-fix |
Runs ruff with --fix to auto-correct lint issues |
Development. Quickly fix auto-fixable lint errors (unused imports, formatting, etc.). |
make migrate |
Applies Django migrations using the local .venv |
Development. After creating or pulling new migrations, run this to update your local database schema. |
make shell |
Opens a Django interactive shell | Development / debugging. Explore models, test queries, or inspect data interactively via the Django ORM. |
make seed |
Loads db/seed.sql into the running Postgres container |
After make up. Populate the database with sample data for manual testing or demos. Requires the Docker stack to be running. |
make clean |
Stops containers and removes volumes (docker compose down -v) |
Nuclear reset. Destroys the database and all container state. Use when you want a completely fresh start. |
Quick demo / full-stack test:
make up # start everything
make seed # load sample data
# open http://localhost:4200
make down # stop when doneDay-to-day development:
make test # run tests (no Docker needed)
make lint-fix # auto-fix lint issues
make migrate # apply new migrations locallyFresh start (wipe database):
make clean # stop + delete volumes
make up # rebuild from scratchOnce running, interactive OpenAPI docs are available at http://localhost:8080/api/docs.
The versioned REST API lives at /api/v1/ (paginated, ViewSet-based). Legacy routes under /erp/ remain for backward compatibility.
.
├── backend/ # Django 5.1 + DRF
│ ├── erp/ # domain app
│ │ ├── api/ # view functions (legacy routes)
│ │ ├── services/ # business logic (inventory, payments)
│ │ ├── viewsets.py # /api/v1/ ViewSets
│ │ └── tests/ # pytest suite
│ └── backend/ # Django project settings
├── frontend/ # Angular 16 SPA
├── db/ # ER diagram, seed data, cleanup scripts
├── docs/ # deployment guide, schema guide
└── docker-compose.yml
The domain is 13 models. The interesting choices (and their trade-offs):
- Transaction ↔ Payment ledger. A
Transaction(PURCHASE or SALE) carries a total; one or morePaymentrows settle it. Status (PENDING→PARTIAL→COMPLETED) is derived from payments vs. total. This is what makes installment payments first-class instead of a booleanis_paid. - Cross-currency settlement. A sale in EUR can be paid in LEK: the
Paymentstores both the converted amount (transaction currency) and theoriginal_amount/original_currency/exchange_rateused. Rates come from a cachedExchangeRatetable synced weekly. - Account ledger. Every payment moves money in/out of a cash or bank
Accountvia anAccountTransactionrow that recordsbalance_after, giving an auditable running balance per account. - Soft deletes. Clients, suppliers, and products use
is_activeflags rather than row deletion, preserving historical transactions. - Decimal money. All monetary values are
DECIMAL(never float) to avoid rounding errors in accounting. - Cookie-based JWT. Access/refresh tokens live in
HttpOnlySameSitecookies (XSS-resistant) rather thanlocalStorage; the trade-off is CSRF surface, mitigated bySameSiteand same-origin proxying.
# Backend
cd backend && pytest # or: python manage.py test erp
# Frontend
cd frontend && npm test