HandbookOperations34

School store.

Every school sells things — uniforms, exercise books, a branded hoodie, a replacement ID lanyard. Most run that trade out of a cash tin and a notebook. YESS gives the school store a real catalogue with stock, an order desk with a fulfilment state machine, returns, and a ledger that books each sale to the right account the moment it is delivered. The same gold finance spine the bursary runs on now reaches the shop counter.

  • 5store screens
  • 8data tables
  • 6fulfilment states
  • ≈12 minto read
The store hub: active products, 30-day revenue, pending fulfilment and low stock on one gold KPI rail, with the four workspaces beneath.

Prologue

A shop, not a cash tin#

The school store is the most under-managed money flow in most schools. Uniforms are sold from a storeroom; the takings go into a drawer; stock is counted once a term, if ever; and nobody can say at the end of the year whether the shop made money or quietly lost it.

YESS treats the store as exactly what it is — a small retail operation that belongs on the school's books. It has a catalogue with real stock levels, an order that moves through a defined lifecycle, a return that the desk decides on, and an automatic journal entry that recognises revenue and the cost of goods sold at the moment of delivery. Eight tables under the hood, five screens on top, and one number the owner can finally trust: shop margin.

A real store with a real ledger — the cash tin closed for good.

Chapter one

A morning at the counter#

It is the first week of term at Greenwood. Follow the store manager through one morning and the rest of the book is detail.

07:50. She opens the store hub at /dashboard/shop. The gold KPI rail reads at a glance: active products, revenue in the last 30 days, pending fulfilment, and low stock. Two items are flagged low — the Junior blazer (size 32) and exercise books — so before anything else she opens /dashboard/shop/products and updates their stock from the morning's delivery.

08:30. A parent pays cash for a senior blazer and a PE kit. The order is recorded at the till, lands in the desk as paid, and the manager opens /dashboard/shop/orders, finds it, and walks it forward: → preparing while she bags it, then → ready, then → delivered when the parent collects. On that last click the system decrements stock and — because the order is both paid and delivered — posts the sale to the general ledger automatically.

10:15. The blazer is the wrong size. The parent comes back; a return is logged against the order and arrives in the manager's queue at /dashboard/shop/returns. She reviews the reason, approves the return, and records the refund amount and method.

16:00. The accountant closes the cashier shift. The cash the store took today is already in the Z-report at /dashboard/finance/cashier-shifts — the shop's cash sales were folded into the till's expected takings, so the count either reconciles or surfaces a variance. No separate tally, no spreadsheet.

Chapter two

Building the catalogue#

Before the store can sell a thing it needs a catalogue. That is two screens: categories that organise the shelf, and products that sit on it.

Categories

Open /dashboard/shop/categories. A category carries a name, a slug (auto-derived from the name if you leave it blank), an optional parent category — so you can nest Uniforms → Blazers — a sort order, an icon key, and an active flag. The vital strip counts total, active, top-level, and how many products are still uncategorised. Categories are soft structure: a product can live without one, but a tidy shelf sells better.

Products

Open /dashboard/shop/products and click Add product. The form captures the name, an SKU (unique per school), a description, a category, the selling price, the cost price (this is what drives cost-of-goods-sold on the ledger), a tax rate, the opening stock, a low-stock threshold, a visibility, and an is-available switch. The vital strip shows the product count, total stock value (stock × price), low-stock items, and how many are hidden.

Visibility has four levels — public, school_only, staff_only and hidden — so a staff-discount item or an internal-only product never appears on the parent storefront. Deleting a product is a soft delete: the row is stamped with a deletion time rather than removed, because past orders reference it and the books must always be able to name what was sold.

Chapter three

The order desk#

An order at the store carries two independent statuses, and learning to read them is the whole job of the desk at /dashboard/shop/orders.

Payment status answers "has the school been paid?" — one of pending, paid, partial, refunded, failed, or cancelled. Fulfilment status answers "has the goods left the shelf?" — one of pending, preparing, ready, delivered, returned, or cancelled. A blazer paid for online but not yet collected is paid + pending; the same blazer once bagged and handed over is paid + delivered.

