new api for offline sales

This commit is contained in:
2026-07-30 17:25:09 +05:30
parent a11c4843ca
commit 583cd89063
10 changed files with 887 additions and 10 deletions

View File

@@ -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"`