new api for offline sales
This commit is contained in:
@@ -439,6 +439,77 @@ type Ordersequences struct {
|
||||
Paymentprefix string `json:"paymentprefix" gorm:"default:PAY"`
|
||||
}
|
||||
|
||||
// ── Offline (in-store) sales import ───────────────────────────────────────────
|
||||
//
|
||||
// A sale rung up at the counter never passes through the app, so nothing
|
||||
// deducts its stock. These types carry a spreadsheet of such sales into the
|
||||
// same order path online orders use, so one ledger remains the single source
|
||||
// of truth for stock and one revenue figure covers both channels.
|
||||
|
||||
// OfflineSaleItem is one spreadsheet row. Only Productid and Qtysold are
|
||||
// required; the rest fall back to the product's own pricing when left blank.
|
||||
// Productname is carried for verification against Productid, not for matching
|
||||
// (see SaleTemplateRow for why a name can't be a key).
|
||||
type OfflineSaleItem struct {
|
||||
Productid int `json:"productid"`
|
||||
Productname string `json:"productname"`
|
||||
Qtysold float64 `json:"qtysold"`
|
||||
Unitprice float64 `json:"unitprice"`
|
||||
Discountamount float64 `json:"discountamount"`
|
||||
Taxpercent float64 `json:"taxpercent"`
|
||||
}
|
||||
|
||||
// OfflineSaleBill is one counter bill — the rows of a spreadsheet grouped by
|
||||
// their billno. Billno is what makes a re-upload of the same file safe: it is
|
||||
// recorded on the order and refused if it is already present for this outlet.
|
||||
type OfflineSaleBill struct {
|
||||
Billno string `json:"billno"`
|
||||
Saledate string `json:"saledate"`
|
||||
Paymentmode string `json:"paymentmode"`
|
||||
Customername string `json:"customername"`
|
||||
Customermobile string `json:"customermobile"`
|
||||
Remarks string `json:"remarks"`
|
||||
Items []OfflineSaleItem `json:"items"`
|
||||
}
|
||||
|
||||
// OfflineSalesUpload is the request body. Locationid is the outlet the sales
|
||||
// belong to and is authorised server-side against Tenantid — a store user
|
||||
// editing the spreadsheet cannot post sales into another branch.
|
||||
type OfflineSalesUpload struct {
|
||||
Tenantid int `json:"tenantid"`
|
||||
Locationid int `json:"locationid"`
|
||||
Userid int `json:"userid"`
|
||||
Bills []OfflineSaleBill `json:"bills"`
|
||||
}
|
||||
|
||||
// Outcomes a single bill can have. A bill is all-or-nothing: it either commits
|
||||
// with its stock movement or it leaves nothing behind.
|
||||
const (
|
||||
OfflineSaleImported = "imported"
|
||||
OfflineSaleDuplicate = "duplicate"
|
||||
OfflineSaleFailed = "failed"
|
||||
)
|
||||
|
||||
// OfflineSaleResult reports one bill's fate. Bills are independent, so a file
|
||||
// with one bad bill still imports the rest and names exactly what it skipped.
|
||||
type OfflineSaleResult struct {
|
||||
Billno string `json:"billno"`
|
||||
Status string `json:"status"`
|
||||
Orderid string `json:"orderid"`
|
||||
Orderheaderid int `json:"orderheaderid"`
|
||||
Itemcount int `json:"itemcount"`
|
||||
Amount float64 `json:"amount"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type OfflineSalesUploadResponse struct {
|
||||
Imported int `json:"imported"`
|
||||
Duplicate int `json:"duplicate"`
|
||||
Failed int `json:"failed"`
|
||||
Totalamount float64 `json:"totalamount"`
|
||||
Results []OfflineSaleResult `json:"results"`
|
||||
}
|
||||
|
||||
type TenantRevenueSummary struct {
|
||||
Tenantid int `json:"tenantid"`
|
||||
Tenantname string `json:"tenantname"`
|
||||
|
||||
@@ -327,6 +327,40 @@ type ProductLocationRef struct {
|
||||
Productid int
|
||||
}
|
||||
|
||||
// SaleTemplateRow is one line of the downloadable offline-sales spreadsheet:
|
||||
// a product actually stocked at one outlet, with the numbers the person at the
|
||||
// till needs to see before they type a sold quantity against it.
|
||||
//
|
||||
// Productid is the only field that identifies the product. It cannot be
|
||||
// productsku: across the live catalogue 6,245 products share just 93 distinct
|
||||
// sku values (one tenant has 463 products all carrying sku "1"), and 154 are
|
||||
// blank, so a sku is not a key. Productname is nearly unique per tenant but
|
||||
// not reliably ("rice" appears 6 times for one tenant), so it travels as a
|
||||
// human-readable confirmation only and is never matched on. That is why the
|
||||
// spreadsheet has to be generated from this endpoint rather than typed from
|
||||
// scratch — the productid column is filled in for the user.
|
||||
type SaleTemplateRow struct {
|
||||
Productid int `json:"productid"`
|
||||
Productname string `json:"productname"`
|
||||
Productunit string `json:"productunit"`
|
||||
Unitvalue string `json:"unitvalue"`
|
||||
Categoryname string `json:"categoryname"`
|
||||
Currentstock int `json:"currentstock"`
|
||||
Price float64 `json:"price"`
|
||||
Taxpercent float64 `json:"taxpercent"`
|
||||
}
|
||||
|
||||
// SaleTemplate is the payload the web app turns into an .xlsx workbook. The
|
||||
// tenant/location identity travels with it so the generated file records which
|
||||
// outlet it belongs to, and the upload can be checked against the file it came
|
||||
// from instead of trusting a hand-typed location.
|
||||
type SaleTemplate struct {
|
||||
Tenantid int `json:"tenantid"`
|
||||
Locationid int `json:"locationid"`
|
||||
Locationname string `json:"locationname"`
|
||||
Products []SaleTemplateRow `json:"products"`
|
||||
}
|
||||
|
||||
type ProductSubcategory struct {
|
||||
Subcatid int `json:"subcatid"`
|
||||
Categoryid int `json:"categoryid"`
|
||||
|
||||
Reference in New Issue
Block a user