@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,248 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { AttributionExceptionCollection } from '../collections/AttributionExceptionCollection.js';
|
|
3
|
+
import { AttributionPolicyCollection } from '../collections/AttributionPolicyCollection.js';
|
|
4
|
+
import { ReferralCollection } from '../collections/ReferralCollection.js';
|
|
5
|
+
import { ReferralProgramCollection } from '../collections/ReferralProgramCollection.js';
|
|
6
|
+
import { ReferralTouchCollection } from '../collections/ReferralTouchCollection.js';
|
|
7
|
+
import { ReferrerCollection } from '../collections/ReferrerCollection.js';
|
|
8
|
+
import { AttributionException } from '../models/AttributionException.js';
|
|
9
|
+
import { Referral } from '../models/Referral.js';
|
|
10
|
+
import { ReferralTouch } from '../models/ReferralTouch.js';
|
|
11
|
+
/**
|
|
12
|
+
* Thrown by {@link AttributionService.override} when the target already has
|
|
13
|
+
* a QUALIFIED referral: qualified credit is governed by its term snapshot
|
|
14
|
+
* and commission adjustments downstream — re-attribution would silently
|
|
15
|
+
* orphan agreed terms.
|
|
16
|
+
*/
|
|
17
|
+
export declare class QualifiedReferralOverrideError extends Error {
|
|
18
|
+
constructor(referralIds: string[]);
|
|
19
|
+
}
|
|
20
|
+
/** Collaborators for {@link AttributionService}. */
|
|
21
|
+
export interface AttributionServiceDeps {
|
|
22
|
+
touches: ReferralTouchCollection;
|
|
23
|
+
referrals: ReferralCollection;
|
|
24
|
+
exceptions: AttributionExceptionCollection;
|
|
25
|
+
policies: AttributionPolicyCollection;
|
|
26
|
+
programs: ReferralProgramCollection;
|
|
27
|
+
referrers: ReferrerCollection;
|
|
28
|
+
}
|
|
29
|
+
/** Why {@link AttributionService.resolve} refused to attribute. */
|
|
30
|
+
export type AttributionRefusalReason = 'existing_client_ineligible' | 'no_active_policy' | 'no_eligible_touches';
|
|
31
|
+
/** Input for {@link AttributionService.resolve}. */
|
|
32
|
+
export interface ResolveAttributionInput {
|
|
33
|
+
/** Generic qualifying target the referrals will point at. */
|
|
34
|
+
targetKind: string;
|
|
35
|
+
/** Identifier within the `targetKind` namespace. */
|
|
36
|
+
targetId: string;
|
|
37
|
+
/** The program to attribute under. */
|
|
38
|
+
programId: string;
|
|
39
|
+
/** Prospect identity to gather touches by (in addition to the target). */
|
|
40
|
+
subjectKind?: string;
|
|
41
|
+
/** Identifier within the `subjectKind` namespace. */
|
|
42
|
+
subjectId?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The prospect's own profile id, when known — enables the self-referral
|
|
45
|
+
* eligibility check against each candidate referrer's `profileId`.
|
|
46
|
+
*/
|
|
47
|
+
subjectProfileId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Caller's statement that the prospect is already a client. Policies with
|
|
50
|
+
* `allowExistingClients: false` refuse the whole resolution.
|
|
51
|
+
*/
|
|
52
|
+
isExistingClient?: boolean;
|
|
53
|
+
/** Overrides the program's `defaultAttributionPolicyKey`. */
|
|
54
|
+
policyKey?: string;
|
|
55
|
+
/** Clock override for deterministic tests. */
|
|
56
|
+
now?: Date;
|
|
57
|
+
}
|
|
58
|
+
/** Result of {@link AttributionService.resolve}. */
|
|
59
|
+
export interface AttributionResolution {
|
|
60
|
+
/**
|
|
61
|
+
* The attributed referral(s): one for sole credit, several siblings for
|
|
62
|
+
* `split` mode. Empty when an exception was raised, the resolution was
|
|
63
|
+
* refused, or nothing was eligible.
|
|
64
|
+
*/
|
|
65
|
+
referrals: Referral[];
|
|
66
|
+
/** The conflict parked for review, when the policy could not conclude. */
|
|
67
|
+
exception: AttributionException | null;
|
|
68
|
+
/** Why nothing was attributed, when refused outright. */
|
|
69
|
+
refused?: AttributionRefusalReason;
|
|
70
|
+
}
|
|
71
|
+
/** Input for {@link AttributionService.recordManualAssignment}. */
|
|
72
|
+
export interface RecordManualAssignmentInput {
|
|
73
|
+
referrerId: string;
|
|
74
|
+
programId: string;
|
|
75
|
+
targetKind: string;
|
|
76
|
+
targetId: string;
|
|
77
|
+
/** Prospect identity; defaults to the target pair when omitted. */
|
|
78
|
+
subjectKind?: string;
|
|
79
|
+
subjectId?: string;
|
|
80
|
+
/** Who recorded the assignment (audit). Required. */
|
|
81
|
+
actorProfileId: string;
|
|
82
|
+
/** Free-form rationale recorded in the touch evidence. */
|
|
83
|
+
reason?: string;
|
|
84
|
+
/** When the assignment applies from; defaults to now. */
|
|
85
|
+
occurredAt?: Date;
|
|
86
|
+
}
|
|
87
|
+
/** One credit award inside a resolution/override. */
|
|
88
|
+
export interface AttributionAward {
|
|
89
|
+
referrerId: string;
|
|
90
|
+
/** Credit share (0–1]; awards must sum to 1.0 (±0.0001). */
|
|
91
|
+
creditFraction: number;
|
|
92
|
+
}
|
|
93
|
+
/** Input for {@link AttributionService.resolveException}. */
|
|
94
|
+
export interface ResolveExceptionInput {
|
|
95
|
+
exceptionId: string;
|
|
96
|
+
awards: AttributionAward[];
|
|
97
|
+
/** Non-empty rationale. Required — resolutions are audited. */
|
|
98
|
+
resolutionReason: string;
|
|
99
|
+
/** Who resolved the exception (audit). Required. */
|
|
100
|
+
actorProfileId: string;
|
|
101
|
+
/** Clock override for deterministic tests. */
|
|
102
|
+
now?: Date;
|
|
103
|
+
}
|
|
104
|
+
/** Input for {@link AttributionService.override}. */
|
|
105
|
+
export interface OverrideAttributionInput {
|
|
106
|
+
targetKind: string;
|
|
107
|
+
targetId: string;
|
|
108
|
+
programId: string;
|
|
109
|
+
awards: AttributionAward[];
|
|
110
|
+
/** Non-empty rationale. Required — overrides are audited. */
|
|
111
|
+
resolutionReason: string;
|
|
112
|
+
/** Who performed the override (audit). Required. */
|
|
113
|
+
actorProfileId: string;
|
|
114
|
+
/** Clock override for deterministic tests. */
|
|
115
|
+
now?: Date;
|
|
116
|
+
}
|
|
117
|
+
/** Result of {@link AttributionService.resolveException} / {@link AttributionService.override}. */
|
|
118
|
+
export interface AttributionAwardResult {
|
|
119
|
+
/** The attributed referral(s) the awards created. */
|
|
120
|
+
referrals: Referral[];
|
|
121
|
+
/** The resolved exception row auditing the decision. */
|
|
122
|
+
exception: AttributionException;
|
|
123
|
+
}
|
|
124
|
+
export declare class AttributionService {
|
|
125
|
+
private readonly deps;
|
|
126
|
+
constructor(deps: AttributionServiceDeps);
|
|
127
|
+
static create(classOptions?: SmrtClassOptions): Promise<AttributionService>;
|
|
128
|
+
/**
|
|
129
|
+
* Resolve attribution for one target under one program.
|
|
130
|
+
*
|
|
131
|
+
* IDEMPOTENT: existing non-disqualified `attributed`/`qualified`
|
|
132
|
+
* referrals for the target+program are returned as-is (no new rows), and
|
|
133
|
+
* an already-OPEN exception for the target+program is returned instead of
|
|
134
|
+
* a duplicate.
|
|
135
|
+
*
|
|
136
|
+
* Flow:
|
|
137
|
+
* 1. **Policy** — the program's `defaultAttributionPolicyKey` (or the
|
|
138
|
+
* `policyKey` override) is resolved via `latestActiveByKey`; no active
|
|
139
|
+
* version → `refused: 'no_active_policy'`.
|
|
140
|
+
* 2. **Existing-client gate** — `isExistingClient` with a policy that
|
|
141
|
+
* disallows it → `refused: 'existing_client_ineligible'`.
|
|
142
|
+
* 3. **Candidates** — touches gathered by the subject pair (when given)
|
|
143
|
+
* and by the target pair, deduplicated, restricted to the program and
|
|
144
|
+
* to `occurredAt` within `[now - windowDays, now]`.
|
|
145
|
+
* 4. **Eligibility** — self-referrals (candidate referrer's `profileId`
|
|
146
|
+
* equals `subjectProfileId`) are dropped unless the policy allows
|
|
147
|
+
* them. Nothing left → `refused: 'no_eligible_touches'`.
|
|
148
|
+
* 5. **Conflicts** — `conflictBehavior: 'review'` with more than one
|
|
149
|
+
* distinct eligible referrer, exact-timestamp ties between distinct
|
|
150
|
+
* referrers in `first_touch`/`last_touch`, or competing manual
|
|
151
|
+
* assignments in `assigned` mode → an OPEN AttributionException
|
|
152
|
+
* carrying the candidates; NO referral rows.
|
|
153
|
+
* 6. **Credit** — otherwise the mode elects the winner(s):
|
|
154
|
+
* `first_touch`/`last_touch` (earliest/latest), `assigned` (most
|
|
155
|
+
* recent manual assignment; assignments beat clicks by definition),
|
|
156
|
+
* or `split` (every distinct eligible referrer, equal fractions
|
|
157
|
+
* rounded to 4 dp with the remainder on the last, one shared
|
|
158
|
+
* `splitGroupId`). Referrals are created `pending → attributed` with
|
|
159
|
+
* the policy pin, winning touch, `attributedAt = now`, and
|
|
160
|
+
* `expiresAt = now + windowDays`.
|
|
161
|
+
*/
|
|
162
|
+
resolve(input: ResolveAttributionInput): Promise<AttributionResolution>;
|
|
163
|
+
/**
|
|
164
|
+
* Record an operator's manual assignment as an immutable
|
|
165
|
+
* `manual_assignment` ReferralTouch (evidence carries the actor, reason,
|
|
166
|
+
* and target) and return it. The caller then runs {@link resolve} — under
|
|
167
|
+
* an `assigned`-mode policy the assignment wins over click evidence.
|
|
168
|
+
*/
|
|
169
|
+
recordManualAssignment(input: RecordManualAssignmentInput): Promise<ReferralTouch>;
|
|
170
|
+
/**
|
|
171
|
+
* Resolve an OPEN exception by awarding credit explicitly.
|
|
172
|
+
*
|
|
173
|
+
* Requires a non-empty `resolutionReason` (throws otherwise) and awards
|
|
174
|
+
* whose `creditFraction`s sum to 1.0 (±0.0001) across distinct referrers.
|
|
175
|
+
* Creates the attributed referral(s) from the exception's target/program
|
|
176
|
+
* (split semantics — shared `splitGroupId` — when several awards), then
|
|
177
|
+
* marks the exception resolved (`resolutionMode: 'override'`, the award
|
|
178
|
+
* audit fields, `resolvedReferralIds`).
|
|
179
|
+
*/
|
|
180
|
+
resolveException(input: ResolveExceptionInput): Promise<AttributionAwardResult>;
|
|
181
|
+
/**
|
|
182
|
+
* Re-attribute an already-attributed target.
|
|
183
|
+
*
|
|
184
|
+
* Requires a non-empty `resolutionReason` and awards summing to 1.0.
|
|
185
|
+
* REFUSES (throws {@link QualifiedReferralOverrideError}) when the target
|
|
186
|
+
* has a QUALIFIED referral — those are governed by term snapshots and
|
|
187
|
+
* commission adjustments downstream. Otherwise: disqualifies the existing
|
|
188
|
+
* `attributed` referrals for the target+program (stamping the audit
|
|
189
|
+
* exception's id into their metadata), creates the newly awarded
|
|
190
|
+
* attributed referral(s), and writes a RESOLVED AttributionException as
|
|
191
|
+
* the audit record (candidates = the displaced + awarded referrers).
|
|
192
|
+
*
|
|
193
|
+
* The new referrals inherit the POLICY PIN the displaced credit carried
|
|
194
|
+
* (an override re-decides WHO earned the introduction, not which policy
|
|
195
|
+
* version governed it); the program's current default active policy is
|
|
196
|
+
* the fallback when nothing was displaced.
|
|
197
|
+
*/
|
|
198
|
+
override(input: OverrideAttributionInput): Promise<AttributionAwardResult>;
|
|
199
|
+
private requireProgram;
|
|
200
|
+
/**
|
|
201
|
+
* The active policy governing a resolution: the explicit `policyKey`
|
|
202
|
+
* override when given, else the program's default key; resolved to its
|
|
203
|
+
* latest ACTIVE version. `null` when no key or no active version.
|
|
204
|
+
*/
|
|
205
|
+
private resolvePolicy;
|
|
206
|
+
/**
|
|
207
|
+
* Candidate touches for a resolution: gathered by the subject pair (when
|
|
208
|
+
* given) and by the target pair, deduplicated, restricted to the program,
|
|
209
|
+
* to `occurredAt` within `[now - windowDays, now]`, and to referrers that
|
|
210
|
+
* pass the self-referral gate. Sorted by `occurredAt` ascending.
|
|
211
|
+
*/
|
|
212
|
+
private gatherEligibleTouches;
|
|
213
|
+
/** Create an OPEN exception carrying the competing candidates. */
|
|
214
|
+
private createOpenException;
|
|
215
|
+
/**
|
|
216
|
+
* The policy pin an exception was raised under (stamped into its
|
|
217
|
+
* metadata at creation); falls back to the program's current default
|
|
218
|
+
* active policy for exceptions created without one.
|
|
219
|
+
*/
|
|
220
|
+
private resolvePolicyPin;
|
|
221
|
+
/**
|
|
222
|
+
* The policy pin an override re-attributes under: the pin the displaced
|
|
223
|
+
* credit carried (looking the exact version's `windowDays` back up for
|
|
224
|
+
* the new expiry), falling back to the program's current default active
|
|
225
|
+
* policy when nothing pinned was displaced.
|
|
226
|
+
*/
|
|
227
|
+
private resolveOverridePolicyPin;
|
|
228
|
+
/**
|
|
229
|
+
* Create one referral as `pending`, transition it `pending → attributed`
|
|
230
|
+
* (stamping `attributedAt`), set the policy pin and expiry, and save.
|
|
231
|
+
*/
|
|
232
|
+
private createAttributedReferral;
|
|
233
|
+
/**
|
|
234
|
+
* Equal split fractions for `n` referrers, each rounded to 4 decimal
|
|
235
|
+
* places, with the LAST adjusted so the set sums to exactly 1.0.
|
|
236
|
+
*/
|
|
237
|
+
private static equalFractions;
|
|
238
|
+
private static assertResolutionReason;
|
|
239
|
+
/**
|
|
240
|
+
* Attribution decisions are audited — a blank actor would permanently
|
|
241
|
+
* resolve/override credit with an empty `resolvedByProfileId`, defeating
|
|
242
|
+
* the audit trail.
|
|
243
|
+
*/
|
|
244
|
+
private static assertActor;
|
|
245
|
+
private static assertAwards;
|
|
246
|
+
}
|
|
247
|
+
export default AttributionService;
|
|
248
|
+
//# sourceMappingURL=AttributionService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttributionService.d.ts","sourceRoot":"","sources":["../../../src/referrals/services/AttributionService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,8BAA8B,EAAE,MAAM,kDAAkD,CAAC;AAClG,OAAO,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAE9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAKhE;;;;;GAKG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;gBAC3C,WAAW,EAAE,MAAM,EAAE;CAQlC;AAED,oDAAoD;AACpD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,UAAU,EAAE,8BAA8B,CAAC;IAC3C,QAAQ,EAAE,2BAA2B,CAAC;IACtC,QAAQ,EAAE,yBAAyB,CAAC;IACpC,SAAS,EAAE,kBAAkB,CAAC;CAC/B;AAED,mEAAmE;AACnE,MAAM,MAAM,wBAAwB,GAChC,4BAA4B,GAC5B,kBAAkB,GAClB,qBAAqB,CAAC;AAE1B,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACtC,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,oDAAoD;AACpD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,0EAA0E;IAC1E,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC,yDAAyD;IACzD,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC;AAED,mEAAmE;AACnE,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB;AAED,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,mGAAmG;AACnG,MAAM,WAAW,sBAAsB;IACrC,qDAAqD;IACrD,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,wDAAwD;IACxD,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,sBAAsB;WAE5C,MAAM,CACjB,YAAY,GAAE,gBAAqB,GAClC,OAAO,CAAC,kBAAkB,CAAC;IAW9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,OAAO,CACX,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,qBAAqB,CAAC;IAoLjC;;;;;OAKG;IACG,sBAAsB,CAC1B,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,aAAa,CAAC;IA4BzB;;;;;;;;;OASG;IACG,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC;IA0DlC;;;;;;;;;;;;;;;;OAgBG;IACG,QAAQ,CACZ,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,sBAAsB,CAAC;YA+GpB,cAAc;IAQ5B;;;;OAIG;YACW,aAAa;IAkB3B;;;;;OAKG;YACW,qBAAqB;IAmDnC,kEAAkE;YACpD,mBAAmB;IAmCjC;;;;OAIG;YACW,gBAAgB;IAiC9B;;;;;OAKG;YACW,wBAAwB;IAoCtC;;;OAGG;YACW,wBAAwB;IAqDtC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAQ7B,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAQrC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAQ1B,OAAO,CAAC,MAAM,CAAC,YAAY;CAiC5B;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { Commission, CommissionCalculationService, CommissionCollection, CommissionComponentSkip, EarningEvent } from '../../commissions/index.js';
|
|
3
|
+
import { ReferralCollection } from '../collections/ReferralCollection.js';
|
|
4
|
+
import { ReferralTermSnapshotCollection } from '../collections/ReferralTermSnapshotCollection.js';
|
|
5
|
+
import { ReferrerCollection } from '../collections/ReferrerCollection.js';
|
|
6
|
+
/**
|
|
7
|
+
* The `termsSnapshotKind` every referral-bridged Commission carries —
|
|
8
|
+
* points `termsSnapshotId` at a ReferralTermSnapshot row.
|
|
9
|
+
*/
|
|
10
|
+
export declare const REFERRAL_TERMS_SNAPSHOT_KIND = "referral_term_snapshot";
|
|
11
|
+
/** Collaborators for {@link ReferralCommissionService}. */
|
|
12
|
+
export interface ReferralCommissionServiceDeps {
|
|
13
|
+
referrals: ReferralCollection;
|
|
14
|
+
snapshots: ReferralTermSnapshotCollection;
|
|
15
|
+
referrers: ReferrerCollection;
|
|
16
|
+
/** The commissions module's calculation service (same package). */
|
|
17
|
+
calculation: CommissionCalculationService;
|
|
18
|
+
/** The commissions module's commission collection (occurrence counting). */
|
|
19
|
+
commissions: CommissionCollection;
|
|
20
|
+
}
|
|
21
|
+
/** Input for {@link ReferralCommissionService.processEarningEvent}. */
|
|
22
|
+
export interface ProcessEarningEventInput {
|
|
23
|
+
/** The (persisted) earning event to pay referrals from. */
|
|
24
|
+
event: EarningEvent;
|
|
25
|
+
/** Resolve qualified referrals by target … */
|
|
26
|
+
targetKind?: string;
|
|
27
|
+
targetId?: string;
|
|
28
|
+
/** … or name them explicitly (takes precedence over the target pair). */
|
|
29
|
+
referralIds?: string[];
|
|
30
|
+
/** Reserved clock override for symmetry with the sibling services. */
|
|
31
|
+
now?: Date;
|
|
32
|
+
}
|
|
33
|
+
/** Why a referral produced no calculation. */
|
|
34
|
+
export type ReferralProcessingSkipReason = 'referral_not_found' | 'not_qualified' | 'missing_snapshot' | 'referrer_missing_earner' | 'tenant_mismatch';
|
|
35
|
+
/** One referral the bridge skipped, and why. */
|
|
36
|
+
export interface ReferralProcessingSkip {
|
|
37
|
+
referralId: string;
|
|
38
|
+
reason: ReferralProcessingSkipReason;
|
|
39
|
+
}
|
|
40
|
+
/** Per-referral calculation outcome. */
|
|
41
|
+
export interface ReferralCommissionOutcome {
|
|
42
|
+
referralId: string;
|
|
43
|
+
/** Commissions newly created for this referral by THIS call. */
|
|
44
|
+
created: Commission[];
|
|
45
|
+
/** Idempotent replays — commissions that already existed. */
|
|
46
|
+
existing: Commission[];
|
|
47
|
+
/** Components the calculation declined, with reasons. */
|
|
48
|
+
skipped: CommissionComponentSkip[];
|
|
49
|
+
}
|
|
50
|
+
/** Result of {@link ReferralCommissionService.processEarningEvent}. */
|
|
51
|
+
export interface ProcessEarningEventResult {
|
|
52
|
+
/** One entry per qualified referral that reached calculation. */
|
|
53
|
+
results: ReferralCommissionOutcome[];
|
|
54
|
+
/** Referrals that never reached calculation, with reasons. */
|
|
55
|
+
skippedReferrals: ReferralProcessingSkip[];
|
|
56
|
+
}
|
|
57
|
+
export declare class ReferralCommissionService {
|
|
58
|
+
private readonly deps;
|
|
59
|
+
constructor(deps: ReferralCommissionServiceDeps);
|
|
60
|
+
static create(classOptions?: SmrtClassOptions): Promise<ReferralCommissionService>;
|
|
61
|
+
/**
|
|
62
|
+
* Turn one earning event into Commissions for every qualified referral it
|
|
63
|
+
* concerns.
|
|
64
|
+
*
|
|
65
|
+
* Referral resolution: explicit `referralIds` when given (rows that are
|
|
66
|
+
* missing / not qualified are reported in `skippedReferrals`), else
|
|
67
|
+
* `findQualifiedByTarget(targetKind, targetId)`. One of the two forms is
|
|
68
|
+
* required.
|
|
69
|
+
*
|
|
70
|
+
* Per referral:
|
|
71
|
+
* 1. Load its ReferralTermSnapshot (`'missing_snapshot'` skip when the
|
|
72
|
+
* pointer is empty/dangling).
|
|
73
|
+
* 2. Resolve the referrer's `earnerId`
|
|
74
|
+
* (`'referrer_missing_earner'` skip when absent — roles without a
|
|
75
|
+
* payout account cannot earn).
|
|
76
|
+
* 3. Delegate to `CommissionCalculationService.calculateForEvent` with
|
|
77
|
+
* the snapshot's plan pin, FROZEN components, currency (mismatches
|
|
78
|
+
* surface as the calc service's `currency_mismatch` skips),
|
|
79
|
+
* clearingDays, the referral's `creditFraction` as the share, its
|
|
80
|
+
* `splitGroupId`, the
|
|
81
|
+
* `('referral_term_snapshot', snapshot.id)` terms reference, and an
|
|
82
|
+
* occurrence resolver counting existing Commissions for
|
|
83
|
+
* `(termsSnapshotId, componentKey)` from PRIOR events.
|
|
84
|
+
*
|
|
85
|
+
* Fully idempotent on replay: the calculation service's dedupe keys
|
|
86
|
+
* return prior rows in each outcome's `existing`.
|
|
87
|
+
*/
|
|
88
|
+
processEarningEvent(input: ProcessEarningEventInput): Promise<ProcessEarningEventResult>;
|
|
89
|
+
}
|
|
90
|
+
export default ReferralCommissionService;
|
|
91
|
+
//# sourceMappingURL=ReferralCommissionService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReferralCommissionService.d.ts","sourceRoot":"","sources":["../../../src/referrals/services/ReferralCommissionService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACL,KAAK,UAAU,EACf,4BAA4B,EAC5B,oBAAoB,EACpB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,8BAA8B,EAAE,MAAM,kDAAkD,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E;;;GAGG;AACH,eAAO,MAAM,4BAA4B,2BAA2B,CAAC;AAErE,2DAA2D;AAC3D,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,8BAA8B,CAAC;IAC1C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,mEAAmE;IACnE,WAAW,EAAE,4BAA4B,CAAC;IAC1C,4EAA4E;IAC5E,WAAW,EAAE,oBAAoB,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,WAAW,wBAAwB;IACvC,2DAA2D;IAC3D,KAAK,EAAE,YAAY,CAAC;IACpB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,sEAAsE;IACtE,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,8CAA8C;AAC9C,MAAM,MAAM,4BAA4B,GACpC,oBAAoB,GACpB,eAAe,GACf,kBAAkB,GAClB,yBAAyB,GACzB,iBAAiB,CAAC;AAEtB,gDAAgD;AAChD,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,4BAA4B,CAAC;CACtC;AAED,wCAAwC;AACxC,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,yDAAyD;IACzD,OAAO,EAAE,uBAAuB,EAAE,CAAC;CACpC;AAED,uEAAuE;AACvE,MAAM,WAAW,yBAAyB;IACxC,iEAAiE;IACjE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACrC,8DAA8D;IAC9D,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;CAC5C;AAED,qBAAa,yBAAyB;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,6BAA6B;WAEnD,MAAM,CACjB,YAAY,GAAE,gBAAqB,GAClC,OAAO,CAAC,yBAAyB,CAAC;IAUrC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,mBAAmB,CACvB,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;CA4HtC;AAED,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { CommissionPlanCollection } from '../../commissions/index.js';
|
|
3
|
+
import { AttributionPolicyCollection } from '../collections/AttributionPolicyCollection.js';
|
|
4
|
+
import { ReferralAgreementCollection } from '../collections/ReferralAgreementCollection.js';
|
|
5
|
+
import { ReferralCollection } from '../collections/ReferralCollection.js';
|
|
6
|
+
import { ReferralTermSnapshotCollection } from '../collections/ReferralTermSnapshotCollection.js';
|
|
7
|
+
import { ReferrerCollection } from '../collections/ReferrerCollection.js';
|
|
8
|
+
import { Referral } from '../models/Referral.js';
|
|
9
|
+
import { ReferralTermSnapshot } from '../models/ReferralTermSnapshot.js';
|
|
10
|
+
/** Collaborators for {@link ReferralQualificationService}. */
|
|
11
|
+
export interface ReferralQualificationServiceDeps {
|
|
12
|
+
referrals: ReferralCollection;
|
|
13
|
+
agreements: ReferralAgreementCollection;
|
|
14
|
+
/** The commissions module's plan collection (same package). */
|
|
15
|
+
plans: CommissionPlanCollection;
|
|
16
|
+
policies: AttributionPolicyCollection;
|
|
17
|
+
snapshots: ReferralTermSnapshotCollection;
|
|
18
|
+
referrers: ReferrerCollection;
|
|
19
|
+
}
|
|
20
|
+
/** Why {@link ReferralQualificationService.qualify} refused. */
|
|
21
|
+
export type QualificationRefusalReason = 'not_attributed' | 'existing_client_ineligible' | 'self_referral_ineligible' | 'no_active_agreement' | 'no_active_plan';
|
|
22
|
+
/** Input for {@link ReferralQualificationService.qualify}. */
|
|
23
|
+
export interface QualifyReferralInput {
|
|
24
|
+
referralId: string;
|
|
25
|
+
/** Clock override for deterministic tests. */
|
|
26
|
+
now?: Date;
|
|
27
|
+
/** Re-check flag: the prospect is already a client. */
|
|
28
|
+
isExistingClient?: boolean;
|
|
29
|
+
/** Re-check flag: the prospect's own profile id (self-referral gate). */
|
|
30
|
+
subjectProfileId?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Result of {@link ReferralQualificationService.qualify}. */
|
|
33
|
+
export interface QualifyReferralResult {
|
|
34
|
+
/** Whether the referral is (now or already) qualified. */
|
|
35
|
+
qualified: boolean;
|
|
36
|
+
/** `true` only when THIS call minted the snapshot. */
|
|
37
|
+
created: boolean;
|
|
38
|
+
/** The governing snapshot (`null` on refusal). */
|
|
39
|
+
snapshot: ReferralTermSnapshot | null;
|
|
40
|
+
/** The (re)loaded referral (`null` only when the id is unknown). */
|
|
41
|
+
referral: Referral | null;
|
|
42
|
+
/** Why qualification was refused, when it was. */
|
|
43
|
+
reason?: QualificationRefusalReason;
|
|
44
|
+
}
|
|
45
|
+
/** Input for {@link ReferralQualificationService.requalify}. */
|
|
46
|
+
export interface RequalifyReferralInput {
|
|
47
|
+
referralId: string;
|
|
48
|
+
/** Non-empty rationale recorded in the referral metadata. Required. */
|
|
49
|
+
reason: string;
|
|
50
|
+
/** Clock override for deterministic tests. */
|
|
51
|
+
now?: Date;
|
|
52
|
+
}
|
|
53
|
+
/** Result of {@link ReferralQualificationService.requalify}. */
|
|
54
|
+
export interface RequalifyReferralResult {
|
|
55
|
+
/** Whether a new snapshot now governs the referral. */
|
|
56
|
+
requalified: boolean;
|
|
57
|
+
/** The NEW governing snapshot (`null` on refusal). */
|
|
58
|
+
snapshot: ReferralTermSnapshot | null;
|
|
59
|
+
/** The previously governing snapshot's id (kept as history). */
|
|
60
|
+
previousSnapshotId: string;
|
|
61
|
+
/** Why requalification was refused, when it was. */
|
|
62
|
+
reason?: 'no_active_agreement' | 'no_active_plan';
|
|
63
|
+
}
|
|
64
|
+
export declare class ReferralQualificationService {
|
|
65
|
+
private readonly deps;
|
|
66
|
+
constructor(deps: ReferralQualificationServiceDeps);
|
|
67
|
+
static create(classOptions?: SmrtClassOptions): Promise<ReferralQualificationService>;
|
|
68
|
+
/**
|
|
69
|
+
* Qualify an attributed referral.
|
|
70
|
+
*
|
|
71
|
+
* Flow:
|
|
72
|
+
* 1. **Status** — the referral must be `attributed`
|
|
73
|
+
* (`reason: 'not_attributed'` otherwise). Already `qualified` →
|
|
74
|
+
* IDEMPOTENT: the existing snapshot returns with `created: false`.
|
|
75
|
+
* 2. **Eligibility re-check** — the attribution-time policy version's
|
|
76
|
+
* flags are re-applied against the caller's current knowledge:
|
|
77
|
+
* `isExistingClient` (`existing_client_ineligible`) and
|
|
78
|
+
* `subjectProfileId` vs the referrer's profile
|
|
79
|
+
* (`self_referral_ineligible`). Refusals do NOT change the referral's
|
|
80
|
+
* status — disqualification stays an explicit caller decision.
|
|
81
|
+
* 3. **Agreement** — the ACTIVE ReferralAgreement for
|
|
82
|
+
* (referrerId, programId) effective at `now`
|
|
83
|
+
* (`reason: 'no_active_agreement'` when none).
|
|
84
|
+
* 4. **Plan** — the agreement's pinned `commissionPlanVersion`, or the
|
|
85
|
+
* latest active version when the pin is `0`; the resolved version must
|
|
86
|
+
* be ACTIVE (`reason: 'no_active_plan'` otherwise).
|
|
87
|
+
* 5. **Snapshot** — mint the immutable ReferralTermSnapshot (frozen
|
|
88
|
+
* component copy, currency, clearingDays, approvalMode, agreement +
|
|
89
|
+
* plan + policy version refs), point `referral.snapshotId` at it, and
|
|
90
|
+
* transition `attributed → qualified` (stamping `qualifiedAt`).
|
|
91
|
+
*/
|
|
92
|
+
qualify(input: QualifyReferralInput): Promise<QualifyReferralResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Explicitly re-snapshot a QUALIFIED referral under the CURRENT terms —
|
|
95
|
+
* the "amendment explicitly applied" path: after a plan/agreement
|
|
96
|
+
* amendment, earnings keep flowing through the OLD snapshot until an
|
|
97
|
+
* operator invokes this.
|
|
98
|
+
*
|
|
99
|
+
* Creates a NEW snapshot from the currently active agreement/plan,
|
|
100
|
+
* repoints `referral.snapshotId`, leaves the old snapshot row untouched
|
|
101
|
+
* (history — commissions it produced still reference it), and appends
|
|
102
|
+
* `{ at, reason, fromSnapshotId, toSnapshotId }` to the referral
|
|
103
|
+
* metadata's `requalifications` array.
|
|
104
|
+
*
|
|
105
|
+
* Throws on misuse (unknown referral, non-qualified status, empty
|
|
106
|
+
* reason); refuses with a typed reason when no active agreement/plan
|
|
107
|
+
* currently governs.
|
|
108
|
+
*/
|
|
109
|
+
requalify(input: RequalifyReferralInput): Promise<RequalifyReferralResult>;
|
|
110
|
+
private requireReferral;
|
|
111
|
+
/**
|
|
112
|
+
* The exact policy version pinned on the referral, when it exists —
|
|
113
|
+
* resolved in the REFERRAL's tenant lane (global fallback): background
|
|
114
|
+
* qualification runs without ambient tenant context, and another
|
|
115
|
+
* tenant's same-keyed policy must never supply the eligibility flags.
|
|
116
|
+
*/
|
|
117
|
+
private findPolicyVersion;
|
|
118
|
+
/**
|
|
119
|
+
* The CommissionPlan an agreement binds: the pinned version when
|
|
120
|
+
* `commissionPlanVersion > 0`, else the latest active version. Either
|
|
121
|
+
* way the resolved row must be ACTIVE — superseded/retired terms cannot
|
|
122
|
+
* govern a NEW qualification (an amendment should be activated, or the
|
|
123
|
+
* agreement re-pinned, first).
|
|
124
|
+
*/
|
|
125
|
+
private resolvePlan;
|
|
126
|
+
/**
|
|
127
|
+
* Mint the immutable snapshot: agreement/plan/policy refs plus the FROZEN
|
|
128
|
+
* component copy and the calculation inputs. `planVersion` records the
|
|
129
|
+
* RESOLVED version — this is where an unpinned (`0`) agreement version
|
|
130
|
+
* gets pinned.
|
|
131
|
+
*/
|
|
132
|
+
private mintSnapshot;
|
|
133
|
+
}
|
|
134
|
+
export default ReferralQualificationService;
|
|
135
|
+
//# sourceMappingURL=ReferralQualificationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReferralQualificationService.d.ts","sourceRoot":"","sources":["../../../src/referrals/services/ReferralQualificationService.ts"],"names":[],"mappings":"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"}
|