Update Po invoice flow.md file
This commit is contained in:
@@ -82,6 +82,89 @@ Invoice
|
|||||||
├── IsActive, CreatedAt — from Template
|
├── IsActive, CreatedAt — from Template
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Invoice Line Items & Version History
|
||||||
|
|
||||||
|
> **Implemented.** Class/column names follow the code: the PO is `ShopPO`/`ShopPOItem`, and the
|
||||||
|
> invoice FK is `FK_ShopPOId` (not `FK_OrderId` as sketched above).
|
||||||
|
|
||||||
|
The invoice no longer bills straight from the PO. It owns its own lines, and every change to them is
|
||||||
|
snapshotted, so the original, printed and finalized quantities can all be compared.
|
||||||
|
|
||||||
|
```
|
||||||
|
InvoiceItem — the live billing lines
|
||||||
|
├── FK_InvoiceId, FK_ShopPOItemId, FK_ItemId
|
||||||
|
├── Quantity (int) — what is billed now (0..ShopPOItem.Quantity)
|
||||||
|
├── UnitPrice (decimal)
|
||||||
|
└── IsActive, CreatedAt — from Template
|
||||||
|
|
||||||
|
InvoiceVersion — immutable snapshot of the invoice
|
||||||
|
├── FK_InvoiceId, VersionNumber — unique per invoice, starts at 1
|
||||||
|
├── Stage (InvoiceVersionStage) — Created, DraftEdited, Printed, Finalized
|
||||||
|
├── TotalAmount, CreatedBy
|
||||||
|
└── Items → InvoiceVersionItem — FK_ShopPOItemId, FK_ItemId, Quantity, UnitPrice
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Add / remove lines (Draft):** `PUT /api/invoices/{id}/items` takes edits keyed by `invoiceItemId`
|
||||||
|
(change quantity/price, or `isRemoved: true` to strike the line — it is kept, excluded from the
|
||||||
|
total, shown struck through on the invoice view and print copy in every status, and can be
|
||||||
|
restored while Draft) or by `itemId` (add a catalog item
|
||||||
|
that was not on the PO; `unitPrice` required, quantity > 0, no duplicate of a live line). Added
|
||||||
|
lines have no `ShopPOItem` (`FK_ShopPOItemId` is nullable → no `orderedQuantity`, no max). Versions
|
||||||
|
store `IsRemoved` per line. At `Finalize`, adjustments are keyed by `invoiceItemId` and can also
|
||||||
|
set `isRemoved`. When the loading sheet is linked to invoices, it must count added lines.
|
||||||
|
- **Original PO quantity** stays on `ShopPOItem.Quantity` (locked once the PO is confirmed) and is
|
||||||
|
returned as `orderedQuantity` on each invoice line.
|
||||||
|
- `InvoiceItem` rows are copied from the PO's active lines at invoice creation (Mode A and Mode B).
|
||||||
|
- A version is written, in the same transaction as the change, when the invoice is:
|
||||||
|
|
||||||
|
| Stage | Written when |
|
||||||
|
|---|---|
|
||||||
|
| `Created` (v1) | Invoice created |
|
||||||
|
| `DraftEdited` | `PUT /api/invoices/{id}/items` while `Draft` |
|
||||||
|
| `Printed` | `POST /api/invoices/{id}/print` — the quantities on the paper copy |
|
||||||
|
| `Finalized` | `POST /api/invoices/{id}/finalize` — after the agent's edits are applied |
|
||||||
|
|
||||||
|
- Comparing `Printed` vs `Finalized` shows what the agent changed on paper.
|
||||||
|
- Migrations `AddInvoiceItems` and `AddInvoiceVersions` backfill lines and one baseline version for
|
||||||
|
invoices that already existed (stage mapped from the invoice's status at migration time, so they
|
||||||
|
have no earlier history).
|
||||||
|
|
||||||
|
### Invoice Screens & Printing (frontend)
|
||||||
|
|
||||||
|
> **Implemented** in `distribution_fe`.
|
||||||
|
|
||||||
|
**Invoice document** — one shared component (`components/invoice/invoice-template.tsx`) renders the
|
||||||
|
invoice for the preview, the draft editor and the print tab:
|
||||||
|
|
||||||
|
- **A4**: `INVOICE` header with Invoice No. (`INV-xxxxxxxx`), date and PO ref; a Status row shown
|
||||||
|
until the invoice is Finalized; *Bill To* (store name, plus address/phone from the PO); item table
|
||||||
|
(Item · Qty · Unit Price · Amount); totals (Subtotal, Total, and Paid / Balance Due when payments
|
||||||
|
exist); payments list; notes lines for the agent's edits on non-finalized copies; signature lines.
|
||||||
|
Soft slate rules, no heavy colour.
|
||||||
|
- **80mm / 58mm receipts**: single black-and-white column with dashed rules (`*** DRAFT ***` /
|
||||||
|
`*** PRINTED ***`, `qty x price` lines, heavy-ruled TOTAL, Paid/Balance, payments, "Thank you!").
|
||||||
|
- Money `Rs. 12,450.00`, quantities up to 3 decimals, dates `dd/MM/yyyy`.
|
||||||
|
- Deleted lines are shown **struck through in every status** and excluded from all totals.
|
||||||
|
- Not included (no data in the app): company/"From" block, logo, bank details, discount, tax, due date.
|
||||||
|
|
||||||
|
**Print tab** — `/invoices/{id}/print` (own route group `(print)`, no dashboard chrome). Has a
|
||||||
|
paper-size selector (A4 / 80mm / 58mm), sets `@page` with `margin: 0` (so the browser's own
|
||||||
|
URL/date header and footer are not printed; the A4 sheet pads itself with 14mm), and opens the
|
||||||
|
print dialog automatically. *Print* on a Draft asks who printed it, calls `POST /print`, then opens
|
||||||
|
this tab (the tab is opened inside the click so popup blockers allow it). Printed/Finalized
|
||||||
|
invoices have *Reprint*, which opens the tab without changing status.
|
||||||
|
|
||||||
|
**Draft editor** — pencil icon (Draft only, also *Edit Items* on the invoice page) opens a full page, `/sales/invoices/{id}/edit` (not a modal), showing the invoice template in edit mode: quantity/price
|
||||||
|
inputs, a trash icon per line (undo icon restores a deleted line), and an add-item row aligned to
|
||||||
|
the table columns (item dropdown excluding items already on the invoice, quantity, price
|
||||||
|
auto-filled from the store's price list, Enter or **+** to add). Save is disabled until something
|
||||||
|
changes, a summary shows what is pending (the browser warns before leaving with unsaved edits), and an item picked but not yet added is included on save. Non-Draft invoices redirect back to the invoice page.
|
||||||
|
Each save writes a `DraftEdited` version.
|
||||||
|
|
||||||
|
**Configuration** — *Invoice Creation* card: two toggles, **Auto** (Mode A) and **Manual** (Mode B,
|
||||||
|
default), mutually exclusive (turning one on turns the other off; the active one is locked on), each
|
||||||
|
with a one-line explanation and a process line (`Confirm PO → … → Edit draft → Print → Finalize`).
|
||||||
|
|
||||||
### New Enum — `InvoiceStatus`
|
### New Enum — `InvoiceStatus`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
@@ -140,6 +223,7 @@ Draft → Printed → Finalized
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| (create) → Draft | **Mode A:** combined with PO confirm above | Invoice created at `Draft`. `Order.InvoiceId` set. |
|
| (create) → Draft | **Mode A:** combined with PO confirm above | Invoice created at `Draft`. `Order.InvoiceId` set. |
|
||||||
| (create) → Draft | **Mode B:** `POST /api/invoices` `{ orderId }` | Office user selects a Confirmed PO (filtered by `agentId` / `routeId` / `shopId`) and creates the Invoice at `Draft`. `Order.InvoiceId` set. |
|
| (create) → Draft | **Mode B:** `POST /api/invoices` `{ orderId }` | Office user selects a Confirmed PO (filtered by `agentId` / `routeId` / `shopId`) and creates the Invoice at `Draft`. `Order.InvoiceId` set. |
|
||||||
|
| Draft (edit) | `PUT /api/invoices/{id}/items` | Office user edits the draft's lines (quantity `0..ordered`, price `>= 0`). Status stays `Draft`; `TotalAmount` recomputed; a `DraftEdited` version is saved. |
|
||||||
| Draft → Printed | `POST /api/invoices/{id}/print` | Invoice is physically printed. `PrintedAt`/`PrintedBy` set. Agent takes the printed copy to the field and makes handwritten edits during the ride. |
|
| Draft → Printed | `POST /api/invoices/{id}/print` | Invoice is physically printed. `PrintedAt`/`PrintedBy` set. Agent takes the printed copy to the field and makes handwritten edits during the ride. |
|
||||||
| Printed → Finalized | `POST /api/invoices/{id}/finalize` | After ride/day-end, agent returns printed invoice. Office worker enters any edits the agent made (quantity adjustments, returns, damage, discounts) and submits. `TotalAmount` locked. `FinalizedAt`/`FinalizedBy` set. |
|
| Printed → Finalized | `POST /api/invoices/{id}/finalize` | After ride/day-end, agent returns printed invoice. Office worker enters any edits the agent made (quantity adjustments, returns, damage, discounts) and submits. `TotalAmount` locked. `FinalizedAt`/`FinalizedBy` set. |
|
||||||
|
|
||||||
@@ -162,7 +246,10 @@ Draft → Printed → Finalized
|
|||||||
Invoice creation for both modes. It is stored as a plain column with no DB-level FK constraint.
|
Invoice creation for both modes. It is stored as a plain column with no DB-level FK constraint.
|
||||||
7. An Invoice cannot skip `Printed` — `Draft → Finalized` directly is not a valid transition.
|
7. An Invoice cannot skip `Printed` — `Draft → Finalized` directly is not a valid transition.
|
||||||
8. `Invoice.TotalAmount` is only authoritative once `Finalized`; through `Draft`/`Printed` it
|
8. `Invoice.TotalAmount` is only authoritative once `Finalized`; through `Draft`/`Printed` it
|
||||||
reflects the current line items.
|
reflects the current invoice lines (`InvoiceItem`), not the PO lines.
|
||||||
|
8a. Invoice lines are editable only while `Draft`. At `Finalize`, only quantities can be adjusted
|
||||||
|
(never above the ordered quantity). Every change writes an `InvoiceVersion` snapshot — see
|
||||||
|
[Invoice Line Items & Version History](#invoice-line-items--version-history).
|
||||||
9. `Order.Status` (fulfillment tracking: Pending/PartiallyFulfilled/Fulfilled/Cancelled) is a
|
9. `Order.Status` (fulfillment tracking: Pending/PartiallyFulfilled/Fulfilled/Cancelled) is a
|
||||||
separate axis from `Invoice.Status` — not merged in this pass.
|
separate axis from `Invoice.Status` — not merged in this pass.
|
||||||
10. `Order.FK_SaleId` is optional — a PO can exist and be confirmed before any `Sale` is recorded.
|
10. `Order.FK_SaleId` is optional — a PO can exist and be confirmed before any `Sale` is recorded.
|
||||||
@@ -201,8 +288,9 @@ Draft → Printed → Finalized
|
|||||||
already has a linked Invoice, what happens to that Invoice (voided? stays Draft?) is unresolved.
|
already has a linked Invoice, what happens to that Invoice (voided? stays Draft?) is unresolved.
|
||||||
If the PO's items were pulled into a loading sheet, what happens to those loading sheet items is
|
If the PO's items were pulled into a loading sheet, what happens to those loading sheet items is
|
||||||
also unresolved. Both cases need explicit cascade rules before implementation.
|
also unresolved. Both cases need explicit cascade rules before implementation.
|
||||||
6. **Exact adjustment payload for `POST /finalize`** — quantity adjustments, returns, damage, and
|
6. **Exact adjustment payload for `POST /finalize`** — *partly resolved:* quantity adjustments
|
||||||
discounts are assumed but the payload shape is not fully specified. "Free issues" handling (items
|
(`{ shopPOItemId, adjustedQuantity }`) are implemented and persisted on the invoice lines.
|
||||||
|
Returns, damage and discounts are still open, as is the payload shape for them. "Free issues" handling (items
|
||||||
given to the shop at no charge) is a known case that needs its own field(s) in the payload.
|
given to the shop at no charge) is a known case that needs its own field(s) in the payload.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user