@happyvertical/smrt-sales 0.54.2 → 0.54.4

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.
Files changed (54) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +21 -7
  3. package/agents/crm.md +41 -2
  4. package/agents/svelte.md +2 -2
  5. package/dist/agreements.js +2 -2
  6. package/dist/chunks/__smrt-register__-Cxp00I21.js +6 -0
  7. package/dist/chunks/__smrt-register__-Cxp00I21.js.map +1 -0
  8. package/dist/chunks/{agreements-CxHyIbZB.js → agreements-BB_tNU0U.js} +25 -1
  9. package/dist/chunks/agreements-BB_tNU0U.js.map +1 -0
  10. package/dist/chunks/{commissions-BlJpPXFC.js → commissions-Cs4MsxVg.js} +65 -1
  11. package/dist/chunks/commissions-Cs4MsxVg.js.map +1 -0
  12. package/dist/chunks/{crm-C2bD25yv.js → crm-DfRKKvWV.js} +850 -185
  13. package/dist/chunks/crm-DfRKKvWV.js.map +1 -0
  14. package/dist/chunks/{referrals-BsGHbnpW.js → referrals-CrFDsdq-.js} +82 -2
  15. package/dist/chunks/referrals-CrFDsdq-.js.map +1 -0
  16. package/dist/commissions.js +2 -2
  17. package/dist/crm/collections/LeadCollection.d.ts +20 -0
  18. package/dist/crm/collections/LeadCollection.d.ts.map +1 -1
  19. package/dist/crm/collections/OpportunityCollection.d.ts.map +1 -1
  20. package/dist/crm/collections/OpportunityConversionCollection.d.ts.map +1 -1
  21. package/dist/crm/index.d.ts +2 -1
  22. package/dist/crm/index.d.ts.map +1 -1
  23. package/dist/crm/services/LeadWorkflowService.d.ts +79 -5
  24. package/dist/crm/services/LeadWorkflowService.d.ts.map +1 -1
  25. package/dist/crm/services/lead-inbox.d.ts +54 -0
  26. package/dist/crm/services/lead-inbox.d.ts.map +1 -0
  27. package/dist/crm/types.d.ts +2 -0
  28. package/dist/crm/types.d.ts.map +1 -1
  29. package/dist/crm.js +2 -2
  30. package/dist/index.js +5 -5
  31. package/dist/manifest.json +17 -2
  32. package/dist/referrals.js +2 -2
  33. package/dist/smrt-knowledge.json +58 -9
  34. package/dist/svelte/components/LeadCreateForm.svelte +146 -0
  35. package/dist/svelte/components/LeadCreateForm.svelte.d.ts +22 -0
  36. package/dist/svelte/components/LeadCreateForm.svelte.d.ts.map +1 -0
  37. package/dist/svelte/components/LeadDetail.svelte +28 -0
  38. package/dist/svelte/components/LeadDetail.svelte.d.ts +6 -1
  39. package/dist/svelte/components/LeadDetail.svelte.d.ts.map +1 -1
  40. package/dist/svelte/components/LeadList.svelte +129 -3
  41. package/dist/svelte/components/LeadList.svelte.d.ts +29 -1
  42. package/dist/svelte/components/LeadList.svelte.d.ts.map +1 -1
  43. package/dist/svelte/index.d.ts +4 -2
  44. package/dist/svelte/index.d.ts.map +1 -1
  45. package/dist/svelte/index.js +2 -1
  46. package/dist/svelte/types.d.ts +49 -0
  47. package/dist/svelte/types.d.ts.map +1 -1
  48. package/package.json +10 -9
  49. package/dist/chunks/__smrt-register__-RRPKjeCe.js +0 -6
  50. package/dist/chunks/__smrt-register__-RRPKjeCe.js.map +0 -1
  51. package/dist/chunks/agreements-CxHyIbZB.js.map +0 -1
  52. package/dist/chunks/commissions-BlJpPXFC.js.map +0 -1
  53. package/dist/chunks/crm-C2bD25yv.js.map +0 -1
  54. package/dist/chunks/referrals-BsGHbnpW.js.map +0 -1
@@ -1,14 +1,22 @@
1
1
  import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
2
- import { TenantContextError, TenantScoped, requireTenantId, tenantId } from "@happyvertical/smrt-tenancy";
2
+ import { TenantContextError, TenantScoped, getTenantId, requireTenantId, tenantId } from "@happyvertical/smrt-tenancy";
3
+ import { createHash } from "node:crypto";
3
4
  //#region src/crm/models/Lead.ts
4
5
  var __defProp$6 = Object.defineProperty;
5
6
  var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
7
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value
12
+ }) : obj[key] = value;
6
13
  var __decorateClass$6 = (decorators, target, key, kind) => {
7
14
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
8
15
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
9
16
  if (kind && result) __defProp$6(target, key, result);
10
17
  return result;
11
18
  };
19
+ var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
12
20
  var LEAD_STATUS_TRANSITIONS = {
13
21
  new: [
14
22
  "working",
@@ -179,6 +187,7 @@ var Lead = class extends SmrtObject {
179
187
  return loadedLeadStatus.get(this);
180
188
  }
181
189
  };
190
+ __publicField$6(Lead, "__smrtPackage__", "@happyvertical/smrt-sales");
182
191
  __decorateClass$6([tenantId({ nullable: true })], Lead.prototype, "tenantId", 2);
183
192
  __decorateClass$6([field({ required: true })], Lead.prototype, "name", 2);
184
193
  __decorateClass$6([crossPackageRef("@happyvertical/smrt-profiles:Profile")], Lead.prototype, "profileId", 2);
@@ -199,15 +208,137 @@ Lead = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({
199
208
  cli: { skipApiCheck: true }
200
209
  })], Lead);
201
210
  //#endregion
211
+ //#region src/crm/types.ts
212
+ var SALES_REPRESENTATIVE_STATUSES = [
213
+ "active",
214
+ "inactive",
215
+ "suspended"
216
+ ];
217
+ var LEAD_STATUSES = [
218
+ "new",
219
+ "working",
220
+ "qualified",
221
+ "disqualified",
222
+ "merged"
223
+ ];
224
+ var PIPELINE_STATUSES = ["active", "archived"];
225
+ var OPPORTUNITY_STATUSES = [
226
+ "open",
227
+ "won",
228
+ "lost"
229
+ ];
230
+ var SALES_ACTIVITY_SUBJECT_KINDS = ["lead", "opportunity"];
231
+ var SALES_ACTIVITY_KINDS = [
232
+ "note",
233
+ "call",
234
+ "email",
235
+ "meeting",
236
+ "task",
237
+ "assignment",
238
+ "qualification",
239
+ "stage_change",
240
+ "merge",
241
+ "status_change"
242
+ ];
243
+ var DEFAULT_PIPELINE_KEY = "default";
244
+ var DEFAULT_PIPELINE_STAGES = [
245
+ {
246
+ key: "new",
247
+ name: "New",
248
+ probability: .1
249
+ },
250
+ {
251
+ key: "qualified",
252
+ name: "Qualified",
253
+ probability: .25
254
+ },
255
+ {
256
+ key: "discovery",
257
+ name: "Discovery",
258
+ probability: .4
259
+ },
260
+ {
261
+ key: "proposal",
262
+ name: "Proposal",
263
+ probability: .6
264
+ },
265
+ {
266
+ key: "negotiation",
267
+ name: "Negotiation",
268
+ probability: .8
269
+ },
270
+ {
271
+ key: "closed_won",
272
+ name: "Closed Won",
273
+ probability: 1,
274
+ isWon: true
275
+ },
276
+ {
277
+ key: "closed_lost",
278
+ name: "Closed Lost",
279
+ probability: 0,
280
+ isLost: true
281
+ }
282
+ ];
283
+ //#endregion
284
+ //#region src/crm/services/lead-inbox.ts
285
+ var INBOX_SORTS = /* @__PURE__ */ new Set([
286
+ "created",
287
+ "next_action",
288
+ "name"
289
+ ]);
290
+ function normalizeLeadInboxOptions(options) {
291
+ if (!options || typeof options !== "object") throw new Error("LeadCollection.listInbox: options are required");
292
+ if (!Number.isSafeInteger(options.limit) || options.limit < 1 || options.limit > 100) throw new Error("LeadCollection.listInbox: limit must be an integer between 1 and 100");
293
+ if (!Number.isSafeInteger(options.offset) || options.offset < 0) throw new Error("LeadCollection.listInbox: offset must be a non-negative integer");
294
+ if (options.status !== void 0 && !LEAD_STATUSES.includes(options.status)) throw new Error(`LeadCollection.listInbox: unsupported status '${String(options.status)}'`);
295
+ const sort = options.sort ?? "created";
296
+ if (!INBOX_SORTS.has(sort)) throw new Error(`LeadCollection.listInbox: unsupported sort '${String(sort)}'`);
297
+ if (options.ownerRepId !== void 0 && (typeof options.ownerRepId !== "string" || !options.ownerRepId.trim())) throw new Error("LeadCollection.listInbox: ownerRepId must be a non-empty string");
298
+ if (options.search !== void 0 && typeof options.search !== "string") throw new Error("LeadCollection.listInbox: search must be a string");
299
+ if (options.now !== void 0 && (!(options.now instanceof Date) || !Number.isFinite(options.now.getTime()))) throw new Error("LeadCollection.listInbox: now must be a valid Date");
300
+ const search = options.search?.trim();
301
+ return {
302
+ ...options.status ? { status: options.status } : {},
303
+ ...options.ownerRepId?.trim() ? { ownerRepId: options.ownerRepId.trim() } : {},
304
+ ...search ? { search } : {},
305
+ unassigned: options.unassigned === true,
306
+ overdue: options.overdue === true,
307
+ sort,
308
+ limit: options.limit,
309
+ offset: options.offset,
310
+ now: options.now ?? /* @__PURE__ */ new Date()
311
+ };
312
+ }
313
+ function emptyLeadInboxStatusCounts() {
314
+ return {
315
+ all: 0,
316
+ new: 0,
317
+ working: 0,
318
+ qualified: 0,
319
+ disqualified: 0,
320
+ merged: 0,
321
+ unassigned: 0,
322
+ overdue: 0
323
+ };
324
+ }
325
+ //#endregion
202
326
  //#region src/crm/models/Opportunity.ts
