# 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 ```text 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 │ ├── erd.txt (v2.4 — old procurement schema) │ ├── new erd.txt (v2.5 — material → supplier → category) │ ├── NewERD.txt (v2.6 — integrated procurement + inventory) │ └── 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`): - **مدیر پروژه (PM)** → lands in the inventory panel (their project) — can record stock changes, manage the project's warehouses and confirm arrivals («کالاهای در راه»). - **مدیر تدارکات / ادمین** (the same person) → 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. Until Casdoor is fully seeded, use «ورود آزمایشی (توسعه)» on the login page (role + project). Casdoor (org `inventory`, app `inventory-app`) is the planned production identity source. --- ## 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 ```powershell cd 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: ```powershell 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): ```powershell 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`): ```powershell .\mc.cmd ls --recursive ``` --- ## Database credentials / connection ```text Host: localhost Port: 5433 Database: procurement Username: postgres Password: postgres ``` ```powershell docker exec -it procurement-postgres psql -U postgres -d procurement ``` --- ## Common operations ```powershell 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 - The backend has **no authentication** (dev placeholder) — do not expose to the public internet. - 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`.