| 37694 |
amit |
1 |
-- Record where stock came from and what it cost, on the stock itself (2026-09-17)
|
|
|
2 |
--
|
|
|
3 |
-- WHY
|
|
|
4 |
-- Stock carried no record of its source, so every internal movement re-derived the origin by tracing serials
|
|
|
5 |
-- (impossible for non-serialised stock that had already moved) and then priced the move from that vendor's CURRENT
|
|
|
6 |
-- catalog TP. Standard practice is a receipt layer: each row of stock knows which outside vendor it came from and
|
|
|
7 |
-- what it cost when it arrived, and an internal movement moves it at that cost.
|
|
|
8 |
--
|
|
|
9 |
-- inventoryItem.origin_vendor_id - outside vendor the unit came from. NULL = genuinely unknown; never a guess
|
|
|
10 |
-- and never an internal supplier.
|
|
|
11 |
-- inventoryItem.receipt_unit_cost - unit price the stock was received at (warehouse.lineitem.unitPrice of the PO
|
|
|
12 |
-- line it arrived on). What an internal movement moves it at.
|
|
|
13 |
-- lineitem.origin_vendor_id - origin the PO line was priced from, so a receiving GRN can copy it onward.
|
|
|
14 |
--
|
|
|
15 |
-- ORDERING: run before deploying the code that writes these columns. Both ALTERs are ADD COLUMN, which MySQL 5.7 does
|
|
|
16 |
-- INPLACE and allows reads/writes throughout; inventoryItem is ~1.5M rows so expect a couple of minutes.
|
|
|
17 |
|
|
|
18 |
ALTER TABLE warehouse.inventoryItem
|
|
|
19 |
ADD COLUMN origin_vendor_id INT NULL
|
|
|
20 |
COMMENT 'outside vendor this stock came from; NULL = unknown, never an internal supplier',
|
|
|
21 |
ADD COLUMN receipt_unit_cost FLOAT NULL
|
|
|
22 |
COMMENT 'unit price this stock was received at; what an internal movement moves it at';
|
|
|
23 |
|
|
|
24 |
ALTER TABLE warehouse.lineitem
|
|
|
25 |
ADD COLUMN origin_vendor_id INT NULL
|
|
|
26 |
COMMENT 'origin vendor this PO line was priced from; copied onto stock at GRN';
|
|
|
27 |
|
|
|
28 |
-- Backfill, facts only --------------------------------------------------------------------------------------------
|
|
|
29 |
-- 1. Stock received directly on an outside vendor's PO: that vendor, and the price on its PO line.
|
|
|
30 |
UPDATE warehouse.inventoryItem ii
|
|
|
31 |
JOIN warehouse.purchase p ON p.id = ii.purchaseId
|
|
|
32 |
JOIN warehouse.purchaseorder po ON po.id = p.purchaseOrder_id
|
|
|
33 |
JOIN warehouse.supplier s ON s.id = po.supplierId AND s.internal = 0
|
|
|
34 |
LEFT JOIN warehouse.lineitem li ON li.purchaseOrder_id = po.id AND li.itemId = ii.itemId
|
|
|
35 |
SET ii.origin_vendor_id = po.supplierId,
|
|
|
36 |
ii.receipt_unit_cost = li.unitPrice
|
|
|
37 |
WHERE ii.origin_vendor_id IS NULL;
|
|
|
38 |
|
|
|
39 |
-- 2. Serialised stock that moved internally: the vendor of the most recent external purchase of that serial, and the
|
|
|
40 |
-- cost recorded against that purchase. Same rule billing uses to price a sale.
|
|
|
41 |
UPDATE warehouse.inventoryItem ii
|
|
|
42 |
JOIN (SELECT o.id,
|
|
|
43 |
(SELECT po2.supplierId FROM warehouse.inventoryItem o2
|
|
|
44 |
JOIN warehouse.purchase p2 ON p2.id = o2.purchaseId
|
|
|
45 |
JOIN warehouse.purchaseorder po2 ON po2.id = p2.purchaseOrder_id
|
|
|
46 |
JOIN warehouse.supplier s2 ON s2.id = po2.supplierId AND s2.internal = 0
|
|
|
47 |
WHERE o2.serialNumber = o.serialNumber AND o2.itemId = o.itemId
|
|
|
48 |
ORDER BY o2.created DESC, o2.id DESC LIMIT 1) AS origin_vendor_id,
|
|
|
49 |
(SELECT o3.receipt_unit_cost FROM warehouse.inventoryItem o3
|
|
|
50 |
JOIN warehouse.purchase p3 ON p3.id = o3.purchaseId
|
|
|
51 |
JOIN warehouse.purchaseorder po3 ON po3.id = p3.purchaseOrder_id
|
|
|
52 |
JOIN warehouse.supplier s3 ON s3.id = po3.supplierId AND s3.internal = 0
|
|
|
53 |
WHERE o3.serialNumber = o.serialNumber AND o3.itemId = o.itemId
|
|
|
54 |
ORDER BY o3.created DESC, o3.id DESC LIMIT 1) AS receipt_unit_cost
|
|
|
55 |
FROM warehouse.inventoryItem o
|
|
|
56 |
WHERE o.origin_vendor_id IS NULL AND o.serialNumber IS NOT NULL AND LENGTH(o.serialNumber) > 0
|
|
|
57 |
AND o.currentQuantity > 0) traced ON traced.id = ii.id
|
|
|
58 |
SET ii.origin_vendor_id = traced.origin_vendor_id,
|
|
|
59 |
ii.receipt_unit_cost = traced.receipt_unit_cost
|
|
|
60 |
WHERE traced.origin_vendor_id IS NOT NULL;
|
|
|
61 |
|
|
|
62 |
-- 3. Everything else keeps NULL: non-serialised stock that has already moved internally has no recoverable origin,
|
|
|
63 |
-- and a guess must never be written as a fact. Those units are priced from the latest externally approved catalog
|
|
|
64 |
-- price at movement time, as before.
|
|
|
65 |
|
|
|
66 |
-- What the backfill reached, for stock currently held.
|
|
|
67 |
SELECT COUNT(*) AS in_stock_rows,
|
|
|
68 |
SUM(origin_vendor_id IS NOT NULL) AS with_origin,
|
|
|
69 |
SUM(receipt_unit_cost IS NOT NULL) AS with_cost,
|
|
|
70 |
SUM(origin_vendor_id IS NULL) AS unknown_origin
|
|
|
71 |
FROM warehouse.inventoryItem WHERE currentQuantity > 0;
|