@open-mercato/core 0.4.5-develop-7f44fcf045 → 0.4.5-develop-8f98466993
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/currencies/api/currencies/route.js +6 -0
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/currencies/api/exchange-rates/route.js +5 -1
- package/dist/modules/currencies/api/exchange-rates/route.js.map +2 -2
- package/dist/modules/currencies/commands/currencies.js +5 -4
- package/dist/modules/currencies/commands/currencies.js.map +2 -2
- package/dist/modules/currencies/commands/exchange-rates.js +5 -4
- package/dist/modules/currencies/commands/exchange-rates.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/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/currencies/api/currencies/route.ts +6 -0
- package/src/modules/currencies/api/exchange-rates/route.ts +5 -1
- package/src/modules/currencies/commands/currencies.ts +5 -4
- package/src/modules/currencies/commands/exchange-rates.ts +5 -4
- 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/workflows/migrations/Migration20260222205305.ts +13 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/currencies/commands/currencies.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { Currency, ExchangeRate } from '../data/entities'\nimport {\n currencyCreateSchema,\n currencyUpdateSchema,\n currencyDeleteSchema,\n type CurrencyCreateInput,\n type CurrencyUpdateInput,\n type CurrencyDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst currencyCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'currency',\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 CurrencySnapshot = {\n id: string\n organizationId: string\n tenantId: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype CurrencyUndoPayload = UndoPayload<CurrencySnapshot>\n\nasync function loadCurrencySnapshot(em: EntityManager, id: string): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(Currency, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n code: record.code,\n name: record.name,\n symbol: record.symbol ?? null,\n decimalPlaces: record.decimalPlaces,\n thousandsSeparator: record.thousandsSeparator ?? null,\n decimalSeparator: record.decimalSeparator ?? null,\n isBase: !!record.isBase,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function enforceBaseCurrency(\n em: EntityManager,\n currencyId: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n await em.nativeUpdate(\n Currency,\n {\n organizationId,\n tenantId,\n id: { $ne: currencyId },\n isBase: true,\n deletedAt: null,\n },\n { isBase: false, updatedAt: new Date() }\n )\n}\n\nconst createCurrencyCommand: CommandHandler<CurrencyCreateInput, { currencyId: string }> = {\n id: 'currencies.currencies.create',\n async execute(input, ctx) {\n const parsed = currencyCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Check for duplicate code\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n\n const now = new Date()\n const record = em.create(Currency, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n code: parsed.code,\n name: parsed.name,\n symbol: parsed.symbol ?? null,\n decimalPlaces: parsed.decimalPlaces ?? 2,\n thousandsSeparator: parsed.thousandsSeparator ?? null,\n decimalSeparator: parsed.decimalSeparator ?? null,\n isBase: parsed.isBase ?? false,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n \n // Enforce only one base currency before flush to prevent race conditions\n if (record.isBase) {\n await enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.create', 'Create currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: string }> = {\n id: 'currencies.currencies.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyUpdateSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Check code uniqueness if changing code\n if (parsed.code && parsed.code !== record.code) {\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n }\n\n const changes = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'code',\n 'name',\n 'symbol',\n 'decimalPlaces',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'isBase',\n 'isActive',\n ])\n\n if (Object.keys(changes).length === 0) {\n return { currencyId: record.id }\n }\n\n for (const [key, change] of Object.entries(\n changes as Record<string, { from: unknown; to: unknown }>,\n )) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n \n // Enforce only one base currency before flush to prevent race conditions\n if (parsed.isBase === true && record.isBase) {\n await enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\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 events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.update', 'Update currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n Object.assign(record, {\n code: before.code,\n name: before.name,\n symbol: before.symbol,\n decimalPlaces: before.decimalPlaces,\n thousandsSeparator: before.thousandsSeparator,\n decimalSeparator: before.decimalSeparator,\n isBase: before.isBase,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteCurrencyCommand: CommandHandler<CurrencyDeleteInput, { currencyId: string }> = {\n id: 'currencies.currencies.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyDeleteSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Prevent deleting base currency\n if (record.isBase) {\n throw new CrudHttpError(400, { error: 'Cannot delete the base currency' })\n }\n\n // Prevent deleting currency with active exchange rates\n const activeRatesCount = await em.count(ExchangeRate, {\n $or: [\n { fromCurrencyCode: record.code },\n { toCurrencyCode: record.code },\n ],\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n deletedAt: null,\n isActive: true,\n })\n \n if (activeRatesCount > 0) {\n throw new CrudHttpError(400, { \n error: `Cannot delete currency ${record.code} because it has ${activeRatesCount} active exchange rate(s). Please delete or deactivate the exchange rates first.` \n })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\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 events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.delete', 'Delete currency'),\n resourceKind: 'currencies.currency',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createCurrencyCommand)\nregisterCommand(updateCurrencyCommand)\nregisterCommand(deleteCurrencyCommand)\n\nexport { createCurrencyCommand, updateCurrencyCommand, deleteCurrencyCommand }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { Currency, ExchangeRate } from '../data/entities'\nimport {\n currencyCreateSchema,\n currencyUpdateSchema,\n currencyDeleteSchema,\n type CurrencyCreateInput,\n type CurrencyUpdateInput,\n type CurrencyDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst currencyCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'currency',\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 CurrencySnapshot = {\n id: string\n organizationId: string\n tenantId: string\n code: string\n name: string\n symbol: string | null\n decimalPlaces: number\n thousandsSeparator: string | null\n decimalSeparator: string | null\n isBase: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype CurrencyUndoPayload = UndoPayload<CurrencySnapshot>\n\nasync function loadCurrencySnapshot(em: EntityManager, id: string): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(Currency, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n code: record.code,\n name: record.name,\n symbol: record.symbol ?? null,\n decimalPlaces: record.decimalPlaces,\n thousandsSeparator: record.thousandsSeparator ?? null,\n decimalSeparator: record.decimalSeparator ?? null,\n isBase: !!record.isBase,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function enforceBaseCurrency(\n em: EntityManager,\n currencyId: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n await em.nativeUpdate(\n Currency,\n {\n organizationId,\n tenantId,\n id: { $ne: currencyId },\n isBase: true,\n deletedAt: null,\n },\n { isBase: false, updatedAt: new Date() }\n )\n}\n\nconst createCurrencyCommand: CommandHandler<CurrencyCreateInput, { currencyId: string }> = {\n id: 'currencies.currencies.create',\n async execute(input, ctx) {\n const parsed = currencyCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Check for duplicate code\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n\n const now = new Date()\n const record = em.create(Currency, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n code: parsed.code,\n name: parsed.name,\n symbol: parsed.symbol ?? null,\n decimalPlaces: parsed.decimalPlaces ?? 2,\n thousandsSeparator: parsed.thousandsSeparator ?? null,\n decimalSeparator: parsed.decimalSeparator ?? null,\n isBase: parsed.isBase ?? false,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n \n // Enforce only one base currency before flush to prevent race conditions\n if (record.isBase) {\n await enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.create', 'Create currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: string }> = {\n id: 'currencies.currencies.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyUpdateSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Check code uniqueness if changing code\n if (parsed.code && parsed.code !== record.code) {\n const existing = await em.findOne(Currency, {\n code: parsed.code,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, { error: 'Currency code already exists for this organization.' })\n }\n }\n\n const allChanges = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'code',\n 'name',\n 'symbol',\n 'decimalPlaces',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'isBase',\n 'isActive',\n ])\n const changes = Object.fromEntries(\n Object.entries(allChanges).filter(([, c]) => c.to !== undefined),\n ) as Record<string, { from: unknown; to: unknown }>\n\n if (Object.keys(changes).length === 0) {\n return { currencyId: record.id }\n }\n\n for (const [key, change] of Object.entries(changes)) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n \n // Enforce only one base currency before flush to prevent race conditions\n if (parsed.isBase === true && record.isBase) {\n await enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\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 events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n const after = snapshots.after as CurrencySnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.update', 'Update currency'),\n resourceKind: 'currencies.currency',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n Object.assign(record, {\n code: before.code,\n name: before.name,\n symbol: before.symbol,\n decimalPlaces: before.decimalPlaces,\n thousandsSeparator: before.thousandsSeparator,\n decimalSeparator: before.decimalSeparator,\n isBase: before.isBase,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteCurrencyCommand: CommandHandler<CurrencyDeleteInput, { currencyId: string }> = {\n id: 'currencies.currencies.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Currency ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadCurrencySnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = currencyDeleteSchema.parse(input)\n requireId(parsed.id, 'Currency ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n\n // Prevent deleting base currency\n if (record.isBase) {\n throw new CrudHttpError(400, { error: 'Cannot delete the base currency' })\n }\n\n // Prevent deleting currency with active exchange rates\n const activeRatesCount = await em.count(ExchangeRate, {\n $or: [\n { fromCurrencyCode: record.code },\n { toCurrencyCode: record.code },\n ],\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n deletedAt: null,\n isActive: true,\n })\n \n if (activeRatesCount > 0) {\n throw new CrudHttpError(400, { \n error: `Cannot delete currency ${record.code} because it has ${activeRatesCount} active exchange rate(s). Please delete or deactivate the exchange rates first.` \n })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\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 events: currencyCrudEvents,\n })\n\n return { currencyId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as CurrencySnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.audit.delete', 'Delete currency'),\n resourceKind: 'currencies.currency',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<CurrencyUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(Currency, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createCurrencyCommand)\nregisterCommand(updateCurrencyCommand)\nregisterCommand(deleteCurrencyCommand)\n\nexport { createCurrencyCommand, updateCurrencyCommand, deleteCurrencyCommand }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -177,7 +177,7 @@ const updateExchangeRateCommand = {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
const
|
|
180
|
+
const allChanges = buildChanges(record, parsed, [
|
|
181
181
|
"fromCurrencyCode",
|
|
182
182
|
"toCurrencyCode",
|
|
183
183
|
"rate",
|
|
@@ -186,12 +186,13 @@ const updateExchangeRateCommand = {
|
|
|
186
186
|
"type",
|
|
187
187
|
"isActive"
|
|
188
188
|
]);
|
|
189
|
+
const changes = Object.fromEntries(
|
|
190
|
+
Object.entries(allChanges).filter(([, c]) => c.to !== void 0)
|
|
191
|
+
);
|
|
189
192
|
if (Object.keys(changes).length === 0) {
|
|
190
193
|
return { exchangeRateId: record.id };
|
|
191
194
|
}
|
|
192
|
-
for (const [key, change] of Object.entries(
|
|
193
|
-
changes
|
|
194
|
-
)) {
|
|
195
|
+
for (const [key, change] of Object.entries(changes)) {
|
|
195
196
|
;
|
|
196
197
|
record[key] = change.to;
|
|
197
198
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/currencies/commands/exchange-rates.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { ExchangeRate, Currency } from '../data/entities'\nimport {\n exchangeRateCreateSchema,\n exchangeRateUpdateSchema,\n exchangeRateDeleteSchema,\n type ExchangeRateCreateInput,\n type ExchangeRateUpdateInput,\n type ExchangeRateDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst exchangeRateCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'exchange_rate',\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 ExchangeRateSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string\n type: string | null\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype ExchangeRateUndoPayload = UndoPayload<ExchangeRateSnapshot>\n\nasync function loadExchangeRateSnapshot(em: EntityManager, id: string): Promise<ExchangeRateSnapshot | null> {\n const record = await em.findOne(ExchangeRate, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n fromCurrencyCode: record.fromCurrencyCode,\n toCurrencyCode: record.toCurrencyCode,\n rate: record.rate,\n date: record.date.toISOString(),\n source: record.source,\n type: record.type ?? null,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function validateCurrenciesExist(\n em: EntityManager,\n fromCode: string,\n toCode: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n const fromCurrency = await em.findOne(Currency, {\n code: fromCode,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!fromCurrency) {\n throw new CrudHttpError(400, { error: `From currency ${fromCode} does not exist or is inactive` })\n }\n\n const toCurrency = await em.findOne(Currency, {\n code: toCode,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!toCurrency) {\n throw new CrudHttpError(400, { error: `To currency ${toCode} does not exist or is inactive` })\n }\n}\n\nconst createExchangeRateCommand: CommandHandler<ExchangeRateCreateInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.create',\n async execute(input, ctx) {\n const parsed = exchangeRateCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Validate currencies exist\n await validateCurrenciesExist(em, parsed.fromCurrencyCode, parsed.toCurrencyCode, parsed.organizationId, parsed.tenantId)\n\n // Check for duplicate rate (same pair + date + source)\n const existing = await em.findOne(ExchangeRate, {\n fromCurrencyCode: parsed.fromCurrencyCode,\n toCurrencyCode: parsed.toCurrencyCode,\n date: parsed.date,\n source: parsed.source,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, {\n error: 'Exchange rate for this currency pair, date, and source already exists',\n })\n }\n\n const now = new Date()\n const record = em.create(ExchangeRate, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n fromCurrencyCode: parsed.fromCurrencyCode,\n toCurrencyCode: parsed.toCurrencyCode,\n rate: parsed.rate,\n date: parsed.date,\n source: parsed.source,\n type: parsed.type ?? null,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadExchangeRateSnapshot(em, result.exchangeRateId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as ExchangeRateSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.create', 'Create exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateExchangeRateCommand: CommandHandler<ExchangeRateUpdateInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Exchange rate ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadExchangeRateSnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = exchangeRateUpdateSchema.parse(input)\n requireId(parsed.id, 'Exchange rate ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Exchange rate not found' })\n }\n\n // Validate currencies if changed\n const fromCode = parsed.fromCurrencyCode ?? record.fromCurrencyCode\n const toCode = parsed.toCurrencyCode ?? record.toCurrencyCode\n if (parsed.fromCurrencyCode || parsed.toCurrencyCode) {\n await validateCurrenciesExist(em, fromCode, toCode, record.organizationId, record.tenantId)\n }\n\n // Check for duplicate if changing pair, date, or source\n if (parsed.fromCurrencyCode || parsed.toCurrencyCode || parsed.date || parsed.source) {\n const date = parsed.date ?? record.date\n const source = parsed.source ?? record.source\n const existing = await em.findOne(ExchangeRate, {\n fromCurrencyCode: fromCode,\n toCurrencyCode: toCode,\n date,\n source,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, {\n error: 'Exchange rate for this currency pair, date, and source already exists',\n })\n }\n }\n\n const changes = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'fromCurrencyCode',\n 'toCurrencyCode',\n 'rate',\n 'date',\n 'source',\n 'type',\n 'isActive',\n ])\n\n if (Object.keys(changes).length === 0) {\n return { exchangeRateId: record.id }\n }\n\n for (const [key, change] of Object.entries(\n changes as Record<string, { from: unknown; to: unknown }>,\n )) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n \n // Validate final state after merging changes\n if (record.fromCurrencyCode === record.toCurrencyCode) {\n throw new CrudHttpError(400, { error: 'From and To currencies must be different' })\n }\n \n const rateValue = parseFloat(record.rate)\n if (isNaN(rateValue) || rateValue <= 0) {\n throw new CrudHttpError(400, { error: 'Rate must be greater than zero' })\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\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 events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadExchangeRateSnapshot(em, result.exchangeRateId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ExchangeRateSnapshot | undefined\n const after = snapshots.after as ExchangeRateSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.update', 'Update exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: before.id })\n if (!record) return\n Object.assign(record, {\n fromCurrencyCode: before.fromCurrencyCode,\n toCurrencyCode: before.toCurrencyCode,\n rate: before.rate,\n date: new Date(before.date),\n source: before.source,\n type: before.type,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteExchangeRateCommand: CommandHandler<ExchangeRateDeleteInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Exchange rate ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadExchangeRateSnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = exchangeRateDeleteSchema.parse(input)\n requireId(parsed.id, 'Exchange rate ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Exchange rate not found' })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\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 events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ExchangeRateSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.delete', 'Delete exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createExchangeRateCommand)\nregisterCommand(updateExchangeRateCommand)\nregisterCommand(deleteExchangeRateCommand)\n\nexport { createExchangeRateCommand, updateExchangeRateCommand, deleteExchangeRateCommand }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { extractUndoPayload, type UndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { ExchangeRate, Currency } from '../data/entities'\nimport {\n exchangeRateCreateSchema,\n exchangeRateUpdateSchema,\n exchangeRateDeleteSchema,\n type ExchangeRateCreateInput,\n type ExchangeRateUpdateInput,\n type ExchangeRateDeleteInput,\n} from '../data/validators'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\n\nconst exchangeRateCrudEvents: CrudEventsConfig = {\n module: 'currencies',\n entity: 'exchange_rate',\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 ExchangeRateSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n fromCurrencyCode: string\n toCurrencyCode: string\n rate: string\n date: string\n source: string\n type: string | null\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype ExchangeRateUndoPayload = UndoPayload<ExchangeRateSnapshot>\n\nasync function loadExchangeRateSnapshot(em: EntityManager, id: string): Promise<ExchangeRateSnapshot | null> {\n const record = await em.findOne(ExchangeRate, { id })\n if (!record) return null\n return {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n fromCurrencyCode: record.fromCurrencyCode,\n toCurrencyCode: record.toCurrencyCode,\n rate: record.rate,\n date: record.date.toISOString(),\n source: record.source,\n type: record.type ?? null,\n isActive: !!record.isActive,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n }\n}\n\nasync function validateCurrenciesExist(\n em: EntityManager,\n fromCode: string,\n toCode: string,\n organizationId: string,\n tenantId: string\n): Promise<void> {\n const fromCurrency = await em.findOne(Currency, {\n code: fromCode,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!fromCurrency) {\n throw new CrudHttpError(400, { error: `From currency ${fromCode} does not exist or is inactive` })\n }\n\n const toCurrency = await em.findOne(Currency, {\n code: toCode,\n organizationId,\n tenantId,\n deletedAt: null,\n })\n if (!toCurrency) {\n throw new CrudHttpError(400, { error: `To currency ${toCode} does not exist or is inactive` })\n }\n}\n\nconst createExchangeRateCommand: CommandHandler<ExchangeRateCreateInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.create',\n async execute(input, ctx) {\n const parsed = exchangeRateCreateSchema.parse(input)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n // Validate currencies exist\n await validateCurrenciesExist(em, parsed.fromCurrencyCode, parsed.toCurrencyCode, parsed.organizationId, parsed.tenantId)\n\n // Check for duplicate rate (same pair + date + source)\n const existing = await em.findOne(ExchangeRate, {\n fromCurrencyCode: parsed.fromCurrencyCode,\n toCurrencyCode: parsed.toCurrencyCode,\n date: parsed.date,\n source: parsed.source,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, {\n error: 'Exchange rate for this currency pair, date, and source already exists',\n })\n }\n\n const now = new Date()\n const record = em.create(ExchangeRate, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n fromCurrencyCode: parsed.fromCurrencyCode,\n toCurrencyCode: parsed.toCurrencyCode,\n rate: parsed.rate,\n date: parsed.date,\n source: parsed.source,\n type: parsed.type ?? null,\n isActive: parsed.isActive !== false,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadExchangeRateSnapshot(em, result.exchangeRateId)\n },\n buildLog: async ({ snapshots }) => {\n const after = snapshots.after as ExchangeRateSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.create', 'Create exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const after = payload?.after ?? null\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: after.id })\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n await em.flush()\n },\n}\n\nconst updateExchangeRateCommand: CommandHandler<ExchangeRateUpdateInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.update',\n async prepare(input, ctx) {\n requireId(input.id, 'Exchange rate ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadExchangeRateSnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = exchangeRateUpdateSchema.parse(input)\n requireId(parsed.id, 'Exchange rate ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Exchange rate not found' })\n }\n\n // Validate currencies if changed\n const fromCode = parsed.fromCurrencyCode ?? record.fromCurrencyCode\n const toCode = parsed.toCurrencyCode ?? record.toCurrencyCode\n if (parsed.fromCurrencyCode || parsed.toCurrencyCode) {\n await validateCurrenciesExist(em, fromCode, toCode, record.organizationId, record.tenantId)\n }\n\n // Check for duplicate if changing pair, date, or source\n if (parsed.fromCurrencyCode || parsed.toCurrencyCode || parsed.date || parsed.source) {\n const date = parsed.date ?? record.date\n const source = parsed.source ?? record.source\n const existing = await em.findOne(ExchangeRate, {\n fromCurrencyCode: fromCode,\n toCurrencyCode: toCode,\n date,\n source,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n id: { $ne: record.id },\n deletedAt: null,\n })\n if (existing) {\n throw new CrudHttpError(400, {\n error: 'Exchange rate for this currency pair, date, and source already exists',\n })\n }\n }\n\n const allChanges = buildChanges(record as unknown as Record<string, unknown>, parsed, [\n 'fromCurrencyCode',\n 'toCurrencyCode',\n 'rate',\n 'date',\n 'source',\n 'type',\n 'isActive',\n ])\n const changes = Object.fromEntries(\n Object.entries(allChanges).filter(([, c]) => c.to !== undefined),\n ) as Record<string, { from: unknown; to: unknown }>\n\n if (Object.keys(changes).length === 0) {\n return { exchangeRateId: record.id }\n }\n\n for (const [key, change] of Object.entries(changes)) {\n ;(record as any)[key] = change.to\n }\n record.updatedAt = new Date()\n \n // Validate final state after merging changes\n if (record.fromCurrencyCode === record.toCurrencyCode) {\n throw new CrudHttpError(400, { error: 'From and To currencies must be different' })\n }\n \n const rateValue = parseFloat(record.rate)\n if (isNaN(rateValue) || rateValue <= 0) {\n throw new CrudHttpError(400, { error: 'Rate must be greater than zero' })\n }\n \n await em.flush()\n\n const de = ctx.container.resolve('dataEngine') as DataEngine\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 events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadExchangeRateSnapshot(em, result.exchangeRateId)\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ExchangeRateSnapshot | undefined\n const after = snapshots.after as ExchangeRateSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.update', 'Update exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? undefined,\n snapshotAfter: after,\n payload: { undo: { before, after } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: before.id })\n if (!record) return\n Object.assign(record, {\n fromCurrencyCode: before.fromCurrencyCode,\n toCurrencyCode: before.toCurrencyCode,\n rate: before.rate,\n date: new Date(before.date),\n source: before.source,\n type: before.type,\n isActive: before.isActive,\n updatedAt: new Date(),\n })\n await em.flush()\n },\n}\n\nconst deleteExchangeRateCommand: CommandHandler<ExchangeRateDeleteInput, { exchangeRateId: string }> = {\n id: 'currencies.exchange_rates.delete',\n async prepare(input, ctx) {\n requireId(input.id, 'Exchange rate ID is required')\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadExchangeRateSnapshot(em, input.id)\n return { before }\n },\n async execute(input, ctx) {\n const parsed = exchangeRateDeleteSchema.parse(input)\n requireId(parsed.id, 'Exchange rate ID is required')\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: parsed.id, deletedAt: null })\n if (!record) {\n throw new CrudHttpError(404, { error: 'Exchange rate not found' })\n }\n\n record.deletedAt = new Date()\n record.isActive = false\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 events: exchangeRateCrudEvents,\n })\n\n return { exchangeRateId: record.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ExchangeRateSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('currencies.rates.audit.delete', 'Delete exchange rate'),\n resourceKind: 'currencies.exchange_rate',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ExchangeRateUndoPayload>(logEntry)\n const before = payload?.before ?? null\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ExchangeRate, { id: before.id })\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n record.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createExchangeRateCommand)\nregisterCommand(updateExchangeRateCommand)\nregisterCommand(deleteExchangeRateCommand)\n\nexport { createExchangeRateCommand, updateExchangeRateCommand, deleteExchangeRateCommand }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,6 +8,8 @@ const features = [
|
|
|
8
8
|
{ id: "customers.activities.view", title: "View activities", module: "customers" },
|
|
9
9
|
{ id: "customers.activities.manage", title: "Manage activities", module: "customers" },
|
|
10
10
|
{ id: "customers.settings.manage", title: "Manage customer settings", module: "customers" },
|
|
11
|
+
{ id: "customers.pipelines.view", title: "View pipelines", module: "customers" },
|
|
12
|
+
{ id: "customers.pipelines.manage", title: "Manage pipelines", module: "customers" },
|
|
11
13
|
{ id: "customers.widgets.todos", title: "Use customer todos widget", module: "customers" },
|
|
12
14
|
{ id: "customers.widgets.next-interactions", title: "Use customer next interactions widget", module: "customers" },
|
|
13
15
|
{ id: "customers.widgets.new-customers", title: "Use customer new customers widget", module: "customers" },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/customers/acl.ts"],
|
|
4
|
-
"sourcesContent": ["export const features = [\n { id: 'customers.people.view', title: 'View people', module: 'customers' },\n { id: 'customers.people.manage', title: 'Manage people', module: 'customers' },\n { id: 'customers.companies.view', title: 'View companies', module: 'customers' },\n { id: 'customers.companies.manage', title: 'Manage companies', module: 'customers' },\n { id: 'customers.deals.view', title: 'View deals', module: 'customers' },\n { id: 'customers.deals.manage', title: 'Manage deals', module: 'customers' },\n { id: 'customers.activities.view', title: 'View activities', module: 'customers' },\n { id: 'customers.activities.manage', title: 'Manage activities', module: 'customers' },\n { id: 'customers.settings.manage', title: 'Manage customer settings', module: 'customers' },\n { id: 'customers.widgets.todos', title: 'Use customer todos widget', module: 'customers' },\n { id: 'customers.widgets.next-interactions', title: 'Use customer next interactions widget', module: 'customers' },\n { id: 'customers.widgets.new-customers', title: 'Use customer new customers widget', module: 'customers' },\n { id: 'customers.widgets.new-deals', title: 'Use customer new deals widget', module: 'customers' },\n]\n\nexport default features\n"],
|
|
5
|
-
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,yBAAyB,OAAO,eAAe,QAAQ,YAAY;AAAA,EACzE,EAAE,IAAI,2BAA2B,OAAO,iBAAiB,QAAQ,YAAY;AAAA,EAC7E,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E,EAAE,IAAI,8BAA8B,OAAO,oBAAoB,QAAQ,YAAY;AAAA,EACnF,EAAE,IAAI,wBAAwB,OAAO,cAAc,QAAQ,YAAY;AAAA,EACvE,EAAE,IAAI,0BAA0B,OAAO,gBAAgB,QAAQ,YAAY;AAAA,EAC3E,EAAE,IAAI,6BAA6B,OAAO,mBAAmB,QAAQ,YAAY;AAAA,EACjF,EAAE,IAAI,+BAA+B,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EACrF,EAAE,IAAI,6BAA6B,OAAO,4BAA4B,QAAQ,YAAY;AAAA,EAC1F,EAAE,IAAI,2BAA2B,OAAO,6BAA6B,QAAQ,YAAY;AAAA,EACzF,EAAE,IAAI,uCAAuC,OAAO,yCAAyC,QAAQ,YAAY;AAAA,EACjH,EAAE,IAAI,mCAAmC,OAAO,qCAAqC,QAAQ,YAAY;AAAA,EACzG,EAAE,IAAI,+BAA+B,OAAO,iCAAiC,QAAQ,YAAY;AACnG;AAEA,IAAO,cAAQ;",
|
|
4
|
+
"sourcesContent": ["export const features = [\n { id: 'customers.people.view', title: 'View people', module: 'customers' },\n { id: 'customers.people.manage', title: 'Manage people', module: 'customers' },\n { id: 'customers.companies.view', title: 'View companies', module: 'customers' },\n { id: 'customers.companies.manage', title: 'Manage companies', module: 'customers' },\n { id: 'customers.deals.view', title: 'View deals', module: 'customers' },\n { id: 'customers.deals.manage', title: 'Manage deals', module: 'customers' },\n { id: 'customers.activities.view', title: 'View activities', module: 'customers' },\n { id: 'customers.activities.manage', title: 'Manage activities', module: 'customers' },\n { id: 'customers.settings.manage', title: 'Manage customer settings', module: 'customers' },\n { id: 'customers.pipelines.view', title: 'View pipelines', module: 'customers' },\n { id: 'customers.pipelines.manage', title: 'Manage pipelines', module: 'customers' },\n { id: 'customers.widgets.todos', title: 'Use customer todos widget', module: 'customers' },\n { id: 'customers.widgets.next-interactions', title: 'Use customer next interactions widget', module: 'customers' },\n { id: 'customers.widgets.new-customers', title: 'Use customer new customers widget', module: 'customers' },\n { id: 'customers.widgets.new-deals', title: 'Use customer new deals widget', module: 'customers' },\n]\n\nexport default features\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,yBAAyB,OAAO,eAAe,QAAQ,YAAY;AAAA,EACzE,EAAE,IAAI,2BAA2B,OAAO,iBAAiB,QAAQ,YAAY;AAAA,EAC7E,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E,EAAE,IAAI,8BAA8B,OAAO,oBAAoB,QAAQ,YAAY;AAAA,EACnF,EAAE,IAAI,wBAAwB,OAAO,cAAc,QAAQ,YAAY;AAAA,EACvE,EAAE,IAAI,0BAA0B,OAAO,gBAAgB,QAAQ,YAAY;AAAA,EAC3E,EAAE,IAAI,6BAA6B,OAAO,mBAAmB,QAAQ,YAAY;AAAA,EACjF,EAAE,IAAI,+BAA+B,OAAO,qBAAqB,QAAQ,YAAY;AAAA,EACrF,EAAE,IAAI,6BAA6B,OAAO,4BAA4B,QAAQ,YAAY;AAAA,EAC1F,EAAE,IAAI,4BAA4B,OAAO,kBAAkB,QAAQ,YAAY;AAAA,EAC/E,EAAE,IAAI,8BAA8B,OAAO,oBAAoB,QAAQ,YAAY;AAAA,EACnF,EAAE,IAAI,2BAA2B,OAAO,6BAA6B,QAAQ,YAAY;AAAA,EACzF,EAAE,IAAI,uCAAuC,OAAO,yCAAyC,QAAQ,YAAY;AAAA,EACjH,EAAE,IAAI,mCAAmC,OAAO,qCAAqC,QAAQ,YAAY;AAAA,EACzG,EAAE,IAAI,+BAA+B,OAAO,iCAAiC,QAAQ,YAAY;AACnG;AAEA,IAAO,cAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -161,6 +161,8 @@ async function GET(request, context) {
|
|
|
161
161
|
description: deal.description ?? null,
|
|
162
162
|
status: deal.status ?? null,
|
|
163
163
|
pipelineStage: deal.pipelineStage ?? null,
|
|
164
|
+
pipelineId: deal.pipelineId ?? null,
|
|
165
|
+
pipelineStageId: deal.pipelineStageId ?? null,
|
|
164
166
|
valueAmount: deal.valueAmount ?? null,
|
|
165
167
|
valueCurrency: deal.valueCurrency ?? null,
|
|
166
168
|
probability: deal.probability ?? null,
|
|
@@ -189,6 +191,8 @@ const dealDetailResponseSchema = z.object({
|
|
|
189
191
|
description: z.string().nullable().optional(),
|
|
190
192
|
status: z.string().nullable().optional(),
|
|
191
193
|
pipelineStage: z.string().nullable().optional(),
|
|
194
|
+
pipelineId: z.string().uuid().nullable().optional(),
|
|
195
|
+
pipelineStageId: z.string().uuid().nullable().optional(),
|
|
192
196
|
valueAmount: z.number().nullable().optional(),
|
|
193
197
|
valueCurrency: z.string().nullable().optional(),
|
|
194
198
|
probability: z.number().nullable().optional(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/api/deals/%5Bid%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerDeal,\n CustomerDealPersonLink,\n CustomerDealCompanyLink,\n CustomerEntity,\n} from '../../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { decryptEntitiesWithFallbackScope } from '@open-mercato/shared/lib/encryption/subscriber'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nfunction notFound(message: string) {\n return NextResponse.json({ error: message }, { status: 404 })\n}\n\nfunction forbidden(message: string) {\n return NextResponse.json({ error: message }, { status: 403 })\n}\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\nfunction normalizePersonAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const email =\n typeof entity.primaryEmail === 'string' && entity.primaryEmail.trim().length\n ? entity.primaryEmail.trim()\n : null\n const phone =\n typeof entity.primaryPhone === 'string' && entity.primaryPhone.trim().length\n ? entity.primaryPhone.trim()\n : null\n const jobTitle =\n entity.personProfile &&\n typeof (entity.personProfile as any)?.jobTitle === 'string' &&\n (entity.personProfile as any).jobTitle.trim().length\n ? ((entity.personProfile as any).jobTitle as string).trim()\n : null\n const subtitle = jobTitle ?? email ?? phone ?? null\n const label = displayName.length ? displayName : email ?? phone ?? entity.id\n return { label, subtitle }\n}\n\nfunction normalizeCompanyAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const domain =\n entity.companyProfile &&\n typeof (entity.companyProfile as any)?.domain === 'string' &&\n (entity.companyProfile as any).domain.trim().length\n ? ((entity.companyProfile as any).domain as string).trim()\n : null\n const website =\n entity.companyProfile &&\n typeof (entity.companyProfile as any)?.websiteUrl === 'string' &&\n (entity.companyProfile as any).websiteUrl.trim().length\n ? ((entity.companyProfile as any).websiteUrl as string).trim()\n : null\n const subtitle = domain ?? website ?? null\n const label = displayName.length ? displayName : domain ?? website ?? entity.id\n return { label, subtitle }\n}\n\nexport async function GET(request: Request, context: { params?: Record<string, unknown> }) {\n const parsedParams = paramsSchema.safeParse(context.params)\n if (!parsedParams.success) {\n return notFound('Deal not found')\n }\n\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(request)\n if (!auth?.sub && !auth?.isApiKey) {\n return NextResponse.json({ error: 'Authentication required' }, { status: 401 })\n }\n\n let rbac: RbacService | null = null\n try {\n rbac = (container.resolve('rbacService') as RbacService)\n } catch {\n rbac = null\n }\n\n if (!rbac || !auth?.sub) {\n return forbidden('Access denied')\n }\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n if (!hasFeature) {\n return forbidden('Access denied')\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const em = (container.resolve('em') as EntityManager)\n\n const deal = await findOneWithDecryption(\n em,\n CustomerDeal,\n { id: parsedParams.data.id, deletedAt: null },\n {\n populate: ['people.person', 'people.person.personProfile', 'companies.company', 'companies.company.companyProfile'],\n },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (!deal) {\n return notFound('Deal not found')\n }\n\n if (auth.tenantId && deal.tenantId && auth.tenantId !== deal.tenantId) {\n return notFound('Deal not found')\n }\n\n const allowedOrgIds = new Set<string>()\n if (Array.isArray(scope?.filterIds)) {\n scope.filterIds.forEach((id) => {\n if (typeof id === 'string' && id.trim().length) allowedOrgIds.add(id)\n })\n } else if (auth.orgId) {\n allowedOrgIds.add(auth.orgId)\n }\n if (allowedOrgIds.size && deal.organizationId && !allowedOrgIds.has(deal.organizationId)) {\n return forbidden('Access denied')\n }\n\n const decryptionScope = {\n tenantId: deal.tenantId ?? auth.tenantId ?? null,\n organizationId: deal.organizationId ?? auth.orgId ?? null,\n }\n const personLinks = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { populate: ['person', 'person.personProfile'] },\n decryptionScope,\n )\n const companyLinks = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { populate: ['company', 'company.companyProfile'] },\n decryptionScope,\n )\n const fallbackTenantId = deal.tenantId ?? auth.tenantId ?? null\n const fallbackOrgId = deal.organizationId ?? auth.orgId ?? null\n await decryptEntitiesWithFallbackScope(personLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n await decryptEntitiesWithFallbackScope(companyLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n\n const people: DealAssociation[] = personLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.person as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n\n const companies: DealAssociation[] = companyLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.company as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n\n const customFieldValues = await loadCustomFieldValues({\n em,\n entityId: E.customers.customer_deal,\n recordIds: [deal.id],\n tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },\n organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },\n tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value): value is string => !!value),\n })\n const customFields = customFieldValues[deal.id] ?? {}\n\n const viewerUserId = auth.isApiKey ? null : auth.sub ?? null\n let viewerName: string | null = null\n let viewerEmail: string | null = auth.email ?? null\n if (viewerUserId) {\n const viewer = await em.findOne(User, { id: viewerUserId })\n viewerName = viewer?.name ?? null\n viewerEmail = viewer?.email ?? viewerEmail ?? null\n }\n\n return NextResponse.json({\n deal: {\n id: deal.id,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status ?? null,\n pipelineStage: deal.pipelineStage ?? null,\n valueAmount: deal.valueAmount ?? null,\n valueCurrency: deal.valueCurrency ?? null,\n probability: deal.probability ?? null,\n expectedCloseAt: deal.expectedCloseAt ? deal.expectedCloseAt.toISOString() : null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n organizationId: deal.organizationId ?? null,\n tenantId: deal.tenantId ?? null,\n createdAt: deal.createdAt.toISOString(),\n updatedAt: deal.updatedAt.toISOString(),\n },\n people,\n companies,\n customFields,\n viewer: {\n userId: viewerUserId,\n name: viewerName,\n email: viewerEmail,\n },\n })\n}\n\nconst dealDetailResponseSchema = z.object({\n deal: z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipelineStage: z.string().nullable().optional(),\n valueAmount: z.number().nullable().optional(),\n valueCurrency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expectedCloseAt: z.string().nullable().optional(),\n ownerUserId: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n people: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('person'),\n }),\n ),\n companies: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('company'),\n }),\n ),\n customFields: z.record(z.string(), z.unknown()),\n viewer: z.object({\n userId: z.string().uuid().nullable(),\n name: z.string().nullable(),\n email: z.string().nullable(),\n }),\n})\n\nconst dealDetailErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Fetch deal detail',\n methods: {\n GET: {\n summary: 'Fetch deal with associations',\n description: 'Returns a deal with linked people, companies, custom fields, and viewer context.',\n responses: [\n { status: 200, description: 'Deal detail payload', schema: dealDetailResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: dealDetailErrorSchema },\n { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealDetailErrorSchema },\n { status: 404, description: 'Deal not found', schema: dealDetailErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerDeal,\n CustomerDealPersonLink,\n CustomerDealCompanyLink,\n CustomerEntity,\n} from '../../../data/entities'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { decryptEntitiesWithFallbackScope } from '@open-mercato/shared/lib/encryption/subscriber'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n})\n\nfunction notFound(message: string) {\n return NextResponse.json({ error: message }, { status: 404 })\n}\n\nfunction forbidden(message: string) {\n return NextResponse.json({ error: message }, { status: 403 })\n}\n\ntype DealAssociation = {\n id: string\n label: string\n subtitle: string | null\n kind: 'person' | 'company'\n}\n\nfunction normalizePersonAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const email =\n typeof entity.primaryEmail === 'string' && entity.primaryEmail.trim().length\n ? entity.primaryEmail.trim()\n : null\n const phone =\n typeof entity.primaryPhone === 'string' && entity.primaryPhone.trim().length\n ? entity.primaryPhone.trim()\n : null\n const jobTitle =\n entity.personProfile &&\n typeof (entity.personProfile as any)?.jobTitle === 'string' &&\n (entity.personProfile as any).jobTitle.trim().length\n ? ((entity.personProfile as any).jobTitle as string).trim()\n : null\n const subtitle = jobTitle ?? email ?? phone ?? null\n const label = displayName.length ? displayName : email ?? phone ?? entity.id\n return { label, subtitle }\n}\n\nfunction normalizeCompanyAssociation(entity: CustomerEntity): { label: string; subtitle: string | null } {\n const displayName = typeof entity.displayName === 'string' ? entity.displayName.trim() : ''\n const domain =\n entity.companyProfile &&\n typeof (entity.companyProfile as any)?.domain === 'string' &&\n (entity.companyProfile as any).domain.trim().length\n ? ((entity.companyProfile as any).domain as string).trim()\n : null\n const website =\n entity.companyProfile &&\n typeof (entity.companyProfile as any)?.websiteUrl === 'string' &&\n (entity.companyProfile as any).websiteUrl.trim().length\n ? ((entity.companyProfile as any).websiteUrl as string).trim()\n : null\n const subtitle = domain ?? website ?? null\n const label = displayName.length ? displayName : domain ?? website ?? entity.id\n return { label, subtitle }\n}\n\nexport async function GET(request: Request, context: { params?: Record<string, unknown> }) {\n const parsedParams = paramsSchema.safeParse(context.params)\n if (!parsedParams.success) {\n return notFound('Deal not found')\n }\n\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(request)\n if (!auth?.sub && !auth?.isApiKey) {\n return NextResponse.json({ error: 'Authentication required' }, { status: 401 })\n }\n\n let rbac: RbacService | null = null\n try {\n rbac = (container.resolve('rbacService') as RbacService)\n } catch {\n rbac = null\n }\n\n if (!rbac || !auth?.sub) {\n return forbidden('Access denied')\n }\n const hasFeature = await rbac.userHasAllFeatures(auth.sub, ['customers.deals.view'], {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n })\n if (!hasFeature) {\n return forbidden('Access denied')\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n const em = (container.resolve('em') as EntityManager)\n\n const deal = await findOneWithDecryption(\n em,\n CustomerDeal,\n { id: parsedParams.data.id, deletedAt: null },\n {\n populate: ['people.person', 'people.person.personProfile', 'companies.company', 'companies.company.companyProfile'],\n },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (!deal) {\n return notFound('Deal not found')\n }\n\n if (auth.tenantId && deal.tenantId && auth.tenantId !== deal.tenantId) {\n return notFound('Deal not found')\n }\n\n const allowedOrgIds = new Set<string>()\n if (Array.isArray(scope?.filterIds)) {\n scope.filterIds.forEach((id) => {\n if (typeof id === 'string' && id.trim().length) allowedOrgIds.add(id)\n })\n } else if (auth.orgId) {\n allowedOrgIds.add(auth.orgId)\n }\n if (allowedOrgIds.size && deal.organizationId && !allowedOrgIds.has(deal.organizationId)) {\n return forbidden('Access denied')\n }\n\n const decryptionScope = {\n tenantId: deal.tenantId ?? auth.tenantId ?? null,\n organizationId: deal.organizationId ?? auth.orgId ?? null,\n }\n const personLinks = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: deal.id },\n { populate: ['person', 'person.personProfile'] },\n decryptionScope,\n )\n const companyLinks = await findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: deal.id },\n { populate: ['company', 'company.companyProfile'] },\n decryptionScope,\n )\n const fallbackTenantId = deal.tenantId ?? auth.tenantId ?? null\n const fallbackOrgId = deal.organizationId ?? auth.orgId ?? null\n await decryptEntitiesWithFallbackScope(personLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n await decryptEntitiesWithFallbackScope(companyLinks, {\n em,\n tenantId: fallbackTenantId,\n organizationId: fallbackOrgId,\n })\n\n const people: DealAssociation[] = personLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.person as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizePersonAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'person' })\n return acc\n }, [])\n\n const companies: DealAssociation[] = companyLinks.reduce<DealAssociation[]>((acc, link) => {\n const entity = link.company as CustomerEntity | null\n if (!entity || entity.deletedAt) return acc\n const { label, subtitle } = normalizeCompanyAssociation(entity)\n acc.push({ id: entity.id, label, subtitle, kind: 'company' })\n return acc\n }, [])\n\n const customFieldValues = await loadCustomFieldValues({\n em,\n entityId: E.customers.customer_deal,\n recordIds: [deal.id],\n tenantIdByRecord: { [deal.id]: deal.tenantId ?? null },\n organizationIdByRecord: { [deal.id]: deal.organizationId ?? null },\n tenantFallbacks: [deal.tenantId ?? auth.tenantId ?? null].filter((value): value is string => !!value),\n })\n const customFields = customFieldValues[deal.id] ?? {}\n\n const viewerUserId = auth.isApiKey ? null : auth.sub ?? null\n let viewerName: string | null = null\n let viewerEmail: string | null = auth.email ?? null\n if (viewerUserId) {\n const viewer = await em.findOne(User, { id: viewerUserId })\n viewerName = viewer?.name ?? null\n viewerEmail = viewer?.email ?? viewerEmail ?? null\n }\n\n return NextResponse.json({\n deal: {\n id: deal.id,\n title: deal.title,\n description: deal.description ?? null,\n status: deal.status ?? null,\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 ? deal.expectedCloseAt.toISOString() : null,\n ownerUserId: deal.ownerUserId ?? null,\n source: deal.source ?? null,\n organizationId: deal.organizationId ?? null,\n tenantId: deal.tenantId ?? null,\n createdAt: deal.createdAt.toISOString(),\n updatedAt: deal.updatedAt.toISOString(),\n },\n people,\n companies,\n customFields,\n viewer: {\n userId: viewerUserId,\n name: viewerName,\n email: viewerEmail,\n },\n })\n}\n\nconst dealDetailResponseSchema = z.object({\n deal: z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipelineStage: z.string().nullable().optional(),\n pipelineId: z.string().uuid().nullable().optional(),\n pipelineStageId: z.string().uuid().nullable().optional(),\n valueAmount: z.number().nullable().optional(),\n valueCurrency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expectedCloseAt: z.string().nullable().optional(),\n ownerUserId: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n }),\n people: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('person'),\n }),\n ),\n companies: z.array(\n z.object({\n id: z.string().uuid(),\n label: z.string(),\n subtitle: z.string().nullable().optional(),\n kind: z.literal('company'),\n }),\n ),\n customFields: z.record(z.string(), z.unknown()),\n viewer: z.object({\n userId: z.string().uuid().nullable(),\n name: z.string().nullable(),\n email: z.string().nullable(),\n }),\n})\n\nconst dealDetailErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Fetch deal detail',\n methods: {\n GET: {\n summary: 'Fetch deal with associations',\n description: 'Returns a deal with linked people, companies, custom fields, and viewer context.',\n responses: [\n { status: 200, description: 'Deal detail payload', schema: dealDetailResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: dealDetailErrorSchema },\n { status: 403, description: 'Forbidden for tenant/organization scope', schema: dealDetailErrorSchema },\n { status: 404, description: 'Deal not found', schema: dealDetailErrorSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,6 +20,8 @@ const listSchema = z.object({
|
|
|
20
20
|
search: z.string().optional(),
|
|
21
21
|
status: z.string().optional(),
|
|
22
22
|
pipelineStage: z.string().optional(),
|
|
23
|
+
pipelineId: z.string().uuid().optional(),
|
|
24
|
+
pipelineStageId: z.string().uuid().optional(),
|
|
23
25
|
sortField: z.string().optional(),
|
|
24
26
|
sortDir: z.enum(["asc", "desc"]).optional(),
|
|
25
27
|
personEntityId: z.string().uuid().optional(),
|
|
@@ -82,6 +84,8 @@ const crud = makeCrudRoute({
|
|
|
82
84
|
"description",
|
|
83
85
|
"status",
|
|
84
86
|
"pipeline_stage",
|
|
87
|
+
"pipeline_id",
|
|
88
|
+
"pipeline_stage_id",
|
|
85
89
|
"value_amount",
|
|
86
90
|
"value_currency",
|
|
87
91
|
"probability",
|
|
@@ -113,6 +117,12 @@ const crud = makeCrudRoute({
|
|
|
113
117
|
if (query.pipelineStage) {
|
|
114
118
|
filters.pipeline_stage = { $eq: query.pipelineStage };
|
|
115
119
|
}
|
|
120
|
+
if (query.pipelineId) {
|
|
121
|
+
filters.pipeline_id = { $eq: query.pipelineId };
|
|
122
|
+
}
|
|
123
|
+
if (query.pipelineStageId) {
|
|
124
|
+
filters.pipeline_stage_id = { $eq: query.pipelineStageId };
|
|
125
|
+
}
|
|
116
126
|
return filters;
|
|
117
127
|
}
|
|
118
128
|
},
|
|
@@ -307,6 +317,8 @@ const dealListItemSchema = z.object({
|
|
|
307
317
|
description: z.string().nullable().optional(),
|
|
308
318
|
status: z.string().nullable().optional(),
|
|
309
319
|
pipeline_stage: z.string().nullable().optional(),
|
|
320
|
+
pipeline_id: z.string().uuid().nullable().optional(),
|
|
321
|
+
pipeline_stage_id: z.string().uuid().nullable().optional(),
|
|
310
322
|
value_amount: z.number().nullable().optional(),
|
|
311
323
|
value_currency: z.string().nullable().optional(),
|
|
312
324
|
probability: z.number().nullable().optional(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customers/api/deals/route.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from '../../data/entities'\nimport { dealCreateSchema, dealUpdateSchema } from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '../utils'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n status: z.string().optional(),\n pipelineStage: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n personEntityId: z.string().uuid().optional(),\n companyEntityId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n}\n\nexport const metadata = routeMetadata\n\ntype DealListQuery = z.infer<typeof listSchema>\n\nfunction parseUuid(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const result = z.string().uuid().safeParse(trimmed)\n return result.success ? trimmed : null\n}\n\nfunction normalizeUuidList(values: Array<unknown>): string[] {\n const set = new Set<string>()\n values.forEach((candidate) => {\n if (Array.isArray(candidate)) {\n candidate.forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n if (typeof candidate === 'string' && candidate.includes(',')) {\n candidate\n .split(',')\n .map((entry) => entry.trim())\n .forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n const parsed = parseUuid(candidate)\n if (parsed) set.add(parsed)\n })\n return Array.from(set)\n}\n\nconst crud = makeCrudRoute<unknown, unknown, DealListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: CustomerDeal,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.customers.customer_deal,\n },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_deal,\n fields: [\n 'id',\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'value_amount',\n 'value_currency',\n 'probability',\n 'expected_close_at',\n 'owner_user_id',\n 'source',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n decorateCustomFields: {\n entityIds: E.customers.customer_deal,\n },\n sortFieldMap: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n title: 'title',\n value: 'value_amount',\n },\n buildFilters: async (query: any) => {\n const filters: Record<string, any> = {}\n if (query.search) {\n filters.title = { $ilike: `%${escapeLikePattern(query.search)}%` }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.pipelineStage) {\n filters.pipeline_stage = { $eq: query.pipelineStage }\n }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'customers.deals.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.dealId ?? result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'customers.deals.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.deals.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.deal_required', 'Deal id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n beforeList: (query, ctx) => {\n const url = ctx.request ? new URL(ctx.request.url) : null\n const legacyPersonId = query.personEntityId ?? null\n const legacyCompanyId = query.companyEntityId ?? null\n const allPersonCandidates: unknown[] = []\n const allCompanyCandidates: unknown[] = []\n if (legacyPersonId) allPersonCandidates.push(legacyPersonId)\n if (legacyCompanyId) allCompanyCandidates.push(legacyCompanyId)\n if (url) {\n const personParams = url.searchParams.getAll('personId')\n const companyParams = url.searchParams.getAll('companyId')\n if (personParams.length) allPersonCandidates.push(...personParams)\n if (companyParams.length) allCompanyCandidates.push(...companyParams)\n const legacyRepeatPerson = url.searchParams.getAll('personEntityId')\n const legacyRepeatCompany = url.searchParams.getAll('companyEntityId')\n if (legacyRepeatPerson.length) allPersonCandidates.push(...legacyRepeatPerson)\n if (legacyRepeatCompany.length) allCompanyCandidates.push(...legacyRepeatCompany)\n }\n const personIds = normalizeUuidList(allPersonCandidates)\n const companyIds = normalizeUuidList(allCompanyCandidates)\n ;(ctx as any).__dealsFilters = {\n personIds,\n companyIds,\n }\n },\n afterList: async (payload, ctx) => {\n const filters = ((ctx as any).__dealsFilters || {}) as {\n personIds?: string[]\n companyIds?: string[]\n }\n const selectedPersonIds = Array.isArray(filters.personIds) ? filters.personIds : []\n const selectedCompanyIds = Array.isArray(filters.companyIds) ? filters.companyIds : []\n\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const scopeSource = (items[0] ?? {}) as Record<string, unknown>\n const fallbackTenantId =\n (typeof scopeSource.tenantId === 'string' && scopeSource.tenantId.trim().length\n ? scopeSource.tenantId\n : typeof (scopeSource as any).tenant_id === 'string' && (scopeSource as any).tenant_id.trim().length\n ? (scopeSource as any).tenant_id\n : null) ?? (ctx as any)?.auth?.tenantId ?? null\n const fallbackOrganizationId =\n (typeof scopeSource.organizationId === 'string' && scopeSource.organizationId.trim().length\n ? scopeSource.organizationId\n : typeof (scopeSource as any).organization_id === 'string' &&\n (scopeSource as any).organization_id.trim().length\n ? (scopeSource as any).organization_id\n : null) ?? (ctx as any)?.auth?.orgId ?? null\n const ids = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const candidate = (item as Record<string, unknown>).id\n return typeof candidate === 'string' && candidate.trim().length ? candidate : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) {\n payload.items = []\n payload.total = 0\n return\n }\n try {\n const em = (ctx.container.resolve('em') as EntityManager)\n const [allPersonLinks, allCompanyLinks] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: { $in: ids } },\n { populate: ['person'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: { $in: ids } },\n { populate: ['company'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n ])\n\n const personAssignments = new Map<string, { id: string; label: string }[]>()\n const personMemberships = new Map<string, Set<string>>()\n allPersonLinks.forEach((link) => {\n const deal = link.deal\n const dealId =\n typeof deal === 'string'\n ? deal\n : deal && typeof deal === 'object' && 'id' in deal && typeof (deal as any).id === 'string'\n ? (deal as any).id\n : null\n if (!dealId) return\n const personRef = link.person\n const personId =\n typeof personRef === 'string'\n ? personRef\n : personRef && typeof personRef === 'object' && 'id' in personRef && typeof (personRef as any).id === 'string'\n ? (personRef as any).id\n : null\n if (!personId) return\n const label =\n personRef && typeof personRef === 'object' && 'displayName' in personRef && typeof (personRef as any).displayName === 'string'\n ? (personRef as any).displayName\n : ''\n const bucket = personAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === personId)) {\n bucket.push({ id: personId, label })\n personAssignments.set(dealId, bucket)\n }\n const membership = personMemberships.get(dealId) ?? new Set<string>()\n membership.add(personId)\n personMemberships.set(dealId, membership)\n })\n\n const companyAssignments = new Map<string, { id: string; label: string }[]>()\n const companyMemberships = new Map<string, Set<string>>()\n allCompanyLinks.forEach((link) => {\n const deal = link.deal\n const dealId =\n typeof deal === 'string'\n ? deal\n : deal && typeof deal === 'object' && 'id' in deal && typeof (deal as any).id === 'string'\n ? (deal as any).id\n : null\n if (!dealId) return\n const companyRef = link.company\n const companyId =\n typeof companyRef === 'string'\n ? companyRef\n : companyRef && typeof companyRef === 'object' && 'id' in companyRef && typeof (companyRef as any).id === 'string'\n ? (companyRef as any).id\n : null\n if (!companyId) return\n const label =\n companyRef && typeof companyRef === 'object' && 'displayName' in companyRef && typeof (companyRef as any).displayName === 'string'\n ? (companyRef as any).displayName\n : ''\n const bucket = companyAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === companyId)) {\n bucket.push({ id: companyId, label })\n companyAssignments.set(dealId, bucket)\n }\n const membership = companyMemberships.get(dealId) ?? new Set<string>()\n membership.add(companyId)\n companyMemberships.set(dealId, membership)\n })\n\n const hasPersonFilter = selectedPersonIds.length > 0\n const hasCompanyFilter = selectedCompanyIds.length > 0\n const matchesAll = (selected: string[], memberships: Map<string, Set<string>>, dealId: string) => {\n if (!selected.length) return true\n const membership = memberships.get(dealId)\n if (!membership || membership.size === 0) return false\n return selected.every((id) => membership.has(id))\n }\n\n const enhancedItems = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const candidate = typeof data.id === 'string' ? data.id : null\n if (!candidate || !candidate.trim().length) return null\n const people = personAssignments.get(candidate) ?? []\n const companies = companyAssignments.get(candidate) ?? []\n const matchesPerson = matchesAll(selectedPersonIds, personMemberships, candidate)\n const matchesCompany = matchesAll(selectedCompanyIds, companyMemberships, candidate)\n if (!matchesPerson || !matchesCompany) return null\n const tenantIdRaw =\n typeof data.tenantId === 'string'\n ? data.tenantId\n : typeof data.tenant_id === 'string'\n ? data.tenant_id\n : null\n const organizationIdRaw =\n typeof data.organizationId === 'string'\n ? data.organizationId\n : typeof data.organization_id === 'string'\n ? data.organization_id\n : null\n const tenantId = tenantIdRaw && tenantIdRaw.trim().length ? tenantIdRaw.trim() : null\n const organizationId = organizationIdRaw && organizationIdRaw.trim().length ? organizationIdRaw.trim() : null\n return {\n ...data,\n personIds: people.map((entry) => entry.id),\n people,\n companyIds: companies.map((entry) => entry.id),\n companies,\n tenantId,\n organizationId,\n }\n })\n .filter(\n (item: Record<string, unknown> | null): item is Record<string, unknown> => item !== null,\n )\n\n payload.items = enhancedItems\n if (hasPersonFilter || hasCompanyFilter) {\n payload.total = enhancedItems.length\n payload.totalPages = 1\n payload.page = 1\n }\n } catch (err) {\n console.warn('[customers.deals] failed to filter by person/company link', err)\n // fall back to unfiltered list to avoid breaking the endpoint\n }\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst dealAssociationSchema = z.object({\n id: z.string().uuid(),\n label: z.string().nullable(),\n})\n\nconst dealListItemSchema = z\n .object({\n id: z.string().uuid(),\n title: z.string().nullable(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipeline_stage: z.string().nullable().optional(),\n value_amount: z.number().nullable().optional(),\n value_currency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expected_close_at: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n personIds: z.array(z.string().uuid()).optional(),\n people: z.array(dealAssociationSchema).optional(),\n companyIds: z.array(z.string().uuid()).optional(),\n companies: z.array(dealAssociationSchema).optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n\nconst dealCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Deal',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(dealListItemSchema),\n create: {\n schema: dealCreateSchema,\n responseSchema: dealCreateResponseSchema,\n description: 'Creates a sales deal, optionally associating people and companies.',\n },\n update: {\n schema: dealUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates pipeline position, metadata, or associations for an existing deal.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a deal by `id`. The identifier may be provided in the body or query parameters.',\n },\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { CustomerDeal, CustomerDealPersonLink, CustomerDealCompanyLink } from '../../data/entities'\nimport { dealCreateSchema, dealUpdateSchema } from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '../utils'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n status: z.string().optional(),\n pipelineStage: z.string().optional(),\n pipelineId: z.string().uuid().optional(),\n pipelineStageId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n personEntityId: z.string().uuid().optional(),\n companyEntityId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.deals.manage'] },\n}\n\nexport const metadata = routeMetadata\n\ntype DealListQuery = z.infer<typeof listSchema>\n\nfunction parseUuid(value: unknown): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const result = z.string().uuid().safeParse(trimmed)\n return result.success ? trimmed : null\n}\n\nfunction normalizeUuidList(values: Array<unknown>): string[] {\n const set = new Set<string>()\n values.forEach((candidate) => {\n if (Array.isArray(candidate)) {\n candidate.forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n if (typeof candidate === 'string' && candidate.includes(',')) {\n candidate\n .split(',')\n .map((entry) => entry.trim())\n .forEach((entry) => {\n const parsed = parseUuid(entry)\n if (parsed) set.add(parsed)\n })\n return\n }\n const parsed = parseUuid(candidate)\n if (parsed) set.add(parsed)\n })\n return Array.from(set)\n}\n\nconst crud = makeCrudRoute<unknown, unknown, DealListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: CustomerDeal,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.customers.customer_deal,\n },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_deal,\n fields: [\n 'id',\n 'title',\n 'description',\n 'status',\n 'pipeline_stage',\n 'pipeline_id',\n 'pipeline_stage_id',\n 'value_amount',\n 'value_currency',\n 'probability',\n 'expected_close_at',\n 'owner_user_id',\n 'source',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n decorateCustomFields: {\n entityIds: E.customers.customer_deal,\n },\n sortFieldMap: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n title: 'title',\n value: 'value_amount',\n },\n buildFilters: async (query: any) => {\n const filters: Record<string, any> = {}\n if (query.search) {\n filters.title = { $ilike: `%${escapeLikePattern(query.search)}%` }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.pipelineStage) {\n filters.pipeline_stage = { $eq: query.pipelineStage }\n }\n if (query.pipelineId) {\n filters.pipeline_id = { $eq: query.pipelineId }\n }\n if (query.pipelineStageId) {\n filters.pipeline_stage_id = { $eq: query.pipelineStageId }\n }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'customers.deals.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({ id: result?.dealId ?? result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'customers.deals.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(dealUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'customers.deals.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.deal_required', 'Deal id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n beforeList: (query, ctx) => {\n const url = ctx.request ? new URL(ctx.request.url) : null\n const legacyPersonId = query.personEntityId ?? null\n const legacyCompanyId = query.companyEntityId ?? null\n const allPersonCandidates: unknown[] = []\n const allCompanyCandidates: unknown[] = []\n if (legacyPersonId) allPersonCandidates.push(legacyPersonId)\n if (legacyCompanyId) allCompanyCandidates.push(legacyCompanyId)\n if (url) {\n const personParams = url.searchParams.getAll('personId')\n const companyParams = url.searchParams.getAll('companyId')\n if (personParams.length) allPersonCandidates.push(...personParams)\n if (companyParams.length) allCompanyCandidates.push(...companyParams)\n const legacyRepeatPerson = url.searchParams.getAll('personEntityId')\n const legacyRepeatCompany = url.searchParams.getAll('companyEntityId')\n if (legacyRepeatPerson.length) allPersonCandidates.push(...legacyRepeatPerson)\n if (legacyRepeatCompany.length) allCompanyCandidates.push(...legacyRepeatCompany)\n }\n const personIds = normalizeUuidList(allPersonCandidates)\n const companyIds = normalizeUuidList(allCompanyCandidates)\n ;(ctx as any).__dealsFilters = {\n personIds,\n companyIds,\n }\n },\n afterList: async (payload, ctx) => {\n const filters = ((ctx as any).__dealsFilters || {}) as {\n personIds?: string[]\n companyIds?: string[]\n }\n const selectedPersonIds = Array.isArray(filters.personIds) ? filters.personIds : []\n const selectedCompanyIds = Array.isArray(filters.companyIds) ? filters.companyIds : []\n\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const scopeSource = (items[0] ?? {}) as Record<string, unknown>\n const fallbackTenantId =\n (typeof scopeSource.tenantId === 'string' && scopeSource.tenantId.trim().length\n ? scopeSource.tenantId\n : typeof (scopeSource as any).tenant_id === 'string' && (scopeSource as any).tenant_id.trim().length\n ? (scopeSource as any).tenant_id\n : null) ?? (ctx as any)?.auth?.tenantId ?? null\n const fallbackOrganizationId =\n (typeof scopeSource.organizationId === 'string' && scopeSource.organizationId.trim().length\n ? scopeSource.organizationId\n : typeof (scopeSource as any).organization_id === 'string' &&\n (scopeSource as any).organization_id.trim().length\n ? (scopeSource as any).organization_id\n : null) ?? (ctx as any)?.auth?.orgId ?? null\n const ids = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const candidate = (item as Record<string, unknown>).id\n return typeof candidate === 'string' && candidate.trim().length ? candidate : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) {\n payload.items = []\n payload.total = 0\n return\n }\n try {\n const em = (ctx.container.resolve('em') as EntityManager)\n const [allPersonLinks, allCompanyLinks] = await Promise.all([\n findWithDecryption(\n em,\n CustomerDealPersonLink,\n { deal: { $in: ids } },\n { populate: ['person'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n findWithDecryption(\n em,\n CustomerDealCompanyLink,\n { deal: { $in: ids } },\n { populate: ['company'] },\n { tenantId: fallbackTenantId, organizationId: fallbackOrganizationId },\n ),\n ])\n\n const personAssignments = new Map<string, { id: string; label: string }[]>()\n const personMemberships = new Map<string, Set<string>>()\n allPersonLinks.forEach((link) => {\n const deal = link.deal\n const dealId =\n typeof deal === 'string'\n ? deal\n : deal && typeof deal === 'object' && 'id' in deal && typeof (deal as any).id === 'string'\n ? (deal as any).id\n : null\n if (!dealId) return\n const personRef = link.person\n const personId =\n typeof personRef === 'string'\n ? personRef\n : personRef && typeof personRef === 'object' && 'id' in personRef && typeof (personRef as any).id === 'string'\n ? (personRef as any).id\n : null\n if (!personId) return\n const label =\n personRef && typeof personRef === 'object' && 'displayName' in personRef && typeof (personRef as any).displayName === 'string'\n ? (personRef as any).displayName\n : ''\n const bucket = personAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === personId)) {\n bucket.push({ id: personId, label })\n personAssignments.set(dealId, bucket)\n }\n const membership = personMemberships.get(dealId) ?? new Set<string>()\n membership.add(personId)\n personMemberships.set(dealId, membership)\n })\n\n const companyAssignments = new Map<string, { id: string; label: string }[]>()\n const companyMemberships = new Map<string, Set<string>>()\n allCompanyLinks.forEach((link) => {\n const deal = link.deal\n const dealId =\n typeof deal === 'string'\n ? deal\n : deal && typeof deal === 'object' && 'id' in deal && typeof (deal as any).id === 'string'\n ? (deal as any).id\n : null\n if (!dealId) return\n const companyRef = link.company\n const companyId =\n typeof companyRef === 'string'\n ? companyRef\n : companyRef && typeof companyRef === 'object' && 'id' in companyRef && typeof (companyRef as any).id === 'string'\n ? (companyRef as any).id\n : null\n if (!companyId) return\n const label =\n companyRef && typeof companyRef === 'object' && 'displayName' in companyRef && typeof (companyRef as any).displayName === 'string'\n ? (companyRef as any).displayName\n : ''\n const bucket = companyAssignments.get(dealId) ?? []\n if (!bucket.some((entry) => entry.id === companyId)) {\n bucket.push({ id: companyId, label })\n companyAssignments.set(dealId, bucket)\n }\n const membership = companyMemberships.get(dealId) ?? new Set<string>()\n membership.add(companyId)\n companyMemberships.set(dealId, membership)\n })\n\n const hasPersonFilter = selectedPersonIds.length > 0\n const hasCompanyFilter = selectedCompanyIds.length > 0\n const matchesAll = (selected: string[], memberships: Map<string, Set<string>>, dealId: string) => {\n if (!selected.length) return true\n const membership = memberships.get(dealId)\n if (!membership || membership.size === 0) return false\n return selected.every((id) => membership.has(id))\n }\n\n const enhancedItems = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const candidate = typeof data.id === 'string' ? data.id : null\n if (!candidate || !candidate.trim().length) return null\n const people = personAssignments.get(candidate) ?? []\n const companies = companyAssignments.get(candidate) ?? []\n const matchesPerson = matchesAll(selectedPersonIds, personMemberships, candidate)\n const matchesCompany = matchesAll(selectedCompanyIds, companyMemberships, candidate)\n if (!matchesPerson || !matchesCompany) return null\n const tenantIdRaw =\n typeof data.tenantId === 'string'\n ? data.tenantId\n : typeof data.tenant_id === 'string'\n ? data.tenant_id\n : null\n const organizationIdRaw =\n typeof data.organizationId === 'string'\n ? data.organizationId\n : typeof data.organization_id === 'string'\n ? data.organization_id\n : null\n const tenantId = tenantIdRaw && tenantIdRaw.trim().length ? tenantIdRaw.trim() : null\n const organizationId = organizationIdRaw && organizationIdRaw.trim().length ? organizationIdRaw.trim() : null\n return {\n ...data,\n personIds: people.map((entry) => entry.id),\n people,\n companyIds: companies.map((entry) => entry.id),\n companies,\n tenantId,\n organizationId,\n }\n })\n .filter(\n (item: Record<string, unknown> | null): item is Record<string, unknown> => item !== null,\n )\n\n payload.items = enhancedItems\n if (hasPersonFilter || hasCompanyFilter) {\n payload.total = enhancedItems.length\n payload.totalPages = 1\n payload.page = 1\n }\n } catch (err) {\n console.warn('[customers.deals] failed to filter by person/company link', err)\n // fall back to unfiltered list to avoid breaking the endpoint\n }\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst dealAssociationSchema = z.object({\n id: z.string().uuid(),\n label: z.string().nullable(),\n})\n\nconst dealListItemSchema = z\n .object({\n id: z.string().uuid(),\n title: z.string().nullable(),\n description: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n pipeline_stage: z.string().nullable().optional(),\n pipeline_id: z.string().uuid().nullable().optional(),\n pipeline_stage_id: z.string().uuid().nullable().optional(),\n value_amount: z.number().nullable().optional(),\n value_currency: z.string().nullable().optional(),\n probability: z.number().nullable().optional(),\n expected_close_at: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n source: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n personIds: z.array(z.string().uuid()).optional(),\n people: z.array(dealAssociationSchema).optional(),\n companyIds: z.array(z.string().uuid()).optional(),\n companies: z.array(dealAssociationSchema).optional(),\n organizationId: z.string().uuid().nullable().optional(),\n tenantId: z.string().uuid().nullable().optional(),\n })\n .passthrough()\n\nconst dealCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Deal',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(dealListItemSchema),\n create: {\n schema: dealCreateSchema,\n responseSchema: dealCreateResponseSchema,\n description: 'Creates a sales deal, optionally associating people and companies.',\n },\n update: {\n schema: dealUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates pipeline position, metadata, or associations for an existing deal.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a deal by `id`. The identifier may be provided in the body or query parameters.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["parsed"]
|
|
7
7
|
}
|