Update Po invoice flow.md file
This commit is contained in:
@@ -82,6 +82,89 @@ Invoice
|
||||
├── 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`
|
||||
|
||||
```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 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. |
|
||||
| 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.
|
||||
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
|
||||
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
|
||||
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.
|
||||
@@ -201,8 +288,9 @@ Draft → Printed → Finalized
|
||||
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
|
||||
also unresolved. Both cases need explicit cascade rules before implementation.
|
||||
6. **Exact adjustment payload for `POST /finalize`** — quantity adjustments, returns, damage, and
|
||||
discounts are assumed but the payload shape is not fully specified. "Free issues" handling (items
|
||||
6. **Exact adjustment payload for `POST /finalize`** — *partly resolved:* quantity adjustments
|
||||
(`{ 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.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user