@classytic/crm 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -1
- package/README.md +1 -1
- package/dist/events.d.ts +1 -0
- package/dist/index.d.ts +6 -13
- package/dist/index.js +125 -85
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@classytic/crm` are documented here.
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1.1.0 - 2026-08-17
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- `tenant` config key replaces `tenantFieldType` across all models and engine (`assertNoLegacyTenantKeys` guard applied)
|
|
9
|
+
- `OpportunityRepository` gains new query methods
|
|
10
|
+
- Model schemas standardised (consistent field ordering, injected tenant field)
|
|
11
|
+
|
|
12
|
+
## 1.0.0 - 2026-07-29
|
|
6
13
|
|
|
7
14
|
### Fixed — Lead and Contact forked one mailbox into two rows (BEHAVIOURAL, 2026-08-08)
|
|
8
15
|
|
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ import { createCrm, LeadService } from '@classytic/crm';
|
|
|
99
99
|
|
|
100
100
|
const crm = await createCrm({
|
|
101
101
|
connection: mongoose.connection,
|
|
102
|
-
|
|
102
|
+
tenant: { fieldType: 'objectId' }, // matches Better Auth orgs; 'string' for UUID hosts
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
const ctx = { organizationId: orgId };
|
package/dist/events.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import "mongoose";
|
|
|
5
5
|
import "@classytic/mongokit";
|
|
6
6
|
import { OperationContext } from "@classytic/primitives/context";
|
|
7
7
|
import "@classytic/primitives/outbox";
|
|
8
|
+
import "@classytic/repo-core/tenant";
|
|
8
9
|
//#region src/events/event-constants.d.ts
|
|
9
10
|
/** PACKAGE_RULES rule 12: `package:resource.verb`. */
|
|
10
11
|
declare const CRM_EVENTS: {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ModelBlueprint, PaginationConfig, PluginType, Repository } from "@class
|
|
|
2
2
|
import { Connection, Model, Schema, SchemaDefinitionProperty } from "mongoose";
|
|
3
3
|
import { DomainEvent, EventHandler, EventMeta, EventTransport } from "@classytic/primitives/events";
|
|
4
4
|
import { InProcessEventBus } from "@classytic/primitives/event-infra";
|
|
5
|
+
import { ResolvedTenantConfig, TenantConfig } from "@classytic/repo-core/tenant";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { StatusChangeEntry, StatusHistory, appendStatus, lastTransitionTo, latestEntry, timeInStatus } from "@classytic/primitives/status-history";
|
|
7
8
|
import { OperationContext } from "@classytic/primitives/context";
|
|
@@ -83,10 +84,9 @@ declare const ALL_CRM_MODULES: ResolvedCrmModules;
|
|
|
83
84
|
declare function resolveCrmModules(modules: CrmModulesConfig | undefined): ResolvedCrmModules;
|
|
84
85
|
//#endregion
|
|
85
86
|
//#region src/models/tenant-field.d.ts
|
|
86
|
-
type TenantFieldType = 'objectId' | 'string';
|
|
87
87
|
interface SchemaBuildConfig {
|
|
88
|
-
/**
|
|
89
|
-
|
|
88
|
+
/** Canonical resolved tenant config. Engine plumbs this everywhere. */
|
|
89
|
+
tenant: ResolvedTenantConfig;
|
|
90
90
|
/** Optional Mongoose model namespace for hosts with generic model names. */
|
|
91
91
|
modelPrefix?: string | undefined;
|
|
92
92
|
}
|
|
@@ -1515,19 +1515,13 @@ interface CrmShape {
|
|
|
1515
1515
|
* Threaded into both schemas and `multiTenantPlugin` so they stay in
|
|
1516
1516
|
* sync (PACKAGE_RULES §9.2).
|
|
1517
1517
|
*/
|
|
1518
|
-
|
|
1518
|
+
tenant?: boolean | TenantConfig | undefined;
|
|
1519
1519
|
/**
|
|
1520
1520
|
* Prefix for Mongoose model registry names (for example `Crm` produces
|
|
1521
1521
|
* `CrmAccount`). Use in large hosts where generic names such as `Account`
|
|
1522
1522
|
* are already owned by accounting or identity modules.
|
|
1523
1523
|
*/
|
|
1524
1524
|
modelPrefix?: string | undefined;
|
|
1525
|
-
/**
|
|
1526
|
-
* When `true` (default), wire `multiTenantPlugin` on every repository
|
|
1527
|
-
* with `required: true`. Set `false` only for fixtures / smoke tests
|
|
1528
|
-
* that intentionally drop the scope.
|
|
1529
|
-
*/
|
|
1530
|
-
multiTenant?: boolean | undefined;
|
|
1531
1525
|
/**
|
|
1532
1526
|
* Progressive feature bundles (PACKAGE_RULES §22). Omit for the
|
|
1533
1527
|
* all-enabled engine. When provided, unspecified modules are disabled;
|
|
@@ -1578,8 +1572,7 @@ interface CrmRuntime {
|
|
|
1578
1572
|
}
|
|
1579
1573
|
interface ResolvedCrmConfig extends SchemaBuildConfig {
|
|
1580
1574
|
connection: Connection;
|
|
1581
|
-
|
|
1582
|
-
multiTenant: boolean;
|
|
1575
|
+
tenant: ResolvedTenantConfig;
|
|
1583
1576
|
forceRecreate: boolean;
|
|
1584
1577
|
autoIndex: boolean;
|
|
1585
1578
|
modules: ResolvedCrmModules;
|
|
@@ -3203,4 +3196,4 @@ declare namespace index_d_exports {
|
|
|
3203
3196
|
export { AddNoteDto, CancelActivityDto, CloseLossDto, CloseWinDto, CompleteCampaignDto, ConfirmSubscriptionDto, ConvertLeadDto, CreateAccountDto, CreateCampaignDto, CreateContactDto, CreateLeadDto, CreateLossReasonDto, CreatePipelineDto, CreateSequenceInput, LeadFilterDto, MoveStageDto, OpenOpportunityDto, OpportunityFilterDto, PlanActivityDto, RecordEmailEventDto, SequenceFilterInput, StageInputDto, SubscribeDto, SuppressDto, UnsubscribeDto, UnsuppressDto, UpdateAccountDto, UpdateCampaignDto, UpdateContactDto, UpdateLeadDto, UpdateLossReasonDto, UpdateNoteDto, UpdatePipelineDto, UpdateSequenceInput, accountFilterSchema, activityFilterSchema, addNoteSchema, campaignFilterSchema, cancelActivitySchema, closeLossSchema, closeWinSchema, completeCampaignSchema, confirmSubscriptionSchema, contactFilterSchema, convertLeadSchema, createAccountSchema, createCampaignSchema, createContactSchema, createLeadSchema, createLossReasonSchema, createPipelineSchema, createSequenceSchema, currencyCodeSchema, emailEventFilterSchema, leadFilterSchema, lossReasonFilterSchema, metadataSchema, moneyAmountSchema, moveStageSchema, noteFilterSchema, objectIdLike, openOpportunitySchema, opportunityFilterSchema, pipelineFilterSchema, planActivitySchema, recordEmailEventSchema, sequenceFilterSchema, sequenceStepSchema, stageInputSchemaExport, subscribeSchema, subscriptionFilterSchema, suppressSchema, suppressionFilterSchema, tagsSchema, unsubscribeSchema, unsuppressSchema, updateAccountSchema, updateActivitySchema, updateCampaignSchema, updateContactSchema, updateLeadSchema, updateLossReasonSchema, updateNoteSchema, updatePipelineSchema, updateSequenceSchema };
|
|
3204
3197
|
}
|
|
3205
3198
|
//#endregion
|
|
3206
|
-
export { ACTIVITY_MACHINE, ACTIVITY_STATUSES, ACTIVITY_TRANSITIONS, ACTIVITY_TYPES, ALL_CRM_MODULES, AccountAssigned, AccountCreated, AccountEnriched, AccountMerged, AccountRepository, AccountRepositoryDeps, AccountUpdated, ActivityCancelled, ActivityCompleted, ActivityOutcomeRecorded, ActivityPlanned, ActivityRepository, ActivityRepositoryDeps, ActivityRescheduled, type ActivityStatus, type ActivityType, AlreadyEnrolledError, AutomationCrmModels, AutomationCrmRepositories, CAMPAIGN_MACHINE, CAMPAIGN_STATUSES, CAMPAIGN_TRANSITIONS, CONVERSATION_CHANNELS, CONVERSATION_PARTICIPANT_KINDS, CONVERSATION_STATUSES, CRM_EVENTS, CRM_MODEL_NAMES, CampaignActivated, CampaignArchived, CampaignCompleted, CampaignCreated, CampaignPaused, CampaignRepository, CampaignRepositoryDeps, CampaignResumed, type CampaignStatus, CloseInput, ContactAnonymized, ContactAssigned, ContactCreated, ContactEmailFormatError, ContactEnriched, ContactMergeRepoint, ContactMerged, ContactRepository, ContactRepositoryDeps, ContactUpdated, ConversationAssigned, type ConversationChannel, ConversationCreated, type ConversationParticipantKind, ConversationReopened, ConversationRepository, ConversationRepositoryDeps, ConversationResolved, type ConversationStatus, ConversionRow, ConvertLeadInput, ConvertResult, CreateCrmRepositoriesDeps, CrmBlueprint, CrmContext, CrmEngine, CrmEngineBase, CrmError, CrmEventDefinition, CrmEventDispatcher, type CrmEventDispatcherOptions, CrmEventName, CrmEventSchema, CrmLogger, CrmModels, CrmModuleView, CrmModulesConfig, CrmRepositories, CrmRuntime, CrmShape, DefineCrmModelsConfig, DuplicateCampaignError, DuplicateContactError, DuplicateLeadError, DuplicateSubscriptionError, EMAIL_EVENT_KINDS, EMAIL_EVENT_SUBJECT_KINDS, ENROLLMENT_MACHINE, ENROLLMENT_STATUSES, ENROLLMENT_SUBJECT_KINDS, ENROLLMENT_TRANSITIONS, EVENT_DERIVED_SUPPRESSION_REASONS, EmailEventBounced, EmailEventClicked, EmailEventComplained, type EmailEventKind, EmailEventOpened, EmailEventRepository, EmailEventRepositoryDeps, EmailEventSubjectKind, EmailEventUnsubscribed, EmptyPipelineError, EngagementCrmModels, EngagementCrmRepositories, EnrollInput, EnrollmentCompleted, EnrollmentCreated, EnrollmentExitReason, EnrollmentExited, EnrollmentPaused, EnrollmentRepository, EnrollmentRepositoryDeps, EnrollmentResumed, type EnrollmentStatus, EnrollmentStepSent, EnrollmentSubjectKind, ForecastOptions, ForecastRow, FullCrmModuleViews, IAccountDocument, IActivityDocument, ICampaignDocument, ICampaignStatusEntry, IContactDocument, IContactInfoEmbedded, IContactName, IConversationDocument, IDelayStep, IEmailEventDocument, IEnrollmentDocument, IEnrollmentStatusEntry, ILeadDocument, ILeadEngagement, ILeadStatusEntry, ILossReasonDocument, IMessageAttachment, IMessageDocument, INoteDocument, IOpportunityDocument, IOpportunityStatusEntry, IPipelineDocument, ISendEmailStep, ISequenceDocument, ISequenceStatusEntry, IStageDocument, IStepResult, ISubscriptionDocument, ISubscriptionStatusEntry, ISuppressionDocument, InProcessCrmBus, type InternalCrmContext, InvalidActivityTransitionError, InvalidCampaignTransitionError, InvalidConfirmationTokenError, InvalidEnrollmentTransitionError, InvalidLeadTransitionError, InvalidOpportunityTransitionError, InvalidSequenceTransitionError, InvalidSubscriptionTransitionError, InvalidUnsubscribeTokenError, LEAD_MACHINE, LEAD_STATUSES, LEAD_TRANSITIONS, LeadAlreadyConvertedError, LeadAnonymized, LeadAssigned, LeadConverted, LeadCreated, LeadDisqualified, LeadEmailFormatError, LeadEngagementSignal, LeadEnriched, LeadInput, LeadRepository, LeadRepositoryDeps, LeadScored, LeadScorer, LeadService, LeadServiceDeps, type LeadStatus, LeadStatusChanged, LossReasonRepository, LossReasonRepositoryDeps, MESSAGE_DELIVERY_STATUSES, MESSAGE_DIRECTIONS, MergeAccountInput, MergeContactInput, type MessageDeliveryStatus, type MessageDirection, MessageReceived, MessageRepository, MessageRepositoryDeps, MessageSent, ModularCrmEngine, ModularCrmModels, ModularCrmModuleViews, ModularCrmRepositories, MoveStageInput, NotFoundError, NoteAdded, NoteRepository, NoteRepositoryDeps, OPPORTUNITY_MACHINE, OPPORTUNITY_STATUSES, OPPORTUNITY_TRANSITIONS, OpenConversationInput, OpenOpportunityInput, OpportunityAbandoned, OpportunityAmountChanged, OpportunityAssigned, OpportunityCreated, OpportunityExtended, OpportunityForecast, OpportunityLost, OpportunityRepository, OpportunityRepositoryDeps, OpportunityStageAdvanced, OpportunityStageRegressed, type OpportunityStatus, OpportunityWon, PENDING_EVENTS, PipelineArchived, PipelineCreated, PipelineRepository, PipelineRepositoryDeps, RecordEmailEventInput, RecordMessageInput, ReplySubject, ResolvedCrmConfig, ResolvedCrmModules, type RunUnitConfig, SEQUENCE_MACHINE, SEQUENCE_STATUSES, SEQUENCE_TRANSITIONS, SUBJECT_KINDS, SUBSCRIPTION_MACHINE, SUBSCRIPTION_STATUSES, SUBSCRIPTION_TRANSITIONS, SUPPRESSION_REASONS, SalesCrmModels, SalesCrmRepositories, SchemaBuildConfig, SequenceActivated, SequenceArchived, SequenceCreated, SequencePaused, SequenceRepository, SequenceRepositoryDeps, type SequenceStatus, SequenceStep, StageNotInPipelineError, type StatusChangeEntry, type StatusHistory, StepResultStatus, type SubjectKind, SubscribeInput, SubscriptionBounced, SubscriptionComplained, SubscriptionConfirmed, SubscriptionCreated, SubscriptionRepository, SubscriptionRepositoryDeps, type SubscriptionStatus, SubscriptionUnsubscribed, SuppressInput, SuppressionAdded, SuppressionEmailFormatError, type SuppressionReason, SuppressionRemoved, SuppressionRepository, SuppressionRepositoryDeps,
|
|
3199
|
+
export { ACTIVITY_MACHINE, ACTIVITY_STATUSES, ACTIVITY_TRANSITIONS, ACTIVITY_TYPES, ALL_CRM_MODULES, AccountAssigned, AccountCreated, AccountEnriched, AccountMerged, AccountRepository, AccountRepositoryDeps, AccountUpdated, ActivityCancelled, ActivityCompleted, ActivityOutcomeRecorded, ActivityPlanned, ActivityRepository, ActivityRepositoryDeps, ActivityRescheduled, type ActivityStatus, type ActivityType, AlreadyEnrolledError, AutomationCrmModels, AutomationCrmRepositories, CAMPAIGN_MACHINE, CAMPAIGN_STATUSES, CAMPAIGN_TRANSITIONS, CONVERSATION_CHANNELS, CONVERSATION_PARTICIPANT_KINDS, CONVERSATION_STATUSES, CRM_EVENTS, CRM_MODEL_NAMES, CampaignActivated, CampaignArchived, CampaignCompleted, CampaignCreated, CampaignPaused, CampaignRepository, CampaignRepositoryDeps, CampaignResumed, type CampaignStatus, CloseInput, ContactAnonymized, ContactAssigned, ContactCreated, ContactEmailFormatError, ContactEnriched, ContactMergeRepoint, ContactMerged, ContactRepository, ContactRepositoryDeps, ContactUpdated, ConversationAssigned, type ConversationChannel, ConversationCreated, type ConversationParticipantKind, ConversationReopened, ConversationRepository, ConversationRepositoryDeps, ConversationResolved, type ConversationStatus, ConversionRow, ConvertLeadInput, ConvertResult, CreateCrmRepositoriesDeps, CrmBlueprint, CrmContext, CrmEngine, CrmEngineBase, CrmError, CrmEventDefinition, CrmEventDispatcher, type CrmEventDispatcherOptions, CrmEventName, CrmEventSchema, CrmLogger, CrmModels, CrmModuleView, CrmModulesConfig, CrmRepositories, CrmRuntime, CrmShape, DefineCrmModelsConfig, DuplicateCampaignError, DuplicateContactError, DuplicateLeadError, DuplicateSubscriptionError, EMAIL_EVENT_KINDS, EMAIL_EVENT_SUBJECT_KINDS, ENROLLMENT_MACHINE, ENROLLMENT_STATUSES, ENROLLMENT_SUBJECT_KINDS, ENROLLMENT_TRANSITIONS, EVENT_DERIVED_SUPPRESSION_REASONS, EmailEventBounced, EmailEventClicked, EmailEventComplained, type EmailEventKind, EmailEventOpened, EmailEventRepository, EmailEventRepositoryDeps, EmailEventSubjectKind, EmailEventUnsubscribed, EmptyPipelineError, EngagementCrmModels, EngagementCrmRepositories, EnrollInput, EnrollmentCompleted, EnrollmentCreated, EnrollmentExitReason, EnrollmentExited, EnrollmentPaused, EnrollmentRepository, EnrollmentRepositoryDeps, EnrollmentResumed, type EnrollmentStatus, EnrollmentStepSent, EnrollmentSubjectKind, ForecastOptions, ForecastRow, FullCrmModuleViews, IAccountDocument, IActivityDocument, ICampaignDocument, ICampaignStatusEntry, IContactDocument, IContactInfoEmbedded, IContactName, IConversationDocument, IDelayStep, IEmailEventDocument, IEnrollmentDocument, IEnrollmentStatusEntry, ILeadDocument, ILeadEngagement, ILeadStatusEntry, ILossReasonDocument, IMessageAttachment, IMessageDocument, INoteDocument, IOpportunityDocument, IOpportunityStatusEntry, IPipelineDocument, ISendEmailStep, ISequenceDocument, ISequenceStatusEntry, IStageDocument, IStepResult, ISubscriptionDocument, ISubscriptionStatusEntry, ISuppressionDocument, InProcessCrmBus, type InternalCrmContext, InvalidActivityTransitionError, InvalidCampaignTransitionError, InvalidConfirmationTokenError, InvalidEnrollmentTransitionError, InvalidLeadTransitionError, InvalidOpportunityTransitionError, InvalidSequenceTransitionError, InvalidSubscriptionTransitionError, InvalidUnsubscribeTokenError, LEAD_MACHINE, LEAD_STATUSES, LEAD_TRANSITIONS, LeadAlreadyConvertedError, LeadAnonymized, LeadAssigned, LeadConverted, LeadCreated, LeadDisqualified, LeadEmailFormatError, LeadEngagementSignal, LeadEnriched, LeadInput, LeadRepository, LeadRepositoryDeps, LeadScored, LeadScorer, LeadService, LeadServiceDeps, type LeadStatus, LeadStatusChanged, LossReasonRepository, LossReasonRepositoryDeps, MESSAGE_DELIVERY_STATUSES, MESSAGE_DIRECTIONS, MergeAccountInput, MergeContactInput, type MessageDeliveryStatus, type MessageDirection, MessageReceived, MessageRepository, MessageRepositoryDeps, MessageSent, ModularCrmEngine, ModularCrmModels, ModularCrmModuleViews, ModularCrmRepositories, MoveStageInput, NotFoundError, NoteAdded, NoteRepository, NoteRepositoryDeps, OPPORTUNITY_MACHINE, OPPORTUNITY_STATUSES, OPPORTUNITY_TRANSITIONS, OpenConversationInput, OpenOpportunityInput, OpportunityAbandoned, OpportunityAmountChanged, OpportunityAssigned, OpportunityCreated, OpportunityExtended, OpportunityForecast, OpportunityLost, OpportunityRepository, OpportunityRepositoryDeps, OpportunityStageAdvanced, OpportunityStageRegressed, type OpportunityStatus, OpportunityWon, PENDING_EVENTS, PipelineArchived, PipelineCreated, PipelineRepository, PipelineRepositoryDeps, RecordEmailEventInput, RecordMessageInput, ReplySubject, ResolvedCrmConfig, ResolvedCrmModules, type RunUnitConfig, SEQUENCE_MACHINE, SEQUENCE_STATUSES, SEQUENCE_TRANSITIONS, SUBJECT_KINDS, SUBSCRIPTION_MACHINE, SUBSCRIPTION_STATUSES, SUBSCRIPTION_TRANSITIONS, SUPPRESSION_REASONS, SalesCrmModels, SalesCrmRepositories, SchemaBuildConfig, SequenceActivated, SequenceArchived, SequenceCreated, SequencePaused, SequenceRepository, SequenceRepositoryDeps, type SequenceStatus, SequenceStep, StageNotInPipelineError, type StatusChangeEntry, type StatusHistory, StepResultStatus, type SubjectKind, SubscribeInput, SubscriptionBounced, SubscriptionComplained, SubscriptionConfirmed, SubscriptionCreated, SubscriptionRepository, SubscriptionRepositoryDeps, type SubscriptionStatus, SubscriptionUnsubscribed, SuppressInput, SuppressionAdded, SuppressionEmailFormatError, type SuppressionReason, SuppressionRemoved, SuppressionRepository, SuppressionRepositoryDeps, UnmanagedSessionError, _ModelTypes, appendStatus, buildAccountSchema, buildActivitySchema, buildCampaignSchema, buildContactSchema, buildConversationSchema, buildEmailEventSchema, buildEnrollmentSchema, buildLeadSchema, buildLossReasonSchema, buildMessageSchema, buildNoteSchema, buildOpportunitySchema, buildPipelineSchema, buildSequenceSchema, buildSubscriptionSchema, buildSuppressionSchema, canTransitionActivity, canTransitionCampaign, canTransitionEnrollment, canTransitionLead, canTransitionOpportunity, canTransitionSequence, canTransitionSubscription, composeScorers, createAccountModel, createActivityModel, createCampaignModel, createContactModel, createConversationModel, createCrmRepositories, createEmailEventModel, createEvent, createLeadModel, createLossReasonModel, createMessageModel, createNoteModel, createOpportunityModel, createPipelineModel, createSubscriptionModel, crmEmailKey, crmEventDefinitions, crmModelName, defaultLeadScorer, defineCrm, defineCrmModels, flushPending, forecast, lastTransitionTo, latestEntry, pipelineConversion, resolveCrmModules, runUnit, suppressionEmailKey, tenantFieldDef, timeInStatus, index_d_exports as validators };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createEvent as createEvent$1 } from "@classytic/primitives/events";
|
|
|
6
6
|
import { IllegalTransitionError, defineStateMachine } from "@classytic/primitives/state-machine";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { InProcessEventBus } from "@classytic/primitives/event-infra";
|
|
9
|
+
import { assertNoLegacyTenantKeys, resolveTenantConfig } from "@classytic/repo-core/tenant";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
import { civilDateOf } from "@classytic/primitives/timezone";
|
|
11
12
|
import { CURRENCY_PATTERN } from "@classytic/primitives/currency";
|
|
@@ -51,15 +52,14 @@ function crmModelName(config, base) {
|
|
|
51
52
|
* `organizationId` — never write the raw type in a schema file.
|
|
52
53
|
*/
|
|
53
54
|
function tenantFieldDef(config) {
|
|
54
|
-
|
|
55
|
+
const { tenant } = config;
|
|
56
|
+
return tenant.fieldType === "objectId" ? {
|
|
55
57
|
type: Schema.Types.ObjectId,
|
|
56
|
-
required: true,
|
|
57
|
-
ref:
|
|
58
|
-
index: true
|
|
58
|
+
...tenant.enabled && tenant.required ? { required: true } : {},
|
|
59
|
+
...tenant.ref ? { ref: tenant.ref } : {}
|
|
59
60
|
} : {
|
|
60
61
|
type: String,
|
|
61
|
-
required: true
|
|
62
|
-
index: true
|
|
62
|
+
...tenant.enabled && tenant.required ? { required: true } : {}
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
//#endregion
|
|
@@ -105,7 +105,7 @@ const stageSubSchema = new Schema({
|
|
|
105
105
|
}, { _id: true });
|
|
106
106
|
function buildPipelineSchema(config) {
|
|
107
107
|
const schema = new Schema({
|
|
108
|
-
|
|
108
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
109
109
|
name: {
|
|
110
110
|
type: String,
|
|
111
111
|
required: true,
|
|
@@ -129,11 +129,11 @@ function buildPipelineSchema(config) {
|
|
|
129
129
|
metadata: { type: Schema.Types.Mixed }
|
|
130
130
|
}, { timestamps: true });
|
|
131
131
|
schema.index({
|
|
132
|
-
|
|
132
|
+
[config.tenant.tenantField]: 1,
|
|
133
133
|
isArchived: 1
|
|
134
134
|
});
|
|
135
135
|
schema.index({
|
|
136
|
-
|
|
136
|
+
[config.tenant.tenantField]: 1,
|
|
137
137
|
name: 1
|
|
138
138
|
});
|
|
139
139
|
return schema;
|
|
@@ -539,7 +539,7 @@ const engagementSchema = new Schema({
|
|
|
539
539
|
}, { _id: false });
|
|
540
540
|
function buildLeadSchema(config) {
|
|
541
541
|
const schema = new Schema({
|
|
542
|
-
|
|
542
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
543
543
|
fullName: {
|
|
544
544
|
type: String,
|
|
545
545
|
required: true,
|
|
@@ -634,18 +634,18 @@ function buildLeadSchema(config) {
|
|
|
634
634
|
metadata: { type: Schema.Types.Mixed }
|
|
635
635
|
}, { timestamps: true });
|
|
636
636
|
schema.index({
|
|
637
|
-
|
|
637
|
+
[config.tenant.tenantField]: 1,
|
|
638
638
|
email: 1
|
|
639
639
|
}, {
|
|
640
640
|
unique: true,
|
|
641
641
|
partialFilterExpression: { email: { $type: "string" } }
|
|
642
642
|
});
|
|
643
643
|
schema.index({
|
|
644
|
-
|
|
644
|
+
[config.tenant.tenantField]: 1,
|
|
645
645
|
createdAt: -1
|
|
646
646
|
});
|
|
647
647
|
schema.index({
|
|
648
|
-
|
|
648
|
+
[config.tenant.tenantField]: 1,
|
|
649
649
|
score: -1
|
|
650
650
|
});
|
|
651
651
|
return schema;
|
|
@@ -705,7 +705,7 @@ const statusEntrySchema$4 = new Schema({
|
|
|
705
705
|
}, { _id: false });
|
|
706
706
|
function buildOpportunitySchema(config) {
|
|
707
707
|
const schema = new Schema({
|
|
708
|
-
|
|
708
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
709
709
|
name: {
|
|
710
710
|
type: String,
|
|
711
711
|
required: true,
|
|
@@ -775,17 +775,17 @@ function buildOpportunitySchema(config) {
|
|
|
775
775
|
metadata: { type: Schema.Types.Mixed }
|
|
776
776
|
}, { timestamps: true });
|
|
777
777
|
schema.index({
|
|
778
|
-
|
|
778
|
+
[config.tenant.tenantField]: 1,
|
|
779
779
|
pipelineId: 1,
|
|
780
780
|
stageId: 1
|
|
781
781
|
});
|
|
782
782
|
schema.index({
|
|
783
|
-
|
|
783
|
+
[config.tenant.tenantField]: 1,
|
|
784
784
|
status: 1,
|
|
785
785
|
expectedCloseAt: 1
|
|
786
786
|
});
|
|
787
787
|
schema.index({
|
|
788
|
-
|
|
788
|
+
[config.tenant.tenantField]: 1,
|
|
789
789
|
ownerId: 1,
|
|
790
790
|
status: 1
|
|
791
791
|
});
|
|
@@ -884,7 +884,7 @@ const contactInfoSchema$1 = new Schema({
|
|
|
884
884
|
}, { _id: false });
|
|
885
885
|
function buildContactSchema(config) {
|
|
886
886
|
const schema = new Schema({
|
|
887
|
-
|
|
887
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
888
888
|
name: {
|
|
889
889
|
type: nameSchema,
|
|
890
890
|
required: true
|
|
@@ -918,22 +918,22 @@ function buildContactSchema(config) {
|
|
|
918
918
|
toObject: { virtuals: true }
|
|
919
919
|
});
|
|
920
920
|
schema.index({
|
|
921
|
-
|
|
921
|
+
[config.tenant.tenantField]: 1,
|
|
922
922
|
"contact.email": 1
|
|
923
923
|
}, {
|
|
924
924
|
unique: true,
|
|
925
925
|
partialFilterExpression: { "contact.email": { $type: "string" } }
|
|
926
926
|
});
|
|
927
927
|
schema.index({
|
|
928
|
-
|
|
928
|
+
[config.tenant.tenantField]: 1,
|
|
929
929
|
accountId: 1
|
|
930
930
|
});
|
|
931
931
|
schema.index({
|
|
932
|
-
|
|
932
|
+
[config.tenant.tenantField]: 1,
|
|
933
933
|
createdAt: -1
|
|
934
934
|
});
|
|
935
935
|
schema.index({
|
|
936
|
-
|
|
936
|
+
[config.tenant.tenantField]: 1,
|
|
937
937
|
tags: 1
|
|
938
938
|
});
|
|
939
939
|
schema.virtual("fullName").get(function() {
|
|
@@ -954,7 +954,7 @@ function createContactModel(connection, config) {
|
|
|
954
954
|
*/
|
|
955
955
|
function buildAccountSchema(config) {
|
|
956
956
|
const schema = new Schema({
|
|
957
|
-
|
|
957
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
958
958
|
name: {
|
|
959
959
|
type: String,
|
|
960
960
|
required: true,
|
|
@@ -993,15 +993,15 @@ function buildAccountSchema(config) {
|
|
|
993
993
|
metadata: { type: Schema.Types.Mixed }
|
|
994
994
|
}, { timestamps: true });
|
|
995
995
|
schema.index({
|
|
996
|
-
|
|
996
|
+
[config.tenant.tenantField]: 1,
|
|
997
997
|
domain: 1
|
|
998
998
|
});
|
|
999
999
|
schema.index({
|
|
1000
|
-
|
|
1000
|
+
[config.tenant.tenantField]: 1,
|
|
1001
1001
|
createdAt: -1
|
|
1002
1002
|
});
|
|
1003
1003
|
schema.index({
|
|
1004
|
-
|
|
1004
|
+
[config.tenant.tenantField]: 1,
|
|
1005
1005
|
tags: 1
|
|
1006
1006
|
});
|
|
1007
1007
|
return schema;
|
|
@@ -1067,7 +1067,7 @@ const statusEntrySchema$3 = new Schema({
|
|
|
1067
1067
|
}, { _id: false });
|
|
1068
1068
|
function buildCampaignSchema(config) {
|
|
1069
1069
|
const schema = new Schema({
|
|
1070
|
-
|
|
1070
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1071
1071
|
name: {
|
|
1072
1072
|
type: String,
|
|
1073
1073
|
required: true,
|
|
@@ -1124,23 +1124,23 @@ function buildCampaignSchema(config) {
|
|
|
1124
1124
|
metadata: { type: Schema.Types.Mixed }
|
|
1125
1125
|
}, { timestamps: true });
|
|
1126
1126
|
schema.index({
|
|
1127
|
-
|
|
1127
|
+
[config.tenant.tenantField]: 1,
|
|
1128
1128
|
name: 1
|
|
1129
1129
|
}, { unique: true });
|
|
1130
1130
|
schema.index({
|
|
1131
|
-
|
|
1131
|
+
[config.tenant.tenantField]: 1,
|
|
1132
1132
|
status: 1
|
|
1133
1133
|
});
|
|
1134
1134
|
schema.index({
|
|
1135
|
-
|
|
1135
|
+
[config.tenant.tenantField]: 1,
|
|
1136
1136
|
startDate: 1
|
|
1137
1137
|
});
|
|
1138
1138
|
schema.index({
|
|
1139
|
-
|
|
1139
|
+
[config.tenant.tenantField]: 1,
|
|
1140
1140
|
createdAt: -1
|
|
1141
1141
|
});
|
|
1142
1142
|
schema.index({
|
|
1143
|
-
|
|
1143
|
+
[config.tenant.tenantField]: 1,
|
|
1144
1144
|
tags: 1
|
|
1145
1145
|
});
|
|
1146
1146
|
return schema;
|
|
@@ -1216,7 +1216,7 @@ const statusEntrySchema$2 = new Schema({
|
|
|
1216
1216
|
}, { _id: false });
|
|
1217
1217
|
function buildSubscriptionSchema(config) {
|
|
1218
1218
|
const schema = new Schema({
|
|
1219
|
-
|
|
1219
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1220
1220
|
contactId: {
|
|
1221
1221
|
type: Schema.Types.ObjectId,
|
|
1222
1222
|
ref: crmModelName(config, "Contact"),
|
|
@@ -1285,12 +1285,12 @@ function buildSubscriptionSchema(config) {
|
|
|
1285
1285
|
collection: "email_subscriptions"
|
|
1286
1286
|
});
|
|
1287
1287
|
schema.index({
|
|
1288
|
-
|
|
1288
|
+
[config.tenant.tenantField]: 1,
|
|
1289
1289
|
campaignId: 1,
|
|
1290
1290
|
status: 1
|
|
1291
1291
|
});
|
|
1292
1292
|
schema.index({
|
|
1293
|
-
|
|
1293
|
+
[config.tenant.tenantField]: 1,
|
|
1294
1294
|
contactId: 1,
|
|
1295
1295
|
campaignId: 1
|
|
1296
1296
|
}, {
|
|
@@ -1298,7 +1298,7 @@ function buildSubscriptionSchema(config) {
|
|
|
1298
1298
|
partialFilterExpression: { status: { $in: ["pending", "confirmed"] } }
|
|
1299
1299
|
});
|
|
1300
1300
|
schema.index({
|
|
1301
|
-
|
|
1301
|
+
[config.tenant.tenantField]: 1,
|
|
1302
1302
|
createdAt: -1
|
|
1303
1303
|
});
|
|
1304
1304
|
return schema;
|
|
@@ -1335,7 +1335,7 @@ const SUPPRESSION_REASONS = [...EVENT_DERIVED_SUPPRESSION_REASONS, "manual"];
|
|
|
1335
1335
|
*/
|
|
1336
1336
|
function buildSuppressionSchema(config) {
|
|
1337
1337
|
const schema = new Schema({
|
|
1338
|
-
|
|
1338
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1339
1339
|
email: {
|
|
1340
1340
|
type: String,
|
|
1341
1341
|
required: true,
|
|
@@ -1371,16 +1371,16 @@ function buildSuppressionSchema(config) {
|
|
|
1371
1371
|
metadata: { type: Schema.Types.Mixed }
|
|
1372
1372
|
}, { timestamps: true });
|
|
1373
1373
|
schema.index({
|
|
1374
|
-
|
|
1374
|
+
[config.tenant.tenantField]: 1,
|
|
1375
1375
|
email: 1
|
|
1376
1376
|
}, { unique: true });
|
|
1377
1377
|
schema.index({
|
|
1378
|
-
|
|
1378
|
+
[config.tenant.tenantField]: 1,
|
|
1379
1379
|
reason: 1,
|
|
1380
1380
|
suppressedAt: -1
|
|
1381
1381
|
});
|
|
1382
1382
|
schema.index({
|
|
1383
|
-
|
|
1383
|
+
[config.tenant.tenantField]: 1,
|
|
1384
1384
|
suppressedAt: -1
|
|
1385
1385
|
});
|
|
1386
1386
|
return schema;
|
|
@@ -1441,7 +1441,7 @@ const SUBJECT_KINDS = [
|
|
|
1441
1441
|
*/
|
|
1442
1442
|
function buildActivitySchema(config) {
|
|
1443
1443
|
const schema = new Schema({
|
|
1444
|
-
|
|
1444
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1445
1445
|
type: {
|
|
1446
1446
|
type: String,
|
|
1447
1447
|
enum: ACTIVITY_TYPES,
|
|
@@ -1496,26 +1496,26 @@ function buildActivitySchema(config) {
|
|
|
1496
1496
|
metadata: { type: Schema.Types.Mixed }
|
|
1497
1497
|
}, { timestamps: true });
|
|
1498
1498
|
schema.index({
|
|
1499
|
-
|
|
1499
|
+
[config.tenant.tenantField]: 1,
|
|
1500
1500
|
subjectKind: 1,
|
|
1501
1501
|
subjectId: 1
|
|
1502
1502
|
});
|
|
1503
1503
|
schema.index({
|
|
1504
|
-
|
|
1504
|
+
[config.tenant.tenantField]: 1,
|
|
1505
1505
|
status: 1,
|
|
1506
1506
|
dueDate: 1
|
|
1507
1507
|
});
|
|
1508
1508
|
schema.index({
|
|
1509
|
-
|
|
1509
|
+
[config.tenant.tenantField]: 1,
|
|
1510
1510
|
ownerId: 1,
|
|
1511
1511
|
status: 1
|
|
1512
1512
|
});
|
|
1513
1513
|
schema.index({
|
|
1514
|
-
|
|
1514
|
+
[config.tenant.tenantField]: 1,
|
|
1515
1515
|
type: 1
|
|
1516
1516
|
});
|
|
1517
1517
|
schema.index({
|
|
1518
|
-
|
|
1518
|
+
[config.tenant.tenantField]: 1,
|
|
1519
1519
|
createdAt: -1
|
|
1520
1520
|
});
|
|
1521
1521
|
return schema;
|
|
@@ -1533,7 +1533,7 @@ function createActivityModel(connection, config) {
|
|
|
1533
1533
|
*/
|
|
1534
1534
|
function buildNoteSchema(config) {
|
|
1535
1535
|
const schema = new Schema({
|
|
1536
|
-
|
|
1536
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1537
1537
|
subjectKind: {
|
|
1538
1538
|
type: String,
|
|
1539
1539
|
enum: SUBJECT_KINDS,
|
|
@@ -1562,7 +1562,7 @@ function buildNoteSchema(config) {
|
|
|
1562
1562
|
metadata: { type: Schema.Types.Mixed }
|
|
1563
1563
|
}, { timestamps: true });
|
|
1564
1564
|
schema.index({
|
|
1565
|
-
|
|
1565
|
+
[config.tenant.tenantField]: 1,
|
|
1566
1566
|
subjectKind: 1,
|
|
1567
1567
|
subjectId: 1,
|
|
1568
1568
|
pinned: -1,
|
|
@@ -1583,7 +1583,7 @@ function createNoteModel(connection, config) {
|
|
|
1583
1583
|
*/
|
|
1584
1584
|
function buildLossReasonSchema(config) {
|
|
1585
1585
|
const schema = new Schema({
|
|
1586
|
-
|
|
1586
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1587
1587
|
name: {
|
|
1588
1588
|
type: String,
|
|
1589
1589
|
required: true,
|
|
@@ -1607,7 +1607,7 @@ function buildLossReasonSchema(config) {
|
|
|
1607
1607
|
metadata: { type: Schema.Types.Mixed }
|
|
1608
1608
|
}, { timestamps: true });
|
|
1609
1609
|
schema.index({
|
|
1610
|
-
|
|
1610
|
+
[config.tenant.tenantField]: 1,
|
|
1611
1611
|
isActive: 1,
|
|
1612
1612
|
sequence: 1
|
|
1613
1613
|
});
|
|
@@ -1664,7 +1664,7 @@ const EMAIL_EVENT_KINDS = [
|
|
|
1664
1664
|
const EMAIL_EVENT_SUBJECT_KINDS = ["contact", "subscription"];
|
|
1665
1665
|
function buildEmailEventSchema(config) {
|
|
1666
1666
|
const schema = new Schema({
|
|
1667
|
-
|
|
1667
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1668
1668
|
kind: {
|
|
1669
1669
|
type: String,
|
|
1670
1670
|
enum: EMAIL_EVENT_KINDS,
|
|
@@ -1712,23 +1712,23 @@ function buildEmailEventSchema(config) {
|
|
|
1712
1712
|
updatedAt: false
|
|
1713
1713
|
} });
|
|
1714
1714
|
schema.index({
|
|
1715
|
-
|
|
1715
|
+
[config.tenant.tenantField]: 1,
|
|
1716
1716
|
createdAt: -1
|
|
1717
1717
|
});
|
|
1718
1718
|
schema.index({
|
|
1719
|
-
|
|
1719
|
+
[config.tenant.tenantField]: 1,
|
|
1720
1720
|
subjectKind: 1,
|
|
1721
1721
|
subjectId: 1,
|
|
1722
1722
|
createdAt: -1
|
|
1723
1723
|
});
|
|
1724
1724
|
schema.index({
|
|
1725
|
-
|
|
1725
|
+
[config.tenant.tenantField]: 1,
|
|
1726
1726
|
campaignId: 1,
|
|
1727
1727
|
kind: 1,
|
|
1728
1728
|
createdAt: -1
|
|
1729
1729
|
});
|
|
1730
1730
|
schema.index({
|
|
1731
|
-
|
|
1731
|
+
[config.tenant.tenantField]: 1,
|
|
1732
1732
|
messageId: 1
|
|
1733
1733
|
});
|
|
1734
1734
|
return schema;
|
|
@@ -1783,7 +1783,7 @@ const MESSAGE_DELIVERY_STATUSES = [
|
|
|
1783
1783
|
*/
|
|
1784
1784
|
function buildConversationSchema(config) {
|
|
1785
1785
|
const schema = new Schema({
|
|
1786
|
-
|
|
1786
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1787
1787
|
channel: {
|
|
1788
1788
|
type: String,
|
|
1789
1789
|
enum: CONVERSATION_CHANNELS,
|
|
@@ -1827,22 +1827,22 @@ function buildConversationSchema(config) {
|
|
|
1827
1827
|
metadata: { type: Schema.Types.Mixed }
|
|
1828
1828
|
}, { timestamps: true });
|
|
1829
1829
|
schema.index({
|
|
1830
|
-
|
|
1830
|
+
[config.tenant.tenantField]: 1,
|
|
1831
1831
|
participantKind: 1,
|
|
1832
1832
|
participantId: 1
|
|
1833
1833
|
});
|
|
1834
1834
|
schema.index({
|
|
1835
|
-
|
|
1835
|
+
[config.tenant.tenantField]: 1,
|
|
1836
1836
|
status: 1,
|
|
1837
1837
|
lastMessageAt: -1
|
|
1838
1838
|
});
|
|
1839
1839
|
schema.index({
|
|
1840
|
-
|
|
1840
|
+
[config.tenant.tenantField]: 1,
|
|
1841
1841
|
assigneeId: 1,
|
|
1842
1842
|
status: 1
|
|
1843
1843
|
});
|
|
1844
1844
|
schema.index({
|
|
1845
|
-
|
|
1845
|
+
[config.tenant.tenantField]: 1,
|
|
1846
1846
|
channel: 1,
|
|
1847
1847
|
channelUserId: 1
|
|
1848
1848
|
});
|
|
@@ -1890,7 +1890,7 @@ const attachmentSchema = new Schema({
|
|
|
1890
1890
|
}, { _id: false });
|
|
1891
1891
|
function buildMessageSchema(config) {
|
|
1892
1892
|
const schema = new Schema({
|
|
1893
|
-
|
|
1893
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
1894
1894
|
conversationId: {
|
|
1895
1895
|
type: Schema.Types.ObjectId,
|
|
1896
1896
|
ref: crmModelName(config, "Conversation"),
|
|
@@ -1935,12 +1935,12 @@ function buildMessageSchema(config) {
|
|
|
1935
1935
|
metadata: { type: Schema.Types.Mixed }
|
|
1936
1936
|
}, { timestamps: true });
|
|
1937
1937
|
schema.index({
|
|
1938
|
-
|
|
1938
|
+
[config.tenant.tenantField]: 1,
|
|
1939
1939
|
conversationId: 1,
|
|
1940
1940
|
createdAt: -1
|
|
1941
1941
|
});
|
|
1942
1942
|
schema.index({
|
|
1943
|
-
|
|
1943
|
+
[config.tenant.tenantField]: 1,
|
|
1944
1944
|
externalMessageId: 1
|
|
1945
1945
|
}, {
|
|
1946
1946
|
unique: true,
|
|
@@ -2005,7 +2005,7 @@ const statusEntrySchema$1 = new Schema({
|
|
|
2005
2005
|
}, { _id: false });
|
|
2006
2006
|
function buildSequenceSchema(config) {
|
|
2007
2007
|
const schema = new Schema({
|
|
2008
|
-
|
|
2008
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
2009
2009
|
name: {
|
|
2010
2010
|
type: String,
|
|
2011
2011
|
required: true,
|
|
@@ -2053,11 +2053,11 @@ function buildSequenceSchema(config) {
|
|
|
2053
2053
|
}
|
|
2054
2054
|
}, { timestamps: true });
|
|
2055
2055
|
schema.index({
|
|
2056
|
-
|
|
2056
|
+
[config.tenant.tenantField]: 1,
|
|
2057
2057
|
status: 1
|
|
2058
2058
|
});
|
|
2059
2059
|
schema.index({
|
|
2060
|
-
|
|
2060
|
+
[config.tenant.tenantField]: 1,
|
|
2061
2061
|
createdAt: -1
|
|
2062
2062
|
});
|
|
2063
2063
|
return schema;
|
|
@@ -2137,9 +2137,9 @@ const statusEntrySchema = new Schema({
|
|
|
2137
2137
|
}, { _id: false });
|
|
2138
2138
|
function buildEnrollmentSchema(config) {
|
|
2139
2139
|
const schema = new Schema({
|
|
2140
|
-
|
|
2140
|
+
[config.tenant.tenantField]: tenantFieldDef(config),
|
|
2141
2141
|
sequenceId: {
|
|
2142
|
-
type: config.
|
|
2142
|
+
type: config.tenant.fieldType === "objectId" ? Schema.Types.ObjectId : String,
|
|
2143
2143
|
required: true,
|
|
2144
2144
|
ref: crmModelName(config, "Sequence")
|
|
2145
2145
|
},
|
|
@@ -2149,7 +2149,7 @@ function buildEnrollmentSchema(config) {
|
|
|
2149
2149
|
required: true
|
|
2150
2150
|
},
|
|
2151
2151
|
subjectId: {
|
|
2152
|
-
type: config.
|
|
2152
|
+
type: config.tenant.fieldType === "objectId" ? Schema.Types.ObjectId : String,
|
|
2153
2153
|
required: true
|
|
2154
2154
|
},
|
|
2155
2155
|
status: {
|
|
@@ -2193,23 +2193,23 @@ function buildEnrollmentSchema(config) {
|
|
|
2193
2193
|
}
|
|
2194
2194
|
}, { timestamps: true });
|
|
2195
2195
|
schema.index({
|
|
2196
|
-
|
|
2196
|
+
[config.tenant.tenantField]: 1,
|
|
2197
2197
|
sequenceId: 1,
|
|
2198
2198
|
subjectId: 1,
|
|
2199
2199
|
status: 1
|
|
2200
2200
|
}, { unique: false });
|
|
2201
2201
|
schema.index({
|
|
2202
|
-
|
|
2202
|
+
[config.tenant.tenantField]: 1,
|
|
2203
2203
|
sequenceId: 1,
|
|
2204
2204
|
status: 1
|
|
2205
2205
|
});
|
|
2206
2206
|
schema.index({
|
|
2207
|
-
|
|
2207
|
+
[config.tenant.tenantField]: 1,
|
|
2208
2208
|
subjectKind: 1,
|
|
2209
2209
|
subjectId: 1
|
|
2210
2210
|
});
|
|
2211
2211
|
schema.index({
|
|
2212
|
-
|
|
2212
|
+
[config.tenant.tenantField]: 1,
|
|
2213
2213
|
createdAt: -1
|
|
2214
2214
|
});
|
|
2215
2215
|
return schema;
|
|
@@ -3089,8 +3089,33 @@ var OpportunityRepository = class extends Repository {
|
|
|
3089
3089
|
opportunityId: id
|
|
3090
3090
|
});
|
|
3091
3091
|
if (amount === existing.amount) return existing;
|
|
3092
|
-
|
|
3093
|
-
|
|
3092
|
+
/**
|
|
3093
|
+
* `claim` with from === to — the status predicate rides the WRITE's filter.
|
|
3094
|
+
*
|
|
3095
|
+
* The `status === 'open'` check above is read-then-write: an opportunity closed
|
|
3096
|
+
* between the two still had its amount changed, so the figure that fed closed-won
|
|
3097
|
+
* reporting depended on scheduling. Not money movement, but the forecast is what
|
|
3098
|
+
* pipeline decisions are made against, and a closed deal whose amount silently
|
|
3099
|
+
* moves after the close is unauditable. The miss disambiguates below: a genuine
|
|
3100
|
+
* status change gets the SAME transition error the pre-check throws, so callers
|
|
3101
|
+
* see one contract whether they lost a race or arrived late.
|
|
3102
|
+
*/
|
|
3103
|
+
const updated = await this.claim(id, {
|
|
3104
|
+
from: "open",
|
|
3105
|
+
to: "open"
|
|
3106
|
+
}, { $set: { amount } }, ctx);
|
|
3107
|
+
if (!updated) {
|
|
3108
|
+
const current = await this.getById(id, {
|
|
3109
|
+
...ctx,
|
|
3110
|
+
throwOnNotFound: false
|
|
3111
|
+
});
|
|
3112
|
+
if (!current) throw new NotFoundError("Opportunity", id);
|
|
3113
|
+
throw new InvalidOpportunityTransitionError({
|
|
3114
|
+
from: current.status,
|
|
3115
|
+
to: current.status,
|
|
3116
|
+
opportunityId: id
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3094
3119
|
await this.events.dispatch(ctx, createEvent(CRM_EVENTS.OPPORTUNITY_AMOUNT_CHANGED, {
|
|
3095
3120
|
opportunityId: id,
|
|
3096
3121
|
amount,
|
|
@@ -4497,11 +4522,11 @@ function createCrmRepositories(models, deps) {
|
|
|
4497
4522
|
const { events, config } = deps;
|
|
4498
4523
|
const pagination = config.pagination;
|
|
4499
4524
|
const plugins = [methodRegistryPlugin()];
|
|
4500
|
-
if (config.
|
|
4501
|
-
tenantField:
|
|
4502
|
-
contextKey:
|
|
4503
|
-
required:
|
|
4504
|
-
fieldType: config.
|
|
4525
|
+
if (config.tenant.enabled) plugins.push(multiTenantPlugin({
|
|
4526
|
+
tenantField: config.tenant.tenantField,
|
|
4527
|
+
contextKey: config.tenant.contextKey,
|
|
4528
|
+
required: config.tenant.required,
|
|
4529
|
+
fieldType: config.tenant.fieldType
|
|
4505
4530
|
}));
|
|
4506
4531
|
plugins.push(aggregateHelpersPlugin());
|
|
4507
4532
|
if (config.plugins.length > 0) plugins.push(...config.plugins);
|
|
@@ -4703,13 +4728,29 @@ async function runUnit(config, ctx, body) {
|
|
|
4703
4728
|
//#endregion
|
|
4704
4729
|
//#region src/engine/define-crm.ts
|
|
4705
4730
|
function defineCrm(shape = {}) {
|
|
4706
|
-
|
|
4707
|
-
|
|
4731
|
+
/**
|
|
4732
|
+
* The pre-consolidation tenant keys are REFUSED, never absorbed.
|
|
4733
|
+
*
|
|
4734
|
+
* This entry reads `shape.tenant`, so a leftover `multiTenant` is invisible and
|
|
4735
|
+
* the `?? true` default applies repo-core's defaults instead. Both outcomes
|
|
4736
|
+
* return plausible data:
|
|
4737
|
+
* - `multiTenant: false` -> tenancy ON, and the host loses its company-wide reads;
|
|
4738
|
+
* - `multiTenant: { tenantField: 'business' }` -> scoping on `organizationId`,
|
|
4739
|
+
* a field those documents do not carry.
|
|
4740
|
+
*
|
|
4741
|
+
* tsc cannot cover this: excess-property checking does not fire for a typed
|
|
4742
|
+
* variable, a spread, or a shape handed over from a dependency's dist — which is
|
|
4743
|
+
* how every real host composes one. Hence a describe-time check, shared from
|
|
4744
|
+
* repo-core so the nine kernels making this same rename cannot each forget it.
|
|
4745
|
+
*/
|
|
4746
|
+
assertNoLegacyTenantKeys(shape, "defineCrm", [["tenantFieldType", "tenant.fieldType"]]);
|
|
4747
|
+
const tenant = resolveTenantConfig(shape.tenant ?? true);
|
|
4748
|
+
if (tenant.strategy === "custom") throw new Error("[crm] tenant.strategy 'custom' is unsupported: CRM repositories require one concrete tenant field");
|
|
4708
4749
|
const forceRecreate = shape.forceRecreate ?? false;
|
|
4709
4750
|
const autoIndex = shape.autoIndex ?? true;
|
|
4710
4751
|
const modules = resolveCrmModules(shape.modules);
|
|
4711
4752
|
const modelBlueprint = defineCrmModels({
|
|
4712
|
-
|
|
4753
|
+
tenant,
|
|
4713
4754
|
...shape.modelPrefix !== void 0 ? { modelPrefix: shape.modelPrefix } : {},
|
|
4714
4755
|
modules,
|
|
4715
4756
|
forceRecreate,
|
|
@@ -4754,9 +4795,8 @@ function defineCrm(shape = {}) {
|
|
|
4754
4795
|
if (runtime.outbox && runtime.outbox.transactionalSave !== true) throw new Error("[crm] the configured OutboxStore does not declare `outbox.transactionalSave` — crm events would not commit atomically with the writes that emit them. See OutboxStore.transactionalSave in @classytic/primitives/outbox; arc's `repositoryAsOutboxStore` declares it. This is a property of the STORE, not of the database, and `allowNonTransactional` does not waive it.");
|
|
4755
4796
|
const resolved = {
|
|
4756
4797
|
connection,
|
|
4757
|
-
|
|
4798
|
+
tenant,
|
|
4758
4799
|
...shape.modelPrefix !== void 0 ? { modelPrefix: shape.modelPrefix } : {},
|
|
4759
|
-
multiTenant,
|
|
4760
4800
|
forceRecreate,
|
|
4761
4801
|
autoIndex,
|
|
4762
4802
|
modules,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/crm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Engine-factory CRM package — leads, accounts, contacts, opportunities, pipelines, campaigns, subscriptions, activities, notes, email engagement. PACKAGE_RULES compliant; built on Mongoose + mongokit + arc primitives.",
|
|
5
5
|
"author": "Classytic",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -55,18 +55,18 @@
|
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@classytic/contact": ">=0.1.0",
|
|
58
|
-
"@classytic/mongokit": ">=3.
|
|
59
|
-
"@classytic/primitives": ">=0.
|
|
60
|
-
"@classytic/repo-core": ">=0.
|
|
58
|
+
"@classytic/mongokit": ">=3.33.0",
|
|
59
|
+
"@classytic/primitives": ">=0.23.0",
|
|
60
|
+
"@classytic/repo-core": ">=0.24.0",
|
|
61
61
|
"mongoose": ">=9.4.1",
|
|
62
62
|
"zod": ">=4.0.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@classytic/contact": ">=0.1.0",
|
|
66
66
|
"@classytic/dev-tools": "^0.2.0",
|
|
67
|
-
"@classytic/mongokit": "^3.
|
|
68
|
-
"@classytic/primitives": ">=0.
|
|
69
|
-
"@classytic/repo-core": ">=0.
|
|
67
|
+
"@classytic/mongokit": "^3.33.0",
|
|
68
|
+
"@classytic/primitives": ">=0.23.0",
|
|
69
|
+
"@classytic/repo-core": ">=0.24.0",
|
|
70
70
|
"@types/node": "^25.5.0",
|
|
71
71
|
"mongodb-memory-server": "^10.1.4",
|
|
72
72
|
"mongoose": "^9.4.1",
|