feat: Implement fixed sale price functionality for items

- Added a toggle for fixed sale price vs stock value in the item creation form.
- Introduced validation to ensure all variants have a price greater than 0 when fixed price mode is selected.
- Updated the item model to include a nullable salePrice field, which is used for sales only and does not affect GRN/FIFO/ledger.
- Enhanced the GRN page to allow off-PO items and included a refresh button to update the item list without reloading the page.
- Updated documentation to reflect changes in item pricing and GRN handling.
This commit is contained in:
2026-07-23 12:12:32 +05:30
parent 5cf9588728
commit a1b3985469
15 changed files with 245 additions and 25 deletions
+3 -1
View File
@@ -143,9 +143,11 @@ Vendors, Items, Categories, Subcategories, UOM, Warehouses, Brands, and Item Typ
- **SKU generation stays client-side** (`buildVariantSku`) and is the *only* record of which colour/size an item is; the server only uniqueness-checks it. **Nothing can query items by colour** — accepted (`docs/10 Part C.9`).
- **`remove()``updateStatus(id, "Inactive")`** everywhere. There are no `DELETE` endpoints on any master (FR-MD-08); the lists show a Status column and Deactivate/Activate.
- **`initialQty` is gone** from the builder — the Item contract has no such field and there is no initial-receipt flow. Stock arrives via a GRN.
- **Sale-price toggle on the builder (2026-07-22).** A "Fixed price / Use stock value" toggle sits on `/dashboard/products/new`. **Use stock value** (default) sends `salePrice: null` on every created item (sold at FIFO value). **Fixed price** reveals a top "fix value" input that pre-fills a per-variant `Sale price` column; each row is editable, and submit is blocked until **every** generated variant has a price `> 0`. The toggle is **frontend-only** — the contract is just the nullable `salePrice` field (`docs/11 §2.1`); it is Sales-only and never affects GRN/costing. The non-transactional create loop still applies — each variant's `salePrice` rides its own `POST /items`.
- **Product Configuration** (`app/dashboard/products/settings`, `GET`/`PUT /product-config`) — only **3** of the original design's ~13 toggles exist. `subcategoriesEnabled`/`brandsEnabled` are server-enforced (`CONFIG_DISABLED`); **`itemTypesEnabled` is advisory** and this app is what honours it (it hides the builder's type section). The UI states that distinction on the screen rather than implying a guarantee.
- **Non-transactional create loop:** the builder's per-row `itemsApi.create()` has no transaction — a `SKU_DUPLICATE` on row 7 of 12 leaves 6 items created. The error message now says how many landed rather than implying nothing happened. A transactional bulk-create endpoint would be the real fix.
- **GRN edit/delete removed** — the API has no `PUT`/`DELETE` for a GRN; receipts are corrected by reversing documents (FR-X-05).
- **GRN off-PO items + inline item create (2026-07-22).** On `/dashboard/receiving/grn/new`, "Add line" is available in **both** PO and direct mode — an added line in PO mode has `poLineId: null` and receives an item not on the PO (editable item/UOM dropdowns, `unitCost` required). A **"New item"** button opens `/dashboard/products/new` in a **new browser tab** (`window.open`, the first such pattern in the app), and a **refresh icon** re-pulls `GET /items?status=Active` so the newly created item is selectable **without** reloading the screen and losing the in-progress GRN. Server treats off-PO lines as direct receipts (no over-receipt check) — `docs/11 §4.1`.
- **RFQ invited-vendors is not persisted** — `POST /rfqs` validates `vendorIds` then discards them, so the list/detail screens show quotations received instead of vendors invited.
- **Known gap — serial numbers:** FR-GRN-04 requires capturing serials on receipt, but `CreateGrnLineInput` has no such field (only `batch`). The UI does not collect them rather than silently discarding them. Needs a backend change to honour the requirement.
@@ -159,7 +161,7 @@ Vendors, Items, Categories, Subcategories, UOM, Warehouses, Brands, and Item Typ
**Client-side (UX only — safe to check locally):** purely input-level facts the browser already has.
- Required fields present.
- Format: SKU pattern, numeric fields numeric, date format, positive integers.
- Range/bounds: `qty > 0`, `unitPrice >= 0`, `factor > 0`.
- Range/bounds: `qty > 0`, `unitPrice >= 0`, `factor > 0`. Item `salePrice` is a client-supplied number — the builder requires `> 0` per variant in fixed mode; the server only checks `>= 0` on a supplied value (`docs/11 §2.1`).
- Simple cross-field input rules: transfer `destWarehouseId != srcWarehouseId`.
- Enum membership via constrained dropdowns (`stockNature` — ex-`itemType`, `trackingMode`, `countType`, `holdStatus`). Note the **Item Type** dropdown is *not* in this category: it's server data (`GET /item-types`), not an enum.