203
327
  var __defProp$5 = Object.defineProperty;
204
328
  var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
329
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, {
330
+ enumerable: true,
331
+ configurable: true,
332
+ writable: true,
333
+ value
334
+ }) : obj[key] = value;
205
335
  var __decorateClass$5 = (decorators, target, key, kind) => {
206
336
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
207
337
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
208
338
  if (kind && result) __defProp$5(target, key, result);
209
339
  return result;
210
340
  };
341
+ var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
211
342
  var OPPORTUNITY_STATUS_TRANSITIONS = {
212
343
  open: ["won", "lost"],
213
344
  won: [],
@@ -343,6 +474,7 @@ var Opportunity = class extends SmrtObject {
343
474
  return loadedOpportunityStatus.get(this);
344
475
  }
345
476
  };
477
+ __publicField$5(Opportunity, "__smrtPackage__", "@happyvertical/smrt-sales");
346
478
  __decorateClass$5([tenantId({ nullable: true })], Opportunity.prototype, "tenantId", 2);
347
479
  __decorateClass$5([field({ required: true })], Opportunity.prototype, "name", 2);
348
480
  __decorateClass$5([foreignKey("Lead")], Opportunity.prototype, "leadId", 2);
@@ -363,12 +495,19 @@ Opportunity = __decorateClass$5([TenantScoped({ mode: "optional" }), smrt({
363
495
  //#region src/crm/models/PipelineStage.ts
364
496
  var __defProp$4 = Object.defineProperty;
365
497
  var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
498
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, {
499
+ enumerable: true,
500
+ configurable: true,
501
+ writable: true,
502
+ value
503
+ }) : obj[key] = value;
366
504
  var __decorateClass$4 = (decorators, target, key, kind) => {
367
505
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
368
506
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
369
507
  if (kind && result) __defProp$4(target, key, result);
370
508
  return result;
371
509
  };
510
+ var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
372
511
  var PipelineStage = class extends SmrtObject {
373
512
  tenantId = null;
374
513
  pipelineId = "";
@@ -422,6 +561,7 @@ var PipelineStage = class extends SmrtObject {
422
561
  this.metadata = JSON.stringify(metadata);
423
562
  }
424
563
  };
564
+ __publicField$4(PipelineStage, "__smrtPackage__", "@happyvertical/smrt-sales");
425
565
  __decorateClass$4([tenantId({ nullable: true })], PipelineStage.prototype, "tenantId", 2);
426
566
  __decorateClass$4([foreignKey("PipelineDefinition", { required: true })], PipelineStage.prototype, "pipelineId", 2);
427
567
  __decorateClass$4([field({ required: true })], PipelineStage.prototype, "key", 2);
@@ -473,12 +613,19 @@ var PipelineStageCollection = class extends SmrtCollection {
473
613
  //#region src/crm/models/SalesActivity.ts
474
614
  var __defProp$3 = Object.defineProperty;
475
615
  var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
616
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, {
617
+ enumerable: true,
618
+ configurable: true,
619
+ writable: true,
620
+ value
621
+ }) : obj[key] = value;
476
622
  var __decorateClass$3 = (decorators, target, key, kind) => {
477
623
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
478
624
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
479
625
  if (kind && result) __defProp$3(target, key, result);
480
626
  return result;
481
627
  };
628
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
482
629
  var workflowCompletionPermits = /* @__PURE__ */ new WeakSet();
483
630
  function permitSalesActivityWorkflowCompletion(activity) {
484
631
  workflowCompletionPermits.add(activity);
@@ -573,6 +720,7 @@ var SalesActivity = class extends SmrtObject {
573
720
  }
574
721
  }
575
722
  };
723
+ __publicField$3(SalesActivity, "__smrtPackage__", "@happyvertical/smrt-sales");
576
724
  __decorateClass$3([tenantId({ nullable: true })], SalesActivity.prototype, "tenantId", 2);
577
725
  __decorateClass$3([field({ required: true })], SalesActivity.prototype, "subjectId", 2);
578
726
  __decorateClass$3([crossPackageRef("@happyvertical/smrt-profiles:Profile")], SalesActivity.prototype, "actorProfileId", 2);
@@ -639,12 +787,20 @@ var OpportunityCollection = class extends SmrtCollection {
639
787
  activityCollectionPromise = null;
640
788
  /** Sibling stage collection sharing this collection's DB connection. */
641
789
  async getStageCollection() {
642
- if (!this.stageCollectionPromise) this.stageCollectionPromise = PipelineStageCollection.create({ db: this.db });
790
+ if (!this.stageCollectionPromise) this.stageCollectionPromise = PipelineStageCollection.create({
791
+ db: this.db,
792
+ _reuseInitializedDb: true,
793
+ _deferRuntimeInitialization: true
794
+ });
643
795
  return this.stageCollectionPromise;
644
796
  }
645
797
  /** Sibling activity collection sharing this collection's DB connection. */
646
798
  async getActivityCollection() {
647
- if (!this.activityCollectionPromise) this.activityCollectionPromise = SalesActivityCollection.create({ db: this.db });
799
+ if (!this.activityCollectionPromise) this.activityCollectionPromise = SalesActivityCollection.create({
800
+ db: this.db,
801
+ _reuseInitializedDb: true,
802
+ _deferRuntimeInitialization: true
803
+ });
648
804
  return this.activityCollectionPromise;
649
805
  }
650
806
  /**
@@ -732,12 +888,19 @@ var OpportunityCollection = class extends SmrtCollection {
732
888
  //#region src/crm/models/PipelineDefinition.ts
733
889
  var __defProp$2 = Object.defineProperty;
734
890
  var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
891
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
892
+ enumerable: true,
893
+ configurable: true,
894
+ writable: true,
895
+ value
896
+ }) : obj[key] = value;
735
897
  var __decorateClass$2 = (decorators, target, key, kind) => {
736
898
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
737
899
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
738
900
  if (kind && result) __defProp$2(target, key, result);
739
901
  return result;
740
902
  };
903
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
741
904
  var PipelineDefinition = class extends SmrtObject {
742
905
  tenantId = null;
743
906
  key = "";
@@ -783,6 +946,7 @@ var PipelineDefinition = class extends SmrtObject {
783
946
  this.metadata = JSON.stringify(metadata);
784
947
  }
785
948
  };
949
+ __publicField$2(PipelineDefinition, "__smrtPackage__", "@happyvertical/smrt-sales");
786
950
  __decorateClass$2([tenantId({ nullable: true })], PipelineDefinition.prototype, "tenantId", 2);
787
951
  __decorateClass$2([field({ required: true })], PipelineDefinition.prototype, "key", 2);
788
952
  PipelineDefinition = __decorateClass$2([TenantScoped({ mode: "optional" }), smrt({
@@ -797,79 +961,6 @@ PipelineDefinition = __decorateClass$2([TenantScoped({ mode: "optional" }), smrt
797
961
  conflictColumns: ["tenant_id", "key"]
798
962
  })], PipelineDefinition);
799
963
  //#endregion
800
- //#region src/crm/types.ts
801
- var SALES_REPRESENTATIVE_STATUSES = [
802
- "active",
803
- "inactive",
804
- "suspended"
805
- ];
806
- var LEAD_STATUSES = [
807
- "new",
808
- "working",
809
- "qualified",
810
- "disqualified",
811
- "merged"
812
- ];
813
- var PIPELINE_STATUSES = ["active", "archived"];
814
- var OPPORTUNITY_STATUSES = [
815
- "open",
816
- "won",
817
- "lost"
818
- ];
819
- var SALES_ACTIVITY_SUBJECT_KINDS = ["lead", "opportunity"];
820
- var SALES_ACTIVITY_KINDS = [
821
- "note",
822
- "call",
823
- "email",
824
- "meeting",
825
- "task",
826
- "assignment",
827
- "qualification",
828
- "stage_change",
829
- "merge",
830
- "status_change"
831
- ];
832
- var DEFAULT_PIPELINE_KEY = "default";
833
- var DEFAULT_PIPELINE_STAGES = [
834
- {
835
- key: "new",
836
- name: "New",
837
- probability: .1
838
- },
839
- {
840
- key: "qualified",
841
- name: "Qualified",
842
- probability: .25
843
- },
844
- {
845
- key: "discovery",
846
- name: "Discovery",
847
- probability: .4
848
- },
849
- {
850
- key: "proposal",
851
- name: "Proposal",
852
- probability: .6
853
- },
854
- {
855
- key: "negotiation",
856
- name: "Negotiation",
857
- probability: .8
858
- },
859
- {
860
- key: "closed_won",
861
- name: "Closed Won",
862
- probability: 1,
863
- isWon: true
864
- },
865
- {
866
- key: "closed_lost",
867
- name: "Closed Lost",
868
- probability: 0,
869
- isLost: true
870
- }
871
- ];
872
- //#endregion
873
964
  //#region src/crm/collections/PipelineDefinitionCollection.ts
874
965
  function requireId$1(value, what) {
875
966
  if (!value) throw new Error(`PipelineDefinitionCollection: ${what} has no id`);
@@ -971,6 +1062,105 @@ var PipelineDefinitionCollection = class extends SmrtCollection {
971
1062
  }
972
1063
  };
973
1064
  //#endregion
1065
+ //#region src/crm/models/SalesRepresentative.ts
1066
+ var __defProp$1 = Object.defineProperty;
1067
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1068
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
1069
+ enumerable: true,
1070
+ configurable: true,
1071
+ writable: true,
1072
+ value
1073
+ }) : obj[key] = value;
1074
+ var __decorateClass$1 = (decorators, target, key, kind) => {
1075
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
1076
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1077
+ if (kind && result) __defProp$1(target, key, result);
1078
+ return result;
1079
+ };
1080
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1081
+ var SalesRepresentative = class extends SmrtObject {
1082
+ tenantId = null;
1083
+ profileId = "";
1084
+ earnerId = "";
1085
+ /** Role status — only `active` reps should receive new assignments. */
1086
+ status = "active";
1087
+ /** Job title shown on CRM surfaces (e.g. `'Account Executive'`). */
1088
+ title = "";
1089
+ /**
1090
+ * Free-form JSON object stored as a string. Use
1091
+ * {@link getMetadata}/{@link setMetadata} instead of parsing manually.
1092
+ */
1093
+ metadata = "{}";
1094
+ constructor(options = {}) {
1095
+ super(options);
1096
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
1097
+ if (options.profileId !== void 0) this.profileId = options.profileId;
1098
+ if (options.earnerId !== void 0) this.earnerId = options.earnerId;
1099
+ if (options.status !== void 0) this.status = options.status;
1100
+ if (options.title !== void 0) this.title = options.title;
1101
+ if (options.metadata !== void 0) this.metadata = options.metadata;
1102
+ }
1103
+ /** Whether the rep may receive new lead/opportunity assignments. */
1104
+ isActive() {
1105
+ return this.status === "active";
1106
+ }
1107
+ /** Parse the metadata JSON string; returns `{}` on malformed content. */
1108
+ getMetadata() {
1109
+ try {
1110
+ const parsed = JSON.parse(this.metadata);
1111
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
1112
+ return {};
1113
+ } catch {
1114
+ return {};
1115
+ }
1116
+ }
1117
+ /** Serialize and store the metadata object. */
1118
+ setMetadata(metadata) {
1119
+ this.metadata = JSON.stringify(metadata);
1120
+ }
1121
+ };
1122
+ __publicField$1(SalesRepresentative, "__smrtPackage__", "@happyvertical/smrt-sales");
1123
+ __decorateClass$1([tenantId({ nullable: true })], SalesRepresentative.prototype, "tenantId", 2);
1124
+ __decorateClass$1([crossPackageRef("@happyvertical/smrt-profiles:Profile")], SalesRepresentative.prototype, "profileId", 2);
1125
+ __decorateClass$1([foreignKey("Earner")], SalesRepresentative.prototype, "earnerId", 2);
1126
+ SalesRepresentative = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
1127
+ api: { include: [
1128
+ "list",
1129
+ "get",
1130
+ "create",
1131
+ "update"
1132
+ ] },
1133
+ mcp: { include: ["list", "get"] },
1134
+ cli: { skipApiCheck: true }
1135
+ })], SalesRepresentative);
1136
+ //#endregion
1137
+ //#region src/crm/collections/SalesRepresentativeCollection.ts
1138
+ var SalesRepresentativeCollection = class extends SmrtCollection {
1139
+ static _itemClass = SalesRepresentative;
1140
+ /**
1141
+ * Find rep roles held by a given profile (a profile may hold the role in
1142
+ * several tenants/contexts).
1143
+ *
1144
+ * @param profileId - smrt-profiles Profile id
1145
+ * @returns Reps for that profile, newest first
1146
+ */
1147
+ async findByProfile(profileId) {
1148
+ return await this.list({
1149
+ where: { profileId },
1150
+ orderBy: "created_at DESC"
1151
+ });
1152
+ }
1153
+ /**
1154
+ * Find all active reps (eligible for new lead/opportunity assignment).
1155
+ */
1156
+ async findActive() {
1157
+ return await this.list({
1158
+ where: { status: "active" },
1159
+ orderBy: "created_at DESC"
1160
+ });
1161
+ }
1162
+ };
1163
+ //#endregion
974
1164
  //#region src/crm/collections/LeadCollection.ts
