@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.
- package/LICENSE +183 -0
- package/README.md +141 -2
- package/dist/AccountingBridge.d.ts +65 -0
- package/dist/AccountingBridge.d.ts.map +1 -0
- package/dist/AccountingBridge.js +102 -0
- package/dist/AccountingBridge.js.map +1 -0
- package/dist/AdvanceOrderStateOperation.d.ts +80 -0
- package/dist/AdvanceOrderStateOperation.d.ts.map +1 -0
- package/dist/AdvanceOrderStateOperation.js +267 -0
- package/dist/AdvanceOrderStateOperation.js.map +1 -0
- package/dist/ApplyAccountCreditOperation.d.ts +97 -0
- package/dist/ApplyAccountCreditOperation.d.ts.map +1 -0
- package/dist/ApplyAccountCreditOperation.js +275 -0
- package/dist/ApplyAccountCreditOperation.js.map +1 -0
- package/dist/BaseDeliveryChannel.d.ts +106 -0
- package/dist/BaseDeliveryChannel.d.ts.map +1 -0
- package/dist/BaseDeliveryChannel.js +36 -0
- package/dist/BaseDeliveryChannel.js.map +1 -0
- package/dist/BasePaymentProvider.d.ts +239 -0
- package/dist/BasePaymentProvider.d.ts.map +1 -0
- package/dist/BasePaymentProvider.js +91 -0
- package/dist/BasePaymentProvider.js.map +1 -0
- package/dist/BundleBehavior.d.ts +139 -0
- package/dist/BundleBehavior.d.ts.map +1 -0
- package/dist/BundleBehavior.js +173 -0
- package/dist/BundleBehavior.js.map +1 -0
- package/dist/BundleEngine.d.ts +60 -0
- package/dist/BundleEngine.d.ts.map +1 -0
- package/dist/BundleEngine.js +197 -0
- package/dist/BundleEngine.js.map +1 -0
- package/dist/CancelSubscriptionOperation.d.ts +98 -0
- package/dist/CancelSubscriptionOperation.d.ts.map +1 -0
- package/dist/CancelSubscriptionOperation.js +317 -0
- package/dist/CancelSubscriptionOperation.js.map +1 -0
- package/dist/CapturePaymentOperation.d.ts +62 -0
- package/dist/CapturePaymentOperation.d.ts.map +1 -0
- package/dist/CapturePaymentOperation.js +463 -0
- package/dist/CapturePaymentOperation.js.map +1 -0
- package/dist/CheckEntitlementOperation.d.ts +27 -0
- package/dist/CheckEntitlementOperation.d.ts.map +1 -0
- package/dist/CheckEntitlementOperation.js +45 -0
- package/dist/CheckEntitlementOperation.js.map +1 -0
- package/dist/CheckoutSessionService.d.ts +258 -0
- package/dist/CheckoutSessionService.d.ts.map +1 -0
- package/dist/CheckoutSessionService.js +1557 -0
- package/dist/CheckoutSessionService.js.map +1 -0
- package/dist/DeliveryBehavior.d.ts +121 -0
- package/dist/DeliveryBehavior.d.ts.map +1 -0
- package/dist/DeliveryBehavior.js +145 -0
- package/dist/DeliveryBehavior.js.map +1 -0
- package/dist/DeliveryRecipientResolver.d.ts +38 -0
- package/dist/DeliveryRecipientResolver.d.ts.map +1 -0
- package/dist/DeliveryRecipientResolver.js +93 -0
- package/dist/DeliveryRecipientResolver.js.map +1 -0
- package/dist/DeliveryResolver.d.ts +14 -0
- package/dist/DeliveryResolver.d.ts.map +1 -0
- package/dist/DeliveryResolver.js +45 -0
- package/dist/DeliveryResolver.js.map +1 -0
- package/dist/EmailDeliveryChannel.d.ts +31 -0
- package/dist/EmailDeliveryChannel.d.ts.map +1 -0
- package/dist/EmailDeliveryChannel.js +158 -0
- package/dist/EmailDeliveryChannel.js.map +1 -0
- package/dist/EntitlementBehavior.d.ts +235 -0
- package/dist/EntitlementBehavior.d.ts.map +1 -0
- package/dist/EntitlementBehavior.js +330 -0
- package/dist/EntitlementBehavior.js.map +1 -0
- package/dist/EntitlementEngine.d.ts +97 -0
- package/dist/EntitlementEngine.d.ts.map +1 -0
- package/dist/EntitlementEngine.js +338 -0
- package/dist/EntitlementEngine.js.map +1 -0
- package/dist/EntitlementGrantClaimDriver.d.ts +42 -0
- package/dist/EntitlementGrantClaimDriver.d.ts.map +1 -0
- package/dist/EntitlementGrantClaimDriver.js +158 -0
- package/dist/EntitlementGrantClaimDriver.js.map +1 -0
- package/dist/EntitlementRead.d.ts +82 -0
- package/dist/EntitlementRead.d.ts.map +1 -0
- package/dist/EntitlementRead.js +368 -0
- package/dist/EntitlementRead.js.map +1 -0
- package/dist/FulfillOrderLinesOperation.d.ts +34 -0
- package/dist/FulfillOrderLinesOperation.d.ts.map +1 -0
- package/dist/FulfillOrderLinesOperation.js +208 -0
- package/dist/FulfillOrderLinesOperation.js.map +1 -0
- package/dist/FulfillmentBehavior.d.ts +101 -0
- package/dist/FulfillmentBehavior.d.ts.map +1 -0
- package/dist/FulfillmentBehavior.js +145 -0
- package/dist/FulfillmentBehavior.js.map +1 -0
- package/dist/GLAccountResolver.d.ts +123 -0
- package/dist/GLAccountResolver.d.ts.map +1 -0
- package/dist/GLAccountResolver.js +175 -0
- package/dist/GLAccountResolver.js.map +1 -0
- package/dist/GetFulfillmentQueueOperation.d.ts +35 -0
- package/dist/GetFulfillmentQueueOperation.d.ts.map +1 -0
- package/dist/GetFulfillmentQueueOperation.js +221 -0
- package/dist/GetFulfillmentQueueOperation.js.map +1 -0
- package/dist/GetOverdueWorklistOperation.d.ts +41 -0
- package/dist/GetOverdueWorklistOperation.d.ts.map +1 -0
- package/dist/GetOverdueWorklistOperation.js +210 -0
- package/dist/GetOverdueWorklistOperation.js.map +1 -0
- package/dist/GiftCardBehavior.d.ts +97 -0
- package/dist/GiftCardBehavior.d.ts.map +1 -0
- package/dist/GiftCardBehavior.js +121 -0
- package/dist/GiftCardBehavior.js.map +1 -0
- package/dist/GiftCardEngine.d.ts +59 -0
- package/dist/GiftCardEngine.d.ts.map +1 -0
- package/dist/GiftCardEngine.js +197 -0
- package/dist/GiftCardEngine.js.map +1 -0
- package/dist/GuestOrderClaimDriver.d.ts +36 -0
- package/dist/GuestOrderClaimDriver.d.ts.map +1 -0
- package/dist/GuestOrderClaimDriver.js +161 -0
- package/dist/GuestOrderClaimDriver.js.map +1 -0
- package/dist/InvoiceBehavior.d.ts +394 -0
- package/dist/InvoiceBehavior.d.ts.map +1 -0
- package/dist/InvoiceBehavior.js +496 -0
- package/dist/InvoiceBehavior.js.map +1 -0
- package/dist/InvoiceBuilder.d.ts +48 -0
- package/dist/InvoiceBuilder.d.ts.map +1 -0
- package/dist/InvoiceBuilder.js +352 -0
- package/dist/InvoiceBuilder.js.map +1 -0
- package/dist/InvoiceDisplay.d.ts +96 -0
- package/dist/InvoiceDisplay.d.ts.map +1 -0
- package/dist/InvoiceDisplay.js +121 -0
- package/dist/InvoiceDisplay.js.map +1 -0
- package/dist/ListEntitlementsOperation.d.ts +21 -0
- package/dist/ListEntitlementsOperation.d.ts.map +1 -0
- package/dist/ListEntitlementsOperation.js +39 -0
- package/dist/ListEntitlementsOperation.js.map +1 -0
- package/dist/ManualPaymentProvider.d.ts +19 -0
- package/dist/ManualPaymentProvider.d.ts.map +1 -0
- package/dist/ManualPaymentProvider.js +92 -0
- package/dist/ManualPaymentProvider.js.map +1 -0
- package/dist/OrderEntityServer.d.ts +542 -0
- package/dist/OrderEntityServer.d.ts.map +1 -0
- package/dist/OrderEntityServer.js +2100 -0
- package/dist/OrderEntityServer.js.map +1 -0
- package/dist/OrderJournalEntryFactory.d.ts +140 -0
- package/dist/OrderJournalEntryFactory.d.ts.map +1 -0
- package/dist/OrderJournalEntryFactory.js +466 -0
- package/dist/OrderJournalEntryFactory.js.map +1 -0
- package/dist/OrderLineEntityServer.d.ts +90 -0
- package/dist/OrderLineEntityServer.d.ts.map +1 -0
- package/dist/OrderLineEntityServer.js +260 -0
- package/dist/OrderLineEntityServer.js.map +1 -0
- package/dist/OrdersSettings.d.ts +36 -0
- package/dist/OrdersSettings.d.ts.map +1 -0
- package/dist/OrdersSettings.js +147 -0
- package/dist/OrdersSettings.js.map +1 -0
- package/dist/PaymentAllocationFactory.d.ts +128 -0
- package/dist/PaymentAllocationFactory.d.ts.map +1 -0
- package/dist/PaymentAllocationFactory.js +235 -0
- package/dist/PaymentAllocationFactory.js.map +1 -0
- package/dist/PaymentHeaderEntityServer.d.ts +180 -0
- package/dist/PaymentHeaderEntityServer.d.ts.map +1 -0
- package/dist/PaymentHeaderEntityServer.js +656 -0
- package/dist/PaymentHeaderEntityServer.js.map +1 -0
- package/dist/PaymentIntentService.d.ts +101 -0
- package/dist/PaymentIntentService.d.ts.map +1 -0
- package/dist/PaymentIntentService.js +150 -0
- package/dist/PaymentIntentService.js.map +1 -0
- package/dist/PaymentJournalEntryFactory.d.ts +99 -0
- package/dist/PaymentJournalEntryFactory.d.ts.map +1 -0
- package/dist/PaymentJournalEntryFactory.js +127 -0
- package/dist/PaymentJournalEntryFactory.js.map +1 -0
- package/dist/PaymentLineEntityServer.d.ts +73 -0
- package/dist/PaymentLineEntityServer.d.ts.map +1 -0
- package/dist/PaymentLineEntityServer.js +303 -0
- package/dist/PaymentLineEntityServer.js.map +1 -0
- package/dist/PaymentProviderBehavior.d.ts +222 -0
- package/dist/PaymentProviderBehavior.d.ts.map +1 -0
- package/dist/PaymentProviderBehavior.js +368 -0
- package/dist/PaymentProviderBehavior.js.map +1 -0
- package/dist/PaymentProviderResolver.d.ts +84 -0
- package/dist/PaymentProviderResolver.d.ts.map +1 -0
- package/dist/PaymentProviderResolver.js +220 -0
- package/dist/PaymentProviderResolver.js.map +1 -0
- package/dist/PaymentReversalFactory.d.ts +107 -0
- package/dist/PaymentReversalFactory.d.ts.map +1 -0
- package/dist/PaymentReversalFactory.js +174 -0
- package/dist/PaymentReversalFactory.js.map +1 -0
- package/dist/PaymentSettlement.d.ts +56 -0
- package/dist/PaymentSettlement.d.ts.map +1 -0
- package/dist/PaymentSettlement.js +243 -0
- package/dist/PaymentSettlement.js.map +1 -0
- package/dist/PaymentTermsBehavior.d.ts +109 -0
- package/dist/PaymentTermsBehavior.d.ts.map +1 -0
- package/dist/PaymentTermsBehavior.js +172 -0
- package/dist/PaymentTermsBehavior.js.map +1 -0
- package/dist/PaymentWebhookHandler.d.ts +103 -0
- package/dist/PaymentWebhookHandler.d.ts.map +1 -0
- package/dist/PaymentWebhookHandler.js +246 -0
- package/dist/PaymentWebhookHandler.js.map +1 -0
- package/dist/PreviewPriceOperation.d.ts +62 -0
- package/dist/PreviewPriceOperation.d.ts.map +1 -0
- package/dist/PreviewPriceOperation.js +161 -0
- package/dist/PreviewPriceOperation.js.map +1 -0
- package/dist/PriceOrderOperation.d.ts +93 -0
- package/dist/PriceOrderOperation.d.ts.map +1 -0
- package/dist/PriceOrderOperation.js +146 -0
- package/dist/PriceOrderOperation.js.map +1 -0
- package/dist/ProductPriceEntityServer.d.ts +34 -0
- package/dist/ProductPriceEntityServer.d.ts.map +1 -0
- package/dist/ProductPriceEntityServer.js +97 -0
- package/dist/ProductPriceEntityServer.js.map +1 -0
- package/dist/RefundPaymentOperation.d.ts +61 -0
- package/dist/RefundPaymentOperation.d.ts.map +1 -0
- package/dist/RefundPaymentOperation.js +177 -0
- package/dist/RefundPaymentOperation.js.map +1 -0
- package/dist/RevenueRecognition.d.ts +76 -0
- package/dist/RevenueRecognition.d.ts.map +1 -0
- package/dist/RevenueRecognition.js +133 -0
- package/dist/RevenueRecognition.js.map +1 -0
- package/dist/ReversalBehavior.d.ts +82 -0
- package/dist/ReversalBehavior.d.ts.map +1 -0
- package/dist/ReversalBehavior.js +100 -0
- package/dist/ReversalBehavior.js.map +1 -0
- package/dist/ReversalResolver.d.ts +37 -0
- package/dist/ReversalResolver.d.ts.map +1 -0
- package/dist/ReversalResolver.js +96 -0
- package/dist/ReversalResolver.js.map +1 -0
- package/dist/SpawnRenewalsOperation.d.ts +109 -0
- package/dist/SpawnRenewalsOperation.d.ts.map +1 -0
- package/dist/SpawnRenewalsOperation.js +295 -0
- package/dist/SpawnRenewalsOperation.js.map +1 -0
- package/dist/StoredValuePaymentProvider.d.ts +52 -0
- package/dist/StoredValuePaymentProvider.d.ts.map +1 -0
- package/dist/StoredValuePaymentProvider.js +205 -0
- package/dist/StoredValuePaymentProvider.js.map +1 -0
- package/dist/StripeACHPaymentProvider.d.ts +50 -0
- package/dist/StripeACHPaymentProvider.d.ts.map +1 -0
- package/dist/StripeACHPaymentProvider.js +211 -0
- package/dist/StripeACHPaymentProvider.js.map +1 -0
- package/dist/StripePaymentProvider.d.ts +83 -0
- package/dist/StripePaymentProvider.d.ts.map +1 -0
- package/dist/StripePaymentProvider.js +442 -0
- package/dist/StripePaymentProvider.js.map +1 -0
- package/dist/SubscriptionBehavior.d.ts +197 -0
- package/dist/SubscriptionBehavior.d.ts.map +1 -0
- package/dist/SubscriptionBehavior.js +415 -0
- package/dist/SubscriptionBehavior.js.map +1 -0
- package/dist/checkoutCaptureAlert.d.ts +16 -0
- package/dist/checkoutCaptureAlert.d.ts.map +1 -0
- package/dist/checkoutCaptureAlert.js +55 -0
- package/dist/checkoutCaptureAlert.js.map +1 -0
- package/dist/checkoutCaptureRetry.d.ts +27 -0
- package/dist/checkoutCaptureRetry.d.ts.map +1 -0
- package/dist/checkoutCaptureRetry.js +50 -0
- package/dist/checkoutCaptureRetry.js.map +1 -0
- package/dist/claimDriverHelpers.d.ts +15 -0
- package/dist/claimDriverHelpers.d.ts.map +1 -0
- package/dist/claimDriverHelpers.js +34 -0
- package/dist/claimDriverHelpers.js.map +1 -0
- package/dist/entity-names.d.ts +15 -0
- package/dist/entity-names.d.ts.map +1 -0
- package/dist/entity-names.js +15 -0
- package/dist/entity-names.js.map +1 -0
- package/dist/identityClaimContracts.d.ts +118 -0
- package/dist/identityClaimContracts.d.ts.map +1 -0
- package/dist/identityClaimContracts.js +58 -0
- package/dist/identityClaimContracts.js.map +1 -0
- package/dist/index.d.ts +126 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +115 -0
- package/dist/index.js.map +1 -0
- package/dist/sql-guards.d.ts +78 -0
- package/dist/sql-guards.d.ts.map +1 -0
- package/dist/sql-guards.js +115 -0
- package/dist/sql-guards.js.map +1 -0
- package/package.json +50 -5
|
@@ -0,0 +1,338 @@
|
|
|
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 { CompositeKey, RunView, } from '@memberjunction/core';
|
|
28
|
+
import { InitialGrantStatus, ReduceGrantForReturn, ResolveEntitlementPolicy, ResolveGrantQuantity, ResolveValidityWindow, ShouldRevokeGrantsOnCancel, } from './EntitlementBehavior.js';
|
|
29
|
+
import { RequireUUID } from './sql-guards.js';
|
|
30
|
+
const PRODUCT_ENTITLEMENT_ENTITY = 'MJ_BizApps_Orders: Product Entitlements';
|
|
31
|
+
const ENTITLEMENT_GRANT_ENTITY = 'MJ_BizApps_Orders: Entitlement Grants';
|
|
32
|
+
const PRODUCT_ENTITY = 'MJ_BizApps_Orders: Products';
|
|
33
|
+
const PRODUCT_CATEGORY_ENTITY = 'MJ_BizApps_Orders: Product Categories';
|
|
34
|
+
const PRODUCT_TYPE_ENTITY = 'MJ_BizApps_Orders: Product Types';
|
|
35
|
+
const EVENT_PRODUCT_ENTITY = 'MJ_BizApps_Orders: Event Products';
|
|
36
|
+
const SUBSCRIPTION_TERM_ENTITY = 'MJ_BizApps_Orders: Subscription Terms';
|
|
37
|
+
const key = (id) => (id ?? '').toLowerCase();
|
|
38
|
+
const quote = (ids) => [...new Set(ids.map((i) => `'${i}'`))].join(',');
|
|
39
|
+
/**
|
|
40
|
+
* Create every grant this order's lines confer.
|
|
41
|
+
*
|
|
42
|
+
* A REVERSAL LINE GRANTS NOTHING. It unwinds a purchase, so running the grant path on it would hand
|
|
43
|
+
* the customer access as a side effect of returning the thing that conferred it — the same class of
|
|
44
|
+
* mistake as materializing a second subscription on a cancellation (D16).
|
|
45
|
+
*/
|
|
46
|
+
export async function CreateEntitlementGrants(order, lines, termsByLine, provider, user, options) {
|
|
47
|
+
const out = { Created: 0, Grants: [] };
|
|
48
|
+
// Reversal lines and anything with no positive quantity confer nothing.
|
|
49
|
+
const grantable = lines.filter((l) => Number(l.Quantity ?? 0) > 0);
|
|
50
|
+
if (!grantable.length)
|
|
51
|
+
return out;
|
|
52
|
+
const rv = new RunView(provider);
|
|
53
|
+
const productIDs = [...new Set(grantable.map((l) => l.ProductID))];
|
|
54
|
+
// THE FAST PATH. One query, and most orders stop here.
|
|
55
|
+
const templates = await rv.RunView({
|
|
56
|
+
EntityName: PRODUCT_ENTITLEMENT_ENTITY,
|
|
57
|
+
ExtraFilter: `ProductID IN (${quote(productIDs)}) AND IsActive = 1`,
|
|
58
|
+
ResultType: 'simple',
|
|
59
|
+
}, user);
|
|
60
|
+
const templateRows = templates?.Results ?? [];
|
|
61
|
+
if (!templateRows.length)
|
|
62
|
+
return out;
|
|
63
|
+
const templatesByProduct = new Map();
|
|
64
|
+
for (const t of templateRows) {
|
|
65
|
+
const list = templatesByProduct.get(key(t.ProductID)) ?? [];
|
|
66
|
+
list.push(t);
|
|
67
|
+
templatesByProduct.set(key(t.ProductID), list);
|
|
68
|
+
}
|
|
69
|
+
// Only the products that actually grant something need their policy resolved.
|
|
70
|
+
const relevantProductIDs = productIDs.filter((id) => templatesByProduct.has(key(id)));
|
|
71
|
+
const policy = await loadPolicyInputs(relevantProductIDs, provider, user);
|
|
72
|
+
// Event dates, for EventWindow. One query for every event product on the order.
|
|
73
|
+
const events = await rv.RunView({
|
|
74
|
+
EntityName: EVENT_PRODUCT_ENTITY,
|
|
75
|
+
ExtraFilter: `ID IN (${quote(relevantProductIDs)})`,
|
|
76
|
+
ResultType: 'simple',
|
|
77
|
+
}, user);
|
|
78
|
+
const eventByProduct = new Map((events?.Results ?? []).map((e) => [key(e.ID), e]));
|
|
79
|
+
// NORMALISE THE CALLER'S KEYS. `materializeSubscriptions` keys `TermsByLine` by the raw line ID
|
|
80
|
+
// as SQL returned it — uppercase — and a lowercased lookup here found nothing, so EVERY
|
|
81
|
+
// subscription grant fell back to Perpetual while looking entirely correct. The suite caught it
|
|
82
|
+
// only because EN6 asserts `ValidityModeApplied` rather than just the dates.
|
|
83
|
+
//
|
|
84
|
+
// Re-keying here rather than asking the caller to change: a map that arrives with unknown casing
|
|
85
|
+
// is the caller's business, and normalising on receipt cannot be forgotten by the next caller.
|
|
86
|
+
const termsByLineKey = new Map();
|
|
87
|
+
for (const [lineID, term] of termsByLine)
|
|
88
|
+
termsByLineKey.set(key(lineID), term);
|
|
89
|
+
// Subscription IDs behind the terms, so a grant can point at both.
|
|
90
|
+
const termIDs = [...termsByLineKey.values()].map((t) => t.ID);
|
|
91
|
+
const subscriptionByTerm = new Map();
|
|
92
|
+
if (termIDs.length) {
|
|
93
|
+
const terms = await rv.RunView({
|
|
94
|
+
EntityName: SUBSCRIPTION_TERM_ENTITY,
|
|
95
|
+
ExtraFilter: `ID IN (${quote(termIDs)})`,
|
|
96
|
+
ResultType: 'simple',
|
|
97
|
+
}, user);
|
|
98
|
+
for (const t of terms?.Results ?? [])
|
|
99
|
+
subscriptionByTerm.set(key(t.ID), t.SubscriptionID);
|
|
100
|
+
}
|
|
101
|
+
for (const line of grantable) {
|
|
102
|
+
const forThisProduct = templatesByProduct.get(key(line.ProductID)) ?? [];
|
|
103
|
+
if (!forThisProduct.length)
|
|
104
|
+
continue;
|
|
105
|
+
const inputs = policy.get(key(line.ProductID));
|
|
106
|
+
if (!inputs) {
|
|
107
|
+
// The product grants something but could not be read. Refusing beats granting on a guess:
|
|
108
|
+
// an access decision made from defaults nobody configured is not a decision.
|
|
109
|
+
throw new Error(`Order line for product ${line.ProductID} confers entitlements, but the product's ` +
|
|
110
|
+
`policy could not be resolved (its type or category tree could not be read). ` +
|
|
111
|
+
`Access is refused rather than granted on assumed defaults.`);
|
|
112
|
+
}
|
|
113
|
+
const term = termsByLineKey.get(key(line.ID)) ?? null;
|
|
114
|
+
for (const template of forThisProduct) {
|
|
115
|
+
const resolved = ResolveEntitlementPolicy(inputs.Product, inputs.CategoryChain, inputs.Type, { ValidityMode: template.ValidityMode });
|
|
116
|
+
const event = eventByProduct.get(key(line.ProductID));
|
|
117
|
+
const validity = ResolveValidityWindow(resolved.ValidityMode, {
|
|
118
|
+
GrantedOn: order.OrderDate,
|
|
119
|
+
DurationDays: template.ValidityDurationDays,
|
|
120
|
+
EventStartsAt: event?.EventStartsAt ? new Date(event.EventStartsAt) : null,
|
|
121
|
+
EventEndsAt: event?.EventEndsAt ? new Date(event.EventEndsAt) : null,
|
|
122
|
+
AccessLeadHours: template.AccessLeadHours,
|
|
123
|
+
AccessLagHours: template.AccessLagHours,
|
|
124
|
+
TermStartDate: term ? new Date(term.StartDate) : null,
|
|
125
|
+
TermEndDate: term ? new Date(term.EndDate) : null,
|
|
126
|
+
});
|
|
127
|
+
const grant = await provider.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, user);
|
|
128
|
+
grant.NewRecord();
|
|
129
|
+
grant.ProductEntitlementID = template.ID;
|
|
130
|
+
grant.OrderLineID = line.ID;
|
|
131
|
+
if (term) {
|
|
132
|
+
grant.SubscriptionTermID = term.ID;
|
|
133
|
+
const subID = subscriptionByTerm.get(key(term.ID));
|
|
134
|
+
if (subID)
|
|
135
|
+
grant.SubscriptionID = subID;
|
|
136
|
+
}
|
|
137
|
+
// BENEFICIARY: the line's own ship-to first, because that is where 'who is this for'
|
|
138
|
+
// lives (D61) — a seat bought for a named colleague, a ticket for an attendee, a gift for
|
|
139
|
+
// a recipient. Only then the buyer.
|
|
140
|
+
const person = line.ShipToPersonID ?? order.BillToPersonID ?? null;
|
|
141
|
+
const organization = line.ShipToOrganizationID ?? order.BillToOrganizationID ?? null;
|
|
142
|
+
if (person)
|
|
143
|
+
grant.BeneficiaryPersonID = person;
|
|
144
|
+
if (organization)
|
|
145
|
+
grant.BeneficiaryOrganizationID = organization;
|
|
146
|
+
grant.Quantity = ResolveGrantQuantity(template.Quantity, Number(line.Quantity ?? 0), resolved.QuantityMode);
|
|
147
|
+
grant.ValidFrom = validity.ValidFrom;
|
|
148
|
+
grant.ValidTo = validity.ValidTo;
|
|
149
|
+
grant.ValidityModeApplied = validity.ModeApplied;
|
|
150
|
+
const status = InitialGrantStatus(resolved.GrantTiming, order);
|
|
151
|
+
grant.Status = status;
|
|
152
|
+
if (!(await grant.Save(options))) {
|
|
153
|
+
throw new Error(`Failed to grant entitlement '${template.Code}' for order line ${line.ID}: ` +
|
|
154
|
+
`${grant.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
155
|
+
}
|
|
156
|
+
out.Created++;
|
|
157
|
+
out.Grants.push({
|
|
158
|
+
ID: grant.ID,
|
|
159
|
+
OrderLineID: line.ID,
|
|
160
|
+
Code: template.Code,
|
|
161
|
+
Status: status,
|
|
162
|
+
ValidityModeApplied: validity.ModeApplied,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Load the policy chain for every relevant product, in a bounded number of queries.
|
|
170
|
+
*
|
|
171
|
+
* The category tree is walked BREADTH-FIRST across all products at once rather than depth-first per
|
|
172
|
+
* product: one query per LEVEL of the deepest tree, not one per ancestor per product. A three-level
|
|
173
|
+
* tree over forty products is three queries instead of a hundred and twenty.
|
|
174
|
+
*
|
|
175
|
+
* The depth cap is a cycle guard, not a modelling limit. `CK_ProductCategory_NoSelfParent` stops a
|
|
176
|
+
* category being its own parent but nothing stops A→B→A, and an unbounded walk on that would hang
|
|
177
|
+
* inside the booking transaction — the worst possible place.
|
|
178
|
+
*/
|
|
179
|
+
async function loadPolicyInputs(productIDs, provider, user) {
|
|
180
|
+
const rv = new RunView(provider);
|
|
181
|
+
const out = new Map();
|
|
182
|
+
if (!productIDs.length)
|
|
183
|
+
return out;
|
|
184
|
+
const products = await rv.RunView({
|
|
185
|
+
EntityName: PRODUCT_ENTITY,
|
|
186
|
+
ExtraFilter: `ID IN (${quote(productIDs)})`,
|
|
187
|
+
ResultType: 'simple',
|
|
188
|
+
}, user);
|
|
189
|
+
const productRows = products?.Results ?? [];
|
|
190
|
+
if (!productRows.length)
|
|
191
|
+
return out;
|
|
192
|
+
const types = await rv.RunView({
|
|
193
|
+
EntityName: PRODUCT_TYPE_ENTITY,
|
|
194
|
+
ExtraFilter: `ID IN (${quote(productRows.map((p) => p.ProductTypeID))})`,
|
|
195
|
+
ResultType: 'simple',
|
|
196
|
+
}, user);
|
|
197
|
+
const typeByID = new Map((types?.Results ?? []).map((t) => [key(t.ID), t]));
|
|
198
|
+
// Breadth-first up the tree, gathering every level in one query each.
|
|
199
|
+
const categoryByID = new Map();
|
|
200
|
+
let frontier = [...new Set(productRows.map((p) => p.ProductCategoryID).filter((c) => !!c))];
|
|
201
|
+
const MAX_DEPTH = 20;
|
|
202
|
+
for (let depth = 0; depth < MAX_DEPTH && frontier.length; depth++) {
|
|
203
|
+
const level = await rv.RunView({
|
|
204
|
+
EntityName: PRODUCT_CATEGORY_ENTITY,
|
|
205
|
+
ExtraFilter: `ID IN (${quote(frontier)})`,
|
|
206
|
+
ResultType: 'simple',
|
|
207
|
+
}, user);
|
|
208
|
+
const rows = level?.Results ?? [];
|
|
209
|
+
if (!rows.length)
|
|
210
|
+
break;
|
|
211
|
+
for (const r of rows) {
|
|
212
|
+
categoryByID.set(key(r.ID), {
|
|
213
|
+
ID: r.ID,
|
|
214
|
+
ParentProductCategoryID: r.ParentProductCategoryID,
|
|
215
|
+
EntitlementGrantTiming: r.DefaultEntitlementGrantTiming,
|
|
216
|
+
EntitlementQuantityMode: r.DefaultEntitlementQuantityMode,
|
|
217
|
+
EntitlementValidityMode: r.DefaultEntitlementValidityMode,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
frontier = [
|
|
221
|
+
...new Set(rows
|
|
222
|
+
.map((r) => r.ParentProductCategoryID)
|
|
223
|
+
.filter((c) => !!c && !categoryByID.has(key(c)))),
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
for (const p of productRows) {
|
|
227
|
+
const type = typeByID.get(key(p.ProductTypeID));
|
|
228
|
+
if (!type)
|
|
229
|
+
continue; // caller refuses — see CreateEntitlementGrants
|
|
230
|
+
// NEAREST FIRST, and stop on a repeat: a cyclic tree must not loop forever.
|
|
231
|
+
const chain = [];
|
|
232
|
+
const seen = new Set();
|
|
233
|
+
let cursor = p.ProductCategoryID;
|
|
234
|
+
while (cursor && !seen.has(key(cursor)) && chain.length < MAX_DEPTH) {
|
|
235
|
+
seen.add(key(cursor));
|
|
236
|
+
const cat = categoryByID.get(key(cursor));
|
|
237
|
+
if (!cat)
|
|
238
|
+
break;
|
|
239
|
+
chain.push({
|
|
240
|
+
ID: cat.ID,
|
|
241
|
+
EntitlementGrantTiming: cat.EntitlementGrantTiming,
|
|
242
|
+
EntitlementQuantityMode: cat.EntitlementQuantityMode,
|
|
243
|
+
EntitlementValidityMode: cat.EntitlementValidityMode,
|
|
244
|
+
});
|
|
245
|
+
cursor = cat.ParentProductCategoryID;
|
|
246
|
+
}
|
|
247
|
+
out.set(key(p.ID), {
|
|
248
|
+
Product: {
|
|
249
|
+
EntitlementGrantTiming: p.EntitlementGrantTiming,
|
|
250
|
+
EntitlementQuantityMode: p.EntitlementQuantityMode,
|
|
251
|
+
EntitlementValidityMode: p.EntitlementValidityMode,
|
|
252
|
+
},
|
|
253
|
+
CategoryChain: chain,
|
|
254
|
+
Type: type,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Unwind the grants a returned line conferred.
|
|
261
|
+
*
|
|
262
|
+
* A full return REVOKES; a partial return reduces the quantity proportionally. Revocation stamps
|
|
263
|
+
* `RevokedAt` and a reason, because "this grant is revoked" with no record of when or why is exactly
|
|
264
|
+
* the shape that makes an access dispute unanswerable — and the database CHECK enforces the pairing.
|
|
265
|
+
*
|
|
266
|
+
* Uncountable grants (a Feature, an AccessLevel) survive a partial return: the customer still holds
|
|
267
|
+
* some of the thing that conferred it. Only sending all of it back takes the feature away.
|
|
268
|
+
*/
|
|
269
|
+
export async function RevokeGrantsForReturn(originOrderLineID, originQuantity, returnedQuantity, reason, provider, user, options) {
|
|
270
|
+
const rv = new RunView(provider);
|
|
271
|
+
const grants = await rv.RunView({
|
|
272
|
+
EntityName: ENTITLEMENT_GRANT_ENTITY,
|
|
273
|
+
ExtraFilter: `OrderLineID = '${originOrderLineID}' AND Status IN ('Active','Suspended')`,
|
|
274
|
+
ResultType: 'simple',
|
|
275
|
+
}, user);
|
|
276
|
+
let revoked = 0;
|
|
277
|
+
let reduced = 0;
|
|
278
|
+
for (const row of grants?.Results ?? []) {
|
|
279
|
+
const decision = ReduceGrantForReturn(row.Quantity, originQuantity, returnedQuantity);
|
|
280
|
+
// `GetEntityObject` with a CompositeKey loads it — the house idiom, same as
|
|
281
|
+
// CancelSubscriptionOperation.
|
|
282
|
+
const grant = await provider.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, CompositeKey.FromID(row.ID), user);
|
|
283
|
+
if (decision.Revoke) {
|
|
284
|
+
grant.Status = 'Revoked';
|
|
285
|
+
grant.RevokedAt = new Date();
|
|
286
|
+
grant.RevocationReason = reason;
|
|
287
|
+
revoked++;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
if (decision.Quantity === row.Quantity)
|
|
291
|
+
continue; // nothing to change
|
|
292
|
+
grant.Quantity = decision.Quantity;
|
|
293
|
+
reduced++;
|
|
294
|
+
}
|
|
295
|
+
if (!(await grant.Save(options))) {
|
|
296
|
+
throw new Error(`Failed to unwind entitlement grant ${row.ID} for the returned line: ` +
|
|
297
|
+
`${grant.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { Revoked: revoked, Reduced: reduced };
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* After a subscription cancel, make stored grant Status less of a lie — still not the read path.
|
|
304
|
+
*
|
|
305
|
+
* Access-through in the future (grace) leaves the rows `Active`; `EvaluateGrantAccess` honours
|
|
306
|
+
* `subscription.EndDate`. Access-through already passed (immediate cancel, no remaining grace)
|
|
307
|
+
* REVOKES, with a reason, so a poll of Status at least stops saying Active.
|
|
308
|
+
*/
|
|
309
|
+
export async function RevokeGrantsForCanceledSubscription(subscriptionID, accessThroughDate, reason, provider, user, options, now = new Date()) {
|
|
310
|
+
const id = RequireUUID(subscriptionID, 'SubscriptionID');
|
|
311
|
+
const rv = new RunView(provider);
|
|
312
|
+
const grants = await rv.RunView({
|
|
313
|
+
EntityName: ENTITLEMENT_GRANT_ENTITY,
|
|
314
|
+
ExtraFilter: `SubscriptionID = '${id}' AND Status IN ('Active','Suspended')`,
|
|
315
|
+
ResultType: 'simple',
|
|
316
|
+
BypassCache: true,
|
|
317
|
+
}, user);
|
|
318
|
+
const rows = grants?.Results ?? [];
|
|
319
|
+
if (!rows.length)
|
|
320
|
+
return { Revoked: 0, LeftStanding: 0 };
|
|
321
|
+
if (!ShouldRevokeGrantsOnCancel(accessThroughDate, now)) {
|
|
322
|
+
return { Revoked: 0, LeftStanding: rows.length };
|
|
323
|
+
}
|
|
324
|
+
let revoked = 0;
|
|
325
|
+
for (const row of rows) {
|
|
326
|
+
const grant = await provider.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, CompositeKey.FromID(row.ID), user);
|
|
327
|
+
grant.Status = 'Revoked';
|
|
328
|
+
grant.RevokedAt = now;
|
|
329
|
+
grant.RevocationReason = reason;
|
|
330
|
+
if (!(await grant.Save(options))) {
|
|
331
|
+
throw new Error(`Failed to revoke entitlement grant ${row.ID} after subscription cancel: ` +
|
|
332
|
+
`${grant.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
333
|
+
}
|
|
334
|
+
revoked++;
|
|
335
|
+
}
|
|
336
|
+
return { Revoked: revoked, LeftStanding: 0 };
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=EntitlementEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitlementEngine.js","sourceRoot":"","sources":["../src/EntitlementEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entitlement Grant Claim Driver
|
|
3
|
+
*
|
|
4
|
+
* Implements the BaseIdentityClaimDriver interface for BizApps Orders entitlement grants.
|
|
5
|
+
* When an external user redeems an invitation or magic link claim, this driver attaches the
|
|
6
|
+
* EntitlementGrant to their authenticated Person record and activates the grant.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/orders-core-entities-server/EntitlementGrantClaimDriver
|
|
9
|
+
*/
|
|
10
|
+
import { BaseIdentityClaimDriver, type ClaimContext, type ClaimRedeemContext, type ClaimResult } from './identityClaimContracts.js';
|
|
11
|
+
/**
|
|
12
|
+
* Pluggable driver for EntitlementGrant identity claims.
|
|
13
|
+
*/
|
|
14
|
+
export declare class EntitlementGrantClaimDriver extends BaseIdentityClaimDriver {
|
|
15
|
+
/**
|
|
16
|
+
* Hook called after an entitlement claim is created.
|
|
17
|
+
*/
|
|
18
|
+
OnCreate(context: ClaimContext): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Hook called when an entitlement claim is redeemed by an authenticated user.
|
|
21
|
+
*/
|
|
22
|
+
OnClaim(context: ClaimRedeemContext): Promise<ClaimResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Hook called when an entitlement claim is revoked.
|
|
25
|
+
*
|
|
26
|
+
* `CK_EntitlementGrant_Revocation` (and the generated `ValidateRevokedAtBasedOnStatus`
|
|
27
|
+
* rule) require `RevokedAt` whenever Status is 'Revoked' — a revocation without the
|
|
28
|
+
* timestamp fails validation and the grant silently stays Active. Stamp both, and
|
|
29
|
+
* surface a failed save instead of swallowing it.
|
|
30
|
+
*/
|
|
31
|
+
OnRevoke(context: ClaimContext): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Hook called when an entitlement claim expires. (`RevokedAt` must NOT be set here — the
|
|
34
|
+
* validation rule requires it to be null for any non-Revoked status.)
|
|
35
|
+
*/
|
|
36
|
+
OnExpire(context: ClaimContext): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Registration helper to ensure class factory decorator executes.
|
|
40
|
+
*/
|
|
41
|
+
export declare function LoadEntitlementGrantClaimDriver(): void;
|
|
42
|
+
//# sourceMappingURL=EntitlementGrantClaimDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitlementGrantClaimDriver.d.ts","sourceRoot":"","sources":["../src/EntitlementGrantClaimDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACH,uBAAuB,EACvB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACnB,MAAM,6BAA6B,CAAC;AAMrC;;GAEG;AACH,qBACa,2BAA4B,SAAQ,uBAAuB;IACpE;;OAEG;IACU,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;OAEG;IACU,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC;IAiEvE;;;;;;;OAOG;IACU,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B3D;;;OAGG;IACU,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAuB9D;AAED;;GAEG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entitlement Grant Claim Driver
|
|
3
|
+
*
|
|
4
|
+
* Implements the BaseIdentityClaimDriver interface for BizApps Orders entitlement grants.
|
|
5
|
+
* When an external user redeems an invitation or magic link claim, this driver attaches the
|
|
6
|
+
* EntitlementGrant to their authenticated Person record and activates the grant.
|
|
7
|
+
*
|
|
8
|
+
* @module @mj-biz-apps/orders-core-entities-server/EntitlementGrantClaimDriver
|
|
9
|
+
*/
|
|
10
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
11
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
12
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
13
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
};
|
|
16
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
17
|
+
import { LogError, Metadata } from '@memberjunction/core';
|
|
18
|
+
// Local fallback contracts — see identityClaimContracts.ts. Restore this import to
|
|
19
|
+
// '@memberjunction/core-entities' once MJ publishes these exports.
|
|
20
|
+
import { BaseIdentityClaimDriver, } from './identityClaimContracts.js';
|
|
21
|
+
import { resolvePersonID } from './claimDriverHelpers.js';
|
|
22
|
+
const ENTITLEMENT_GRANT_ENTITY = 'MJ_BizApps_Orders: Entitlement Grants';
|
|
23
|
+
/**
|
|
24
|
+
* Pluggable driver for EntitlementGrant identity claims.
|
|
25
|
+
*/
|
|
26
|
+
let EntitlementGrantClaimDriver = class EntitlementGrantClaimDriver extends BaseIdentityClaimDriver {
|
|
27
|
+
/**
|
|
28
|
+
* Hook called after an entitlement claim is created.
|
|
29
|
+
*/
|
|
30
|
+
async OnCreate(context) {
|
|
31
|
+
// Claim registered in pending state
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Hook called when an entitlement claim is redeemed by an authenticated user.
|
|
35
|
+
*/
|
|
36
|
+
async OnClaim(context) {
|
|
37
|
+
const { Claim, User } = context;
|
|
38
|
+
let grantID = Claim.RecordID;
|
|
39
|
+
if (!grantID && Claim.PayloadJSON) {
|
|
40
|
+
try {
|
|
41
|
+
const payload = JSON.parse(Claim.PayloadJSON);
|
|
42
|
+
if (typeof payload.GrantID === 'string') {
|
|
43
|
+
grantID = payload.GrantID;
|
|
44
|
+
}
|
|
45
|
+
else if (typeof payload.grantId === 'string') {
|
|
46
|
+
grantID = payload.grantId;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore parse errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!grantID) {
|
|
54
|
+
return {
|
|
55
|
+
Success: false,
|
|
56
|
+
ErrorMessage: 'Claim does not reference an EntitlementGrant RecordID'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const md = new Metadata();
|
|
60
|
+
const grant = await md.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, User);
|
|
61
|
+
const loaded = await grant.Load(grantID);
|
|
62
|
+
if (!loaded) {
|
|
63
|
+
return {
|
|
64
|
+
Success: false,
|
|
65
|
+
ErrorMessage: `EntitlementGrant with ID ${grantID} not found`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const personID = await resolvePersonID(User, Claim.ProviderToUse);
|
|
69
|
+
if (personID && (!grant.BeneficiaryPersonID || grant.BeneficiaryPersonID !== personID)) {
|
|
70
|
+
grant.BeneficiaryPersonID = personID;
|
|
71
|
+
}
|
|
72
|
+
grant.Status = 'Active';
|
|
73
|
+
grant.ProvisionedAt = new Date();
|
|
74
|
+
const saved = await grant.Save();
|
|
75
|
+
if (!saved) {
|
|
76
|
+
return {
|
|
77
|
+
Success: false,
|
|
78
|
+
ErrorMessage: `Failed to activate EntitlementGrant: ${grant.LatestResult?.Message ?? 'Unknown save error'}`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
Success: true,
|
|
83
|
+
Data: {
|
|
84
|
+
GrantID: grant.ID,
|
|
85
|
+
BeneficiaryPersonID: grant.BeneficiaryPersonID,
|
|
86
|
+
ProductEntitlementID: grant.ProductEntitlementID,
|
|
87
|
+
ProvisionedAt: grant.ProvisionedAt?.toISOString()
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Hook called when an entitlement claim is revoked.
|
|
93
|
+
*
|
|
94
|
+
* `CK_EntitlementGrant_Revocation` (and the generated `ValidateRevokedAtBasedOnStatus`
|
|
95
|
+
* rule) require `RevokedAt` whenever Status is 'Revoked' — a revocation without the
|
|
96
|
+
* timestamp fails validation and the grant silently stays Active. Stamp both, and
|
|
97
|
+
* surface a failed save instead of swallowing it.
|
|
98
|
+
*/
|
|
99
|
+
async OnRevoke(context) {
|
|
100
|
+
const { Claim, User } = context;
|
|
101
|
+
if (!Claim.RecordID)
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
const md = new Metadata();
|
|
105
|
+
const grant = await md.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, User);
|
|
106
|
+
const loaded = await grant.Load(Claim.RecordID);
|
|
107
|
+
if (loaded && grant.Status === 'Active') {
|
|
108
|
+
grant.Status = 'Revoked';
|
|
109
|
+
grant.RevokedAt = new Date();
|
|
110
|
+
grant.RevocationReason = 'Identity claim revoked';
|
|
111
|
+
const saved = await grant.Save();
|
|
112
|
+
if (!saved) {
|
|
113
|
+
LogError(`[EntitlementGrantClaimDriver] OnRevoke failed to save grant ${grant.ID}: ${grant.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
119
|
+
LogError(`[EntitlementGrantClaimDriver] OnRevoke error for claim ${Claim.ID}: ${msg}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Hook called when an entitlement claim expires. (`RevokedAt` must NOT be set here — the
|
|
124
|
+
* validation rule requires it to be null for any non-Revoked status.)
|
|
125
|
+
*/
|
|
126
|
+
async OnExpire(context) {
|
|
127
|
+
const { Claim, User } = context;
|
|
128
|
+
if (!Claim.RecordID)
|
|
129
|
+
return;
|
|
130
|
+
try {
|
|
131
|
+
const md = new Metadata();
|
|
132
|
+
const grant = await md.GetEntityObject(ENTITLEMENT_GRANT_ENTITY, User);
|
|
133
|
+
const loaded = await grant.Load(Claim.RecordID);
|
|
134
|
+
if (loaded && grant.Status === 'Active') {
|
|
135
|
+
grant.Status = 'Expired';
|
|
136
|
+
const saved = await grant.Save();
|
|
137
|
+
if (!saved) {
|
|
138
|
+
LogError(`[EntitlementGrantClaimDriver] OnExpire failed to save grant ${grant.ID}: ${grant.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
144
|
+
LogError(`[EntitlementGrantClaimDriver] OnExpire error for claim ${Claim.ID}: ${msg}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
EntitlementGrantClaimDriver = __decorate([
|
|
149
|
+
RegisterClass(BaseIdentityClaimDriver, 'EntitlementGrantClaimDriver')
|
|
150
|
+
], EntitlementGrantClaimDriver);
|
|
151
|
+
export { EntitlementGrantClaimDriver };
|
|
152
|
+
/**
|
|
153
|
+
* Registration helper to ensure class factory decorator executes.
|
|
154
|
+
*/
|
|
155
|
+
export function LoadEntitlementGrantClaimDriver() {
|
|
156
|
+
// Explicit trigger for bundler tree-shaking preservation
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=EntitlementGrantClaimDriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitlementGrantClaimDriver.js","sourceRoot":"","sources":["../src/EntitlementGrantClaimDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entitlement read path — load the rows, then ask `EvaluateGrantAccess`.
|
|
3
|
+
*
|
|
4
|
+
* `Orders.CheckEntitlement` and `Orders.ListEntitlements` share this so they cannot disagree
|
|
5
|
+
* about which grants count, how email resolves, or how a cancelled subscription is read.
|
|
6
|
+
* The judgement itself lives in `EntitlementBehavior` and has no database.
|
|
7
|
+
*
|
|
8
|
+
* v1 is person grants only. Org-only rows (`BeneficiaryPersonID` null) are never loaded.
|
|
9
|
+
* Email is a convenience: normalised, and refused when it matches more than one person —
|
|
10
|
+
* first-match would be an authorization bug dressed as helpfulness.
|
|
11
|
+
*
|
|
12
|
+
* CONNECTS TO:
|
|
13
|
+
* PURE: ./EntitlementBehavior.ts
|
|
14
|
+
* OPS: ./CheckEntitlementOperation.ts, ./ListEntitlementsOperation.ts
|
|
15
|
+
* DOC: plans/entitlement-read-contract.md
|
|
16
|
+
*/
|
|
17
|
+
import { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
18
|
+
import { type EntitlementDecision } from './EntitlementBehavior.js';
|
|
19
|
+
export interface CheckEntitlementInput {
|
|
20
|
+
/** Authoritative. When present, Email is ignored. */
|
|
21
|
+
PersonID?: string | null;
|
|
22
|
+
/** Convenience. Normalised; ambiguous-if-duplicate → NoGrant, same shape as unknown. */
|
|
23
|
+
Email?: string | null;
|
|
24
|
+
/** Capability key (`ProductEntitlement.Code`), unique per product not globally. */
|
|
25
|
+
Code: string;
|
|
26
|
+
/**
|
|
27
|
+
* Diagnostics only — historical audit. The trust path omits this (evaluates at now).
|
|
28
|
+
* Values more than a minute in the future are rejected (NTP skew is allowed).
|
|
29
|
+
* CacheUntil is always issued from wall-clock now, never from this date.
|
|
30
|
+
*/
|
|
31
|
+
AsOf?: string | Date | null;
|
|
32
|
+
/** Optional company narrowing via the template's product. */
|
|
33
|
+
CompanyID?: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface CheckEntitlementOutput {
|
|
36
|
+
HasAccess: boolean;
|
|
37
|
+
Decision: EntitlementDecision;
|
|
38
|
+
ValidFrom?: string;
|
|
39
|
+
ValidTo?: string;
|
|
40
|
+
Quantity?: number;
|
|
41
|
+
GrantID?: string;
|
|
42
|
+
EvaluatedAt: string;
|
|
43
|
+
CacheUntil: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ListEntitlementsInput {
|
|
46
|
+
PersonID?: string | null;
|
|
47
|
+
Email?: string | null;
|
|
48
|
+
AsOf?: string | Date | null;
|
|
49
|
+
CompanyID?: string | null;
|
|
50
|
+
/** When false, only in-force capabilities. Default true — a library includes lapsed ones. */
|
|
51
|
+
IncludeInactive?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface ListedEntitlement {
|
|
54
|
+
Code: string;
|
|
55
|
+
HasAccess: boolean;
|
|
56
|
+
Decision: EntitlementDecision;
|
|
57
|
+
ValidFrom?: string;
|
|
58
|
+
ValidTo?: string;
|
|
59
|
+
Quantity?: number;
|
|
60
|
+
GrantID?: string;
|
|
61
|
+
CacheUntil: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ListEntitlementsOutput {
|
|
64
|
+
EvaluatedAt: string;
|
|
65
|
+
Items: ListedEntitlement[];
|
|
66
|
+
}
|
|
67
|
+
/** NTP skew between LXP and Orders. CacheUntil does not use AsOf, so this cannot mint a long cache. */
|
|
68
|
+
export declare const ASOF_FUTURE_TOLERANCE_MS = 60000;
|
|
69
|
+
/**
|
|
70
|
+
* Ask: does this person currently have this capability?
|
|
71
|
+
*
|
|
72
|
+
* Unknown person, ambiguous email, unknown code, and known-person-without-access share one
|
|
73
|
+
* response shape. Lookup faults fail closed the same way. Caller bugs (missing Code, bad
|
|
74
|
+
* UUID) throw `InvalidOperationInputError` so an integrator learns they sent garbage.
|
|
75
|
+
*/
|
|
76
|
+
export declare function CheckPersonEntitlement(input: CheckEntitlementInput, provider: IMetadataProvider, user: UserInfo): Promise<CheckEntitlementOutput>;
|
|
77
|
+
/**
|
|
78
|
+
* The person's library, one row per Code, each evaluated the same way as a point check.
|
|
79
|
+
* Empty when the person cannot be resolved — same non-leak as the check.
|
|
80
|
+
*/
|
|
81
|
+
export declare function ListPersonEntitlements(input: ListEntitlementsInput, provider: IMetadataProvider, user: UserInfo): Promise<ListEntitlementsOutput>;
|
|
82
|
+
//# sourceMappingURL=EntitlementRead.d.ts.map
|