← All work A-03 Case · /en/dev/oms

OMS (staff_v2)

Order management for a furniture factory: the whole path of an order — client, payments, designers and drawings, production deadlines in working days, shipping, closing documents — plus purchasing, statistics and a photo library. Replaced the 2017 PHP/MySQL system whose spec I wrote as a sales manager.

Status
In production since June 2026
Role
Everything: backend, front end, migration, deploy
Stack
Python 3.13FastAPISQLAlchemy 2 asyncPostgreSQL 16 + pgvectorRedisRabbitMQReact 19TypeScriptViteTanStack Query + TableTailwind + shadcnTiptapRechartsCaddyDocker ComposeGitHub Actions
W 1160
A-031440 × 900 · 390 × 844
staff · internal
OMS staff_v2: orders list with filters, columns and statuses
OMS staff_v2 on a phone: orders as cards
Orders list on desktop and on a phone — cards instead of tables. Data blurred on purpose.
01What it does replacing the 2017 system

The entire order in one card

Staff keep the entire order in one card: client and contacts, amount and payments, designers, files and drawings, a discussion thread with a rich-text editor, and a change log of who changed which field. Purchasing lives next to it: requests for materials per order or per workshop, suppliers with their assortment tags, invoices and files.

Management sees statistics on money, production, purchasing and managers. A separate module turns live product photos into catalog frames through Gemini and keeps all of the company's photos in one searchable library. It works on a phone too — cards instead of tables.

02Services 4 + 3 services · 1 host4 + 3 services

How it is built

Docker Compose
Staff · desktop and phone
AI assistant · read-only SQL over HTTP
GitHub Actions · scp + ssh deploy
Caddy
HTTPS, proxy, static
Frontend SPA · React 19
backend
OMS API, FastAPI
image_service · API
PostgreSQL + pgvector · images
PostgreSQL 16 · OMS
Redis · sessions, permission cache, lock
media/
RabbitMQ · 4 queues + retry/DLQ
image_worker
isdayoff.ru · production calendar
SMTP
Gemini
Legend
  • HTTP
  • event
  • external
  • requests
  • store
Docker Compose
GitHub Actions · scp + ssh deploy
Staff · desktop and phone
AI assistant · read-only SQL over HTTP
Caddy · HTTPS, proxy, static
Frontend SPA · React 19
backend · OMS API, FastAPI
PostgreSQL 16 · OMS
Redis · sessions, permission cache, lock
media/
image_service · API
RabbitMQ · 4 queues + retry/DLQ
image_worker
PostgreSQL + pgvector · images
Gemini
isdayoff.ru · production calendar
SMTP
Legend
  • HTTP
  • event
  • external
  • requests
  • store
Four application services and three infrastructure ones on one Docker Compose host; no application port is open to the internet — only Caddy.
03Numbers snapshot · 2026-10-27

In numbers

  • 155 HTTP endpoints
  • 901 tests
  • 29 data models
  • 22 releases in the in-app changelog

Also: 319 commits · 12 migration scripts from the old system · 20 permissions · 4 application services.

04Engineering decisions 4 decisions

What was hard

Decision 01

Safe read-only SQL for the AI assistant

The assistant has to answer questions like "how many orders did a manager close this quarter", and writing an endpoint per question is endless. So the OMS hands out a text description of its schema and executes the SQL the model writes — behind six layers: a filter for one SELECT without dangerous functions; the real boundary, a PostgreSQL READ ONLY transaction (SERIALIZABLE, DEFERRABLE) that rejects even a write hidden in a CTE; a 5-second statement timeout; an outer LIMIT so the driver does not materialise the whole result; a separate small connection pool so a stuck query cannot starve the application; and always ROLLBACK. Access only by a service key compared in constant time.

Decision 02

Field-level permissions with one source of truth

Who may edit a field depends on global permissions and on the role in that particular order — its manager, its designer, a senior sales manager. The matrix "field → predicate" lives in one backend module and is used twice: to check a PATCH and to compute the editable fields returned with the card. The front end does not duplicate the rules: it makes the listed fields editable and sends only the diff. Permissions are an enum with fixed ids seeded by an idempotent migration and cached in Redis. The "in progress" status cannot be set by hand — it follows the first payment or a credit mark, and every automatic transition is written to the history.

A-03.21440 × 900 · 390 × 844
staff · internal
OMS order card: tabs, fields, contacts and a discussion with a rich-text editor
OMS on a phone: the order card
Order card on desktop (tabs, fields, contacts, discussion with a rich-text editor) and on a phone.
Decision 03

Deadlines in working days

The planned ready date is the date of the actual first payment plus the promised working days by the Russian production calendar. The calendar is fetched from isdayoff.ru by a background task once a day under a distributed Redis lock (the backend runs four workers), with the network call made before the transaction is opened, and a year that is not yet approved is simply skipped.

A-03.31440 × 900
staff · internal
OMS production calendar overlay
Production calendar overlay.
Decision 04

Moving off the old PHP/MySQL system

Twelve scripts run in strict order — users, clients, contacts, suppliers, topics, orders and so on to purchasing — translating old ids into new ones through JSON maps and reporting every skipped row with a reason. Values people were used to were kept. Production has neither Python nor MySQL on the host, so MySQL is started once as a container in the application network, the scripts run inside the backend container, and the container is removed.

05Timeline 24.05 → 10.09.2026

How the project went

  1. 24.05.2026Prototype of catalog-photo generation as a separate local app — later moved into the OMS
  2. 30.05.2026First commit: authorisation, users, clients, base repositories; the same day — suppliers, topics, legal entities, order list, first migration scripts
  3. 01–02.06Payments in kopecks, production calendar and deadlines, order card with field permissions, change history, files and discussions, migration of history and files
  4. 05–07.06Purchasing module; statistics on finance, production, purchasing, managers and clients
  5. 10–12.06PDF print of the order card, automatic "in progress", CI/CD
  6. 15.06.2026Version 2.0.1 — first entry in the in-app changelog; staff start working in it. 2.0.2–2.0.17 follow within ten days from their feedback
  7. 22–23.06Service key and read-only SQL for the AI assistant; automatic letters to clients about pickup
  8. 08–10.09.2026Version 2.1: the Images module (image_service, RabbitMQ, pgvector), photo tab in the order, mobile layouts
06Screens 6 shots · 1440 × 900
07Integrations 7

What it talks to

  • Google Gemini
  • isdayoff.ru
  • SMTP
  • GitHub Actions
  • Caddy
  • Docker Compose
  • AI assistant (read-only SQL)
08Related the same factory