# Unit Conversion Sales Implementation Checklist

Goal: add proper unit-conversion selling across all Vozex POS systems.

Example requirement: product is stocked/priced in `KG`, cashier can sell `500 g`; system calculates price as `unitPricePerKg * 0.5`, deducts `0.5 KG` from stock, and prints `500 g` on receipt.

Target projects:

- [x] `C:\Vozex Projects\vozex-hardware-pos`
- [x] `C:\Vozex Projects\vozex-bookshop-pos`
- [x] `C:\Vozex Projects\vozex-clothing-pos`
- [x] `C:\Vozex Projects\vozex-restaurant-pos`
- [x] `C:\Vozex Projects\vozex-spare-part-pos`

## Implementation Status

- [x] Hardware POS MVP implemented:
  - shared sale/product/receipt contracts include optional unit fields
  - billing calculations convert sale quantity to base stock quantity
  - modern and classic billing quantity rows include unit selector and base preview
  - sale payload keeps `qty` as base quantity and carries `saleQty/saleUnit`
  - backend stock deduction uses converted base `qty`
  - browser, thermal, and ESC/POS receipts display sale quantity/unit when present
  - focused billing tests added
  - `npm run lint` passed
  - `npm run test -- billing-calculations billing-sale-workflow billing-store` passed
- [x] Bookshop POS MVP implemented and verified:
  - targeted contract/backend/receipt/UI patches preserve book-specific fields
  - `npm run lint` passed
  - `npm run test -- billing-calculations billing-sale-workflow billing-store` passed
- [x] Clothing POS MVP implemented and verified:
  - targeted contract/backend/receipt/UI patches preserve clothing return, size, color, brand, style, and article fields
  - `npm run lint` passed
  - `npm run test -- billing-calculations billing-sale-workflow billing-store` passed
- [x] Restaurant POS MVP implemented and verified:
  - targeted contract/backend/receipt/UI patches preserve variant selection and kitchen bill printing
  - `npm run lint` passed
  - `npm run test -- billing-calculations billing-sale-workflow billing-store` passed
- [x] Spare Part POS MVP implemented and verified:
  - targeted contract/backend/receipt/UI patches preserve vehicle, warranty, fitment, price-tier, and spare-part fields
  - `npm run lint` passed
  - `npm run test -- billing-calculations billing-sale-workflow billing-store` passed for matched test files
- [x] Remaining projects were patched per-project. Do not copy the full hardware `ipc.ts` or full backend service files into the other repos because each project has domain-specific contract fields.

## Current Gap

- [ ] Sale math currently uses `qty * unitPrice`.
- [ ] Product `unit` is mostly a display/catalog label.
- [ ] Sale line has no `saleUnit`, `baseUnit`, or `conversionFactor`.
- [ ] Stock deduction subtracts sale `qty` directly from inventory.
- [ ] Quantity sanitizer clamps values below `1`, blocking `0.5 KG`.
- [ ] Receipts do not show converted units such as `500 g`.
- [ ] Reports aggregate quantity without knowing the unit used.

## Product Unit Model

- [ ] Add canonical base unit fields to product/inventory model.
- [ ] Add `baseUnit`, examples: `kg`, `g`, `l`, `ml`, `m`, `cm`, `pcs`.
- [ ] Add `saleUnitOptions` or a related table for allowed sale units.
- [ ] Add conversion factor per sale unit.
- [ ] Define conversion direction clearly:
  - `baseQty = saleQty * conversionToBase`
  - Example: base `kg`, sale `g`, `conversionToBase = 0.001`
- [ ] Keep existing `unit` field backward-compatible.
- [ ] Decide migration behavior for old products:
  - existing `unit` becomes `baseUnit`
  - if no unit, default to `pcs`
- [ ] Prevent invalid conversion between incompatible dimensions, e.g. `kg` to `ml`.

## Unit Categories

- [ ] Define unit categories:
  - count: `pcs`, `unit`, `pair`, `set`, `dozen`
  - weight: `kg`, `g`, `mg`
  - volume: `l`, `ml`
  - length: `m`, `cm`, `mm`
