@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 @@
|
|
|
1
|
+
{"version":3,"file":"EntitlementRead.d.ts","sourceRoot":"","sources":["../src/EntitlementRead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACH,iBAAiB,EAKjB,QAAQ,EACX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAIH,KAAK,mBAAmB,EAE3B,MAAM,0BAA0B,CAAC;AAUlC,MAAM,WAAW,qBAAqB;IAClC,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,6FAA6F;IAC7F,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAqDD,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAiO/C;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CACxC,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,sBAAsB,CAAC,CAkFjC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CACxC,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,sBAAsB,CAAC,CA2GjC"}
|
|
@@ -0,0 +1,368 @@
|
|
|
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 { LogError, LogStatus, RunView, } from '@memberjunction/core';
|
|
18
|
+
import { CacheUntilFor, EvaluateGrantAccess, PickWinningAccess, } from './EntitlementBehavior.js';
|
|
19
|
+
import { EscapeText, InvalidOperationInputError, RequireOptionalUUID, RequireUUID } from './sql-guards.js';
|
|
20
|
+
const PRODUCT_ENTITLEMENT_ENTITY = 'MJ_BizApps_Orders: Product Entitlements';
|
|
21
|
+
const ENTITLEMENT_GRANT_ENTITY = 'MJ_BizApps_Orders: Entitlement Grants';
|
|
22
|
+
const PRODUCT_ENTITY = 'MJ_BizApps_Orders: Products';
|
|
23
|
+
const SUBSCRIPTION_ENTITY = 'MJ_BizApps_Orders: Subscriptions';
|
|
24
|
+
const SUBSCRIPTION_TERM_ENTITY = 'MJ_BizApps_Orders: Subscription Terms';
|
|
25
|
+
const PERSON_ENTITY = 'MJ_BizApps_Common: People';
|
|
26
|
+
function quoteIds(ids, field) {
|
|
27
|
+
return [...new Set(ids.map((id) => `'${RequireUUID(id, field)}'`))].join(',');
|
|
28
|
+
}
|
|
29
|
+
function toDate(value) {
|
|
30
|
+
if (value == null || value === '')
|
|
31
|
+
return null;
|
|
32
|
+
if (value instanceof Date)
|
|
33
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
34
|
+
const d = new Date(String(value));
|
|
35
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
36
|
+
}
|
|
37
|
+
function toISO(d) {
|
|
38
|
+
return d ? d.toISOString() : undefined;
|
|
39
|
+
}
|
|
40
|
+
/** NTP skew between LXP and Orders. CacheUntil does not use AsOf, so this cannot mint a long cache. */
|
|
41
|
+
export const ASOF_FUTURE_TOLERANCE_MS = 60_000;
|
|
42
|
+
function parseAsOf(value, now = new Date()) {
|
|
43
|
+
if (value == null || value === '')
|
|
44
|
+
return now;
|
|
45
|
+
let d;
|
|
46
|
+
if (value instanceof Date) {
|
|
47
|
+
d = value;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const text = String(value);
|
|
51
|
+
if (/['";]/.test(text)) {
|
|
52
|
+
throw new InvalidOperationInputError('AsOf must be an ISO datetime.');
|
|
53
|
+
}
|
|
54
|
+
d = new Date(text);
|
|
55
|
+
}
|
|
56
|
+
if (Number.isNaN(d.getTime())) {
|
|
57
|
+
throw new InvalidOperationInputError('AsOf must be an ISO datetime.');
|
|
58
|
+
}
|
|
59
|
+
// Historical audit ("did they have access on the 3rd?") is the point of AsOf.
|
|
60
|
+
// Hours/years ahead is refused. Sub-minute skew is NTP, not a diagnostic of 2030.
|
|
61
|
+
if (d.getTime() > now.getTime() + ASOF_FUTURE_TOLERANCE_MS) {
|
|
62
|
+
throw new InvalidOperationInputError('AsOf cannot be in the future.');
|
|
63
|
+
}
|
|
64
|
+
return d;
|
|
65
|
+
}
|
|
66
|
+
function requireCode(value) {
|
|
67
|
+
const code = String(value ?? '').trim();
|
|
68
|
+
if (!code)
|
|
69
|
+
throw new InvalidOperationInputError('Code is required.');
|
|
70
|
+
if (code.length > 80)
|
|
71
|
+
throw new InvalidOperationInputError('Code is too long.');
|
|
72
|
+
return code;
|
|
73
|
+
}
|
|
74
|
+
function normalizeEmail(value) {
|
|
75
|
+
const email = String(value ?? '').trim().toLowerCase();
|
|
76
|
+
if (!email)
|
|
77
|
+
return null;
|
|
78
|
+
if (email.length > 254)
|
|
79
|
+
throw new InvalidOperationInputError('Email is too long.');
|
|
80
|
+
return email;
|
|
81
|
+
}
|
|
82
|
+
function closedCheck(evaluatedAt, issuedAt = new Date()) {
|
|
83
|
+
return {
|
|
84
|
+
HasAccess: false,
|
|
85
|
+
Decision: 'NoGrant',
|
|
86
|
+
EvaluatedAt: evaluatedAt.toISOString(),
|
|
87
|
+
CacheUntil: CacheUntilFor(issuedAt, null, false).toISOString(),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function toCheckOutput(evaluatedAt, picked, issuedAt = new Date()) {
|
|
91
|
+
if (!picked)
|
|
92
|
+
return closedCheck(evaluatedAt, issuedAt);
|
|
93
|
+
return {
|
|
94
|
+
HasAccess: picked.HasAccess,
|
|
95
|
+
Decision: picked.Decision,
|
|
96
|
+
ValidFrom: toISO(picked.ValidFrom),
|
|
97
|
+
ValidTo: toISO(picked.ValidTo),
|
|
98
|
+
Quantity: picked.Quantity ?? undefined,
|
|
99
|
+
GrantID: picked.GrantID,
|
|
100
|
+
EvaluatedAt: evaluatedAt.toISOString(),
|
|
101
|
+
CacheUntil: CacheUntilFor(issuedAt, picked.ValidTo, picked.HasAccess).toISOString(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function runSimple(rv, user, entity, extraFilter, fields, maxRows) {
|
|
105
|
+
const result = await rv.RunView({
|
|
106
|
+
EntityName: entity,
|
|
107
|
+
ExtraFilter: extraFilter,
|
|
108
|
+
...(fields ? { Fields: fields } : {}),
|
|
109
|
+
...(maxRows != null ? { MaxRows: maxRows } : {}),
|
|
110
|
+
ResultType: 'simple',
|
|
111
|
+
BypassCache: true,
|
|
112
|
+
}, user);
|
|
113
|
+
if (!result?.Success) {
|
|
114
|
+
LogError(`[ENTITLEMENT-READ] ${entity} lookup failed: ${result?.ErrorMessage ?? 'unknown error'}`);
|
|
115
|
+
return { ok: false, rows: [] };
|
|
116
|
+
}
|
|
117
|
+
return { ok: true, rows: result.Results ?? [] };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* PersonID is authoritative. Email is convenience: 0 or >1 matches → null, indistinguishable
|
|
121
|
+
* from unknown. Never first-match.
|
|
122
|
+
*/
|
|
123
|
+
async function resolvePersonID(rv, user, personID, email) {
|
|
124
|
+
if (personID)
|
|
125
|
+
return RequireUUID(personID, 'PersonID');
|
|
126
|
+
const normalised = normalizeEmail(email);
|
|
127
|
+
if (!normalised)
|
|
128
|
+
return null;
|
|
129
|
+
const escaped = EscapeText(normalised);
|
|
130
|
+
const found = await runSimple(rv, user, PERSON_ENTITY, `LOWER(Email) = '${escaped}'`, ['ID'], 2);
|
|
131
|
+
if (!found.ok)
|
|
132
|
+
return null;
|
|
133
|
+
if (found.rows.length !== 1)
|
|
134
|
+
return null;
|
|
135
|
+
const id = found.rows[0]?.ID;
|
|
136
|
+
if (!id)
|
|
137
|
+
return null;
|
|
138
|
+
try {
|
|
139
|
+
return RequireUUID(id, 'PersonID');
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function filterTemplatesByCompany(rv, user, templates, companyID) {
|
|
146
|
+
const productIDs = [...new Set(templates.map((t) => t.ProductID).filter(Boolean))];
|
|
147
|
+
if (!productIDs.length)
|
|
148
|
+
return [];
|
|
149
|
+
const products = await runSimple(rv, user, PRODUCT_ENTITY, `ID IN (${quoteIds(productIDs, 'ProductID')}) AND CompanyID = '${companyID}'`, ['ID', 'CompanyID']);
|
|
150
|
+
if (!products.ok)
|
|
151
|
+
return null;
|
|
152
|
+
const allowed = new Set(products.rows.map((p) => p.ID.toLowerCase()));
|
|
153
|
+
return templates.filter((t) => allowed.has(t.ProductID.toLowerCase()));
|
|
154
|
+
}
|
|
155
|
+
async function loadContext(rv, user, grants) {
|
|
156
|
+
const subs = new Map();
|
|
157
|
+
const terms = new Map();
|
|
158
|
+
const subIDs = [...new Set(grants.map((g) => g.SubscriptionID).filter((id) => !!id))];
|
|
159
|
+
const termIDs = [...new Set(grants.map((g) => g.SubscriptionTermID).filter((id) => !!id))];
|
|
160
|
+
if (subIDs.length) {
|
|
161
|
+
const loaded = await runSimple(rv, user, SUBSCRIPTION_ENTITY, `ID IN (${quoteIds(subIDs, 'SubscriptionID')})`, ['ID', 'Status', 'EndDate']);
|
|
162
|
+
if (!loaded.ok)
|
|
163
|
+
return { ok: false, subs, terms };
|
|
164
|
+
for (const row of loaded.rows)
|
|
165
|
+
subs.set(row.ID.toLowerCase(), row);
|
|
166
|
+
}
|
|
167
|
+
if (termIDs.length) {
|
|
168
|
+
const loaded = await runSimple(rv, user, SUBSCRIPTION_TERM_ENTITY, `ID IN (${quoteIds(termIDs, 'SubscriptionTermID')})`, ['ID', 'Status', 'StartDate', 'EndDate']);
|
|
169
|
+
if (!loaded.ok)
|
|
170
|
+
return { ok: false, subs, terms };
|
|
171
|
+
for (const row of loaded.rows)
|
|
172
|
+
terms.set(row.ID.toLowerCase(), row);
|
|
173
|
+
}
|
|
174
|
+
return { ok: true, subs, terms };
|
|
175
|
+
}
|
|
176
|
+
function evaluateGrant(grant, asOf, code, subs, terms) {
|
|
177
|
+
const sub = grant.SubscriptionID ? subs.get(grant.SubscriptionID.toLowerCase()) : undefined;
|
|
178
|
+
const term = grant.SubscriptionTermID ? terms.get(grant.SubscriptionTermID.toLowerCase()) : undefined;
|
|
179
|
+
const evaluation = EvaluateGrantAccess({
|
|
180
|
+
Status: grant.Status,
|
|
181
|
+
ValidFrom: toDate(grant.ValidFrom),
|
|
182
|
+
ValidTo: toDate(grant.ValidTo),
|
|
183
|
+
LinkedToSubscription: !!grant.SubscriptionID,
|
|
184
|
+
LinkedToTerm: !!grant.SubscriptionTermID,
|
|
185
|
+
}, asOf, sub
|
|
186
|
+
? { Status: sub.Status, EndDate: toDate(sub.EndDate) }
|
|
187
|
+
: grant.SubscriptionID
|
|
188
|
+
? null
|
|
189
|
+
: undefined, term
|
|
190
|
+
? {
|
|
191
|
+
Status: term.Status,
|
|
192
|
+
StartDate: toDate(term.StartDate) ?? asOf,
|
|
193
|
+
EndDate: toDate(term.EndDate) ?? asOf,
|
|
194
|
+
}
|
|
195
|
+
: grant.SubscriptionTermID
|
|
196
|
+
? null
|
|
197
|
+
: undefined);
|
|
198
|
+
return {
|
|
199
|
+
...evaluation,
|
|
200
|
+
Code: code,
|
|
201
|
+
GrantID: grant.ID,
|
|
202
|
+
Quantity: grant.Quantity,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Ask: does this person currently have this capability?
|
|
207
|
+
*
|
|
208
|
+
* Unknown person, ambiguous email, unknown code, and known-person-without-access share one
|
|
209
|
+
* response shape. Lookup faults fail closed the same way. Caller bugs (missing Code, bad
|
|
210
|
+
* UUID) throw `InvalidOperationInputError` so an integrator learns they sent garbage.
|
|
211
|
+
*/
|
|
212
|
+
export async function CheckPersonEntitlement(input, provider, user) {
|
|
213
|
+
const evaluatedAt = parseAsOf(input?.AsOf);
|
|
214
|
+
const code = requireCode(input?.Code);
|
|
215
|
+
RequireOptionalUUID(input?.PersonID ?? undefined, 'PersonID');
|
|
216
|
+
const companyID = RequireOptionalUUID(input?.CompanyID ?? undefined, 'CompanyID') || undefined;
|
|
217
|
+
if (!input?.PersonID && !normalizeEmail(input?.Email)) {
|
|
218
|
+
throw new InvalidOperationInputError('PersonID or Email is required.');
|
|
219
|
+
}
|
|
220
|
+
const closed = (reason) => {
|
|
221
|
+
LogStatus(`[ENTITLEMENT-CHECK] code=${code} decision=NoGrant hasAccess=false grant=- (${reason})`);
|
|
222
|
+
return closedCheck(evaluatedAt);
|
|
223
|
+
};
|
|
224
|
+
try {
|
|
225
|
+
const rv = new RunView(provider);
|
|
226
|
+
const personID = await resolvePersonID(rv, user, input.PersonID, input.Email);
|
|
227
|
+
if (!personID)
|
|
228
|
+
return closed('unresolved-person');
|
|
229
|
+
const templates = await runSimple(rv, user, PRODUCT_ENTITLEMENT_ENTITY, `Code = '${EscapeText(code)}'`, ['ID', 'ProductID', 'Code']);
|
|
230
|
+
if (!templates.ok)
|
|
231
|
+
return closed('template-lookup-failed');
|
|
232
|
+
let matching = templates.rows;
|
|
233
|
+
if (companyID && matching.length) {
|
|
234
|
+
const scoped = await filterTemplatesByCompany(rv, user, matching, companyID);
|
|
235
|
+
if (scoped == null)
|
|
236
|
+
return closed('company-lookup-failed');
|
|
237
|
+
matching = scoped;
|
|
238
|
+
}
|
|
239
|
+
if (!matching.length)
|
|
240
|
+
return closed('no-template');
|
|
241
|
+
const grants = await runSimple(rv, user, ENTITLEMENT_GRANT_ENTITY, `BeneficiaryPersonID = '${personID}' AND ProductEntitlementID IN (${quoteIds(matching.map((t) => t.ID), 'ProductEntitlementID')})`, [
|
|
242
|
+
'ID',
|
|
243
|
+
'ProductEntitlementID',
|
|
244
|
+
'Status',
|
|
245
|
+
'ValidFrom',
|
|
246
|
+
'ValidTo',
|
|
247
|
+
'Quantity',
|
|
248
|
+
'SubscriptionID',
|
|
249
|
+
'SubscriptionTermID',
|
|
250
|
+
]);
|
|
251
|
+
if (!grants.ok)
|
|
252
|
+
return closed('grant-lookup-failed');
|
|
253
|
+
if (!grants.rows.length)
|
|
254
|
+
return closed('no-grant');
|
|
255
|
+
const ctx = await loadContext(rv, user, grants.rows);
|
|
256
|
+
if (!ctx.ok)
|
|
257
|
+
return closed('context-lookup-failed');
|
|
258
|
+
const codeByTemplate = new Map(matching.map((t) => [t.ID.toLowerCase(), t.Code]));
|
|
259
|
+
const evaluated = grants.rows.map((g) => evaluateGrant(g, evaluatedAt, codeByTemplate.get(g.ProductEntitlementID.toLowerCase()) ?? code, ctx.subs, ctx.terms));
|
|
260
|
+
const picked = PickWinningAccess(evaluated);
|
|
261
|
+
const out = toCheckOutput(evaluatedAt, picked);
|
|
262
|
+
LogStatus(`[ENTITLEMENT-CHECK] code=${code} decision=${out.Decision} hasAccess=${out.HasAccess} grant=${out.GrantID ?? '-'}`);
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
if (err instanceof InvalidOperationInputError)
|
|
267
|
+
throw err;
|
|
268
|
+
LogError(`[ENTITLEMENT-CHECK] failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
269
|
+
return closed('fault');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The person's library, one row per Code, each evaluated the same way as a point check.
|
|
274
|
+
* Empty when the person cannot be resolved — same non-leak as the check.
|
|
275
|
+
*/
|
|
276
|
+
export async function ListPersonEntitlements(input, provider, user) {
|
|
277
|
+
const evaluatedAt = parseAsOf(input?.AsOf);
|
|
278
|
+
RequireOptionalUUID(input?.PersonID ?? undefined, 'PersonID');
|
|
279
|
+
const companyID = RequireOptionalUUID(input?.CompanyID ?? undefined, 'CompanyID') || undefined;
|
|
280
|
+
const includeInactive = input?.IncludeInactive !== false;
|
|
281
|
+
if (!input?.PersonID && !normalizeEmail(input?.Email)) {
|
|
282
|
+
throw new InvalidOperationInputError('PersonID or Email is required.');
|
|
283
|
+
}
|
|
284
|
+
const empty = () => ({
|
|
285
|
+
EvaluatedAt: evaluatedAt.toISOString(),
|
|
286
|
+
Items: [],
|
|
287
|
+
});
|
|
288
|
+
try {
|
|
289
|
+
const rv = new RunView(provider);
|
|
290
|
+
const personID = await resolvePersonID(rv, user, input.PersonID, input.Email);
|
|
291
|
+
if (!personID) {
|
|
292
|
+
LogStatus('[ENTITLEMENT-LIST] person=- items=0 (unresolved-person)');
|
|
293
|
+
return empty();
|
|
294
|
+
}
|
|
295
|
+
const grants = await runSimple(rv, user, ENTITLEMENT_GRANT_ENTITY, `BeneficiaryPersonID = '${personID}'`, [
|
|
296
|
+
'ID',
|
|
297
|
+
'ProductEntitlementID',
|
|
298
|
+
'Status',
|
|
299
|
+
'ValidFrom',
|
|
300
|
+
'ValidTo',
|
|
301
|
+
'Quantity',
|
|
302
|
+
'SubscriptionID',
|
|
303
|
+
'SubscriptionTermID',
|
|
304
|
+
]);
|
|
305
|
+
if (!grants.ok)
|
|
306
|
+
return empty();
|
|
307
|
+
if (!grants.rows.length) {
|
|
308
|
+
LogStatus(`[ENTITLEMENT-LIST] person=${personID} items=0`);
|
|
309
|
+
return empty();
|
|
310
|
+
}
|
|
311
|
+
const templateIDs = [...new Set(grants.rows.map((g) => g.ProductEntitlementID))];
|
|
312
|
+
const templates = await runSimple(rv, user, PRODUCT_ENTITLEMENT_ENTITY, `ID IN (${quoteIds(templateIDs, 'ProductEntitlementID')})`, ['ID', 'ProductID', 'Code']);
|
|
313
|
+
if (!templates.ok)
|
|
314
|
+
return empty();
|
|
315
|
+
let matching = templates.rows;
|
|
316
|
+
if (companyID && matching.length) {
|
|
317
|
+
const scoped = await filterTemplatesByCompany(rv, user, matching, companyID);
|
|
318
|
+
if (scoped == null)
|
|
319
|
+
return empty();
|
|
320
|
+
matching = scoped;
|
|
321
|
+
}
|
|
322
|
+
const templateByID = new Map(matching.map((t) => [t.ID.toLowerCase(), t]));
|
|
323
|
+
const inScope = grants.rows.filter((g) => templateByID.has(g.ProductEntitlementID.toLowerCase()));
|
|
324
|
+
if (!inScope.length) {
|
|
325
|
+
LogStatus(`[ENTITLEMENT-LIST] person=${personID} items=0`);
|
|
326
|
+
return empty();
|
|
327
|
+
}
|
|
328
|
+
const ctx = await loadContext(rv, user, inScope);
|
|
329
|
+
if (!ctx.ok)
|
|
330
|
+
return empty();
|
|
331
|
+
const evaluated = inScope.map((g) => {
|
|
332
|
+
const template = templateByID.get(g.ProductEntitlementID.toLowerCase());
|
|
333
|
+
return evaluateGrant(g, evaluatedAt, template.Code, ctx.subs, ctx.terms);
|
|
334
|
+
});
|
|
335
|
+
const byCode = new Map();
|
|
336
|
+
for (const row of evaluated) {
|
|
337
|
+
const list = byCode.get(row.Code) ?? [];
|
|
338
|
+
list.push(row);
|
|
339
|
+
byCode.set(row.Code, list);
|
|
340
|
+
}
|
|
341
|
+
const issuedAt = new Date();
|
|
342
|
+
const items = [...byCode.entries()]
|
|
343
|
+
.map(([code, group]) => {
|
|
344
|
+
const picked = PickWinningAccess(group);
|
|
345
|
+
return {
|
|
346
|
+
Code: code,
|
|
347
|
+
HasAccess: picked.HasAccess,
|
|
348
|
+
Decision: picked.Decision,
|
|
349
|
+
ValidFrom: toISO(picked.ValidFrom),
|
|
350
|
+
ValidTo: toISO(picked.ValidTo),
|
|
351
|
+
Quantity: picked.Quantity ?? undefined,
|
|
352
|
+
GrantID: picked.GrantID,
|
|
353
|
+
CacheUntil: CacheUntilFor(issuedAt, picked.ValidTo, picked.HasAccess).toISOString(),
|
|
354
|
+
};
|
|
355
|
+
})
|
|
356
|
+
.filter((item) => includeInactive || item.HasAccess)
|
|
357
|
+
.sort((a, b) => a.Code.localeCompare(b.Code));
|
|
358
|
+
LogStatus(`[ENTITLEMENT-LIST] person=${personID} items=${items.length}`);
|
|
359
|
+
return { EvaluatedAt: evaluatedAt.toISOString(), Items: items };
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
if (err instanceof InvalidOperationInputError)
|
|
363
|
+
throw err;
|
|
364
|
+
LogError(`[ENTITLEMENT-LIST] failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
365
|
+
return empty();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=EntitlementRead.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitlementRead.js","sourceRoot":"","sources":["../src/EntitlementRead.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `Orders.FulfillOrderLines` — flip lines, and close the order when the last one goes.
|
|
3
|
+
*
|
|
4
|
+
* WHY ONE OPERATION AND NOT TWO. Marking lines shipped and advancing the order are the same
|
|
5
|
+
* decision. Split across two calls there is a window in which every line is Fulfilled and the order
|
|
6
|
+
* still reads Posted — which a warehouse experiences as the system losing its work, and which a
|
|
7
|
+
* second caller can widen indefinitely by never making the second call.
|
|
8
|
+
*
|
|
9
|
+
* THE ADVANCE RULE, and the mistake it avoids. An order advances when nothing is AWAITING
|
|
10
|
+
* fulfilment — not when every line is Fulfilled. On a mixed order the subscription line never flips,
|
|
11
|
+
* because it has nothing to flip, so the stricter test would hold that order open forever while the
|
|
12
|
+
* warehouse insists it shipped everything. Only lines that CAN be fulfilled are allowed to hold it.
|
|
13
|
+
*
|
|
14
|
+
* NO JOURNAL ENTRY FIRES (D15). Fulfilment is a logistics fact; revenue was settled at booking and
|
|
15
|
+
* releases on its own schedule. Coupling them would let a warehouse delay restate a closed period.
|
|
16
|
+
*
|
|
17
|
+
* A REFUSAL IS DATA, NOT AN ERROR. Scanning an already-shipped item is an ordinary mistake, so
|
|
18
|
+
* refusals come back per line with reasons and the rest of the batch still lands — a picker who
|
|
19
|
+
* scans one wrong item should not lose nine good scans. `AllOrNothing` is there for callers that
|
|
20
|
+
* want the opposite.
|
|
21
|
+
*
|
|
22
|
+
* @module @mj-biz-apps/orders-core-entities-server
|
|
23
|
+
*/
|
|
24
|
+
import { type IMetadataProvider, type UserInfo } from '@memberjunction/core';
|
|
25
|
+
import { OrdersFulfillOrderLinesOperation as OrdersFulfillOrderLinesOperationBase, type OrdersFulfillOrderLinesInput, type OrdersFulfillOrderLinesOutput } from '@mj-biz-apps/orders-entities';
|
|
26
|
+
export declare class FulfillOrderLinesOperation extends OrdersFulfillOrderLinesOperationBase {
|
|
27
|
+
protected InternalExecute(input: OrdersFulfillOrderLinesInput, provider: IMetadataProvider, user: UserInfo): Promise<OrdersFulfillOrderLinesOutput>;
|
|
28
|
+
/** product id → does its type require fulfilment. Two queries, not one per line. */
|
|
29
|
+
private buildRequiresFulfillment;
|
|
30
|
+
private refuseAll;
|
|
31
|
+
}
|
|
32
|
+
/** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
|
|
33
|
+
export declare function LoadFulfillOrderLinesOperation(): void;
|
|
34
|
+
//# sourceMappingURL=FulfillOrderLinesOperation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FulfillOrderLinesOperation.d.ts","sourceRoot":"","sources":["../src/FulfillOrderLinesOperation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAIH,KAAK,iBAAiB,EAEtB,KAAK,QAAQ,EAChB,MAAM,sBAAsB,CAAC;AAM9B,OAAO,EACH,gCAAgC,IAAI,oCAAoC,EACxE,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAGrC,MAAM,8BAA8B,CAAC;AAkBtC,qBACa,0BAA2B,SAAQ,oCAAoC;cAChE,eAAe,CAC3B,KAAK,EAAE,4BAA4B,EACnC,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,6BAA6B,CAAC;IAiLzC,oFAAoF;YACtE,wBAAwB;IA0BtC,OAAO,CAAC,SAAS;CAWpB;AAED,0FAA0F;AAC1F,wBAAgB,8BAA8B,IAAI,IAAI,CAErD"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview `Orders.FulfillOrderLines` — flip lines, and close the order when the last one goes.
|
|
9
|
+
*
|
|
10
|
+
* WHY ONE OPERATION AND NOT TWO. Marking lines shipped and advancing the order are the same
|
|
11
|
+
* decision. Split across two calls there is a window in which every line is Fulfilled and the order
|
|
12
|
+
* still reads Posted — which a warehouse experiences as the system losing its work, and which a
|
|
13
|
+
* second caller can widen indefinitely by never making the second call.
|
|
14
|
+
*
|
|
15
|
+
* THE ADVANCE RULE, and the mistake it avoids. An order advances when nothing is AWAITING
|
|
16
|
+
* fulfilment — not when every line is Fulfilled. On a mixed order the subscription line never flips,
|
|
17
|
+
* because it has nothing to flip, so the stricter test would hold that order open forever while the
|
|
18
|
+
* warehouse insists it shipped everything. Only lines that CAN be fulfilled are allowed to hold it.
|
|
19
|
+
*
|
|
20
|
+
* NO JOURNAL ENTRY FIRES (D15). Fulfilment is a logistics fact; revenue was settled at booking and
|
|
21
|
+
* releases on its own schedule. Coupling them would let a warehouse delay restate a closed period.
|
|
22
|
+
*
|
|
23
|
+
* A REFUSAL IS DATA, NOT AN ERROR. Scanning an already-shipped item is an ordinary mistake, so
|
|
24
|
+
* refusals come back per line with reasons and the rest of the batch still lands — a picker who
|
|
25
|
+
* scans one wrong item should not lose nine good scans. `AllOrNothing` is there for callers that
|
|
26
|
+
* want the opposite.
|
|
27
|
+
*
|
|
28
|
+
* @module @mj-biz-apps/orders-core-entities-server
|
|
29
|
+
*/
|
|
30
|
+
import { BaseRemotableOperation, RunView, } from '@memberjunction/core';
|
|
31
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
32
|
+
import { OrdersFulfillOrderLinesOperation as OrdersFulfillOrderLinesOperationBase, } from '@mj-biz-apps/orders-entities';
|
|
33
|
+
import { ExplainRefusal, RefuseFlip, ShouldAdvanceToFulfilled, } from './FulfillmentBehavior.js';
|
|
34
|
+
import { RequireUUID } from './sql-guards.js';
|
|
35
|
+
const ORDER_HEADER_ENTITY = 'MJ_BizApps_Orders: Order Headers';
|
|
36
|
+
const ORDER_LINE_ENTITY = 'MJ_BizApps_Orders: Order Lines';
|
|
37
|
+
const PRODUCT_ENTITY = 'MJ_BizApps_Orders: Products';
|
|
38
|
+
const PRODUCT_TYPE_ENTITY = 'MJ_BizApps_Orders: Product Types';
|
|
39
|
+
const key = (id) => (id ?? '').toLowerCase();
|
|
40
|
+
const quote = (ids) => [...new Set(ids.map((i) => `'${RequireUUID(i, 'OrderLineID')}'`))].join(',');
|
|
41
|
+
let FulfillOrderLinesOperation = class FulfillOrderLinesOperation extends OrdersFulfillOrderLinesOperationBase {
|
|
42
|
+
async InternalExecute(input, provider, user) {
|
|
43
|
+
const requested = [...new Set((input?.OrderLineIDs ?? []).filter(Boolean))];
|
|
44
|
+
if (!requested.length) {
|
|
45
|
+
return this.refuseAll('No order lines were given to fulfil.');
|
|
46
|
+
}
|
|
47
|
+
const rv = new RunView(provider);
|
|
48
|
+
const lines = await rv.RunView({ EntityName: ORDER_LINE_ENTITY, ExtraFilter: `ID IN (${quote(requested)})`, ResultType: 'simple' }, user);
|
|
49
|
+
const lineRows = lines.Results ?? [];
|
|
50
|
+
// Every order the requested lines belong to — plus, crucially, ALL of those orders' lines,
|
|
51
|
+
// because deciding whether an order may advance needs its whole picture, not just the lines
|
|
52
|
+
// in this batch.
|
|
53
|
+
const orderIDs = [...new Set(lineRows.map((l) => l.OrderHeaderID))];
|
|
54
|
+
const orders = orderIDs.length
|
|
55
|
+
? await rv.RunView({ EntityName: ORDER_HEADER_ENTITY, ExtraFilter: `ID IN (${quote(orderIDs)})`, ResultType: 'simple' }, user)
|
|
56
|
+
: { Results: [] };
|
|
57
|
+
const orderByID = new Map((orders.Results ?? []).map((o) => [key(o.ID), o]));
|
|
58
|
+
const allLines = orderIDs.length
|
|
59
|
+
? await rv.RunView({
|
|
60
|
+
EntityName: ORDER_LINE_ENTITY,
|
|
61
|
+
ExtraFilter: `OrderHeaderID IN (${quote(orderIDs)})`,
|
|
62
|
+
ResultType: 'simple',
|
|
63
|
+
}, user)
|
|
64
|
+
: { Results: [] };
|
|
65
|
+
const allLineRows = allLines.Results ?? [];
|
|
66
|
+
const requiresFulfillment = await this.buildRequiresFulfillment(rv, allLineRows, user);
|
|
67
|
+
const shape = (l) => ({
|
|
68
|
+
ID: l.ID,
|
|
69
|
+
RequiresFulfillment: requiresFulfillment(l.ProductID),
|
|
70
|
+
FulfillmentStatus: l.FulfillmentStatus ?? null,
|
|
71
|
+
ReversesOrderLineID: l.ReversesOrderLineID,
|
|
72
|
+
IsRollupParent: !!l.IsRollupParent,
|
|
73
|
+
});
|
|
74
|
+
// ── DECIDE FIRST, WRITE SECOND ──
|
|
75
|
+
// With AllOrNothing the caller expects nothing to move if anything is refused, so every
|
|
76
|
+
// decision has to be made before the first write.
|
|
77
|
+
const byID = new Map(lineRows.map((l) => [key(l.ID), l]));
|
|
78
|
+
const decisions = requested.map((id) => {
|
|
79
|
+
const row = byID.get(key(id));
|
|
80
|
+
const order = row ? orderByID.get(key(row.OrderHeaderID)) : undefined;
|
|
81
|
+
const refusal = RefuseFlip(row ? shape(row) : null, order?.Status ?? '');
|
|
82
|
+
return refusal
|
|
83
|
+
? { OrderLineID: id, Fulfilled: false, Refusal: refusal, RefusalReason: ExplainRefusal(refusal, id) }
|
|
84
|
+
: { OrderLineID: id, Fulfilled: true, Refusal: null, RefusalReason: null };
|
|
85
|
+
});
|
|
86
|
+
const refused = decisions.filter((d) => !d.Fulfilled);
|
|
87
|
+
if (input?.AllOrNothing && refused.length) {
|
|
88
|
+
return {
|
|
89
|
+
Success: false,
|
|
90
|
+
Message: `Refused: ${refused.length} of ${requested.length} line(s) cannot be fulfilled, and ` +
|
|
91
|
+
`AllOrNothing was set, so nothing was changed. First reason: ${refused[0].RefusalReason}`,
|
|
92
|
+
Lines: decisions.map((d) => ({ ...d, Fulfilled: false })),
|
|
93
|
+
Orders: [],
|
|
94
|
+
FulfilledCount: 0,
|
|
95
|
+
RefusedCount: refused.length,
|
|
96
|
+
AdvancedCount: 0,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// ── WRITE ──
|
|
100
|
+
const flipped = new Set();
|
|
101
|
+
for (const decision of decisions.filter((d) => d.Fulfilled)) {
|
|
102
|
+
const entity = await provider.GetEntityObject(ORDER_LINE_ENTITY, user);
|
|
103
|
+
const loaded = await entity.Load(decision.OrderLineID);
|
|
104
|
+
if (!loaded) {
|
|
105
|
+
decision.Fulfilled = false;
|
|
106
|
+
decision.Refusal = 'LineNotFound';
|
|
107
|
+
decision.RefusalReason = ExplainRefusal('LineNotFound', decision.OrderLineID);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
entity.FulfillmentStatus = 'Fulfilled';
|
|
111
|
+
if (!(await entity.Save())) {
|
|
112
|
+
// FulfillmentStatus is the ONE line column the immutability trigger lets a fulfiller
|
|
113
|
+
// change on a Confirmed order (the D15 carve-out). A refusal here means something
|
|
114
|
+
// else objected, and the caller needs the reason rather than a silent skip.
|
|
115
|
+
decision.Fulfilled = false;
|
|
116
|
+
decision.Refusal = 'SaveFailed';
|
|
117
|
+
decision.RefusalReason =
|
|
118
|
+
`Order line ${decision.OrderLineID} could not be marked Fulfilled: ` +
|
|
119
|
+
`${entity.LatestResult?.CompleteMessage ?? 'no reason given'}`;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
flipped.add(key(decision.OrderLineID));
|
|
123
|
+
}
|
|
124
|
+
// ── ADVANCE the orders whose last fulfillable line just went ──
|
|
125
|
+
const advanced = [];
|
|
126
|
+
for (const orderID of orderIDs) {
|
|
127
|
+
const order = orderByID.get(key(orderID));
|
|
128
|
+
if (!order)
|
|
129
|
+
continue;
|
|
130
|
+
const after = allLineRows
|
|
131
|
+
.filter((l) => key(l.OrderHeaderID) === key(orderID))
|
|
132
|
+
.map((l) => (flipped.has(key(l.ID)) ? { ...l, FulfillmentStatus: 'Fulfilled' } : l))
|
|
133
|
+
.map(shape);
|
|
134
|
+
const remaining = after.filter((l) => l.RequiresFulfillment &&
|
|
135
|
+
!l.ReversesOrderLineID &&
|
|
136
|
+
!l.IsRollupParent &&
|
|
137
|
+
(l.FulfillmentStatus ?? 'Pending') === 'Pending').length;
|
|
138
|
+
let statusAfter = order.FulfillmentStatus ?? 'Pending';
|
|
139
|
+
let didAdvance = false;
|
|
140
|
+
if (ShouldAdvanceToFulfilled(after) && order.FulfillmentStatus !== 'Fulfilled') {
|
|
141
|
+
const header = await provider.GetEntityObject(ORDER_HEADER_ENTITY, user);
|
|
142
|
+
if (await header.Load(orderID)) {
|
|
143
|
+
header.FulfillmentStatus = 'Fulfilled';
|
|
144
|
+
if (await header.Save()) {
|
|
145
|
+
didAdvance = true;
|
|
146
|
+
statusAfter = 'Fulfilled';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
advanced.push({
|
|
151
|
+
OrderHeaderID: orderID,
|
|
152
|
+
OrderNumber: order.OrderNumber,
|
|
153
|
+
StatusBefore: order.FulfillmentStatus ?? 'Pending',
|
|
154
|
+
StatusAfter: statusAfter,
|
|
155
|
+
AdvancedToFulfilled: didAdvance || remaining === 0,
|
|
156
|
+
RemainingLineCount: remaining,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const fulfilledCount = decisions.filter((d) => d.Fulfilled).length;
|
|
160
|
+
const refusedCount = decisions.length - fulfilledCount;
|
|
161
|
+
return {
|
|
162
|
+
Success: true,
|
|
163
|
+
Message: refusedCount > 0
|
|
164
|
+
? `${fulfilledCount} line(s) fulfilled, ${refusedCount} refused. See Lines for reasons.`
|
|
165
|
+
: undefined,
|
|
166
|
+
Lines: decisions,
|
|
167
|
+
Orders: advanced,
|
|
168
|
+
FulfilledCount: fulfilledCount,
|
|
169
|
+
RefusedCount: refusedCount,
|
|
170
|
+
AdvancedCount: advanced.filter((a) => a.AdvancedToFulfilled).length,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** product id → does its type require fulfilment. Two queries, not one per line. */
|
|
174
|
+
async buildRequiresFulfillment(rv, lines, user) {
|
|
175
|
+
const productIDs = [...new Set(lines.map((l) => l.ProductID))].filter(Boolean);
|
|
176
|
+
if (!productIDs.length)
|
|
177
|
+
return () => false;
|
|
178
|
+
const products = await rv.RunView({ EntityName: PRODUCT_ENTITY, ExtraFilter: `ID IN (${quote(productIDs)})`, ResultType: 'simple' }, user);
|
|
179
|
+
const rows = products.Results ?? [];
|
|
180
|
+
const typeIDs = [...new Set(rows.map((p) => p.ProductTypeID))].filter(Boolean);
|
|
181
|
+
if (!typeIDs.length)
|
|
182
|
+
return () => false;
|
|
183
|
+
const types = await rv.RunView({ EntityName: PRODUCT_TYPE_ENTITY, ExtraFilter: `ID IN (${quote(typeIDs)})`, ResultType: 'simple' }, user);
|
|
184
|
+
const requiresByType = new Map((types.Results ?? []).map((t) => [key(t.ID), !!t.RequiresFulfillment]));
|
|
185
|
+
const typeByProduct = new Map(rows.map((p) => [key(p.ID), key(p.ProductTypeID)]));
|
|
186
|
+
return (productID) => requiresByType.get(typeByProduct.get(key(productID)) ?? '') ?? false;
|
|
187
|
+
}
|
|
188
|
+
refuseAll(message) {
|
|
189
|
+
return {
|
|
190
|
+
Success: false,
|
|
191
|
+
Message: message,
|
|
192
|
+
Lines: [],
|
|
193
|
+
Orders: [],
|
|
194
|
+
FulfilledCount: 0,
|
|
195
|
+
RefusedCount: 0,
|
|
196
|
+
AdvancedCount: 0,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
FulfillOrderLinesOperation = __decorate([
|
|
201
|
+
RegisterClass(BaseRemotableOperation, 'Orders.FulfillOrderLines')
|
|
202
|
+
], FulfillOrderLinesOperation);
|
|
203
|
+
export { FulfillOrderLinesOperation };
|
|
204
|
+
/** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
|
|
205
|
+
export function LoadFulfillOrderLinesOperation() {
|
|
206
|
+
// intentionally empty
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=FulfillOrderLinesOperation.js.map
|