Add a stock ledger, inter-branch transfers and a sync protocol that cannot jam
Stock (PRD 5.2): a branch's stock of a product is now the sum of its StockMovement rows. Every movement names the document that caused it and (type, refId) is unique, so replaying a sale, retrying a transfer or a second M-Pesa callback never moves the same stock twice. - Cash sales and confirmed M-Pesa payments deduct stock; purchases of a catalogue product add it. - POST /api/stock/transfers moves stock between two branches (owner: any two; manager: out of their own) and refuses to send more than the sending branch holds. Double submits are applied once. - POST /api/stock/adjustments records opening stock, breakage or a physical count (the ledger is corrected to the counted number). - GET /api/stock returns every branch x product with LOW/OUT flags (per-product lowStockThreshold, default 10); GET /api/stock/movements is the history. Sync (PRD 5.3/5.5): POST /api/transactions/cash now returns a result per sale - saved, duplicate or rejected - instead of silently skipping replays. The old client never learned a replayed sale had synced, so rows piled up until the batch passed the 100-sale limit and every flush failed with 400. Also: - A sale can hold several products (one Transaction per line, grouped by saleId); the old single-product payload is still accepted. - One bad sale is rejected on its own instead of failing the batch. - A queued sale is only accepted from the cashier who recorded it, and stays with the branch where it happened. - A product deactivated while offline is still recorded at its price; an invalid phone is dropped rather than losing the sale; the phone is now optional for cash sales. - The price the device showed is kept and a difference is flagged (priceMismatch) instead of being silently overwritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { connectDb } from "@/lib/server/db";
|
||||
import { isMpesaEnabled } from "@/lib/server/env";
|
||||
import { Transaction } from "@/lib/server/models/Transaction";
|
||||
import { recordSaleMovement } from "@/lib/server/stock";
|
||||
import { MpesaCallbackLog } from "@/lib/server/models/MpesaCallbackLog";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
@@ -99,6 +100,7 @@ export async function POST(req: NextRequest) {
|
||||
txn.mpesaDetails.resultDesc = resultDesc;
|
||||
txn.mpesaDetails.receiptNumber = mpesaReceiptNumber;
|
||||
await txn.save();
|
||||
await recordSaleMovement(txn);
|
||||
|
||||
await MpesaCallbackLog.create({
|
||||
sourceIp,
|
||||
|
||||
Reference in New Issue
Block a user