ali_mohaghegh c5997ac243 feat(frontend): unified warehouse UI, calendar fix, partial receipts, RBAC views, admin panel, dev-mode 3-step
- header: icon box + dynamic subheader '[user] | [role] – [project/همه پروژهها]'
  on every warehouse page; module context kept in the hamburger (items 1-2)
- project manager view: office button + inline logout removed; hamburger
  contains only «خروج از حساب»; sub-views get «بازگشت به داشبورد» (Header
  backTo/goBack); «افزایش موجود» primary button on the dashboard (item 8)
- daily control: cells render the day-of-month (was the Jalali year),
  month nav uses real Jalali months, header is pure Jalali
  «امروز ۸ شهریور ۱۴۰۵», today gets a blue accent (item 3)
- arrivals: partial (item/qty) receipt — any valid item is enough to submit,
  ceiling + allocation-sum validation with inline errors, Received/In-Transit
  badges; ledger shows «رسیده X از Y» / «در راه Z» + «رسید جزیی» badge (item 4)
- api client: multipart uploads no longer send Content-Type json (fixes the
  invoice/proforma 400 «دادهی ارسالشده نامعتبر است»); undefined query
  params are dropped (fixes empty admin project filter, item 5)
- organization: user-creation card removed, role column read-only for
  procurement manager, Casdoor sync button (item 6)
- admin panel (/admin): Casdoor user sync + grant/revoke «مدیر تدارکات»;
  Developer Mode is a 3-step admin-gated flow on the login page (item 7)
2026-09-01 17:28:19 +03:30

Procurement Stack v1.3 — Dockerized (PostgreSQL + FastAPI + React + MinIO)

Self-contained ship package: everything needed to run the stack is in this folder — no external files or sibling folders required.

