Skip to content
Anton GoncharovAI systems developer

Web app · Laravel · PostgreSQL

Warehouse system on Laravel

A sales manager mustn’t sell what’s already in someone else’s reservation, and a storekeeper shouldn’t search the whole warehouse for an item. Here there are 12 items across bins and 6 orders in different statuses: an order is reserved in full or not at all, the pick list is sorted by bin, purchasing sees what has dropped below the minimum, and stocktaking posts surpluses and shortages. On the “Control” page two processes reserve the last belt at the same time — with SELECT … FOR UPDATE and without — and reconciliation finds a stock view that was “fixed” by an UPDATE bypassing the ledger.

Scenario: Yacheyka components warehouse — fictional.

Demo screenshot: Warehouse system on Laravel

Open the demo

Real Laravel and PostgreSQL on the site’s server (the sklad schema). The warehouse is shared by all visitors; “Reset demo” restores the starting stock.

The app runs in your browser: nothing is sent to a server.

What the demo shows

  • An overview with KPIs and tasks by role: reservations for sales, picking for the storekeeper, what to order for purchasing

  • Stock by bin: on the shelf, reserved, available and a stock bar down to the minimum

  • A product card with movements: every ledger entry and the balance after it

  • Orders: all-or-nothing reservation with a list of shortages, a pick list, shipping, cancellation and returns

  • Receiving as a document in a single transaction and CSV import with a line-by-line error report

  • Stocktaking: actual vs. book, with the difference posted as a surplus or a write-off

  • A ledger with filters and CSV export for Excel

  • A race between two PHP processes with and without row locking, reconciliation and rebuilding the stock view from the ledger

How to try it

  1. Reserve an order

    Open ЗАК-1006: there aren’t enough sensors — the reservation fails for every line, and the response says what’s missing and how much.

  2. Be the storekeeper

    Pick ЗАК-1002 from the pick list and ship it — stock decreases and entries appear in the ledger.

  3. Run the race

    On “Control” — first with locking: the second process waits and is refused. Without it, both sell the same item.

  4. Break and reconcile

    “Break the stock view”, then “Reconcile” — the discrepancy is found; “Rebuild from ledger” fixes it.

No paid neural networks

The warehouse is shared by all visitors, and roles switch in the header without a login (the server still checks permissions). The race without locking runs on a separate demo counter, and the reservation from the locked race is released immediately — real stock doesn’t change. Import goes through the sync queue, without Redis. In a production project: sandboxes or tenants, Horizon and exchange with the accounting system.

The demo runs without OpenAI, YandexGPT or GigaChat API keys. Wherever a production system calls a model, there’s a local stub with the same interface — so the demo is free, always available and never makes things up.

What a production project adds

  • Sign-in and roles from SSO, a separate warehouse per company
  • Horizon: scheduled import and nightly reconciliation with alerts
  • Exchange with MoySklad / 1C and reservations from a CRM deal
  • A barcode scanner for picking and receiving

Stack

  • All demos by stack:
  • PHP
  • Laravel 12
  • PHP 8.4
  • PostgreSQL
  • Eloquent
  • Blade
  • PHPUnit

Based on

Books and university courses whose ideas are at work in this demo.

  • Book · Robert C. Martin

    The Clean Coder

    Warehouse accounting is covered by PHPUnit tests: the code can be changed without fear of breaking stock levels.

  • Book · Harvey Deitel, Paul Deitel, David Choffnes

    Operating Systems

    A race between two real PHP processes for the same stock in PostgreSQL — with a row lock and without: you can see why a shared resource needs mutual exclusion.

  • Book · James Martin

    Computer Data-Base Organization

    Stock is the sum of postings, and the stock view is rebuilt from the journal: how users see the data is separate from how it is stored.

  • Book · Bruce Lee

    Bruce Lee: Artist of Life

    I pick the stack for the task, not out of habit: warehouse accounting on Laravel and PostgreSQL, bots on Python, shops on WordPress.

  • University course · CMC MSU

    Databases

    Goods receipt as one transaction, all-or-nothing reservations and a row lock against stock races.

  • University course · CMC MSU

    Operating Systems

    A race between two PHP processes for a shared resource and the lock that prevents it.

Developer’s comment

Anton Goncharov

AI systems developer · Python

For business

  • The same item won’t be sold twice: the reservation holds it for the order, and two simultaneous requests won’t slip through.
  • An order is reserved in full or not at all — there’s never “half an order” stuck in reserve.
  • The picker follows the pick list by bin, purchasing sees when it’s time to reorder — everyone gets their own task screen.
  • Reconciliation catches manual edits that bypass the books, and recalculating from the journal fixes them.

For developers

  • Stock is mutated only through StockService: balance rows are locked with lockForUpdate in product_id order, so two orders don’t deadlock.
  • The race is two artisan sklad:race-worker processes via proc_open with a shared start moment; in SQLite tests the sides run one after the other.
  • Laravel 12, a sklad schema in PostgreSQL, Blade without a frontend build; PHPUnit for orders, import, reconciliation, roles and the race.

Tips

  • Tip 01

    Don’t decrement qty in a controller: one domain service, a journal of postings and a row lock.

  • Tip 02

    Multi-line documents go in one transaction with locks in a single order, otherwise you get deadlocks under load.

  • Tip 03

    Make reconciliation a report that can be run manually and on a schedule.

Want the same for your business?

Tell me about your task — I’ll tell you what from the demo fits, how long it will take and what I’ll need from you.

Other demos in this section

All demos
  • Web app · OCR · validating company details

    live

    Document recognition

    Upload an invoice or a certificate of completion — a photo, scan or PDF. The app recognizes the text, extracts company details and table rows and validates them: tax ID and account checksums, the total and VAT.

    • React
    • TypeScript
    • In-browser neural network
  • Web app · conversations · deals · analytics

    live

    Operator panel

    The workspace of an operator who receives difficult requests from a bot: a queue with a timer, a conversation summary, a reply draft, deals on a kanban board and analytics — on a live stream of customers.

    • React
    • TypeScript
  • Web app · Recharts · RBAC · audit

    live

    SaaS dashboard

    A SaaS revenue team’s workspace: MRR by plan, revenue movement, churn, customer health, CSV export for Excel and an activity log. All numbers are calculated from a single year of history — changing a plan in a customer card moves MRR on the charts immediately.

    • React
    • TypeScript
  • Web app · metrics · alerts

    live

    Bot monitoring

    How a bot is watched in production: how many requests, how fast it answers, how often it fails or doesn’t know the answer. A request log with a trace of every answer and rules that message the on-call engineer in Telegram when something goes wrong.

    • React
    • TypeScript
  • Python scraper · runs in the browser

    live

    Competitor price monitoring

    A Python scraper crawls the websites of three competitors of an electronics store every day: an HTML catalog with pagination, a sitemap with JSON-LD and an SPA with an API. It matches products with the catalog, sends notifications about changes, recommends a price by rules and exports a report to Excel. Python runs right in the browser.

    • Python
    • React
    • TypeScript
  • Web app · PWA · vanilla JS

    live

    Offline courier app

    A courier’s working day: orders, a call to the customer, handover with a photo and signature. The app opens and works without a network, and changes go to dispatch when the connection returns. If the dispatcher changed the same order in the meantime, the courier chooses which version is right.

    • Vanilla JS