# Inventory Module — Business & Functional Documentation

## Overview

The Inventory module manages the full lifecycle of physical goods: from defining warehouses and items through purchasing and goods receipt through to stock valuation and reporting. It integrates with the Finance module to post accounting entries automatically when goods are received.

---

## Setup Data

### Warehouses

A warehouse is a physical location where stock is kept. Every warehouse belongs to a branch and carries a unique code. Warehouses can be marked active or inactive; only active warehouses appear in transaction forms. Each warehouse tracks its own stock balance independently, meaning the same item can have different quantities at different warehouses.

### Item Categories

Item categories group related items together. Each category can have three GL accounts linked to it: an inventory asset account (used when stock is received), a cost-of-goods-sold account (used when stock is sold), and a revenue account. These category-level accounts serve as defaults when an individual item does not have its own accounts assigned.

### Units of Measure

Units of measure (UOM) define how quantities are expressed — for example "piece", "kilogram", or "box". Each UOM has a unique code and a translatable name. Items are assigned a UOM at creation, and all quantities on purchase orders and receipts are expressed in that unit.

### Suppliers

A supplier represents a vendor from whom goods are purchased. Each supplier has:

- A unique code and name (translated in Arabic and English)
- Contact details: phone, email, and address
- A tax registration number (optional)
- A credit limit and default payment terms in days
- A link to an Accounts Payable GL account in the Finance module
- An optional opening balance representing the outstanding amount owed to the supplier at system setup time
- A branch assignment for multi-branch environments

The supplier's current payable balance is currently based on the opening balance. Future posting of purchase order payments will layer on top of this.

---

## Items

An item represents anything that is bought, sold, or kept in stock. Items carry a type field:

- **stock** — physically held items whose quantities are tracked in warehouses
- Other types (services, etc.) can exist but are excluded from stock-tracking operations

Key item attributes include:

- Category and unit of measure
- Standard purchase price and sale price (informational; actual costs come from purchase receipts)
- Reorder level and reorder quantity — used by the low-stock report to flag items that need replenishment
- Cost method: either "average" (weighted average cost) or "fifo" (FIFO; currently the weighted average algorithm is used in all cases)
- Average cost — maintained automatically by the system whenever stock is received
- Three optional GL account overrides: inventory account, COGS account, and revenue account (fall back to category-level accounts when not set)
- An allow-negative-stock flag — when false, the system prevents goods from being issued beyond the available quantity

---

## Purchase Orders

A purchase order (PO) is the formal request to a supplier to deliver goods at agreed prices.

### Creating a Purchase Order

A buyer selects a supplier, a branch, a PO date, and optionally an expected delivery date. They then add one or more line items, each specifying an item, a quantity, and a unit cost. The system calculates the line total (quantity × unit cost) and sums them into the total amount. The PO number is generated automatically from the numbering sequence with the prefix "PO-".

### Status Lifecycle

Purchase orders follow a strict status machine:

1. **draft** — The order has been entered but not yet approved. The buyer can edit or delete it.
2. **confirmed** — A manager confirms the order, signalling that it has been approved and sent to the supplier. Only draft orders can be confirmed. Confirmed orders can no longer be edited.
3. **partially_received** — At least one goods receipt has been posted against this PO, but not all ordered quantities have arrived.
4. **received** — All ordered quantities have been fully received. The system sets this automatically when the last outstanding quantity is received.
5. **cancelled** — The order has been voided. Draft or confirmed orders can be cancelled. A cancelled order can never be received.

Only draft orders can be deleted. Confirmed, partially received, received, or cancelled orders are permanent records.

### Line Items and Received Quantities

Each PO line stores the ordered quantity alongside a running received_quantity counter. Every time a purchase receipt is posted against a PO line, the received_quantity increments by the received amount. The system compares total received versus ordered quantities across all lines to determine whether the PO is partially or fully received.

---

## Goods Receipt (Purchase Receipts)

A purchase receipt (also called a Goods Receipt Note or GRN) records the physical arrival of goods at a warehouse.

### Creating a Receipt

A receipt must specify the supplier, the destination warehouse, and a receipt date. Optionally, it can be linked to a confirmed (or partially received) purchase order. Line items carry item, quantity, and unit cost; the system auto-fills these when a PO is selected, pre-populating only the remaining undelivered quantities so partial receipts are handled naturally. Receipts not linked to a PO can also be created freeform (for example, unplanned deliveries or opening stock entries).

The receipt number is auto-generated with the prefix "GRN-".

### Posting a Receipt

A draft receipt has no effect on stock. Posting is a deliberate second step that commits the receipt to inventory. When a receipt is posted, the system:

1. For each receipt line, calls the stock balance engine to increase the stock of that item in the specified warehouse.
2. Updates the item's average cost using the weighted average method (described below).
3. Creates a stock movement record for each line, stamping the movement type as "in" with a reference back to the receipt.
4. If the receipt is linked to a PO, increments the PO line received quantities and updates the PO status (to partially_received or received as appropriate).
5. If the Finance module is configured with a purchase journal, posts a double-entry journal: debit the inventory asset account(s), credit the supplier's accounts payable account.

### Partial Receipts

Multiple receipts can be posted against the same PO. Each receipt only records the quantities that physically arrived. The PO tracks cumulative received quantities, and the system transitions the PO status accordingly.

### Reversing a Receipt

