@happyvertical/smrt-sales 0.57.22 → 0.57.24
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 +8 -0
- package/README.md +34 -0
- package/agents/agreements.md +32 -0
- package/agents/commissions.md +2 -1
- package/agents/crm.md +150 -0
- package/agents/svelte.md +7 -0
- package/dist/agreements/collections/AgreementCollection.d.ts +3 -2
- package/dist/agreements/collections/AgreementCollection.d.ts.map +1 -1
- package/dist/agreements/models/Agreement.d.ts.map +1 -1
- package/dist/agreements/services/AgreementExecutionService.d.ts +9 -0
- package/dist/agreements/services/AgreementExecutionService.d.ts.map +1 -1
- package/dist/agreements/types.d.ts +2 -0
- package/dist/agreements/types.d.ts.map +1 -1
- package/dist/agreements.js +2 -2
- package/dist/chunks/__smrt-register__-Yzz7_2xD.js +6 -0
- package/dist/chunks/__smrt-register__-Yzz7_2xD.js.map +1 -0
- package/dist/chunks/{agreements-jBiVmttD.js → agreements-RsyRLeDn.js} +35 -16
- package/dist/chunks/agreements-RsyRLeDn.js.map +1 -0
- package/dist/chunks/{commissions-D5x24d4o.js → commissions-nMBMa0DT.js} +410 -344
- package/dist/chunks/commissions-nMBMa0DT.js.map +1 -0
- package/dist/chunks/{crm-BRICSwf2.js → crm-BENK-hHd.js} +793 -123
- package/dist/chunks/crm-BENK-hHd.js.map +1 -0
- package/dist/chunks/{referrals-BRdTkEFt.js → referrals-BPEaXqCz.js} +2 -2
- package/dist/chunks/{referrals-BRdTkEFt.js.map → referrals-BPEaXqCz.js.map} +1 -1
- package/dist/commissions/collections/CommissionPayoutCollection.d.ts +3 -1
- package/dist/commissions/collections/CommissionPayoutCollection.d.ts.map +1 -1
- package/dist/commissions/models/CommissionPayout.d.ts +15 -1
- package/dist/commissions/models/CommissionPayout.d.ts.map +1 -1
- package/dist/commissions/services/CommissionPayoutService.d.ts.map +1 -1
- package/dist/commissions/types.d.ts +2 -0
- package/dist/commissions/types.d.ts.map +1 -1
- package/dist/commissions.js +2 -2
- package/dist/crm/collections/SalesGovernanceEventCollection.d.ts +7 -0
- package/dist/crm/collections/SalesGovernanceEventCollection.d.ts.map +1 -0
- package/dist/crm/collections/SalesGovernanceStateCollection.d.ts +13 -0
- package/dist/crm/collections/SalesGovernanceStateCollection.d.ts.map +1 -0
- package/dist/crm/index.d.ts +6 -0
- package/dist/crm/index.d.ts.map +1 -1
- package/dist/crm/models/SalesGovernanceEvent.d.ts +19 -0
- package/dist/crm/models/SalesGovernanceEvent.d.ts.map +1 -0
- package/dist/crm/models/SalesGovernanceState.d.ts +17 -0
- package/dist/crm/models/SalesGovernanceState.d.ts.map +1 -0
- package/dist/crm/services/SalesGovernanceService.d.ts +68 -0
- package/dist/crm/services/SalesGovernanceService.d.ts.map +1 -0
- package/dist/crm/services/governance-write.d.ts +4 -0
- package/dist/crm/services/governance-write.d.ts.map +1 -0
- package/dist/crm/services/sales-governance-types.d.ts +108 -0
- package/dist/crm/services/sales-governance-types.d.ts.map +1 -0
- package/dist/crm.js +3 -3
- package/dist/index.js +6 -6
- package/dist/manifest.json +592 -5
- package/dist/referrals.js +2 -2
- package/dist/smrt-knowledge.json +320 -14
- package/dist/svelte/components/SellerGovernancePanel.svelte +65 -0
- package/dist/svelte/components/SellerGovernancePanel.svelte.d.ts +13 -0
- package/dist/svelte/components/SellerGovernancePanel.svelte.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +2 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/types.d.ts +22 -1
- package/dist/svelte/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/dist/chunks/__smrt-register__-bn1C2AUF.js +0 -6
- package/dist/chunks/__smrt-register__-bn1C2AUF.js.map +0 -1
- package/dist/chunks/agreements-jBiVmttD.js.map +0 -1
- package/dist/chunks/commissions-D5x24d4o.js.map +0 -1
- package/dist/chunks/crm-BRICSwf2.js.map +0 -1
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { SmrtCollection, SmrtObject, SmrtRecipe, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
2
|
-
import { TenantContextError, TenantScoped, getTenantId, requireTenantId, tenantId } from "@happyvertical/smrt-tenancy";
|
|
2
|
+
import { TenantContextError, TenantScoped, getTenantId, requireTenant, requireTenantId, tenantId, withTenant } from "@happyvertical/smrt-tenancy";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
import { getDatabase } from "@happyvertical/sql";
|
|
4
6
|
//#region src/crm/models/Lead.ts
|
|
5
|
-
var __defProp$
|
|
6
|
-
var __getOwnPropDesc$
|
|
7
|
-
var __defNormalProp$
|
|
7
|
+
var __defProp$8 = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, {
|
|
8
10
|
enumerable: true,
|
|
9
11
|
configurable: true,
|
|
10
12
|
writable: true,
|
|
11
13
|
value
|
|
12
14
|
}) : obj[key] = value;
|
|
13
|
-
var __decorateClass$
|
|
14
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
15
|
+
var __decorateClass$8 = (decorators, target, key, kind) => {
|
|
16
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$8(target, key) : target;
|
|
15
17
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
16
|
-
if (kind && result) __defProp$
|
|
18
|
+
if (kind && result) __defProp$8(target, key, result);
|
|
17
19
|
return result;
|
|
18
20
|
};
|
|
19
|
-
var __publicField$
|
|
21
|
+
var __publicField$8 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
20
22
|
var LEAD_STATUS_TRANSITIONS = {
|
|
21
23
|
new: [
|
|
22
24
|
"working",
|
|
@@ -182,21 +184,21 @@ var Lead = class extends SmrtObject {
|
|
|
182
184
|
return loadedLeadStatus.get(this);
|
|
183
185
|
}
|
|
184
186
|
};
|
|
185
|
-
__publicField$
|
|
186
|
-
__decorateClass$
|
|
187
|
-
__decorateClass$
|
|
187
|
+
__publicField$8(Lead, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
188
|
+
__decorateClass$8([tenantId({ nullable: true })], Lead.prototype, "tenantId", 2);
|
|
189
|
+
__decorateClass$8([field({
|
|
188
190
|
required: true,
|
|
189
191
|
description: "What you call this lead."
|
|
190
192
|
})], Lead.prototype, "name", 2);
|
|
191
|
-
__decorateClass$
|
|
192
|
-
__decorateClass$
|
|
193
|
-
__decorateClass$
|
|
194
|
-
__decorateClass$
|
|
195
|
-
__decorateClass$
|
|
196
|
-
__decorateClass$
|
|
197
|
-
__decorateClass$
|
|
198
|
-
__decorateClass$
|
|
199
|
-
Lead = __decorateClass$
|
|
193
|
+
__decorateClass$8([field({ description: "The person to talk to." })], Lead.prototype, "contactName", 2);
|
|
194
|
+
__decorateClass$8([field({ description: "Their email address." })], Lead.prototype, "email", 2);
|
|
195
|
+
__decorateClass$8([field({ description: "Their phone number." })], Lead.prototype, "phone", 2);
|
|
196
|
+
__decorateClass$8([field({ description: "The company they are with." })], Lead.prototype, "organizationName", 2);
|
|
197
|
+
__decorateClass$8([crossPackageRef("@happyvertical/smrt-profiles:Profile")], Lead.prototype, "profileId", 2);
|
|
198
|
+
__decorateClass$8([field({ description: "The sales representative who owns this lead." }), foreignKey("SalesRepresentative")], Lead.prototype, "ownerRepId", 2);
|
|
199
|
+
__decorateClass$8([field({ description: "Where the lead stands, for example new or qualified." })], Lead.prototype, "status", 2);
|
|
200
|
+
__decorateClass$8([foreignKey("Lead")], Lead.prototype, "mergedIntoId", 2);
|
|
201
|
+
Lead = __decorateClass$8([TenantScoped({ mode: "optional" }), smrt({
|
|
200
202
|
api: { include: [
|
|
201
203
|
"list",
|
|
202
204
|
"get",
|
|
@@ -327,21 +329,21 @@ function emptyLeadInboxStatusCounts() {
|
|
|
327
329
|
}
|
|
328
330
|
//#endregion
|
|
329
331
|
//#region src/crm/models/Opportunity.ts
|
|
330
|
-
var __defProp$
|
|
331
|
-
var __getOwnPropDesc$
|
|
332
|
-
var __defNormalProp$
|
|
332
|
+
var __defProp$7 = Object.defineProperty;
|
|
333
|
+
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
334
|
+
var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, {
|
|
333
335
|
enumerable: true,
|
|
334
336
|
configurable: true,
|
|
335
337
|
writable: true,
|
|
336
338
|
value
|
|
337
339
|
}) : obj[key] = value;
|
|
338
|
-
var __decorateClass$
|
|
339
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
340
|
+
var __decorateClass$7 = (decorators, target, key, kind) => {
|
|
341
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
|
|
340
342
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
341
|
-
if (kind && result) __defProp$
|
|
343
|
+
if (kind && result) __defProp$7(target, key, result);
|
|
342
344
|
return result;
|
|
343
345
|
};
|
|
344
|
-
var __publicField$
|
|
346
|
+
var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
345
347
|
var OPPORTUNITY_STATUS_TRANSITIONS = {
|
|
346
348
|
open: ["won", "lost"],
|
|
347
349
|
won: [],
|
|
@@ -469,22 +471,22 @@ var Opportunity = class extends SmrtObject {
|
|
|
469
471
|
return loadedOpportunityStatus.get(this);
|
|
470
472
|
}
|
|
471
473
|
};
|
|
472
|
-
__publicField$
|
|
473
|
-
__decorateClass$
|
|
474
|
-
__decorateClass$
|
|
474
|
+
__publicField$7(Opportunity, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
475
|
+
__decorateClass$7([tenantId({ nullable: true })], Opportunity.prototype, "tenantId", 2);
|
|
476
|
+
__decorateClass$7([field({
|
|
475
477
|
required: true,
|
|
476
478
|
description: "What you call this deal."
|
|
477
479
|
})], Opportunity.prototype, "name", 2);
|
|
478
|
-
__decorateClass$
|
|
479
|
-
__decorateClass$
|
|
480
|
-
__decorateClass$
|
|
481
|
-
__decorateClass$
|
|
482
|
-
__decorateClass$
|
|
483
|
-
__decorateClass$
|
|
484
|
-
__decorateClass$
|
|
485
|
-
__decorateClass$
|
|
486
|
-
__decorateClass$
|
|
487
|
-
Opportunity = __decorateClass$
|
|
480
|
+
__decorateClass$7([field({ description: "The lead it came from." }), foreignKey("Lead")], Opportunity.prototype, "leadId", 2);
|
|
481
|
+
__decorateClass$7([field({ description: "The sales representative who owns this deal." }), foreignKey("SalesRepresentative")], Opportunity.prototype, "ownerRepId", 2);
|
|
482
|
+
__decorateClass$7([field({ description: "The pipeline it moves through." }), foreignKey("PipelineDefinition")], Opportunity.prototype, "pipelineId", 2);
|
|
483
|
+
__decorateClass$7([field({ description: "The stage it is at now." }), foreignKey("PipelineStage")], Opportunity.prototype, "stageId", 2);
|
|
484
|
+
__decorateClass$7([field({ description: "What you expect the deal to be worth, in cents." })], Opportunity.prototype, "expectedValueCents", 2);
|
|
485
|
+
__decorateClass$7([field({ description: "The currency of that value." })], Opportunity.prototype, "currency", 2);
|
|
486
|
+
__decorateClass$7([field({ description: "How likely it is to close, as a decimal (0.5 is 50%)." })], Opportunity.prototype, "probability", 2);
|
|
487
|
+
__decorateClass$7([field({ description: "When you expect it to close." })], Opportunity.prototype, "expectedCloseAt", 2);
|
|
488
|
+
__decorateClass$7([field({ description: "open, won or lost." })], Opportunity.prototype, "status", 2);
|
|
489
|
+
Opportunity = __decorateClass$7([TenantScoped({ mode: "optional" }), smrt({
|
|
488
490
|
api: { include: [
|
|
489
491
|
"list",
|
|
490
492
|
"get",
|
|
@@ -496,21 +498,21 @@ Opportunity = __decorateClass$5([TenantScoped({ mode: "optional" }), smrt({
|
|
|
496
498
|
})], Opportunity);
|
|
497
499
|
//#endregion
|
|
498
500
|
//#region src/crm/models/PipelineStage.ts
|
|
499
|
-
var __defProp$
|
|
500
|
-
var __getOwnPropDesc$
|
|
501
|
-
var __defNormalProp$
|
|
501
|
+
var __defProp$6 = Object.defineProperty;
|
|
502
|
+
var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
|
|
503
|
+
var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, {
|
|
502
504
|
enumerable: true,
|
|
503
505
|
configurable: true,
|
|
504
506
|
writable: true,
|
|
505
507
|
value
|
|
506
508
|
}) : obj[key] = value;
|
|
507
|
-
var __decorateClass$
|
|
508
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
509
|
+
var __decorateClass$6 = (decorators, target, key, kind) => {
|
|
510
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
|
|
509
511
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
510
|
-
if (kind && result) __defProp$
|
|
512
|
+
if (kind && result) __defProp$6(target, key, result);
|
|
511
513
|
return result;
|
|
512
514
|
};
|
|
513
|
-
var __publicField$
|
|
515
|
+
var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
514
516
|
var PipelineStage = class extends SmrtObject {
|
|
515
517
|
tenantId = null;
|
|
516
518
|
pipelineId = "";
|
|
@@ -556,19 +558,19 @@ var PipelineStage = class extends SmrtObject {
|
|
|
556
558
|
this.metadata = JSON.stringify(metadata);
|
|
557
559
|
}
|
|
558
560
|
};
|
|
559
|
-
__publicField$
|
|
560
|
-
__decorateClass$
|
|
561
|
-
__decorateClass$
|
|
562
|
-
__decorateClass$
|
|
561
|
+
__publicField$6(PipelineStage, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
562
|
+
__decorateClass$6([tenantId({ nullable: true })], PipelineStage.prototype, "tenantId", 2);
|
|
563
|
+
__decorateClass$6([field({ description: "The pipeline this stage belongs to." }), foreignKey("PipelineDefinition", { required: true })], PipelineStage.prototype, "pipelineId", 2);
|
|
564
|
+
__decorateClass$6([field({
|
|
563
565
|
required: true,
|
|
564
566
|
description: "A short code for the stage."
|
|
565
567
|
})], PipelineStage.prototype, "key", 2);
|
|
566
|
-
__decorateClass$
|
|
567
|
-
__decorateClass$
|
|
568
|
-
__decorateClass$
|
|
569
|
-
__decorateClass$
|
|
570
|
-
__decorateClass$
|
|
571
|
-
PipelineStage = __decorateClass$
|
|
568
|
+
__decorateClass$6([field({ description: "What the stage is called." })], PipelineStage.prototype, "name", 2);
|
|
569
|
+
__decorateClass$6([field({ description: "Where it comes in the pipeline." })], PipelineStage.prototype, "sortOrder", 2);
|
|
570
|
+
__decorateClass$6([field({ description: "How likely a deal at this stage is to close, as a decimal." })], PipelineStage.prototype, "probability", 2);
|
|
571
|
+
__decorateClass$6([field({ description: "Whether reaching this stage closes the deal as won." })], PipelineStage.prototype, "isWon", 2);
|
|
572
|
+
__decorateClass$6([field({ description: "Whether reaching this stage closes the deal as lost." })], PipelineStage.prototype, "isLost", 2);
|
|
573
|
+
PipelineStage = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({
|
|
572
574
|
api: { include: [
|
|
573
575
|
"list",
|
|
574
576
|
"get",
|
|
@@ -614,21 +616,21 @@ var PipelineStageCollection = class extends SmrtCollection {
|
|
|
614
616
|
};
|
|
615
617
|
//#endregion
|
|
616
618
|
//#region src/crm/models/SalesActivity.ts
|
|
617
|
-
var __defProp$
|
|
618
|
-
var __getOwnPropDesc$
|
|
619
|
-
var __defNormalProp$
|
|
619
|
+
var __defProp$5 = Object.defineProperty;
|
|
620
|
+
var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
|
|
621
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, {
|
|
620
622
|
enumerable: true,
|
|
621
623
|
configurable: true,
|
|
622
624
|
writable: true,
|
|
623
625
|
value
|
|
624
626
|
}) : obj[key] = value;
|
|
625
|
-
var __decorateClass$
|
|
626
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
627
|
+
var __decorateClass$5 = (decorators, target, key, kind) => {
|
|
628
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
|
|
627
629
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
628
|
-
if (kind && result) __defProp$
|
|
630
|
+
if (kind && result) __defProp$5(target, key, result);
|
|
629
631
|
return result;
|
|
630
632
|
};
|
|
631
|
-
var __publicField$
|
|
633
|
+
var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
632
634
|
var workflowCompletionPermits = /* @__PURE__ */ new WeakSet();
|
|
633
635
|
function permitSalesActivityWorkflowCompletion(activity) {
|
|
634
636
|
workflowCompletionPermits.add(activity);
|
|
@@ -713,19 +715,19 @@ var SalesActivity = class extends SmrtObject {
|
|
|
713
715
|
}
|
|
714
716
|
}
|
|
715
717
|
};
|
|
716
|
-
__publicField$
|
|
717
|
-
__decorateClass$
|
|
718
|
-
__decorateClass$
|
|
719
|
-
__decorateClass$
|
|
718
|
+
__publicField$5(SalesActivity, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
719
|
+
__decorateClass$5([tenantId({ nullable: true })], SalesActivity.prototype, "tenantId", 2);
|
|
720
|
+
__decorateClass$5([field({ description: "What the activity is about: a lead or an opportunity." })], SalesActivity.prototype, "subjectKind", 2);
|
|
721
|
+
__decorateClass$5([field({
|
|
720
722
|
required: true,
|
|
721
723
|
description: "The lead or opportunity it is about."
|
|
722
724
|
})], SalesActivity.prototype, "subjectId", 2);
|
|
723
|
-
__decorateClass$
|
|
724
|
-
__decorateClass$
|
|
725
|
-
__decorateClass$
|
|
726
|
-
__decorateClass$
|
|
727
|
-
__decorateClass$
|
|
728
|
-
SalesActivity = __decorateClass$
|
|
725
|
+
__decorateClass$5([field({ description: "The kind of activity, for example call, note or email." })], SalesActivity.prototype, "activityKind", 2);
|
|
726
|
+
__decorateClass$5([field({ description: "What happened or what needs doing." })], SalesActivity.prototype, "summary", 2);
|
|
727
|
+
__decorateClass$5([field({ description: "When it is due." })], SalesActivity.prototype, "dueAt", 2);
|
|
728
|
+
__decorateClass$5([field({ description: "When it was done." })], SalesActivity.prototype, "completedAt", 2);
|
|
729
|
+
__decorateClass$5([crossPackageRef("@happyvertical/smrt-profiles:Profile")], SalesActivity.prototype, "actorProfileId", 2);
|
|
730
|
+
SalesActivity = __decorateClass$5([TenantScoped({ mode: "optional" }), smrt({
|
|
729
731
|
api: { include: [
|
|
730
732
|
"create",
|
|
731
733
|
"list",
|
|
@@ -887,21 +889,21 @@ var OpportunityCollection = class extends SmrtCollection {
|
|
|
887
889
|
};
|
|
888
890
|
//#endregion
|
|
889
891
|
//#region src/crm/models/PipelineDefinition.ts
|
|
890
|
-
var __defProp$
|
|
891
|
-
var __getOwnPropDesc$
|
|
892
|
-
var __defNormalProp$
|
|
892
|
+
var __defProp$4 = Object.defineProperty;
|
|
893
|
+
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
894
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, {
|
|
893
895
|
enumerable: true,
|
|
894
896
|
configurable: true,
|
|
895
897
|
writable: true,
|
|
896
898
|
value
|
|
897
899
|
}) : obj[key] = value;
|
|
898
|
-
var __decorateClass$
|
|
899
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
900
|
+
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
901
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
|
|
900
902
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
901
|
-
if (kind && result) __defProp$
|
|
903
|
+
if (kind && result) __defProp$4(target, key, result);
|
|
902
904
|
return result;
|
|
903
905
|
};
|
|
904
|
-
var __publicField$
|
|
906
|
+
var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
905
907
|
var PipelineDefinition = class extends SmrtObject {
|
|
906
908
|
tenantId = null;
|
|
907
909
|
key = "";
|
|
@@ -937,17 +939,17 @@ var PipelineDefinition = class extends SmrtObject {
|
|
|
937
939
|
this.metadata = JSON.stringify(metadata);
|
|
938
940
|
}
|
|
939
941
|
};
|
|
940
|
-
__publicField$
|
|
941
|
-
__decorateClass$
|
|
942
|
-
__decorateClass$
|
|
942
|
+
__publicField$4(PipelineDefinition, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
943
|
+
__decorateClass$4([tenantId({ nullable: true })], PipelineDefinition.prototype, "tenantId", 2);
|
|
944
|
+
__decorateClass$4([field({
|
|
943
945
|
required: true,
|
|
944
946
|
description: "A short code for the pipeline."
|
|
945
947
|
})], PipelineDefinition.prototype, "key", 2);
|
|
946
|
-
__decorateClass$
|
|
947
|
-
__decorateClass$
|
|
948
|
-
__decorateClass$
|
|
949
|
-
__decorateClass$
|
|
950
|
-
PipelineDefinition = __decorateClass$
|
|
948
|
+
__decorateClass$4([field({ description: "What the pipeline is called." })], PipelineDefinition.prototype, "name", 2);
|
|
949
|
+
__decorateClass$4([field({ description: "Whether this is the default pipeline for new deals." })], PipelineDefinition.prototype, "isDefault", 2);
|
|
950
|
+
__decorateClass$4([field({ description: "Whether the pipeline is active (accepts new deals) or archived." })], PipelineDefinition.prototype, "status", 2);
|
|
951
|
+
__decorateClass$4([field({ description: "Extra details kept with the pipeline." })], PipelineDefinition.prototype, "metadata", 2);
|
|
952
|
+
PipelineDefinition = __decorateClass$4([TenantScoped({ mode: "optional" }), smrt({
|
|
951
953
|
api: { include: [
|
|
952
954
|
"list",
|
|
953
955
|
"get",
|
|
@@ -1061,21 +1063,21 @@ var PipelineDefinitionCollection = class extends SmrtCollection {
|
|
|
1061
1063
|
};
|
|
1062
1064
|
//#endregion
|
|
1063
1065
|
//#region src/crm/models/SalesRepresentative.ts
|
|
1064
|
-
var __defProp$
|
|
1065
|
-
var __getOwnPropDesc$
|
|
1066
|
-
var __defNormalProp$
|
|
1066
|
+
var __defProp$3 = Object.defineProperty;
|
|
1067
|
+
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
|
1068
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, {
|
|
1067
1069
|
enumerable: true,
|
|
1068
1070
|
configurable: true,
|
|
1069
1071
|
writable: true,
|
|
1070
1072
|
value
|
|
1071
1073
|
}) : obj[key] = value;
|
|
1072
|
-
var __decorateClass$
|
|
1073
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
1074
|
+
var __decorateClass$3 = (decorators, target, key, kind) => {
|
|
1075
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
|
1074
1076
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1075
|
-
if (kind && result) __defProp$
|
|
1077
|
+
if (kind && result) __defProp$3(target, key, result);
|
|
1076
1078
|
return result;
|
|
1077
1079
|
};
|
|
1078
|
-
var __publicField$
|
|
1080
|
+
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1079
1081
|
var SalesRepresentative = class extends SmrtObject {
|
|
1080
1082
|
tenantId = null;
|
|
1081
1083
|
profileId = "";
|
|
@@ -1117,11 +1119,11 @@ var SalesRepresentative = class extends SmrtObject {
|
|
|
1117
1119
|
this.metadata = JSON.stringify(metadata);
|
|
1118
1120
|
}
|
|
1119
1121
|
};
|
|
1120
|
-
__publicField$
|
|
1121
|
-
__decorateClass$
|
|
1122
|
-
__decorateClass$
|
|
1123
|
-
__decorateClass$
|
|
1124
|
-
SalesRepresentative = __decorateClass$
|
|
1122
|
+
__publicField$3(SalesRepresentative, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
1123
|
+
__decorateClass$3([tenantId({ nullable: true })], SalesRepresentative.prototype, "tenantId", 2);
|
|
1124
|
+
__decorateClass$3([crossPackageRef("@happyvertical/smrt-profiles:Profile")], SalesRepresentative.prototype, "profileId", 2);
|
|
1125
|
+
__decorateClass$3([foreignKey("Earner")], SalesRepresentative.prototype, "earnerId", 2);
|
|
1126
|
+
SalesRepresentative = __decorateClass$3([TenantScoped({ mode: "optional" }), smrt({
|
|
1125
1127
|
api: { include: [
|
|
1126
1128
|
"list",
|
|
1127
1129
|
"get",
|
|
@@ -1619,21 +1621,21 @@ function toInboxCount(value) {
|
|
|
1619
1621
|
}
|
|
1620
1622
|
//#endregion
|
|
1621
1623
|
//#region src/crm/models/OpportunityConversion.ts
|
|
1622
|
-
var __defProp = Object.defineProperty;
|
|
1623
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
1624
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
1624
|
+
var __defProp$2 = Object.defineProperty;
|
|
1625
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
1626
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
|
|
1625
1627
|
enumerable: true,
|
|
1626
1628
|
configurable: true,
|
|
1627
1629
|
writable: true,
|
|
1628
1630
|
value
|
|
1629
1631
|
}) : obj[key] = value;
|
|
1630
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
1631
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
1632
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
1633
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
1632
1634
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1633
|
-
if (kind && result) __defProp(target, key, result);
|
|
1635
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
1634
1636
|
return result;
|
|
1635
1637
|
};
|
|
1636
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1638
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1637
1639
|
var OpportunityConversion = class extends SmrtObject {
|
|
1638
1640
|
tenantId = null;
|
|
1639
1641
|
opportunityId = "";
|
|
@@ -1688,12 +1690,12 @@ var OpportunityConversion = class extends SmrtObject {
|
|
|
1688
1690
|
return await super.save();
|
|
1689
1691
|
}
|
|
1690
1692
|
};
|
|
1691
|
-
__publicField(OpportunityConversion, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
1692
|
-
__decorateClass([tenantId({ nullable: true })], OpportunityConversion.prototype, "tenantId", 2);
|
|
1693
|
-
__decorateClass([foreignKey("Opportunity", { required: true })], OpportunityConversion.prototype, "opportunityId", 2);
|
|
1694
|
-
__decorateClass([field({ required: true })], OpportunityConversion.prototype, "targetKind", 2);
|
|
1695
|
-
__decorateClass([field({ required: true })], OpportunityConversion.prototype, "targetId", 2);
|
|
1696
|
-
OpportunityConversion = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
|
|
1693
|
+
__publicField$2(OpportunityConversion, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
1694
|
+
__decorateClass$2([tenantId({ nullable: true })], OpportunityConversion.prototype, "tenantId", 2);
|
|
1695
|
+
__decorateClass$2([foreignKey("Opportunity", { required: true })], OpportunityConversion.prototype, "opportunityId", 2);
|
|
1696
|
+
__decorateClass$2([field({ required: true })], OpportunityConversion.prototype, "targetKind", 2);
|
|
1697
|
+
__decorateClass$2([field({ required: true })], OpportunityConversion.prototype, "targetId", 2);
|
|
1698
|
+
OpportunityConversion = __decorateClass$2([TenantScoped({ mode: "optional" }), smrt({
|
|
1697
1699
|
api: { include: [
|
|
1698
1700
|
"list",
|
|
1699
1701
|
"get",
|
|
@@ -1769,6 +1771,156 @@ var OpportunityConversionCollection = class extends SmrtCollection {
|
|
|
1769
1771
|
}
|
|
1770
1772
|
};
|
|
1771
1773
|
//#endregion
|
|
1774
|
+
//#region src/crm/services/governance-write.ts
|
|
1775
|
+
var writes = new AsyncLocalStorage();
|
|
1776
|
+
function withGovernanceWrite(db, fn) {
|
|
1777
|
+
return writes.run(db, fn);
|
|
1778
|
+
}
|
|
1779
|
+
function assertGovernanceWrite(db) {
|
|
1780
|
+
if (writes.getStore() !== db) throw new Error("Sales governance state is service-owned");
|
|
1781
|
+
}
|
|
1782
|
+
//#endregion
|
|
1783
|
+
//#region src/crm/models/SalesGovernanceEvent.ts
|
|
1784
|
+
var __defProp$1 = Object.defineProperty;
|
|
1785
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
1786
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
|
|
1787
|
+
enumerable: true,
|
|
1788
|
+
configurable: true,
|
|
1789
|
+
writable: true,
|
|
1790
|
+
value
|
|
1791
|
+
}) : obj[key] = value;
|
|
1792
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
1793
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
1794
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1795
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
1796
|
+
return result;
|
|
1797
|
+
};
|
|
1798
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1799
|
+
var snapshots = /* @__PURE__ */ new WeakMap();
|
|
1800
|
+
var SalesGovernanceEvent = class extends SmrtObject {
|
|
1801
|
+
tenantId = "";
|
|
1802
|
+
intent = "";
|
|
1803
|
+
result = "";
|
|
1804
|
+
constructor(options = {}) {
|
|
1805
|
+
super(options);
|
|
1806
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
1807
|
+
if (options.intent !== void 0) this.intent = options.intent;
|
|
1808
|
+
if (options.result !== void 0) this.result = options.result;
|
|
1809
|
+
}
|
|
1810
|
+
async initialize() {
|
|
1811
|
+
await super.initialize();
|
|
1812
|
+
if (await this.isSaved()) snapshots.set(this, this.snapshot());
|
|
1813
|
+
return this;
|
|
1814
|
+
}
|
|
1815
|
+
async save() {
|
|
1816
|
+
const previous = snapshots.get(this);
|
|
1817
|
+
if (previous !== void 0 && previous !== this.snapshot()) throw new Error("Sales governance events are immutable");
|
|
1818
|
+
assertGovernanceWrite(this.db);
|
|
1819
|
+
await super.save();
|
|
1820
|
+
snapshots.set(this, this.snapshot());
|
|
1821
|
+
return this;
|
|
1822
|
+
}
|
|
1823
|
+
async delete() {
|
|
1824
|
+
throw new Error("Sales governance events cannot be deleted");
|
|
1825
|
+
}
|
|
1826
|
+
snapshot() {
|
|
1827
|
+
return JSON.stringify([
|
|
1828
|
+
this.tenantId,
|
|
1829
|
+
this.intent,
|
|
1830
|
+
this.result
|
|
1831
|
+
]);
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
__publicField$1(SalesGovernanceEvent, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
1835
|
+
__decorateClass$1([tenantId()], SalesGovernanceEvent.prototype, "tenantId", 2);
|
|
1836
|
+
SalesGovernanceEvent = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
|
|
1837
|
+
api: false,
|
|
1838
|
+
mcp: false,
|
|
1839
|
+
cli: false
|
|
1840
|
+
})], SalesGovernanceEvent);
|
|
1841
|
+
//#endregion
|
|
1842
|
+
//#region src/crm/collections/SalesGovernanceEventCollection.ts
|
|
1843
|
+
var SalesGovernanceEventCollection = class extends SmrtCollection {
|
|
1844
|
+
static _itemClass = SalesGovernanceEvent;
|
|
1845
|
+
};
|
|
1846
|
+
//#endregion
|
|
1847
|
+
//#region src/crm/models/SalesGovernanceState.ts
|
|
1848
|
+
var __defProp = Object.defineProperty;
|
|
1849
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
1850
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
1851
|
+
enumerable: true,
|
|
1852
|
+
configurable: true,
|
|
1853
|
+
writable: true,
|
|
1854
|
+
value
|
|
1855
|
+
}) : obj[key] = value;
|
|
1856
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
1857
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
1858
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1859
|
+
if (kind && result) __defProp(target, key, result);
|
|
1860
|
+
return result;
|
|
1861
|
+
};
|
|
1862
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1863
|
+
var SalesGovernanceState = class extends SmrtObject {
|
|
1864
|
+
tenantId = "";
|
|
1865
|
+
revision = 0;
|
|
1866
|
+
payload = "{}";
|
|
1867
|
+
constructor(options = {}) {
|
|
1868
|
+
super(options);
|
|
1869
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
1870
|
+
if (options.revision !== void 0) this.revision = options.revision;
|
|
1871
|
+
if (options.payload !== void 0) this.payload = options.payload;
|
|
1872
|
+
}
|
|
1873
|
+
async save() {
|
|
1874
|
+
assertGovernanceWrite(this.db);
|
|
1875
|
+
return super.save();
|
|
1876
|
+
}
|
|
1877
|
+
async delete() {
|
|
1878
|
+
throw new Error("Sales governance state is service-owned");
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
__publicField(SalesGovernanceState, "__smrtPackage__", "@happyvertical/smrt-sales");
|
|
1882
|
+
__decorateClass([tenantId()], SalesGovernanceState.prototype, "tenantId", 2);
|
|
1883
|
+
SalesGovernanceState = __decorateClass([TenantScoped({ mode: "required" }), smrt({
|
|
1884
|
+
api: false,
|
|
1885
|
+
mcp: false,
|
|
1886
|
+
cli: false
|
|
1887
|
+
})], SalesGovernanceState);
|
|
1888
|
+
//#endregion
|
|
1889
|
+
//#region src/crm/collections/SalesGovernanceStateCollection.ts
|
|
1890
|
+
var SalesGovernanceStateCollection = class extends SmrtCollection {
|
|
1891
|
+
static _itemClass = SalesGovernanceState;
|
|
1892
|
+
/** Insert the shared resource fence without changing its current owner. */
|
|
1893
|
+
async ensure(id) {
|
|
1894
|
+
assertGovernanceWrite(this.db);
|
|
1895
|
+
await this.query(`INSERT INTO ${this.tableName} (id, slug, context, tenant_id, revision, payload)
|
|
1896
|
+
VALUES (?, ?, '', ?, 0, '{}') ON CONFLICT DO NOTHING`, [
|
|
1897
|
+
id,
|
|
1898
|
+
id,
|
|
1899
|
+
requireTenantId()
|
|
1900
|
+
], { allowRawOnTenantScoped: true });
|
|
1901
|
+
}
|
|
1902
|
+
/** Serialize a resource on pooled adapters and reload its committed state. */
|
|
1903
|
+
async lock(id) {
|
|
1904
|
+
await this.ensure(id);
|
|
1905
|
+
assertGovernanceWrite(this.db);
|
|
1906
|
+
if ((await this.query(`UPDATE ${this.tableName} SET revision = revision WHERE id = ? AND tenant_id = ? RETURNING id`, [id, requireTenantId()], { allowRawOnTenantScoped: true })).length !== 1) throw new Error("Sales governance resource unavailable");
|
|
1907
|
+
const state = await this.get({ id }, { cache: false });
|
|
1908
|
+
if (!state) throw new Error("Sales governance resource unavailable");
|
|
1909
|
+
return state;
|
|
1910
|
+
}
|
|
1911
|
+
/** CAS is the cross-process serialization point; losers must retry the operation. */
|
|
1912
|
+
async compareAndSwap(id, revision, payload) {
|
|
1913
|
+
assertGovernanceWrite(this.db);
|
|
1914
|
+
return (await this.query(`UPDATE ${this.tableName} SET revision = revision + 1, payload = ?
|
|
1915
|
+
WHERE id = ? AND tenant_id = ? AND revision = ? RETURNING id`, [
|
|
1916
|
+
payload,
|
|
1917
|
+
id,
|
|
1918
|
+
requireTenantId(),
|
|
1919
|
+
revision
|
|
1920
|
+
], { allowRawOnTenantScoped: true })).length === 1;
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
//#endregion
|
|
1772
1924
|
//#region src/crm/recipes.ts
|
|
1773
1925
|
var SalesPipelineRecipe = class extends SmrtRecipe {
|
|
1774
1926
|
static id = "sales.pipeline";
|
|
@@ -1882,8 +2034,8 @@ var LeadConversionService = class LeadConversionService {
|
|
|
1882
2034
|
async findForTarget(input) {
|
|
1883
2035
|
const tenantId = requireTenantId();
|
|
1884
2036
|
if (input.tenantId !== tenantId) throw new Error("Lead is unavailable in the active tenant");
|
|
1885
|
-
const targetKind = text(input.targetKind, "targetKind");
|
|
1886
|
-
const targetId = text(input.targetId, "targetId");
|
|
2037
|
+
const targetKind = text$1(input.targetKind, "targetKind");
|
|
2038
|
+
const targetId = text$1(input.targetId, "targetId");
|
|
1887
2039
|
const candidates = await this.activities.list({
|
|
1888
2040
|
where: {
|
|
1889
2041
|
tenantId,
|
|
@@ -1908,8 +2060,8 @@ var LeadConversionService = class LeadConversionService {
|
|
|
1908
2060
|
/** Exact retries preserve the original actor and receipt; changed intent fails. */
|
|
1909
2061
|
async recordForLead(input) {
|
|
1910
2062
|
if (!this.callerTransaction) throw new Error("Lead conversion requires a caller-owned transaction");
|
|
1911
|
-
const targetId = text(input.targetId, "targetId");
|
|
1912
|
-
const actorProfileId = text(input.actorProfileId, "actorProfileId");
|
|
2063
|
+
const targetId = text$1(input.targetId, "targetId");
|
|
2064
|
+
const actorProfileId = text$1(input.actorProfileId, "actorProfileId");
|
|
1913
2065
|
const metadata = jsonObject(input.metadata ?? {});
|
|
1914
2066
|
const existing = await this.getForLead(input);
|
|
1915
2067
|
if (existing) {
|
|
@@ -1943,8 +2095,8 @@ var LeadConversionService = class LeadConversionService {
|
|
|
1943
2095
|
async scope(input) {
|
|
1944
2096
|
const tenantId = requireTenantId();
|
|
1945
2097
|
if (input.tenantId !== tenantId) throw new Error("Lead is unavailable in the active tenant");
|
|
1946
|
-
const leadId = text(input.leadId, "leadId");
|
|
1947
|
-
const targetKind = text(input.targetKind, "targetKind");
|
|
2098
|
+
const leadId = text$1(input.leadId, "leadId");
|
|
2099
|
+
const targetKind = text$1(input.targetKind, "targetKind");
|
|
1948
2100
|
const lead = await this.leads.get({ id: leadId }, { cache: false });
|
|
1949
2101
|
if (!lead || lead.tenantId !== tenantId) throw new Error("Lead is unavailable in the active tenant");
|
|
1950
2102
|
return {
|
|
@@ -1954,7 +2106,7 @@ var LeadConversionService = class LeadConversionService {
|
|
|
1954
2106
|
};
|
|
1955
2107
|
}
|
|
1956
2108
|
};
|
|
1957
|
-
function text(value, name) {
|
|
2109
|
+
function text$1(value, name) {
|
|
1958
2110
|
if (typeof value !== "string" || !value.trim() || value !== value.trim()) throw new Error(`Lead conversion ${name} must be a nonempty trimmed string`);
|
|
1959
2111
|
return value;
|
|
1960
2112
|
}
|
|
@@ -2868,6 +3020,524 @@ function uuidFromHash(hash) {
|
|
|
2868
3020
|
return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
|
|
2869
3021
|
}
|
|
2870
3022
|
//#endregion
|
|
2871
|
-
|
|
3023
|
+
//#region src/crm/services/SalesGovernanceService.ts
|
|
3024
|
+
var SalesGovernanceError = class extends Error {
|
|
3025
|
+
constructor(reason) {
|
|
3026
|
+
super(`Sales governance refused: ${reason}`);
|
|
3027
|
+
this.reason = reason;
|
|
3028
|
+
this.name = "SalesGovernanceError";
|
|
3029
|
+
}
|
|
3030
|
+
reason;
|
|
3031
|
+
code = "SALES_GOVERNANCE_DENIED";
|
|
3032
|
+
};
|
|
3033
|
+
var ownedTransactions = /* @__PURE__ */ new WeakSet();
|
|
3034
|
+
var SalesGovernanceService = class SalesGovernanceService {
|
|
3035
|
+
constructor(database, backend, host, now) {
|
|
3036
|
+
this.host = host;
|
|
3037
|
+
this.now = now;
|
|
3038
|
+
this.#database = database;
|
|
3039
|
+
this.#backend = backend;
|
|
3040
|
+
}
|
|
3041
|
+
host;
|
|
3042
|
+
now;
|
|
3043
|
+
#database;
|
|
3044
|
+
#backend;
|
|
3045
|
+
/** Owned adapter for migrations and host reads; transaction writes use withTransaction. */
|
|
3046
|
+
get database() {
|
|
3047
|
+
return this.#database;
|
|
3048
|
+
}
|
|
3049
|
+
static async create(options) {
|
|
3050
|
+
const configuration = { ...options?.database };
|
|
3051
|
+
const backend = configuration.type;
|
|
3052
|
+
if (backend !== "postgres") deny("unsupported_backend");
|
|
3053
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
3054
|
+
"type",
|
|
3055
|
+
"url",
|
|
3056
|
+
"cache",
|
|
3057
|
+
"dbid",
|
|
3058
|
+
"max",
|
|
3059
|
+
"connectionTimeoutMillis",
|
|
3060
|
+
"idleTimeoutMillis"
|
|
3061
|
+
]);
|
|
3062
|
+
if (typeof configuration.url !== "string" || !configuration.url.trim() || Object.keys(configuration).some((field) => !allowed.has(field)) || configuration.cache !== void 0 && configuration.cache !== false) deny("invalid_database_configuration");
|
|
3063
|
+
if (!options.host) deny("host_required");
|
|
3064
|
+
const db = await getDatabase({
|
|
3065
|
+
...configuration,
|
|
3066
|
+
cache: false
|
|
3067
|
+
});
|
|
3068
|
+
return new SalesGovernanceService(db, backend, options.host, options.now ?? (() => /* @__PURE__ */ new Date()));
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Run host writes and any governance mutations on one positively identified
|
|
3072
|
+
* executor. A thrown callback rolls back all changes. Do not retain tx.
|
|
3073
|
+
*/
|
|
3074
|
+
async withTransaction(fn) {
|
|
3075
|
+
this.assertBackend();
|
|
3076
|
+
const db = this.database;
|
|
3077
|
+
if (typeof db.transaction !== "function") deny("transaction_unavailable");
|
|
3078
|
+
return db.transaction(async (tx) => {
|
|
3079
|
+
ownedTransactions.add(tx);
|
|
3080
|
+
try {
|
|
3081
|
+
return await fn(tx);
|
|
3082
|
+
} finally {
|
|
3083
|
+
ownedTransactions.delete(tx);
|
|
3084
|
+
}
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
/** First successful registration wins until expiresAtMs <= now, or release. */
|
|
3088
|
+
async acquireLease(input, transaction) {
|
|
3089
|
+
input = {
|
|
3090
|
+
...operationIdentity(input),
|
|
3091
|
+
sellerId: uuid(input.sellerId),
|
|
3092
|
+
advertiserId: uuid(input.advertiserId)
|
|
3093
|
+
};
|
|
3094
|
+
return this.operation("acquire", input, transaction, async (deps, db) => {
|
|
3095
|
+
const policy = await this.seller(input, input.sellerId, deps, db);
|
|
3096
|
+
await this.advertiser(input, input.advertiserId, db);
|
|
3097
|
+
const state = await deps.states.lock(key("lease", input.tenantId, input.advertiserId));
|
|
3098
|
+
const previous = leaseSnapshot(parse(state.payload));
|
|
3099
|
+
const now = this.time();
|
|
3100
|
+
if (previous && !previous.released && previous.expiresAtMs > now) deny("advertiser_already_registered");
|
|
3101
|
+
const lease = {
|
|
3102
|
+
tenantId: input.tenantId,
|
|
3103
|
+
advertiserId: input.advertiserId,
|
|
3104
|
+
sellerId: input.sellerId,
|
|
3105
|
+
leaseId: input.operationId,
|
|
3106
|
+
expiresAtMs: expiry(now, policy.leaseDurationMs),
|
|
3107
|
+
released: false
|
|
3108
|
+
};
|
|
3109
|
+
await this.store(deps, state, lease);
|
|
3110
|
+
return lease;
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
/** Renewal requires a live matching owner token; it starts a fresh policy duration. */
|
|
3114
|
+
async renewLease(input, transaction) {
|
|
3115
|
+
return this.changeLease("renew", input, transaction);
|
|
3116
|
+
}
|
|
3117
|
+
/** Release never transfers ownership and is audited even at the expiry boundary. */
|
|
3118
|
+
async releaseLease(input, transaction) {
|
|
3119
|
+
return this.changeLease("release", input, transaction);
|
|
3120
|
+
}
|
|
3121
|
+
async changeLease(kind, input, tx) {
|
|
3122
|
+
input = {
|
|
3123
|
+
...operationIdentity(input),
|
|
3124
|
+
sellerId: uuid(input.sellerId),
|
|
3125
|
+
advertiserId: uuid(input.advertiserId),
|
|
3126
|
+
leaseId: uuid(input.leaseId)
|
|
3127
|
+
};
|
|
3128
|
+
return this.operation(kind, input, tx, async (deps, db) => {
|
|
3129
|
+
const policy = await this.seller(input, input.sellerId, deps, db);
|
|
3130
|
+
await this.advertiser(input, input.advertiserId, db);
|
|
3131
|
+
const state = await deps.states.lock(key("lease", input.tenantId, input.advertiserId));
|
|
3132
|
+
const lease = leaseSnapshot(parse(state.payload));
|
|
3133
|
+
if (!lease || lease.leaseId !== input.leaseId || lease.sellerId !== input.sellerId || lease.released) deny("lease_owner_mismatch");
|
|
3134
|
+
if (kind === "renew") {
|
|
3135
|
+
if (lease.expiresAtMs <= this.time()) deny("lease_expired");
|
|
3136
|
+
lease.expiresAtMs = expiry(this.time(), policy.leaseDurationMs);
|
|
3137
|
+
} else lease.released = true;
|
|
3138
|
+
await this.store(deps, state, lease);
|
|
3139
|
+
return lease;
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
/** Freeze one immutable commercial revision. Amendments use a greater revision. */
|
|
3143
|
+
async createQuote(input, transaction) {
|
|
3144
|
+
input = {
|
|
3145
|
+
...operationIdentity(input),
|
|
3146
|
+
terms: quoteTermsIdentity(input.terms)
|
|
3147
|
+
};
|
|
3148
|
+
validateTerms(input.terms);
|
|
3149
|
+
return this.operation("quote", input, transaction, async (deps, db) => {
|
|
3150
|
+
const policy = await this.seller(input, input.terms.sellerId, deps, db);
|
|
3151
|
+
await this.advertiser(input, input.terms.advertiserId, db);
|
|
3152
|
+
await this.assertLiveLease(input.tenantId, input.terms.sellerId, input.terms.advertiserId, deps);
|
|
3153
|
+
if (await this.host.validateQuote({
|
|
3154
|
+
...input.terms,
|
|
3155
|
+
tenantId: input.tenantId
|
|
3156
|
+
}, db) !== true) deny("quote_unavailable");
|
|
3157
|
+
const head = await deps.states.lock(key("quote-head", input.tenantId, input.terms.quoteId));
|
|
3158
|
+
const previous = parse(head.payload);
|
|
3159
|
+
if (previous && previous.revision >= input.terms.revision) deny("quote_revision_not_new");
|
|
3160
|
+
const state = await deps.states.lock(quoteKey(input.tenantId, input.terms.quoteId, input.terms.revision));
|
|
3161
|
+
if (parse(state.payload)) deny("quote_revision_exists");
|
|
3162
|
+
const quote = {
|
|
3163
|
+
...input.terms,
|
|
3164
|
+
tenantId: input.tenantId,
|
|
3165
|
+
makerProfileId: input.actorProfileId,
|
|
3166
|
+
policy,
|
|
3167
|
+
status: "draft",
|
|
3168
|
+
approval: null
|
|
3169
|
+
};
|
|
3170
|
+
await this.store(deps, state, quote);
|
|
3171
|
+
await this.store(deps, head, { revision: quote.revision });
|
|
3172
|
+
return quote;
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
async requestApproval(input, transaction) {
|
|
3176
|
+
input = quoteOperationIdentity(input);
|
|
3177
|
+
return this.operation("request-approval", input, transaction, async (deps, db) => {
|
|
3178
|
+
const [state, quote] = await this.quote(input, deps);
|
|
3179
|
+
await this.validateSellerQuote(input, quote, deps, db);
|
|
3180
|
+
if (quote.status !== "draft" || quote.approval) deny("approval_already_requested");
|
|
3181
|
+
quote.approval = {
|
|
3182
|
+
requestId: input.operationId,
|
|
3183
|
+
status: "pending",
|
|
3184
|
+
termsHash: termsHash(quote),
|
|
3185
|
+
checkerProfileId: null,
|
|
3186
|
+
expiresAtMs: expiry(this.time(), quote.policy.approvalDurationMs),
|
|
3187
|
+
reason: ""
|
|
3188
|
+
};
|
|
3189
|
+
await this.store(deps, state, quote);
|
|
3190
|
+
return quote;
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
async decideApproval(input, transaction) {
|
|
3194
|
+
input = {
|
|
3195
|
+
...quoteOperationIdentity(input),
|
|
3196
|
+
requestId: uuid(input.requestId)
|
|
3197
|
+
};
|
|
3198
|
+
if (![
|
|
3199
|
+
"approved",
|
|
3200
|
+
"rejected",
|
|
3201
|
+
"revoked"
|
|
3202
|
+
].includes(input.decision) || !input.reason?.trim()) deny("invalid_decision");
|
|
3203
|
+
return this.operation("decide-approval", input, transaction, async (deps, db) => {
|
|
3204
|
+
const [state, quote] = await this.quote(input, deps);
|
|
3205
|
+
await this.context(input, db);
|
|
3206
|
+
await this.assertLiveLease(input.tenantId, quote.sellerId, quote.advertiserId, deps);
|
|
3207
|
+
if (uuid(input.actorProfileId) === uuid(quote.makerProfileId)) deny("self_approval");
|
|
3208
|
+
if (await this.host.canApprove(input, db) !== true) deny("finance_unauthorized");
|
|
3209
|
+
const approval = quote.approval;
|
|
3210
|
+
if (!approval || approval.requestId !== input.requestId || approval.termsHash !== termsHash(quote)) deny("approval_mismatch");
|
|
3211
|
+
if (input.decision === "revoked") {
|
|
3212
|
+
if (approval.status !== "approved") deny("approval_not_approved");
|
|
3213
|
+
} else {
|
|
3214
|
+
if (approval.status !== "pending" || quote.status !== "draft") deny("approval_not_pending");
|
|
3215
|
+
if (approval.expiresAtMs <= this.time()) deny("approval_expired");
|
|
3216
|
+
}
|
|
3217
|
+
approval.status = input.decision;
|
|
3218
|
+
approval.checkerProfileId = input.actorProfileId;
|
|
3219
|
+
approval.reason = input.reason.trim();
|
|
3220
|
+
await this.store(deps, state, quote);
|
|
3221
|
+
return quote;
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
async issueQuote(input, transaction) {
|
|
3225
|
+
return this.transition("issue", input, transaction);
|
|
3226
|
+
}
|
|
3227
|
+
/** Commit local sign authority before provider I/O; never send directly from a stale quote. */
|
|
3228
|
+
async signQuote(input, transaction) {
|
|
3229
|
+
return this.transition("sign", input, transaction);
|
|
3230
|
+
}
|
|
3231
|
+
async transition(kind, input, tx) {
|
|
3232
|
+
input = quoteOperationIdentity(input);
|
|
3233
|
+
return this.operation(kind, input, tx, async (deps, db) => {
|
|
3234
|
+
const [state, quote] = await this.quote(input, deps);
|
|
3235
|
+
await this.validateSellerQuote(input, quote, deps, db);
|
|
3236
|
+
if (kind === "issue" ? quote.status !== "draft" : quote.status !== "issued") deny("invalid_quote_transition");
|
|
3237
|
+
this.approved(quote);
|
|
3238
|
+
quote.status = kind === "issue" ? "issued" : "signed";
|
|
3239
|
+
await this.store(deps, state, quote);
|
|
3240
|
+
return quote;
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
/** Provider preflight, including retries of already-signed local records. */
|
|
3244
|
+
async assertCanSign(input, transaction) {
|
|
3245
|
+
input = {
|
|
3246
|
+
...contextIdentity(input),
|
|
3247
|
+
quoteId: uuid(input.quoteId)
|
|
3248
|
+
};
|
|
3249
|
+
this.assertContext(input);
|
|
3250
|
+
await this.run(transaction, async (deps, db) => {
|
|
3251
|
+
const [, quote] = await this.quote(input, deps);
|
|
3252
|
+
await this.validateSellerQuote(input, quote, deps, db);
|
|
3253
|
+
if (quote.status !== "signed") deny("quote_not_signed");
|
|
3254
|
+
this.approved(quote);
|
|
3255
|
+
});
|
|
3256
|
+
}
|
|
3257
|
+
/** Read canonical registration for an authorized current tenant member. */
|
|
3258
|
+
async getLease(input) {
|
|
3259
|
+
input = {
|
|
3260
|
+
...contextIdentity(input),
|
|
3261
|
+
advertiserId: uuid(input.advertiserId)
|
|
3262
|
+
};
|
|
3263
|
+
this.assertContext(input);
|
|
3264
|
+
return this.run(void 0, async (deps, db) => {
|
|
3265
|
+
await this.context(input, db);
|
|
3266
|
+
await this.advertiser(input, input.advertiserId, db);
|
|
3267
|
+
const state = await deps.states.get({ id: key("lease", input.tenantId, input.advertiserId) }, { cache: false });
|
|
3268
|
+
return state ? leaseSnapshot(parse(state.payload)) : null;
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
/** Tenant-scoped UI projection. Authority is always rechecked by mutation methods. */
|
|
3272
|
+
async getQuote(input) {
|
|
3273
|
+
input = {
|
|
3274
|
+
...input,
|
|
3275
|
+
tenantId: uuid(input.tenantId),
|
|
3276
|
+
quoteId: uuid(input.quoteId)
|
|
3277
|
+
};
|
|
3278
|
+
if (uuid(requireTenantId()) !== input.tenantId) deny("tenant_context_mismatch");
|
|
3279
|
+
return this.inCanonicalTenant(async () => {
|
|
3280
|
+
const state = await (await SalesGovernanceStateCollection.create({ db: this.database })).get({ id: quoteKey(input.tenantId, input.quoteId, input.revision) }, { cache: false });
|
|
3281
|
+
return state ? quoteSnapshot(parse(state.payload)) : null;
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3284
|
+
approved(quote) {
|
|
3285
|
+
if (BigInt(quote.subtotalCents - quote.totalCents) * 10000n <= BigInt(quote.subtotalCents) * BigInt(quote.policy.maxDiscountBps)) return;
|
|
3286
|
+
const approval = quote.approval;
|
|
3287
|
+
if (!approval || approval.status !== "approved" || approval.expiresAtMs <= this.time() || approval.termsHash !== termsHash(quote) || !approval.checkerProfileId || uuid(approval.checkerProfileId) === uuid(quote.makerProfileId)) deny("discount_approval_required");
|
|
3288
|
+
}
|
|
3289
|
+
async assertLiveLease(tenantId, sellerId, advertiserId, deps) {
|
|
3290
|
+
const lease = leaseSnapshot(parse((await deps.states.lock(key("lease", uuid(tenantId), uuid(advertiserId)))).payload));
|
|
3291
|
+
if (!lease || lease.tenantId !== uuid(tenantId) || lease.advertiserId !== uuid(advertiserId) || lease.sellerId !== uuid(sellerId) || lease.released || lease.expiresAtMs <= this.time()) deny("advertiser_lease_required");
|
|
3292
|
+
}
|
|
3293
|
+
async validateSellerQuote(input, quote, deps, db) {
|
|
3294
|
+
const policy = await this.seller(input, quote.sellerId, deps, db);
|
|
3295
|
+
if (JSON.stringify(policy) !== JSON.stringify(quote.policy)) deny("policy_changed");
|
|
3296
|
+
await this.advertiser(input, quote.advertiserId, db);
|
|
3297
|
+
await this.assertLiveLease(input.tenantId, quote.sellerId, quote.advertiserId, deps);
|
|
3298
|
+
if (await this.host.validateQuote(quote, db) !== true) deny("quote_unavailable");
|
|
3299
|
+
if (quote.approval?.status === "approved" && quote.approval.checkerProfileId) {
|
|
3300
|
+
const checker = {
|
|
3301
|
+
tenantId: input.tenantId,
|
|
3302
|
+
actorProfileId: quote.approval.checkerProfileId
|
|
3303
|
+
};
|
|
3304
|
+
await this.context(checker, db);
|
|
3305
|
+
if (await this.host.canApprove(checker, db) !== true) deny("finance_unauthorized");
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
async quote(input, deps) {
|
|
3309
|
+
text(input.quoteId);
|
|
3310
|
+
integer(input.revision, 1);
|
|
3311
|
+
if (parse((await deps.states.lock(key("quote-head", input.tenantId, input.quoteId))).payload)?.revision !== input.revision) deny("stale_quote_revision");
|
|
3312
|
+
const state = await deps.states.lock(quoteKey(input.tenantId, input.quoteId, input.revision));
|
|
3313
|
+
const quote = quoteSnapshot(parse(state.payload));
|
|
3314
|
+
if (!quote || quote.tenantId !== input.tenantId) deny("quote_unavailable");
|
|
3315
|
+
return [state, quote];
|
|
3316
|
+
}
|
|
3317
|
+
async seller(input, sellerId, deps, db) {
|
|
3318
|
+
await this.context(input, db);
|
|
3319
|
+
text(sellerId);
|
|
3320
|
+
if ((await deps.reps.query(`UPDATE ${deps.reps.tableName} SET status = status WHERE id = ? AND tenant_id = ? AND profile_id = ? AND status = 'active' RETURNING id`, [
|
|
3321
|
+
sellerId,
|
|
3322
|
+
input.tenantId,
|
|
3323
|
+
input.actorProfileId
|
|
3324
|
+
], { allowRawOnTenantScoped: true })).length !== 1) deny("seller_unauthorized");
|
|
3325
|
+
const context = {
|
|
3326
|
+
...input,
|
|
3327
|
+
sellerId
|
|
3328
|
+
};
|
|
3329
|
+
if (await this.host.canSell(context, db) !== true) deny("seller_policy_denied");
|
|
3330
|
+
const policy = await this.host.policy(context, db);
|
|
3331
|
+
if (!policy || typeof policy !== "object") deny("invalid_policy");
|
|
3332
|
+
text(policy.version);
|
|
3333
|
+
integer(policy.maxDiscountBps, 0, 1e4);
|
|
3334
|
+
integer(policy.leaseDurationMs, 1);
|
|
3335
|
+
integer(policy.approvalDurationMs, 1);
|
|
3336
|
+
return {
|
|
3337
|
+
version: policy.version,
|
|
3338
|
+
maxDiscountBps: policy.maxDiscountBps,
|
|
3339
|
+
leaseDurationMs: policy.leaseDurationMs,
|
|
3340
|
+
approvalDurationMs: policy.approvalDurationMs
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
async context(input, db) {
|
|
3344
|
+
this.assertBackend();
|
|
3345
|
+
this.assertContext(input);
|
|
3346
|
+
if (await this.host.isMember(input, db) !== true) deny("membership_required");
|
|
3347
|
+
if (await this.host.isUnsuspended(input, db) !== true) deny("suspended");
|
|
3348
|
+
}
|
|
3349
|
+
async advertiser(input, advertiserId, db) {
|
|
3350
|
+
text(advertiserId);
|
|
3351
|
+
if (await this.host.advertiserExists({
|
|
3352
|
+
...input,
|
|
3353
|
+
advertiserId
|
|
3354
|
+
}, db) !== true) deny("advertiser_unavailable");
|
|
3355
|
+
}
|
|
3356
|
+
assertContext(input) {
|
|
3357
|
+
text(input.tenantId);
|
|
3358
|
+
text(input.actorProfileId);
|
|
3359
|
+
if (uuid(requireTenantId()) !== input.tenantId) deny("tenant_context_mismatch");
|
|
3360
|
+
}
|
|
3361
|
+
time() {
|
|
3362
|
+
const now = this.now().getTime();
|
|
3363
|
+
integer(now, 0);
|
|
3364
|
+
return now;
|
|
3365
|
+
}
|
|
3366
|
+
async store(deps, state, value) {
|
|
3367
|
+
if (!await deps.states.compareAndSwap(state.id, state.revision, JSON.stringify(value))) deny("concurrent_change_retry");
|
|
3368
|
+
}
|
|
3369
|
+
async operation(kind, input, tx, fn) {
|
|
3370
|
+
this.assertBackend();
|
|
3371
|
+
this.assertContext(input);
|
|
3372
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(input.operationId)) deny("invalid_operation_id");
|
|
3373
|
+
const intent = canonical(normalizeIdentityEnvelope({
|
|
3374
|
+
kind,
|
|
3375
|
+
input
|
|
3376
|
+
}));
|
|
3377
|
+
return this.run(tx, async (deps, db) => {
|
|
3378
|
+
await deps.states.lock(key("operation", input.operationId));
|
|
3379
|
+
const previous = await deps.events.get({ id: input.operationId }, { cache: false });
|
|
3380
|
+
if (previous) {
|
|
3381
|
+
if (canonical(normalizeIdentityEnvelope(JSON.parse(previous.intent))) !== intent) deny("replay_conflict");
|
|
3382
|
+
return normalizeIdentityEnvelope(JSON.parse(previous.result));
|
|
3383
|
+
}
|
|
3384
|
+
const result = await fn(deps, db);
|
|
3385
|
+
await deps.events.create({
|
|
3386
|
+
id: input.operationId,
|
|
3387
|
+
slug: input.operationId,
|
|
3388
|
+
tenantId: input.tenantId,
|
|
3389
|
+
intent,
|
|
3390
|
+
result: JSON.stringify(result)
|
|
3391
|
+
});
|
|
3392
|
+
return result;
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
assertBackend() {
|
|
3396
|
+
if (this.#backend !== "postgres") deny("unsupported_backend");
|
|
3397
|
+
}
|
|
3398
|
+
async inCanonicalTenant(fn) {
|
|
3399
|
+
const current = requireTenant();
|
|
3400
|
+
return withTenant({
|
|
3401
|
+
...current,
|
|
3402
|
+
tenantId: uuid(current.tenantId)
|
|
3403
|
+
}, fn);
|
|
3404
|
+
}
|
|
3405
|
+
async run(tx, fn) {
|
|
3406
|
+
this.assertBackend();
|
|
3407
|
+
const execute = async (db2) => this.inCanonicalTenant(() => withGovernanceWrite(db2, async () => fn({
|
|
3408
|
+
states: await SalesGovernanceStateCollection.create({ db: db2 }),
|
|
3409
|
+
events: await SalesGovernanceEventCollection.create({ db: db2 }),
|
|
3410
|
+
reps: await SalesRepresentativeCollection.create({ db: db2 })
|
|
3411
|
+
}, db2)));
|
|
3412
|
+
if (tx) {
|
|
3413
|
+
if (!ownedTransactions.has(tx)) deny("transaction_executor_required");
|
|
3414
|
+
return execute(tx);
|
|
3415
|
+
}
|
|
3416
|
+
const db = this.database;
|
|
3417
|
+
if (typeof db.transaction !== "function") deny("transaction_unavailable");
|
|
3418
|
+
return db.transaction(execute);
|
|
3419
|
+
}
|
|
3420
|
+
};
|
|
3421
|
+
function deny(reason) {
|
|
3422
|
+
throw new SalesGovernanceError(reason);
|
|
3423
|
+
}
|
|
3424
|
+
function text(value) {
|
|
3425
|
+
if (typeof value !== "string" || !value.trim() || value.length > 256) deny("invalid_identity");
|
|
3426
|
+
}
|
|
3427
|
+
function integer(value, min, max = Number.MAX_SAFE_INTEGER) {
|
|
3428
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) deny("invalid_numeric_value");
|
|
3429
|
+
}
|
|
3430
|
+
function expiry(now, duration) {
|
|
3431
|
+
const value = now + duration;
|
|
3432
|
+
integer(value, 0);
|
|
3433
|
+
return value;
|
|
3434
|
+
}
|
|
3435
|
+
function parse(payload) {
|
|
3436
|
+
return payload === "{}" ? null : JSON.parse(payload);
|
|
3437
|
+
}
|
|
3438
|
+
function key(...parts) {
|
|
3439
|
+
const hash = createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
3440
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-5${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
|
|
3441
|
+
}
|
|
3442
|
+
function quoteKey(tenant, quote, revision) {
|
|
3443
|
+
return key("quote", tenant, quote, revision);
|
|
3444
|
+
}
|
|
3445
|
+
function validateTerms(terms) {
|
|
3446
|
+
if (!terms) deny("invalid_quote");
|
|
3447
|
+
for (const value of [
|
|
3448
|
+
terms.quoteId,
|
|
3449
|
+
terms.sellerId,
|
|
3450
|
+
terms.advertiserId,
|
|
3451
|
+
terms.rateCardVersion,
|
|
3452
|
+
terms.inventoryVersion
|
|
3453
|
+
]) text(value);
|
|
3454
|
+
integer(terms.revision, 1);
|
|
3455
|
+
integer(terms.subtotalCents, 0);
|
|
3456
|
+
integer(terms.totalCents, 0, terms.subtotalCents);
|
|
3457
|
+
if (!/^[A-Z]{3}$/.test(terms.currency)) deny("invalid_currency");
|
|
3458
|
+
}
|
|
3459
|
+
function termsHash(quote) {
|
|
3460
|
+
const { status: _status, approval: _approval, ...terms } = quote;
|
|
3461
|
+
return createHash("sha256").update(canonical(terms)).digest("hex");
|
|
3462
|
+
}
|
|
3463
|
+
function canonical(value) {
|
|
3464
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
3465
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
3466
|
+
return `{${Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${canonical(value[k])}`).join(",")}}`;
|
|
3467
|
+
}
|
|
3468
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
3469
|
+
function uuid(value) {
|
|
3470
|
+
if (typeof value !== "string" || !UUID.test(value)) deny("invalid_uuid_identity");
|
|
3471
|
+
return value.toLowerCase();
|
|
3472
|
+
}
|
|
3473
|
+
function contextIdentity(input) {
|
|
3474
|
+
return {
|
|
3475
|
+
...input,
|
|
3476
|
+
tenantId: uuid(input.tenantId),
|
|
3477
|
+
actorProfileId: uuid(input.actorProfileId)
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
function operationIdentity(input) {
|
|
3481
|
+
return {
|
|
3482
|
+
...contextIdentity(input),
|
|
3483
|
+
operationId: uuid(input.operationId)
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
function quoteOperationIdentity(input) {
|
|
3487
|
+
return {
|
|
3488
|
+
...operationIdentity(input),
|
|
3489
|
+
quoteId: uuid(input.quoteId)
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
function quoteTermsIdentity(terms) {
|
|
3493
|
+
if (!terms) deny("invalid_quote");
|
|
3494
|
+
return {
|
|
3495
|
+
...terms,
|
|
3496
|
+
quoteId: uuid(terms.quoteId),
|
|
3497
|
+
sellerId: uuid(terms.sellerId),
|
|
3498
|
+
advertiserId: uuid(terms.advertiserId)
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
function leaseSnapshot(lease) {
|
|
3502
|
+
return lease && {
|
|
3503
|
+
...lease,
|
|
3504
|
+
tenantId: uuid(lease.tenantId),
|
|
3505
|
+
advertiserId: uuid(lease.advertiserId),
|
|
3506
|
+
sellerId: uuid(lease.sellerId),
|
|
3507
|
+
leaseId: uuid(lease.leaseId)
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
function quoteSnapshot(quote) {
|
|
3511
|
+
return quote && {
|
|
3512
|
+
...quote,
|
|
3513
|
+
...quoteTermsIdentity(quote),
|
|
3514
|
+
tenantId: uuid(quote.tenantId),
|
|
3515
|
+
makerProfileId: uuid(quote.makerProfileId),
|
|
3516
|
+
approval: quote.approval && {
|
|
3517
|
+
...quote.approval,
|
|
3518
|
+
requestId: uuid(quote.approval.requestId),
|
|
3519
|
+
checkerProfileId: quote.approval.checkerProfileId === null ? null : uuid(quote.approval.checkerProfileId)
|
|
3520
|
+
}
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
var IDENTITY_FIELDS = /* @__PURE__ */ new Set([
|
|
3524
|
+
"tenantId",
|
|
3525
|
+
"actorProfileId",
|
|
3526
|
+
"sellerId",
|
|
3527
|
+
"advertiserId",
|
|
3528
|
+
"quoteId",
|
|
3529
|
+
"operationId",
|
|
3530
|
+
"leaseId",
|
|
3531
|
+
"requestId",
|
|
3532
|
+
"makerProfileId",
|
|
3533
|
+
"checkerProfileId"
|
|
3534
|
+
]);
|
|
3535
|
+
function normalizeIdentityEnvelope(value) {
|
|
3536
|
+
if (!value || typeof value !== "object") return value;
|
|
3537
|
+
if (Array.isArray(value)) return value.map(normalizeIdentityEnvelope);
|
|
3538
|
+
return Object.fromEntries(Object.entries(value).map(([field, item]) => [field, IDENTITY_FIELDS.has(field) && item !== null ? uuid(item) : normalizeIdentityEnvelope(item)]));
|
|
3539
|
+
}
|
|
3540
|
+
//#endregion
|
|
3541
|
+
export { OPPORTUNITY_STATUSES as A, SalesActivity as C, DEFAULT_PIPELINE_KEY as D, Opportunity as E, Lead as F, SALES_ACTIVITY_KINDS as M, SALES_ACTIVITY_SUBJECT_KINDS as N, DEFAULT_PIPELINE_STAGES as O, SALES_REPRESENTATIVE_STATUSES as P, SalesActivityCollection as S, PipelineStage as T, SalesRepresentativeCollection as _, LeadWorkflowValidationError as a, PipelineDefinition as b, LeadConversionService as c, SalesGovernanceState as d, SalesGovernanceEventCollection as f, LeadCollection as g, OpportunityConversion as h, LeadWorkflowService as i, PIPELINE_STATUSES as j, LEAD_STATUSES as k, SalesPipelineRecipe as l, OpportunityConversionCollection as m, SalesGovernanceService as n, MAX_LEAD_WORKFLOW_TEXT_LENGTH as o, SalesGovernanceEvent as p, LEAD_HUMAN_ACTIVITY_KINDS as r, projectLeadWorkQueue as s, SalesGovernanceError as t, SalesGovernanceStateCollection as u, SalesRepresentative as v, PipelineStageCollection as w, OpportunityCollection as x, PipelineDefinitionCollection as y };
|
|
2872
3542
|
|
|
2873
|
-
//# sourceMappingURL=crm-
|
|
3543
|
+
//# sourceMappingURL=crm-BENK-hHd.js.map
|