Update docs for procurement and shop PO processes, including GRN rework functionality

This commit is contained in:
2026-10-08 12:59:08 +05:30
parent 25b21d6fa8
commit dd8f50f54c
3 changed files with 143 additions and 9 deletions
+36 -9
View File
@@ -97,11 +97,18 @@ PurchaseOrderItem
├── IsCancelled (bool) — true once this line is individually cancelled, default false
```
> **Update (GRN rework):** a goods receipt no longer requires a PO — see "Direct goods receipt" and
> the costing / free-issue / tax additions in
> [refactor_docs/grn_costing_and_taxes.md](./refactor_docs/grn_costing_and_taxes.md). The tree
> below shows the original design plus those changes, marked *(later)*.
### GoodsReceipt
```
GoodsReceipt
├── Id (Guid) — PK, from Template
├── FK_PurchaseOrderId (Guid) — required
├── FK_PurchaseOrderId (Guid?) — nullable *(later)*: null = direct receipt (no PO)
├── FK_SupplierId (Guid?) — *(later)* set on a direct receipt; a PO receipt reaches the supplier via the PO
├── DiscountPercent, VatPercent, NbtPercent (decimal 5,2) — *(later)* GRN-wide defaults for lines
├── FK_ReceivedByUserId (Guid) — required
├── FK_WarehouseId (Guid) — required; the warehouse the accepted quantities are received into.
│ Not anticipated in the original design — added because
@@ -116,9 +123,13 @@ GoodsReceipt
GoodsReceiptItem
├── Id (Guid) — PK, from Template
├── FK_GoodsReceiptId (Guid) — required
├── FK_PurchaseOrderItemId (Guid) — required, ties the receipt line back to its ordered line
├── FK_PurchaseOrderItemId (Guid?) — ties the receipt line back to its ordered line; null on a direct receipt
├── FK_ItemId (Guid?) — *(later)* the item, on a direct receipt line
├── AcceptedQuantity (int) — added to warehouse stock
├── RejectedQuantity (int) — damaged/short at receipt, default 0, not added to stock
├── FreeQuantity (int) — *(later)* free-issue units: added to stock, not costed, not counted against the PO
├── UnitCost (decimal 18,2) — *(later)* cost per unit; PO receipts default to the PO line's UnitCost
├── DiscountPercent, NbtPercent, VatPercent (decimal 5,2) — *(later)* per-line rates
```
### PurchaseReturn
@@ -154,11 +165,16 @@ gives one receiving warehouse per PO in practice; see Open Questions for the mul
## Migrations
One migration, applied:
1. **AddProcurement** (`20260824110925_AddProcurement`) — created `PurchaseOrders`,
`PurchaseOrderItems`, `GoodsReceipts`, `GoodsReceiptItems`, `PurchaseReturns`,
`PurchaseReturnItems`, with FKs to `Suppliers`/`Items`/`Warehouses` and indexes on every FK
column. Applied via `dotnet ef database update`.
2. **AddDirectGoodsReceipt** (`20261006070042`) — made the PO links nullable and added the supplier /
item links for direct receipts.
3. **AddGrnPricing**, **AddGrnFreeQuantity**, **AddGrnNbt** (`20261007…`, `20261008…`) — GRN costing,
free issues and NBT. Pricing and free quantity are applied; **AddGrnNbt was still pending** when
last checked, and GRN reads/writes fail until it is applied — see
[refactor_docs/grn_costing_and_taxes.md](./refactor_docs/grn_costing_and_taxes.md) §7.
---
@@ -182,11 +198,17 @@ One migration, applied:
| `resolveInvoiceVariance(poId, variance, resolution)` | `POST /api/purchase-orders/{id}/resolve-variance` |
### Goods Receipt (`Features/Grn`)
> A PO is **not mandatory** for a goods receipt: `POST /api/goods-receipts/direct` receives stock
> with no PO (no ordered-quantity limit, no PO status, no purchase returns / invoice matching for
> it). See [refactor_docs/grn_costing_and_taxes.md](./refactor_docs/grn_costing_and_taxes.md) §1 for
> when to use which.
| Function reference | Endpoint |
|---|---|
| `createGoodsReceipt(poId, warehouseId, items[])` | `POST /api/purchase-orders/{id}/goods-receipts` |
| `createGoodsReceipt(poId, warehouseId, items[])` | `POST /api/purchase-orders/{id}/goods-receipts` (also `POST /api/goods-receipts` with `purchaseOrderId` in the body) |
| `createDirectGoodsReceipt(warehouseId, supplierId?, items[])` | `POST /api/goods-receipts/direct` |
| `getGoodsReceipt(grnId)` | `GET /api/goods-receipts/{id}` |
| `listGoodsReceipts(poId)` | `GET /api/purchase-orders/{id}/goods-receipts` |
| `listGoodsReceipts(page)` | `GET /api/goods-receipts` |
### Purchase Returns (`Features/PurchaseReturn`)
| Function reference | Endpoint |
@@ -215,8 +237,12 @@ One migration, applied:
{ "purchaseOrderItemId": "...", "acceptedQuantity": 95, "rejectedQuantity": 5 }
] }
```
`rejectedQuantity` defaults to 0 if omitted; only `acceptedQuantity` is credited to
`StockBalance`.
`rejectedQuantity` defaults to 0 if omitted. `acceptedQuantity` **plus `freeQuantity`** (free-issue
units, default 0) is credited to `StockBalance`; only accepted units are costed and only accepted
units advance the PO line's `ReceivedQuantity`. Optional pricing fields (`unitCost`,
`discountPercent`, `vatPercent`, `nbtPercent` per line; `discountPercent`, `vatPercent`,
`nbtPercent` for the whole GRN) are described in
[refactor_docs/grn_costing_and_taxes.md](./refactor_docs/grn_costing_and_taxes.md).
### POST `/api/purchase-orders/{id}/returns`
```json
@@ -297,9 +323,10 @@ above) per 14's existing target design.
- Where should cost-vs-sale-price margin reporting (`PurchaseOrderItem.UnitCost` vs
`PriceListItem.Price`/`Item.SalePrice`) live — a read-only endpoint/report in this step, or a
later analytics/reporting step? Not a blocker for the core PO→GRN flow.
- Landed cost (freight, duty, taxes allocated onto received stock) is out of scope —
`PurchaseOrderItem.UnitCost` is the only cost basis captured. Defer unless accurate per-unit cost
is needed sooner.
- Landed cost (freight, duty allocated onto received stock) is out of scope. The GRN now records
unit cost, discount, NBT and VAT per line (see
[refactor_docs/grn_costing_and_taxes.md](./refactor_docs/grn_costing_and_taxes.md)), but these do
not feed stock valuation or `Item.CostPrice`, and the PO itself carries no discount or tax.
- Reorder triggers (`setReorderPoint`/`sendLowStockAlert`) are not wired to procurement — restocking
stays fully manual until Notifications & Alerts exists.
- `createPurchaseReturn` assumes a single receiving warehouse per PO (uses the most recent