The desk is a set of one-click transitions that walk the order forward: Mark paid (pending → paid), → preparing, → ready, → delivered. Each row shows the customer — a linked student where the order is for a child, otherwise the typed customer name — and a customer type of student, parent, staff, or external. A detail dialog opens the line items: the product-name snapshot, quantity, unit price and any variant key, all frozen at the moment of sale so a later price change never rewrites history.

Two things happen automatically as the status moves. When an order becomes paid, its stock is reserved so it can't be oversold while it waits. When it becomes delivered, the reservation is released and stock is decremented — and if the product is linked to an inventory item, that operational stock decrements too, with a stock_out transaction written against the shop order.

Chapter four

How a sale hits the books#

This is the chapter that separates the YESS store from a cash tin. The moment an order is both paid and delivered, a balanced journal entry posts to the general ledger automatically — through a database trigger, not application code — and the store's books are never out of step with its sales.

The entry is built from the order's own numbers. The cash account is chosen by payment method (1100 Cash for cash, the bank account otherwise) and debited the total. Shop revenue account 4600 is credited the net of subtotal and discount. If there is tax, Sales Tax Payable 2200 takes its share. And because each line item froze its unit cost at sale time, the system also posts cost of goods sold — debit Shop COGS 5600, credit Inventory–Shop 1310 — so the margin on every blazer is on the books, not in someone's head.

The posting is idempotent: the journal-entry id is written back onto the order, and the trigger refuses to post twice. You can read every shop entry at /dashboard/accounting/journal and the accounts themselves at /dashboard/accounting/chart. Wallet-paid orders are the deliberate exception — they recognise revenue through the wallet ledger instead, so the school never double-counts the same sale.

Cash sales also flow into the cashier's day. The Z-report at /dashboard/finance/cashier-shifts folds cash-method shop orders into the till's expected takings, so the counter's declared cash reconciles against everything it actually sold — fees, canteen, and shop together.

Chapter five

Two ways to buy#

A family can reach the store in two ways, and it is worth being clear about how they differ today.

At the counter (the order desk). A cash or wallet sale becomes a real shop order with line items, stock reservation, the fulfilment state machine, and — on delivery — the journal entry described above. This is the path the four store screens manage end to end.

From the parent portal. Parents browse the same catalogue at /portal/shop and check out online. Today that checkout takes a deliberately simpler route: it raises a charge invoice per category that the parent pays through the normal online-payment flow, reusing the fees machinery rather than creating a shop order. It is an honest way to let families pay from home — but it does not yet reserve shop stock or appear on the Orders desk. Unifying the two paths is on the roadmap.

Chapter six

Returns and refunds#

A return is logged against an order and its line item, with a reason, and lands in the queue at /dashboard/shop/returns. The vital strip surfaces what needs attention: returns awaiting a decision, approved-but-unpaid refunds, and the count and value refunded in the last 30 days.

The manager's decision walks a four-state lifecycle — requestedapprovedrefunded, or rejected. Approve records the refund amount; Refund records the amount and method (cash, mobile_money, card, wallet, or a credit_note); Reject records a reason. Every decision stamps who processed it and when.

Chapter seven

Stock and the storeroom#

Every product carries its own stock count, a reserved-stock figure, and a low-stock threshold. The hub and the products screen both surface low stock so the manager reorders before a shelf runs dry. Reservation on payment and decrement on delivery keep the count honest through the order lifecycle.

A product can also be linked to an inventory item — the operational asset register the school already keeps. When a linked product is delivered, the inventory item's stock decrements in the same breath and a stock_out transaction is written referencing the shop order. The store and the storeroom stay in agreement.

Products support variants — sizes, colours — in the data model, and the order and ledger machinery honours them. The on-screen variant editor is still to come, so for now variant stock is set up by import; the chapter on what comes next says so plainly.

Chapter eight

What makes the store elite#