975
1165
  var MERGE_FILL_CONTACT_FIELDS = [
976
1166
  "contactName",
@@ -987,21 +1177,43 @@ var LeadCollection = class extends SmrtCollection {
987
1177
  opportunityCollectionPromise = null;
988
1178
  pipelineCollectionPromise = null;
989
1179
  activityCollectionPromise = null;
1180
+ representativeCollectionPromise = null;
990
1181
  /** Sibling opportunity collection sharing this collection's DB connection. */
991
1182
  async getOpportunityCollection() {
992
- if (!this.opportunityCollectionPromise) this.opportunityCollectionPromise = OpportunityCollection.create({ db: this.db });
1183
+ if (!this.opportunityCollectionPromise) this.opportunityCollectionPromise = OpportunityCollection.create({
1184
+ db: this.db,
1185
+ _reuseInitializedDb: true,
1186
+ _deferRuntimeInitialization: true
1187
+ });
993
1188
  return this.opportunityCollectionPromise;
994
1189
  }
995
1190
  /** Sibling pipeline collection sharing this collection's DB connection. */
996
1191
  async getPipelineCollection() {
997
- if (!this.pipelineCollectionPromise) this.pipelineCollectionPromise = PipelineDefinitionCollection.create({ db: this.db });
1192
+ if (!this.pipelineCollectionPromise) this.pipelineCollectionPromise = PipelineDefinitionCollection.create({
1193
+ db: this.db,
1194
+ _reuseInitializedDb: true,
1195
+ _deferRuntimeInitialization: true
1196
+ });
998
1197
  return this.pipelineCollectionPromise;
999
1198
  }
1000
1199
  /** Sibling activity collection sharing this collection's DB connection. */
1001
1200
  async getActivityCollection() {
1002
- if (!this.activityCollectionPromise) this.activityCollectionPromise = SalesActivityCollection.create({ db: this.db });
1201
+ if (!this.activityCollectionPromise) this.activityCollectionPromise = SalesActivityCollection.create({
1202
+ db: this.db,
1203
+ _reuseInitializedDb: true,
1204
+ _deferRuntimeInitialization: true
1205
+ });
1003
1206
  return this.activityCollectionPromise;
1004
1207
  }
1208
+ /** Sibling representative collection sharing this collection's DB connection. */
1209
+ async getRepresentativeCollection() {
1210
+ if (!this.representativeCollectionPromise) this.representativeCollectionPromise = SalesRepresentativeCollection.create({
1211
+ db: this.db,
1212
+ _reuseInitializedDb: true,
1213
+ _deferRuntimeInitialization: true
1214
+ });
1215
+ return this.representativeCollectionPromise;
1216
+ }
1005
1217
  /** Leads in a given lifecycle status, newest first. */
1006
1218
  async findByStatus(status) {
1007
1219
  return await this.list({
@@ -1017,6 +1229,107 @@ var LeadCollection = class extends SmrtCollection {
1017
1229
  });
1018
1230
  }
1019
1231
  /**
1232
+ * Return one tenant's Lead inbox with exact pagination and tab counts.
1233
+ *
1234
+ * `total` applies every requested filter. `statusCounts` applies the same
1235
+ * owner and search scope, but deliberately ignores status/unassigned/overdue
1236
+ * so a host can switch tabs without issuing a second request. Search is a
1237
+ * trimmed, case-insensitive literal substring across name, contact name,
1238
+ * email, phone, and organization. `next_action` sorts the earliest open task
1239
+ * first and places Leads without an open task last. Every order has an id
1240
+ * tie-breaker, so offset pages remain stable while the underlying data does.
1241
+ *
1242
+ * The read performs a constant number of queries: page, counts, earliest
1243
+ * open tasks, and owners. It requires ambient tenancy and applies an explicit
1244
+ * tenant predicate to both Lead and activity SQL.
1245
+ */
1246
+ async listInbox(options) {
1247
+ const tenantId = getTenantId();
1248
+ if (!tenantId) throw new TenantContextError("LeadCollection.listInbox: an ambient tenant context is required");
1249
+ const normalized = normalizeLeadInboxOptions(options);
1250
+ const now = normalized.now.toISOString();
1251
+ const readScope = await this.resolveListReadPredicate();
1252
+ const base = buildInboxBasePredicate(normalized, tenantId, readScope);
1253
+ const tab = buildInboxTabPredicate(normalized, now);
1254
+ const nextActionsCte = inboxNextActionsCte();
1255
+ const orderBy = inboxOrderBy(normalized.sort);
1256
+ const leads = await this.query(`${nextActionsCte.sql}
1257
+ SELECT l.*
1258
+ FROM ${this.tableName} l
1259
+ LEFT JOIN inbox_next_actions na ON na.subject_id = CAST(l.id AS TEXT)
1260
+ WHERE (${base.sql}) AND (${tab.sql})
1261
+ ORDER BY ${orderBy}
1262
+ LIMIT ? OFFSET ?`, [
1263
+ tenantId,
1264
+ ...base.values,
1265
+ ...tab.values,
1266
+ normalized.limit,
1267
+ normalized.offset
1268
+ ], { allowRawOnTenantScoped: true });
1269
+ const visibleLeads = await readScope.finish(leads);
1270
+ const countResult = await this.db.query(`${nextActionsCte.sql}
1271
+ SELECT l.status,
1272
+ COUNT(*) AS status_count,
1273
+ SUM(CASE WHEN l.owner_rep_id IS NULL OR CAST(l.owner_rep_id AS TEXT) = '' THEN 1 ELSE 0 END) AS unassigned_count,
1274
+ SUM(CASE WHEN na.due_at < ? THEN 1 ELSE 0 END) AS overdue_count,
1275
+ SUM(CASE WHEN ${tab.sql} THEN 1 ELSE 0 END) AS filtered_count
1276
+ FROM ${this.tableName} l
1277
+ LEFT JOIN inbox_next_actions na ON na.subject_id = CAST(l.id AS TEXT)
1278
+ WHERE ${base.sql}
1279
+ GROUP BY l.status`, tenantId, now, ...tab.values, ...base.values);
1280
+ const statusCounts = emptyLeadInboxStatusCounts();
1281
+ let total = 0;
1282
+ for (const row of countResult.rows) {
1283
+ const status = String(row.status);
1284
+ if (status in statusCounts && status !== "all") statusCounts[status] = toInboxCount(row.status_count);
1285
+ statusCounts.all += toInboxCount(row.status_count);
1286
+ statusCounts.unassigned += toInboxCount(row.unassigned_count);
1287
+ statusCounts.overdue += toInboxCount(row.overdue_count);
1288
+ total += toInboxCount(row.filtered_count);
1289
+ }
1290
+ if (visibleLeads.length === 0) return {
1291
+ leads: visibleLeads,
1292
+ items: [],
1293
+ total,
1294
+ statusCounts
1295
+ };
1296
+ const leadIds = visibleLeads.map((lead) => requireId(lead.id, "inbox lead"));
1297
+ const activities = await this.getActivityCollection();
1298
+ const idPlaceholders = leadIds.map(() => "?").join(", ");
1299
+ const nextActions = await activities.query(`SELECT a.*
1300
+ FROM sales_activities a
1301
+ WHERE a.tenant_id = ?
1302
+ AND a.subject_kind = 'lead'
1303
+ AND a.activity_kind = 'task'
1304
+ AND a.completed_at IS NULL
1305
+ AND a.due_at IS NOT NULL
1306
+ AND a.subject_id IN (${idPlaceholders})
1307
+ AND NOT EXISTS (
1308
+ SELECT 1 FROM sales_activities earlier
1309
+ WHERE earlier.tenant_id = a.tenant_id
1310
+ AND earlier.subject_kind = a.subject_kind
1311
+ AND earlier.subject_id = a.subject_id
1312
+ AND earlier.activity_kind = 'task'
1313
+ AND earlier.completed_at IS NULL
1314
+ AND earlier.due_at IS NOT NULL
1315
+ AND (earlier.due_at < a.due_at OR (earlier.due_at = a.due_at AND earlier.id < a.id))
1316
+ )`, [tenantId, ...leadIds], { allowRawOnTenantScoped: true });
1317
+ const nextActionByLead = new Map(nextActions.map((activity) => [activity.subjectId, activity]));
1318
+ const ownerIds = [...new Set(visibleLeads.map((lead) => lead.ownerRepId).filter((id) => id !== ""))];
1319
+ const owners = ownerIds.length ? await (await this.getRepresentativeCollection()).listByIds(ownerIds) : [];
1320
+ const ownerById = new Map(owners.map((owner) => [requireId(owner.id, "inbox owner"), owner]));
1321
+ return {
1322
+ leads: visibleLeads,
1323
+ items: visibleLeads.map((lead) => ({
1324
+ lead,
1325
+ owner: ownerById.get(lead.ownerRepId) ?? null,
1326
+ nextAction: nextActionByLead.get(requireId(lead.id, "inbox lead")) ?? null
1327
+ })),
1328
+ total,
1329
+ statusCounts
1330
+ };
1331
+ }
1332
+ /**
1020
1333
  * Qualify a lead into an Opportunity.
1021
1334
  *
1022
1335
  * Transitions the lead to `qualified` (stamping `qualifiedAt` once) and
@@ -1086,6 +1399,7 @@ var LeadCollection = class extends SmrtCollection {
1086
1399
  subjectId: leadId,
1087
1400
  activityKind: "qualification",
1088
1401
  summary: `Qualified into opportunity '${opportunity.name}'`,
1402
+ actorProfileId: params.actorProfileId ?? "",
1089
1403
  metadata: JSON.stringify({
1090
1404
  opportunityId,
1091
1405
  pipelineId: pipeline.id ?? "",
@@ -1099,6 +1413,7 @@ var LeadCollection = class extends SmrtCollection {
1099
1413
  subjectId: opportunityId,
1100
1414
  activityKind: "qualification",
1101
1415
  summary: `Created from qualified lead '${lead.name}'`,
1416
+ actorProfileId: params.actorProfileId ?? "",
1102
1417
  metadata: JSON.stringify({
1103
1418
  leadId,
1104
1419
  pipelineId: pipeline.id ?? "",
@@ -1237,16 +1552,90 @@ var LeadCollection = class extends SmrtCollection {
1237
1552
  });
1238
1553
  }
1239
1554
  };
1555
+ function inboxNextActionsCte() {
1556
+ return {
1557
+ sql: `WITH inbox_next_actions AS (
1558
+ SELECT subject_id, MIN(due_at) AS due_at
1559
+ FROM sales_activities
1560
+ WHERE tenant_id = ?
1561
+ AND subject_kind = 'lead'
1562
+ AND activity_kind = 'task'
1563
+ AND completed_at IS NULL
1564
+ AND due_at IS NOT NULL
1565
+ GROUP BY subject_id
1566
+ )`,
1567
+ values: []
1568
+ };
1569
+ }
1570
+ function buildInboxBasePredicate(options, tenantId, readScope) {
1571
+ const clauses = ["l.tenant_id = ?", `(${readScope.sql})`];
1572
+ const values = [tenantId, ...readScope.values];
1573
+ if (options.ownerRepId) {
1574
+ clauses.push("l.owner_rep_id = ?");
1575
+ values.push(options.ownerRepId);
1576
+ }
1577
+ if (options.search) {
1578
+ const match = `%${options.search.toLowerCase().replace(/[!%_]/g, "!$&")}%`;
1579
+ clauses.push(`(
1580
+ LOWER(COALESCE(l.name, '')) LIKE ? ESCAPE '!'
1581
+ OR LOWER(COALESCE(l.contact_name, '')) LIKE ? ESCAPE '!'
1582
+ OR LOWER(COALESCE(l.email, '')) LIKE ? ESCAPE '!'
1583
+ OR LOWER(COALESCE(l.phone, '')) LIKE ? ESCAPE '!'
1584
+ OR LOWER(COALESCE(l.organization_name, '')) LIKE ? ESCAPE '!'
1585
+ )`);
1586
+ values.push(match, match, match, match, match);
1587
+ }
1588
+ return {
1589
+ sql: clauses.join(" AND "),
1590
+ values
1591
+ };
1592
+ }
1593
+ function buildInboxTabPredicate(options, now) {
1594
+ const clauses = [];
1595
+ const values = [];
1596
+ if (options.status) {
1597
+ clauses.push("l.status = ?");
1598
+ values.push(options.status);
1599
+ }
1600
+ if (options.unassigned) clauses.push("(l.owner_rep_id IS NULL OR CAST(l.owner_rep_id AS TEXT) = '')");
1601
+ if (options.overdue) {
1602
+ clauses.push("na.due_at < ?");
1603
+ values.push(now);
1604
+ }
1605
+ return {
1606
+ sql: clauses.join(" AND ") || "1 = 1",
1607
+ values
1608
+ };
1609
+ }
1610
+ function inboxOrderBy(sort) {
1611
+ switch (sort) {
1612
+ case "name": return "LOWER(l.name) ASC, l.id ASC";
1613
+ case "next_action": return "CASE WHEN na.due_at IS NULL THEN 1 ELSE 0 END ASC, na.due_at ASC, l.id ASC";
1614
+ default: return "l.created_at DESC, l.id ASC";
1615
+ }
1616
+ }
1617
+ function toInboxCount(value) {
1618
+ const count = Number(value ?? 0);
1619
+ if (!Number.isSafeInteger(count) || count < 0) throw new Error(`LeadCollection.listInbox: database returned invalid count '${String(value)}'`);
1620
+ return count;
1621
+ }
1240
1622
  //#endregion
1241
1623
  //#region src/crm/models/OpportunityConversion.ts
1242
- var __defProp$1 = Object.defineProperty;
1243
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1244
- var __decorateClass$1 = (decorators, target, key, kind) => {
1245
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
1624
+ var __defProp = Object.defineProperty;
1625
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
1626
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
1627
+ enumerable: true,
1628
+ configurable: true,
1629
+ writable: true,
1630
+ value
1631
+ }) : obj[key] = value;
1632
+ var __decorateClass = (decorators, target, key, kind) => {
1633
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
1246
1634
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1247
- if (kind && result) __defProp$1(target, key, result);
1635
+ if (kind && result) __defProp(target, key, result);
1248
1636
  return result;
1249
1637
  };
1638
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1250
1639
  var OpportunityConversion = class extends SmrtObject {
1251
1640
  tenantId = null;
1252
1641
  opportunityId = "";
@@ -1301,11 +1690,12 @@ var OpportunityConversion = class extends SmrtObject {
1301
1690
  return await super.save();
1302
1691
  }
1303
1692
  };
1304
- __decorateClass$1([tenantId({ nullable: true })], OpportunityConversion.prototype, "tenantId", 2);
1305
- __decorateClass$1([foreignKey("Opportunity", { required: true })], OpportunityConversion.prototype, "opportunityId", 2);
1306
- __decorateClass$1([field({ required: true })], OpportunityConversion.prototype, "targetKind", 2);
1307
- __decorateClass$1([field({ required: true })], OpportunityConversion.prototype, "targetId", 2);
1308
- OpportunityConversion = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
1693
+ __publicField(OpportunityConversion, "__smrtPackage__", "@happyvertical/smrt-sales");
1694
+ __decorateClass([tenantId({ nullable: true })], OpportunityConversion.prototype, "tenantId", 2);
1695
+ __decorateClass([foreignKey("Opportunity", { required: true })], OpportunityConversion.prototype, "opportunityId", 2);
1696
+ __decorateClass([field({ required: true })], OpportunityConversion.prototype, "targetKind", 2);
1697
+ __decorateClass([field({ required: true })], OpportunityConversion.prototype, "targetId", 2);
1698
+ OpportunityConversion = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
1309
1699
  api: { include: [
1310
1700
  "list",
1311
1701
  "get",
@@ -1326,7 +1716,11 @@ var OpportunityConversionCollection = class extends SmrtCollection {
1326
1716
  opportunityCollectionPromise = null;
1327
1717
  /** Sibling opportunity collection sharing this collection's DB connection. */
1328
1718
  async getOpportunityCollection() {
1329
- if (!this.opportunityCollectionPromise) this.opportunityCollectionPromise = OpportunityCollection.create({ db: this.db });
1719
+ if (!this.opportunityCollectionPromise) this.opportunityCollectionPromise = OpportunityCollection.create({
1720
+ db: this.db,
1721
+ _reuseInitializedDb: true,
1722
+ _deferRuntimeInitialization: true
1723
+ });
1330
1724
  return this.opportunityCollectionPromise;
1331
1725
  }
1332
1726
  /** All conversion links recorded for one opportunity, oldest first. */
@@ -1377,97 +1771,6 @@ var OpportunityConversionCollection = class extends SmrtCollection {
1377
1771
  }
1378
1772
  };
1379
1773
  //#endregion
1380
- //#region src/crm/models/SalesRepresentative.ts
1381
- var __defProp = Object.defineProperty;
1382
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
1383
- var __decorateClass = (decorators, target, key, kind) => {
1384
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
1385
- for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1386
- if (kind && result) __defProp(target, key, result);
1387
- return result;
1388
- };
1389
- var SalesRepresentative = class extends SmrtObject {
1390
- tenantId = null;
1391
- profileId = "";
1392
- earnerId = "";
1393
- /** Role status — only `active` reps should receive new assignments. */
1394
- status = "active";
1395
- /** Job title shown on CRM surfaces (e.g. `'Account Executive'`). */
1396
- title = "";
1397
- /**
1398
- * Free-form JSON object stored as a string. Use
1399
- * {@link getMetadata}/{@link setMetadata} instead of parsing manually.
1400
- */
1401
- metadata = "{}";
1402
- constructor(options = {}) {
1403
- super(options);
1404
- if (options.tenantId !== void 0) this.tenantId = options.tenantId;
1405
- if (options.profileId !== void 0) this.profileId = options.profileId;
1406
- if (options.earnerId !== void 0) this.earnerId = options.earnerId;
1407
- if (options.status !== void 0) this.status = options.status;
1408
- if (options.title !== void 0) this.title = options.title;
1409
- if (options.metadata !== void 0) this.metadata = options.metadata;
1410
- }
1411
- /** Whether the rep may receive new lead/opportunity assignments. */
1412
- isActive() {
1413
- return this.status === "active";
1414
- }
1415
- /** Parse the metadata JSON string; returns `{}` on malformed content. */
1416
- getMetadata() {
1417
- try {
1418
- const parsed = JSON.parse(this.metadata);
1419
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
1420
- return {};
1421
- } catch {
1422
- return {};
1423
- }
1424
- }
1425
- /** Serialize and store the metadata object. */
1426
- setMetadata(metadata) {
1427
- this.metadata = JSON.stringify(metadata);
1428
- }
1429
- };
1430
- __decorateClass([tenantId({ nullable: true })], SalesRepresentative.prototype, "tenantId", 2);
1431
- __decorateClass([crossPackageRef("@happyvertical/smrt-profiles:Profile")], SalesRepresentative.prototype, "profileId", 2);
1432
- __decorateClass([foreignKey("Earner")], SalesRepresentative.prototype, "earnerId", 2);
1433
- SalesRepresentative = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
1434
- api: { include: [
1435
- "list",
1436
- "get",
1437
- "create",
1438
- "update"
1439
- ] },
1440
- mcp: { include: ["list", "get"] },
1441
- cli: { skipApiCheck: true }
1442
- })], SalesRepresentative);
1443
- //#endregion
1444
- //#region src/crm/collections/SalesRepresentativeCollection.ts
1445
- var SalesRepresentativeCollection = class extends SmrtCollection {
1446
- static _itemClass = SalesRepresentative;
1447
- /**
1448
- * Find rep roles held by a given profile (a profile may hold the role in
1449
- * several tenants/contexts).
1450
- *
1451
- * @param profileId - smrt-profiles Profile id
1452
- * @returns Reps for that profile, newest first
1453
- */
1454
- async findByProfile(profileId) {
1455
- return await this.list({
1456
- where: { profileId },
1457
- orderBy: "created_at DESC"
1458
- });
1459
- }
1460
- /**
1461
- * Find all active reps (eligible for new lead/opportunity assignment).
1462
- */
1463
- async findActive() {
1464
- return await this.list({
1465
- where: { status: "active" },
1466
- orderBy: "created_at DESC"
1467
- });
1468
- }
1469
- };
1470
- //#endregion
1471
1774
  //#region src/crm/services/LeadWorkflowService.ts
