The Core Theory: Product & Inventory Taxonomy
The Core Theory: Product & Inventory Taxonomy
The first thing professional systems do is separate two distinct concerns:
"What is the product?" vs. "How do I track its stock?"
These are answered by two different data models that professional ERPs keep separate.
Step 1: Understand Product Categories (What Am I Selling?)
Professional systems classify all sellable things into a handful of fundamental archetypes:
| Archetype | Examples | Key Trait |
|---|---|---|
| Storable Product | Laptop, Keyboard, Shirt | Has a physical count; tracked individually |
| Consumable | Paper, Pens, Cleaning supplies | Has a count, but not individually tracked (you don't care which pen was sold) |
| Perishable | Fruits, Vegetables, Dairy, Meat | Has a count AND an expiry date; FIFO is critical |
| Service | Haircut, Plumbing, Tutoring | No stock; just a time/slot. Zero inventory concept |
| Digital | E-Books, Software Keys | No physical stock; unlimited (or license-counted) |
| Bundled / Kit | Computer Set (CPU + Monitor + Keyboard) | A virtual product composed of other real products |
Your database's products table has a column like product_type: enum('storable', 'consumable', 'perishable', 'service', 'digital', 'bundle'). This single field drives almost all the business logic downstream.
Step 2: The Variant Problem
A "T-Shirt" is not really one product. It is a Product Template with Variants.
Product Template: Nike T-Shirt
├── Attribute: Size → [S, M, L, XL]
└── Attribute: Color → [Red, Blue, Black]
This generates 4 × 3 = 12 Product Variants, each with its own SKU.
Each variant has its OWN stock count.
This is how Odoo, Shopify, WooCommerce, and ERPNext all handle it. The key insight:
- The template holds name, description, price range, images, and attributes.
- Each variant holds its own SKU, price offset, stock level, and barcode.
Step 3: The Tracking Level Decision (Your Biggest Confusion)
This is where the magic happens. Not all products need the same depth of tracking. Professional systems let you choose per-product:
Level 0: No Tracking (Services, Digital)
Haircut, Delivery Fee, Photography Session
Stock count = NULL / ∞
Logic: Never go out of stock. Just "is the vendor available?"
Level 1: Quantity Tracking (Basic Count)
Notebooks, Bolts, Noodles packets
Stock count = INTEGER (e.g., 500 packs in store)
Logic: Decrement by 1 per sale. Alert when below threshold.
This is the correct approach for grocery stores — you track packets of noodles (e.g., 500 packs of Wai Wai), not individual noodles inside.
Level 2: Lot / Batch Tracking (Perishables)
Milk, Yogurt, Vegetables, Medicines
Stock is grouped by BATCH / LOT (e.g., "Batch #A2025-06-15")
Each batch has a manufacture date and expiry date.
Logic: Always sell the oldest batch first (FIFO). Alert when nearing expiry.
A fruit vendor doesn't track individual apples — they track crates (lots). "I received 5 crates today, expiry 3 days."
Level 3: Serial Number Tracking (High-Value Electronics)
Laptops, Phones, AC Units, TVs
Each physical unit has a unique Serial Number (e.g., SN: APPLE-MB-20250101-0042)
Logic: You know EXACTLY which unit was sold to whom. Critical for warranty.
Step 4: Your Exact Question — How Do Grocery Stores Work?
You are 100% right that nobody tracks individual biscuits. Here's the professional answer:
Grocery stores use "Quantity Only" tracking at the SKU level.
SKU: WAIWAL-CHICKEN-75G
Name: Wai Wai Chicken Noodles (75g)
Stock: 1,200 packs
Reorder Point: 200 packs
Reorder Qty: 500 packs
Sold Unit: 1 pack = Rs. 30
They don't open the carton and track each packet. They buy 1 carton = 24 packets (this is a Purchase Order in bulk), and when it's received, stock goes up by 24. When sold, it goes down by 1 per packet.
What professional grocery POS (Square, Lightspeed) does: They scan barcodes at checkout. Every scan decrements the count by 1. The owner gets a low-stock alert automatically. They never manually count shelves.
Step 5: How the Big Systems Handle It
Odoo
Odoo uses a concept called Inventory Routes and has the most complete model:
product.template→product.product(variants) →stock.quant(actual stock in a location)- It has a
trackingfield per product:none,lot,serial - It supports multi-warehouse, multi-location (e.g., stock in "Shelf A" vs. "Back Room")
ERPNext
Very similar to Odoo. Has Item (template) → Item Variant. Also has:
- Batch tracking for perishables with expiry dates
- Serial number tracking for electronics
- A concept called Valuation Method: FIFO vs. Moving Average for costing
Lightspeed (Retail POS)
Designed for brick-and-mortar retail:
- Focuses on quick barcode scanning
- Variants are called "Matrices" (e.g., a shoe in multiple sizes/colors)
- Does NOT natively support batch/lot tracking (it's a POS, not a full ERP)
Square
The simplest. Good for small vendors:
- Just
ItemwithVariants(size, color) - Stock count per variant
- No lot or serial tracking — it's designed for simplicity
The Recommended Architecture for Khoji Nepal
Given your scope (from grocery vendors to electronics shops to service providers), here is a clean, scalable model:
products (Template)
├── id
├── name
├── vendor_id
├── category_id
├── product_type: enum('storable', 'perishable', 'service', 'digital', 'bundle')
├── tracking_type: enum('none', 'quantity', 'batch', 'serial')
├── is_active
product_variants
├── id
├── product_id (FK → products)
├── sku
├── attributes: JSON → {"size": "L", "color": "Red"}
├── price
├── barcode
inventory_items (the actual "where is my stock")
├── id
├── product_variant_id (FK)
├── quantity_on_hand
├── reorder_point
├── reorder_quantity
inventory_batches (ONLY for tracking_type = 'batch')
├── id
├── inventory_item_id (FK)
├── batch_number
├── manufactured_at
├── expires_at
├── quantity
inventory_serials (ONLY for tracking_type = 'serial')
├── id
├── inventory_item_id (FK)
├── serial_number
├── status: enum('in_stock', 'sold', 'returned', 'defective')
├── sold_at
The Final Decision Rule
A vendor just picks their product type when onboarding:
| Product | Choose |
|---|---|
| Apples, Milk, Vegetables | perishable + batch tracking |
| Noodles, Biscuits, Soap | storable + quantity tracking |
| Laptop, Phone, Refrigerator | storable + serial tracking |
| Haircut, Home Cleaning | service + none |
| Online Course | digital + none |
Your UI then shows or hides fields based on these choices. A fruit vendor never sees "Serial Number" fields. An electronics vendor never sees "Expiry Date" fields.
Short Answer to "Should We Skip Grocery?"
No, don't skip it. The quantity-only tracking mode IS the correct model for grocery. The vendor just sets their stock count when listing a product, and your system decrements it per sale. Optional barcode scanning at POS level can be added later. Professional grocery systems literally do exactly this.
This forms the complete theoretical foundation. Want me to start implementing the Laravel migration files and model structure for this, or do you have more questions to clarify first?