@happyvertical/smrt-sales 0.39.2
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/AGENTS.md +87 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/chunks/__smrt-register__-kWdT7LOt.js +6 -0
- package/dist/chunks/__smrt-register__-kWdT7LOt.js.map +1 -0
- package/dist/chunks/commissions-BCxqOKVM.js +2181 -0
- package/dist/chunks/commissions-BCxqOKVM.js.map +1 -0
- package/dist/chunks/crm-DwEz7E2r.js +1428 -0
- package/dist/chunks/crm-DwEz7E2r.js.map +1 -0
- package/dist/chunks/referrals-BY47p70T.js +2880 -0
- package/dist/chunks/referrals-BY47p70T.js.map +1 -0
- package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts +26 -0
- package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts.map +1 -0
- package/dist/commissions/collections/CommissionCollection.d.ts +43 -0
- package/dist/commissions/collections/CommissionCollection.d.ts.map +1 -0
- package/dist/commissions/collections/CommissionPayoutCollection.d.ts +19 -0
- package/dist/commissions/collections/CommissionPayoutCollection.d.ts.map +1 -0
- package/dist/commissions/collections/CommissionPlanCollection.d.ts +46 -0
- package/dist/commissions/collections/CommissionPlanCollection.d.ts.map +1 -0
- package/dist/commissions/collections/EarnerCollection.d.ts +14 -0
- package/dist/commissions/collections/EarnerCollection.d.ts.map +1 -0
- package/dist/commissions/collections/EarningEventCollection.d.ts +27 -0
- package/dist/commissions/collections/EarningEventCollection.d.ts.map +1 -0
- package/dist/commissions/collections/index.d.ts +11 -0
- package/dist/commissions/collections/index.d.ts.map +1 -0
- package/dist/commissions/index.d.ts +20 -0
- package/dist/commissions/index.d.ts.map +1 -0
- package/dist/commissions/models/Commission.d.ts +150 -0
- package/dist/commissions/models/Commission.d.ts.map +1 -0
- package/dist/commissions/models/CommissionAdjustment.d.ts +62 -0
- package/dist/commissions/models/CommissionAdjustment.d.ts.map +1 -0
- package/dist/commissions/models/CommissionPayout.d.ts +119 -0
- package/dist/commissions/models/CommissionPayout.d.ts.map +1 -0
- package/dist/commissions/models/CommissionPlan.d.ts +117 -0
- package/dist/commissions/models/CommissionPlan.d.ts.map +1 -0
- package/dist/commissions/models/Earner.d.ts +49 -0
- package/dist/commissions/models/Earner.d.ts.map +1 -0
- package/dist/commissions/models/EarningEvent.d.ts +89 -0
- package/dist/commissions/models/EarningEvent.d.ts.map +1 -0
- package/dist/commissions/models/index.d.ts +11 -0
- package/dist/commissions/models/index.d.ts.map +1 -0
- package/dist/commissions/money.d.ts +42 -0
- package/dist/commissions/money.d.ts.map +1 -0
- package/dist/commissions/services/CommissionBalanceService.d.ts +14 -0
- package/dist/commissions/services/CommissionBalanceService.d.ts.map +1 -0
- package/dist/commissions/services/CommissionCalculationService.d.ts +142 -0
- package/dist/commissions/services/CommissionCalculationService.d.ts.map +1 -0
- package/dist/commissions/services/CommissionPayoutService.d.ts +134 -0
- package/dist/commissions/services/CommissionPayoutService.d.ts.map +1 -0
- package/dist/commissions/services/CommissionSettlementService.d.ts +42 -0
- package/dist/commissions/services/CommissionSettlementService.d.ts.map +1 -0
- package/dist/commissions/services/index.d.ts +9 -0
- package/dist/commissions/services/index.d.ts.map +1 -0
- package/dist/commissions/types.d.ts +243 -0
- package/dist/commissions/types.d.ts.map +1 -0
- package/dist/commissions.d.ts +2 -0
- package/dist/commissions.d.ts.map +1 -0
- package/dist/commissions.js +3 -0
- package/dist/crm/collections/LeadCollection.d.ts +79 -0
- package/dist/crm/collections/LeadCollection.d.ts.map +1 -0
- package/dist/crm/collections/OpportunityCollection.d.ts +42 -0
- package/dist/crm/collections/OpportunityCollection.d.ts.map +1 -0
- package/dist/crm/collections/OpportunityConversionCollection.d.ts +26 -0
- package/dist/crm/collections/OpportunityConversionCollection.d.ts.map +1 -0
- package/dist/crm/collections/PipelineDefinitionCollection.d.ts +46 -0
- package/dist/crm/collections/PipelineDefinitionCollection.d.ts.map +1 -0
- package/dist/crm/collections/PipelineStageCollection.d.ts +22 -0
- package/dist/crm/collections/PipelineStageCollection.d.ts.map +1 -0
- package/dist/crm/collections/SalesActivityCollection.d.ts +24 -0
- package/dist/crm/collections/SalesActivityCollection.d.ts.map +1 -0
- package/dist/crm/collections/SalesRepresentativeCollection.d.ts +19 -0
- package/dist/crm/collections/SalesRepresentativeCollection.d.ts.map +1 -0
- package/dist/crm/index.d.ts +27 -0
- package/dist/crm/index.d.ts.map +1 -0
- package/dist/crm/models/Lead.d.ts +135 -0
- package/dist/crm/models/Lead.d.ts.map +1 -0
- package/dist/crm/models/Opportunity.d.ts +111 -0
- package/dist/crm/models/Opportunity.d.ts.map +1 -0
- package/dist/crm/models/OpportunityConversion.d.ts +66 -0
- package/dist/crm/models/OpportunityConversion.d.ts.map +1 -0
- package/dist/crm/models/PipelineDefinition.d.ts +58 -0
- package/dist/crm/models/PipelineDefinition.d.ts.map +1 -0
- package/dist/crm/models/PipelineStage.d.ts +66 -0
- package/dist/crm/models/PipelineStage.d.ts.map +1 -0
- package/dist/crm/models/SalesActivity.d.ts +76 -0
- package/dist/crm/models/SalesActivity.d.ts.map +1 -0
- package/dist/crm/models/SalesRepresentative.d.ts +60 -0
- package/dist/crm/models/SalesRepresentative.d.ts.map +1 -0
- package/dist/crm/types.d.ts +228 -0
- package/dist/crm/types.d.ts.map +1 -0
- package/dist/crm.d.ts +2 -0
- package/dist/crm.d.ts.map +1 -0
- package/dist/crm.js +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/manifest.json +9340 -0
- package/dist/referrals/collections/AttributionExceptionCollection.d.ts +17 -0
- package/dist/referrals/collections/AttributionExceptionCollection.d.ts.map +1 -0
- package/dist/referrals/collections/AttributionPolicyCollection.d.ts +50 -0
- package/dist/referrals/collections/AttributionPolicyCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralAgreementCollection.d.ts +48 -0
- package/dist/referrals/collections/ReferralAgreementCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralCollection.d.ts +25 -0
- package/dist/referrals/collections/ReferralCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralLinkCollection.d.ts +96 -0
- package/dist/referrals/collections/ReferralLinkCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralProgramCollection.d.ts +18 -0
- package/dist/referrals/collections/ReferralProgramCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts +13 -0
- package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferralTouchCollection.d.ts +30 -0
- package/dist/referrals/collections/ReferralTouchCollection.d.ts.map +1 -0
- package/dist/referrals/collections/ReferrerCollection.d.ts +17 -0
- package/dist/referrals/collections/ReferrerCollection.d.ts.map +1 -0
- package/dist/referrals/index.d.ts +38 -0
- package/dist/referrals/index.d.ts.map +1 -0
- package/dist/referrals/models/AttributionException.d.ts +85 -0
- package/dist/referrals/models/AttributionException.d.ts.map +1 -0
- package/dist/referrals/models/AttributionPolicy.d.ts +128 -0
- package/dist/referrals/models/AttributionPolicy.d.ts.map +1 -0
- package/dist/referrals/models/Referral.d.ts +112 -0
- package/dist/referrals/models/Referral.d.ts.map +1 -0
- package/dist/referrals/models/ReferralAgreement.d.ts +125 -0
- package/dist/referrals/models/ReferralAgreement.d.ts.map +1 -0
- package/dist/referrals/models/ReferralLink.d.ts +71 -0
- package/dist/referrals/models/ReferralLink.d.ts.map +1 -0
- package/dist/referrals/models/ReferralProgram.d.ts +57 -0
- package/dist/referrals/models/ReferralProgram.d.ts.map +1 -0
- package/dist/referrals/models/ReferralTermSnapshot.d.ts +61 -0
- package/dist/referrals/models/ReferralTermSnapshot.d.ts.map +1 -0
- package/dist/referrals/models/ReferralTouch.d.ts +55 -0
- package/dist/referrals/models/ReferralTouch.d.ts.map +1 -0
- package/dist/referrals/models/Referrer.d.ts +69 -0
- package/dist/referrals/models/Referrer.d.ts.map +1 -0
- package/dist/referrals/services/AttributionService.d.ts +248 -0
- package/dist/referrals/services/AttributionService.d.ts.map +1 -0
- package/dist/referrals/services/ReferralCommissionService.d.ts +91 -0
- package/dist/referrals/services/ReferralCommissionService.d.ts.map +1 -0
- package/dist/referrals/services/ReferralQualificationService.d.ts +135 -0
- package/dist/referrals/services/ReferralQualificationService.d.ts.map +1 -0
- package/dist/referrals/types.d.ts +226 -0
- package/dist/referrals/types.d.ts.map +1 -0
- package/dist/referrals.d.ts +2 -0
- package/dist/referrals.d.ts.map +1 -0
- package/dist/referrals.js +3 -0
- package/dist/smrt-knowledge.json +5501 -0
- package/dist/svelte/__tests__/format.test.js +68 -0
- package/dist/svelte/__tests__/types.test.js +349 -0
- package/dist/svelte/components/AttributionConflictQueue.svelte +361 -0
- package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts +15 -0
- package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts.map +1 -0
- package/dist/svelte/components/CommissionBreakdown.svelte +251 -0
- package/dist/svelte/components/CommissionBreakdown.svelte.d.ts +11 -0
- package/dist/svelte/components/CommissionBreakdown.svelte.d.ts.map +1 -0
- package/dist/svelte/components/CommissionExpenseSummary.svelte +186 -0
- package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts +13 -0
- package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ExecutedAgreementsList.svelte +136 -0
- package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts +11 -0
- package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/LeadList.svelte +224 -0
- package/dist/svelte/components/LeadList.svelte.d.ts +19 -0
- package/dist/svelte/components/LeadList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/OpportunityBoard.svelte +230 -0
- package/dist/svelte/components/OpportunityBoard.svelte.d.ts +17 -0
- package/dist/svelte/components/OpportunityBoard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/OpportunityDetail.svelte +472 -0
- package/dist/svelte/components/OpportunityDetail.svelte.d.ts +27 -0
- package/dist/svelte/components/OpportunityDetail.svelte.d.ts.map +1 -0
- package/dist/svelte/components/PayoutBatchReview.svelte +285 -0
- package/dist/svelte/components/PayoutBatchReview.svelte.d.ts +21 -0
- package/dist/svelte/components/PayoutBatchReview.svelte.d.ts.map +1 -0
- package/dist/svelte/components/PayoutHistoryList.svelte +245 -0
- package/dist/svelte/components/PayoutHistoryList.svelte.d.ts +11 -0
- package/dist/svelte/components/PayoutHistoryList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReferralLinkManager.svelte +216 -0
- package/dist/svelte/components/ReferralLinkManager.svelte.d.ts +17 -0
- package/dist/svelte/components/ReferralLinkManager.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReferralStatusList.svelte +118 -0
- package/dist/svelte/components/ReferralStatusList.svelte.d.ts +11 -0
- package/dist/svelte/components/ReferralStatusList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReferrerEarningsSummary.svelte +106 -0
- package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts +11 -0
- package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts.map +1 -0
- package/dist/svelte/components/SalesDashboard.svelte +192 -0
- package/dist/svelte/components/SalesDashboard.svelte.d.ts +29 -0
- package/dist/svelte/components/SalesDashboard.svelte.d.ts.map +1 -0
- package/dist/svelte/format.d.ts +46 -0
- package/dist/svelte/format.d.ts.map +1 -0
- package/dist/svelte/format.js +75 -0
- package/dist/svelte/index.d.ts +50 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +37 -0
- package/dist/svelte/types.d.ts +422 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +376 -0
- package/package.json +89 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CommissionPlanComponent, CommissionPlanOptions, CommissionPlanStatus } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validate a components array. Throws a descriptive error on the first
|
|
5
|
+
* violation. Exported for reuse by the calculation service's input guards
|
|
6
|
+
* and by referral-terms builders that assemble component arrays.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* - component keys are non-empty and unique within the plan
|
|
10
|
+
* - `trigger` is a non-empty string (`'*'` matches every event kind)
|
|
11
|
+
* - `basis` is one of {@link COMMISSION_BASES}
|
|
12
|
+
* - basis `fixed` requires an integer `fixedAmountCents`
|
|
13
|
+
* - every other basis requires `rate` in `[0, 1]`
|
|
14
|
+
* - basis `custom` additionally requires a non-empty `customBasisKey`
|
|
15
|
+
* - `recurrence.kind` (when present) is `one_time` or `recurring`;
|
|
16
|
+
* `maxOccurrences` / `windowMonths` (when present) are positive integers
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateCommissionPlanComponents(components: CommissionPlanComponent[]): void;
|
|
19
|
+
export declare class CommissionPlan extends SmrtObject {
|
|
20
|
+
/** Tenant ID for multi-tenant isolation (nullable → global plans). */
|
|
21
|
+
tenantId: string | null;
|
|
22
|
+
/** Stable plan identity shared by every version of the plan. */
|
|
23
|
+
planKey: string;
|
|
24
|
+
/** Monotonic version within `planKey`. Amendments insert `max + 1`. */
|
|
25
|
+
version: number;
|
|
26
|
+
/** Human-readable plan name. */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Longer human-readable description of the terms. */
|
|
29
|
+
description: string;
|
|
30
|
+
/**
|
|
31
|
+
* Lifecycle status — see {@link PLAN_STATUS_TRANSITIONS}. Mutate via
|
|
32
|
+
* {@link activate} / {@link supersede} / {@link retire} (or a legal
|
|
33
|
+
* single-step assignment; the save-time guard rejects illegal edges).
|
|
34
|
+
*/
|
|
35
|
+
status: CommissionPlanStatus;
|
|
36
|
+
/** When this version takes effect. Frozen once the plan activates. */
|
|
37
|
+
effectiveFrom: Date | null;
|
|
38
|
+
/** ISO 4217 currency the plan's terms are denominated in. */
|
|
39
|
+
currency: string;
|
|
40
|
+
/**
|
|
41
|
+
* Calculation components as a JSON-string array — see
|
|
42
|
+
* {@link CommissionPlanComponent}. Use {@link getComponents} /
|
|
43
|
+
* {@link setComponents} (the setter validates).
|
|
44
|
+
*/
|
|
45
|
+
components: string;
|
|
46
|
+
/** Additional metadata as a JSON string. */
|
|
47
|
+
metadata: string;
|
|
48
|
+
constructor(options?: CommissionPlanOptions);
|
|
49
|
+
/**
|
|
50
|
+
* Re-coerce date fields after the framework reapplies raw option values,
|
|
51
|
+
* record the loaded status for the transition guard, and capture the
|
|
52
|
+
* frozen snapshot when the row arrived already activated. The snapshot is
|
|
53
|
+
* captured for every non-draft status (not just `active`) so a superseded
|
|
54
|
+
* or retired version — history — can't be rewritten either.
|
|
55
|
+
*/
|
|
56
|
+
initialize(): Promise<this>;
|
|
57
|
+
isDraft(): boolean;
|
|
58
|
+
isActive(): boolean;
|
|
59
|
+
/** Parse {@link components}; returns `[]` on empty/invalid JSON. */
|
|
60
|
+
getComponents(): CommissionPlanComponent[];
|
|
61
|
+
/**
|
|
62
|
+
* Validate and store the components array. Throws on invalid components —
|
|
63
|
+
* see {@link validateCommissionPlanComponents} for the rules.
|
|
64
|
+
*/
|
|
65
|
+
setComponents(components: CommissionPlanComponent[]): void;
|
|
66
|
+
/** Parse {@link metadata}; returns `{}` on empty/invalid JSON. */
|
|
67
|
+
getMetadata(): Record<string, unknown>;
|
|
68
|
+
/** Serialize and store {@link metadata}. */
|
|
69
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
70
|
+
/**
|
|
71
|
+
* Transition `draft → active`. Validates components first so no active
|
|
72
|
+
* plan can carry malformed terms.
|
|
73
|
+
*/
|
|
74
|
+
activate(): void;
|
|
75
|
+
/** Transition `active → superseded` (a newer version took over). */
|
|
76
|
+
supersede(): void;
|
|
77
|
+
/** Transition `draft | active → retired` (terminal). */
|
|
78
|
+
retire(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Save with two guards:
|
|
81
|
+
*
|
|
82
|
+
* 1. **Status transition** — the about-to-be-written status must be a
|
|
83
|
+
* legal edge from the authoritative prior persisted status (re-read
|
|
84
|
+
* from the DB so a `create({ id: <existing>, _skipLoad: true })` upsert
|
|
85
|
+
* can't sidestep the guard — commerce pattern).
|
|
86
|
+
* 2. **Frozen calculation identity** — once the row has been saved
|
|
87
|
+
* non-draft, `components` / `currency` / `planKey` / `version` /
|
|
88
|
+
* `effectiveFrom` must match the captured snapshot. Amend by inserting
|
|
89
|
+
* a new version instead.
|
|
90
|
+
*
|
|
91
|
+
* Activating saves also re-validate components, so an `active` row always
|
|
92
|
+
* carries well-formed terms regardless of which write path set them.
|
|
93
|
+
*/
|
|
94
|
+
save(): Promise<this>;
|
|
95
|
+
/**
|
|
96
|
+
* Refuse a save whose `(tenantId, planKey, version)` natural key already
|
|
97
|
+
* belongs to a DIFFERENT row. The frozen-identity guard above is
|
|
98
|
+
* instance-local (WeakMap), so a FRESH instance carrying an existing
|
|
99
|
+
* natural key would otherwise sail through and the conflict-column
|
|
100
|
+
* upsert would rewrite the persisted terms (and rotate the row id).
|
|
101
|
+
* Edit drafts by hydrating them; change terms with
|
|
102
|
+
* `CommissionPlanCollection.createAmendment()`.
|
|
103
|
+
*/
|
|
104
|
+
private assertNaturalKeyNotTaken;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve the AUTHORITATIVE prior status from the database; fall back to
|
|
107
|
+
* the loaded-status WeakMap only when the DB is unavailable. `undefined`
|
|
108
|
+
* means no persisted row exists (genuinely new).
|
|
109
|
+
*/
|
|
110
|
+
private resolvePriorStatus;
|
|
111
|
+
private assertStatusTransition;
|
|
112
|
+
private assertFrozenIdentityUnchanged;
|
|
113
|
+
private serializeFrozenSnapshot;
|
|
114
|
+
private static coerceDate;
|
|
115
|
+
}
|
|
116
|
+
export default CommissionPlan;
|
|
117
|
+
//# sourceMappingURL=CommissionPlan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommissionPlan.d.ts","sourceRoot":"","sources":["../../../src/commissions/models/CommissionPlan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAS,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAmCrB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gCAAgC,CAC9C,UAAU,EAAE,uBAAuB,EAAE,GACpC,IAAI,CA6EN;AAED,qBAgBa,cAAe,SAAQ,UAAU;IAC5C,sEAAsE;IAEtE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,gEAAgE;IAEhE,OAAO,EAAE,MAAM,CAAM;IAErB,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAK;IAEpB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAM;IAElB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAM;IAEzB;;;;OAIG;IACH,MAAM,EAAE,oBAAoB,CAAW;IAEvC,sEAAsE;IACtE,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAS;IAEzB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAQ;IAE1B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,qBAA0B;IAgB/C;;;;;;OAMG;IACY,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAc1C,OAAO,IAAI,OAAO;IAIlB,QAAQ,IAAI,OAAO;IAMnB,oEAAoE;IACpE,aAAa,IAAI,uBAAuB,EAAE;IAU1C;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,uBAAuB,EAAE,GAAG,IAAI;IAK1D,kEAAkE;IAClE,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYtC,4CAA4C;IAC5C,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMhD;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAUhB,oEAAoE;IACpE,SAAS,IAAI,IAAI;IASjB,wDAAwD;IACxD,MAAM,IAAI,IAAI;IAWd;;;;;;;;;;;;;;OAcG;IACY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBpC;;;;;;;;OAQG;YACW,wBAAwB;IAgCtC;;;;OAIG;YACW,kBAAkB;IAgBhC,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,6BAA6B;IAerC,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,MAAM,CAAC,UAAU;CAS1B;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { EarnerOptions, EarnerStatus, PayoutMethod } from '../types.js';
|
|
3
|
+
export declare class Earner extends SmrtObject {
|
|
4
|
+
/**
|
|
5
|
+
* Tenant ID for multi-tenant isolation. Nullable so global/operator-level
|
|
6
|
+
* earners remain possible; unlike legacy affiliates, sales earners are
|
|
7
|
+
* tenant-owned by default.
|
|
8
|
+
*/
|
|
9
|
+
tenantId: string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Identity link to a smrt-profiles Profile (cross-package string
|
|
12
|
+
* reference — never a DDL foreign key).
|
|
13
|
+
*/
|
|
14
|
+
profileId: string;
|
|
15
|
+
/** Human-readable display name for portals and operator views. */
|
|
16
|
+
displayName: string;
|
|
17
|
+
/** Account lifecycle: `pending` (default) → `active` / `suspended`. */
|
|
18
|
+
status: EarnerStatus;
|
|
19
|
+
/** Preferred payout delivery method. */
|
|
20
|
+
payoutMethod: PayoutMethod;
|
|
21
|
+
/**
|
|
22
|
+
* Minimum unsettled balance (integer cents) before a payout batch is
|
|
23
|
+
* created. Default $50.00 = 5000 cents.
|
|
24
|
+
*/
|
|
25
|
+
payoutThresholdCents: number;
|
|
26
|
+
/**
|
|
27
|
+
* Payout cadence key. Open string so applications can define their own
|
|
28
|
+
* schedules (`manual`, `monthly`, `weekly`, `net_30`, …); `manual` means
|
|
29
|
+
* an operator triggers batches explicitly.
|
|
30
|
+
*/
|
|
31
|
+
payoutScheduleKey: string;
|
|
32
|
+
/** ISO 4217 currency all of this earner's balances settle in. */
|
|
33
|
+
currency: string;
|
|
34
|
+
/**
|
|
35
|
+
* Additional metadata as a JSON string (tax info, payout-rail details,
|
|
36
|
+
* …). Use {@link getMetadata}/{@link setMetadata}.
|
|
37
|
+
*/
|
|
38
|
+
metadata: string;
|
|
39
|
+
constructor(options?: EarnerOptions);
|
|
40
|
+
isActive(): boolean;
|
|
41
|
+
isPending(): boolean;
|
|
42
|
+
isSuspended(): boolean;
|
|
43
|
+
/** Parse {@link metadata}; returns `{}` on empty/invalid JSON. */
|
|
44
|
+
getMetadata(): Record<string, unknown>;
|
|
45
|
+
/** Serialize and store {@link metadata}. */
|
|
46
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
47
|
+
}
|
|
48
|
+
export default Earner;
|
|
49
|
+
//# sourceMappingURL=Earner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Earner.d.ts","sourceRoot":"","sources":["../../../src/commissions/models/Earner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmB,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE7E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7E,qBAMa,MAAO,SAAQ,UAAU;IACpC;;;;OAIG;IAEH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B;;;OAGG;IAEH,SAAS,EAAE,MAAM,CAAM;IAEvB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAM;IAEzB,uEAAuE;IACvE,MAAM,EAAE,YAAY,CAAa;IAEjC,wCAAwC;IACxC,YAAY,EAAE,YAAY,CAAmB;IAE7C;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAQ;IAEpC;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAY;IAErC,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAS;IAEzB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,aAAkB;IAiBvC,QAAQ,IAAI,OAAO;IAInB,SAAS,IAAI,OAAO;IAIpB,WAAW,IAAI,OAAO;IAItB,kEAAkE;IAClE,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYtC,4CAA4C;IAC5C,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGjD;AAED,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { EarningEventOptions } from '../types.js';
|
|
3
|
+
export declare class EarningEvent extends SmrtObject {
|
|
4
|
+
/** Tenant ID for multi-tenant isolation (nullable → global events). */
|
|
5
|
+
tenantId: string | null;
|
|
6
|
+
/**
|
|
7
|
+
* What kind of commercial event this is. Open string — see
|
|
8
|
+
* `EARNING_EVENT_KINDS` for the recommended vocabulary. Plan components
|
|
9
|
+
* match on this via their `trigger`.
|
|
10
|
+
*/
|
|
11
|
+
eventKind: string;
|
|
12
|
+
/** When the commercial event occurred (not when it was ingested). */
|
|
13
|
+
occurredAt: Date;
|
|
14
|
+
/**
|
|
15
|
+
* Generic earning-source discriminator (`referral`, `opportunity`,
|
|
16
|
+
* `subscription`, `ad_event`, …). Free-form; this module attaches no
|
|
17
|
+
* semantics to it.
|
|
18
|
+
*/
|
|
19
|
+
sourceKind: string;
|
|
20
|
+
/** Id of the source record named by {@link sourceKind}. */
|
|
21
|
+
sourceId: string;
|
|
22
|
+
/** Gross amount of the event in integer cents. */
|
|
23
|
+
grossAmountCents: number;
|
|
24
|
+
/**
|
|
25
|
+
* Net amount in integer cents, when the ingesting system defines one.
|
|
26
|
+
* `null` means "net is not defined for this event" — `net`-basis
|
|
27
|
+
* components then SKIP rather than falling back to gross (net is never
|
|
28
|
+
* derived).
|
|
29
|
+
*/
|
|
30
|
+
netAmountCents: number | null;
|
|
31
|
+
/**
|
|
32
|
+
* Recognized margin in integer cents, when defined. `null` skips
|
|
33
|
+
* `margin`-basis components — margin is never derived.
|
|
34
|
+
*/
|
|
35
|
+
marginCents: number | null;
|
|
36
|
+
/** ISO 4217 currency of the event's amounts. */
|
|
37
|
+
currency: string;
|
|
38
|
+
/**
|
|
39
|
+
* JSON map of `basisKey → integer cents` for `custom`-basis plan
|
|
40
|
+
* components. Use {@link getCustomBases}/{@link setCustomBases}.
|
|
41
|
+
*/
|
|
42
|
+
customBases: string;
|
|
43
|
+
/**
|
|
44
|
+
* Idempotency natural key. Required. Callers embed tenant/source identity
|
|
45
|
+
* (see the class doc) — the framework does not synthesize it.
|
|
46
|
+
*/
|
|
47
|
+
dedupeKey: string;
|
|
48
|
+
/** Additional metadata as a JSON string. */
|
|
49
|
+
metadata: string;
|
|
50
|
+
constructor(options?: EarningEventOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Re-coerce {@link occurredAt} after the framework reapplies raw option /
|
|
53
|
+
* hydrated row values (SQLite hands back ISO strings), and capture the
|
|
54
|
+
* persisted state for the immutability guard when this instance hydrated
|
|
55
|
+
* an existing row.
|
|
56
|
+
*/
|
|
57
|
+
initialize(): Promise<this>;
|
|
58
|
+
/**
|
|
59
|
+
* Save with the evidence-immutability guard. EarningEvents are immutable
|
|
60
|
+
* commercial evidence; three write vectors are closed:
|
|
61
|
+
*
|
|
62
|
+
* - a HYDRATED persisted row must serialize identically to its captured
|
|
63
|
+
* state (no-op re-saves pass, any change throws);
|
|
64
|
+
* - an instance carrying an existing id WITHOUT having hydrated it
|
|
65
|
+
* (`create({ id, _skipLoad: true })`) is rejected outright;
|
|
66
|
+
* - a NEW instance whose `dedupeKey` already belongs to another row is
|
|
67
|
+
* refused outright: the natural-key upsert would not only rewrite the
|
|
68
|
+
* evidence values but ROTATE the row's id (orphaning any Commission
|
|
69
|
+
* whose `earningEventId` points at it). Idempotent ingestion goes
|
|
70
|
+
* through `EarningEventCollection.getOrCreateByDedupeKey()`, which
|
|
71
|
+
* finds first and never upserts.
|
|
72
|
+
*/
|
|
73
|
+
save(): Promise<this>;
|
|
74
|
+
private serializeState;
|
|
75
|
+
/**
|
|
76
|
+
* Parse {@link customBases} into a `basisKey → cents` map; non-numeric
|
|
77
|
+
* values are dropped. Returns `{}` on empty/invalid JSON.
|
|
78
|
+
*/
|
|
79
|
+
getCustomBases(): Record<string, number>;
|
|
80
|
+
/** Serialize and store {@link customBases}. */
|
|
81
|
+
setCustomBases(bases: Record<string, number>): void;
|
|
82
|
+
/** Parse {@link metadata}; returns `{}` on empty/invalid JSON. */
|
|
83
|
+
getMetadata(): Record<string, unknown>;
|
|
84
|
+
/** Serialize and store {@link metadata}. */
|
|
85
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
86
|
+
private static coerceDate;
|
|
87
|
+
}
|
|
88
|
+
export default EarningEvent;
|
|
89
|
+
//# sourceMappingURL=EarningEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EarningEvent.d.ts","sourceRoot":"","sources":["../../../src/commissions/models/EarningEvent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAS,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAEnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AASvD,qBAYa,YAAa,SAAQ,UAAU;IAC1C,uEAAuE;IAEvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B;;;;OAIG;IAEH,SAAS,EAAE,MAAM,CAAM;IAEvB,qEAAqE;IACrE,UAAU,EAAE,IAAI,CAAc;IAE9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAM;IAExB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAM;IAEtB,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,CAAK;IAE7B;;;;;OAKG;IAEH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC;;;OAGG;IAEH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAElC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAS;IAEzB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAQ;IAE3B;;;OAGG;IAEH,SAAS,EAAE,MAAM,CAAM;IAEvB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,mBAAwB;IAsB7C;;;;;OAKG;IACY,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAS1C;;;;;;;;;;;;;;OAcG;IACY,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDpC,OAAO,CAAC,cAAc;IAkBtB;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAqBxC,+CAA+C;IAC/C,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAInD,kEAAkE;IAClE,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYtC,4CAA4C;IAC5C,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhD,OAAO,CAAC,MAAM,CAAC,UAAU;CAS1B;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model barrel for the commissions module.
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
export { Commission } from './Commission.js';
|
|
6
|
+
export { CommissionAdjustment } from './CommissionAdjustment.js';
|
|
7
|
+
export { CommissionPayout } from './CommissionPayout.js';
|
|
8
|
+
export { CommissionPlan, validateCommissionPlanComponents, } from './CommissionPlan.js';
|
|
9
|
+
export { Earner } from './Earner.js';
|
|
10
|
+
export { EarningEvent } from './EarningEvent.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commissions/models/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,cAAc,EACd,gCAAgC,GACjC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integer-cents money helpers for the commissions module.
|
|
3
|
+
*
|
|
4
|
+
* Every monetary field in this module is stored as integer cents (`*Cents`
|
|
5
|
+
* suffix). Rounding happens exactly once per calculation step via
|
|
6
|
+
* {@link roundCents} using half-away-from-zero semantics, and every
|
|
7
|
+
* Commission persists a `calculationTrace` naming that rounding mode so
|
|
8
|
+
* amounts stay reproducible.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Round to the nearest integer cent, half away from zero.
|
|
14
|
+
*
|
|
15
|
+
* `Math.round` alone rounds -2.5 to -2 (half toward +∞); financial
|
|
16
|
+
* conventions want symmetric behaviour, so the sign is factored out first:
|
|
17
|
+
* `Math.sign(v) * Math.round(Math.abs(v))` → `roundCents(2.5) === 3` and
|
|
18
|
+
* `roundCents(-2.5) === -3`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function roundCents(value: number): number;
|
|
21
|
+
/** Convert integer cents to a decimal major-unit amount (`/ 100`). */
|
|
22
|
+
export declare function centsToAmount(cents: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Convert a decimal major-unit amount to integer cents, rounding half away
|
|
25
|
+
* from zero (`roundCents(amount * 100)`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function amountToCents(amount: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Calculate a commission amount in integer cents:
|
|
30
|
+
* `roundCents(baseCents * rate * (shareFraction ?? 1))`.
|
|
31
|
+
*
|
|
32
|
+
* Rounding is applied once, on the final product, so split siblings each
|
|
33
|
+
* round independently and their sum can differ from the unsplit amount by at
|
|
34
|
+
* most one cent per sibling — the calculation trace records the inputs so any
|
|
35
|
+
* such drift is auditable.
|
|
36
|
+
*
|
|
37
|
+
* @param baseCents - Base amount in integer cents
|
|
38
|
+
* @param rate - Commission rate (0–1)
|
|
39
|
+
* @param shareFraction - Optional split share (0–1); defaults to 1
|
|
40
|
+
*/
|
|
41
|
+
export declare function calculateCommissionAmountCents(baseCents: number, rate: number, shareFraction?: number): number;
|
|
42
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../src/commissions/money.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,CAER"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CommissionAdjustmentCollection } from '../collections/CommissionAdjustmentCollection.js';
|
|
3
|
+
import { CommissionCollection } from '../collections/CommissionCollection.js';
|
|
4
|
+
import { EarnerBalance } from '../types.js';
|
|
5
|
+
export declare class CommissionBalanceService {
|
|
6
|
+
private readonly commissions;
|
|
7
|
+
private readonly adjustments;
|
|
8
|
+
constructor(commissions: CommissionCollection, adjustments: CommissionAdjustmentCollection);
|
|
9
|
+
static create(classOptions?: SmrtClassOptions): Promise<CommissionBalanceService>;
|
|
10
|
+
/** Compute the {@link EarnerBalance} for one earner in one currency. */
|
|
11
|
+
getBalance(earnerId: string, currency: string): Promise<EarnerBalance>;
|
|
12
|
+
}
|
|
13
|
+
export default CommissionBalanceService;
|
|
14
|
+
//# sourceMappingURL=CommissionBalanceService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommissionBalanceService.d.ts","sourceRoot":"","sources":["../../../src/commissions/services/CommissionBalanceService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,kDAAkD,CAAC;AAClG,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAErB,qBAAa,wBAAwB;IAEjC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,WAAW,EAAE,oBAAoB,EACjC,WAAW,EAAE,8BAA8B;WAGjD,MAAM,CACjB,YAAY,GAAE,gBAAqB,GAClC,OAAO,CAAC,wBAAwB,CAAC;IAOpC,wEAAwE;IAClE,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CAkD7E;AAED,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CommissionCollection } from '../collections/CommissionCollection.js';
|
|
3
|
+
import { EarnerCollection } from '../collections/EarnerCollection.js';
|
|
4
|
+
import { Commission } from '../models/Commission.js';
|
|
5
|
+
import { EarningEvent } from '../models/EarningEvent.js';
|
|
6
|
+
import { CommissionPlanComponent } from '../types.js';
|
|
7
|
+
/** Input for {@link CommissionCalculationService.calculateForEvent}. */
|
|
8
|
+
export interface CommissionCalculationInput {
|
|
9
|
+
/** The (persisted) earning event to calculate from. */
|
|
10
|
+
event: EarningEvent;
|
|
11
|
+
/** Snapshot reference recorded on every created Commission. */
|
|
12
|
+
planKey: string;
|
|
13
|
+
/** Snapshot reference recorded on every created Commission. */
|
|
14
|
+
planVersion: number;
|
|
15
|
+
/** The calculation terms to apply (typically from a frozen snapshot). */
|
|
16
|
+
components: CommissionPlanComponent[];
|
|
17
|
+
/** The earner the commissions belong to. */
|
|
18
|
+
earnerId: string;
|
|
19
|
+
/**
|
|
20
|
+
* Split share (0–1) this earner receives; defaults to 1. Callers running
|
|
21
|
+
* a split invoke the service once per earner with the shares and a shared
|
|
22
|
+
* `splitGroupId`.
|
|
23
|
+
*/
|
|
24
|
+
shareFraction?: number;
|
|
25
|
+
/** Groups the sibling commissions of one split. */
|
|
26
|
+
splitGroupId?: string;
|
|
27
|
+
/** Generic polymorphic terms-snapshot reference (kind). */
|
|
28
|
+
termsSnapshotKind?: string;
|
|
29
|
+
/** Generic polymorphic terms-snapshot reference (id). */
|
|
30
|
+
termsSnapshotId?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Clearing window in days: created commissions get
|
|
33
|
+
* `clearingEndsAt = event.occurredAt + clearingDays`. Omitted → no
|
|
34
|
+
* clearing (`clearingEndsAt: null`, immediately sweepable).
|
|
35
|
+
*/
|
|
36
|
+
clearingDays?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Currency the plan/terms are denominated in. When provided and different
|
|
39
|
+
* from `event.currency`, every matching component skips with reason
|
|
40
|
+
* `'currency_mismatch'` (this module performs no FX). Omitted → the event
|
|
41
|
+
* currency is taken as authoritative and no mismatch is possible.
|
|
42
|
+
*/
|
|
43
|
+
currency?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Resolves how many occurrences of a component this earner has already
|
|
46
|
+
* consumed under these terms (commissions from PRIOR events — the current
|
|
47
|
+
* event must not be counted). Drives `one_time` / `maxOccurrences` limits
|
|
48
|
+
* and the `occurrenceIndex` in the dedupe key and trace. Omitted →
|
|
49
|
+
* occurrence count `0`, i.e. recurrence limits are NOT enforced.
|
|
50
|
+
*/
|
|
51
|
+
occurrenceCountResolver?: (componentKey: string) => Promise<number>;
|
|
52
|
+
/**
|
|
53
|
+
* Anchor for `windowMonths` recurrence checks (e.g. an agreement's
|
|
54
|
+
* effective date). Omitted → window checks are skipped.
|
|
55
|
+
*/
|
|
56
|
+
anchorAt?: Date;
|
|
57
|
+
}
|
|
58
|
+
/** One component the calculation declined, and why. */
|
|
59
|
+
export interface CommissionComponentSkip {
|
|
60
|
+
componentKey: string;
|
|
61
|
+
/**
|
|
62
|
+
* `'net_basis_undefined'` | `'margin_basis_undefined'` |
|
|
63
|
+
* `'fixed_amount_missing'` | `'rate_missing'` | `'custom_basis_missing'`
|
|
64
|
+
* | `'currency_mismatch'` | `'occurrence_limit_reached'` |
|
|
65
|
+
* `'outside_recurrence_window'`
|
|
66
|
+
*/
|
|
67
|
+
reason: string;
|
|
68
|
+
}
|
|
69
|
+
/** Result of {@link CommissionCalculationService.calculateForEvent}. */
|
|
70
|
+
export interface CommissionCalculationResult {
|
|
71
|
+
/** Commissions newly created by THIS call. */
|
|
72
|
+
created: Commission[];
|
|
73
|
+
/** Components that produced nothing, with reasons. */
|
|
74
|
+
skipped: CommissionComponentSkip[];
|
|
75
|
+
/**
|
|
76
|
+
* Idempotent replays: commissions that already existed for this
|
|
77
|
+
* (event, terms, component, earner) tuple. Never re-created, never
|
|
78
|
+
* mutated, and never in `created`.
|
|
79
|
+
*/
|
|
80
|
+
existing: Commission[];
|
|
81
|
+
}
|
|
82
|
+
export declare class CommissionCalculationService {
|
|
83
|
+
private readonly commissions;
|
|
84
|
+
/**
|
|
85
|
+
* Optional earner lookup for the tenant-lane guard. When provided,
|
|
86
|
+
* `calculateForEvent` refuses an earner from a different tenant lane
|
|
87
|
+
* than the event (a cross-tenant `earnerId` would create a commission
|
|
88
|
+
* payable to another tenant's account). `static create()` always wires
|
|
89
|
+
* it; direct constructors may omit it for narrow test fixtures.
|
|
90
|
+
*/
|
|
91
|
+
private readonly earners?;
|
|
92
|
+
constructor(commissions: CommissionCollection,
|
|
93
|
+
/**
|
|
94
|
+
* Optional earner lookup for the tenant-lane guard. When provided,
|
|
95
|
+
* `calculateForEvent` refuses an earner from a different tenant lane
|
|
96
|
+
* than the event (a cross-tenant `earnerId` would create a commission
|
|
97
|
+
* payable to another tenant's account). `static create()` always wires
|
|
98
|
+
* it; direct constructors may omit it for narrow test fixtures.
|
|
99
|
+
*/
|
|
100
|
+
earners?: EarnerCollection | undefined);
|
|
101
|
+
static create(classOptions?: SmrtClassOptions): Promise<CommissionCalculationService>;
|
|
102
|
+
/**
|
|
103
|
+
* Calculate commissions for one event × one earner × a component set.
|
|
104
|
+
*
|
|
105
|
+
* For each component whose `trigger` matches `event.eventKind` (or `'*'`
|
|
106
|
+
* — non-matching components are silently filtered, not "skipped"):
|
|
107
|
+
*
|
|
108
|
+
* 1. **Idempotency** — if a Commission already exists for this
|
|
109
|
+
* (event, terms, component, earner) tuple, it is returned in
|
|
110
|
+
* `existing` and nothing else runs for the component.
|
|
111
|
+
* 2. **Currency** — `input.currency` (when given) must equal the event's;
|
|
112
|
+
* otherwise skip `'currency_mismatch'`.
|
|
113
|
+
* 3. **Recurrence** — `one_time` components skip
|
|
114
|
+
* `'occurrence_limit_reached'` once the resolver reports ≥ 1 prior
|
|
115
|
+
* occurrence; `recurring` components honor `maxOccurrences` and
|
|
116
|
+
* `windowMonths` (events after `anchorAt + windowMonths` skip
|
|
117
|
+
* `'outside_recurrence_window'`).
|
|
118
|
+
* 4. **Basis** — gross → `grossAmountCents`; net → `netAmountCents`
|
|
119
|
+
* (skip `'net_basis_undefined'` when null — net is explicit, NEVER
|
|
120
|
+
* derived from gross); margin → `marginCents` (skip
|
|
121
|
+
* `'margin_basis_undefined'` when null); fixed → `fixedAmountCents`;
|
|
122
|
+
* custom → `getCustomBases()[customBasisKey]` (skip
|
|
123
|
+
* `'custom_basis_missing'`).
|
|
124
|
+
* 5. **Amount** — `roundCents(base * rate * shareFraction)`; for `fixed`,
|
|
125
|
+
* `roundCents(fixedAmountCents * shareFraction)` with `rate` recorded
|
|
126
|
+
* as `0`. Rounding happens exactly once, on the final product.
|
|
127
|
+
*
|
|
128
|
+
* Every created Commission is persisted `pending`, carries the event's
|
|
129
|
+
* tenant/currency/source, a complete {@link CommissionCalculationTrace},
|
|
130
|
+
* `clearingEndsAt` when `clearingDays` was given, and the dedupe key
|
|
131
|
+
* `` `${event.dedupeKey}:${termsSnapshotId || planKey + '@' + planVersion}:${componentKey}:${earnerId}:${occurrenceIndex}` ``.
|
|
132
|
+
*/
|
|
133
|
+
calculateForEvent(input: CommissionCalculationInput): Promise<CommissionCalculationResult>;
|
|
134
|
+
/**
|
|
135
|
+
* Calendar-month addition (UTC). JS `setUTCMonth` semantics: day-of-month
|
|
136
|
+
* overflow rolls into the next month (Jan 31 + 1 month → Mar 2/3), which
|
|
137
|
+
* is acceptable for coarse recurrence windows.
|
|
138
|
+
*/
|
|
139
|
+
private static addMonths;
|
|
140
|
+
}
|
|
141
|
+
export default CommissionCalculationService;
|
|
142
|
+
//# sourceMappingURL=CommissionCalculationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommissionCalculationService.d.ts","sourceRoot":"","sources":["../../../src/commissions/services/CommissionCalculationService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,aAAa,CAAC;AAIrB,wEAAwE;AACxE,MAAM,WAAW,0BAA0B;IACzC,uDAAuD;IACvD,KAAK,EAAE,YAAY,CAAC;IACpB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE;;;OAGG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,2BAA2B;IAC1C,8CAA8C;IAC9C,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sDAAsD;IACtD,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC;;;;OAIG;IACH,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,qBAAa,4BAA4B;IAErC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBARR,WAAW,EAAE,oBAAoB;IAClD;;;;;;OAMG;IACc,OAAO,CAAC,EAAE,gBAAgB,YAAA;WAGhC,MAAM,CACjB,YAAY,GAAE,gBAAqB,GAClC,OAAO,CAAC,4BAA4B,CAAC;IAOxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,iBAAiB,CACrB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC;IAoSvC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS;CAKzB;AAED,eAAe,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CommissionAdjustmentCollection } from '../collections/CommissionAdjustmentCollection.js';
|
|
3
|
+
import { CommissionCollection } from '../collections/CommissionCollection.js';
|
|
4
|
+
import { CommissionPayoutCollection } from '../collections/CommissionPayoutCollection.js';
|
|
5
|
+
import { EarnerCollection } from '../collections/EarnerCollection.js';
|
|
6
|
+
import { CommissionPayout } from '../models/CommissionPayout.js';
|
|
7
|
+
import { PayoutMethod } from '../types.js';
|
|
8
|
+
/** Collaborators for {@link CommissionPayoutService}. */
|
|
9
|
+
export interface CommissionPayoutServiceDeps {
|
|
10
|
+
earners: EarnerCollection;
|
|
11
|
+
commissions: CommissionCollection;
|
|
12
|
+
adjustments: CommissionAdjustmentCollection;
|
|
13
|
+
payouts: CommissionPayoutCollection;
|
|
14
|
+
}
|
|
15
|
+
/** Input for {@link CommissionPayoutService.createPayoutBatch}. */
|
|
16
|
+
export interface CreatePayoutBatchInput {
|
|
17
|
+
earnerId: string;
|
|
18
|
+
currency: string;
|
|
19
|
+
/** Informational period bounds recorded on the payout. */
|
|
20
|
+
periodStart?: Date;
|
|
21
|
+
periodEnd?: Date;
|
|
22
|
+
/**
|
|
23
|
+
* Idempotency natural key. Defaults to
|
|
24
|
+
* `` `${earnerId}:${currency}:${periodEnd ISO date}` `` where the date is
|
|
25
|
+
* the `YYYY-MM-DD` of `periodEnd` (falling back to `now`). Callers running
|
|
26
|
+
* more than one batch per earner/currency/day must supply their own key.
|
|
27
|
+
*/
|
|
28
|
+
idempotencyKey?: string;
|
|
29
|
+
/** Overrides the earner's `payoutThresholdCents`. */
|
|
30
|
+
minimumThresholdCents?: number;
|
|
31
|
+
/** Overrides the earner's `payoutMethod`. */
|
|
32
|
+
payoutMethod?: PayoutMethod;
|
|
33
|
+
/** Clock override for deterministic tests. */
|
|
34
|
+
now?: Date;
|
|
35
|
+
}
|
|
36
|
+
/** Result of {@link CommissionPayoutService.createPayoutBatch}. */
|
|
37
|
+
export interface CreatePayoutBatchResult {
|
|
38
|
+
/** The created (or, on an idempotent replay, existing) payout — `null` on refusal. */
|
|
39
|
+
payout: CommissionPayout | null;
|
|
40
|
+
/** `true` only when THIS call minted the payout. */
|
|
41
|
+
created: boolean;
|
|
42
|
+
/** Why no payout was created, when refused. */
|
|
43
|
+
reason?: 'below_threshold' | 'nothing_payable';
|
|
44
|
+
/** Ids of the commissions THIS call stamped onto the payout. */
|
|
45
|
+
settledCommissionIds: string[];
|
|
46
|
+
/** Ids of the adjustments THIS call stamped onto the payout. */
|
|
47
|
+
settledAdjustmentIds: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare class CommissionPayoutService {
|
|
50
|
+
private readonly deps;
|
|
51
|
+
constructor(deps: CommissionPayoutServiceDeps);
|
|
52
|
+
static create(classOptions?: SmrtClassOptions): Promise<CommissionPayoutService>;
|
|
53
|
+
/**
|
|
54
|
+
* Create a settlement batch for one earner in one currency.
|
|
55
|
+
*
|
|
56
|
+
* Flow:
|
|
57
|
+
* 1. **Idempotency + repair** — an existing payout with the (defaulted)
|
|
58
|
+
* key is returned as `{ payout, created: false }`. A clean replay
|
|
59
|
+
* touches nothing (new payable work is never swept into an existing
|
|
60
|
+
* batch). A PENDING payout whose stored totals disagree with the rows
|
|
61
|
+
* stamped with its id — the signature of an interrupted claim pass —
|
|
62
|
+
* is repaired: the claim pass re-runs and the totals are reconciled
|
|
63
|
+
* from the verified membership. Past `pending` the batch is frozen.
|
|
64
|
+
* 2. **Gather** — payable unsettled commissions for the earner/currency,
|
|
65
|
+
* plus unsettled adjustments whose parent commission is
|
|
66
|
+
* earned/approved/payable/paid (same eligibility as the balance
|
|
67
|
+
* service, so the batch settles exactly what the balance reports).
|
|
68
|
+
* 3. **Refuse** — `netTotal <= 0` → `'nothing_payable'`;
|
|
69
|
+
* `netTotal < threshold` (earner default, overridable) →
|
|
70
|
+
* `'below_threshold'`. Nothing is minted or stamped on refusal.
|
|
71
|
+
* 4. **Mint, claim, reconcile** — create the `pending` payout, then
|
|
72
|
+
* CLAIM the gathered rows through the collections' conditional
|
|
73
|
+
* `claimForPayout` (rows grabbed by another batch in the interim are
|
|
74
|
+
* skipped, never double-claimed), and finally store totals computed
|
|
75
|
+
* from the rows that were VERIFIABLY claimed — the payout's totals
|
|
76
|
+
* are always reproducible from its member rows.
|
|
77
|
+
*
|
|
78
|
+
* Concurrency: claims are conditional with post-save verification, which
|
|
79
|
+
* narrows but does not eliminate races between batches with different
|
|
80
|
+
* keys (the collection layer exposes no cross-row transaction — the same
|
|
81
|
+
* stance as commerce/ledgers compensation). Settlement runs are expected
|
|
82
|
+
* to be single-writer per earner; totals are correct-by-construction from
|
|
83
|
+
* claimed rows either way.
|
|
84
|
+
*/
|
|
85
|
+
createPayoutBatch(input: CreatePayoutBatchInput): Promise<CreatePayoutBatchResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Whether a payout's stored totals are reproducible from the rows
|
|
88
|
+
* actually stamped with its id — the invariant an interrupted claim pass
|
|
89
|
+
* breaks. Clean replays short-circuit on this; repair runs only when it
|
|
90
|
+
* fails.
|
|
91
|
+
*/
|
|
92
|
+
private membershipConsistent;
|
|
93
|
+
/**
|
|
94
|
+
* Claim pass + totals reconciliation for a PENDING payout.
|
|
95
|
+
*
|
|
96
|
+
* The claim set is the union of rows already stamped with this payout
|
|
97
|
+
* (an interrupted earlier pass) and the currently gathered eligible
|
|
98
|
+
* rows. Claims go through the collections' conditional `claimForPayout`
|
|
99
|
+
* (rows owned by another batch are skipped); totals are then recomputed
|
|
100
|
+
* from the claimed rows and saved when they drift from what the payout
|
|
101
|
+
* carries. In the pathological all-rows-raced-away case the payout keeps
|
|
102
|
+
* zero totals and a note — auditable, never double-paid.
|
|
103
|
+
*/
|
|
104
|
+
private claimAndReconcile;
|
|
105
|
+
/**
|
|
106
|
+
* Complete a payout: flip the batch's settled commissions
|
|
107
|
+
* `payable → paid` FIRST, then `payout.complete(paymentReference)`
|
|
108
|
+
* (requires status `processing`). Ordering matters for recoverability —
|
|
109
|
+
* if a member save fails mid-loop the payout is still `processing`, so a
|
|
110
|
+
* retry finishes the remaining members (already-paid ones are skipped)
|
|
111
|
+
* and then finalizes; the terminal transition never strands `payable`
|
|
112
|
+
* members behind a `completed` payout. Adjustments carry no status —
|
|
113
|
+
* stamping `payoutId` at batch time already settled them.
|
|
114
|
+
*/
|
|
115
|
+
completePayout(payoutId: string, paymentReference: string, now?: Date): Promise<CommissionPayout>;
|
|
116
|
+
/**
|
|
117
|
+
* Fail a payout (`approved | processing → failed`). The batch's rows stay
|
|
118
|
+
* stamped — after `resetFromFailed()` the SAME payout retries the SAME
|
|
119
|
+
* rows; releasing the rows to a different batch would double-pay them if
|
|
120
|
+
* the failed remittance later settled.
|
|
121
|
+
*/
|
|
122
|
+
failPayout(payoutId: string, reason: string): Promise<CommissionPayout>;
|
|
123
|
+
/**
|
|
124
|
+
* Unsettled adjustments for the earner/currency whose parent commission
|
|
125
|
+
* is earned/approved/payable/paid — the same eligibility rule the balance
|
|
126
|
+
* service applies, so batches settle exactly what balances report.
|
|
127
|
+
*/
|
|
128
|
+
private findEligibleUnsettledAdjustments;
|
|
129
|
+
private requirePayout;
|
|
130
|
+
/** `${earnerId}:${currency}:${YYYY-MM-DD of periodEnd}` — see the input doc. */
|
|
131
|
+
private static defaultIdempotencyKey;
|
|
132
|
+
}
|
|
133
|
+
export default CommissionPayoutService;
|
|
134
|
+
//# sourceMappingURL=CommissionPayoutService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommissionPayoutService.d.ts","sourceRoot":"","sources":["../../../src/commissions/services/CommissionPayoutService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,kDAAkD,CAAC;AAClG,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,8CAA8C,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAErB,yDAAyD;AACzD,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,oBAAoB,CAAC;IAClC,WAAW,EAAE,8BAA8B,CAAC;IAC5C,OAAO,EAAE,0BAA0B,CAAC;CACrC;AAED,mEAAmE;AACnE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,8CAA8C;IAC9C,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,mEAAmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,sFAAsF;IACtF,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;IAC/C,gEAAgE;IAChE,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,gEAAgE;IAChE,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,2BAA2B;WAEjD,MAAM,CACjB,YAAY,GAAE,gBAAqB,GAClC,OAAO,CAAC,uBAAuB,CAAC;IASnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACG,iBAAiB,CACrB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,uBAAuB,CAAC;IAqHnC;;;;;OAKG;YACW,oBAAoB;IAsBlC;;;;;;;;;;OAUG;YACW,iBAAiB;IA8E/B;;;;;;;;;OASG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,EACxB,GAAG,GAAE,IAAiB,GACrB,OAAO,CAAC,gBAAgB,CAAC;IA0B5B;;;;;OAKG;IACG,UAAU,CACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,CAAC;IAO5B;;;;OAIG;YACW,gCAAgC;YA6BhC,aAAa;IAU3B,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,qBAAqB;CAOrC;AAED,eAAe,uBAAuB,CAAC"}
|