@mj-biz-apps/orders-core-entities-server 0.0.1 → 5.2.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.
Files changed (267) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +141 -2
  3. package/dist/AccountingBridge.d.ts +65 -0
  4. package/dist/AccountingBridge.d.ts.map +1 -0
  5. package/dist/AccountingBridge.js +102 -0
  6. package/dist/AccountingBridge.js.map +1 -0
  7. package/dist/AdvanceOrderStateOperation.d.ts +80 -0
  8. package/dist/AdvanceOrderStateOperation.d.ts.map +1 -0
  9. package/dist/AdvanceOrderStateOperation.js +267 -0
  10. package/dist/AdvanceOrderStateOperation.js.map +1 -0
  11. package/dist/ApplyAccountCreditOperation.d.ts +97 -0
  12. package/dist/ApplyAccountCreditOperation.d.ts.map +1 -0
  13. package/dist/ApplyAccountCreditOperation.js +275 -0
  14. package/dist/ApplyAccountCreditOperation.js.map +1 -0
  15. package/dist/BaseDeliveryChannel.d.ts +106 -0
  16. package/dist/BaseDeliveryChannel.d.ts.map +1 -0
  17. package/dist/BaseDeliveryChannel.js +36 -0
  18. package/dist/BaseDeliveryChannel.js.map +1 -0
  19. package/dist/BasePaymentProvider.d.ts +239 -0
  20. package/dist/BasePaymentProvider.d.ts.map +1 -0
  21. package/dist/BasePaymentProvider.js +91 -0
  22. package/dist/BasePaymentProvider.js.map +1 -0
  23. package/dist/BundleBehavior.d.ts +139 -0
  24. package/dist/BundleBehavior.d.ts.map +1 -0
  25. package/dist/BundleBehavior.js +173 -0
  26. package/dist/BundleBehavior.js.map +1 -0
  27. package/dist/BundleEngine.d.ts +60 -0
  28. package/dist/BundleEngine.d.ts.map +1 -0
  29. package/dist/BundleEngine.js +197 -0
  30. package/dist/BundleEngine.js.map +1 -0
  31. package/dist/CancelSubscriptionOperation.d.ts +98 -0
  32. package/dist/CancelSubscriptionOperation.d.ts.map +1 -0
  33. package/dist/CancelSubscriptionOperation.js +317 -0
  34. package/dist/CancelSubscriptionOperation.js.map +1 -0
  35. package/dist/CapturePaymentOperation.d.ts +62 -0
  36. package/dist/CapturePaymentOperation.d.ts.map +1 -0
  37. package/dist/CapturePaymentOperation.js +463 -0
  38. package/dist/CapturePaymentOperation.js.map +1 -0
  39. package/dist/CheckEntitlementOperation.d.ts +27 -0
  40. package/dist/CheckEntitlementOperation.d.ts.map +1 -0
  41. package/dist/CheckEntitlementOperation.js +45 -0
  42. package/dist/CheckEntitlementOperation.js.map +1 -0
  43. package/dist/CheckoutSessionService.d.ts +258 -0
  44. package/dist/CheckoutSessionService.d.ts.map +1 -0
  45. package/dist/CheckoutSessionService.js +1557 -0
  46. package/dist/CheckoutSessionService.js.map +1 -0
  47. package/dist/DeliveryBehavior.d.ts +121 -0
  48. package/dist/DeliveryBehavior.d.ts.map +1 -0
  49. package/dist/DeliveryBehavior.js +145 -0
  50. package/dist/DeliveryBehavior.js.map +1 -0
  51. package/dist/DeliveryRecipientResolver.d.ts +38 -0
  52. package/dist/DeliveryRecipientResolver.d.ts.map +1 -0
  53. package/dist/DeliveryRecipientResolver.js +93 -0
  54. package/dist/DeliveryRecipientResolver.js.map +1 -0
  55. package/dist/DeliveryResolver.d.ts +14 -0
  56. package/dist/DeliveryResolver.d.ts.map +1 -0
  57. package/dist/DeliveryResolver.js +45 -0
  58. package/dist/DeliveryResolver.js.map +1 -0
  59. package/dist/EmailDeliveryChannel.d.ts +31 -0
  60. package/dist/EmailDeliveryChannel.d.ts.map +1 -0
  61. package/dist/EmailDeliveryChannel.js +158 -0
  62. package/dist/EmailDeliveryChannel.js.map +1 -0
  63. package/dist/EntitlementBehavior.d.ts +235 -0
  64. package/dist/EntitlementBehavior.d.ts.map +1 -0
  65. package/dist/EntitlementBehavior.js +330 -0
  66. package/dist/EntitlementBehavior.js.map +1 -0
  67. package/dist/EntitlementEngine.d.ts +97 -0
  68. package/dist/EntitlementEngine.d.ts.map +1 -0
  69. package/dist/EntitlementEngine.js +338 -0
  70. package/dist/EntitlementEngine.js.map +1 -0
  71. package/dist/EntitlementGrantClaimDriver.d.ts +42 -0
  72. package/dist/EntitlementGrantClaimDriver.d.ts.map +1 -0
  73. package/dist/EntitlementGrantClaimDriver.js +158 -0
  74. package/dist/EntitlementGrantClaimDriver.js.map +1 -0
  75. package/dist/EntitlementRead.d.ts +82 -0
  76. package/dist/EntitlementRead.d.ts.map +1 -0
  77. package/dist/EntitlementRead.js +368 -0
  78. package/dist/EntitlementRead.js.map +1 -0
  79. package/dist/FulfillOrderLinesOperation.d.ts +34 -0
  80. package/dist/FulfillOrderLinesOperation.d.ts.map +1 -0
  81. package/dist/FulfillOrderLinesOperation.js +208 -0
  82. package/dist/FulfillOrderLinesOperation.js.map +1 -0
  83. package/dist/FulfillmentBehavior.d.ts +101 -0
  84. package/dist/FulfillmentBehavior.d.ts.map +1 -0
  85. package/dist/FulfillmentBehavior.js +145 -0
  86. package/dist/FulfillmentBehavior.js.map +1 -0
  87. package/dist/GLAccountResolver.d.ts +123 -0
  88. package/dist/GLAccountResolver.d.ts.map +1 -0
  89. package/dist/GLAccountResolver.js +175 -0
  90. package/dist/GLAccountResolver.js.map +1 -0
  91. package/dist/GetFulfillmentQueueOperation.d.ts +35 -0
  92. package/dist/GetFulfillmentQueueOperation.d.ts.map +1 -0
  93. package/dist/GetFulfillmentQueueOperation.js +221 -0
  94. package/dist/GetFulfillmentQueueOperation.js.map +1 -0
  95. package/dist/GetOverdueWorklistOperation.d.ts +41 -0
  96. package/dist/GetOverdueWorklistOperation.d.ts.map +1 -0
  97. package/dist/GetOverdueWorklistOperation.js +210 -0
  98. package/dist/GetOverdueWorklistOperation.js.map +1 -0
  99. package/dist/GiftCardBehavior.d.ts +97 -0
  100. package/dist/GiftCardBehavior.d.ts.map +1 -0
  101. package/dist/GiftCardBehavior.js +121 -0
  102. package/dist/GiftCardBehavior.js.map +1 -0
  103. package/dist/GiftCardEngine.d.ts +59 -0
  104. package/dist/GiftCardEngine.d.ts.map +1 -0
  105. package/dist/GiftCardEngine.js +197 -0
  106. package/dist/GiftCardEngine.js.map +1 -0
  107. package/dist/GuestOrderClaimDriver.d.ts +36 -0
  108. package/dist/GuestOrderClaimDriver.d.ts.map +1 -0
  109. package/dist/GuestOrderClaimDriver.js +161 -0
  110. package/dist/GuestOrderClaimDriver.js.map +1 -0
  111. package/dist/InvoiceBehavior.d.ts +394 -0
  112. package/dist/InvoiceBehavior.d.ts.map +1 -0
  113. package/dist/InvoiceBehavior.js +496 -0
  114. package/dist/InvoiceBehavior.js.map +1 -0
  115. package/dist/InvoiceBuilder.d.ts +48 -0
  116. package/dist/InvoiceBuilder.d.ts.map +1 -0
  117. package/dist/InvoiceBuilder.js +352 -0
  118. package/dist/InvoiceBuilder.js.map +1 -0
  119. package/dist/InvoiceDisplay.d.ts +96 -0
  120. package/dist/InvoiceDisplay.d.ts.map +1 -0
  121. package/dist/InvoiceDisplay.js +121 -0
  122. package/dist/InvoiceDisplay.js.map +1 -0
  123. package/dist/ListEntitlementsOperation.d.ts +21 -0
  124. package/dist/ListEntitlementsOperation.d.ts.map +1 -0
  125. package/dist/ListEntitlementsOperation.js +39 -0
  126. package/dist/ListEntitlementsOperation.js.map +1 -0
  127. package/dist/ManualPaymentProvider.d.ts +19 -0
  128. package/dist/ManualPaymentProvider.d.ts.map +1 -0
  129. package/dist/ManualPaymentProvider.js +92 -0
  130. package/dist/ManualPaymentProvider.js.map +1 -0
  131. package/dist/OrderEntityServer.d.ts +542 -0
  132. package/dist/OrderEntityServer.d.ts.map +1 -0
  133. package/dist/OrderEntityServer.js +2100 -0
  134. package/dist/OrderEntityServer.js.map +1 -0
  135. package/dist/OrderJournalEntryFactory.d.ts +140 -0
  136. package/dist/OrderJournalEntryFactory.d.ts.map +1 -0
  137. package/dist/OrderJournalEntryFactory.js +466 -0
  138. package/dist/OrderJournalEntryFactory.js.map +1 -0
  139. package/dist/OrderLineEntityServer.d.ts +90 -0
  140. package/dist/OrderLineEntityServer.d.ts.map +1 -0
  141. package/dist/OrderLineEntityServer.js +260 -0
  142. package/dist/OrderLineEntityServer.js.map +1 -0
  143. package/dist/OrdersSettings.d.ts +36 -0
  144. package/dist/OrdersSettings.d.ts.map +1 -0
  145. package/dist/OrdersSettings.js +147 -0
  146. package/dist/OrdersSettings.js.map +1 -0
  147. package/dist/PaymentAllocationFactory.d.ts +128 -0
  148. package/dist/PaymentAllocationFactory.d.ts.map +1 -0
  149. package/dist/PaymentAllocationFactory.js +235 -0
  150. package/dist/PaymentAllocationFactory.js.map +1 -0
  151. package/dist/PaymentHeaderEntityServer.d.ts +180 -0
  152. package/dist/PaymentHeaderEntityServer.d.ts.map +1 -0
  153. package/dist/PaymentHeaderEntityServer.js +656 -0
  154. package/dist/PaymentHeaderEntityServer.js.map +1 -0
  155. package/dist/PaymentIntentService.d.ts +101 -0
  156. package/dist/PaymentIntentService.d.ts.map +1 -0
  157. package/dist/PaymentIntentService.js +150 -0
  158. package/dist/PaymentIntentService.js.map +1 -0
  159. package/dist/PaymentJournalEntryFactory.d.ts +99 -0
  160. package/dist/PaymentJournalEntryFactory.d.ts.map +1 -0
  161. package/dist/PaymentJournalEntryFactory.js +127 -0
  162. package/dist/PaymentJournalEntryFactory.js.map +1 -0
  163. package/dist/PaymentLineEntityServer.d.ts +73 -0
  164. package/dist/PaymentLineEntityServer.d.ts.map +1 -0
  165. package/dist/PaymentLineEntityServer.js +303 -0
  166. package/dist/PaymentLineEntityServer.js.map +1 -0
  167. package/dist/PaymentProviderBehavior.d.ts +222 -0
  168. package/dist/PaymentProviderBehavior.d.ts.map +1 -0
  169. package/dist/PaymentProviderBehavior.js +368 -0
  170. package/dist/PaymentProviderBehavior.js.map +1 -0
  171. package/dist/PaymentProviderResolver.d.ts +84 -0
  172. package/dist/PaymentProviderResolver.d.ts.map +1 -0
  173. package/dist/PaymentProviderResolver.js +220 -0
  174. package/dist/PaymentProviderResolver.js.map +1 -0
  175. package/dist/PaymentReversalFactory.d.ts +107 -0
  176. package/dist/PaymentReversalFactory.d.ts.map +1 -0
  177. package/dist/PaymentReversalFactory.js +174 -0
  178. package/dist/PaymentReversalFactory.js.map +1 -0
  179. package/dist/PaymentSettlement.d.ts +56 -0
  180. package/dist/PaymentSettlement.d.ts.map +1 -0
  181. package/dist/PaymentSettlement.js +243 -0
  182. package/dist/PaymentSettlement.js.map +1 -0
  183. package/dist/PaymentTermsBehavior.d.ts +109 -0
  184. package/dist/PaymentTermsBehavior.d.ts.map +1 -0
  185. package/dist/PaymentTermsBehavior.js +172 -0
  186. package/dist/PaymentTermsBehavior.js.map +1 -0
  187. package/dist/PaymentWebhookHandler.d.ts +103 -0
  188. package/dist/PaymentWebhookHandler.d.ts.map +1 -0
  189. package/dist/PaymentWebhookHandler.js +246 -0
  190. package/dist/PaymentWebhookHandler.js.map +1 -0
  191. package/dist/PreviewPriceOperation.d.ts +62 -0
  192. package/dist/PreviewPriceOperation.d.ts.map +1 -0
  193. package/dist/PreviewPriceOperation.js +161 -0
  194. package/dist/PreviewPriceOperation.js.map +1 -0
  195. package/dist/PriceOrderOperation.d.ts +93 -0
  196. package/dist/PriceOrderOperation.d.ts.map +1 -0
  197. package/dist/PriceOrderOperation.js +146 -0
  198. package/dist/PriceOrderOperation.js.map +1 -0
  199. package/dist/ProductPriceEntityServer.d.ts +34 -0
  200. package/dist/ProductPriceEntityServer.d.ts.map +1 -0
  201. package/dist/ProductPriceEntityServer.js +97 -0
  202. package/dist/ProductPriceEntityServer.js.map +1 -0
  203. package/dist/RefundPaymentOperation.d.ts +61 -0
  204. package/dist/RefundPaymentOperation.d.ts.map +1 -0
  205. package/dist/RefundPaymentOperation.js +177 -0
  206. package/dist/RefundPaymentOperation.js.map +1 -0
  207. package/dist/RevenueRecognition.d.ts +76 -0
  208. package/dist/RevenueRecognition.d.ts.map +1 -0
  209. package/dist/RevenueRecognition.js +133 -0
  210. package/dist/RevenueRecognition.js.map +1 -0
  211. package/dist/ReversalBehavior.d.ts +82 -0
  212. package/dist/ReversalBehavior.d.ts.map +1 -0
  213. package/dist/ReversalBehavior.js +100 -0
  214. package/dist/ReversalBehavior.js.map +1 -0
  215. package/dist/ReversalResolver.d.ts +37 -0
  216. package/dist/ReversalResolver.d.ts.map +1 -0
  217. package/dist/ReversalResolver.js +96 -0
  218. package/dist/ReversalResolver.js.map +1 -0
  219. package/dist/SpawnRenewalsOperation.d.ts +109 -0
  220. package/dist/SpawnRenewalsOperation.d.ts.map +1 -0
  221. package/dist/SpawnRenewalsOperation.js +295 -0
  222. package/dist/SpawnRenewalsOperation.js.map +1 -0
  223. package/dist/StoredValuePaymentProvider.d.ts +52 -0
  224. package/dist/StoredValuePaymentProvider.d.ts.map +1 -0
  225. package/dist/StoredValuePaymentProvider.js +205 -0
  226. package/dist/StoredValuePaymentProvider.js.map +1 -0
  227. package/dist/StripeACHPaymentProvider.d.ts +50 -0
  228. package/dist/StripeACHPaymentProvider.d.ts.map +1 -0
  229. package/dist/StripeACHPaymentProvider.js +211 -0
  230. package/dist/StripeACHPaymentProvider.js.map +1 -0
  231. package/dist/StripePaymentProvider.d.ts +83 -0
  232. package/dist/StripePaymentProvider.d.ts.map +1 -0
  233. package/dist/StripePaymentProvider.js +442 -0
  234. package/dist/StripePaymentProvider.js.map +1 -0
  235. package/dist/SubscriptionBehavior.d.ts +197 -0
  236. package/dist/SubscriptionBehavior.d.ts.map +1 -0
  237. package/dist/SubscriptionBehavior.js +415 -0
  238. package/dist/SubscriptionBehavior.js.map +1 -0
  239. package/dist/checkoutCaptureAlert.d.ts +16 -0
  240. package/dist/checkoutCaptureAlert.d.ts.map +1 -0
  241. package/dist/checkoutCaptureAlert.js +55 -0
  242. package/dist/checkoutCaptureAlert.js.map +1 -0
  243. package/dist/checkoutCaptureRetry.d.ts +27 -0
  244. package/dist/checkoutCaptureRetry.d.ts.map +1 -0
  245. package/dist/checkoutCaptureRetry.js +50 -0
  246. package/dist/checkoutCaptureRetry.js.map +1 -0
  247. package/dist/claimDriverHelpers.d.ts +15 -0
  248. package/dist/claimDriverHelpers.d.ts.map +1 -0
  249. package/dist/claimDriverHelpers.js +34 -0
  250. package/dist/claimDriverHelpers.js.map +1 -0
  251. package/dist/entity-names.d.ts +15 -0
  252. package/dist/entity-names.d.ts.map +1 -0
  253. package/dist/entity-names.js +15 -0
  254. package/dist/entity-names.js.map +1 -0
  255. package/dist/identityClaimContracts.d.ts +118 -0
  256. package/dist/identityClaimContracts.d.ts.map +1 -0
  257. package/dist/identityClaimContracts.js +58 -0
  258. package/dist/identityClaimContracts.js.map +1 -0
  259. package/dist/index.d.ts +126 -0
  260. package/dist/index.d.ts.map +1 -0
  261. package/dist/index.js +115 -0
  262. package/dist/index.js.map +1 -0
  263. package/dist/sql-guards.d.ts +78 -0
  264. package/dist/sql-guards.d.ts.map +1 -0
  265. package/dist/sql-guards.js +115 -0
  266. package/dist/sql-guards.js.map +1 -0
  267. package/package.json +50 -5
