PRD: Vendors
| Module | Inventory | PRD ID | PRD-VEN-001 |
| Status | Superseded | FEAT | VEN |
| Epic | — | Plane | — |
| Date | 2026-07-13 | Version | v1.0 |
| Packages | @nx/inventory | URD | VEN |
| Surface | Client · Owner/MgrBO · Ops | ||
| Owner | Phát Nguyễn | ||
What a vendor is
A merchant buys from many places. A vendor is the profile of one party selling to that merchant: name, tax code, address, transaction currency, and a list of contacts.
Knowing a vendor's name is not enough to raise a correct purchase order. So every vendor - item pair is its own record, carrying all the purchasing data for that item at that particular vendor: the ordering unit, the minimum order quantity, the lead time, the price tiers, the vendor's own item code, and the last purchase price.
The conversion factor lives on the item, not on the vendor
Quoted from Units of Measure & Conversion so nobody has to open another PRD. Every item has a base unit - the smallest unit it is counted in, and all stock is always held in that unit. A conversion factor ("1 case = 24 cartons") is a fixed property of a unit on that item.
A vendor row carries no factor of its own. It only points at a unit already declared on the item. That is what makes it impossible for two vendors to disagree about how much a case holds.
Why the purchase price is not on the item
The same case of milk carries a different price, a different minimum order quantity, and a different lead time at every vendor. At a single vendor, every item has its own packaging. With nowhere to store those numbers, the owner has to hold them in their head or scroll back through Zalo messages every time they order, and a new employee simply cannot place an order at all.
Purchase price is also not sale price. What the customer pays lives in the Pricing module and travels with the product variant. What the merchant pays travels with the vendor - item pair, and the same item bought in two places has two prices. Mixing the two into one field is the root of every wrong margin number.
One item, two vendors, end to end
The material "Fresh milk", base unit carton. Vendor A sells by the Case of 24, vendor B by the Case of 12.
| What happens | Price suggested on the line | Vendor A's last purchase price | Fresh milk stock |
|---|---|---|---|
| Link Fresh milk to A, enter tiers: 50,000d from 10 cases, 46,000d from 50 cases | - | none yet | 0 cartons |
| Raise a purchase order for 12 cases from A | 50,000d (the 10-case tier) | none yet | 0 cartons |
| Receive the goods, the real price is 55,000d | - | 55,000d | 288 cartons |
| Raise a purchase order for 5 cases from A, no tier matches | 55,000d (last purchase price) | 55,000d | 288 cartons |
| Raise a purchase order for 2 cases from B, the item is not linked to B | blank, the merchant types it | 55,000d | 288 cartons |
| Receive B's goods | - | 55,000d | 312 cartons |
- 12 cases from A produce 288 cartons (12 x 24). 2 cases from B produce 24 cartons (2 x 12). The factor comes from the unit on the item, never from the vendor row.
- The last purchase price is recorded at receipt, not at order. Order at 50,000d and receive at 55,000d, and the number stored is 55,000d, because that is the money actually paid.
- A tier wins when the quantity being ordered matches one. When no tier matches, the last purchase price is used.
- A's record and B's record exist side by side, each holding its own price.
The easiest thing to get wrong
A vendor with a different pack size gets a new unit, not an edited factor
Vendor B sells cases of 12 cartons while the item's Case is 24. The right move is to declare a new Case of 12 unit on the item, then point vendor B's row at that unit.
Editing the Case factor from 24 down to 12 rewrites history: every past receipt from A changes meaning. The factor is locked the moment an item has any stock movement.
The consequence: "5 cases from this vendor" always resolves to exactly one number, including during a stock count or a transfer, when nobody remembers which vendor the goods came from.