84 Commits
Author SHA1 Message Date
ali_mohaghegh 0cd4f8f361 feat(ui): AppSwitcherMenu parity + hub-only warehouse headers + calendar badge removal
- shared AppSwitcherMenu ('پنل‌ها' title, active filled item with ✓ فعلی,
  «مدیریت انبار»/«مدیریت تدارکات» switcher, divider + خروج از حساب) used by
  both the procurement dashboard and the warehouse hub (PM variant keeps
  logout-only); replaced the old raw «بازگشت به دفتر» link
- warehouse header mirrors the procurement pattern: icon + hamburger only
  on the hub dashboard; sub-views (تغییر موجودی، کنترل روزانه، انبارها،
  کالاهای در راه، سازمان‌دهی) render a minimal header with just the title
  and «بازگشت به داشبورد»
- removed the self-referencing «داشبورد انبار» tab from the hub strip
- removed the red per-day transaction-count badges from the daily-control
  calendar (backend untouched — purely visual)
- removed the test subtitle «داده‌ها در پایگاه داده ذخیره می‌شوند» from the
  procurement dashboard header
2026-09-03 15:01:07 +03:30
ali_mohaghegh e4ae34ddbc feat(sync): live cross-module cache invalidation (no manual refresh)
- procurement store refreshes right after a warehouse receipt (arrivals) and
  after role/project changes (organization/admin), so the ledger reflects
  warehouse actions immediately
- refetchOnWindowFocus enabled on the shared query client
- BroadcastChannel (lib/crossTab.ts): mutations broadcast 'quotes' /
  'inventory'; all warehouse pages + the store hook resync open tabs
  automatically (receipt in one tab updates the ledger in another)
- warehouse mutations (stock changes) notify other tabs; same-page refresh
  behavior unchanged
2026-09-03 15:00:58 +03:30
ali_mohaghegh f9ec68815e fix(suppliers): enforce Category → Supplier hierarchy, remove rogue-category bug
Domain model (v2.6): suppliers.category_id NOT NULL (1:N from the 8 fixed
categories), materials are global and link to suppliers via
supplier_materials. This fixes three regressions of the old inverted tree:

- remove the intermediate 'یک متریال را انتخاب کنید' screen: a category
  card now routes straight to that category's supplier list
  (/suppliers/category/:category); TopicPage is deleted and legacy
  /topics/:topic[/:category] URLs redirect to the new hierarchy
- kill the accidental-category bug: api.materials.create silently POSTed
  /material-categories for any unknown 'category' (a material name!), which
  is how the rogue «پریز» category (+ junk material) appeared. Unknown
  categories now fail loudly instead of creating catalog rows (DB cleaned:
  exactly the 8 canonical categories remain)