1472
1775
  var LEAD_HUMAN_ACTIVITY_KINDS = [
1473
1776
  "note",
@@ -1511,8 +1814,219 @@ var LeadWorkflowService = class LeadWorkflowService {
1511
1814
  return new LeadWorkflowService({
1512
1815
  leads: await LeadCollection.create(options),
1513
1816
  activities: await SalesActivityCollection.create(options),
1514
- representatives: await SalesRepresentativeCollection.create(options)
1817
+ representatives: await SalesRepresentativeCollection.create(options),
1818
+ opportunities: await OpportunityCollection.create(options),
1819
+ conversions: await OpportunityConversionCollection.create(options),
1820
+ pipelines: await PipelineDefinitionCollection.create(options),
1821
+ stages: await PipelineStageCollection.create(options)
1822
+ });
1823
+ }
1824
+ /** Create or explicitly report a tenant-local duplicate Lead. */
1825
+ async createLead(input) {
1826
+ const normalized = this.normalizeCreateLeadInput(input);
1827
+ const tenantId = this.requireActiveTenant();
1828
+ const intentHash = sha256(stableStringify(normalized.intent));
1829
+ const operationId = normalized.idempotencyKey || normalized.dedupe !== "none" ? uuidFromHash(sha256(`lead-intake:${tenantId}:${normalized.idempotencyKey ? `key:${normalized.idempotencyKey}` : `intent:${intentHash}`}`)) : void 0;
1830
+ const result = await this.runMutation(async (deps, activeTenantId) => {
1831
+ if (operationId) {
1832
+ await this.lockOperationFence(deps, `lead-intake:${operationId}`);
1833
+ const replay = await deps.activities.get({ id: operationId }, { cache: false });
1834
+ if (replay) {
1835
+ const metadata = replay.getMetadata();
1836
+ if (replay.activityKind !== "lead_intake" && replay.activityKind !== "inbound") throw this.refusal("idempotency_conflict", "Lead intake operation key is already used by another activity");
1837
+ if (metadata.intentHash !== intentHash) throw this.refusal("idempotency_conflict", "Lead intake idempotency key was already used with different input");
1838
+ return {
1839
+ leadId: replay.subjectId,
1840
+ created: false
1841
+ };
1842
+ }
1843
+ }
1844
+ if (normalized.ownerRepId) {
1845
+ const representative = await deps.representatives.get({ id: normalized.ownerRepId }, { cache: false });
1846
+ if (!representative) throw this.refusal("representative_unavailable", "Representative is unavailable in the active tenant");
1847
+ if (!representative.isActive()) throw this.refusal("representative_inactive", "Representative is not active");
1848
+ }
1849
+ const matches = await this.findDedupeMatches(deps, activeTenantId, normalized);
1850
+ const active = matches.find((candidate) => candidate.status === "new" || candidate.status === "working");
1851
+ if (active) {
1852
+ const leadId2 = this.requireLeadId(active);
1853
+ await deps.activities.create({
1854
+ ...operationId ? { id: operationId } : {},
1855
+ tenantId: activeTenantId,
1856
+ subjectKind: "lead",
1857
+ subjectId: leadId2,
1858
+ activityKind: "inbound",
1859
+ summary: "Received duplicate lead intake",
1860
+ actorProfileId: normalized.actorProfileId,
1861
+ metadata: JSON.stringify({
1862
+ intentHash,
1863
+ created: false,
1864
+ dedupe: normalized.dedupe,
1865
+ receivedAt: normalized.now.toISOString(),
1866
+ sourceKind: normalized.sourceKind,
1867
+ sourceId: normalized.sourceId
1868
+ })
1869
+ });
1870
+ return {
1871
+ leadId: leadId2,
1872
+ created: false
1873
+ };
1874
+ }
1875
+ const terminal = matches[0];
1876
+ if (terminal) {
1877
+ const duplicateOfId = this.requireLeadId(terminal);
1878
+ return {
1879
+ leadId: duplicateOfId,
1880
+ created: false,
1881
+ duplicateOfId
1882
+ };
1883
+ }
1884
+ const lead2 = await deps.leads.create({
1885
+ tenantId: activeTenantId,
1886
+ name: normalized.name,
1887
+ email: normalized.email,
1888
+ contactName: normalized.contactName,
1889
+ phone: normalized.phone,
1890
+ organizationName: normalized.organizationName,
1891
+ sourceKind: normalized.sourceKind,
1892
+ sourceId: normalized.sourceId,
1893
+ acquisitionContext: stableStringify(normalized.acquisitionContext),
1894
+ ownerRepId: normalized.ownerRepId,
1895
+ status: "new"
1896
+ });
1897
+ const leadId = this.requireLeadId(lead2);
1898
+ await deps.activities.create({
1899
+ ...operationId ? { id: operationId } : {},
1900
+ tenantId: activeTenantId,
1901
+ subjectKind: "lead",
1902
+ subjectId: leadId,
1903
+ activityKind: "lead_intake",
1904
+ summary: "Created lead from intake",
1905
+ actorProfileId: normalized.actorProfileId,
1906
+ metadata: JSON.stringify({
1907
+ intentHash,
1908
+ created: true,
1909
+ dedupe: normalized.dedupe,
1910
+ receivedAt: normalized.now.toISOString(),
1911
+ sourceKind: normalized.sourceKind,
1912
+ sourceId: normalized.sourceId
1913
+ })
1914
+ });
1915
+ return {
1916
+ leadId,
1917
+ created: true
1918
+ };
1515
1919
  });
1920
+ const lead = await this.readLead(this.deps.leads, result.leadId, tenantId);
1921
+ return {
1922
+ lead,
1923
+ created: result.created,
1924
+ ...result.duplicateOfId ? { duplicateOf: lead } : {}
1925
+ };
1926
+ }
1927
+ /** Qualify a Lead through the collection lifecycle in one transaction. */
1928
+ async qualifyLead(input) {
1929
+ const actorProfileId = this.requireIdentifier(input.actorProfileId, "actorProfileId");
1930
+ if (input.pipelineId && !UUID_RE.test(input.pipelineId)) throw this.refusal("pipeline_unavailable", "Pipeline is unavailable in the active tenant");
1931
+ if (input.expectedValueCents !== void 0 && (!Number.isSafeInteger(input.expectedValueCents) || input.expectedValueCents < 0)) throw this.refusal("invalid_value", "Expected value must be a non-negative safe integer number of cents");
1932
+ const tenantId = this.requireActiveTenant();
1933
+ const result = await this.runMutation(async (deps, activeTenantId) => {
1934
+ const lead2 = await this.lockLead(deps, input.leadId, activeTenantId);
1935
+ if (lead2.status === "disqualified" || lead2.status === "merged") throw this.refusal("invalid_transition", `Lead cannot be qualified from '${lead2.status}'`);
1936
+ if (input.pipelineId) {
1937
+ const pipeline = await deps.pipelines.get({ id: input.pipelineId }, { cache: false });
1938
+ if (!pipeline || pipeline.tenantId !== activeTenantId) throw this.refusal("pipeline_unavailable", "Pipeline is unavailable in the active tenant");
1939
+ }
1940
+ const leadId = this.requireLeadId(lead2);
1941
+ const prior = await deps.opportunities.findByLead(leadId);
1942
+ let opportunity2;
1943
+ try {
1944
+ opportunity2 = await deps.leads.qualify({
1945
+ leadId,
1946
+ actorProfileId,
1947
+ pipelineId: input.pipelineId,
1948
+ opportunityName: input.opportunityName,
1949
+ expectedValueCents: input.expectedValueCents,
1950
+ currency: input.currency,
1951
+ expectedCloseAt: input.expectedCloseAt,
1952
+ now: input.now
1953
+ });
1954
+ } catch (error) {
1955
+ if (error instanceof Error && (error.message.includes("pipeline") || error.message.includes("stages"))) throw this.refusal("pipeline_unavailable", error.message);
1956
+ throw error;
1957
+ }
1958
+ return {
1959
+ leadId,
1960
+ opportunityId: this.requireOpportunityId(opportunity2),
1961
+ created: prior.length === 0
1962
+ };
1963
+ });
1964
+ return {
1965
+ lead: await this.readLead(this.deps.leads, result.leadId, tenantId),
1966
+ opportunity: await this.readOpportunity(result.opportunityId, tenantId),
1967
+ created: result.created
1968
+ };
1969
+ }
1970
+ /** Close an opportunity at its configured terminal stage and optionally link conversion. */
1971
+ async closeOpportunity(input) {
1972
+ const actorProfileId = this.requireIdentifier(input.actorProfileId, "actorProfileId");
1973
+ if (!UUID_RE.test(input.opportunityId)) throw this.refusal("opportunity_unavailable", "Opportunity is unavailable in the active tenant");
1974
+ if (input.outcome !== "won" && input.outcome !== "lost") throw this.refusal("invalid_transition", "Opportunity outcome must be 'won' or 'lost'");
1975
+ if (input.conversion && input.outcome !== "won") throw this.refusal("invalid_conversion", "A conversion can only be recorded for a won opportunity");
1976
+ const conversion = input.conversion ? {
1977
+ targetKind: this.requireOpenIdentifier(input.conversion.targetKind, "conversion targetKind"),
1978
+ targetId: this.requireOpenIdentifier(input.conversion.targetId, "conversion targetId"),
1979
+ note: input.conversion.note?.trim() ?? ""
1980
+ } : void 0;
1981
+ const reason = input.reason?.trim();
1982
+ if (reason && reason.length > 1e3) throw this.refusal("reason_too_long", `Lead workflow reason must be at most ${MAX_LEAD_WORKFLOW_TEXT_LENGTH} characters`);
1983
+ const tenantId = this.requireActiveTenant();
1984
+ const result = await this.runMutation(async (deps, activeTenantId) => {
1985
+ const opportunity2 = await this.lockOpportunity(deps, input.opportunityId, activeTenantId);
1986
+ let changed = false;
1987
+ if (opportunity2.status !== "open") {
1988
+ if (opportunity2.status !== input.outcome) throw this.refusal("opportunity_replay_conflict", `Opportunity is already closed as '${opportunity2.status}'`);
1989
+ if (reason && opportunity2.outcomeReason !== reason) throw this.refusal("opportunity_replay_conflict", "Opportunity was already closed with a different reason");
1990
+ } else {
1991
+ const terminal = (await deps.stages.list({
1992
+ where: { pipelineId: opportunity2.pipelineId },
1993
+ orderBy: "sort_order ASC"
1994
+ })).find((stage) => input.outcome === "won" ? stage.isWon : stage.isLost);
1995
+ if (!terminal?.id) throw this.refusal("pipeline_has_no_terminal_stage", `Opportunity pipeline has no '${input.outcome}' terminal stage`);
1996
+ await deps.opportunities.moveToStage({
1997
+ opportunityId: input.opportunityId,
1998
+ stageId: terminal.id,
1999
+ actorProfileId,
2000
+ outcomeReason: reason,
2001
+ now: input.now
2002
+ });
2003
+ changed = true;
2004
+ }
2005
+ let conversionId;
2006
+ let conversionCreated;
2007
+ if (conversion) {
2008
+ const recorded = await deps.conversions.recordConversion({
2009
+ opportunityId: input.opportunityId,
2010
+ ...conversion
2011
+ });
2012
+ conversionId = this.requireConversionId(recorded.conversion);
2013
+ conversionCreated = recorded.created;
2014
+ }
2015
+ return {
2016
+ opportunityId: input.opportunityId,
2017
+ changed,
2018
+ conversionId,
2019
+ conversionCreated
2020
+ };
2021
+ });
2022
+ const opportunity = await this.readOpportunity(result.opportunityId, tenantId);
2023
+ const persistedConversion = result.conversionId ? await this.deps.conversions.get({ id: result.conversionId }, { cache: false }) : void 0;
2024
+ return {
2025
+ opportunity,
2026
+ changed: result.changed,
2027
+ ...persistedConversion ? { conversion: persistedConversion } : {},
2028
+ ...result.conversionCreated !== void 0 ? { conversionCreated: result.conversionCreated } : {}
2029
+ };
1516
2030
  }
1517
2031
  /** Assign or reassign an active representative, with one audit row per change. */
1518
2032
  async assignLead(input) {
@@ -1683,12 +2197,17 @@ var LeadWorkflowService = class LeadWorkflowService {
1683
2197
  const tenantId = this.requireActiveTenant();
1684
2198
  const lead = await this.readLead(this.deps.leads, input.leadId, tenantId);
1685
2199
  const leadId = this.requireLeadId(lead);
1686
- const [owner, openTasks] = await Promise.all([lead.ownerRepId ? this.deps.representatives.get({ id: lead.ownerRepId }, { cache: false }) : Promise.resolve(null), this.deps.activities.findOpenTasks("lead", leadId)]);
2200
+ const [owner, openTasks, linkedOpportunities] = await Promise.all([
2201
+ lead.ownerRepId ? this.deps.representatives.get({ id: lead.ownerRepId }, { cache: false }) : Promise.resolve(null),
2202
+ this.deps.activities.findOpenTasks("lead", leadId),
2203
+ this.deps.opportunities.findByLead(leadId)
2204
+ ]);
1687
2205
  const earliestOpenTask = openTasks[0] ?? null;
1688
2206
  return {
1689
2207
  lead,
1690
2208
  owner,
1691
2209
  earliestOpenTask,
2210
+ opportunity: linkedOpportunities[0] ?? null,
1692
2211
  queue: projectLeadWorkQueue({
1693
2212
  status: lead.status,
1694
2213
  ownerRepId: lead.ownerRepId,
@@ -1720,6 +2239,26 @@ var LeadWorkflowService = class LeadWorkflowService {
1720
2239
  _reuseInitializedDb: true,
1721
2240
  _deferRuntimeInitialization: true
1722
2241
  }),
2242
+ opportunities: await OpportunityCollection.create({
2243
+ db: tx,
2244
+ _reuseInitializedDb: true,
2245
+ _deferRuntimeInitialization: true
2246
+ }),
2247
+ conversions: await OpportunityConversionCollection.create({
2248
+ db: tx,
2249
+ _reuseInitializedDb: true,
2250
+ _deferRuntimeInitialization: true
2251
+ }),
2252
+ pipelines: await PipelineDefinitionCollection.create({
2253
+ db: tx,
2254
+ _reuseInitializedDb: true,
2255
+ _deferRuntimeInitialization: true
2256
+ }),
2257
+ stages: await PipelineStageCollection.create({
2258
+ db: tx,
2259
+ _reuseInitializedDb: true,
2260
+ _deferRuntimeInitialization: true
2261
+ }),
1723
2262
  supportsRowLocks
1724
2263
  }, tenantId));
1725
2264
  if (supportsRowLocks) return await runTransaction();
@@ -1759,12 +2298,110 @@ var LeadWorkflowService = class LeadWorkflowService {
1759
2298
  if (!task || task.tenantId !== tenantId || task.subjectKind !== "lead" || task.subjectId !== leadId) throw this.refusal("task_unavailable", "Task is unavailable for this lead");
1760
2299
  return task;
1761
2300
  }
2301
+ async lockOpportunity(deps, opportunityId, tenantId) {
2302
+ if (deps.supportsRowLocks) {
2303
+ const opportunity = (await deps.opportunities.query(`SELECT * FROM ${deps.opportunities.tableName}
2304
+ WHERE id = $1 AND tenant_id = $2
2305
+ FOR UPDATE`, [opportunityId, tenantId], { allowRawOnTenantScoped: true }))[0];
2306
+ if (opportunity && opportunity.tenantId === tenantId) return opportunity;
2307
+ throw this.refusal("opportunity_unavailable", "Opportunity is unavailable in the active tenant");
2308
+ }
2309
+ return await this.readOpportunityFrom(deps.opportunities, opportunityId, tenantId);
2310
+ }
2311
+ async lockOperationFence(deps, key) {
2312
+ if (!deps.supportsRowLocks) return;
2313
+ await deps.leads.db.query("SELECT pg_advisory_xact_lock(hashtext($1))", key);
2314
+ }
2315
+ async findDedupeMatches(deps, tenantId, input) {
2316
+ if (input.dedupe === "none") return [];
2317
+ const clauses = [];
2318
+ const identityLocks = [];
2319
+ const params = [tenantId];
2320
+ if (input.email) {
2321
+ params.push(input.email);
2322
+ clauses.push(`LOWER(TRIM(email)) = $${params.length}`);
2323
+ identityLocks.push(`lead-dedupe:${tenantId}:email:${input.email}`);
2324
+ }
2325
+ if (input.dedupe === "email_or_org" && input.organizationName) {
2326
+ const organizationName = input.organizationName.toLowerCase();
2327
+ params.push(organizationName);
2328
+ clauses.push(`LOWER(TRIM(organization_name)) = $${params.length}`);
2329
+ identityLocks.push(`lead-dedupe:${tenantId}:organization:${organizationName}`);
2330
+ }
2331
+ if (clauses.length === 0) return [];
2332
+ if (deps.supportsRowLocks) for (const identityLock of identityLocks.sort()) await this.lockOperationFence(deps, identityLock);
2333
+ return await deps.leads.query(`SELECT * FROM ${deps.leads.tableName}
2334
+ WHERE tenant_id = $1 AND (${clauses.join(" OR ")})
2335
+ ORDER BY created_at DESC${deps.supportsRowLocks ? " FOR UPDATE" : ""}`, params, { allowRawOnTenantScoped: true });
2336
+ }
1762
2337
  async readLead(leads, leadId, tenantId) {
1763
2338
  if (!UUID_RE.test(leadId)) throw this.refusal("lead_unavailable", "Lead is unavailable in the active tenant");
1764
2339
  const lead = await leads.get({ id: leadId }, { cache: false });
1765
2340
  if (!lead || lead.tenantId !== tenantId) throw this.refusal("lead_unavailable", "Lead is unavailable in the active tenant");
1766
2341
  return lead;
1767
2342
  }
2343
+ async readOpportunity(opportunityId, tenantId) {
2344
+ return await this.readOpportunityFrom(this.deps.opportunities, opportunityId, tenantId);
2345
+ }
2346
+ async readOpportunityFrom(opportunities, opportunityId, tenantId) {
2347
+ const opportunity = await opportunities.get({ id: opportunityId }, { cache: false });
2348
+ if (!opportunity || opportunity.tenantId !== tenantId) throw this.refusal("opportunity_unavailable", "Opportunity is unavailable in the active tenant");
2349
+ return opportunity;
2350
+ }
2351
+ normalizeCreateLeadInput(input) {
2352
+ const name = typeof input.name === "string" ? input.name.trim() : "";
2353
+ if (!name) throw this.refusal("name_required", "Lead name is required");
2354
+ const email = typeof input.email === "string" ? input.email.trim().toLowerCase() : "";
2355
+ const phone = typeof input.phone === "string" ? input.phone.trim() : "";
2356
+ if (!email && !phone) throw this.refusal("contact_required", "Lead intake requires at least one of email or phone");
2357
+ if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(email)) throw this.refusal("invalid_email", "Lead email is invalid");
2358
+ const sourceKind = typeof input.sourceKind === "string" ? input.sourceKind.trim() : "";
2359
+ if (!sourceKind) throw this.refusal("source_kind_required", "Lead sourceKind is required");
2360
+ const dedupe = input.dedupe ?? "email";
2361
+ if (![
2362
+ "none",
2363
+ "email",
2364
+ "email_or_org"
2365
+ ].includes(dedupe)) throw this.refusal("invalid_dedupe_policy", "Lead dedupe policy is invalid");
2366
+ const idempotencyKey = input.idempotencyKey?.trim() ?? "";
2367
+ if (idempotencyKey.length > 255) throw this.refusal("invalid_idempotency_key", "Lead idempotency key must be at most 255 characters");
2368
+ const acquisitionContext = input.acquisitionContext ?? {};
2369
+ if (!isPlainJsonObject(acquisitionContext) || !isJsonValue(acquisitionContext, /* @__PURE__ */ new Set())) throw this.refusal("invalid_metadata", "Lead acquisitionContext must be a plain JSON object with JSON values");
2370
+ const ownerRepId = input.ownerRepId ? this.requireIdentifier(input.ownerRepId, "ownerRepId") : "";
2371
+ const actorProfileId = input.actorProfileId ? this.requireIdentifier(input.actorProfileId, "actorProfileId") : "";
2372
+ const now = input.now ?? /* @__PURE__ */ new Date();
2373
+ if (!Number.isFinite(now.getTime())) throw this.refusal("invalid_due_at", "Lead intake now must be valid");
2374
+ const normalized = {
2375
+ name,
2376
+ email,
2377
+ contactName: input.contactName?.trim() ?? "",
2378
+ phone,
2379
+ organizationName: input.organizationName?.trim() ?? "",
2380
+ sourceKind,
2381
+ sourceId: input.sourceId?.trim() ?? "",
2382
+ acquisitionContext,
2383
+ ownerRepId,
2384
+ actorProfileId,
2385
+ dedupe,
2386
+ idempotencyKey,
2387
+ now
2388
+ };
2389
+ return {
2390
+ ...normalized,
2391
+ intent: {
2392
+ name: normalized.name,
2393
+ email: normalized.email,
2394
+ contactName: normalized.contactName,
2395
+ phone: normalized.phone,
2396
+ organizationName: normalized.organizationName,
2397
+ sourceKind: normalized.sourceKind,
2398
+ sourceId: normalized.sourceId,
2399
+ acquisitionContext: normalized.acquisitionContext,
2400
+ ownerRepId: normalized.ownerRepId,
2401
+ dedupe: normalized.dedupe
2402
+ }
2403
+ };
2404
+ }
1768
2405
  assertActiveFollowUpLead(lead) {
1769
2406
  if (lead.status !== "new" && lead.status !== "working") throw this.refusal("lead_not_actionable", `Lead is not actionable for ordinary follow-up while '${lead.status}'`);
1770
2407
  }
@@ -1818,6 +2455,19 @@ var LeadWorkflowService = class LeadWorkflowService {
1818
2455
  if (!activity.id) throw this.refusal("task_unavailable", "Task is unavailable for this lead");
1819
2456
  return activity.id;
1820
2457
  }
2458
+ requireOpportunityId(opportunity) {
2459
+ if (!opportunity.id) throw this.refusal("opportunity_unavailable", "Opportunity is unavailable in the active tenant");
2460
+ return opportunity.id;
2461
+ }
2462
+ requireConversionId(conversion) {
2463
+ if (!conversion.id) throw this.refusal("invalid_conversion", "Opportunity conversion could not be persisted");
2464
+ return conversion.id;
2465
+ }
2466
+ requireOpenIdentifier(value, field) {
2467
+ const normalized = typeof value === "string" ? value.trim() : "";
2468
+ if (!normalized) throw this.refusal("invalid_conversion", `${field} is required`);
2469
+ return normalized;
2470
+ }
1821
2471
  now(value) {
1822
2472
  return value ?? /* @__PURE__ */ new Date();
1823
2473
  }
@@ -1887,7 +2537,22 @@ function isJsonValue(value, ancestors) {
1887
2537
  }
1888
2538
  return false;
1889
2539
  }
