mega-plant/lib/cash-float.ts
David 1ec52961ee Cash float card, Office role + Slips tab, job card PDF, manual close/delete
- Cash Floats: ledger card (opening balance, top-ups, slips), manual entries, slip photos
- Office access level; clock app Slips tab (photograph a slip, popup for supplier/cost
  code/comments/amount); only Office sees it; per-role route table in lib/clock-access
- Job cards: Machine Service PDF (with Oil block), JC numbers, manual Close job, Delete
- Hide Expenses > Vehicle Costs and Finance (Bastion) from the menu

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 15:09:57 +02:00

26 lines
1.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** The cash-float card is a ledger: balance = opening + top-ups − slips. */
// Loose on purpose: PocketBase rows arrive as RecordModel, so only `kind` and `amount` are read.
export type CashEntry = { [k: string]: any };
export const sign = (kind: unknown) => (kind === "expense" ? -1 : 1);
export function cashBalance(entries: CashEntry[]): number {
const cents = entries.reduce((s, e) => s + sign(e.kind) * Math.round(Number(e.amount || 0) * 100), 0);
return cents / 100;
}
export const hasOpening = (entries: CashEntry[]) => entries.some((e) => e.kind === "opening");
export const MAX_AMOUNT = 1_000_000;
/** Parse a rand amount typed by a person ("1 250,50" / "1250.5"). null if it is not a positive number. */
export function parseAmount(raw: unknown, allowZero = false): number | null {
const text = String(raw ?? "").replace(/\s/g, "").replace(",", ".");
if (text === "") return null;
const n = Number(text);
if (!Number.isFinite(n) || n < 0 || (n === 0 && !allowZero) || n > MAX_AMOUNT) return null;
return Math.round(n * 100) / 100;
}
export const rand = (n: number) =>
`${n < 0 ? "-" : ""}R ${Math.abs(n).toLocaleString("en-ZA", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;