Five things that make this elite

  1. 1

    Revenue and COGS by trigger

    A paid-and-delivered order posts a balanced journal entry automatically — debit cash, credit Shop Revenue 4600, and debit Shop COGS 5600 / credit Inventory–Shop 1310 from the cost frozen on each line. The posting is idempotent, guarded by the order's journal-entry id. Shop margin is on the books, not in a notebook.

  2. 2

    Two-axis order state

    Payment status and fulfilment status are separate, because 'paid' and 'delivered' are genuinely different questions. The desk reads both at a glance and a paid-but-uncollected order can never be confused with a finished one.

  3. 3

    Stock that can't be oversold

    Stock reserves on payment and decrements on delivery. Linked inventory items decrement in lockstep with a stock_out transaction, so the store and the school's asset register never drift apart.

  4. 4

    History that never rewrites

    Every line item snapshots the product name, SKU, unit price and unit cost at sale time, and products soft-delete rather than vanish. A price change next term cannot alter what last term's receipt says.

  5. 5

    One till, every counter

    Cash shop sales fold into the same cashier-shift Z-report as fees and the canteen, so the counter reconciles its declared cash against everything it sold that day — not the shop in one place and the canteen in another.

Planned features

What comes next#

The store ships a real catalogue, a real order lifecycle, and a real ledger. The features below are built in part or scaffolded in the database, and are honestly not yet wired end to end. They appear here so you can plan around them.

Five features coming next

  1. 1

    A checkout that creates an order

    The atomic, oversell-safe shop_place_order routine — which locks stock, prices the basket server-side, and writes the order and its line items in one transaction — exists in the database but is not yet called by any screen. Wiring it gives staff a real 'new order' button on the desk.

  2. 2

    One storefront, one order

    Today the parent portal checkout raises a per-category invoice rather than a shop order, so online sales bypass stock reservation and the Orders desk. The planned upgrade routes the storefront through shop_place_order so a portal purchase is a first-class order like a counter sale.

  3. 3

    Returns that move money and stock

    Returns record the decision accurately but do not yet auto-restock the shelf, reverse the ledger, or credit the wallet. The planned chain ties an approved refund to a reversing journal entry, a stock increment, and the chosen refund channel.

  4. 4

    On-screen variant editor

    Variants are honoured by the order and ledger machinery, but there is no form to add sizes and colours yet — they are set by import. The planned editor brings variant stock and pricing into the product screen.

  5. 5

    Delivery proof and signatures

    The schema carries a delivery-event log with signed-by name and signature image, intended for classroom-drop and collection proof. No screen writes or reads it yet; the planned fulfilment view captures the signature on hand-over.

Adjacent modules

Where the store connects#

Tutorial

Do it step by step#

Stand up the store and run a sale end to end — catalogue, counter, books, and the till. Six steps, in the order the shop actually works.

  1. 1

    Organise the shelf

    Open /dashboard/shop/categories and create the categories the store sells under — Uniforms, Books, Supplies — nesting where it helps (Uniforms → Blazers). Each carries a name, an auto-slug, an optional parent, a sort order and an active flag.

    A product can live without a category, but a tidy shelf is easier to browse and easier to report on.

  2. 2

    Stock the catalogue

    Open /dashboard/shop/products, click Add product, and fill in the name, SKU, price, cost price, tax rate, opening stock, low-stock threshold and visibility. Mark it available when it is ready to sell.

    Set the cost price honestly — it is what makes Shop COGS, and therefore shop margin, a real number rather than a guess.

  3. 3

    Read the order's two statuses

    Open the desk at /dashboard/shop/orders. Each order shows a payment status (has the school been paid?) and a fulfilment status (has the goods left the shelf?). Open a row to see its line items, snapshotted at the moment of sale.
  4. 4

    Walk the order to delivered

    Advance the order with the one-click transitions — Mark paid, → preparing, → ready, → delivered. Stock reserves on paid and decrements on delivered; on delivery the sale posts to the ledger automatically.

    Delivered is the trigger that matters — it is the click that books revenue and cost of goods sold.

  5. 5

    Decide on a return

    When goods come back, work the queue at /dashboard/shop/returns: approve with a refund amount, refund with an amount and method, or reject with a reason. Each decision is stamped with who and when.
  6. 6

    Close the till and read the books

    At end of day the store's cash is already in the Z-report at /dashboard/finance/cashier-shifts. Every delivered sale is a journal entry you can read at /dashboard/accounting/journal — revenue, tax and cost of goods, all on the books.

Catalogue stocked, orders walked to delivered, returns decided, the till reconciled — the school store closed the day on a balanced ledger.