- fix inverted wording: supplier list is «تأمین‌کننده‌های این دسته» with
  category-first breadcrumbs; the legacy material multi-select on the
  add-supplier form is gone (a supplier gets the page's category)
- supplier details screen logic untouched — only its breadcrumb link
  targets were updated to the new routes
2026-09-03 15:00:50 +03:30
ali_mohaghegh b4ed18af71 fix(nginx): resolve api upstream at request time via Docker DNS
After the api container restarts it gets a new IP; nginx cached the old one
at startup and kept proxying /api/* to the stale address — which by then
belonged to another service (casdoor), so login returned Casdoor's error
envelope and the frontend crashed with "Cannot read properties of null
(reading 'user')". proxy_pass now uses a variable + resolver 127.0.0.11 so
the upstream re-resolves per request. Verified: login works, and still works
after restarting the api container.
2026-09-02 18:42:02 +03:30
ali_mohaghegh a55c679bb7 fix(seed): enforce only the 8 canonical topic categories
The topic grids (PM system + wizard) showed extra categories like «سیم»,
«کابل», «ثبثب», «dsfg». Those rows were leftover junk in material_categories
from earlier testing — previously invisible because the frontend hardcoded
the 8-topic list; the API-driven topic grid surfaced them. The rows are
deleted (they had no references) and the seeder now deletes any category
outside the 8 canonical slugs, so a re-seed always yields exactly the same
8 categories as before. Dump refreshed from the clean state.
2026-09-02 18:28:54 +03:30
ali_mohaghegh acb9958059 docs+data: refresh demo dumps (clean seeded state) + restore/seeder docs
- backups/procurement.dump regenerated from the clean seeded state (was the
  junk test data); README documents both the app seeder and the restore path
- docs/casdoor-auth.md: later-batch changelog (fulfillment split, demo
  seeder, API-driven topics, hub-and-spoke, count modal, FOUND ceiling,
  semantic colors)
2026-09-02 18:15:57 +03:30
ali_mohaghegh ac76af2ffe feat(frontend): fulfillment split, API-driven topics, hub-and-spoke nav, stock-count modal, FOUND ceiling, semantic colors
- bug1: quote header fulfillment counter («رسید: X از Y قلم» amber / «تکمیل
  شد» green / «در راه»); expanded line items visually split into the
  received part (رسید شده ✓ + date + target warehouse from the receipt
  allocations) and the in-transit remainder — quantities always sum to the
  original line (invariant D holds, display-only split)
- bug2: 8-topic grids (suppliers/wizard/units) now come from
  /material-categories via the store (new lib/topics.ts; fixed the mismatched
  «ابزار و تجهیزات ساخت» label); suppliers topic->category resolution no
  longer falls back to an arbitrary category; duplicate month/status lists
  reuse the shared dictionaries; TopicGrid gained a loading EmptyState
- bug3: hub-and-spoke — the tab strip renders only on the warehouse
  dashboard; sub-views hide it and navigate back via «بازگشت به داشبورد»;
  «افزایش موجود» became the «تغییر موجودی» entry button
- bug4: stock count goes through a confirmation modal (current vs counted
  vs variance with کسری/مازاد badges) before posting STOCK_COUNT; numbers
  parsed defensively
- bug5: found qty is capped client-side (red input + inline warning, submit
  disabled) and backend returns 422 when exceeding the missing balance;
  fully-recovered losses drop off the pending list
- bug6+7: quantities render as absolute values with semantic color coding
  (emerald inflow / rose outflow / blue neutral) across dashboard timeline,
  daily control and the change-inventory form
2026-09-02 18:15:32 +03:30
ali_mohaghegh ca84d8664e feat(backend): demo seeder, receipt allocations in expand, FOUND ceiling guard
- tools/seed.py: wipes app tables and seeds a clean realistic demo (3
  projects x 2 warehouses, 4 suppliers, 12 materials across all 8 topic
  categories, 4 quotes covering offered / in-transit / partial / completed
  with receipts + matching stock); user roles bound to Casdoor identities
  by username (seed_casdoor.py first)
- expand_service: the line-item receipt aggregate now carries the warehouse
  allocations (target warehouse per received part) for the ledger's
  «رسید شده» split
- inventory_service: FOUND («پیدا شده») is guarded so recovery cannot exceed
  the missing balance for the same (warehouse, material, brand, specs) —
  422 with the remaining balance; STOCK_COUNT semantics unchanged (absolute
  counted qty, auto-diff)
2026-09-02 18:15:11 +03:30
ali_mohaghegh d56cd13cb9 chore(backups): ship exact DB-state dumps + restore scripts
The databases live in Docker volumes (not in git), so teammates on a fresh
boot saw an empty system. backups/procurement.dump + casdoor.dump are exact
pg_dump snapshots of the current demo state (users/roles, projects,
warehouses, quotes with receipts, inventory rows; Casdoor org/app/users);
restore.ps1 / restore.sh replace both DBs with the committed state
(--clean --if-exists, verified by a round-trip restore into throwaway DBs).
README documents how to restore and how to take a fresh dump.
2026-09-02 11:21:09 +03:30
ali_mohaghegh 31290ffbda docs: README + casdoor-auth guide for the v1.3 changes
- README: Traefik edge (app at http://localhost), single postgres (casdoor
  DB inside), prod overlay usage, partial receipts, admin panel, dev-mode
  3-step flow, layout for infra/ and 02-casdoor-db.sql
- docs/casdoor-auth.md: new section «Stack changes in this release» (DB
  merge, Traefik, RBAC, partial receipt, calendar, upload, project filter),
  updated addresses and redirect-URI notes
2026-09-01 17:28:45 +03:30
ali_mohaghegh 8825f333a7 infra: single postgres instance (casdoor DB merged) + Traefik v3 edge
- remove the separate casdoor-postgres container; casdoor uses the main
  postgres (db casdoor, created by sql/02-casdoor-db.sql on fresh volumes);
  data migrated via pg_dump/pg_restore (backups kept outside the repo)
- Traefik v3.6 as the public edge (ports 80/443): docker provider pinned to
  the dedicated traefik_network (fixed-name network), host rules for
  app/api/auth/s3/storage (env-overridable, localhost defaults), frontend
  nginx demoted to internal SPA server; docker-compose.prod.yml adds real
  domains + Let's Encrypt + HTTP→HTTPS redirect (redirect middleware in
  infra/traefik-dynamic.yml)
- v3.6 required (Traefik <=3.3 fails against Docker Engine 29.x); socket
  mounted rw (Docker Desktop rejects :ro)
- start.ps1: app URL now http://localhost, casdoor + db notes
2026-09-01 17:28:33 +03:30
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
ali_mohaghegh 83f4fb0747 fix(backend): arrival-confirmation 500, receipt state, RBAC, Casdoor user sync
- pm.py/inventory.py: remove db.begin() on already-begun sessions — the
  scope dependencies read from the session first, so opening a new
  transaction raised InvalidRequestError -> 500 on every receipt/transaction
  (JWT path); get_db already commits/rolls back atomically
- pm.py: receipt response returns the real derived state (was hardcoded
  'arrived'); dev-user creation is race-free (insert on conflict)
- expand_service: aggregate 1:N receipts per line item (received_qty sum +
  count) so the ledger can show partial-receipt totals
- users.py: role changes are admin-only (RBAC); new POST /users/sync mirrors
  Casdoor org users into the app users table (identity only, roles untouched)
- config/compose: CASDOOR_ADMIN_USER/PASSWORD for the sync endpoint
- seed_casdoor: redirect URI updated for the Traefik edge
2026-09-01 17:28:06 +03:30
ali_mohaghegh c59f5ad1a3 docs: Casdoor auth guide + README login/known-behavior update
- docs/casdoor-auth.md: addresses and credentials (Casdoor console
  admin/123, seeded users), in-page login flow, status-code table, the
  User/Organization/Role/Permission/Application model, the 9 failure
  scenarios with debug steps, Test 1-6 results, 23-item checklist, and how
  to manage Casdoor (console, seeding, first-admin bootstrap)
- README: replace the 'no authentication' and 'Casdoor not seeded' notes
  with the current login flow, developer-mode toggle and a link to the
  auth guide
2026-08-29 14:44:27 +03:30
ali_mohaghegh 9caa4d804e feat(frontend): in-page Casdoor login form + env-gated Developer Mode panel
- redesigned /login (Tailwind, RTL): centered username/password/organization
  form posting to POST /api/v1/auth/login — session starts without leaving
  the page; PM lands on the inventory panel, others on the procurement office
- Developer Mode button fixed top-left opens a role/project test panel
  (admin role added) gated by VITE_ENABLE_DEV_LOGIN (DEV_LOGIN_ENABLED build
  arg in Docker, default enabled)
- removed the old Casdoor redirect button, the PKCE ?code= callback, and the
  public casdoor-config.js — the client secret is no longer shipped to the
  browser; backend mediates the password grant instead
2026-08-29 14:44:13 +03:30
ali_mohaghegh fde2168606 feat(casdoor): seeder for current Casdoor version + wire api env and dev-login build arg
- seed_casdoor.py: JSON type=login + cookie session (the old form-encoded
  login-normal API is gone), application with grantTypes incl. password,
  passwordType=plain for the seeded users, idempotent re-runs; adds a
  dedicated tester user
- docker-compose: Casdoor env for the api service (endpoint casdoor:8000
  in-network, issuer http://localhost:8080 as advertised by discovery) and
  the DEV_LOGIN_ENABLED build arg for the frontend
2026-08-29 14:44:01 +03:30
ali_mohaghegh 3cd76d335f fix(auth): Casdoor JWT verification and 401 semantics; add /auth/login password-grant endpoint
- verify kid from the JWT header (pyjwt decode() returns claims, not the
  header — the old lookup was always None and rejected every token)
- convert JWK entries to crypto keys explicitly (pyjwt 2.13 dropped dict
  keys) and fetch the JWKS from /.well-known/jwks
- 401 for missing/invalid/expired/wrong-issuer/wrong-audience tokens
  (was 403/400); 403 stays for valid-but-roleless users; 503 when Casdoor
  is unreachable; new CASDOOR_ISSUER setting separates the in-network
  endpoint from the browser-facing issuer
- dev-header fallback in inventory/pm scopes now requires an explicit
  X-User-Role header (no auth info at all -> 401)
- new POST /api/v1/auth/login: password grant against Casdoor, local JWT
  verification, auto-provisioning, returns token + profile with the role
  resolved from the app users table
2026-08-29 14:43:49 +03:30
ali_mohaghegh 06a9a654a5 docs: update README layout and login/access notes 2026-08-27 15:04:25 +03:30
ali_mohaghegh 6a91d32cd5 fix(dashboard): show price unit (toman) inside the amount-in-letters bracket 2026-08-27 15:04:25 +03:30
ali_mohaghegh 068c51909c fix(db): fill item_receipts.confirmed_by in office paths; resilient per-statement v2.6 upgrade (root cause of 500 on receipt) 2026-08-27 15:04:25 +03:30
ali_mohaghegh eb2a50eee1 feat(inventory): role-aware header navigation - PM top nav without hamburger, admin top nav plus compact account menu 2026-08-27 15:04:25 +03:30
ali_mohaghegh 5e690a7fc8 feat(inventory): daily control - admin project filter and calendar navigation layout 2026-08-27 15:04:25 +03:30
ali_mohaghegh 0774f08823 fix(inventory): adjustment form - transfer rows, safe date handling, Shamsi date picker, drop new-item category field 2026-08-27 15:04:25 +03:30
ali_mohaghegh a191147eca feat(arrivals): in-transit goods dashboard - quote cards, per-item receive and warehouse allocation, days elapsed, admin access, received<=ordered limit 2026-08-27 15:04:25 +03:30
ali_mohaghegh f48f3752af feat(org): user and PM management - list/define users, roles, project assignment (API + organization panel) 2026-08-27 15:04:24 +03:30
ali_mohaghegh 034c05c713 feat(auth): dev login offers only the two business roles (procurement/project manager) 2026-08-27 15:04:24 +03:30
ali_mohaghegh f30c98adc0 chore: remove legacy HTML inventory-dash prototype (superseded by React port) 2026-08-27 15:04:24 +03:30
ali_mohaghegh debb193b90 chore: add .gitattributes — normalize line endings to LF 2026-08-25 19:08:26 +03:30
ali_mohaghegh 76151c9df4 chore(legacy): add shipped HTML inventory-dash prototype as reference 2026-08-25 19:08:15 +03:30
ali_mohaghegh 4e105c5cd5 feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login 2026-08-25 19:08:15 +03:30
ali_mohaghegh 170aed66ea feat(backend): add FastAPI backend — procurement, inventory domain, Casdoor auth, PM arrival endpoints 2026-08-25 19:08:14 +03:30
ali_mohaghegh c8a9b66395 feat(db): add v2.6 schema migration — global materials, supplier categories, inventory ledger, PM arrival flow, triggers 2026-08-25 19:08:13 +03:30
ali_mohaghegh aca56dc1a5 docs: add ERD diagrams (v2.4/v2.5/v2.6) and material-supplier-category migration plan 2026-08-25 19:08:13 +03:30
ali_mohaghegh 5b27647a0a chore: add repo scaffolding (compose, scripts, gitignore, env example) 2026-08-25 19:08:02 +03:30