# Inventory Costing Guide — Multi-Price Stock Batches

## The Problem You Are Facing

Your current `Product` model stores **one single `mainprice`** (cost) and one `sellingprice`.  
This breaks completely once you receive the same product at a different cost on a different date.

**Example:**
| Date | Event | Units | Cost Each | Sell Price |
|------|-------|-------|-----------|------------|
| Jan 20 | Bought medicines | 10 | $1,000 | $1,200 |
| Apr 15 | New shipment arrives | 5 | $1,100 | $1,350 |

If you sell 1 medicine today, which cost do you use to calculate gross profit — $1,000 or $1,100?  
Your current system has no way to answer this. It will just use whatever `mainprice` is stored on the product, which is wrong.

---

## The Core Concept: Inventory Batches (Lots)

Instead of storing one cost on the product, you store **every purchase as a separate batch**.  
Each batch records: **when** it was bought, **how many** units, and **how much** each cost.

```
Product: "Dell medicine XPS15"
  └── Batch A  (Jan 20)  → 10 units @ $1,000 cost, $1,200 selling price, 7 remaining
  └── Batch B  (Apr 15)  → 5  units @ $1,100 cost, $1,350 selling price, 5 remaining
```

When you make a sale, you **pick from a specific batch**, or the system picks for you automatically depending on the costing method you choose.

---

## The Four Costing Methods — Choose One

### 1. FIFO — First In, First Out (Most Common for Retail)
The oldest batch is always sold first. You cannot choose — it's automatic.

- Sell the Jan 20 medicines first. Once they are gone, start selling Apr 15 medicines.
- **Gross profit** is calculated from the oldest cost automatically.
- **Best for:** Perishable goods, shops that want simplicity, shops that want to clear old stock.

### 2. LIFO — Last In, First Out
Newest batch is always sold first. Automatic like FIFO.

- Sell the Apr 15 medicines first.
- **Gross profit** uses the newest, higher cost, so it looks lower on paper.
- **Note:** LIFO is banned under IFRS (international accounting). Only allowed in the US. Not recommended.

### 3. Weighted Average Cost
Every time a new shipment arrives, you recalculate the average cost across all remaining units.

```
Before Apr 15 shipment:  10 units × $1,000 = $10,000
Apr 15 shipment arrives: 5  units × $1,100 = $5,500
Total: 15 units, $15,500 → Average cost = $15,500 / 15 = $1,033.33
```
Every sale uses $1,033.33 as cost, regardless of which physical unit you grabbed.

- **Best for:** Goods that are truly identical and interchangeable (liquids, bulk items).
- **Not great for** a pharmacy because individual units (by serial number) matter.

### 4. Specific Identification — What You Want
You manually choose **which batch** a sold item comes from at the time of sale.

- Cashier sells 2 medicines. They see: "Batch A (Jan 20): $1,000 cost | Batch B (Apr 15): $1,100 cost."
- They pick which batch the physical medicines belong to.
- Gross profit is calculated exactly from that batch's cost.
- **Best for:** High-value items where serial numbers matter (medicines, phones, cars, jewelry).
- **This is the method recommended for your pharmacy.**

---

## How to Implement This in Your System — Step by Step

### Step 1 — Add a New Model: `StockBatch`

You need a new collection `StockBatch`. Each document represents one purchase/shipment.

**Fields needed:**
```
StockBatch {
  product       → ref to Product  (which product this batch belongs to)
  batchCode     → String          (optional human label, e.g. "APR-2026-DELL")
  purchaseDate  → Date            (when you bought this batch)
  quantityIn    → Number          (how many units arrived)
  quantityLeft  → Number          (how many are still unsold — decremented on sales)
  costPrice     → Number          (what you paid per unit)
  sellingPrice  → Number          (what you sell each unit for)
  supplier      → String          (optional: who you bought from)
  notes         → String          (optional)
}
```

### Step 2 — Change the Product Model

Remove `mainprice` from the product (it is now on each batch).  
Keep `sellingprice` as a **default/suggested** selling price for new batches only (optional).  
Keep `stock` as a **computed/cached** total across all batches (sum of `quantityLeft` for that product).

You can either:
- **Option A:** Remove `stock` from Product and always compute it live by summing `StockBatch.quantityLeft`.
- **Option B:** Keep `stock` on Product as a cached value and update it every time a batch is changed (more complex but faster for reads).

**Recommendation:** Use Option A for correctness. Add an index on `StockBatch.product` so the sum query is fast.

### Step 3 — Change the Bill/Sale Flow

When creating a bill (a sale), each line item must now reference:
- The `product` (as now)
- The **`batchId`** (which `StockBatch` this unit came from)
- The `costPrice` (snapshot from the batch at time of sale — important, never recalculate later)
- The `sellingPrice` / `unitprice` (what the customer paid)

**Bill item fields needed:**
```
BillItem {
  item          → ref to Product   (already exists)
  batchId       → ref to StockBatch  ← NEW
  quantity      → Number            (already exists)
  unitprice     → Number            (what customer pays, already exists)
  costPrice     → Number            ← NEW (snapshot from batch — locked at time of sale)
  grossProfit   → Number            ← NEW (computed: (unitprice - costPrice) × quantity)
}
```

