implement fe with backend

This commit is contained in:
2026-08-02 01:25:17 +05:30
parent 3c5b476635
commit 266a2a2c14
38 changed files with 5229 additions and 32 deletions
+224
View File
@@ -0,0 +1,224 @@
# 14 · BACKEND — Sales API Reference
> **Authoritative for:** sales API contracts for invoices, slips, free issues, and sales reports.
> **Navigation:** start from `00-CORE.md`. Sales business rules live in `docs/SALES_MODULE_PLAN.md` and the sales-related backend progress is tracked in `Backend/PROGRESS.md`.
> **Scope:** this document covers the sales endpoints currently implemented in ERPCore. Free issue is modeled as a sales-slip alias, not a separate table.
---
## 1. Conventions
- Base URL: `https://{host}/api/v1`
- Authentication: same as the rest of the v1 API
- Concurrency: `ETag` / `If-Match` on mutable resources
- Validation: `ProblemDetails` / `ValidationProblemDetails`
---
## 2. Sales Invoices
### `GET /api/v1/sales-invoices`
Query:
- `page`
- `pageSize`
- `q`
- `status`
- `customerId`
- `warehouseId`
Returns a paged list of `SalesInvoiceSummaryDto`.
The current frontend register uses this endpoint directly for the invoice hub view.
### `GET /api/v1/sales-invoices/{salesInvoiceId}`
Returns `SalesInvoiceDto`.
The detail page shows the document template and only exposes edit actions when the invoice is `Draft`.
### `POST /api/v1/sales-invoices`
Creates a draft sales invoice.
Request:
```json
{
"customerId": 2,
"warehouseId": 1,
"invoiceType": "B2C",
"lines": [
{
"itemId": 1,
"uomId": 1,
"warehouseId": 1,
"qty": 1,
"freeQty": 0,
"unitPrice": 100,
"allowManualPriceOverride": false,
"discountMode": "Percentage",
"discountPct": 0,
"discountAmount": 0,
"discountValue": 0,
"taxPct": 0,
"isFreeIssue": false,
"parentLineId": null
}
]
}
```
### `PUT /api/v1/sales-invoices/{salesInvoiceId}`
Updates a draft invoice. Requires `If-Match`.
### `POST /api/v1/sales-invoices/{salesInvoiceId}/post`
Posts stock out and marks the invoice as `Posted`.
Before posting, the UI calls `GET /api/v1/sales-invoices/{salesInvoiceId}/posting-check` to show shortages.
### `POST /api/v1/sales-invoices/{salesInvoiceId}/cancel`
Cancels a draft invoice.
---
## 3. Sales Slips
### `GET /api/v1/sales-slips`
Query:
- `page`
- `pageSize`
- `q`
- `status`
- `customerId`
- `warehouseId`
Returns a paged list of `SalesSlipSummaryDto`.
### `GET /api/v1/sales-slips/{salesSlipId}`
Returns `SalesSlipDto`.
The detail page follows the same document-style layout and draft-only edit behavior as invoices.
### `POST /api/v1/sales-slips`
Creates a draft sales slip.
Request:
```json
{
"customerId": 2,
"warehouseId": 1,
"cashierUserId": 1,
"lines": [
{
"itemId": 1,
"uomId": 1,
"warehouseId": 1,
"qty": 1,
"freeQty": 0,
"unitPrice": 50,
"allowManualPriceOverride": false,
"discountMode": "Percentage",
"discountPct": 0,
"discountAmount": 0,
"discountValue": 0,
"taxPct": 0,
"isFreeIssue": false,
"parentLineId": null
}
]
}
```
### `PUT /api/v1/sales-slips/{salesSlipId}`
Updates a draft slip. Requires `If-Match`.
### `POST /api/v1/sales-slips/{salesSlipId}/post`
Posts stock out and marks the slip as `Posted`.
Before posting, the UI calls `GET /api/v1/sales-slips/{salesSlipId}/posting-check`.
### `POST /api/v1/sales-slips/{salesSlipId}/cancel`
Cancels a draft slip.
---
## 4. Free Issues
Free issue is a business alias over sales slips.
There is no separate free-issue table in the current schema.
### `GET /api/v1/free-issues`
Same contract as `GET /api/v1/sales-slips`.
### `GET /api/v1/free-issues/{freeIssueId}`
Same contract as `GET /api/v1/sales-slips/{salesSlipId}`.
### `POST /api/v1/free-issues`
Same contract as `POST /api/v1/sales-slips`.
### `PUT /api/v1/free-issues/{freeIssueId}`
Same contract as `PUT /api/v1/sales-slips/{salesSlipId}`.
### `POST /api/v1/free-issues/{freeIssueId}/post`
Same contract as `POST /api/v1/sales-slips/{salesSlipId}/post`.
### `POST /api/v1/free-issues/{freeIssueId}/cancel`
Same contract as `POST /api/v1/sales-slips/{salesSlipId}/cancel`.
Business rule:
- free issue is represented by `IsFreeIssue=true` and/or `FreeQty>0`
- it still posts stock out through the normal slip posting flow
- the frontend free-issue screen is the main working page for create/list/edit/cancel
- cancelled free-issue records are hidden from the working list view
---
## 5. Sales Reports
### `GET /api/v1/reports/sales`
Returns the report catalog.
Response:
```json
[
{
"id": "daily-summary",
"name": "Daily Summary",
"description": "Aggregated sales by day across posted invoices and slips.",
"supportedFilters": ["from", "to"]
}
]
```
### `GET /api/v1/reports/sales/{reportId}`
Returns a single report definition by id.
### `POST /api/v1/reports/sales/query`
Returns the actual report data.
Request:
```json
{
"reportType": "item-summary",
"from": "2026-07-01",
"to": "2026-07-28",
"itemId": 1,
"customerId": null,
"warehouseId": 1
}
```
Supported report types:
- `daily-summary`
- `item-summary`
- `customer-summary`
- `warehouse-summary`
- `discount-summary`
- `free-issue-summary`
Validation rule:
- invalid filters for a given report type are rejected
- unsupported report types are rejected
- the frontend report page uses the same endpoint for all report types
- `daily-summary` auto-loads with a default date range in the UI
- the report results table is rendered from the backend response rows, not hardcoded data
---
## 6. Notes
- The sales report service reads both invoices and slips where relevant.
- Free issue reporting is derived from the same sales document lines.
- There is no separate free-issue table in the current schema.