sinfactura-types 1.10.310 → 1.10.311
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/cart.d.ts +125 -1
- package/dist/cjs/marketing.js +29 -1
- package/dist/marketing.d.ts +157 -8
- package/dist/marketing.js +28 -0
- package/dist/userActivity.d.ts +21 -3
- package/package.json +1 -1
package/dist/cart.d.ts
CHANGED
|
@@ -67,6 +67,18 @@ declare global {
|
|
|
67
67
|
* redemption. See `CartCoupon`.
|
|
68
68
|
*/
|
|
69
69
|
coupon?: CartCoupon;
|
|
70
|
+
/**
|
|
71
|
+
* The promotion this cart carries, if any — at most one. Applied by
|
|
72
|
+
* `applyPromotion`, cleared by `removePromotion`, and, like the coupon,
|
|
73
|
+
* holding it is not a redemption. See {@link CartPromotion}.
|
|
74
|
+
*/
|
|
75
|
+
promotion?: CartPromotion;
|
|
76
|
+
/**
|
|
77
|
+
* What `promotion` came to on this cart's current lines, re-derived on
|
|
78
|
+
* every write. Absent when no promotion is applied. See
|
|
79
|
+
* {@link CartPromotionEffect}.
|
|
80
|
+
*/
|
|
81
|
+
promotionEffect?: CartPromotionEffect;
|
|
70
82
|
/**
|
|
71
83
|
* The CART-LEVEL cut this coupon produced, re-derived on every write from
|
|
72
84
|
* `coupon`'s frozen grant against the current subtotal. Absent when no
|
|
@@ -410,6 +422,89 @@ declare global {
|
|
|
410
422
|
*/
|
|
411
423
|
maxDiscountAmount?: number;
|
|
412
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* The promotion a cart currently carries — the granted TERMS, frozen at
|
|
427
|
+
* apply time, exactly as {@link CartCoupon} freezes a coupon's.
|
|
428
|
+
*
|
|
429
|
+
* ⚠️ The terms are frozen, not a reference to the `Promotion` row, and
|
|
430
|
+
* for the same reason the coupon's are: a promotion the merchant edits or
|
|
431
|
+
* ends after a shopper applied it keeps working for that cart until checkout
|
|
432
|
+
* re-validates. Re-reading the row on every cart write would reprice a
|
|
433
|
+
* shopper mid-session from a change they never saw.
|
|
434
|
+
*
|
|
435
|
+
* ⚠️ A cart carries at most ONE of these, the same single slot the
|
|
436
|
+
* coupon has. Stacking two promotions of the same type on one cart is a
|
|
437
|
+
* question nobody has answered — what a second buy-2-get-1 does to units
|
|
438
|
+
* the first already rewarded has no obvious right answer — so applying one
|
|
439
|
+
* over another REPLACES it rather than quietly compounding a cut. When that
|
|
440
|
+
* question is answered, this becomes a list and the replace becomes an
|
|
441
|
+
* append; until then a single slot is the shape that cannot pay out twice
|
|
442
|
+
* for the same unit.
|
|
443
|
+
*
|
|
444
|
+
* ⚠️ `'coupon'` is absent from the union on purpose. A coupon promotion's
|
|
445
|
+
* money lives in the `Coupon` row, reaches the cart through `applyCoupon`,
|
|
446
|
+
* and sits in `Cart.coupon` — a cart that could hold it here as well would
|
|
447
|
+
* have two places to disagree about one cut.
|
|
448
|
+
*/
|
|
449
|
+
type CartPromotion = {
|
|
450
|
+
promotionId: string;
|
|
451
|
+
/** The promotion's name at apply time, so a receipt can say what was given. */
|
|
452
|
+
name: string;
|
|
453
|
+
/** When the shopper applied it. */
|
|
454
|
+
appliedAt: number;
|
|
455
|
+
} & ({
|
|
456
|
+
type: 'buyXGetY';
|
|
457
|
+
buyXGetY: BuyXGetYTerms;
|
|
458
|
+
} | {
|
|
459
|
+
type: 'freeShipping';
|
|
460
|
+
freeShipping: FreeShippingTerms;
|
|
461
|
+
} | {
|
|
462
|
+
type: 'bundle';
|
|
463
|
+
bundle: BundleTerms;
|
|
464
|
+
});
|
|
465
|
+
/**
|
|
466
|
+
* What the frozen {@link CartPromotion} actually came to on THIS cart —
|
|
467
|
+
* re-derived on every write, never stored as the answer.
|
|
468
|
+
*
|
|
469
|
+
* ⚠️ Server-owned, like {@link CartDiscount}'s `amount`. The client may
|
|
470
|
+
* read it and must never send it: it is a function of the cart's current
|
|
471
|
+
* lines and their current prices, so a client-supplied value is a claim about
|
|
472
|
+
* money the server is about to recompute anyway.
|
|
473
|
+
*/
|
|
474
|
+
interface CartPromotionEffect {
|
|
475
|
+
promotionId: string;
|
|
476
|
+
type: 'buyXGetY' | 'freeShipping' | 'bundle';
|
|
477
|
+
/**
|
|
478
|
+
* The DERIVED cut, in currency units, already included in
|
|
479
|
+
* `CartTotals.discount`.
|
|
480
|
+
*
|
|
481
|
+
* ⚠️ ALWAYS `0` for `'freeShipping'`, and that is the invariant, not
|
|
482
|
+
* an accident of the current numbers. Free shipping is not a discount —
|
|
483
|
+
* it zeroes `CartTotals.shipping`, which is a different term of the
|
|
484
|
+
* `grandTotal` formula. A free-shipping promotion that ever contributed
|
|
485
|
+
* here would be cutting the merchandise price for a delivery the merchant
|
|
486
|
+
* agreed to absorb, and the comprobante would discriminate a discount the
|
|
487
|
+
* customer was never given on the goods.
|
|
488
|
+
*/
|
|
489
|
+
amount: number;
|
|
490
|
+
/**
|
|
491
|
+
* How many times the promotion applied. `1` for a promotion that has no
|
|
492
|
+
* repeat notion (`'freeShipping'`), and `0` when the cart carries the
|
|
493
|
+
* promotion but no longer qualifies — a shopper who applied a buy-2-get-1
|
|
494
|
+
* and then removed a line keeps the frozen grant and earns nothing by it.
|
|
495
|
+
*/
|
|
496
|
+
applications: number;
|
|
497
|
+
/**
|
|
498
|
+
* Whether this promotion waives the cart's shipping.
|
|
499
|
+
*
|
|
500
|
+
* ⚠️ `CartTotals.shipping` is structurally `0` today — nothing in the
|
|
501
|
+
* api computes a shipping cost yet, by design, because it is chosen at
|
|
502
|
+
* checkout. So the waiver currently changes no money, and this flag is the
|
|
503
|
+
* only place the GRANT survives to be read by whoever lands the shipping
|
|
504
|
+
* quote. Do not read its absence as "shipping was charged".
|
|
505
|
+
*/
|
|
506
|
+
shippingWaived?: boolean;
|
|
507
|
+
}
|
|
413
508
|
/**
|
|
414
509
|
* A redeemable coupon. `PK: COUPON#{storeId}`, `SK: <normalized code>`.
|
|
415
510
|
*
|
|
@@ -622,7 +717,7 @@ declare global {
|
|
|
622
717
|
* at the DynamoDB marshaller instead of at validation.
|
|
623
718
|
* - `merge.items` is `min(1).max(50)`.
|
|
624
719
|
*/
|
|
625
|
-
type CartActionRequest = CartActionAddLine | CartActionChangeQuantity | CartActionRemoveLine | CartActionClear | CartActionMerge | CartActionSaveLine | CartActionRestoreLine | CartActionRemoveSavedLine | CartActionApplyCoupon | CartActionRemoveCoupon | CartActionSetLineDiscount;
|
|
720
|
+
type CartActionRequest = CartActionAddLine | CartActionChangeQuantity | CartActionRemoveLine | CartActionClear | CartActionMerge | CartActionSaveLine | CartActionRestoreLine | CartActionRemoveSavedLine | CartActionApplyCoupon | CartActionRemoveCoupon | CartActionApplyPromotion | CartActionRemovePromotion | CartActionSetLineDiscount;
|
|
626
721
|
/**
|
|
627
722
|
* Fields common to every action.
|
|
628
723
|
*
|
|
@@ -897,6 +992,35 @@ declare global {
|
|
|
897
992
|
interface CartActionRemoveCoupon extends CartActionBase {
|
|
898
993
|
mode: 'removeCoupon';
|
|
899
994
|
}
|
|
995
|
+
/**
|
|
996
|
+
* Applies a promotion to the cart by id.
|
|
997
|
+
*
|
|
998
|
+
* ⚠️ A cart holds AT MOST ONE promotion. Applying a second REPLACES the
|
|
999
|
+
* first rather than stacking — the same rule, and the same reason, as
|
|
1000
|
+
* {@link CartActionApplyCoupon}: nothing in the request says what a second
|
|
1001
|
+
* buy-2-get-1 does to units the first already rewarded.
|
|
1002
|
+
*
|
|
1003
|
+
* ⚠️ The action names an ID and nothing else. Every term comes off the
|
|
1004
|
+
* `Promotion` row, so a client cannot name its own cut.
|
|
1005
|
+
*
|
|
1006
|
+
* ⚠️ A promotion and a coupon can BOTH be on one cart. They are separate
|
|
1007
|
+
* slots and both cuts land in `CartTotals.discount`, which is clamped at the
|
|
1008
|
+
* subtotal — so the two together can take the cart to zero but never below.
|
|
1009
|
+
*/
|
|
1010
|
+
interface CartActionApplyPromotion extends CartActionBase {
|
|
1011
|
+
mode: 'applyPromotion';
|
|
1012
|
+
promotionId: string;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Clears the cart's promotion and its derived effect.
|
|
1016
|
+
*
|
|
1017
|
+
* A cart carrying no promotion is a no-op `200`, matching every other removal
|
|
1018
|
+
* verb here. Nothing is released, because a promotion has no redemption
|
|
1019
|
+
* counter to consume in the first place.
|
|
1020
|
+
*/
|
|
1021
|
+
interface CartActionRemovePromotion extends CartActionBase {
|
|
1022
|
+
mode: 'removePromotion';
|
|
1023
|
+
}
|
|
900
1024
|
/**
|
|
901
1025
|
* Sets or clears ONE line's discount — the operator's per-line cut.
|
|
902
1026
|
*
|
package/dist/cjs/marketing.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AUTOMATION_RULE_STATUSES = exports.AUTOMATION_SEND_STATUSES = exports.ATTRIBUTION_WINDOW_HOURS = exports.RFM_SEGMENT_LABELS = exports.AUTOMATION_TRIGGERS = void 0;
|
|
3
|
+
exports.CART_APPLICABLE_PROMOTION_TYPES = exports.PROMOTION_TYPES = exports.AUTOMATION_RULE_STATUSES = exports.AUTOMATION_SEND_STATUSES = exports.ATTRIBUTION_WINDOW_HOURS = exports.RFM_SEGMENT_LABELS = exports.AUTOMATION_TRIGGERS = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* The {@link AutomationTrigger} vocabulary as a RUNTIME value, so the api's Zod
|
|
6
6
|
* enum, the configuration screen's picker and the published union all derive
|
|
@@ -70,3 +70,31 @@ exports.AUTOMATION_RULE_STATUSES = [
|
|
|
70
70
|
'active',
|
|
71
71
|
'archived',
|
|
72
72
|
];
|
|
73
|
+
/**
|
|
74
|
+
* {@link PromotionType} as a runtime value — see {@link AUTOMATION_TRIGGERS}.
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ `'coupon'` is in the tuple even though a row may express it by carrying
|
|
77
|
+
* no `type` at all. The tuple is the vocabulary a writer may DECLARE; the absent
|
|
78
|
+
* spelling is a reader's concern, and conflating the two is how a CRUD schema
|
|
79
|
+
* comes to reject the one value every legacy row means.
|
|
80
|
+
*/
|
|
81
|
+
exports.PROMOTION_TYPES = [
|
|
82
|
+
'coupon',
|
|
83
|
+
'buyXGetY',
|
|
84
|
+
'freeShipping',
|
|
85
|
+
'bundle',
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* The promotion types a CART can freeze onto itself — every type but `'coupon'`.
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ `'coupon'` is excluded on purpose, and not for tidiness. A coupon
|
|
91
|
+
* promotion's money lives in the `Coupon` row its `couponCode` names, and that
|
|
92
|
+
* row already reaches the cart through `applyCoupon` and sits in `Cart.coupon`.
|
|
93
|
+
* Letting it also occupy the promotion slot would give one cart two places to
|
|
94
|
+
* disagree about the same cut.
|
|
95
|
+
*/
|
|
96
|
+
exports.CART_APPLICABLE_PROMOTION_TYPES = [
|
|
97
|
+
'buyXGetY',
|
|
98
|
+
'freeShipping',
|
|
99
|
+
'bundle',
|
|
100
|
+
];
|
package/dist/marketing.d.ts
CHANGED
|
@@ -221,25 +221,155 @@ declare global {
|
|
|
221
221
|
updatedAt?: number;
|
|
222
222
|
}
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
224
|
+
* How a promotion cuts money — the discriminant on {@link Promotion}.
|
|
225
225
|
*
|
|
226
|
-
* ⚠️
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
226
|
+
* ⚠️ `'coupon'` is the ABSENT value, not merely the first one. Every row
|
|
227
|
+
* written before this union existed carries no `type` at all, and
|
|
228
|
+
* forward-only means none of them is ever rewritten — so a reader that asks
|
|
229
|
+
* `promotion.type === 'coupon'` gets `false` on every one of them.
|
|
230
|
+
*
|
|
231
|
+
* ⚠️ The move, for EVERY consumer of this package, is `promotion.type ??
|
|
232
|
+
* 'coupon'` — or a `switch` whose `default` arm is the coupon arm. The api
|
|
233
|
+
* has a `promotionType()` (`stacks/services/promotions.ts`) that does exactly
|
|
234
|
+
* this, but it lives under `stacks/` and nothing importing these types can
|
|
235
|
+
* reach it: it is named here so a reader stops looking for it, not so one
|
|
236
|
+
* goes hunting.
|
|
230
237
|
*/
|
|
231
|
-
|
|
238
|
+
type PromotionType = 'coupon' | 'buyXGetY' | 'freeShipping' | 'bundle';
|
|
239
|
+
/** What every promotion carries, whatever it does to the money. */
|
|
240
|
+
interface PromotionBase {
|
|
232
241
|
storeId: string;
|
|
233
242
|
promotionId: string;
|
|
234
243
|
name: string;
|
|
235
|
-
/** FK into the existing `Coupon` entity. */
|
|
236
|
-
couponCode?: string;
|
|
237
244
|
status: 'draft' | 'active' | 'ended';
|
|
238
245
|
startsAt?: number;
|
|
239
246
|
endsAt?: number;
|
|
240
247
|
createdAt: number;
|
|
241
248
|
updatedAt?: number;
|
|
242
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* A marketing wrapper around a coupon that already exists — the original
|
|
252
|
+
* shape, and the one every unmigrated row still has.
|
|
253
|
+
*
|
|
254
|
+
* ⚠️ It carries NO discount mechanics of its own. `couponCode` is the only
|
|
255
|
+
* link, and every money field — type, value, minimum subtotal, cap, currency
|
|
256
|
+
* — stays owned by `Coupon`. A second place that can disagree with the
|
|
257
|
+
* coupon on terms is a second answer to "what did the customer actually get".
|
|
258
|
+
*
|
|
259
|
+
* ⚠️ This variant's discriminant is OPTIONAL, and it is the only one that
|
|
260
|
+
* is. That is what keeps every pre-union row readable with no migration, and
|
|
261
|
+
* it is why narrowing reaches this arm through a `switch`'s `default` rather
|
|
262
|
+
* than through `case 'coupon'`.
|
|
263
|
+
*/
|
|
264
|
+
interface CouponPromotion extends PromotionBase {
|
|
265
|
+
type?: 'coupon';
|
|
266
|
+
/** FK into the existing `Coupon` entity. */
|
|
267
|
+
couponCode?: string;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* "Buy X, get Y" — a quantity of one product arms a cut on another.
|
|
271
|
+
*
|
|
272
|
+
* ⚠️ The reward is a PERCENT, never money, for the same reason
|
|
273
|
+
* {@link CartDiscount} carries the grant and not the cut: it is re-derived on
|
|
274
|
+
* every cart write against the unit price the rewarded line actually has at
|
|
275
|
+
* that moment, which moves whenever a quantity break re-resolves `basePrice`.
|
|
276
|
+
* A frozen money amount would keep paying out the price the cart had when the
|
|
277
|
+
* shopper applied it.
|
|
278
|
+
*/
|
|
279
|
+
interface BuyXGetYTerms {
|
|
280
|
+
/** The product whose quantity ARMS the promotion. */
|
|
281
|
+
buyProductId: string;
|
|
282
|
+
/** How many units of `buyProductId` one application consumes. At least 1. */
|
|
283
|
+
buyQuantity: number;
|
|
284
|
+
/** The product the reward is taken on. May be the same as `buyProductId`. */
|
|
285
|
+
getProductId: string;
|
|
286
|
+
/** How many units of `getProductId` one application rewards. At least 1. */
|
|
287
|
+
getQuantity: number;
|
|
288
|
+
/**
|
|
289
|
+
* The cut on the rewarded units, as a percent of their line's unit price.
|
|
290
|
+
* `100` is the plain "get one free" shape; `50` is "get one half price".
|
|
291
|
+
*/
|
|
292
|
+
getDiscountPercent: number;
|
|
293
|
+
/**
|
|
294
|
+
* How many times one cart may apply this promotion. Absent means as often
|
|
295
|
+
* as the cart's own quantities allow.
|
|
296
|
+
*/
|
|
297
|
+
maxApplications?: number;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* What a bundle charges once its members are all present.
|
|
301
|
+
*
|
|
302
|
+
* ⚠️ A discriminated pair rather than two optional fields, because "both
|
|
303
|
+
* set" and "neither set" are both unanswerable at money time and neither
|
|
304
|
+
* should be representable. The CRUD schema does not have to refuse a shape
|
|
305
|
+
* the type cannot express.
|
|
306
|
+
*/
|
|
307
|
+
type BundlePricing = {
|
|
308
|
+
mode: 'fixedPrice';
|
|
309
|
+
/** What the whole bundle costs, in the cart's currency. */
|
|
310
|
+
amount: number;
|
|
311
|
+
} | {
|
|
312
|
+
mode: 'percentOff';
|
|
313
|
+
/** The cut on the summed member lines, as a percent. */
|
|
314
|
+
percent: number;
|
|
315
|
+
};
|
|
316
|
+
/** A set of products that, bought together, price as one. */
|
|
317
|
+
interface BundleTerms {
|
|
318
|
+
/**
|
|
319
|
+
* Every product that must be present, at one unit each, for a single
|
|
320
|
+
* application. Order is not significant.
|
|
321
|
+
*/
|
|
322
|
+
productIds: readonly string[];
|
|
323
|
+
pricing: BundlePricing;
|
|
324
|
+
/**
|
|
325
|
+
* How many times one cart may apply this bundle. Absent means as often as
|
|
326
|
+
* the cart's own quantities allow.
|
|
327
|
+
*/
|
|
328
|
+
maxApplications?: number;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* When shipping is waived.
|
|
332
|
+
*
|
|
333
|
+
* ⚠️ Eligibility ONLY — there is deliberately no money field here.
|
|
334
|
+
* Free shipping does not grant a discount, it zeroes a total the cart already
|
|
335
|
+
* carries, so a value on this object would be a second, disagreeing answer to
|
|
336
|
+
* what the shipping costs.
|
|
337
|
+
*/
|
|
338
|
+
interface FreeShippingTerms {
|
|
339
|
+
/** The cart subtotal that qualifies. Absent means any subtotal does. */
|
|
340
|
+
minSubtotal?: number;
|
|
341
|
+
/**
|
|
342
|
+
* At least one of these products must be on the cart. Absent means any
|
|
343
|
+
* cart qualifies.
|
|
344
|
+
*/
|
|
345
|
+
productIds?: readonly string[];
|
|
346
|
+
}
|
|
347
|
+
interface BuyXGetYPromotion extends PromotionBase {
|
|
348
|
+
type: 'buyXGetY';
|
|
349
|
+
buyXGetY: BuyXGetYTerms;
|
|
350
|
+
}
|
|
351
|
+
interface FreeShippingPromotion extends PromotionBase {
|
|
352
|
+
type: 'freeShipping';
|
|
353
|
+
freeShipping: FreeShippingTerms;
|
|
354
|
+
}
|
|
355
|
+
interface BundlePromotion extends PromotionBase {
|
|
356
|
+
type: 'bundle';
|
|
357
|
+
bundle: BundleTerms;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* A promotion a store runs — one of four mechanics, discriminated on `type`.
|
|
361
|
+
*
|
|
362
|
+
* ⚠️ Each variant carries its terms under a key NAMED FOR ITS TYPE
|
|
363
|
+
* (`buyXGetY`, `freeShipping`, `bundle`) rather than a shared `terms` key.
|
|
364
|
+
* That is what lets the union narrow: a shared key would have to be the union
|
|
365
|
+
* of every terms shape, so a `bundle` row could typecheck while carrying
|
|
366
|
+
* buy-X-get-Y terms and nothing but a runtime check would object.
|
|
367
|
+
*
|
|
368
|
+
* ⚠️ The `'coupon'` variant is the ONLY one whose money lives elsewhere
|
|
369
|
+
* — in the `Coupon` row its `couponCode` names. The other three carry their
|
|
370
|
+
* own mechanics, and are the only three a cart can freeze onto itself.
|
|
371
|
+
*/
|
|
372
|
+
type Promotion = CouponPromotion | BuyXGetYPromotion | FreeShippingPromotion | BundlePromotion;
|
|
243
373
|
/**
|
|
244
374
|
* What arms an {@link AutomationRule} — the SIGNAL a rule listens for.
|
|
245
375
|
*
|
|
@@ -724,4 +854,23 @@ export declare const ATTRIBUTION_WINDOW_HOURS = 72;
|
|
|
724
854
|
export declare const AUTOMATION_SEND_STATUSES: readonly ["queued", "sent", "failed", "skipped"];
|
|
725
855
|
/** {@link AutomationRuleStatus} as a runtime value — see {@link AUTOMATION_TRIGGERS}. */
|
|
726
856
|
export declare const AUTOMATION_RULE_STATUSES: readonly ["paused", "active", "archived"];
|
|
857
|
+
/**
|
|
858
|
+
* {@link PromotionType} as a runtime value — see {@link AUTOMATION_TRIGGERS}.
|
|
859
|
+
*
|
|
860
|
+
* ⚠️ `'coupon'` is in the tuple even though a row may express it by carrying
|
|
861
|
+
* no `type` at all. The tuple is the vocabulary a writer may DECLARE; the absent
|
|
862
|
+
* spelling is a reader's concern, and conflating the two is how a CRUD schema
|
|
863
|
+
* comes to reject the one value every legacy row means.
|
|
864
|
+
*/
|
|
865
|
+
export declare const PROMOTION_TYPES: readonly ["coupon", "buyXGetY", "freeShipping", "bundle"];
|
|
866
|
+
/**
|
|
867
|
+
* The promotion types a CART can freeze onto itself — every type but `'coupon'`.
|
|
868
|
+
*
|
|
869
|
+
* ⚠️ `'coupon'` is excluded on purpose, and not for tidiness. A coupon
|
|
870
|
+
* promotion's money lives in the `Coupon` row its `couponCode` names, and that
|
|
871
|
+
* row already reaches the cart through `applyCoupon` and sits in `Cart.coupon`.
|
|
872
|
+
* Letting it also occupy the promotion slot would give one cart two places to
|
|
873
|
+
* disagree about the same cut.
|
|
874
|
+
*/
|
|
875
|
+
export declare const CART_APPLICABLE_PROMOTION_TYPES: readonly ["buyXGetY", "freeShipping", "bundle"];
|
|
727
876
|
export {};
|
package/dist/marketing.js
CHANGED
|
@@ -67,3 +67,31 @@ export const AUTOMATION_RULE_STATUSES = [
|
|
|
67
67
|
'active',
|
|
68
68
|
'archived',
|
|
69
69
|
];
|
|
70
|
+
/**
|
|
71
|
+
* {@link PromotionType} as a runtime value — see {@link AUTOMATION_TRIGGERS}.
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ `'coupon'` is in the tuple even though a row may express it by carrying
|
|
74
|
+
* no `type` at all. The tuple is the vocabulary a writer may DECLARE; the absent
|
|
75
|
+
* spelling is a reader's concern, and conflating the two is how a CRUD schema
|
|
76
|
+
* comes to reject the one value every legacy row means.
|
|
77
|
+
*/
|
|
78
|
+
export const PROMOTION_TYPES = [
|
|
79
|
+
'coupon',
|
|
80
|
+
'buyXGetY',
|
|
81
|
+
'freeShipping',
|
|
82
|
+
'bundle',
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* The promotion types a CART can freeze onto itself — every type but `'coupon'`.
|
|
86
|
+
*
|
|
87
|
+
* ⚠️ `'coupon'` is excluded on purpose, and not for tidiness. A coupon
|
|
88
|
+
* promotion's money lives in the `Coupon` row its `couponCode` names, and that
|
|
89
|
+
* row already reaches the cart through `applyCoupon` and sits in `Cart.coupon`.
|
|
90
|
+
* Letting it also occupy the promotion slot would give one cart two places to
|
|
91
|
+
* disagree about the same cut.
|
|
92
|
+
*/
|
|
93
|
+
export const CART_APPLICABLE_PROMOTION_TYPES = [
|
|
94
|
+
'buyXGetY',
|
|
95
|
+
'freeShipping',
|
|
96
|
+
'bundle',
|
|
97
|
+
];
|
package/dist/userActivity.d.ts
CHANGED
|
@@ -494,10 +494,18 @@ declare global {
|
|
|
494
494
|
interface BasketDiscountGrantedEvent extends UserActivityEventBase {
|
|
495
495
|
event: 'Basket Discount Granted';
|
|
496
496
|
/** Which verb — a withdrawal is as worth auditing as a grant. */
|
|
497
|
-
verb: 'setLineDiscount' | 'applyCoupon' | 'removeCoupon';
|
|
497
|
+
verb: 'setLineDiscount' | 'applyCoupon' | 'removeCoupon' | 'applyPromotion' | 'removePromotion';
|
|
498
498
|
/**
|
|
499
|
-
* The grant's unit. Absent on `removeCoupon`, which
|
|
500
|
-
*
|
|
499
|
+
* The grant's unit. Absent on `removeCoupon` and `removePromotion`, which
|
|
500
|
+
* withdraw rather than grant and therefore have no terms of their own.
|
|
501
|
+
*
|
|
502
|
+
* ⚠️ Also absent on `applyPromotion`, which GRANTS — and that is the
|
|
503
|
+
* one case where absence does not mean withdrawal. A promotion's grant is a
|
|
504
|
+
* whole terms object (a trigger, a reward, a repeat cap); there is no
|
|
505
|
+
* `(type, value)` pair that states it without lying by omission, so `code`
|
|
506
|
+
* carries the promotion id and the row holds the terms. A review reading
|
|
507
|
+
* this trail for abuse gets `amount` — the money — either way, which is
|
|
508
|
+
* the figure the review is actually made of.
|
|
501
509
|
*/
|
|
502
510
|
type?: 'percent' | 'amount';
|
|
503
511
|
/** The GRANT, in the unit `type` names — NOT money. See `amount`. */
|
|
@@ -515,6 +523,16 @@ declare global {
|
|
|
515
523
|
amount?: number;
|
|
516
524
|
/** The coupon code, on the two coupon verbs. Absent for a line discount. */
|
|
517
525
|
code?: string;
|
|
526
|
+
/**
|
|
527
|
+
* The promotion, on `applyPromotion`.
|
|
528
|
+
*
|
|
529
|
+
* ⚠️ Its OWN field rather than a promotion id shipped in `code`. They
|
|
530
|
+
* are different identifiers into different entities, and a trail that put
|
|
531
|
+
* both in one column would make "which coupon was granted most often"
|
|
532
|
+
* unanswerable without knowing each row's verb — which is exactly the kind
|
|
533
|
+
* of join a review does not do before drawing a conclusion.
|
|
534
|
+
*/
|
|
535
|
+
promotion_id?: string;
|
|
518
536
|
/** The line the cut was applied to, on `setLineDiscount`. */
|
|
519
537
|
line_id?: string;
|
|
520
538
|
/** ⚠️ Optional for the same reason as `BasketUpdatedEvent.customer_id`: a
|