The ticket-vouchers lookup previously returned the tier entitlement every time, so a donor could keep claiming free tickets by re- submitting the form. It now subtracts vouchers already consumed: remaining = entitled - used where `used` is the sum of the Vouchers column across that donor's prior ticket orders (each checkout stores what it applied). Response gains entitled/used/remaining; `vouchers` is now the remaining count the form should grant. Consumption is implicit — no counter to keep in sync — and resets by zeroing/deleting the Vouchers value on the order row in NocoDB. - nocodb: findByEmail + vouchersUsedByEmail (case-insensitive). - 8 new tests (36 total). Doc updated with the new response + reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { FakeNocoDB } from "./fakeNocodb.js";
|
|
import { COL } from "../fields.js";
|
|
|
|
/** Mirror the ticket-vouchers endpoint's remaining math. */
|
|
function remaining(entitled: number, used: number): number {
|
|
return Math.max(0, entitled - used);
|
|
}
|
|
|
|
describe("voucher consumption", () => {
|
|
it("sums vouchers used across a donor's orders", async () => {
|
|
const db = new FakeNocoDB(0);
|
|
await db.create({ [COL.email]: "donor@example.com", [COL.vouchers]: 2 });
|
|
await db.create({ [COL.email]: "donor@example.com", [COL.vouchers]: 1 });
|
|
await db.create({ [COL.email]: "someone-else@example.com", [COL.vouchers]: 2 });
|
|
await db.create({ [COL.email]: "donor@example.com", [COL.vouchers]: 0 }); // non-voucher order
|
|
expect(await db.vouchersUsedByEmail("donor@example.com")).toBe(3);
|
|
});
|
|
|
|
it("matches email case-insensitively", async () => {
|
|
const db = new FakeNocoDB(0);
|
|
await db.create({ [COL.email]: "Donor@Example.com", [COL.vouchers]: 2 });
|
|
expect(await db.vouchersUsedByEmail("donor@example.com")).toBe(2);
|
|
});
|
|
|
|
it("returns 0 used for a donor with no orders", async () => {
|
|
const db = new FakeNocoDB(0);
|
|
expect(await db.vouchersUsedByEmail("nobody@example.com")).toBe(0);
|
|
});
|
|
|
|
it("remaining = entitled - used, floored at 0", () => {
|
|
expect(remaining(2, 0)).toBe(2); // fresh 2-voucher donor
|
|
expect(remaining(2, 1)).toBe(1); // used one
|
|
expect(remaining(2, 2)).toBe(0); // used both — no more free tickets
|
|
expect(remaining(1, 2)).toBe(0); // over-consumed (edge) never goes negative
|
|
expect(remaining(0, 0)).toBe(0); // non-donor
|
|
});
|
|
});
|