@open-mercato/core 0.4.5-develop-0f0e676c72 → 0.4.5-develop-e694581d9f
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/dist/generated/entities/customer_deal/index.js +4 -0
- package/dist/generated/entities/customer_deal/index.js.map +2 -2
- package/dist/generated/entities/customer_pipeline/index.js +17 -0
- package/dist/generated/entities/customer_pipeline/index.js.map +7 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js +19 -0
- package/dist/generated/entities/customer_pipeline_stage/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +4 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/acl.js +2 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +4 -0
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +12 -0
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +20 -1
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +69 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +7 -0
- package/dist/modules/customers/api/pipeline-stages/route.js +275 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +7 -0
- package/dist/modules/customers/api/pipelines/route.js +245 -0
- package/dist/modules/customers/api/pipelines/route.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/page.js +2 -0
- package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +439 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +7 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js +17 -0
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.meta.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +19 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +35 -1
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +102 -74
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/cli.js +28 -2
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +34 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/index.js +2 -0
- package/dist/modules/customers/commands/index.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +126 -0
- package/dist/modules/customers/commands/pipeline-stages.js.map +7 -0
- package/dist/modules/customers/commands/pipelines.js +87 -0
- package/dist/modules/customers/commands/pipelines.js.map +7 -0
- package/dist/modules/customers/components/DictionarySettings.js +0 -5
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +474 -0
- package/dist/modules/customers/components/PipelineSettings.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +84 -12
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/data/entities.js +78 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +44 -0
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260218191730.js +77 -0
- package/dist/modules/customers/migrations/Migration20260218191730.js.map +7 -0
- package/dist/modules/customers/setup.js +7 -3
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +46 -44
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +10 -1
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/translations/commands/index.js +2 -0
- package/dist/modules/translations/commands/index.js.map +7 -0
- package/dist/modules/translations/commands/translations.js +160 -0
- package/dist/modules/translations/commands/translations.js.map +7 -0
- package/dist/modules/translations/index.js +1 -0
- package/dist/modules/translations/index.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260222205305.js +14 -0
- package/dist/modules/workflows/migrations/Migration20260222205305.js.map +7 -0
- package/generated/entities/customer_deal/index.ts +2 -0
- package/generated/entities/customer_pipeline/index.ts +7 -0
- package/generated/entities/customer_pipeline_stage/index.ts +8 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +4 -0
- package/package.json +2 -2
- package/src/modules/customers/acl.ts +2 -0
- package/src/modules/customers/api/deals/[id]/route.ts +4 -0
- package/src/modules/customers/api/deals/route.ts +12 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +21 -1
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +71 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +296 -0
- package/src/modules/customers/api/pipelines/route.ts +261 -0
- package/src/modules/customers/backend/config/customers/page.tsx +2 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.meta.ts +13 -0
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +512 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +21 -1
- package/src/modules/customers/backend/customers/deals/page.tsx +33 -1
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +119 -79
- package/src/modules/customers/cli.ts +29 -1
- package/src/modules/customers/commands/deals.ts +44 -1
- package/src/modules/customers/commands/index.ts +2 -0
- package/src/modules/customers/commands/pipeline-stages.ts +156 -0
- package/src/modules/customers/commands/pipelines.ts +105 -0
- package/src/modules/customers/components/DictionarySettings.tsx +0 -5
- package/src/modules/customers/components/PipelineSettings.tsx +570 -0
- package/src/modules/customers/components/detail/DealForm.tsx +89 -11
- package/src/modules/customers/data/entities.ts +64 -0
- package/src/modules/customers/data/validators.ts +57 -0
- package/src/modules/customers/i18n/de.json +4 -0
- package/src/modules/customers/i18n/en.json +4 -0
- package/src/modules/customers/i18n/es.json +4 -0
- package/src/modules/customers/i18n/pl.json +5 -1
- package/src/modules/customers/migrations/Migration20260218191730.ts +84 -0
- package/src/modules/customers/setup.ts +5 -1
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -60
- package/src/modules/translations/api/context.ts +12 -0
- package/src/modules/translations/commands/index.ts +1 -0
- package/src/modules/translations/commands/translations.ts +253 -0
- package/src/modules/translations/index.ts +1 -0
- package/src/modules/workflows/migrations/Migration20260222205305.ts +13 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
emitCrudUndoSideEffects,
|
|
7
7
|
requireId
|
|
8
8
|
} from "@open-mercato/shared/lib/commands/helpers";
|
|
9
|
-
import { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from "../data/entities.js";
|
|
9
|
+
import { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink, CustomerPipelineStage } from "../data/entities.js";
|
|
10
10
|
import {
|
|
11
11
|
dealCreateSchema,
|
|
12
12
|
dealUpdateSchema
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
ensureTenantScope,
|
|
17
17
|
requireCustomerEntity,
|
|
18
18
|
ensureSameScope,
|
|
19
|
-
extractUndoPayload
|
|
19
|
+
extractUndoPayload,
|
|
20
|
+
ensureDictionaryEntry
|
|
20
21
|
} from "./shared.js";
|
|
21
22
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
22
23
|
import {
|
|
@@ -43,6 +44,17 @@ const dealCrudEvents = {
|
|
|
43
44
|
tenantId: ctx.identifiers.tenantId
|
|
44
45
|
})
|
|
45
46
|
};
|
|
47
|
+
async function resolvePipelineStageValue(em, pipelineStageId, tenantId, organizationId) {
|
|
48
|
+
const stage = await em.findOne(CustomerPipelineStage, { id: pipelineStageId });
|
|
49
|
+
if (!stage) return null;
|
|
50
|
+
const entry = await ensureDictionaryEntry(em, {
|
|
51
|
+
tenantId,
|
|
52
|
+
organizationId,
|
|
53
|
+
kind: "pipeline_stage",
|
|
54
|
+
value: stage.label
|
|
55
|
+
});
|
|
56
|
+
return entry?.value ?? stage.label;
|
|
57
|
+
}
|
|
46
58
|
async function loadDealSnapshot(em, id) {
|
|
47
59
|
const deal = await em.findOne(CustomerDeal, { id, deletedAt: null });
|
|
48
60
|
if (!deal) return null;
|
|
@@ -76,6 +88,8 @@ async function loadDealSnapshot(em, id) {
|
|
|
76
88
|
description: deal.description ?? null,
|
|
77
89
|
status: deal.status,
|
|
78
90
|
pipelineStage: deal.pipelineStage ?? null,
|
|
91
|
+
pipelineId: deal.pipelineId ?? null,
|
|
92
|
+
pipelineStageId: deal.pipelineStageId ?? null,
|
|
79
93
|
valueAmount: deal.valueAmount ?? null,
|
|
80
94
|
valueCurrency: deal.valueCurrency ?? null,
|
|
81
95
|
probability: deal.probability ?? null,
|
|
@@ -140,6 +154,8 @@ const createDealCommand = {
|
|
|
140
154
|
description: parsed.description ?? null,
|
|
141
155
|
status: parsed.status ?? "open",
|
|
142
156
|
pipelineStage: parsed.pipelineStage ?? null,
|
|
157
|
+
pipelineId: parsed.pipelineId ?? null,
|
|
158
|
+
pipelineStageId: parsed.pipelineStageId ?? null,
|
|
143
159
|
valueAmount: toNumericString(parsed.valueAmount),
|
|
144
160
|
valueCurrency: parsed.valueCurrency ?? null,
|
|
145
161
|
probability: parsed.probability ?? null,
|
|
@@ -148,6 +164,10 @@ const createDealCommand = {
|
|
|
148
164
|
source: parsed.source ?? null
|
|
149
165
|
});
|
|
150
166
|
em.persist(deal);
|
|
167
|
+
if (deal.pipelineStageId && !deal.pipelineStage) {
|
|
168
|
+
const resolved = await resolvePipelineStageValue(em, deal.pipelineStageId, parsed.tenantId, parsed.organizationId);
|
|
169
|
+
if (resolved) deal.pipelineStage = resolved;
|
|
170
|
+
}
|
|
151
171
|
await em.flush();
|
|
152
172
|
await syncDealPeople(em, deal, parsed.personIds ?? []);
|
|
153
173
|
await syncDealCompanies(em, deal, parsed.companyIds ?? []);
|
|
@@ -230,6 +250,12 @@ const updateDealCommand = {
|
|
|
230
250
|
if (parsed.description !== void 0) record.description = parsed.description ?? null;
|
|
231
251
|
if (parsed.status !== void 0) record.status = parsed.status ?? record.status;
|
|
232
252
|
if (parsed.pipelineStage !== void 0) record.pipelineStage = parsed.pipelineStage ?? null;
|
|
253
|
+
if (parsed.pipelineId !== void 0) record.pipelineId = parsed.pipelineId ?? null;
|
|
254
|
+
if (parsed.pipelineStageId !== void 0) record.pipelineStageId = parsed.pipelineStageId ?? null;
|
|
255
|
+
if (record.pipelineStageId && (parsed.pipelineStageId !== void 0 || !record.pipelineStage)) {
|
|
256
|
+
const resolved = await resolvePipelineStageValue(em, record.pipelineStageId, record.tenantId, record.organizationId);
|
|
257
|
+
if (resolved) record.pipelineStage = resolved;
|
|
258
|
+
}
|
|
233
259
|
if (parsed.valueAmount !== void 0) record.valueAmount = toNumericString(parsed.valueAmount);
|
|
234
260
|
if (parsed.valueCurrency !== void 0) record.valueCurrency = parsed.valueCurrency ?? null;
|
|
235
261
|
if (parsed.probability !== void 0) record.probability = parsed.probability ?? null;
|
|
@@ -330,6 +356,8 @@ const updateDealCommand = {
|
|
|
330
356
|
description: before.deal.description,
|
|
331
357
|
status: before.deal.status,
|
|
332
358
|
pipelineStage: before.deal.pipelineStage,
|
|
359
|
+
pipelineId: before.deal.pipelineId,
|
|
360
|
+
pipelineStageId: before.deal.pipelineStageId,
|
|
333
361
|
valueAmount: before.deal.valueAmount,
|
|
334
362
|
valueCurrency: before.deal.valueCurrency,
|
|
335
363
|
probability: before.deal.probability,
|
|
@@ -343,6 +371,8 @@ const updateDealCommand = {
|
|
|
343
371
|
deal.description = before.deal.description;
|
|
344
372
|
deal.status = before.deal.status;
|
|
345
373
|
deal.pipelineStage = before.deal.pipelineStage;
|
|
374
|
+
deal.pipelineId = before.deal.pipelineId;
|
|
375
|
+
deal.pipelineStageId = before.deal.pipelineStageId;
|
|
346
376
|
deal.valueAmount = before.deal.valueAmount;
|
|
347
377
|
deal.valueCurrency = before.deal.valueCurrency;
|
|
348
378
|
deal.probability = before.deal.probability;
|
|
@@ -449,6 +479,8 @@ const deleteDealCommand = {
|
|
|
449
479
|
description: before.deal.description,
|
|
450
480
|
status: before.deal.status,
|
|
451
481
|
pipelineStage: before.deal.pipelineStage,
|
|
482
|
+
pipelineId: before.deal.pipelineId,
|
|
483
|
+
pipelineStageId: before.deal.pipelineStageId,
|
|
452
484
|
valueAmount: before.deal.valueAmount,
|
|
453
485
|
valueCurrency: before.deal.valueCurrency,
|
|
454
486
|
probability: before.deal.probability,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/commands/deals.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from '../data/entities'\nimport {\n dealCreateSchema,\n dealUpdateSchema,\n type DealCreateInput,\n type DealUpdateInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n requireCustomerEntity,\n ensureSameScope,\n extractUndoPayload,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n type CustomFieldChangeSet,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\n\nconst DEAL_ENTITY_ID = 'customers:customer_deal'\nconst dealCrudIndexer: CrudIndexerConfig<CustomerDeal> = {\n entityType: E.customers.customer_deal,\n}\n\nconst dealCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'deal',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype DealSnapshot = {\n deal: {\n id: string\n organizationId: string\n tenantId: string\n title: string\n description: string | null\n status: string\n pipelineStage: string | null\n valueAmount: string | null\n valueCurrency: string | null\n probability: number | null\n expectedCloseAt: Date | null\n ownerUserId: string | null\n source: string | null\n }\n people: string[]\n companies: string[]\n custom?: Record<string, unknown>\n}\n\ntype DealUndoPayload = {\n before?: DealSnapshot | null\n after?: DealSnapshot | null\n}\n\ntype DealChangeMap = Record<string, { from: unknown; to: unknown }> & {\n custom?: CustomFieldChangeSet\n}\n\nasync function loadDealSnapshot(em: EntityManager, id: string): Promise<DealSnapshot | null> {\n const deal = await em.findOne(CustomerDeal, { id, deletedAt: null })\n if (!deal) return null\n const decryptionScope = { tenantId: deal.tenantId ?? null, organizationId: deal.organizationId ?? null }\n const peopleLinks = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal },\n { populate: ['person'] },\n decryptionScope,\n )\n const companyLinks = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal },\n { populate: ['company'] },\n decryptionScope,\n )\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n tenantId: deal.tenantId,\n organizationId: deal.organizationId,\n })\n return {\n deal: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status,\n pipelineStage: deal.pipelineStage ?? null,\n valueAmount: deal.valueAmount ?? null,\n valueCurrency: deal.valueCurrency ?? null,\n probability: deal.probability ?? null,\n expectedCloseAt: deal.expectedCloseAt ?? null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n },\n people: peopleLinks.map((link) =>\n typeof link.person === 'string' ? link.person : link.person.id\n ),\n companies: companyLinks.map((link) =>\n typeof link.company === 'string' ? link.company : link.company.id\n ),\n custom,\n }\n}\n\nfunction toNumericString(value: number | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return value.toString()\n}\n\nasync function syncDealPeople(\n em: EntityManager,\n deal: CustomerDeal,\n personIds: string[] | undefined | null\n): Promise<void> {\n if (personIds === undefined) return\n await em.nativeDelete(CustomerDealPersonLink, { deal })\n if (!personIds || !personIds.length) return\n const unique = Array.from(new Set(personIds))\n for (const personId of unique) {\n const person = await requireCustomerEntity(em, personId, 'person', 'Person not found')\n ensureSameScope(person, deal.organizationId, deal.tenantId)\n const link = em.create(CustomerDealPersonLink, {\n deal,\n person,\n })\n em.persist(link)\n }\n}\n\nasync function syncDealCompanies(\n em: EntityManager,\n deal: CustomerDeal,\n companyIds: string[] | undefined | null\n): Promise<void> {\n if (companyIds === undefined) return\n await em.nativeDelete(CustomerDealCompanyLink, { deal })\n if (!companyIds || !companyIds.length) return\n const unique = Array.from(new Set(companyIds))\n for (const companyId of unique) {\n const company = await requireCustomerEntity(em, companyId, 'company', 'Company not found')\n ensureSameScope(company, deal.organizationId, deal.tenantId)\n const link = em.create(CustomerDealCompanyLink, {\n deal,\n company,\n })\n em.persist(link)\n }\n}\n\nconst createDealCommand: CommandHandler<DealCreateInput, { dealId: string }> = {\n id: 'customers.deals.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(dealCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = em.create(CustomerDeal, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n title: parsed.title,\n description: parsed.description ?? null,\n status: parsed.status ?? 'open',\n pipelineStage: parsed.pipelineStage ?? null,\n valueAmount: toNumericString(parsed.valueAmount),\n valueCurrency: parsed.valueCurrency ?? null,\n probability: parsed.probability ?? null,\n expectedCloseAt: parsed.expectedCloseAt ?? null,\n ownerUserId: parsed.ownerUserId ?? null,\n source: parsed.source ?? null,\n })\n em.persist(deal)\n await em.flush()\n\n await syncDealPeople(em, deal, parsed.personIds ?? [])\n await syncDealCompanies(em, deal, parsed.companyIds ?? [])\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: custom,\n notify: false,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n return { dealId: deal.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadDealSnapshot(em, result.dealId)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const snapshot = snapshots.after as DealSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.deals.create', 'Create deal'),\n resourceKind: 'customers.deal',\n resourceId: result.dealId,\n tenantId: snapshot?.deal.tenantId ?? null,\n organizationId: snapshot?.deal.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const dealId = logEntry?.resourceId\n if (!dealId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id: dealId })\n if (!deal) return\n await em.nativeDelete(CustomerDealPersonLink, { deal })\n await em.nativeDelete(CustomerDealCompanyLink, { deal })\n em.remove(deal)\n await em.flush()\n },\n}\n\nconst updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {\n id: 'customers.deals.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(dealUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadDealSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(dealUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id: parsed.id, deletedAt: null })\n const record = deal ?? null\n if (!record) throw new CrudHttpError(404, { error: 'Deal not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n const previousStatus = record.status\n\n if (parsed.title !== undefined) record.title = parsed.title\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.status !== undefined) record.status = parsed.status ?? record.status\n if (parsed.pipelineStage !== undefined) record.pipelineStage = parsed.pipelineStage ?? null\n if (parsed.valueAmount !== undefined) record.valueAmount = toNumericString(parsed.valueAmount)\n if (parsed.valueCurrency !== undefined) record.valueCurrency = parsed.valueCurrency ?? null\n if (parsed.probability !== undefined) record.probability = parsed.probability ?? null\n if (parsed.expectedCloseAt !== undefined) record.expectedCloseAt = parsed.expectedCloseAt ?? null\n if (parsed.ownerUserId !== undefined) record.ownerUserId = parsed.ownerUserId ?? null\n if (parsed.source !== undefined) record.source = parsed.source ?? null\n\n await syncDealPeople(em, record, parsed.personIds)\n await syncDealCompanies(em, record, parsed.companyIds)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n notify: false,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n // Send notifications for deal won/lost status changes\n const newStatus = record.status\n const normalizedStatus = newStatus === 'win' ? 'won' : newStatus === 'loose' ? 'lost' : newStatus\n if (previousStatus !== newStatus && (normalizedStatus === 'won' || normalizedStatus === 'lost') && record.ownerUserId) {\n try {\n const notificationService = resolveNotificationService(ctx.container)\n const notificationType = normalizedStatus === 'won' ? 'customers.deal.won' : 'customers.deal.lost'\n const typeDef = notificationTypes.find((type) => type.type === notificationType)\n if (typeDef) {\n const valueDisplay = record.valueAmount && record.valueCurrency\n ? `${record.valueCurrency} ${record.valueAmount}`\n : ''\n\n const notificationInput = buildNotificationFromType(typeDef, {\n recipientUserId: record.ownerUserId,\n bodyVariables: {\n dealTitle: record.title,\n dealValue: valueDisplay,\n },\n sourceEntityType: 'customers:customer_deal',\n sourceEntityId: record.id,\n linkHref: `/backend/customers/deals/${record.id}`,\n })\n\n await notificationService.create(notificationInput, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n }\n } catch {\n // Notification creation is non-critical, don't fail the command\n }\n }\n\n return { dealId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadDealSnapshot(em, result.dealId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as DealSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as DealSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.deals.update', 'Update deal'),\n resourceKind: 'customers.deal',\n resourceId: before.deal.id,\n tenantId: before.deal.tenantId,\n organizationId: before.deal.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DealUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let deal = await em.findOne(CustomerDeal, { id: before.deal.id })\n if (!deal) {\n deal = em.create(CustomerDeal, {\n id: before.deal.id,\n organizationId: before.deal.organizationId,\n tenantId: before.deal.tenantId,\n title: before.deal.title,\n description: before.deal.description,\n status: before.deal.status,\n pipelineStage: before.deal.pipelineStage,\n valueAmount: before.deal.valueAmount,\n valueCurrency: before.deal.valueCurrency,\n probability: before.deal.probability,\n expectedCloseAt: before.deal.expectedCloseAt,\n ownerUserId: before.deal.ownerUserId,\n source: before.deal.source,\n })\n em.persist(deal)\n } else {\n deal.title = before.deal.title\n deal.description = before.deal.description\n deal.status = before.deal.status\n deal.pipelineStage = before.deal.pipelineStage\n deal.valueAmount = before.deal.valueAmount\n deal.valueCurrency = before.deal.valueCurrency\n deal.probability = before.deal.probability\n deal.expectedCloseAt = before.deal.expectedCloseAt\n deal.ownerUserId = before.deal.ownerUserId\n deal.source = before.deal.source\n }\n await em.flush()\n await syncDealPeople(em, deal, before.people)\n await syncDealCompanies(em, deal, before.companies)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n const resetValues = buildCustomFieldResetMap(before.custom, payload?.after?.custom)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n },\n}\n\nconst deleteDealCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { dealId: string }> =\n {\n id: 'customers.deals.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Deal id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadDealSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Deal id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id, deletedAt: null })\n const record = deal ?? null\n if (!record) throw new CrudHttpError(404, { error: 'Deal not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n await em.nativeDelete(CustomerDealPersonLink, { deal: record })\n await em.nativeDelete(CustomerDealCompanyLink, { deal: record })\n em.remove(record)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n return { dealId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as DealSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.deals.delete', 'Delete deal'),\n resourceKind: 'customers.deal',\n resourceId: before.deal.id,\n tenantId: before.deal.tenantId,\n organizationId: before.deal.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DealUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let deal = await em.findOne(CustomerDeal, { id: before.deal.id })\n if (!deal) {\n deal = em.create(CustomerDeal, {\n id: before.deal.id,\n organizationId: before.deal.organizationId,\n tenantId: before.deal.tenantId,\n title: before.deal.title,\n description: before.deal.description,\n status: before.deal.status,\n pipelineStage: before.deal.pipelineStage,\n valueAmount: before.deal.valueAmount,\n valueCurrency: before.deal.valueCurrency,\n probability: before.deal.probability,\n expectedCloseAt: before.deal.expectedCloseAt,\n ownerUserId: before.deal.ownerUserId,\n source: before.deal.source,\n })\n em.persist(deal)\n }\n await em.flush()\n await syncDealPeople(em, deal, before.people)\n await syncDealCompanies(em, deal, before.companies)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n const resetValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n },\n }\n\nregisterCommand(createDealCommand)\nregisterCommand(updateDealCommand)\nregisterCommand(deleteDealCommand)\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,uBAAuB;AAEhC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,cAAc,wBAAwB,+BAA+B;AAC9E;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,qBAAqB;AAE9B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,kCAAkC;AAC3C,SAAS,iCAAiC;AAC1C,SAAS,yBAAyB;AAElC,MAAM,iBAAiB;AACvB,MAAM,kBAAmD;AAAA,EACvD,YAAY,EAAE,UAAU;AAC1B;AAEA,MAAM,iBAAmC;AAAA,EACvC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAgCA,eAAe,iBAAiB,IAAmB,IAA0C;AAC3F,QAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,WAAW,KAAK,CAAC;AACnE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,kBAAkB,EAAE,UAAU,KAAK,YAAY,MAAM,gBAAgB,KAAK,kBAAkB,KAAK;AACvG,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA,EAAE,KAAW;AAAA,IACb,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,IACvB;AAAA,EACF;AACA,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA,EAAE,KAAW;AAAA,IACb,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,IACxB;AAAA,EACF;AACA,QAAM,SAAS,MAAM,wBAAwB,IAAI;AAAA,IAC/C,UAAU;AAAA,IACV,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,EACvB,CAAC;AACD,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,IAAI,KAAK;AAAA,MACT,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK,eAAe;AAAA,MACjC,QAAQ,KAAK;AAAA,MACb,eAAe,KAAK,iBAAiB;AAAA,MACrC,aAAa,KAAK,eAAe;AAAA,MACjC,eAAe,KAAK,iBAAiB;AAAA,MACrC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK,mBAAmB;AAAA,MACzC,aAAa,KAAK,eAAe;AAAA,MACjC,QAAQ,KAAK,UAAU;AAAA,IACzB;AAAA,IACA,QAAQ,YAAY;AAAA,MAAI,CAAC,SACvB,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,KAAK,OAAO;AAAA,IAC9D;AAAA,IACA,WAAW,aAAa;AAAA,MAAI,CAAC,SAC3B,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,KAAK,QAAQ;AAAA,IACjE;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAiD;AACxE,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,SAAO,MAAM,SAAS;AACxB;AAEA,eAAe,eACb,IACA,MACA,WACe;AACf,MAAI,cAAc,OAAW;AAC7B,QAAM,GAAG,aAAa,wBAAwB,EAAE,KAAK,CAAC;AACtD,MAAI,CAAC,aAAa,CAAC,UAAU,OAAQ;AACrC,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,SAAS,CAAC;AAC5C,aAAW,YAAY,QAAQ;AAC7B,UAAM,SAAS,MAAM,sBAAsB,IAAI,UAAU,UAAU,kBAAkB;AACrF,oBAAgB,QAAQ,KAAK,gBAAgB,KAAK,QAAQ;AAC1D,UAAM,OAAO,GAAG,OAAO,wBAAwB;AAAA,MAC7C;AAAA,MACA;AAAA,IACF,CAAC;AACD,OAAG,QAAQ,IAAI;AAAA,EACjB;AACF;AAEA,eAAe,kBACb,IACA,MACA,YACe;AACf,MAAI,eAAe,OAAW;AAC9B,QAAM,GAAG,aAAa,yBAAyB,EAAE,KAAK,CAAC;AACvD,MAAI,CAAC,cAAc,CAAC,WAAW,OAAQ;AACvC,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC;AAC7C,aAAW,aAAa,QAAQ;AAC9B,UAAM,UAAU,MAAM,sBAAsB,IAAI,WAAW,WAAW,mBAAmB;AACzF,oBAAgB,SAAS,KAAK,gBAAgB,KAAK,QAAQ;AAC3D,UAAM,OAAO,GAAG,OAAO,yBAAyB;AAAA,MAC9C;AAAA,MACA;AAAA,IACF,CAAC;AACD,OAAG,QAAQ,IAAI;AAAA,EACjB;AACF;AAEA,MAAM,oBAAyE;AAAA,EAC7E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,kBAAkB,QAAQ;AAC3E,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,GAAG,OAAO,cAAc;AAAA,MACnC,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,aAAa,OAAO,eAAe;AAAA,MACnC,QAAQ,OAAO,UAAU;AAAA,MACzB,eAAe,OAAO,iBAAiB;AAAA,MACvC,aAAa,gBAAgB,OAAO,WAAW;AAAA,MAC/C,eAAe,OAAO,iBAAiB;AAAA,MACvC,aAAa,OAAO,eAAe;AAAA,MACnC,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,aAAa,OAAO,eAAe;AAAA,MACnC,QAAQ,OAAO,UAAU;AAAA,IAC3B,CAAC;AACD,OAAG,QAAQ,IAAI;AACf,UAAM,GAAG,MAAM;AAEf,UAAM,eAAe,IAAI,MAAM,OAAO,aAAa,CAAC,CAAC;AACrD,UAAM,kBAAkB,IAAI,MAAM,OAAO,cAAc,CAAC,CAAC;AACzD,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,qBAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU,KAAK;AAAA,MACf,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,KAAK;AAAA,QACT,gBAAgB,KAAK;AAAA,QACrB,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAED,WAAO,EAAE,QAAQ,KAAK,GAAG;AAAA,EAC3B;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,MAAM,iBAAiB,IAAI,OAAO,MAAM;AAAA,EACjD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM;AACzC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,WAAW,UAAU;AAC3B,WAAO;AAAA,MACL,aAAa,UAAU,gCAAgC,aAAa;AAAA,MACpE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,UAAU,KAAK,YAAY;AAAA,MACrC,gBAAgB,UAAU,KAAK,kBAAkB;AAAA,MACjD,eAAe,YAAY;AAAA,MAC3B,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAM;AACX,UAAM,GAAG,aAAa,wBAAwB,EAAE,KAAK,CAAC;AACtD,UAAM,GAAG,aAAa,yBAAyB,EAAE,KAAK,CAAC;AACvD,OAAG,OAAO,IAAI;AACd,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,MAAM,oBAAyE;AAAA,EAC7E,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,OAAO,IAAI,sBAAsB,kBAAkB,QAAQ;AACnE,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,iBAAiB,IAAI,OAAO,EAAE;AACrD,WAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,kBAAkB,QAAQ;AAC3E,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK,CAAC;AAC9E,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACrE,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,iBAAiB,OAAO;AAE9B,QAAI,OAAO,UAAU,OAAW,QAAO,QAAQ,OAAO;AACtD,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,QAAI,OAAO,WAAW,OAAW,QAAO,SAAS,OAAO,UAAU,OAAO;AACzE,QAAI,OAAO,kBAAkB,OAAW,QAAO,gBAAgB,OAAO,iBAAiB;AACvF,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,gBAAgB,OAAO,WAAW;AAC7F,QAAI,OAAO,kBAAkB,OAAW,QAAO,gBAAgB,OAAO,iBAAiB;AACvF,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,QAAI,OAAO,oBAAoB,OAAW,QAAO,kBAAkB,OAAO,mBAAmB;AAC7F,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,QAAI,OAAO,WAAW,OAAW,QAAO,SAAS,OAAO,UAAU;AAElE,UAAM,eAAe,IAAI,QAAQ,OAAO,SAAS;AACjD,UAAM,kBAAkB,IAAI,QAAQ,OAAO,UAAU;AACrD,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,qBAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAGD,UAAM,YAAY,OAAO;AACzB,UAAM,mBAAmB,cAAc,QAAQ,QAAQ,cAAc,UAAU,SAAS;AACxF,QAAI,mBAAmB,cAAc,qBAAqB,SAAS,qBAAqB,WAAW,OAAO,aAAa;AACrH,UAAI;AACF,cAAM,sBAAsB,2BAA2B,IAAI,SAAS;AACpE,cAAM,mBAAmB,qBAAqB,QAAQ,uBAAuB;AAC7E,cAAM,UAAU,kBAAkB,KAAK,CAAC,SAAS,KAAK,SAAS,gBAAgB;AAC/E,YAAI,SAAS;AACX,gBAAM,eAAe,OAAO,eAAe,OAAO,gBAC9C,GAAG,OAAO,aAAa,IAAI,OAAO,WAAW,KAC7C;AAEJ,gBAAM,oBAAoB,0BAA0B,SAAS;AAAA,YAC3D,iBAAiB,OAAO;AAAA,YACxB,eAAe;AAAA,cACb,WAAW,OAAO;AAAA,cAClB,WAAW;AAAA,YACb;AAAA,YACA,kBAAkB;AAAA,YAClB,gBAAgB,OAAO;AAAA,YACvB,UAAU,4BAA4B,OAAO,EAAE;AAAA,UACjD,CAAC;AAED,gBAAM,oBAAoB,OAAO,mBAAmB;AAAA,YAClD,UAAU,OAAO;AAAA,YACjB,gBAAgB,OAAO;AAAA,UACzB,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,OAAO,GAAG;AAAA,EAC7B;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,MAAM,iBAAiB,IAAI,OAAO,MAAM;AAAA,EACjD;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,gBAAgB,UAAU;AAChC,WAAO;AAAA,MACL,aAAa,UAAU,gCAAgC,aAAa;AAAA,MACpE,cAAc;AAAA,MACd,YAAY,OAAO,KAAK;AAAA,MACxB,UAAU,OAAO,KAAK;AAAA,MACtB,gBAAgB,OAAO,KAAK;AAAA,MAC5B,gBAAgB;AAAA,MAChB,eAAe,iBAAiB;AAAA,MAChC,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA,OAAO,iBAAiB;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,OAAO,KAAK,GAAG,CAAC;AAChE,QAAI,CAAC,MAAM;AACT,aAAO,GAAG,OAAO,cAAc;AAAA,QAC7B,IAAI,OAAO,KAAK;AAAA,QAChB,gBAAgB,OAAO,KAAK;AAAA,QAC5B,UAAU,OAAO,KAAK;AAAA,QACtB,OAAO,OAAO,KAAK;AAAA,QACnB,aAAa,OAAO,KAAK;AAAA,QACzB,QAAQ,OAAO,KAAK;AAAA,QACpB,eAAe,OAAO,KAAK;AAAA,QAC3B,aAAa,OAAO,KAAK;AAAA,QACzB,eAAe,OAAO,KAAK;AAAA,QAC3B,aAAa,OAAO,KAAK;AAAA,QACzB,iBAAiB,OAAO,KAAK;AAAA,QAC7B,aAAa,OAAO,KAAK;AAAA,QACzB,QAAQ,OAAO,KAAK;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,IAAI;AAAA,IACjB,OAAO;AACL,WAAK,QAAQ,OAAO,KAAK;AACzB,WAAK,cAAc,OAAO,KAAK;AAC/B,WAAK,SAAS,OAAO,KAAK;AAC1B,WAAK,gBAAgB,OAAO,KAAK;AACjC,WAAK,cAAc,OAAO,KAAK;AAC/B,WAAK,gBAAgB,OAAO,KAAK;AACjC,WAAK,cAAc,OAAO,KAAK;AAC/B,WAAK,kBAAkB,OAAO,KAAK;AACnC,WAAK,cAAc,OAAO,KAAK;AAC/B,WAAK,SAAS,OAAO,KAAK;AAAA,IAC5B;AACA,UAAM,GAAG,MAAM;AACf,UAAM,eAAe,IAAI,MAAM,OAAO,MAAM;AAC5C,UAAM,kBAAkB,IAAI,MAAM,OAAO,SAAS;AAClD,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,KAAK;AAAA,QACT,gBAAgB,KAAK;AAAA,QACrB,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,cAAc,yBAAyB,OAAO,QAAQ,SAAS,OAAO,MAAM;AAClF,QAAI,OAAO,KAAK,WAAW,EAAE,QAAQ;AACnC,YAAM,qBAAqB;AAAA,QACzB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,MAAM,oBACJ;AAAA,EACE,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,kBAAkB;AAC9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,iBAAiB,IAAI,EAAE;AAC9C,WAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,UAAU,OAAO,kBAAkB;AAC9C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,WAAW,KAAK,CAAC;AACnE,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACrE,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAClD,UAAM,GAAG,aAAa,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAC9D,UAAM,GAAG,aAAa,yBAAyB,EAAE,MAAM,OAAO,CAAC;AAC/D,OAAG,OAAO,MAAM;AAChB,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,oBAAoB;AAAA,MACxB,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,EAAE,QAAQ,OAAO,GAAG;AAAA,EAC7B;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,gCAAgC,aAAa;AAAA,MACpE,cAAc;AAAA,MACd,YAAY,OAAO,KAAK;AAAA,MACxB,UAAU,OAAO,KAAK;AAAA,MACtB,gBAAgB,OAAO,KAAK;AAAA,MAC5B,gBAAgB;AAAA,MAChB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,OAAO,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,OAAO,KAAK,GAAG,CAAC;AAChE,QAAI,CAAC,MAAM;AACT,aAAO,GAAG,OAAO,cAAc;AAAA,QAC7B,IAAI,OAAO,KAAK;AAAA,QAChB,gBAAgB,OAAO,KAAK;AAAA,QAC5B,UAAU,OAAO,KAAK;AAAA,QACtB,OAAO,OAAO,KAAK;AAAA,QACnB,aAAa,OAAO,KAAK;AAAA,QACzB,QAAQ,OAAO,KAAK;AAAA,QACpB,eAAe,OAAO,KAAK;AAAA,QAC3B,aAAa,OAAO,KAAK;AAAA,QACzB,eAAe,OAAO,KAAK;AAAA,QAC3B,aAAa,OAAO,KAAK;AAAA,QACzB,iBAAiB,OAAO,KAAK;AAAA,QAC7B,aAAa,OAAO,KAAK;AAAA,QACzB,QAAQ,OAAO,KAAK;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,IAAI;AAAA,IACjB;AACA,UAAM,GAAG,MAAM;AACf,UAAM,eAAe,IAAI,MAAM,OAAO,MAAM;AAC5C,UAAM,kBAAkB,IAAI,MAAM,OAAO,SAAS;AAClD,UAAM,GAAG,MAAM;AAEf,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,UAAM,wBAAwB;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,KAAK;AAAA,QACT,gBAAgB,KAAK;AAAA,QACrB,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,cAAc,yBAAyB,OAAO,QAAQ,MAAS;AACrE,QAAI,OAAO,KAAK,WAAW,EAAE,QAAQ;AACnC,YAAM,qBAAqB;AAAA,QACzB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEF,gBAAgB,iBAAiB;AACjC,gBAAgB,iBAAiB;AACjC,gBAAgB,iBAAiB;",
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink, CustomerPipelineStage } from '../data/entities'\nimport {\n dealCreateSchema,\n dealUpdateSchema,\n type DealCreateInput,\n type DealUpdateInput,\n} from '../data/validators'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n requireCustomerEntity,\n ensureSameScope,\n extractUndoPayload,\n ensureDictionaryEntry,\n} from './shared'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n type CustomFieldChangeSet,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveNotificationService } from '../../notifications/lib/notificationService'\nimport { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'\nimport { notificationTypes } from '../notifications'\n\nconst DEAL_ENTITY_ID = 'customers:customer_deal'\nconst dealCrudIndexer: CrudIndexerConfig<CustomerDeal> = {\n entityType: E.customers.customer_deal,\n}\n\nconst dealCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'deal',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nasync function resolvePipelineStageValue(\n em: EntityManager,\n pipelineStageId: string,\n tenantId: string,\n organizationId: string,\n): Promise<string | null> {\n const stage = await em.findOne(CustomerPipelineStage, { id: pipelineStageId })\n if (!stage) return null\n const entry = await ensureDictionaryEntry(em, {\n tenantId,\n organizationId,\n kind: 'pipeline_stage',\n value: stage.label,\n })\n return entry?.value ?? stage.label\n}\n\ntype DealSnapshot = {\n deal: {\n id: string\n organizationId: string\n tenantId: string\n title: string\n description: string | null\n status: string\n pipelineStage: string | null\n pipelineId: string | null\n pipelineStageId: string | null\n valueAmount: string | null\n valueCurrency: string | null\n probability: number | null\n expectedCloseAt: Date | null\n ownerUserId: string | null\n source: string | null\n }\n people: string[]\n companies: string[]\n custom?: Record<string, unknown>\n}\n\ntype DealUndoPayload = {\n before?: DealSnapshot | null\n after?: DealSnapshot | null\n}\n\ntype DealChangeMap = Record<string, { from: unknown; to: unknown }> & {\n custom?: CustomFieldChangeSet\n}\n\nasync function loadDealSnapshot(em: EntityManager, id: string): Promise<DealSnapshot | null> {\n const deal = await em.findOne(CustomerDeal, { id, deletedAt: null })\n if (!deal) return null\n const decryptionScope = { tenantId: deal.tenantId ?? null, organizationId: deal.organizationId ?? null }\n const peopleLinks = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal },\n { populate: ['person'] },\n decryptionScope,\n )\n const companyLinks = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal },\n { populate: ['company'] },\n decryptionScope,\n )\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n tenantId: deal.tenantId,\n organizationId: deal.organizationId,\n })\n return {\n deal: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status,\n pipelineStage: deal.pipelineStage ?? null,\n pipelineId: deal.pipelineId ?? null,\n pipelineStageId: deal.pipelineStageId ?? null,\n valueAmount: deal.valueAmount ?? null,\n valueCurrency: deal.valueCurrency ?? null,\n probability: deal.probability ?? null,\n expectedCloseAt: deal.expectedCloseAt ?? null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n },\n people: peopleLinks.map((link) =>\n typeof link.person === 'string' ? link.person : link.person.id\n ),\n companies: companyLinks.map((link) =>\n typeof link.company === 'string' ? link.company : link.company.id\n ),\n custom,\n }\n}\n\nfunction toNumericString(value: number | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return value.toString()\n}\n\nasync function syncDealPeople(\n em: EntityManager,\n deal: CustomerDeal,\n personIds: string[] | undefined | null\n): Promise<void> {\n if (personIds === undefined) return\n await em.nativeDelete(CustomerDealPersonLink, { deal })\n if (!personIds || !personIds.length) return\n const unique = Array.from(new Set(personIds))\n for (const personId of unique) {\n const person = await requireCustomerEntity(em, personId, 'person', 'Person not found')\n ensureSameScope(person, deal.organizationId, deal.tenantId)\n const link = em.create(CustomerDealPersonLink, {\n deal,\n person,\n })\n em.persist(link)\n }\n}\n\nasync function syncDealCompanies(\n em: EntityManager,\n deal: CustomerDeal,\n companyIds: string[] | undefined | null\n): Promise<void> {\n if (companyIds === undefined) return\n await em.nativeDelete(CustomerDealCompanyLink, { deal })\n if (!companyIds || !companyIds.length) return\n const unique = Array.from(new Set(companyIds))\n for (const companyId of unique) {\n const company = await requireCustomerEntity(em, companyId, 'company', 'Company not found')\n ensureSameScope(company, deal.organizationId, deal.tenantId)\n const link = em.create(CustomerDealCompanyLink, {\n deal,\n company,\n })\n em.persist(link)\n }\n}\n\nconst createDealCommand: CommandHandler<DealCreateInput, { dealId: string }> = {\n id: 'customers.deals.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(dealCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = em.create(CustomerDeal, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n title: parsed.title,\n description: parsed.description ?? null,\n status: parsed.status ?? 'open',\n pipelineStage: parsed.pipelineStage ?? null,\n pipelineId: parsed.pipelineId ?? null,\n pipelineStageId: parsed.pipelineStageId ?? null,\n valueAmount: toNumericString(parsed.valueAmount),\n valueCurrency: parsed.valueCurrency ?? null,\n probability: parsed.probability ?? null,\n expectedCloseAt: parsed.expectedCloseAt ?? null,\n ownerUserId: parsed.ownerUserId ?? null,\n source: parsed.source ?? null,\n })\n em.persist(deal)\n\n if (deal.pipelineStageId && !deal.pipelineStage) {\n const resolved = await resolvePipelineStageValue(em, deal.pipelineStageId, parsed.tenantId, parsed.organizationId)\n if (resolved) deal.pipelineStage = resolved\n }\n\n await em.flush()\n\n await syncDealPeople(em, deal, parsed.personIds ?? [])\n await syncDealCompanies(em, deal, parsed.companyIds ?? [])\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: custom,\n notify: false,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n return { dealId: deal.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadDealSnapshot(em, result.dealId)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const snapshot = snapshots.after as DealSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.deals.create', 'Create deal'),\n resourceKind: 'customers.deal',\n resourceId: result.dealId,\n tenantId: snapshot?.deal.tenantId ?? null,\n organizationId: snapshot?.deal.organizationId ?? null,\n snapshotAfter: snapshot ?? null,\n payload: {\n undo: {\n after: snapshot,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const dealId = logEntry?.resourceId\n if (!dealId) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id: dealId })\n if (!deal) return\n await em.nativeDelete(CustomerDealPersonLink, { deal })\n await em.nativeDelete(CustomerDealCompanyLink, { deal })\n em.remove(deal)\n await em.flush()\n },\n}\n\nconst updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {\n id: 'customers.deals.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(dealUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadDealSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(dealUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id: parsed.id, deletedAt: null })\n const record = deal ?? null\n if (!record) throw new CrudHttpError(404, { error: 'Deal not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n const previousStatus = record.status\n\n if (parsed.title !== undefined) record.title = parsed.title\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.status !== undefined) record.status = parsed.status ?? record.status\n if (parsed.pipelineStage !== undefined) record.pipelineStage = parsed.pipelineStage ?? null\n if (parsed.pipelineId !== undefined) record.pipelineId = parsed.pipelineId ?? null\n if (parsed.pipelineStageId !== undefined) record.pipelineStageId = parsed.pipelineStageId ?? null\n\n if (record.pipelineStageId && (parsed.pipelineStageId !== undefined || !record.pipelineStage)) {\n const resolved = await resolvePipelineStageValue(em, record.pipelineStageId, record.tenantId, record.organizationId)\n if (resolved) record.pipelineStage = resolved\n }\n if (parsed.valueAmount !== undefined) record.valueAmount = toNumericString(parsed.valueAmount)\n if (parsed.valueCurrency !== undefined) record.valueCurrency = parsed.valueCurrency ?? null\n if (parsed.probability !== undefined) record.probability = parsed.probability ?? null\n if (parsed.expectedCloseAt !== undefined) record.expectedCloseAt = parsed.expectedCloseAt ?? null\n if (parsed.ownerUserId !== undefined) record.ownerUserId = parsed.ownerUserId ?? null\n if (parsed.source !== undefined) record.source = parsed.source ?? null\n\n await syncDealPeople(em, record, parsed.personIds)\n await syncDealCompanies(em, record, parsed.companyIds)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n notify: false,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n // Send notifications for deal won/lost status changes\n const newStatus = record.status\n const normalizedStatus = newStatus === 'win' ? 'won' : newStatus === 'loose' ? 'lost' : newStatus\n if (previousStatus !== newStatus && (normalizedStatus === 'won' || normalizedStatus === 'lost') && record.ownerUserId) {\n try {\n const notificationService = resolveNotificationService(ctx.container)\n const notificationType = normalizedStatus === 'won' ? 'customers.deal.won' : 'customers.deal.lost'\n const typeDef = notificationTypes.find((type) => type.type === notificationType)\n if (typeDef) {\n const valueDisplay = record.valueAmount && record.valueCurrency\n ? `${record.valueCurrency} ${record.valueAmount}`\n : ''\n\n const notificationInput = buildNotificationFromType(typeDef, {\n recipientUserId: record.ownerUserId,\n bodyVariables: {\n dealTitle: record.title,\n dealValue: valueDisplay,\n },\n sourceEntityType: 'customers:customer_deal',\n sourceEntityId: record.id,\n linkHref: `/backend/customers/deals/${record.id}`,\n })\n\n await notificationService.create(notificationInput, {\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n }\n } catch {\n // Notification creation is non-critical, don't fail the command\n }\n }\n\n return { dealId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return await loadDealSnapshot(em, result.dealId)\n },\n buildLog: async ({ snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as DealSnapshot | undefined\n if (!before) return null\n const afterSnapshot = snapshots.after as DealSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.deals.update', 'Update deal'),\n resourceKind: 'customers.deal',\n resourceId: before.deal.id,\n tenantId: before.deal.tenantId,\n organizationId: before.deal.organizationId,\n snapshotBefore: before,\n snapshotAfter: afterSnapshot ?? null,\n payload: {\n undo: {\n before,\n after: afterSnapshot ?? null,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DealUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let deal = await em.findOne(CustomerDeal, { id: before.deal.id })\n if (!deal) {\n deal = em.create(CustomerDeal, {\n id: before.deal.id,\n organizationId: before.deal.organizationId,\n tenantId: before.deal.tenantId,\n title: before.deal.title,\n description: before.deal.description,\n status: before.deal.status,\n pipelineStage: before.deal.pipelineStage,\n pipelineId: before.deal.pipelineId,\n pipelineStageId: before.deal.pipelineStageId,\n valueAmount: before.deal.valueAmount,\n valueCurrency: before.deal.valueCurrency,\n probability: before.deal.probability,\n expectedCloseAt: before.deal.expectedCloseAt,\n ownerUserId: before.deal.ownerUserId,\n source: before.deal.source,\n })\n em.persist(deal)\n } else {\n deal.title = before.deal.title\n deal.description = before.deal.description\n deal.status = before.deal.status\n deal.pipelineStage = before.deal.pipelineStage\n deal.pipelineId = before.deal.pipelineId\n deal.pipelineStageId = before.deal.pipelineStageId\n deal.valueAmount = before.deal.valueAmount\n deal.valueCurrency = before.deal.valueCurrency\n deal.probability = before.deal.probability\n deal.expectedCloseAt = before.deal.expectedCloseAt\n deal.ownerUserId = before.deal.ownerUserId\n deal.source = before.deal.source\n }\n await em.flush()\n await syncDealPeople(em, deal, before.people)\n await syncDealCompanies(em, deal, before.companies)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n const resetValues = buildCustomFieldResetMap(before.custom, payload?.after?.custom)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n },\n}\n\nconst deleteDealCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, { dealId: string }> =\n {\n id: 'customers.deals.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Deal id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadDealSnapshot(em, id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Deal id required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const deal = await em.findOne(CustomerDeal, { id, deletedAt: null })\n const record = deal ?? null\n if (!record) throw new CrudHttpError(404, { error: 'Deal not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n await em.nativeDelete(CustomerDealPersonLink, { deal: record })\n await em.nativeDelete(CustomerDealCompanyLink, { deal: record })\n em.remove(record)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n return { dealId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as DealSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.deals.delete', 'Delete deal'),\n resourceKind: 'customers.deal',\n resourceId: before.deal.id,\n tenantId: before.deal.tenantId,\n organizationId: before.deal.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies DealUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DealUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let deal = await em.findOne(CustomerDeal, { id: before.deal.id })\n if (!deal) {\n deal = em.create(CustomerDeal, {\n id: before.deal.id,\n organizationId: before.deal.organizationId,\n tenantId: before.deal.tenantId,\n title: before.deal.title,\n description: before.deal.description,\n status: before.deal.status,\n pipelineStage: before.deal.pipelineStage,\n pipelineId: before.deal.pipelineId,\n pipelineStageId: before.deal.pipelineStageId,\n valueAmount: before.deal.valueAmount,\n valueCurrency: before.deal.valueCurrency,\n probability: before.deal.probability,\n expectedCloseAt: before.deal.expectedCloseAt,\n ownerUserId: before.deal.ownerUserId,\n source: before.deal.source,\n })\n em.persist(deal)\n }\n await em.flush()\n await syncDealPeople(em, deal, before.people)\n await syncDealCompanies(em, deal, before.companies)\n await em.flush()\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'created',\n entity: deal,\n identifiers: {\n id: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n },\n indexer: dealCrudIndexer,\n events: dealCrudEvents,\n })\n\n const resetValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: DEAL_ENTITY_ID,\n recordId: deal.id,\n organizationId: deal.organizationId,\n tenantId: deal.tenantId,\n values: resetValues,\n notify: false,\n })\n }\n },\n }\n\nregisterCommand(createDealCommand)\nregisterCommand(updateDealCommand)\nregisterCommand(deleteDealCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/commands/index.ts"],
|
|
4
|
-
"sourcesContent": ["import './people'\nimport './companies'\nimport './deals'\nimport './activities'\nimport './comments'\nimport './addresses'\nimport './tags'\nimport './todos'\nimport './settings'\nimport './dictionaries'\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;",
|
|
4
|
+
"sourcesContent": ["import './people'\nimport './companies'\nimport './deals'\nimport './activities'\nimport './comments'\nimport './addresses'\nimport './tags'\nimport './todos'\nimport './settings'\nimport './dictionaries'\nimport './pipelines'\nimport './pipeline-stages'\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
2
|
+
import { CustomerPipelineStage, CustomerDeal } from "../data/entities.js";
|
|
3
|
+
import {
|
|
4
|
+
pipelineStageCreateSchema,
|
|
5
|
+
pipelineStageUpdateSchema,
|
|
6
|
+
pipelineStageDeleteSchema,
|
|
7
|
+
pipelineStageReorderSchema
|
|
8
|
+
} from "../data/validators.js";
|
|
9
|
+
import { ensureOrganizationScope, ensureTenantScope, ensureDictionaryEntry } from "./shared.js";
|
|
10
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
11
|
+
const createPipelineStageCommand = {
|
|
12
|
+
id: "customers.pipeline-stages.create",
|
|
13
|
+
async execute(rawInput, ctx) {
|
|
14
|
+
const parsed = pipelineStageCreateSchema.parse(rawInput);
|
|
15
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
16
|
+
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
17
|
+
const em = ctx.container.resolve("em").fork();
|
|
18
|
+
const existingCount = await em.count(CustomerPipelineStage, {
|
|
19
|
+
organizationId: parsed.organizationId,
|
|
20
|
+
tenantId: parsed.tenantId,
|
|
21
|
+
pipelineId: parsed.pipelineId
|
|
22
|
+
});
|
|
23
|
+
const stage = em.create(CustomerPipelineStage, {
|
|
24
|
+
organizationId: parsed.organizationId,
|
|
25
|
+
tenantId: parsed.tenantId,
|
|
26
|
+
pipelineId: parsed.pipelineId,
|
|
27
|
+
label: parsed.label,
|
|
28
|
+
order: parsed.order ?? existingCount,
|
|
29
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
30
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
31
|
+
});
|
|
32
|
+
em.persist(stage);
|
|
33
|
+
await em.flush();
|
|
34
|
+
await ensureDictionaryEntry(em, {
|
|
35
|
+
tenantId: parsed.tenantId,
|
|
36
|
+
organizationId: parsed.organizationId,
|
|
37
|
+
kind: "pipeline_stage",
|
|
38
|
+
value: stage.label,
|
|
39
|
+
color: parsed.color,
|
|
40
|
+
icon: parsed.icon
|
|
41
|
+
});
|
|
42
|
+
await em.flush();
|
|
43
|
+
return { stageId: stage.id };
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const updatePipelineStageCommand = {
|
|
47
|
+
id: "customers.pipeline-stages.update",
|
|
48
|
+
async execute(rawInput, ctx) {
|
|
49
|
+
const parsed = pipelineStageUpdateSchema.parse(rawInput);
|
|
50
|
+
const em = ctx.container.resolve("em").fork();
|
|
51
|
+
const stage = await em.findOne(CustomerPipelineStage, { id: parsed.id });
|
|
52
|
+
if (!stage) throw new CrudHttpError(404, { error: "Pipeline stage not found" });
|
|
53
|
+
ensureTenantScope(ctx, stage.tenantId);
|
|
54
|
+
ensureOrganizationScope(ctx, stage.organizationId);
|
|
55
|
+
if (parsed.label !== void 0) stage.label = parsed.label;
|
|
56
|
+
if (parsed.order !== void 0) stage.order = parsed.order;
|
|
57
|
+
stage.updatedAt = /* @__PURE__ */ new Date();
|
|
58
|
+
await em.flush();
|
|
59
|
+
if (parsed.label !== void 0 || parsed.color !== void 0 || parsed.icon !== void 0) {
|
|
60
|
+
await ensureDictionaryEntry(em, {
|
|
61
|
+
tenantId: stage.tenantId,
|
|
62
|
+
organizationId: stage.organizationId,
|
|
63
|
+
kind: "pipeline_stage",
|
|
64
|
+
value: stage.label,
|
|
65
|
+
color: parsed.color,
|
|
66
|
+
icon: parsed.icon
|
|
67
|
+
});
|
|
68
|
+
await em.flush();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const deletePipelineStageCommand = {
|
|
73
|
+
id: "customers.pipeline-stages.delete",
|
|
74
|
+
async execute(rawInput, ctx) {
|
|
75
|
+
const parsed = pipelineStageDeleteSchema.parse(rawInput);
|
|
76
|
+
const em = ctx.container.resolve("em").fork();
|
|
77
|
+
const stage = await em.findOne(CustomerPipelineStage, { id: parsed.id });
|
|
78
|
+
if (!stage) throw new CrudHttpError(404, { error: "Pipeline stage not found" });
|
|
79
|
+
ensureTenantScope(ctx, stage.tenantId);
|
|
80
|
+
ensureOrganizationScope(ctx, stage.organizationId);
|
|
81
|
+
const activeDealsCount = await em.count(CustomerDeal, {
|
|
82
|
+
pipelineStageId: parsed.id,
|
|
83
|
+
deletedAt: null
|
|
84
|
+
});
|
|
85
|
+
if (activeDealsCount > 0) {
|
|
86
|
+
throw new CrudHttpError(409, { error: "Cannot delete pipeline stage with active deals" });
|
|
87
|
+
}
|
|
88
|
+
em.remove(stage);
|
|
89
|
+
await em.flush();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const reorderPipelineStagesCommand = {
|
|
93
|
+
id: "customers.pipeline-stages.reorder",
|
|
94
|
+
async execute(rawInput, ctx) {
|
|
95
|
+
const parsed = pipelineStageReorderSchema.parse(rawInput);
|
|
96
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
97
|
+
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
98
|
+
const em = ctx.container.resolve("em").fork();
|
|
99
|
+
const ids = parsed.stages.map((s) => s.id);
|
|
100
|
+
const stages = await em.find(CustomerPipelineStage, {
|
|
101
|
+
id: { $in: ids },
|
|
102
|
+
organizationId: parsed.organizationId,
|
|
103
|
+
tenantId: parsed.tenantId
|
|
104
|
+
});
|
|
105
|
+
const stageMap = /* @__PURE__ */ new Map();
|
|
106
|
+
stages.forEach((stage) => stageMap.set(stage.id, stage));
|
|
107
|
+
for (const { id, order } of parsed.stages) {
|
|
108
|
+
const stage = stageMap.get(id);
|
|
109
|
+
if (!stage) continue;
|
|
110
|
+
stage.order = order;
|
|
111
|
+
stage.updatedAt = /* @__PURE__ */ new Date();
|
|
112
|
+
}
|
|
113
|
+
await em.flush();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
registerCommand(createPipelineStageCommand);
|
|
117
|
+
registerCommand(updatePipelineStageCommand);
|
|
118
|
+
registerCommand(deletePipelineStageCommand);
|
|
119
|
+
registerCommand(reorderPipelineStagesCommand);
|
|
120
|
+
export {
|
|
121
|
+
createPipelineStageCommand,
|
|
122
|
+
deletePipelineStageCommand,
|
|
123
|
+
reorderPipelineStagesCommand,
|
|
124
|
+
updatePipelineStageCommand
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=pipeline-stages.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/commands/pipeline-stages.ts"],
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerPipelineStage, CustomerDeal } from '../data/entities'\nimport {\n pipelineStageCreateSchema,\n pipelineStageUpdateSchema,\n pipelineStageDeleteSchema,\n pipelineStageReorderSchema,\n type PipelineStageCreateInput,\n type PipelineStageUpdateInput,\n type PipelineStageDeleteInput,\n type PipelineStageReorderInput,\n} from '../data/validators'\nimport { ensureOrganizationScope, ensureTenantScope, ensureDictionaryEntry } from './shared'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst createPipelineStageCommand: CommandHandler<PipelineStageCreateInput, { stageId: string }> = {\n id: 'customers.pipeline-stages.create',\n async execute(rawInput, ctx) {\n const parsed = pipelineStageCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const existingCount = await em.count(CustomerPipelineStage, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n pipelineId: parsed.pipelineId,\n })\n\n const stage = em.create(CustomerPipelineStage, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n pipelineId: parsed.pipelineId,\n label: parsed.label,\n order: parsed.order ?? existingCount,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(stage)\n await em.flush()\n\n await ensureDictionaryEntry(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n kind: 'pipeline_stage',\n value: stage.label,\n color: parsed.color,\n icon: parsed.icon,\n })\n await em.flush()\n\n return { stageId: stage.id }\n },\n}\n\nconst updatePipelineStageCommand: CommandHandler<PipelineStageUpdateInput, void> = {\n id: 'customers.pipeline-stages.update',\n async execute(rawInput, ctx) {\n const parsed = pipelineStageUpdateSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const stage = await em.findOne(CustomerPipelineStage, { id: parsed.id })\n if (!stage) throw new CrudHttpError(404, { error: 'Pipeline stage not found' })\n\n ensureTenantScope(ctx, stage.tenantId)\n ensureOrganizationScope(ctx, stage.organizationId)\n\n if (parsed.label !== undefined) stage.label = parsed.label\n if (parsed.order !== undefined) stage.order = parsed.order\n stage.updatedAt = new Date()\n\n await em.flush()\n\n if (parsed.label !== undefined || parsed.color !== undefined || parsed.icon !== undefined) {\n await ensureDictionaryEntry(em, {\n tenantId: stage.tenantId,\n organizationId: stage.organizationId,\n kind: 'pipeline_stage',\n value: stage.label,\n color: parsed.color,\n icon: parsed.icon,\n })\n await em.flush()\n }\n },\n}\n\nconst deletePipelineStageCommand: CommandHandler<PipelineStageDeleteInput, void> = {\n id: 'customers.pipeline-stages.delete',\n async execute(rawInput, ctx) {\n const parsed = pipelineStageDeleteSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const stage = await em.findOne(CustomerPipelineStage, { id: parsed.id })\n if (!stage) throw new CrudHttpError(404, { error: 'Pipeline stage not found' })\n\n ensureTenantScope(ctx, stage.tenantId)\n ensureOrganizationScope(ctx, stage.organizationId)\n\n const activeDealsCount = await em.count(CustomerDeal, {\n pipelineStageId: parsed.id,\n deletedAt: null,\n })\n if (activeDealsCount > 0) {\n throw new CrudHttpError(409, { error: 'Cannot delete pipeline stage with active deals' })\n }\n\n em.remove(stage)\n await em.flush()\n },\n}\n\nconst reorderPipelineStagesCommand: CommandHandler<PipelineStageReorderInput, void> = {\n id: 'customers.pipeline-stages.reorder',\n async execute(rawInput, ctx) {\n const parsed = pipelineStageReorderSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const ids = parsed.stages.map((s) => s.id)\n const stages = await em.find(CustomerPipelineStage, {\n id: { $in: ids },\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n })\n\n const stageMap = new Map<string, CustomerPipelineStage>()\n stages.forEach((stage) => stageMap.set(stage.id, stage))\n\n for (const { id, order } of parsed.stages) {\n const stage = stageMap.get(id)\n if (!stage) continue\n stage.order = order\n stage.updatedAt = new Date()\n }\n\n await em.flush()\n },\n}\n\nregisterCommand(createPipelineStageCommand)\nregisterCommand(updatePipelineStageCommand)\nregisterCommand(deletePipelineStageCommand)\nregisterCommand(reorderPipelineStagesCommand)\n\nexport {\n createPipelineStageCommand,\n updatePipelineStageCommand,\n deletePipelineStageCommand,\n reorderPipelineStagesCommand,\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,uBAAuB;AAGhC,SAAS,uBAAuB,oBAAoB;AACpD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP,SAAS,yBAAyB,mBAAmB,6BAA6B;AAClF,SAAS,qBAAqB;AAE9B,MAAM,6BAA4F;AAAA,EAChG,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,0BAA0B,MAAM,QAAQ;AACvD,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAE/D,UAAM,gBAAgB,MAAM,GAAG,MAAM,uBAAuB;AAAA,MAC1D,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,IACrB,CAAC;AAED,UAAM,QAAQ,GAAG,OAAO,uBAAuB;AAAA,MAC7C,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,MACnB,OAAO,OAAO;AAAA,MACd,OAAO,OAAO,SAAS;AAAA,MACvB,WAAW,oBAAI,KAAK;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AACD,OAAG,QAAQ,KAAK;AAChB,UAAM,GAAG,MAAM;AAEf,UAAM,sBAAsB,IAAI;AAAA,MAC9B,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,MAAM;AAAA,MACN,OAAO,MAAM;AAAA,MACb,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,IACf,CAAC;AACD,UAAM,GAAG,MAAM;AAEf,WAAO,EAAE,SAAS,MAAM,GAAG;AAAA,EAC7B;AACF;AAEA,MAAM,6BAA6E;AAAA,EACjF,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,0BAA0B,MAAM,QAAQ;AAEvD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AACvE,QAAI,CAAC,MAAO,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAE9E,sBAAkB,KAAK,MAAM,QAAQ;AACrC,4BAAwB,KAAK,MAAM,cAAc;AAEjD,QAAI,OAAO,UAAU,OAAW,OAAM,QAAQ,OAAO;AACrD,QAAI,OAAO,UAAU,OAAW,OAAM,QAAQ,OAAO;AACrD,UAAM,YAAY,oBAAI,KAAK;AAE3B,UAAM,GAAG,MAAM;AAEf,QAAI,OAAO,UAAU,UAAa,OAAO,UAAU,UAAa,OAAO,SAAS,QAAW;AACzF,YAAM,sBAAsB,IAAI;AAAA,QAC9B,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,MACf,CAAC;AACD,YAAM,GAAG,MAAM;AAAA,IACjB;AAAA,EACF;AACF;AAEA,MAAM,6BAA6E;AAAA,EACjF,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,0BAA0B,MAAM,QAAQ;AAEvD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AACvE,QAAI,CAAC,MAAO,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAE9E,sBAAkB,KAAK,MAAM,QAAQ;AACrC,4BAAwB,KAAK,MAAM,cAAc;AAEjD,UAAM,mBAAmB,MAAM,GAAG,MAAM,cAAc;AAAA,MACpD,iBAAiB,OAAO;AAAA,MACxB,WAAW;AAAA,IACb,CAAC;AACD,QAAI,mBAAmB,GAAG;AACxB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,iDAAiD,CAAC;AAAA,IAC1F;AAEA,OAAG,OAAO,KAAK;AACf,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,MAAM,+BAAgF;AAAA,EACpF,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,SAAS,2BAA2B,MAAM,QAAQ;AACxD,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAE/D,UAAM,MAAM,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE;AACzC,UAAM,SAAS,MAAM,GAAG,KAAK,uBAAuB;AAAA,MAClD,IAAI,EAAE,KAAK,IAAI;AAAA,MACf,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,IACnB,CAAC;AAED,UAAM,WAAW,oBAAI,IAAmC;AACxD,WAAO,QAAQ,CAAC,UAAU,SAAS,IAAI,MAAM,IAAI,KAAK,CAAC;AAEvD,eAAW,EAAE,IAAI,MAAM,KAAK,OAAO,QAAQ;AACzC,YAAM,QAAQ,SAAS,IAAI,EAAE;AAC7B,UAAI,CAAC,MAAO;AACZ,YAAM,QAAQ;AACd,YAAM,YAAY,oBAAI,KAAK;AAAA,IAC7B;AAEA,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,gBAAgB,0BAA0B;AAC1C,gBAAgB,0BAA0B;AAC1C,gBAAgB,0BAA0B;AAC1C,gBAAgB,4BAA4B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
2
|
+
import { CustomerPipeline, CustomerDeal } from "../data/entities.js";
|
|
3
|
+
import {
|
|
4
|
+
pipelineCreateSchema,
|
|
5
|
+
pipelineUpdateSchema,
|
|
6
|
+
pipelineDeleteSchema
|
|
7
|
+
} from "../data/validators.js";
|
|
8
|
+
import { ensureOrganizationScope, ensureTenantScope } from "./shared.js";
|
|
9
|
+
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
10
|
+
const createPipelineCommand = {
|
|
11
|
+
id: "customers.pipelines.create",
|
|
12
|
+
async execute(rawInput, ctx) {
|
|
13
|
+
const parsed = pipelineCreateSchema.parse(rawInput);
|
|
14
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
15
|
+
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
16
|
+
const em = ctx.container.resolve("em").fork();
|
|
17
|
+
if (parsed.isDefault) {
|
|
18
|
+
await em.nativeUpdate(
|
|
19
|
+
CustomerPipeline,
|
|
20
|
+
{ organizationId: parsed.organizationId, tenantId: parsed.tenantId, isDefault: true },
|
|
21
|
+
{ isDefault: false }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const pipeline = em.create(CustomerPipeline, {
|
|
25
|
+
organizationId: parsed.organizationId,
|
|
26
|
+
tenantId: parsed.tenantId,
|
|
27
|
+
name: parsed.name,
|
|
28
|
+
isDefault: parsed.isDefault ?? false,
|
|
29
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
30
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
31
|
+
});
|
|
32
|
+
em.persist(pipeline);
|
|
33
|
+
await em.flush();
|
|
34
|
+
return { pipelineId: pipeline.id };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const updatePipelineCommand = {
|
|
38
|
+
id: "customers.pipelines.update",
|
|
39
|
+
async execute(rawInput, ctx) {
|
|
40
|
+
const parsed = pipelineUpdateSchema.parse(rawInput);
|
|
41
|
+
const em = ctx.container.resolve("em").fork();
|
|
42
|
+
const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id });
|
|
43
|
+
if (!pipeline) throw new CrudHttpError(404, { error: "Pipeline not found" });
|
|
44
|
+
ensureTenantScope(ctx, pipeline.tenantId);
|
|
45
|
+
ensureOrganizationScope(ctx, pipeline.organizationId);
|
|
46
|
+
if (parsed.isDefault && !pipeline.isDefault) {
|
|
47
|
+
await em.nativeUpdate(
|
|
48
|
+
CustomerPipeline,
|
|
49
|
+
{ organizationId: pipeline.organizationId, tenantId: pipeline.tenantId, isDefault: true },
|
|
50
|
+
{ isDefault: false }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (parsed.name !== void 0) pipeline.name = parsed.name;
|
|
54
|
+
if (parsed.isDefault !== void 0) pipeline.isDefault = parsed.isDefault;
|
|
55
|
+
pipeline.updatedAt = /* @__PURE__ */ new Date();
|
|
56
|
+
await em.flush();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const deletePipelineCommand = {
|
|
60
|
+
id: "customers.pipelines.delete",
|
|
61
|
+
async execute(rawInput, ctx) {
|
|
62
|
+
const parsed = pipelineDeleteSchema.parse(rawInput);
|
|
63
|
+
const em = ctx.container.resolve("em").fork();
|
|
64
|
+
const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id });
|
|
65
|
+
if (!pipeline) throw new CrudHttpError(404, { error: "Pipeline not found" });
|
|
66
|
+
ensureTenantScope(ctx, pipeline.tenantId);
|
|
67
|
+
ensureOrganizationScope(ctx, pipeline.organizationId);
|
|
68
|
+
const activeDealsCount = await em.count(CustomerDeal, {
|
|
69
|
+
pipelineId: parsed.id,
|
|
70
|
+
deletedAt: null
|
|
71
|
+
});
|
|
72
|
+
if (activeDealsCount > 0) {
|
|
73
|
+
throw new CrudHttpError(409, { error: "Cannot delete pipeline with active deals" });
|
|
74
|
+
}
|
|
75
|
+
em.remove(pipeline);
|
|
76
|
+
await em.flush();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
registerCommand(createPipelineCommand);
|
|
80
|
+
registerCommand(updatePipelineCommand);
|
|
81
|
+
registerCommand(deletePipelineCommand);
|
|
82
|
+
export {
|
|
83
|
+
createPipelineCommand,
|
|
84
|
+
deletePipelineCommand,
|
|
85
|
+
updatePipelineCommand
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=pipelines.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/customers/commands/pipelines.ts"],
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomerPipeline, CustomerDeal } from '../data/entities'\nimport {\n pipelineCreateSchema,\n pipelineUpdateSchema,\n pipelineDeleteSchema,\n type PipelineCreateInput,\n type PipelineUpdateInput,\n type PipelineDeleteInput,\n} from '../data/validators'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst createPipelineCommand: CommandHandler<PipelineCreateInput, { pipelineId: string }> = {\n id: 'customers.pipelines.create',\n async execute(rawInput, ctx) {\n const parsed = pipelineCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n if (parsed.isDefault) {\n await em.nativeUpdate(\n CustomerPipeline,\n { organizationId: parsed.organizationId, tenantId: parsed.tenantId, isDefault: true },\n { isDefault: false }\n )\n }\n\n const pipeline = em.create(CustomerPipeline, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n name: parsed.name,\n isDefault: parsed.isDefault ?? false,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(pipeline)\n await em.flush()\n\n return { pipelineId: pipeline.id }\n },\n}\n\nconst updatePipelineCommand: CommandHandler<PipelineUpdateInput, void> = {\n id: 'customers.pipelines.update',\n async execute(rawInput, ctx) {\n const parsed = pipelineUpdateSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id })\n if (!pipeline) throw new CrudHttpError(404, { error: 'Pipeline not found' })\n\n ensureTenantScope(ctx, pipeline.tenantId)\n ensureOrganizationScope(ctx, pipeline.organizationId)\n\n if (parsed.isDefault && !pipeline.isDefault) {\n await em.nativeUpdate(\n CustomerPipeline,\n { organizationId: pipeline.organizationId, tenantId: pipeline.tenantId, isDefault: true },\n { isDefault: false }\n )\n }\n\n if (parsed.name !== undefined) pipeline.name = parsed.name\n if (parsed.isDefault !== undefined) pipeline.isDefault = parsed.isDefault\n pipeline.updatedAt = new Date()\n\n await em.flush()\n },\n}\n\nconst deletePipelineCommand: CommandHandler<PipelineDeleteInput, void> = {\n id: 'customers.pipelines.delete',\n async execute(rawInput, ctx) {\n const parsed = pipelineDeleteSchema.parse(rawInput)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const pipeline = await em.findOne(CustomerPipeline, { id: parsed.id })\n if (!pipeline) throw new CrudHttpError(404, { error: 'Pipeline not found' })\n\n ensureTenantScope(ctx, pipeline.tenantId)\n ensureOrganizationScope(ctx, pipeline.organizationId)\n\n const activeDealsCount = await em.count(CustomerDeal, {\n pipelineId: parsed.id,\n deletedAt: null,\n })\n if (activeDealsCount > 0) {\n throw new CrudHttpError(409, { error: 'Cannot delete pipeline with active deals' })\n }\n\n em.remove(pipeline)\n await em.flush()\n },\n}\n\nregisterCommand(createPipelineCommand)\nregisterCommand(updatePipelineCommand)\nregisterCommand(deletePipelineCommand)\n\nexport { createPipelineCommand, updatePipelineCommand, deletePipelineCommand }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -38,11 +38,6 @@ function DictionarySettings() {
|
|
|
38
38
|
title: t("customers.config.dictionaries.sections.dealStatuses.title", "Deal statuses"),
|
|
39
39
|
description: t("customers.config.dictionaries.sections.dealStatuses.description", "Manage the statuses available for deals.")
|
|
40
40
|
},
|
|
41
|
-
{
|
|
42
|
-
kind: "pipeline-stages",
|
|
43
|
-
title: t("customers.config.dictionaries.sections.pipelineStages.title", "Pipeline stages"),
|
|
44
|
-
description: t("customers.config.dictionaries.sections.pipelineStages.description", "Define the stages used in your deal pipeline.")
|
|
45
|
-
},
|
|
46
41
|
{
|
|
47
42
|
kind: "job-titles",
|
|
48
43
|
title: t("customers.config.dictionaries.sections.jobTitles.title", "Job titles"),
|