Lots & Expirations
A lot is the batch identity of stock: which production run or delivery a unit came from. An expiration is when that batch stops being usable. Together they answer the two questions cost alone cannot: which units are these, and how long are they good for. Pharus tracks both across every movement, every location, and every module, yet they are typed in exactly once.
Entered once, at the door
Lot and expiration enter the system on receipt lines, the moment stock physically arrives:
| Where | What the line carries |
|---|---|
| Purchase order receipt | The vendor's lot and its expiration |
| Work order receipt | The lot and expiration of the goods produced |
| Production order receipt | The lot and expiration of the goods produced |
That receipt line is the only place the two values are stored. They are stamped onto the arrival's ledger entry, and both stay editable in place on the receipt: correct a lot number or an expiration and the arrival entry updates with it, no counter entries, no rebooking.
Everything else you will ever see about a lot, on a shipment, a transfer, a consumption, a position, is derived from those arrivals on read.
Derived everywhere else
When stock leaves a location (a job consumes it, a sale ships it, a transfer moves it out), Pharus does not ask you which lot it was. It replays that item's ledger at that location and lets the organization's costing model decide which arrivals the outflow drew from: FIFO draws the oldest first, FEFO draws the soonest-expiring first, and so on. The lots the draw touched are the lots that left.
The same replay answers every downstream question:
- A consumption knows which lots it used, because the draw took them.
- A transfer arrival inherits exactly the lots its shipment drew at the source, each with its own expiration and cost, so identity survives the move intact.
- A position knows which lots remain on hand, because they are whatever the draws have not taken yet.
Because attribution is derived, it is also self-correcting. Fix an upstream mistake, a receipt's quantity, its lot, its expiration, or a date, and every downstream attribution recomputes from the corrected history the next time it is read. There is no stored copy to go stale, and nothing to re-enter.
What is stored versus what you see
The ledger stores one row per movement. A consumption of 100 units is one entry; a transfer is one entry out and one entry in. That row is the auditable fact: what moved, where, when.
But one movement can touch several lots. Draw 100 units off a shelf holding 40 of lot B and 60 of lot A and the single consumption spans two batches, and while its cost can blend into one number, its identity cannot: there is no average lot, and no average expiration.
So the read layer splits. Wherever lot detail matters, the API and the ledger tables present that one stored movement as one line per lot:
| What you see | Quantity | Lot | Expiration | Unit cost |
|---|---|---|---|---|
| CONSUME | 40 | B | 2027-03-01 | 12.00 |
| CONSUME | 60 | A | 2027-09-01 | 10.00 |
Both lines are views of the same single ledger row. Nothing was split in storage, and nothing was duplicated; the decomposition is recomputed on every read, exactly like cost. A movement that stayed within one lot shows as one line, unchanged.
Why FEFO depends on this
FEFO (first expired, first out) is only as good as the expirations it can see. Because expiration travels with each lot through every consumption and every transfer, FEFO at any location orders the true dates, even for stock that arrived from three warehouses away. Move goods around as much as the business needs; the soonest-expiring batch is still the first one drawn wherever it ends up.
When history cannot answer
If a movement asks for more than its source history can supply (say a transfer's quantity was edited above what the source ever held), the shortfall arrives with no lot, no expiration, and zero cost. That is deliberate: Pharus never invents an identity. A no-lot line at zero cost is a signal that the upstream history needs correcting, and once it is corrected, the line heals on its own.
See also
- Costing Model for how the same replay derives cost.
- Materials Inventory and Finished Goods Inventory for the ledgers themselves.
- Inventory Transfers for what travels with a move.