I used to think building a POS was simple. One product, one price, one stock count. Done. Then I jumped into real projects and realized: the retail world is way more complicated than that.
My client said: “Hey, we sell bottled water by the piece, by the pack, and by the case. How do we handle that?” I answered casually: “Just create three separate products.” He shook his head. “But it’s the same inventory. If we sell one pack, the individual bottle count needs to decrease too.”
Oh. Turns out I needed to learn something called Multi-Unit.
And that was just the beginning. After that came Multi-Variant, Multi-Warehouse, Multi-Barcode, and all sorts of “Multi” concepts. Let me share my journey dealing with these.
Multi-Unit: One Product, Multiple Packages
The Problem I Faced
Client had bottled water. Sold in three packages:
- Individual (bottle): Rp 5.000
- Pack (6 bottles): Rp 25.000
- Case (48 bottles): Rp 180.000
Cashiers needed to sell in any package. But inventory was one thing: counted in individual bottles.
My challenge: How do I build a system that can sell various packages while keeping inventory accurate?
How I Solved It
I created a product_units table storing conversions:
CREATE TABLE product_units (
id INT PRIMARY KEY,
product_id INT,
unit_name VARCHAR(50), -- Individual, Pack, Case
conversion_value INT, -- Convert to smallest unit
price DECIMAL(15,2)
);
-- Data for Bottled Water:
-- Individual: conversion = 1 (base)
-- Pack: conversion = 6 (1 pack = 6 bottles)
-- Case: conversion = 48 (1 case = 48 bottles)
How it works:
- Stock stored in smallest unit (bottles)
- Cashier sells 2 packs? System deducts 12 bottles (2 × 6)
- Cashier sells 1 case? System deducts 48 bottles
Result: Stock always accurate, cashiers free to sell in any package.
What I Learned
Multi-Unit is super useful for products sold in various packages. But there are important points:
- Always have a base unit (smallest unit for storing inventory)
- Price per package can differ (buying a case is cheaper per bottle)
- Each package can have its own barcode
Multi-Variant: The Clothing Store Story
New Problem Emerged
My second client ran a clothing store. One t-shirt model had multiple choices:
- Sizes: S, M, L, XL
- Colors: Red, Blue, Black, White
“How do we handle inventory? Red size M and Blue size L have different stock counts, right?”
Ah, this is different from Multi-Unit!
Multi-Unit vs Multi-Variant
I was confused at first. What’s the difference?
| Multi-Unit | Multi-Variant |
|---|---|
| Exact same product | Product with different attributes |
| Combined inventory | Separate inventory per combination |
| Example: bottle, pack, case | Example: colors, sizes, flavors |
Multi-Unit Example:
Bottled Water (stock: 480 bottles)
├─ Sell individual: stock -1
├─ Sell pack: stock -6
└─ Sell case: stock -48
Multi-Variant Example:
Plain T-Shirt
├─ Red S (stock: 20)
├─ Red M (stock: 35)
├─ Blue L (stock: 15)
└─ Black XL (stock: 10)
Sell Red S: only Red S stock decreases
How I Solved It
I built a hierarchical variant system:
-- Attributes table (Color, Size)
CREATE TABLE variant_attributes (
id INT PRIMARY KEY,
name VARCHAR(50) -- Color, Size
);
-- Values table (Red, Blue, or S, M, L)
CREATE TABLE variant_values (
id INT PRIMARY KEY,
attribute_id INT,
value VARCHAR(100) -- Red, Blue, or S, M, L
);
-- Product combinations table
CREATE TABLE product_variants (
id INT PRIMARY KEY,
parent_product_id INT,
sku VARCHAR(50), -- TS-RED-M (T-Shirt Red M)
variant_name VARCHAR(255),
price DECIMAL(15,2),
stock INT -- Separate inventory!
);
Each color-size combination becomes a separate product with its own inventory.
What I Learned
Mistake I made: combining Multi-Unit and Multi-Variant in one table. Disaster. Inventory went haywire.
Lesson: Keep Multi-Unit and Multi-Variant separate. They’re different concepts.
Multi-Warehouse: The Branch Store Drama
Problem I Didn’t Anticipate
Third client: “We have 5 branches. Inventory needs to be separate per branch.”
I thought just add a warehouse_id column to the stock table. But turns out…
It was more complex:
- Customer calls branch A asking about stock. Sold out. But branch B has it.
- Branch A wants to borrow stock from branch B (inter-branch transfer)
- Manager wants to see inventory reports across all branches
How I Solved It
I created inventory tables per warehouse:
CREATE TABLE warehouses (
id INT PRIMARY KEY,
code VARCHAR(20),
name VARCHAR(100), -- Main Warehouse, Branch A, Branch B
type ENUM('warehouse', 'store')
);
CREATE TABLE product_stock (
id INT PRIMARY KEY,
product_id INT,
warehouse_id INT,
quantity INT,
min_stock INT, -- Minimum threshold for alerts
UNIQUE (product_id, warehouse_id)
);
Features I added:
- Check stock availability at other branches
- Inter-branch stock transfers with documentation
- Inventory reports per location
- Alerts when stock at specific branches runs low
What I Learned
Multi-Warehouse dramatically increases complexity. But it’s essential for businesses with multiple locations.
My tip: Set one warehouse as the “Main Warehouse” to simplify management.
Multi-Barcode: Surprise from Imported Products
The Unexpected Story
One day a cashier complained: “This product has two barcodes. One from the foreign manufacturer, one from the local distributor. But the system only accepts one barcode.”
Oops. Didn’t think about this.
Why Multi-Barcode is Needed?
Turns out there are many cases:
- Imported products have international barcode + local barcode
- Repackaged products get new barcodes
- Different packages have different barcodes (individual vs pack)
How I Solved It
CREATE TABLE product_barcodes (
id INT PRIMARY KEY,
product_id INT,
product_unit_id INT, -- If barcode is specific to a package
barcode VARCHAR(100) UNIQUE,
is_primary BOOLEAN -- Main barcode
);
Now one product can have multiple barcodes. Cashier scans any barcode, system still knows which product it is.
What I Learned
Don’t underestimate barcodes. In practice, one product can have 2-3 different barcodes.
Multi-Supplier: Learning from Mistakes
My Mistake
Initially, I only stored one supplier per product. Then the client said: “We buy sugar from three suppliers. Prices are different. How do we compare?”
I had to restructure the database. What a pain.
Should’ve Done This from the Start
CREATE TABLE product_suppliers (
id INT PRIMARY KEY,
product_id INT,
supplier_id INT,
supplier_sku VARCHAR(100), -- Product code at supplier
purchase_price DECIMAL(15,2),
min_order_qty INT,
lead_time_days INT, -- Delivery wait time
is_preferred BOOLEAN -- Primary supplier
);
Benefits:
- Compare prices across suppliers
- Backup supplier if primary is out of stock
- Track supplier performance (fastest delivery, cheapest price)
What I Learned
Don’t wait for requests. Think ahead: could this product be purchased from multiple suppliers?
Multi-Payment: Customer Wants to Split Payment
What Happened at the Store
Customer: “Can I pay half cash, half debit card?”
Cashier: “Hey developer, the system can’t do that…”
Me: ”…” (thinking: oops, forgot to build this feature)
How I Fixed It
CREATE TABLE transaction_payments (
id INT PRIMARY KEY,
transaction_id INT,
payment_method_id INT, -- Cash, Card, QR, etc
amount DECIMAL(15,2),
reference_no VARCHAR(100) -- Reference number for card/transfer
);
Now one transaction can use multiple payment methods:
Total Purchase: Rp 5.000.000
├─ Cash: Rp 3.000.000
├─ Debit Card: Rp 1.500.000
└─ QR Payment: Rp 500.000
What I Learned
Split payment is more common than I thought. Many customers want to pay part cash, part cashless.
Implementation Priority: What to Build First?
From my experience, here’s the order:
Must-Have (Definitely Need)
- Multi-Unit - almost every business needs this
- Multi-Variant - especially for fashion, shoes, electronics
- Multi-Warehouse - if you have more than one location
- Multi-Barcode - makes scanning easier
Should-Have (Really Useful)
- Multi-Supplier - for price comparison
- Multi-Payment - customers love flexibility
- Multi-Tax - depends on business regulations
Nice-to-Have (Extra Features)
- Multi-Bundle (Product Packages)
- Multi-Category (Multiple Categories)
- Multi-Currency - only for international business
Mistakes I’ve Made
So you don’t repeat my mistakes:
- Combining Multi-Unit and Multi-Variant in one table (huge mistake).
- Not separating inventory by location from the start.
- Forgetting to validate negative stock.
- Not logging stock changes history.
My Conclusions
Building a POS with various “Multi” concepts taught me a lot:
- Don’t assume it’s simple - what looks easy can be complex.
- Listen to users - they know the real problems.
- Separate concepts clearly - Multi-Unit ≠ Multi-Variant.
- Think about scalability - plan for growth early.
- Log everything - essential for debugging.
If you’re building a POS system, I hope my stories help. Don’t be afraid to make mistakes, but learn from others’ experiences so you don’t fall too much.
Happy coding!