@bash-app/bash-common 30.275.0 → 30.276.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/groupTicketUtils.test.d.ts +2 -0
- package/dist/__tests__/groupTicketUtils.test.d.ts.map +1 -0
- package/dist/__tests__/groupTicketUtils.test.js +38 -0
- package/dist/__tests__/groupTicketUtils.test.js.map +1 -0
- package/dist/competitionIdeas.d.ts +19 -0
- package/dist/competitionIdeas.d.ts.map +1 -0
- package/dist/competitionIdeas.js +405 -0
- package/dist/competitionIdeas.js.map +1 -0
- package/dist/competitionValidation.d.ts +10 -0
- package/dist/competitionValidation.d.ts.map +1 -0
- package/dist/competitionValidation.js +29 -0
- package/dist/competitionValidation.js.map +1 -0
- package/dist/definitions.d.ts +4 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +351 -1
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +15 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/groupTicketUtils.d.ts +19 -0
- package/dist/groupTicketUtils.d.ts.map +1 -0
- package/dist/groupTicketUtils.js +74 -0
- package/dist/groupTicketUtils.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/__tests__/paymentUtils.test.js +18 -0
- package/dist/utils/__tests__/paymentUtils.test.js.map +1 -1
- package/dist/utils/__tests__/ticketListUtils.test.js +10 -0
- package/dist/utils/__tests__/ticketListUtils.test.js.map +1 -1
- package/dist/utils/groupTicketCartUtils.d.ts +12 -0
- package/dist/utils/groupTicketCartUtils.d.ts.map +1 -0
- package/dist/utils/groupTicketCartUtils.js +30 -0
- package/dist/utils/groupTicketCartUtils.js.map +1 -0
- package/dist/utils/paymentUtils.d.ts +10 -0
- package/dist/utils/paymentUtils.d.ts.map +1 -1
- package/dist/utils/paymentUtils.js +37 -1
- package/dist/utils/paymentUtils.js.map +1 -1
- package/dist/utils/ticketListUtils.d.ts.map +1 -1
- package/dist/utils/ticketListUtils.js +15 -3
- package/dist/utils/ticketListUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/competition-prize-catalog-entry-tier.sql +39 -0
- package/prisma/schema.prisma +90 -7
- package/src/__tests__/groupTicketUtils.test.ts +53 -0
- package/src/competitionIdeas.ts +429 -0
- package/src/competitionValidation.ts +42 -0
- package/src/definitions.ts +5 -0
- package/src/extendedSchemas.ts +47 -0
- package/src/groupTicketUtils.ts +101 -0
- package/src/index.ts +4 -0
- package/src/utils/__tests__/paymentUtils.test.ts +19 -0
- package/src/utils/__tests__/ticketListUtils.test.ts +12 -0
- package/src/utils/groupTicketCartUtils.ts +36 -0
- package/src/utils/paymentUtils.ts +42 -1
- package/src/utils/ticketListUtils.ts +19 -5
|
@@ -289,6 +289,25 @@ describe("PaymentUtils - Core Functions", () => {
|
|
|
289
289
|
test("calculateTotalPriceWithoutTax returns 0 for empty list", () => {
|
|
290
290
|
expect(calculateTotalPriceWithoutTax([], new Map())).toBe(0);
|
|
291
291
|
});
|
|
292
|
+
|
|
293
|
+
test("calculateTotalPriceWithoutTax uses unitPriceCents for pay-what-you-can tiers", () => {
|
|
294
|
+
const tiers = [
|
|
295
|
+
{ id: "t1", price: 2000, pricingType: "USD", isDonationTier: true },
|
|
296
|
+
] as any;
|
|
297
|
+
const ticketList = new Map([
|
|
298
|
+
[
|
|
299
|
+
"t1",
|
|
300
|
+
[
|
|
301
|
+
{
|
|
302
|
+
numberOfTickets: 2,
|
|
303
|
+
ticketDateTime: new Date(),
|
|
304
|
+
unitPriceCents: 5000,
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
],
|
|
308
|
+
]);
|
|
309
|
+
expect(calculateTotalPriceWithoutTax(tiers, ticketList)).toBe(10000);
|
|
310
|
+
});
|
|
292
311
|
});
|
|
293
312
|
|
|
294
313
|
describe("Ticket total BashPoints", () => {
|
|
@@ -29,6 +29,18 @@ describe("ticketListUtils", () => {
|
|
|
29
29
|
expect(decoded.get("tier-xyz")![0].numberOfTickets).toBe(3);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
test("round-trips unitPriceCents for pay-what-you-can tiers", () => {
|
|
33
|
+
const ticketList = new Map<string, NumberOfTicketsForDate[]>([
|
|
34
|
+
[
|
|
35
|
+
"tier-pwyc",
|
|
36
|
+
[{ numberOfTickets: 1, ticketDateTime: isoDate, unitPriceCents: 7500 }],
|
|
37
|
+
],
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const decoded = ticketListStrToTicketList(ticketListToString(ticketList));
|
|
41
|
+
expect(decoded.get("tier-pwyc")![0].unitPriceCents).toBe(7500);
|
|
42
|
+
});
|
|
43
|
+
|
|
32
44
|
test("omits tiers with zero tickets", () => {
|
|
33
45
|
const ticketList = new Map<string, NumberOfTicketsForDate[]>([
|
|
34
46
|
["tier-empty", [{ numberOfTickets: 0, ticketDateTime: isoDate }]],
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { TicketTier } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
import type { NumberOfTicketsForDate } from "../definitions.js";
|
|
4
|
+
import { isGroupTicketTier } from "../groupTicketUtils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates cart quantities for group package tiers: qty = packages, not seats.
|
|
8
|
+
*/
|
|
9
|
+
export function validateGroupTicketCartForTiers(
|
|
10
|
+
ticketTiers: TicketTier[],
|
|
11
|
+
ticketList: Map<string, NumberOfTicketsForDate[]>
|
|
12
|
+
): { ok: true } | { ok: false; message: string } {
|
|
13
|
+
for (const tier of ticketTiers) {
|
|
14
|
+
if (!isGroupTicketTier(tier)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const rows = ticketList.get(tier.id);
|
|
18
|
+
if (!rows?.length) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const packageQty = rows.reduce((s, r) => s + (r.numberOfTickets ?? 0), 0);
|
|
22
|
+
if (packageQty < 1) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
message: `${tier.title} is a group package — select one package to buy.`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (packageQty > 1) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
message: `${tier.title} is one package per checkout. Buy one table/package at a time.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { ok: true };
|
|
36
|
+
}
|
|
@@ -44,6 +44,46 @@ export function calculateDiscountFromPromoCode(
|
|
|
44
44
|
return 0;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Unit price in cents for checkout (supports pay-what-you-can tiers).
|
|
49
|
+
*/
|
|
50
|
+
export function effectiveTicketUnitPriceCents(
|
|
51
|
+
tier: TicketTier,
|
|
52
|
+
row: NumberOfTicketsForDate
|
|
53
|
+
): number {
|
|
54
|
+
if (tier.isDonationTier === true && row.unitPriceCents != null) {
|
|
55
|
+
return Math.max(tier.price, row.unitPriceCents);
|
|
56
|
+
}
|
|
57
|
+
return tier.price;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function validatePayWhatYouCanTicketList(
|
|
61
|
+
ticketTiers: TicketTier[],
|
|
62
|
+
ticketList: Map<string, NumberOfTicketsForDate[]>
|
|
63
|
+
): { ok: true } | { ok: false; message: string } {
|
|
64
|
+
for (const tier of ticketTiers) {
|
|
65
|
+
if (tier.isDonationTier !== true) continue;
|
|
66
|
+
const rows = ticketList.get(tier.id);
|
|
67
|
+
if (!rows?.length) continue;
|
|
68
|
+
for (const row of rows) {
|
|
69
|
+
if ((row.numberOfTickets ?? 0) <= 0) continue;
|
|
70
|
+
if (row.unitPriceCents == null) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
message: `Enter an amount for ${tier.title} (minimum $${convertCentsToDollars(tier.price).toFixed(2)}).`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (row.unitPriceCents < tier.price) {
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
message: `${tier.title} requires at least $${convertCentsToDollars(tier.price).toFixed(2)} per ticket.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { ok: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
47
87
|
/**
|
|
48
88
|
* Returns the total price in USD cents based on a map where the keys are the ticketTierIds
|
|
49
89
|
* Only counts USD-priced tickets. BashPoints tickets are excluded (they have price=0).
|
|
@@ -60,7 +100,8 @@ export function calculateTotalPriceWithoutTax(
|
|
|
60
100
|
if (tier.pricingType !== "BASHPOINTS") {
|
|
61
101
|
const ticketListNumAndDate = ticketList.get(tier.id);
|
|
62
102
|
ticketListNumAndDate?.forEach((numAndDate: NumberOfTicketsForDate) => {
|
|
63
|
-
|
|
103
|
+
const unitCents = effectiveTicketUnitPriceCents(tier, numAndDate);
|
|
104
|
+
total += unitCents * (numAndDate.numberOfTickets ?? 0);
|
|
64
105
|
});
|
|
65
106
|
}
|
|
66
107
|
});
|
|
@@ -80,7 +80,12 @@ export function ticketListToString(
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
ticketArgs.push(
|
|
83
|
-
`${ticketNumAndDates.numberOfTickets}${URL_PARAMS_TICKETS_DATE_DELIM}${dateTimeStr}
|
|
83
|
+
`${ticketNumAndDates.numberOfTickets}${URL_PARAMS_TICKETS_DATE_DELIM}${dateTimeStr}${
|
|
84
|
+
ticketNumAndDates.unitPriceCents != null &&
|
|
85
|
+
ticketNumAndDates.unitPriceCents > 0
|
|
86
|
+
? `${URL_PARAMS_TICKETS_DATE_DELIM}${ticketNumAndDates.unitPriceCents}`
|
|
87
|
+
: ""
|
|
88
|
+
}`
|
|
84
89
|
);
|
|
85
90
|
}
|
|
86
91
|
}
|
|
@@ -147,10 +152,11 @@ export function ticketListStrToTicketList(
|
|
|
147
152
|
.filter((ticketNumAndDateStr): boolean => !!ticketNumAndDateStr);
|
|
148
153
|
|
|
149
154
|
for (const ticketNumAndDateStr of ticketNumAndDates) {
|
|
150
|
-
// [numberOfTickets];;[date]
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
// [numberOfTickets];;[date];;[unitPriceCents?]
|
|
156
|
+
const segments = ticketNumAndDateStr.split(URL_PARAMS_TICKETS_DATE_DELIM);
|
|
157
|
+
const numberOfTickets = segments[0];
|
|
158
|
+
const ticketDateTimeStr = segments[1];
|
|
159
|
+
const unitPriceCentsRaw = segments[2];
|
|
154
160
|
const ticketDateTime = DateTime.fromISO(ticketDateTimeStr);
|
|
155
161
|
let ticketDateTimeFormattedStr: string | undefined = undefined;
|
|
156
162
|
|
|
@@ -158,9 +164,17 @@ export function ticketListStrToTicketList(
|
|
|
158
164
|
ticketDateTimeFormattedStr = ticketDateTime.toISO() ?? undefined;
|
|
159
165
|
}
|
|
160
166
|
|
|
167
|
+
const unitPriceCents =
|
|
168
|
+
unitPriceCentsRaw != null && unitPriceCentsRaw !== ""
|
|
169
|
+
? parseInt(unitPriceCentsRaw, 10)
|
|
170
|
+
: undefined;
|
|
171
|
+
|
|
161
172
|
ticketNumAndDatesArr.push({
|
|
162
173
|
numberOfTickets: parseInt(numberOfTickets),
|
|
163
174
|
ticketDateTime: ticketDateTimeFormattedStr,
|
|
175
|
+
...(unitPriceCents != null && !Number.isNaN(unitPriceCents)
|
|
176
|
+
? { unitPriceCents }
|
|
177
|
+
: {}),
|
|
164
178
|
});
|
|
165
179
|
}
|
|
166
180
|
ticketList.set(ticketTierId, ticketNumAndDatesArr);
|