2540
+ function stableStringify(value) {
2541
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
2542
+ if (isPlainJsonObject(value)) return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
2543
+ return JSON.stringify(value);
2544
+ }
2545
+ function sha256(value) {
2546
+ return createHash("sha256").update(value).digest("hex");
2547
+ }
2548
+ function uuidFromHash(hash) {
2549
+ const chars = hash.slice(0, 32).split("");
2550
+ chars[12] = "5";
2551
+ chars[16] = (Number.parseInt(chars[16] ?? "0", 16) & 3 | 8).toString(16);
2552
+ const value = chars.join("");
2553
+ return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
2554
+ }
1890
2555
  //#endregion
1891
- export { SalesActivity as C, Lead as D, Opportunity as E, SalesActivityCollection as S, PipelineStage as T, SALES_ACTIVITY_KINDS as _, projectLeadWorkQueue as a, PipelineDefinition as b, OpportunityConversionCollection as c, PipelineDefinitionCollection as d, DEFAULT_PIPELINE_KEY as f, PIPELINE_STATUSES as g, OPPORTUNITY_STATUSES as h, MAX_LEAD_WORKFLOW_TEXT_LENGTH as i, OpportunityConversion as l, LEAD_STATUSES as m, LeadWorkflowService as n, SalesRepresentativeCollection as o, DEFAULT_PIPELINE_STAGES as p, LeadWorkflowValidationError as r, SalesRepresentative as s, LEAD_HUMAN_ACTIVITY_KINDS as t, LeadCollection as u, SALES_ACTIVITY_SUBJECT_KINDS as v, PipelineStageCollection as w, OpportunityCollection as x, SALES_REPRESENTATIVE_STATUSES as y };
2556
+ export { PIPELINE_STATUSES as C, Lead as D, SALES_REPRESENTATIVE_STATUSES as E, OPPORTUNITY_STATUSES as S, SALES_ACTIVITY_SUBJECT_KINDS as T, PipelineStage as _, projectLeadWorkQueue as a, DEFAULT_PIPELINE_STAGES as b, LeadCollection as c, PipelineDefinitionCollection as d, PipelineDefinition as f, PipelineStageCollection as g, SalesActivity as h, MAX_LEAD_WORKFLOW_TEXT_LENGTH as i, SalesRepresentativeCollection as l, SalesActivityCollection as m, LeadWorkflowService as n, OpportunityConversionCollection as o, OpportunityCollection as p, LeadWorkflowValidationError as r, OpportunityConversion as s, LEAD_HUMAN_ACTIVITY_KINDS as t, SalesRepresentative as u, Opportunity as v, SALES_ACTIVITY_KINDS as w, LEAD_STATUSES as x, DEFAULT_PIPELINE_KEY as y };
1892
2557
 
1893
- //# sourceMappingURL=crm-C2bD25yv.js.map
2558
+ //# sourceMappingURL=crm-DfRKKvWV.js.map