@absolutejs/commerce 0.5.0-beta.0 → 0.6.0-beta.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/core/analytics.d.ts +41 -0
- package/dist/drizzle/index.d.ts +192 -0
- package/dist/drizzle/index.js +35 -0
- package/dist/drizzle/queries.d.ts +25 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type AnalyticsOrderLine = {
|
|
2
|
+
product: string;
|
|
3
|
+
quantity: number;
|
|
4
|
+
amountTotal: number;
|
|
5
|
+
};
|
|
6
|
+
export type AnalyticsOrder = {
|
|
7
|
+
amount_total: number | null;
|
|
8
|
+
created_at: Date | string;
|
|
9
|
+
status: string;
|
|
10
|
+
customer_email?: string | null;
|
|
11
|
+
line_items?: AnalyticsOrderLine[] | null;
|
|
12
|
+
};
|
|
13
|
+
export type DayRevenue = {
|
|
14
|
+
date: string;
|
|
15
|
+
revenueCents: number;
|
|
16
|
+
orders: number;
|
|
17
|
+
};
|
|
18
|
+
export type ProductRevenue = {
|
|
19
|
+
product: string;
|
|
20
|
+
quantity: number;
|
|
21
|
+
revenueCents: number;
|
|
22
|
+
};
|
|
23
|
+
export type SalesSummary = {
|
|
24
|
+
paidOrders: number;
|
|
25
|
+
revenueCents: number;
|
|
26
|
+
averageOrderCents: number;
|
|
27
|
+
statusCounts: Record<string, number>;
|
|
28
|
+
revenueByDay: DayRevenue[];
|
|
29
|
+
topProducts: ProductRevenue[];
|
|
30
|
+
};
|
|
31
|
+
/** Aggregate revenue, AOV, status mix, revenue-by-day, and top products. */
|
|
32
|
+
export declare const salesSummary: (orders: AnalyticsOrder[]) => SalesSummary;
|
|
33
|
+
export type CustomerSummary = {
|
|
34
|
+
email: string;
|
|
35
|
+
orders: number;
|
|
36
|
+
totalSpentCents: number;
|
|
37
|
+
firstOrderAt: string;
|
|
38
|
+
lastOrderAt: string;
|
|
39
|
+
};
|
|
40
|
+
/** Per-customer rollup (realized-revenue orders), newest activity first. */
|
|
41
|
+
export declare const customerSummaries: (orders: AnalyticsOrder[]) => CustomerSummary[];
|
package/dist/drizzle/index.d.ts
CHANGED
|
@@ -1148,6 +1148,102 @@ export declare const commerceAbandonedCarts: import("drizzle-orm/pg-core").PgTab
|
|
|
1148
1148
|
};
|
|
1149
1149
|
dialect: "pg";
|
|
1150
1150
|
}>;
|
|
1151
|
+
export declare const commerceGiftCards: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1152
|
+
name: "gift_cards";
|
|
1153
|
+
schema: undefined;
|
|
1154
|
+
columns: {
|
|
1155
|
+
balance_cents: import("drizzle-orm/pg-core").PgColumn<{
|
|
1156
|
+
name: "balance_cents";
|
|
1157
|
+
tableName: "gift_cards";
|
|
1158
|
+
dataType: "number";
|
|
1159
|
+
columnType: "PgInteger";
|
|
1160
|
+
data: number;
|
|
1161
|
+
driverParam: string | number;
|
|
1162
|
+
notNull: true;
|
|
1163
|
+
hasDefault: false;
|
|
1164
|
+
isPrimaryKey: false;
|
|
1165
|
+
isAutoincrement: false;
|
|
1166
|
+
hasRuntimeDefault: false;
|
|
1167
|
+
enumValues: undefined;
|
|
1168
|
+
baseColumn: never;
|
|
1169
|
+
identity: undefined;
|
|
1170
|
+
generated: undefined;
|
|
1171
|
+
}, {}, {}>;
|
|
1172
|
+
code: import("drizzle-orm/pg-core").PgColumn<{
|
|
1173
|
+
name: "code";
|
|
1174
|
+
tableName: "gift_cards";
|
|
1175
|
+
dataType: "string";
|
|
1176
|
+
columnType: "PgVarchar";
|
|
1177
|
+
data: string;
|
|
1178
|
+
driverParam: string;
|
|
1179
|
+
notNull: true;
|
|
1180
|
+
hasDefault: false;
|
|
1181
|
+
isPrimaryKey: true;
|
|
1182
|
+
isAutoincrement: false;
|
|
1183
|
+
hasRuntimeDefault: false;
|
|
1184
|
+
enumValues: [string, ...string[]];
|
|
1185
|
+
baseColumn: never;
|
|
1186
|
+
identity: undefined;
|
|
1187
|
+
generated: undefined;
|
|
1188
|
+
}, {}, {
|
|
1189
|
+
length: 40;
|
|
1190
|
+
}>;
|
|
1191
|
+
created_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
1192
|
+
name: "created_at";
|
|
1193
|
+
tableName: "gift_cards";
|
|
1194
|
+
dataType: "date";
|
|
1195
|
+
columnType: "PgTimestamp";
|
|
1196
|
+
data: Date;
|
|
1197
|
+
driverParam: string;
|
|
1198
|
+
notNull: true;
|
|
1199
|
+
hasDefault: true;
|
|
1200
|
+
isPrimaryKey: false;
|
|
1201
|
+
isAutoincrement: false;
|
|
1202
|
+
hasRuntimeDefault: false;
|
|
1203
|
+
enumValues: undefined;
|
|
1204
|
+
baseColumn: never;
|
|
1205
|
+
identity: undefined;
|
|
1206
|
+
generated: undefined;
|
|
1207
|
+
}, {}, {}>;
|
|
1208
|
+
initial_cents: import("drizzle-orm/pg-core").PgColumn<{
|
|
1209
|
+
name: "initial_cents";
|
|
1210
|
+
tableName: "gift_cards";
|
|
1211
|
+
dataType: "number";
|
|
1212
|
+
columnType: "PgInteger";
|
|
1213
|
+
data: number;
|
|
1214
|
+
driverParam: string | number;
|
|
1215
|
+
notNull: true;
|
|
1216
|
+
hasDefault: false;
|
|
1217
|
+
isPrimaryKey: false;
|
|
1218
|
+
isAutoincrement: false;
|
|
1219
|
+
hasRuntimeDefault: false;
|
|
1220
|
+
enumValues: undefined;
|
|
1221
|
+
baseColumn: never;
|
|
1222
|
+
identity: undefined;
|
|
1223
|
+
generated: undefined;
|
|
1224
|
+
}, {}, {}>;
|
|
1225
|
+
recipient_email: import("drizzle-orm/pg-core").PgColumn<{
|
|
1226
|
+
name: "recipient_email";
|
|
1227
|
+
tableName: "gift_cards";
|
|
1228
|
+
dataType: "string";
|
|
1229
|
+
columnType: "PgVarchar";
|
|
1230
|
+
data: string;
|
|
1231
|
+
driverParam: string;
|
|
1232
|
+
notNull: false;
|
|
1233
|
+
hasDefault: false;
|
|
1234
|
+
isPrimaryKey: false;
|
|
1235
|
+
isAutoincrement: false;
|
|
1236
|
+
hasRuntimeDefault: false;
|
|
1237
|
+
enumValues: [string, ...string[]];
|
|
1238
|
+
baseColumn: never;
|
|
1239
|
+
identity: undefined;
|
|
1240
|
+
generated: undefined;
|
|
1241
|
+
}, {}, {
|
|
1242
|
+
length: 320;
|
|
1243
|
+
}>;
|
|
1244
|
+
};
|
|
1245
|
+
dialect: "pg";
|
|
1246
|
+
}>;
|
|
1151
1247
|
export declare const commerceFavorites: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1152
1248
|
name: "favorites";
|
|
1153
1249
|
schema: undefined;
|
|
@@ -2125,6 +2221,102 @@ export declare const commerceDrizzleSchema: {
|
|
|
2125
2221
|
};
|
|
2126
2222
|
dialect: "pg";
|
|
2127
2223
|
}>;
|
|
2224
|
+
giftCards: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2225
|
+
name: "gift_cards";
|
|
2226
|
+
schema: undefined;
|
|
2227
|
+
columns: {
|
|
2228
|
+
balance_cents: import("drizzle-orm/pg-core").PgColumn<{
|
|
2229
|
+
name: "balance_cents";
|
|
2230
|
+
tableName: "gift_cards";
|
|
2231
|
+
dataType: "number";
|
|
2232
|
+
columnType: "PgInteger";
|
|
2233
|
+
data: number;
|
|
2234
|
+
driverParam: string | number;
|
|
2235
|
+
notNull: true;
|
|
2236
|
+
hasDefault: false;
|
|
2237
|
+
isPrimaryKey: false;
|
|
2238
|
+
isAutoincrement: false;
|
|
2239
|
+
hasRuntimeDefault: false;
|
|
2240
|
+
enumValues: undefined;
|
|
2241
|
+
baseColumn: never;
|
|
2242
|
+
identity: undefined;
|
|
2243
|
+
generated: undefined;
|
|
2244
|
+
}, {}, {}>;
|
|
2245
|
+
code: import("drizzle-orm/pg-core").PgColumn<{
|
|
2246
|
+
name: "code";
|
|
2247
|
+
tableName: "gift_cards";
|
|
2248
|
+
dataType: "string";
|
|
2249
|
+
columnType: "PgVarchar";
|
|
2250
|
+
data: string;
|
|
2251
|
+
driverParam: string;
|
|
2252
|
+
notNull: true;
|
|
2253
|
+
hasDefault: false;
|
|
2254
|
+
isPrimaryKey: true;
|
|
2255
|
+
isAutoincrement: false;
|
|
2256
|
+
hasRuntimeDefault: false;
|
|
2257
|
+
enumValues: [string, ...string[]];
|
|
2258
|
+
baseColumn: never;
|
|
2259
|
+
identity: undefined;
|
|
2260
|
+
generated: undefined;
|
|
2261
|
+
}, {}, {
|
|
2262
|
+
length: 40;
|
|
2263
|
+
}>;
|
|
2264
|
+
created_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
2265
|
+
name: "created_at";
|
|
2266
|
+
tableName: "gift_cards";
|
|
2267
|
+
dataType: "date";
|
|
2268
|
+
columnType: "PgTimestamp";
|
|
2269
|
+
data: Date;
|
|
2270
|
+
driverParam: string;
|
|
2271
|
+
notNull: true;
|
|
2272
|
+
hasDefault: true;
|
|
2273
|
+
isPrimaryKey: false;
|
|
2274
|
+
isAutoincrement: false;
|
|
2275
|
+
hasRuntimeDefault: false;
|
|
2276
|
+
enumValues: undefined;
|
|
2277
|
+
baseColumn: never;
|
|
2278
|
+
identity: undefined;
|
|
2279
|
+
generated: undefined;
|
|
2280
|
+
}, {}, {}>;
|
|
2281
|
+
initial_cents: import("drizzle-orm/pg-core").PgColumn<{
|
|
2282
|
+
name: "initial_cents";
|
|
2283
|
+
tableName: "gift_cards";
|
|
2284
|
+
dataType: "number";
|
|
2285
|
+
columnType: "PgInteger";
|
|
2286
|
+
data: number;
|
|
2287
|
+
driverParam: string | number;
|
|
2288
|
+
notNull: true;
|
|
2289
|
+
hasDefault: false;
|
|
2290
|
+
isPrimaryKey: false;
|
|
2291
|
+
isAutoincrement: false;
|
|
2292
|
+
hasRuntimeDefault: false;
|
|
2293
|
+
enumValues: undefined;
|
|
2294
|
+
baseColumn: never;
|
|
2295
|
+
identity: undefined;
|
|
2296
|
+
generated: undefined;
|
|
2297
|
+
}, {}, {}>;
|
|
2298
|
+
recipient_email: import("drizzle-orm/pg-core").PgColumn<{
|
|
2299
|
+
name: "recipient_email";
|
|
2300
|
+
tableName: "gift_cards";
|
|
2301
|
+
dataType: "string";
|
|
2302
|
+
columnType: "PgVarchar";
|
|
2303
|
+
data: string;
|
|
2304
|
+
driverParam: string;
|
|
2305
|
+
notNull: false;
|
|
2306
|
+
hasDefault: false;
|
|
2307
|
+
isPrimaryKey: false;
|
|
2308
|
+
isAutoincrement: false;
|
|
2309
|
+
hasRuntimeDefault: false;
|
|
2310
|
+
enumValues: [string, ...string[]];
|
|
2311
|
+
baseColumn: never;
|
|
2312
|
+
identity: undefined;
|
|
2313
|
+
generated: undefined;
|
|
2314
|
+
}, {}, {
|
|
2315
|
+
length: 320;
|
|
2316
|
+
}>;
|
|
2317
|
+
};
|
|
2318
|
+
dialect: "pg";
|
|
2319
|
+
}>;
|
|
2128
2320
|
orders: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
2129
2321
|
name: "orders";
|
|
2130
2322
|
schema: undefined;
|
package/dist/drizzle/index.js
CHANGED
|
@@ -57,6 +57,29 @@ var saveDesign = async (db, design) => {
|
|
|
57
57
|
const [created] = await db.insert(commerceSavedDesigns).values(design).returning();
|
|
58
58
|
return created;
|
|
59
59
|
};
|
|
60
|
+
var issueGiftCard = async (db, input) => {
|
|
61
|
+
const [created] = await db.insert(commerceGiftCards).values({
|
|
62
|
+
balance_cents: input.cents,
|
|
63
|
+
code: input.code.trim().toUpperCase(),
|
|
64
|
+
initial_cents: input.cents,
|
|
65
|
+
recipient_email: input.recipientEmail ?? null
|
|
66
|
+
}).returning();
|
|
67
|
+
return created;
|
|
68
|
+
};
|
|
69
|
+
var getGiftCard = async (db, code) => {
|
|
70
|
+
const [card] = await db.select().from(commerceGiftCards).where(eq(commerceGiftCards.code, code.trim().toUpperCase())).limit(1);
|
|
71
|
+
return card ?? null;
|
|
72
|
+
};
|
|
73
|
+
var redeemGiftCard = async (db, code, amountCents) => {
|
|
74
|
+
const card = await getGiftCard(db, code);
|
|
75
|
+
if (!card)
|
|
76
|
+
return null;
|
|
77
|
+
const applied = Math.max(0, Math.min(card.balance_cents, amountCents));
|
|
78
|
+
const balance = card.balance_cents - applied;
|
|
79
|
+
if (applied > 0)
|
|
80
|
+
await db.update(commerceGiftCards).set({ balance_cents: balance }).where(eq(commerceGiftCards.code, card.code));
|
|
81
|
+
return { appliedCents: applied, balanceCents: balance };
|
|
82
|
+
};
|
|
60
83
|
var createReturnRequest = async (db, request) => {
|
|
61
84
|
const [created] = await db.insert(commerceReturnRequests).values(request).returning();
|
|
62
85
|
return created;
|
|
@@ -164,6 +187,13 @@ var commerceAbandonedCarts = pgTable("abandoned_carts", {
|
|
|
164
187
|
recovered: boolean().notNull().default(false),
|
|
165
188
|
reminded: boolean().notNull().default(false)
|
|
166
189
|
});
|
|
190
|
+
var commerceGiftCards = pgTable("gift_cards", {
|
|
191
|
+
balance_cents: integer().notNull(),
|
|
192
|
+
code: varchar({ length: 40 }).primaryKey(),
|
|
193
|
+
created_at: timestamp().notNull().defaultNow(),
|
|
194
|
+
initial_cents: integer().notNull(),
|
|
195
|
+
recipient_email: varchar({ length: 320 })
|
|
196
|
+
});
|
|
167
197
|
var commerceFavorites = pgTable("favorites", {
|
|
168
198
|
created_at: timestamp().notNull().defaultNow(),
|
|
169
199
|
customer_email: varchar({ length: 320 }).notNull(),
|
|
@@ -207,6 +237,7 @@ var commerceDrizzleSchema = {
|
|
|
207
237
|
designs: commerceDesigns,
|
|
208
238
|
discounts: commerceDiscounts,
|
|
209
239
|
favorites: commerceFavorites,
|
|
240
|
+
giftCards: commerceGiftCards,
|
|
210
241
|
orders: commerceOrders,
|
|
211
242
|
quotes: commerceQuotes,
|
|
212
243
|
returnRequests: commerceReturnRequests,
|
|
@@ -220,6 +251,7 @@ export {
|
|
|
220
251
|
setReviewStatus,
|
|
221
252
|
setReturnStatus,
|
|
222
253
|
saveDesign,
|
|
254
|
+
redeemGiftCard,
|
|
223
255
|
recordAbandonedCart,
|
|
224
256
|
ratingSummaries,
|
|
225
257
|
markReminded,
|
|
@@ -230,6 +262,8 @@ export {
|
|
|
230
262
|
listFavorites,
|
|
231
263
|
listApprovedReviews,
|
|
232
264
|
listAllReviews,
|
|
265
|
+
issueGiftCard,
|
|
266
|
+
getGiftCard,
|
|
233
267
|
findOrderForTracking,
|
|
234
268
|
dueForReminder,
|
|
235
269
|
deleteSavedDesign,
|
|
@@ -241,6 +275,7 @@ export {
|
|
|
241
275
|
commerceReturnRequests,
|
|
242
276
|
commerceQuotes,
|
|
243
277
|
commerceOrders,
|
|
278
|
+
commerceGiftCards,
|
|
244
279
|
commerceFavorites,
|
|
245
280
|
commerceDrizzleSchema,
|
|
246
281
|
commerceDiscounts,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PgDatabase } from 'drizzle-orm/pg-core';
|
|
2
|
-
import { commerceFavorites, commerceReturnRequests, commerceReviews, commerceSavedDesigns } from './index';
|
|
2
|
+
import { commerceFavorites, commerceGiftCards, commerceReturnRequests, commerceReviews, commerceSavedDesigns } from './index';
|
|
3
3
|
export type CommerceDb = PgDatabase<any, any, any>;
|
|
4
4
|
export type Review = typeof commerceReviews.$inferSelect;
|
|
5
5
|
export type NewReview = typeof commerceReviews.$inferInsert;
|
|
@@ -8,6 +8,11 @@ export type SavedDesign = typeof commerceSavedDesigns.$inferSelect;
|
|
|
8
8
|
export type NewSavedDesign = typeof commerceSavedDesigns.$inferInsert;
|
|
9
9
|
export type ReturnRequest = typeof commerceReturnRequests.$inferSelect;
|
|
10
10
|
export type NewReturnRequest = typeof commerceReturnRequests.$inferInsert;
|
|
11
|
+
export type GiftCard = typeof commerceGiftCards.$inferSelect;
|
|
12
|
+
export type GiftCardRedemption = {
|
|
13
|
+
appliedCents: number;
|
|
14
|
+
balanceCents: number;
|
|
15
|
+
};
|
|
11
16
|
export type RatingSummary = {
|
|
12
17
|
productId: string;
|
|
13
18
|
count: number;
|
|
@@ -962,6 +967,25 @@ export declare const saveDesign: (db: CommerceDb, design: NewSavedDesign) => Pro
|
|
|
962
967
|
label: string | null;
|
|
963
968
|
snapshot: Record<string, unknown>;
|
|
964
969
|
} | undefined>;
|
|
970
|
+
export declare const issueGiftCard: (db: CommerceDb, input: {
|
|
971
|
+
code: string;
|
|
972
|
+
cents: number;
|
|
973
|
+
recipientEmail?: string | null;
|
|
974
|
+
}) => Promise<{
|
|
975
|
+
created_at: Date;
|
|
976
|
+
balance_cents: number;
|
|
977
|
+
code: string;
|
|
978
|
+
initial_cents: number;
|
|
979
|
+
recipient_email: string | null;
|
|
980
|
+
} | undefined>;
|
|
981
|
+
export declare const getGiftCard: (db: CommerceDb, code: string) => Promise<{
|
|
982
|
+
balance_cents: number;
|
|
983
|
+
code: string;
|
|
984
|
+
created_at: Date;
|
|
985
|
+
initial_cents: number;
|
|
986
|
+
recipient_email: string | null;
|
|
987
|
+
} | null>;
|
|
988
|
+
export declare const redeemGiftCard: (db: CommerceDb, code: string, amountCents: number) => Promise<GiftCardRedemption | null>;
|
|
965
989
|
export declare const createReturnRequest: (db: CommerceDb, request: NewReturnRequest) => Promise<{
|
|
966
990
|
status: string;
|
|
967
991
|
created_at: Date;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,72 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// src/core/analytics.ts
|
|
3
|
+
var REVENUE_STATES = new Set(["paid", "shipped"]);
|
|
4
|
+
var isoDay = (value) => {
|
|
5
|
+
const text = typeof value === "string" ? value : value.toISOString();
|
|
6
|
+
return text.slice(0, 10);
|
|
7
|
+
};
|
|
8
|
+
var salesSummary = (orders) => {
|
|
9
|
+
const statusCounts = {};
|
|
10
|
+
const byDay = new Map;
|
|
11
|
+
const byProduct = new Map;
|
|
12
|
+
let revenueCents = 0;
|
|
13
|
+
let paidOrders = 0;
|
|
14
|
+
for (const order of orders) {
|
|
15
|
+
statusCounts[order.status] = (statusCounts[order.status] ?? 0) + 1;
|
|
16
|
+
if (!REVENUE_STATES.has(order.status))
|
|
17
|
+
continue;
|
|
18
|
+
const amount = order.amount_total ?? 0;
|
|
19
|
+
revenueCents += amount;
|
|
20
|
+
paidOrders += 1;
|
|
21
|
+
const day = isoDay(order.created_at);
|
|
22
|
+
const existing = byDay.get(day) ?? { date: day, orders: 0, revenueCents: 0 };
|
|
23
|
+
existing.orders += 1;
|
|
24
|
+
existing.revenueCents += amount;
|
|
25
|
+
byDay.set(day, existing);
|
|
26
|
+
for (const line of order.line_items ?? []) {
|
|
27
|
+
const row = byProduct.get(line.product) ?? {
|
|
28
|
+
product: line.product,
|
|
29
|
+
quantity: 0,
|
|
30
|
+
revenueCents: 0
|
|
31
|
+
};
|
|
32
|
+
row.quantity += line.quantity;
|
|
33
|
+
row.revenueCents += line.amountTotal;
|
|
34
|
+
byProduct.set(line.product, row);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
averageOrderCents: paidOrders ? Math.round(revenueCents / paidOrders) : 0,
|
|
39
|
+
paidOrders,
|
|
40
|
+
revenueByDay: [...byDay.values()].sort((left, right) => left.date < right.date ? -1 : 1),
|
|
41
|
+
revenueCents,
|
|
42
|
+
statusCounts,
|
|
43
|
+
topProducts: [...byProduct.values()].sort((left, right) => right.revenueCents - left.revenueCents).slice(0, 8)
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
var customerSummaries = (orders) => {
|
|
47
|
+
const byEmail = new Map;
|
|
48
|
+
for (const order of orders) {
|
|
49
|
+
if (!order.customer_email || !REVENUE_STATES.has(order.status))
|
|
50
|
+
continue;
|
|
51
|
+
const email = order.customer_email.toLowerCase();
|
|
52
|
+
const at = isoDay(order.created_at);
|
|
53
|
+
const existing = byEmail.get(email) ?? {
|
|
54
|
+
email,
|
|
55
|
+
firstOrderAt: at,
|
|
56
|
+
lastOrderAt: at,
|
|
57
|
+
orders: 0,
|
|
58
|
+
totalSpentCents: 0
|
|
59
|
+
};
|
|
60
|
+
existing.orders += 1;
|
|
61
|
+
existing.totalSpentCents += order.amount_total ?? 0;
|
|
62
|
+
if (at < existing.firstOrderAt)
|
|
63
|
+
existing.firstOrderAt = at;
|
|
64
|
+
if (at > existing.lastOrderAt)
|
|
65
|
+
existing.lastOrderAt = at;
|
|
66
|
+
byEmail.set(email, existing);
|
|
67
|
+
}
|
|
68
|
+
return [...byEmail.values()].sort((left, right) => left.lastOrderAt < right.lastOrderAt ? 1 : -1);
|
|
69
|
+
};
|
|
2
70
|
// src/core/cart.ts
|
|
3
71
|
var cartCount = (lines) => lines.reduce((sum, line) => sum + line.quantity, 0);
|
|
4
72
|
var lineTotal = (line) => line.setupFee + line.unitPrice * line.quantity;
|
|
@@ -127,6 +195,7 @@ var DEFAULT_APPAREL_PARCEL = {
|
|
|
127
195
|
export {
|
|
128
196
|
toProductionStage,
|
|
129
197
|
toCents,
|
|
198
|
+
salesSummary,
|
|
130
199
|
roundMoney,
|
|
131
200
|
renderEmail,
|
|
132
201
|
quantityDiscount,
|
|
@@ -143,6 +212,7 @@ export {
|
|
|
143
212
|
emailButton,
|
|
144
213
|
discountAmountCents,
|
|
145
214
|
depositCents,
|
|
215
|
+
customerSummaries,
|
|
146
216
|
cartSubtotal,
|
|
147
217
|
cartSetupTotal,
|
|
148
218
|
cartCount,
|
package/package.json
CHANGED