**Why snapshot `costPrice`?** Because if you delete or edit a batch later, you must never lose what the true profit was at the time of the sale. Always store the cost at sale time.

### Step 4 — The Sale Transaction Logic

When a sale is made, for each line item:

```
1. Load the StockBatch by batchId
2. Check: batch.quantityLeft >= item.quantity  → if not, reject the sale
3. Decrement: batch.quantityLeft -= item.quantity
4. Save the snapshot: item.costPrice = batch.costPrice
5. Compute:  item.grossProfit = (item.unitprice - item.costPrice) × item.quantity
6. Save batch and bill
```

For the bill totals:
```
bill.totalGrossProfit = sum of all item.grossProfit
bill.total            = sum of (item.unitprice × item.quantity) - bill.discount
```

### Step 5 — Adding New Stock (Receiving a Shipment)

When new medicines arrive at $1,100, you:

1. Create a new `StockBatch` document for the product.
2. Set `quantityIn = 5`, `quantityLeft = 5`, `costPrice = 1100`, `sellingPrice = 1350`, `purchaseDate = today`.
3. Optionally update `Product.stock += 5` if you use the cached approach.

**You do NOT touch the old batch.** Old Jan 20 batch stays at whatever `quantityLeft` it has.

### Step 6 — The UI/API (What the Frontend Needs)

When a cashier adds a product to a bill, they should see:

```
Product: Dell XPS15
Available Batches:
  [Batch A - Jan 20, 2026]  7 units left  — Cost: $1,000  — Selling: $1,200
  [Batch B - Apr 15, 2026]  5 units left  — Cost: $1,100  — Selling: $1,350
```

The cashier selects a batch (specific identification). The selected batch's `sellingPrice` auto-fills the unit price, but they can still override it.

For FIFO (if you want automatic mode instead): the API automatically picks the batch with the oldest `purchaseDate` that still has stock.

---

## How Gross Profit is Calculated (The Full Picture)

```
For one line item:
  Revenue      = unitprice × quantity          (what customer paid)
  COGS         = costPrice × quantity          (what the item cost you)
  Gross Profit = Revenue - COGS

For the whole bill:
  Total Revenue      = Σ (unitprice × quantity) for all items
  Total COGS         = Σ (costPrice × quantity) for all items
  Total Gross Profit = Total Revenue - Total COGS - discount

Gross Profit Margin % = (Total Gross Profit / Total Revenue) × 100
```

**Example:**
- Sell 2 medicines from Batch A: revenue = 2 × $1,200 = $2,400, cost = 2 × $1,000 = $2,000 → GP = $400
- Sell 1 medicine from Batch B: revenue = 1 × $1,350 = $1,350, cost = 1 × $1,100 = $1,100 → GP = $250
- Bill total GP = $650

---

## Impact on Analytics & Reports

Your `analyticsController.js` and `reportsController.js` must be updated to:

1. **Gross Profit by Product:** Group bill items by product, sum `grossProfit`.
2. **Gross Profit by Batch:** Group bill items by `batchId`, sum `grossProfit` — lets you see which shipment was more profitable.
3. **Stock Valuation Report:** Sum `quantityLeft × costPrice` across all `StockBatch` documents — this is the true value of your current inventory on hand.
4. **Cost of Goods Sold (COGS):** Sum `costPrice × quantity` from all sold bill items in a period.

---

## Which Models Change

| Model | Change |
|-------|--------|
| `ProductModle.js` | Remove `mainprice`. Keep or remove `stock` (see Step 2). |
| `BillsModel.js` | Add `batchId`, `costPrice`, `grossProfit` to each bill item. |
| `StockBatch.js` | **New model** — create this. |
| `OrdersModel.js` | If orders also track inventory, same changes as Bills. |

## Which Controllers Change

| Controller | Change |
|------------|--------|
| `billsController.js` | On create: resolve batch, snapshot costPrice, decrement quantityLeft. |
| `productControllers.js` | On stock add: create new StockBatch instead of updating `mainprice`. |
| `analyticsController.js` | All profit queries must use `costPrice` from bill items, not product `mainprice`. |
| `reportsController.js` | Add stock valuation report using StockBatch. |

---

## Recommended Implementation Order

1. Create the `StockBatch` model.
2. Write a migration script: convert every existing product's `mainprice` + `stock` into one `StockBatch` document per product (with `purchaseDate = today`, `quantityIn = stock`, `quantityLeft = stock`, `costPrice = mainprice`).
3. Update `billsController` — this is the core change.
4. Update `productControllers` — stock receive flow.
5. Update analytics/reports.
6. Remove `mainprice` from Product schema last (only after migration is confirmed working).

---

## Summary

| Concept | What It Means for You |
|---------|----------------------|
| StockBatch | One document per shipment per product |
| costPrice on StockBatch | What you paid for that specific batch |
| FIFO | System auto-picks oldest batch — easiest to implement |
| Specific Identification | Cashier picks from which batch — most accurate for medicines |
| Snapshot costPrice on BillItem | Lock in the cost at sale time — never lose this data |
| Gross Profit | revenue minus cost, calculated per item & per bill |
| Stock Valuation | Sum of quantityLeft × costPrice across all batches |