@@ -0,0 +1,330 @@
1
+ /**
2
+ * What a purchase grants, and for how long — the arithmetic, with no database in sight.
3
+ *
4
+ * A `ProductEntitlement` is a TEMPLATE: this product confers this feature, access level, or resource
5
+ * quantity. An `EntitlementGrant` is the INSTANCE created when somebody buys it, carrying a
6
+ * beneficiary and a validity window. This module decides what that instance should look like.
7
+ *
8
+ * THREE SETTINGS, ONE WALK (D76). Timing, quantity and validity are all configurable per deployment,
9
+ * and all three resolve down the SAME chain taxability uses:
10
+ *
11
+ * product → its category → that category's ancestors → the product type
12
+ *
13
+ * Nullable at product and category so "ask my parent" is sayable; NOT NULL at the type so the walk
14
+ * always terminates with a real answer. Sharing the chain is deliberate rather than tidy: anyone who
15
+ * understands how taxability resolves already understands this, and the two cannot drift into
16
+ * disagreeing about what "the product's category tree" means.
17
+ *
18
+ * WHY VALIDITY IS DIFFERENT FROM THE OTHER TWO. It is read from the `ProductEntitlement` FIRST and
19
+ * from the walk only when the template is silent, because one product can grant two entitlements with
20
+ * different windows. A course might give perpetual access to the downloadable materials and ninety
21
+ * days in the discussion forum. A policy resolved purely from the product could not say that.
22
+ *
23
+ * THE NON-SUBSCRIPTION CASES ARE THE INTERESTING ONES. It is tempting to assume access follows a
24
+ * subscription term, because that is the case with the most moving parts. But the commonest grants
25
+ * have nothing to do with subscriptions at all: a digital download is good forever, and a ticket to
26
+ * an online event is good for the length of the event and not a minute longer.
27
+ *
28
+ * CONNECTS TO:
29
+ * SERVER: ./EntitlementEngine.ts (the lookups and the writes)
30
+ * CALLER: OrderEntityServer (grants are created inside the booking transaction)
31
+ * MIRROR: ./TaxResolver.ts — the same walk, for the same reason
32
+ * DOC: plans/archive/bizapps-orders-master.md D27, D76
33
+ */
34
+ /**
35
+ * Walk the chain for all three settings at once.
36
+ *
37
+ * ONE walk rather than three, because they resolve identically and a caller that walked separately
38
+ * would do the same lookups three times and could be given three different category chains.
39
+ *
40
+ * `entitlement` is consulted for VALIDITY ONLY — see the module header. Timing and quantity are
41
+ * properties of the purchase, not of the individual thing granted: it would make no sense for one
42
+ * entitlement on a line to appear at confirm and another to wait for payment.
43
+ */
44
+ export function ResolveEntitlementPolicy(product,
45
+ /** NEAREST FIRST: the product's own category, then its parent, up to the root. */
46
+ categoryChain, type, entitlement) {
47
+ const walk = (key, typeValue) => {
48
+ const own = product[key];
49
+ if (own != null)
50
+ return { value: own, level: 'Product' };
51
+ for (const level of categoryChain) {
52
+ const v = level[key];
53
+ if (v != null)
54
+ return { value: v, level: 'ProductCategory' };
55
+ }
56
+ return { value: typeValue, level: 'ProductType' };
57
+ };
58
+ const timing = walk('EntitlementGrantTiming', type.DefaultEntitlementGrantTiming);
59
+ const quantity = walk('EntitlementQuantityMode', type.DefaultEntitlementQuantityMode);
60
+ // The template wins on validity, and only on validity.
61
+ const fromTemplate = entitlement?.ValidityMode ?? null;
62
+ const validity = fromTemplate != null
63
+ ? { value: fromTemplate, level: 'ProductEntitlement' }
64
+ : walk('EntitlementValidityMode', type.DefaultEntitlementValidityMode);
65
+ return {
66
+ GrantTiming: timing.value,
67
+ QuantityMode: quantity.value,
68
+ ValidityMode: validity.value,
69
+ DecidedAt: {
70
+ GrantTiming: timing.level,
71
+ QuantityMode: quantity.level,
72
+ ValidityMode: validity.level,
73
+ },
74
+ };
75
+ }
76
+ /**
77
+ * How much of the entitlement this line grants.
78
+ *
79
+ * `PerUnit` multiplies, so three 5-seat packs give fifteen seats — which is what somebody buying
80
+ * three packs expects. `Flat` ignores the line quantity entirely, for entitlements that are not
81
+ * countable: "access to the member portal" does not become three portals.
82
+ *
83
+ * ROUNDED UP, and only ever up. A prorated subscription line carries a fractional quantity (0.5833
84
+ * for a short first period), and 0.5833 × 5 seats is 2.9165. Rounding down hands the customer two
85
+ * seats for a five-seat product and generates a support ticket; rounding up hands them three and
86
+ * generates nothing. Under-granting is the expensive direction, exactly as under-collecting is with
87
+ * tax, and for the same reason: the customer notices.
88
+ *
89
+ * A null template quantity means the entitlement is not a countable thing (a Feature or AccessLevel
90
+ * rather than a ResourceQuantity), and stays null rather than becoming zero — zero would read as
91
+ * "granted none of it".
92
+ */
93
+ export function ResolveGrantQuantity(templateQuantity, lineQuantity, mode) {
94
+ if (templateQuantity == null)
95
+ return null;
96
+ if (mode === 'Flat')
97
+ return templateQuantity;
98
+ const scaled = templateQuantity * Math.abs(lineQuantity);
99
+ return Math.ceil(Math.round(scaled * 1e6) / 1e6);
100
+ }
101
+ const HOUR = 60 * 60 * 1000;
102
+ const DAY = 24 * HOUR;
103
+ /**
104
+ * Turn a validity mode plus its context into an actual window.
105
+ *
106
+ * FALLS BACK RATHER THAN THROWING when a mode's own inputs are missing, and records what it actually
107
+ * applied. A ticket whose product has no event dates should not fail the customer's order — but the
108
+ * grant must not silently claim to be an event window either, so `ModeApplied` says `Perpetual` and
109
+ * an auditor can see the mode was requested and could not be honoured.
110
+ *
111
+ * The one thing it will not do is produce a window that ends before it starts. An event whose end
112
+ * date precedes its start, or a lag that somehow lands behind the lead, yields a window ending at its
113
+ * own start — an instantaneous grant, which reads as "nothing was granted" and is at least honest,
114
+ * rather than a negative window the database CHECK would reject at insert time with an error naming
115
+ * neither the product nor the event.
116
+ */
117
+ export function ResolveValidityWindow(mode, ctx) {
118
+ const clamp = (from, to) => ({
119
+ ValidFrom: from,
120
+ ValidTo: to != null && to.getTime() < from.getTime() ? new Date(from.getTime()) : to,
121
+ ModeApplied: mode,
122
+ });
123
+ switch (mode) {
124
+ case 'Perpetual':
125
+ // A digital download. Good forever, and that is a fact rather than an omission.
126
+ return { ValidFrom: ctx.GrantedOn, ValidTo: null, ModeApplied: 'Perpetual' };
127
+ case 'FixedDuration': {
128
+ const days = ctx.DurationDays ?? 0;
129
+ if (days <= 0) {
130
+ return { ValidFrom: ctx.GrantedOn, ValidTo: null, ModeApplied: 'Perpetual' };
131
+ }
132
+ return clamp(ctx.GrantedOn, new Date(ctx.GrantedOn.getTime() + days * DAY));
133
+ }
134
+ case 'EventWindow': {
135
+ if (!ctx.EventStartsAt || !ctx.EventEndsAt) {
136
+ return { ValidFrom: ctx.GrantedOn, ValidTo: null, ModeApplied: 'Perpetual' };
137
+ }
138
+ // Lead and lag are separate decisions, which is why they are separate columns: opening
139
+ // the stream an hour early is a courtesy, and leaving the recording up for a day
140
+ // afterwards is a different courtesy with different cost.
141
+ const lead = (ctx.AccessLeadHours ?? 0) * HOUR;
142
+ const lag = (ctx.AccessLagHours ?? 0) * HOUR;
143
+ return clamp(new Date(ctx.EventStartsAt.getTime() - lead), new Date(ctx.EventEndsAt.getTime() + lag));
144
+ }
145
+ case 'SubscriptionTerm': {
146
+ if (!ctx.TermStartDate || !ctx.TermEndDate) {
147
+ // Asked to follow a term that does not exist — a non-subscription product configured
148
+ // as though it were one. Perpetual is the safe read: the customer bought something.
149
+ return { ValidFrom: ctx.GrantedOn, ValidTo: null, ModeApplied: 'Perpetual' };
150
+ }
151
+ return clamp(ctx.TermStartDate, ctx.TermEndDate);
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Should the grant be ACTIVE yet, given when access is supposed to begin?
157
+ *
158
+ * The grant row is written at confirm either way — downstream apps poll grants (D27), and a grant
159
+ * that does not exist until payment clears cannot be seen coming. What timing changes is the STATUS:
160
+ *
161
+ * OnConfirm Active immediately. A confirmed order is a claim on the seller.
162
+ * OnPaidInFull Suspended until the order's balance reaches zero. Access follows cash.
163
+ * OnActivation Suspended until something explicitly activates it. Note that nothing does yet —
164
+ * this exists because it was asked for, and a deployment choosing it is choosing to
165
+ * grant nothing until it builds the caller.
166
+ *
167
+ * `Suspended` rather than a missing row, and rather than `Revoked`: revoked means somebody took it
168
+ * away, which is a different fact that an access dispute turns on.
169
+ */
170
+ export function InitialGrantStatus(timing, order) {
171
+ if (timing === 'OnConfirm')
172
+ return 'Active';
173
+ if (timing === 'OnActivation')
174
+ return 'Suspended';
175
+ // OnPaidInFull. A zero-value order is paid by definition — a free line should not leave the
176
+ // customer waiting for a payment that will never arrive.
177
+ const gross = Number(order.TotalGross ?? 0);
178
+ if (gross <= 0)
179
+ return 'Active';
180
+ return Number(order.Balance ?? gross) <= 0 ? 'Active' : 'Suspended';
181
+ }
182
+ /**
183
+ * How much of a grant survives a partial return.
184
+ *
185
+ * A customer who bought five seats and sent two back keeps three. Returning everything revokes the
186
+ * grant outright rather than leaving a zero-quantity row, because zero is indistinguishable from a
187
+ * Feature grant that never had a quantity.
188
+ */
189
+ export function ReduceGrantForReturn(grantQuantity, originalLineQuantity, returnedQuantity) {
190
+ const returned = Math.abs(returnedQuantity);
191
+ const original = Math.abs(originalLineQuantity);
192
+ if (original <= 0 || returned >= original)
193
+ return { Quantity: grantQuantity, Revoke: true };
194
+ if (grantQuantity == null) {
195
+ // A Feature or AccessLevel: not countable, so a partial return cannot partially remove it.
196
+ // The customer still holds some of the thing that conferred it, so the grant stands.
197
+ return { Quantity: null, Revoke: false };
198
+ }
199
+ const remaining = grantQuantity * ((original - returned) / original);
200
+ // Rounded UP, for the same reason ResolveGrantQuantity rounds up: taking away a seat the customer
201
+ // still paid for is worse than leaving one they did not.
202
+ return { Quantity: Math.ceil(Math.round(remaining * 1e6) / 1e6), Revoke: false };
203
+ }
204
+ /** Subscription statuses that still confer access. Anything else is inactive. */
205
+ const ACCESSING_SUB_STATUS = new Set(['Active', 'Trialing']);
206
+ /**
207
+ * Is this grant in force at `asOf`?
208
+ *
209
+ * ONE function, because a second “is this in force” will drift from the first — that failure
210
+ * has already bitten this codebase twice. Bounds are inclusive: access holds at the exact
211
+ * `ValidFrom` / `ValidTo` / cancel `EndDate` instant.
212
+ *
213
+ * Cancelled subscriptions are the interesting case. The grant's `ValidTo` is the original term
214
+ * end, which is a LIE after an immediate cancel: access-through is `subscription.EndDate`
215
+ * (grace included). Grace can also EXTEND past `ValidTo`. Either way, `EndDate` is the bound
216
+ * once the sub is `Canceled`. Paused/Migrated refuse immediately — they have no grace story.
217
+ */
218
+ export function EvaluateGrantAccess(grant, asOf, subscription, term) {
219
+ const denied = (Decision) => ({
220
+ HasAccess: false,
221
+ Decision,
222
+ ValidFrom: grant.ValidFrom,
223
+ ValidTo: grant.ValidTo,
224
+ });
225
+ const granted = () => ({
226
+ HasAccess: true,
227
+ Decision: 'Granted',
228
+ ValidFrom: grant.ValidFrom,
229
+ ValidTo: grant.ValidTo,
230
+ });
231
+ if (grant.Status === 'Revoked')
232
+ return denied('Revoked');
233
+ if (grant.Status === 'Suspended')
234
+ return denied('Suspended');
235
+ if (grant.Status === 'Expired')
236
+ return denied('Expired');
237
+ if (grant.Status !== 'Active')
238
+ return denied('NoGrant');
239
+ if (grant.ValidFrom && asOf.getTime() < grant.ValidFrom.getTime()) {
240
+ return denied('NotYetValid');
241
+ }
242
+ // Linked rows we could not load: fail closed rather than grant on a guess.
243
+ if (grant.LinkedToSubscription && !subscription)
244
+ return denied('SubscriptionInactive');
245
+ if (grant.LinkedToTerm && !term)
246
+ return denied('SubscriptionInactive');
247
+ if (subscription && !ACCESSING_SUB_STATUS.has(subscription.Status)) {
248
+ if (subscription.Status === 'Canceled') {
249
+ if (!subscription.EndDate || asOf.getTime() > subscription.EndDate.getTime()) {
250
+ return {
251
+ HasAccess: false,
252
+ Decision: 'SubscriptionInactive',
253
+ ValidFrom: grant.ValidFrom,
254
+ ValidTo: subscription.EndDate,
255
+ };
256
+ }
257
+ // Inside access-through: EndDate is the operative bound — it can cut the original
258
+ // ValidTo short or extend it (grace). Report that, so CacheUntil and "access
259
+ // through {ValidTo}" both tell the truth.
260
+ return {
261
+ HasAccess: true,
262
+ Decision: 'Granted',
263
+ ValidFrom: grant.ValidFrom,
264
+ ValidTo: subscription.EndDate,
265
+ };
266
+ }
267
+ return denied('SubscriptionInactive');
268
+ }
269
+ if (grant.ValidTo && asOf.getTime() > grant.ValidTo.getTime()) {
270
+ return denied('Expired');
271
+ }
272
+ if (term) {
273
+ if (asOf.getTime() < term.StartDate.getTime())
274
+ return denied('NotYetValid');
275
+ if (asOf.getTime() > term.EndDate.getTime())
276
+ return denied('SubscriptionInactive');
277
+ }
278
+ return granted();
279
+ }
280
+ /** Lower rank wins among denials — “not yet” is a more useful screen than “an old grant was revoked”. */
281
+ const DENIAL_RANK = {
282
+ Granted: 0,
283
+ NotYetValid: 1,
284
+ Suspended: 2,
285
+ SubscriptionInactive: 3,
286
+ Expired: 4,
287
+ Revoked: 5,
288
+ NoGrant: 6,
289
+ };
290
+ /**
291
+ * One answer from many grants for the same Code. A Granted perpetual beats a Granted with an
292
+ * end; among denials the most useful reason wins, not the most recent row.
293
+ */
294
+ export function PickWinningAccess(results) {
295
+ if (!results.length)
296
+ return null;
297
+ const granted = results.filter((r) => r.HasAccess);
298
+ if (granted.length) {
299
+ return granted.reduce((best, r) => {
300
+ if (best.ValidTo == null)
301
+ return best;
302
+ if (r.ValidTo == null)
303
+ return r;
304
+ return r.ValidTo.getTime() > best.ValidTo.getTime() ? r : best;
305
+ });
306
+ }
307
+ return results.reduce((best, r) => DENIAL_RANK[r.Decision] < DENIAL_RANK[best.Decision] ? r : best);
308
+ }
309
+ /** Default CacheUntil cap for a check answer. Short so a revoke is felt quickly. */
310
+ export const ENTITLEMENT_CHECK_TTL_MS = 60_000;
311
+ /**
312
+ * When the caller may reuse this answer. Never past `ValidTo` on a grant that is in force —
313
+ * caching a Granted past its own end is how paid content stays open after the window closed.
314
+ */
315
+ export function CacheUntilFor(evaluatedAt, validTo, hasAccess) {
316
+ const cap = new Date(evaluatedAt.getTime() + ENTITLEMENT_CHECK_TTL_MS);
317
+ if (hasAccess && validTo != null && validTo.getTime() < cap.getTime()) {
318
+ return new Date(validTo.getTime());
319
+ }
320
+ return cap;
321
+ }
322
+ /**
323
+ * After `Orders.CancelSubscription`, stored grant Status is a lie unless we revoke when
324
+ * access has already ended. Grace (`AccessThroughDate` in the future) leaves the rows
325
+ * standing — the evaluator honours `subscription.EndDate`.
326
+ */
327
+ export function ShouldRevokeGrantsOnCancel(accessThroughDate, now) {
328
+ return now.getTime() > accessThroughDate.getTime();
329
+ }
330
+ //# sourceMappingURL=EntitlementBehavior.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntitlementBehavior.js","sourceRoot":"","sources":["../src/EntitlementBehavior.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * EntitlementEngine — turns a confirmed order into the grants it confers, and unwinds them on return.
3
+ *
4
+ * The server half of `EntitlementBehavior`: this finds the rows, that decides what they mean. Same
5
+ * split as `PriceResolver`/`PricingBehavior`, for the same reason — the rules about quantity, validity
6
+ * and timing are provable without a database, and only finding the templates needs one.
7
+ *
8
+ * BATCHED, NOT PER LINE. Every lookup here is one `RunView` across the whole order: the entitlement
9
+ * templates, the products, the category ancestors, the event dates, the terms. A sixty-line order
10
+ * would otherwise issue several hundred round trips inside the booking transaction, holding write
11
+ * locks the entire time. The volume bundle confirms sixty-line orders precisely to make that visible.
12
+ *
13
+ * THE FAST PATH MATTERS. Most orders grant nothing at all — no product on them has a single
14
+ * `ProductEntitlement` row. That case is one query and an early return, because entitlements must not
15
+ * make ordinary orders slower.
16
+ *
17
+ * WHY GRANTS ARE WRITTEN INSIDE THE BOOKING TRANSACTION. Access and the receivable are the same
18
+ * decision: an order that booked revenue but failed to grant access has charged for nothing, and one
19
+ * that granted access without booking has given something away. All-or-none is the only coherent
20
+ * option, which is also why a failure here rolls back the confirm rather than being logged.
21
+ *
22
+ * CONNECTS TO:
23
+ * PURE: ./EntitlementBehavior.ts
24
+ * CALLER: OrderEntityServer.Save (after subscriptions materialize — terms drive validity)
25
+ * DOC: plans/archive/bizapps-orders-master.md D27, D76
26
+ */
27
+ import { EntitySaveOptions, IMetadataProvider, UserInfo } from '@memberjunction/core';
28
+ import { type ValidityMode } from './EntitlementBehavior.js';
29
+ /** The line facts grant creation needs. Deliberately structural, so a caller can pass anything shaped right. */
30
+ export interface GrantableLine {
31
+ ID: string;
32
+ ProductID: string;
33
+ Quantity: number;
34
+ /** Per-line ship-to doubles as WHO an intangible line is for (D61) — the natural beneficiary. */
35
+ ShipToPersonID?: string | null;
36
+ ShipToOrganizationID?: string | null;
37
+ }
38
+ /** The order facts. `Balance`/`TotalGross` decide whether an OnPaidInFull grant starts Active. */
39
+ export interface GrantableOrder {
40
+ ID: string;
41
+ OrderDate: Date;
42
+ Balance: number | null;
43
+ TotalGross: number | null;
44
+ BillToPersonID?: string | null;
45
+ BillToOrganizationID?: string | null;
46
+ }
47
+ /** What `materializeSubscriptions` already knows, passed in rather than re-queried. */
48
+ export interface TermForLine {
49
+ ID: string;
50
+ StartDate: Date;
51
+ EndDate: Date;
52
+ }
53
+ export interface GrantOutcome {
54
+ Created: number;
55
+ /** Per grant, for the caller to log or assert on. */
56
+ Grants: Array<{
57
+ ID: string;
58
+ OrderLineID: string;
59
+ Code: string;
60
+ Status: string;
61
+ ValidityModeApplied: ValidityMode;
62
+ }>;
63
+ }
64
+ /**
65
+ * Create every grant this order's lines confer.
66
+ *
67
+ * A REVERSAL LINE GRANTS NOTHING. It unwinds a purchase, so running the grant path on it would hand
68
+ * the customer access as a side effect of returning the thing that conferred it — the same class of
69
+ * mistake as materializing a second subscription on a cancellation (D16).
70
+ */
71
+ export declare function CreateEntitlementGrants(order: GrantableOrder, lines: GrantableLine[], termsByLine: Map<string, TermForLine>, provider: IMetadataProvider, user: UserInfo, options?: EntitySaveOptions): Promise<GrantOutcome>;
72
+ /**
73
+ * Unwind the grants a returned line conferred.
74
+ *
75
+ * A full return REVOKES; a partial return reduces the quantity proportionally. Revocation stamps
76
+ * `RevokedAt` and a reason, because "this grant is revoked" with no record of when or why is exactly
77
+ * the shape that makes an access dispute unanswerable — and the database CHECK enforces the pairing.
78
+ *
79
+ * Uncountable grants (a Feature, an AccessLevel) survive a partial return: the customer still holds
80
+ * some of the thing that conferred it. Only sending all of it back takes the feature away.
81
+ */
82
+ export declare function RevokeGrantsForReturn(originOrderLineID: string, originQuantity: number, returnedQuantity: number, reason: string, provider: IMetadataProvider, user: UserInfo, options?: EntitySaveOptions): Promise<{
83
+ Revoked: number;
84
+ Reduced: number;
85
+ }>;
86
+ /**
87
+ * After a subscription cancel, make stored grant Status less of a lie — still not the read path.
88
+ *
89
+ * Access-through in the future (grace) leaves the rows `Active`; `EvaluateGrantAccess` honours
90
+ * `subscription.EndDate`. Access-through already passed (immediate cancel, no remaining grace)
91
+ * REVOKES, with a reason, so a poll of Status at least stops saying Active.
92
+ */
93
+ export declare function RevokeGrantsForCanceledSubscription(subscriptionID: string, accessThroughDate: Date, reason: string, provider: IMetadataProvider, user: UserInfo, options?: EntitySaveOptions, now?: Date): Promise<{
94
+ Revoked: number;
95
+ LeftStanding: number;
96
+ }>;
97
+ //# sourceMappingURL=EntitlementEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntitlementEngine.d.ts","sourceRoot":"","sources":["../src/EntitlementEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAGH,iBAAiB,EACjB,iBAAiB,EAGjB,QAAQ,EACX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAUH,KAAK,YAAY,EACpB,MAAM,0BAA0B,CAAC;AAclC,gHAAgH;AAChH,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iGAAiG;IACjG,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;CACvH;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CACzC,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,aAAa,EAAE,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,EACd,OAAO,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAsKvB;AA+ID;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACvC,iBAAiB,EAAE,MAAM,EACzB,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,EACd,OAAO,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C/C;AAED;;;;;;GAMG;AACH,wBAAsB,mCAAmC,CACrD,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,EACd,OAAO,CAAC,EAAE,iBAAiB,EAC3B,GAAG,GAAE,IAAiB,GACvB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAsCpD"}