- [ ] Add helper to normalize labels, e.g. `KG`, `kg`, `Kg` -> `kg`.
- [ ] Add helper to format units for UI and receipts.
- [ ] Add helper to validate conversion within the same category.

## Billing Line Model

- [ ] Extend sale line contract with:
  - `saleQty`
  - `saleUnit`
  - `baseQty`
  - `baseUnit`
  - `conversionToBase`
  - `unitPriceBase`
  - `unitPriceSale`
- [ ] Keep `qty` backward-compatible or map it to `baseQty`.
- [ ] Confirm API payload remains compatible for old bills.
- [ ] Update offline billing queue schema to include unit fields.
- [ ] Update held bills/session recovery storage.
- [ ] Update returns to return the same converted/base quantity correctly.

## Price Calculation

- [ ] Add calculation helper:
  - input: `unitPriceBase`, `saleQty`, `saleUnit`, `conversionToBase`
  - output: `lineTotal`
- [ ] Example: `LKR 1200 / kg`, sale `500 g`
  - `baseQty = 500 * 0.001 = 0.5 kg`
  - `lineTotal = 1200 * 0.5 = 600`
- [ ] Support manual price override safely.
- [ ] Support discounts after conversion.
- [ ] Support tax after converted line total.
- [ ] Support batch price and variant price with conversion.
- [ ] Add rounding rules:
  - quantity precision: 3 decimals or configurable
  - money precision: 2 decimals

## Quantity Validation

- [ ] Update `sanitizeQuantity()` to allow fractional quantity where unit allows it.
- [ ] Keep integer-only quantities for count units if needed.
- [ ] Add minimum quantity rules by unit:
  - `kg`: allow `0.001`
  - `g`: allow `1`
  - `l`: allow `0.001`
  - `ml`: allow `1`
  - `pcs`: allow `1`
- [ ] Prevent zero/negative sale quantity except refund flows.
- [ ] Validate available stock using `baseQty`, not display/sale quantity.

## Stock Deduction

- [ ] Backend must deduct `baseQty` from inventory `onHand`.
- [ ] Batch stock must deduct `baseQty`.
- [ ] Variant stock must deduct `baseQty`.
- [ ] Inventory history should store:
  - displayed sale quantity/unit
  - base quantity/unit
  - conversion factor
- [ ] Returns should add back `baseQty`.
- [ ] Damaged stock/adjustments/transfers should remain base-unit based unless explicitly converted.

## Billing UI

- [ ] Add unit selector beside quantity input.
- [ ] Show available sale units for the selected product.
- [ ] Show base conversion preview:
  - `500 g = 0.5 kg`
- [ ] Show converted price preview:
  - `LKR 1200/kg -> LKR 600`
- [ ] Allow barcode scan to add default sale unit.
- [ ] Allow cashier to change unit after item is in cart.
- [ ] Update classic billing quantity controls to allow fractional input.
- [ ] Update compact billing cart row quantity input.
- [ ] Update custom product dialog to support unit and conversion or keep it as simple non-stock custom sale.

## Inventory UI

- [ ] Add base unit selector in product editor.
- [ ] Add allowed sale units section.
- [ ] Add conversion factor editor.
- [ ] Add default sale unit selector.
- [ ] Add examples/help text only where needed:
  - `1 g = 0.001 kg`
  - `1 ml = 0.001 l`
- [ ] Update import/export CSV to include unit conversion fields.
- [ ] Update barcode label generation if unit price display is needed.

## Purchases / GRN

- [ ] Decide whether purchases are entered in base unit only or purchase unit can differ.
- [ ] If purchase unit can differ, add purchase conversion:
  - e.g. buy `25 kg bag`, stock as `kg`
- [ ] GRN custom rows should convert received quantity to base stock.
- [ ] Batch quantity should store base quantity.
- [ ] Purchase cost should calculate per base unit.

## Receipts / Printing