Service Container Tech Exposed port
postgres procurement-postgres PostgreSQL 18 5433
minio procurement-minio MinIO object storage (S3-compatible) 9000 (API), 9001 (console)
casdoor procurement-casdoor Casdoor (OIDC identity provider) 8080
api procurement-api FastAPI + SQLAlchemy (asyncpg) 8000
frontend procurement-frontend React 19 + Vite, served by nginx 3000
Browser ──► frontend :3000 (nginx SPA)
                │
                └── /api/* proxy ──► api :8000 (FastAPI, /api/v1)
                                          │
                                          ├── postgres :5432 (procurement DB)
                                          ├── minio :9000 (invoice/proforma files, bucket `procurement-files`)
                                          └── casdoor :8000 (OIDC — login for the inventory panel)

📁 Layout

v1.3/
├── docker-compose.yml        ← all paths are relative to this folder
├── start.ps1                 ← PowerShell helper (build + start + status)
├── mc.cmd                    ← MinIO Client helper (inspect the bucket)
├── README.md
├── sql/
│   └── migration.sql         ← DB schema v2.6 (auto-runs on first boot)
├── docs/                     ← ERDs + migration plan + auth guide
│   ├── erd.txt               (v2.4 — old procurement schema)
│   ├── new erd.txt           (v2.5 — material → supplier → category)
│   ├── NewERD.txt            (v2.6 — integrated procurement + inventory)
│   ├── casdoor-auth.md       (v2.6 — Casdoor login, roles, failure scenarios)
│   └── material-supplier-category-migration-plan.md
├── backend/                  ← build context of the `api` service
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── .dockerignore
│   ├── app/                  (source code — procurement + inventory + auth)
│   └── tools/                (smoke-test + seed scripts, copied into the image)
└── frontend/                 ← build context of the `frontend` service
    ├── Dockerfile
    ├── nginx.conf
    ├── .dockerignore
    ├── .env.example          (copy to .env if you need a custom API base URL)
    ├── index.html
    ├── package.json
    ├── package-lock.json
    ├── tsconfig*.json
    ├── vite.config.ts
    ├── public/
    └── src/
        └── pages/
            ├── Dashboard/    (procurement office)
            ├── Wizard/       (add new offer)
            ├── Suppliers/    (suppliers & topics)
            ├── Projects/     (projects)
            ├── Units/        (unit conversions)
            └── Inventory/    (warehouse management panel — React port)

🔐 Login

The whole system is behind a login page (/login). Login is in-page: the form (username / password / optional organization) is sent to POST /api/v1/auth/login; the backend exchanges it with Casdoor (password grant), verifies the returned JWT, and starts the session — no browser redirect to Casdoor.

  • مدیر پروژه (PM) → lands in the inventory panel (their project) — can record stock changes, manage the project's warehouses and confirm arrivals («کالاهای در راه»).
  • مدیر تدارکات / ادمین → lands in the procurement office — can manage quotes, suppliers, projects, and in the inventory panel: dashboard, daily control, «کالاهای در راه» (receiving included) and «سازمان‌دهی» (users, PMs, warehouses). The «انبارها» and «تغییر موجودی» sections are PM-only.

Developer Mode (top-left button on the login page) allows test login as مدیر تدارکات / ادمین / مدیر پروژه without Casdoor. It is controlled by the VITE_ENABLE_DEV_LOGIN env var (in Docker: the DEV_LOGIN_ENABLED compose build arg, default true — set it to false for production builds).

What Where Credentials
Casdoor console http://localhost:8080 admin / 123
Seeded app users (org inventory) razaei (admin), mousavi (مدیر تدارکات), mohaghegh (مدیر پروژه), tester (مدیر پروژه، پروژه «تست») password 123456

Run backend/tools/seed_casdoor.py once after the first boot to create the Casdoor organization, application and users (idempotent). Full details — addresses, credentials, role model, failure scenarios and the checklist — are in docs/casdoor-auth.md.


Prerequisites (target machine)

  • Docker Desktop (Windows) or Docker Engine + Compose plugin (Linux/macOS)
  • ~3–4 GB free disk for images (postgres:18, python:3.13-slim, node:22-alpine, nginx:alpine, minio/minio, minio/mc)
  • Internet access on first build (image + npm ci downloads)

Start the whole stack

cd <this-folder>
docker compose up -d --build

First run downloads base images and builds the frontend (takes a few minutes). Subsequent runs: docker compose up -d.

Wait until all services are up:

docker compose ps

All should show Up (postgres also (healthy)).


Verify

What Where
Frontend UI http://localhost:3000
API docs (Swagger) http://localhost:8000/docs
API health http://localhost:8000/health → {"status":"ok","service":"procurement-api"}
MinIO console http://localhost:9001 (user/pass minioadmin/minioadmin)
Database localhost:5433, db procurement, user/pass postgres/postgres

Quick end-to-end check (frontend proxy → API → DB):

curl http://localhost:3000/api/v1/quotes?expand=supplier,project,line_items,termsheets,events,decision,receipt

MinIO (object storage for invoice / proforma files)

Item Value
S3 API endpoint localhost:9000 (from the API container: minio:9000)
Web console http://localhost:9001
Root user / password minioadmin / minioadmin (dev — change for production)
Bucket procurement-files (created automatically by the minio-init container)
Object key layout {quote_id}/{kind}/{attachment_id} where kind = invoice | proforma

File bytes live in MinIO; the quote_attachments table holds the metadata.

Ad-hoc bucket inspection from the host (see mc.cmd):

.\mc.cmd ls --recursive

Database credentials / connection

Host:     localhost
Port:     5433
Database: procurement
Username: postgres
Password: postgres
docker exec -it procurement-postgres psql -U postgres -d procurement

Common operations

docker compose logs -f api          # see logs
docker compose down                 # stop (keep data)
docker compose up -d --build        # rebuild after code changes
docker compose down -v              # ⚠️ full reset — wipes DB + MinIO volumes

Known behavior

  • Authentication is Casdoor-based (JWT verified via JWKS): /auth, /users, /inventory and /pm are protected (401 without a token, 403 without a role). The procurement CRUD endpoints (quotes, suppliers, materials, …) are still open — dev placeholder, do not expose to the public internet.
  • Roles live in the app's users table (Casdoor is identity-only): pm, procurement_manager, admin. See docs/casdoor-auth.md.
  • Schema runs automatically ONLY on the first boot of an empty postgres_data volume (see docker-entrypoint-initdb.d); the API also runs CREATE TABLE IF NOT EXISTS for the units-dashboard tables and quote_attachments at startup.
  • Invoice upload requires an accepted/arrived quote; proforma has no restriction.
  • The supplier detail page (/suppliers/:id) shows the purchase history with invoice/proforma download links from MinIO.
  • If builds fail with disk-full errors: docker system prune -a, then rebuild.
  • If the frontend shows «اتصال به سرور برقرار نشد» → the api container is not reachable; check docker compose ps and docker compose logs api.
S
Description
No description provided
Readme
3.9 MiB
Languages
TypeScript 54.2%
Python 40.6%
PLpgSQL 2.5%
CSS 1.7%
PowerShell 0.6%
Other 0.2%