feat: add ZAR Paid field to JEI_payment_batches (optional)
This commit is contained in:
parent
ca1c6cfedd
commit
551baeeb80
6 changed files with 64 additions and 1 deletions
|
|
@ -22,6 +22,10 @@ export async function POST(req: NextRequest) {
|
|||
const paymentDate = String(form.get("payment_date") || "").trim();
|
||||
const invoiceIdsRaw = String(form.get("invoice_ids") || "");
|
||||
const popFile = form.get("pop_file");
|
||||
// Optional ZAR amount actually paid for this batch. Empty / missing -> 0.
|
||||
// PB "number" field accepts numeric strings or numbers via the SDK.
|
||||
const zarPaidRaw = String(form.get("zar_paid") || "").trim();
|
||||
const zarPaid = zarPaidRaw === "" ? 0 : Number(zarPaidRaw);
|
||||
|
||||
// 1. invoice_ids
|
||||
let invoiceIds: string[];
|
||||
|
|
@ -115,6 +119,7 @@ export async function POST(req: NextRequest) {
|
|||
batch_ref: batchRef,
|
||||
payment_date: paymentDate,
|
||||
pop_file: popFile,
|
||||
zar_paid: Number.isFinite(zarPaid) ? zarPaid : 0,
|
||||
});
|
||||
batchId = created.id;
|
||||
} catch (e: any) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export interface PaymentBatch {
|
|||
payment_date: string; // ISO date "YYYY-MM-DD"
|
||||
pop_file: string; // PB file URL (relative path)
|
||||
notes: string;
|
||||
zar_paid?: number; // optional ZAR amount actually paid; 0/omitted when paid from a £ bank account
|
||||
created: string;
|
||||
updated: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type Invoice = {
|
|||
};
|
||||
|
||||
type BatchWithInvoices = {
|
||||
batch: { id: string; batch_ref: string; payment_date: string; pop_file: string };
|
||||
batch: { id: string; batch_ref: string; payment_date: string; pop_file: string; zar_paid?: number };
|
||||
invoices: Invoice[];
|
||||
popUrl: string | null;
|
||||
};
|
||||
|
|
@ -99,6 +99,9 @@ export default function RemittanceClient({ batches }: { batches: BatchWithInvoic
|
|||
<div className="text-xs text-slate-500">
|
||||
{fmtDate(batch.payment_date)} · {invoices.length} invoice{invoices.length === 1 ? "" : "s"} ·{" "}
|
||||
<span className="tabular-nums">{fmtGBP(total)}</span>
|
||||
{batch.zar_paid && batch.zar_paid > 0 ? (
|
||||
<span className="ml-1">· ZAR Paid <span className="tabular-nums font-semibold text-slate-700">R{batch.zar_paid.toLocaleString("en-ZA", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span></span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default async function RemittancePage() {
|
|||
try {
|
||||
const r = await pb.collection("JEI_payment_batches").getList(1, 500, {
|
||||
sort: "-payment_date",
|
||||
fields: "id,batch_ref,payment_date,pop_file,zar_paid",
|
||||
});
|
||||
batches = r.items;
|
||||
} catch (e: any) {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export default function UnpaidClient({ initialRecords }: { initialRecords: Invoi
|
|||
const [batchRef, setBatchRef] = useState(nextBatchRefSuggestion());
|
||||
const [paymentDate, setPaymentDate] = useState(() => new Date().toISOString().slice(0, 10));
|
||||
const [popFile, setPopFile] = useState<File | null>(null);
|
||||
const [zarPaid, setZarPaid] = useState<string>(""); // optional ZAR amount actually paid; "" means omitted
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
|
|
@ -108,6 +109,11 @@ export default function UnpaidClient({ initialRecords }: { initialRecords: Invoi
|
|||
fd.set("payment_date", paymentDate);
|
||||
fd.set("pop_file", popFile);
|
||||
fd.set("invoice_ids", JSON.stringify(Array.from(selected)));
|
||||
// Optional ZAR paid: only set when the user typed something, so the API
|
||||
// treats missing as "no ZAR amount recorded" rather than as 0.
|
||||
if (zarPaid.trim() !== "") {
|
||||
fd.set("zar_paid", zarPaid.trim());
|
||||
}
|
||||
const resp = await fetch("/api/payments/create", { method: "POST", body: fd });
|
||||
const json = await resp.json().catch(() => ({}));
|
||||
if (!resp.ok) {
|
||||
|
|
@ -241,6 +247,19 @@ export default function UnpaidClient({ initialRecords }: { initialRecords: Invoi
|
|||
className="w-full border rounded px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-slate-500">ZAR Paid <span className="text-slate-400">(optional)</span></label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
inputMode="decimal"
|
||||
value={zarPaid}
|
||||
onChange={(e) => setZarPaid(e.target.value)}
|
||||
placeholder="e.g. 12500.00"
|
||||
className="w-full border rounded px-3 py-2 tabular-nums"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-50 border border-red-200 text-red-800 rounded p-3 text-sm">
|
||||
|
|
|
|||
|
|
@ -115,6 +115,16 @@ def payment_batches_schema():
|
|||
"max": 1000,
|
||||
"pattern": "",
|
||||
},
|
||||
{
|
||||
# Optional ZAR amount actually paid for this batch. Defaults to 0
|
||||
# if not entered (e.g. paid from a £ UK bank account).
|
||||
"name": "zar_paid",
|
||||
"type": "number",
|
||||
"required": False,
|
||||
"min": 0,
|
||||
"max": None,
|
||||
"noDecimal": False,
|
||||
},
|
||||
],
|
||||
**SUPERUSER_RULES,
|
||||
}
|
||||
|
|
@ -182,6 +192,30 @@ def main():
|
|||
sys.exit(f"ERROR: failed to add payment_batch field: {err}")
|
||||
print(f"ADDED: JEI_invoices.payment_batch relation -> {batches_id}")
|
||||
|
||||
# 3b. Ensure JEI_payment_batches has the zar_paid field (added 2026-09-18).
|
||||
batches = pb_get(f"/api/collections/{batches_id}")
|
||||
batches_field_names = {f.get("name") for f in batches.get("fields", [])}
|
||||
if "zar_paid" in batches_field_names:
|
||||
print("EXISTS: JEI_payment_batches.zar_paid field")
|
||||
else:
|
||||
new_zp = {
|
||||
"name": "zar_paid",
|
||||
"type": "number",
|
||||
"required": False,
|
||||
"min": 0,
|
||||
"max": None,
|
||||
"noDecimal": False,
|
||||
}
|
||||
patched_fields = list(batches.get("fields", [])) + [new_zp]
|
||||
clean_fields = [{k: v for k, v in f.items() if k != "options"} for f in patched_fields]
|
||||
resp, err = pb_patch(
|
||||
f"/api/collections/{batches_id}",
|
||||
{"fields": clean_fields},
|
||||
)
|
||||
if err is not None:
|
||||
sys.exit(f"ERROR: failed to add zar_paid field: {err}")
|
||||
print(f"ADDED: JEI_payment_batches.zar_paid number field")
|
||||
|
||||
# 4. Wake-up round-trip: PATCH each collection's rule fields. Even though
|
||||
# the values are already empty strings, the round-trip activates the
|
||||
# collection's rule machinery in PB v0.39.8.
|
||||
|
|
|
|||
Loading…
Reference in a new issue