Skip to content

PRD: Vendors

ModuleInventoryPRD IDPRD-VEN-001
StatusSupersededFEATVEN
EpicPlane
Date2026-07-13Versionv1.0
Packages@nx/inventoryURDVEN
SurfaceClient · Owner/MgrBO · Ops
OwnerPhá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 happensPrice suggested on the lineVendor A's last purchase priceFresh milk stock
Link Fresh milk to A, enter tiers: 50,000d from 10 cases, 46,000d from 50 cases-none yet0 cartons
Raise a purchase order for 12 cases from A50,000d (the 10-case tier)none yet0 cartons
Receive the goods, the real price is 55,000d-55,000d288 cartons
Raise a purchase order for 5 cases from A, no tier matches55,000d (last purchase price)55,000d288 cartons
Raise a purchase order for 2 cases from B, the item is not linked to Bblank, the merchant types it55,000d288 cartons
Receive B's goods-55,000d312 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.

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.