Blame | Last modification | View Log | RSS feed
-- Record where stock came from and what it cost, on the stock itself (2026-09-17)---- WHY-- Stock carried no record of its source, so every internal movement re-derived the origin by tracing serials-- (impossible for non-serialised stock that had already moved) and then priced the move from that vendor's CURRENT-- catalog TP. Standard practice is a receipt layer: each row of stock knows which outside vendor it came from and-- what it cost when it arrived, and an internal movement moves it at that cost.---- inventoryItem.origin_vendor_id - outside vendor the unit came from. NULL = genuinely unknown; never a guess-- and never an internal supplier.-- inventoryItem.receipt_unit_cost - unit price the stock was received at (warehouse.lineitem.unitPrice of the PO-- line it arrived on). What an internal movement moves it at.-- lineitem.origin_vendor_id - origin the PO line was priced from, so a receiving GRN can copy it onward.---- ORDERING: run before deploying the code that writes these columns. Both ALTERs are ADD COLUMN, which MySQL 5.7 does-- INPLACE and allows reads/writes throughout; inventoryItem is ~1.5M rows so expect a couple of minutes.ALTER TABLE warehouse.inventoryItemADD COLUMN origin_vendor_id INT NULLCOMMENT 'outside vendor this stock came from; NULL = unknown, never an internal supplier',ADD COLUMN receipt_unit_cost FLOAT NULLCOMMENT 'unit price this stock was received at; what an internal movement moves it at';ALTER TABLE warehouse.lineitemADD COLUMN origin_vendor_id INT NULLCOMMENT 'origin vendor this PO line was priced from; copied onto stock at GRN';-- Backfill, facts only ---------------------------------------------------------------------------------------------- 1. Stock received directly on an outside vendor's PO: that vendor, and the price on its PO line.UPDATE warehouse.inventoryItem iiJOIN warehouse.purchase p ON p.id = ii.purchaseIdJOIN warehouse.purchaseorder po ON po.id = p.purchaseOrder_idJOIN warehouse.supplier s ON s.id = po.supplierId AND s.internal = 0LEFT JOIN warehouse.lineitem li ON li.purchaseOrder_id = po.id AND li.itemId = ii.itemIdSET ii.origin_vendor_id = po.supplierId,ii.receipt_unit_cost = li.unitPriceWHERE ii.origin_vendor_id IS NULL;-- 2. Serialised stock that moved internally: the vendor of the most recent external purchase of that serial, and the-- cost recorded against that purchase. Same rule billing uses to price a sale.UPDATE warehouse.inventoryItem iiJOIN (SELECT o.id,(SELECT po2.supplierId FROM warehouse.inventoryItem o2JOIN warehouse.purchase p2 ON p2.id = o2.purchaseIdJOIN warehouse.purchaseorder po2 ON po2.id = p2.purchaseOrder_idJOIN warehouse.supplier s2 ON s2.id = po2.supplierId AND s2.internal = 0WHERE o2.serialNumber = o.serialNumber AND o2.itemId = o.itemIdORDER BY o2.created DESC, o2.id DESC LIMIT 1) AS origin_vendor_id,(SELECT o3.receipt_unit_cost FROM warehouse.inventoryItem o3JOIN warehouse.purchase p3 ON p3.id = o3.purchaseIdJOIN warehouse.purchaseorder po3 ON po3.id = p3.purchaseOrder_idJOIN warehouse.supplier s3 ON s3.id = po3.supplierId AND s3.internal = 0WHERE o3.serialNumber = o.serialNumber AND o3.itemId = o.itemIdORDER BY o3.created DESC, o3.id DESC LIMIT 1) AS receipt_unit_costFROM warehouse.inventoryItem oWHERE o.origin_vendor_id IS NULL AND o.serialNumber IS NOT NULL AND LENGTH(o.serialNumber) > 0AND o.currentQuantity > 0) traced ON traced.id = ii.idSET ii.origin_vendor_id = traced.origin_vendor_id,ii.receipt_unit_cost = traced.receipt_unit_costWHERE traced.origin_vendor_id IS NOT NULL;-- 3. Everything else keeps NULL: non-serialised stock that has already moved internally has no recoverable origin,-- and a guess must never be written as a fact. Those units are priced from the latest externally approved catalog-- price at movement time, as before.-- What the backfill reached, for stock currently held.SELECT COUNT(*) AS in_stock_rows,SUM(origin_vendor_id IS NOT NULL) AS with_origin,SUM(receipt_unit_cost IS NOT NULL) AS with_cost,SUM(origin_vendor_id IS NULL) AS unknown_originFROM warehouse.inventoryItem WHERE currentQuantity > 0;