- [ ] Receipt line should display sale quantity and sale unit.
- [ ] Example: `Sugar 500 g x LKR 1.20/g = LKR 600`
- [ ] Optionally show base equivalent: `0.5 kg`.
- [ ] Return receipt should show same unit data.
- [ ] Kitchen/restaurant print should not be affected unless weighted items are used.
- [ ] ESC/POS and browser receipt previews must match.

## Reports / Dashboard

- [ ] Sales reports should show both revenue and base quantity.
- [ ] Product movement reports should aggregate by base unit.
- [ ] Top selling reports should avoid mixing incompatible units.
- [ ] Inventory valuation should use base quantity.
- [ ] Dashboard low stock should compare base `onHand` to base `reorderLevel`.
- [ ] AI assistant metadata should include unit fields so AI answers are correct.

## API / Contracts

- [ ] Update shared IPC schemas.
- [ ] Update `BillingProduct` schema.
- [ ] Update `SaleLine` schema.
- [ ] Update product create/update schemas.
- [ ] Update import/export schemas.
- [ ] Add runtime validation for unit compatibility.
- [ ] Keep backward compatibility for old payloads.

## Database Migration

- [ ] Add product unit conversion fields/tables.
- [ ] Add sale item unit fields.
- [ ] Add inventory history unit fields.
- [ ] Backfill old products.
- [ ] Backfill old sale items:
  - `saleUnit = product.unit`
  - `baseUnit = product.unit`
  - `conversionToBase = 1`
  - `baseQty = qty`
- [ ] Backfill inventory history similarly.
- [ ] Validate decimal precision is enough for small quantities.

## Tests

- [ ] Unit tests for conversion helper.
- [ ] Unit tests for billing totals with converted units.
- [ ] Unit tests for discount/tax with converted units.
- [ ] Unit tests for fractional quantity sanitizer.
- [ ] Backend tests for sale stock deduction using base quantity.
- [ ] Backend tests for returns adding back base quantity.
- [ ] GRN tests for purchase conversion if enabled.
- [ ] Receipt preview tests for converted unit display.
- [ ] Offline/held bill recovery tests with unit metadata.
- [ ] Migration/backfill tests.

## Per-Project Notes

### Hardware POS

- [ ] High priority: hardware can sell weight/length items.
- [ ] Support `kg/g`, `m/cm`, `l/ml`, `pcs`.
- [ ] Verify GRN custom rows and batch stock conversion.

### Bookshop POS

- [ ] Lower priority, mostly count units.
- [ ] Keep integer-only default for books/stationery.
- [ ] Support conversion only if needed for stationery packs or sheets.

### Clothing POS

- [ ] Mostly count units with size/color variants.
- [ ] Keep `pcs` integer default.
- [ ] Avoid breaking size/color variant stock.

### Restaurant POS

- [ ] Medium/high priority for ingredients or weighted menu items.
- [ ] Menu item variants like half/full are not the same as unit conversion.
- [ ] If ingredient stock is tracked, conversion should be base-unit aware.

### Spare Part POS

- [ ] Mostly count units.
- [ ] Some products may need length/volume conversion, e.g. wire, oil, hose.
- [ ] Preserve fitments/alternate numbers/variant behavior.

## Acceptance Criteria

- [ ] Product stocked/priced in `KG` can be sold in `g`.
- [ ] Selling `500 g` from `10 kg` stock leaves `9.5 kg`.
- [ ] Receipt shows `500 g`, not only `0.5`.
- [ ] Reports aggregate stock movement as `0.5 kg`.
- [ ] Returns restore `0.5 kg`.
- [ ] Count-based products still require whole units unless configured otherwise.
- [ ] Existing products and old sales remain readable.
- [ ] `npm run lint` passes in all five projects.
- [ ] Relevant tests pass in all five projects.

## Suggested Development Order

1. Create shared unit conversion helpers and tests.
2. Update shared contracts.
3. Add database migration and backfill.
4. Update backend sale persistence and stock deduction.
5. Update billing calculations and store.
6. Update billing UI unit selector and quantity input.
7. Update inventory product editor.
8. Update receipts.
9. Update reports/dashboard/AI metadata.
10. Port to all five projects and run tests.
