feat(subs): print an advisory "out-of-window" slip at early entry
A subscriber entering outside their plan's window owes a deferred charge, but
nothing printed — they had no paper proof a fee was pending. Print a best-effort
ADVISORY slip at entry ("PARKIM — JASHTË ORARIT"): holder, entry time, "entered
out-of-window (window opens HH:MM)", and the key line "⚠ fee computed at exit"
+ the occurrence number. It is NOT a payable ticket and carries NO amount — the
total is computed at the booth on settlement, combining early-entry AND any
late-exit time into one number (windowOwedBetween over the whole stay).
Best-effort like the Z-report / subscription card: printed AFTER the barrier
opens and fully swallowed, so a missing/failed printer never blocks entry. New
printWindowChargeNotice (booth-print.ts) via the generic printReport; wired into
the subscription entry flow when an out-of-window entry charge applies.
(The "both charges at the booth" requirement was already satisfied by the
windowOwedBetween fix — verified: early-entry + late-exit minutes combine in one
calc at lookup/exit. This commit only adds the entry paper trail.) Build+lint 12/12.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { eq, ledgerEvents, siteConfig, type Db } from "@parking/db";
|
||||
import {
|
||||
formatStampSq,
|
||||
printWithFailover,
|
||||
registry,
|
||||
type PrinterDevice,
|
||||
@@ -154,3 +155,38 @@ export async function printSubscriptionCard(
|
||||
logger.info(`subscription card ${card.code} printed on ${printedBy}`);
|
||||
return printedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an ADVISORY "out-of-window" slip when a subscriber enters (or exits) outside
|
||||
* their plan's allowed hours. It is NOT a payable ticket and carries NO final amount —
|
||||
* the total is computed at the booth on settlement (early-entry AND any late-exit time
|
||||
* combined). It just gives the subscriber paper proof that a fee is pending against this
|
||||
* occurrence. Albanian (like every customer-facing slip — see i18n.md). Best-effort:
|
||||
* the caller swallows failures so a missing printer never blocks the barrier.
|
||||
*/
|
||||
export async function printWindowChargeNotice(
|
||||
db: Db,
|
||||
notice: { occurrenceId: string; holderName?: string | null; at: string; windowOpensMin?: number; edge: "entry" | "exit" },
|
||||
logger: FastifyBaseLogger,
|
||||
): Promise<string> {
|
||||
const printers = loadPrinters(db);
|
||||
const hhmm = (m?: number) =>
|
||||
m == null ? "" : `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
|
||||
const lines = [
|
||||
`Abonent: ${notice.holderName || "-"}`,
|
||||
`${notice.edge === "entry" ? "Hyrje" : "Dalje"}: ${formatStampSq(notice.at)}`,
|
||||
notice.edge === "entry"
|
||||
? `Ka hyrë jashtë orarit${notice.windowOpensMin != null ? ` (orari hap ${hhmm(notice.windowOpensMin)})` : ""}`
|
||||
: "Ka dalë jashtë orarit",
|
||||
"",
|
||||
"⚠ Detyrim do të llogaritet në dalje",
|
||||
" (paguhet në kabinë para se të dilni)",
|
||||
"",
|
||||
`Nr: ${notice.occurrenceId}`,
|
||||
];
|
||||
const printedBy = await printWithFailover(printers, "booth-receipt", (d: PrinterDevice) =>
|
||||
d.printReport({ title: "PARKIM — JASHTË ORARIT", lines }),
|
||||
);
|
||||
logger.info(`out-of-window notice printed for ${notice.occurrenceId} on ${printedBy}`);
|
||||
return printedBy;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user