A posted receipt can be reversed. Reversal creates offsetting stock movements (type "out") that reduce stock by the originally received quantities, returns the item average costs, and posts a mirror journal entry (swapped debits and credits) into the same fiscal period as the original. The receipt status changes to "reversed". Reversed receipts cannot be reversed again.

---

## Stock Movements

Stock movements are the granular audit trail of every quantity change in the warehouse. They are created automatically when receipts are posted or reversed, and can also be entered manually for adjustments.

### Movement Types

- **in** — Stock entering the warehouse. Used for purchase receipts and positive adjustments. Increases the on-hand quantity.
- **out** — Stock leaving the warehouse. Used for sales fulfilment (via the Sales module) and negative adjustments. Decreases on-hand quantity.
- **adjustment** — A direct quantity correction. Used for physical count discrepancies, write-offs, or opening stock entry. The quantity field holds the new absolute quantity; the service sets the balance directly.

### Reference Linking

Every automatically-generated movement carries a reference_type (e.g. "purchase_receipt", "purchase_receipt_reversal") and a reference_id pointing to the source document. Manually entered adjustments can include a free-text note. This makes it possible to trace any balance change back to its originating document.

### Posting Status

Movements created from receipt posting or reversal are marked "posted" immediately. Manually entered movements go in as "pending" and can be promoted to "posted" by the inventory posting service when finance integration is in use. Reversed movements are stamped "reversed".

---

## Stock Balance

### How Stock is Tracked

The system maintains a dedicated stock_balances table with one row per item-warehouse pair. This is a running summary rather than a recalculated figure, meaning the on-hand quantity and total value are updated atomically every time a movement is applied. The table holds three values per row: the current quantity, the current weighted average cost per unit, and the total inventory value (quantity × average cost).

### Weighted Average Cost Method

When stock arrives (movement type "in"), the system recalculates the average cost using the weighted average formula:

- New average cost = (existing total value + incoming quantity × incoming unit cost) / (existing quantity + incoming quantity)

This new average cost is written back to the stock_balances row and also to the item's average_cost field. When stock leaves (movement type "out"), the cost per unit applied to COGS is the average cost at the moment of the movement, and the balance total value decreases by that amount.

### Negative Stock Prevention

If allow_negative_stock is false for an item, the system raises an error when a movement would reduce the warehouse balance below zero. If allow_negative_stock is true, the balance can go negative (useful for high-volume environments where physical counts lag).

### Balance Rebuild

The service includes a rebuildBalances method that wipes the stock_balances table and replays all historical movements in chronological order to reconstruct the current state. This is a recovery tool for data-integrity issues; it is not part of normal operations.

---

## Low-Stock Reporting

The system identifies items at or below their reorder level by joining the stock_balances table with the items table and comparing on-hand quantity against the reorder_level threshold. Items with reorder_level set to zero are excluded (zero is interpreted as "no reorder point defined"). The low-stock report shows the current quantity, the reorder level, and the suggested reorder quantity (reorder_qty) for each flagged item, grouped by warehouse.

---

## Inventory Valuation

The stock valuation report presents the financial value of every item currently held in each warehouse. It reads directly from the stock_balances table (which always holds the current weighted average cost and total value) and can be filtered by warehouse and by item category. The report also totals across all rows to give a single figure for total inventory value.

This report is point-in-time: it shows the current state as of the moment it is run. It does not currently support as-of-date historical valuation (that would require replaying movements up to a specific date).

---

## Stock Report

The stock report is a movement-level summary per item per warehouse. It aggregates all historical stock movements grouped by item and warehouse, computing:

- Total units in (all "in" movements)
- Total units out (all "out" movements)
- Adjustment quantity (all "adjustment" movements)
- Net quantity (total in − total out + adjustments)
- Average unit cost across movements
- Estimated stock value (net quantity × average unit cost)

Filters are available by branch, warehouse, and item. The report also totals all rows.

---

## Inventory Ledger (Stock Ledger)

The stock ledger presents the full chronological list of stock movements for a single item, optionally filtered by warehouse and date range. It shows every movement individually — date, type, quantity, unit cost, total cost, and the average cost before and after each movement — giving a complete audit trail of how the item's balance and cost evolved over time.

---

## Movement Summary Report

The movement summary aggregates stock movements over a selected date range, grouped by item and warehouse. For each group it shows total units in, total units out, and total cost of movements in that period. It can be filtered to a single warehouse. This is useful for understanding purchasing and consumption activity within a period without the detail of individual movement lines.

---

## Purchase History Report

The purchase history report lists all purchase orders (excluding cancelled ones) within a selected date range. It can be filtered by supplier. Each row shows the PO number, date, supplier, branch, total amount, and status. This gives a simple chronological view of purchasing activity.

---

## Finance Integration

When the purchase_journal_id setting is configured in the system, posting a purchase receipt automatically creates a double-entry journal:

- Debit: inventory asset account(s) — one debit line per distinct inventory GL account across all lines
- Credit: the supplier's accounts payable account for the total receipt value

The inventory account is resolved in priority order: item-level account → category-level account → system default. The AP account is resolved from the supplier record or from a system default. If either cannot be resolved, posting fails with an error and the receipt remains in draft.

Reversals post a mirror journal entry (credits and debits swapped) targeting the same fiscal period as the original entry, so the net effect is zero in that period's balances.

If the finance module is not configured (purchase_journal_id not set), inventory operations proceed normally without any GL postings.
