@open-mercato/core 0.9.1-develop.7532.1.8eac7e198f → 0.9.1-develop.7536.1.f9a5776a07

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/logout.js +8 -5
  3. package/dist/modules/auth/api/logout.js.map +2 -2
  4. package/dist/modules/currencies/backend/currencies/[id]/page.js +5 -2
  5. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  6. package/dist/modules/currencies/commands/currencies.js +399 -137
  7. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  8. package/dist/modules/currencies/lib/seeds.js +2 -0
  9. package/dist/modules/currencies/lib/seeds.js.map +2 -2
  10. package/dist/modules/customers/commands/companies.js +10 -3
  11. package/dist/modules/customers/commands/companies.js.map +2 -2
  12. package/dist/modules/customers/commands/interactionRestoreDetails.js +75 -0
  13. package/dist/modules/customers/commands/interactionRestoreDetails.js.map +7 -0
  14. package/dist/modules/customers/commands/people.js +10 -3
  15. package/dist/modules/customers/commands/people.js.map +2 -2
  16. package/package.json +7 -7
  17. package/src/modules/auth/api/logout.ts +9 -5
  18. package/src/modules/currencies/backend/currencies/[id]/page.tsx +5 -1
  19. package/src/modules/currencies/commands/currencies.ts +475 -168
  20. package/src/modules/currencies/i18n/de.json +4 -0
  21. package/src/modules/currencies/i18n/en.json +4 -0
  22. package/src/modules/currencies/i18n/es.json +4 -0
  23. package/src/modules/currencies/i18n/ko.json +4 -0
  24. package/src/modules/currencies/i18n/pl.json +4 -0
  25. package/src/modules/currencies/lib/seeds.ts +2 -0
  26. package/src/modules/customers/commands/companies.ts +16 -4
  27. package/src/modules/customers/commands/interactionRestoreDetails.ts +110 -0
  28. package/src/modules/customers/commands/people.ts +16 -4
@@ -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 { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, conflict, isUniqueViolation } 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'\nimport { buildCurrencyCommandWhere, ensureCurrencyCommandScope } from './scope'\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(\n em: EntityManager,\n id: string,\n ctx: Parameters<typeof buildCurrencyCommandWhere>[0],\n): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id }),\n )\n if (!record) return null\n ensureCurrencyCommandScope(ctx, record)\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 ensureCurrencyCommandScope(ctx, parsed)\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 conflict('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 // Demote any existing base currency and insert the new record in one\n // transaction; a partial commit would leave zero or two base currencies.\n try {\n await withAtomicFlush(\n em,\n [\n () =>\n record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\n } catch (err) {\n if (isUniqueViolation(err, 'currencies_code_scope_unique')) {\n throw conflict('Currency code already exists for this organization.')\n }\n throw err\n }\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, ctx)\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 redo: makeCreateRedo<Currency, CurrencySnapshot, CurrencyCreateInput, { currencyId: string }>({\n entityClass: Currency,\n buildResult: (entity) => ({ currencyId: entity.id }),\n events: currencyCrudEvents,\n afterRestore: async ({ em, entity }) => {\n if (entity.isBase) {\n await enforceBaseCurrency(em, entity.id, entity.organizationId, entity.tenantId)\n await em.flush()\n }\n },\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, ctx)\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(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, record)\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 conflict('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 // Demote any existing base currency and persist the scalar changes in one\n // transaction; a partial commit would leave zero or two base currencies.\n // The scalar mutations live in the first phase so the per-phase flush issues\n // the pending changeset on the managed `record` before `enforceBaseCurrency`\n // runs its interleaved `nativeUpdate` (which would otherwise drop the pending\n // UPDATE under v7).\n await withAtomicFlush(\n em,\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 () =>\n parsed.isBase === true && record.isBase\n ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)\n : undefined,\n ],\n { transaction: true },\n )\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, ctx)\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, ctx)\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(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!record) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, record)\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 { resolveRedoSnapshot, restoreCreatedRow, reviveSnapshotSeed } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, conflict, isUniqueViolation } 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'\nimport { buildCurrencyCommandWhere, ensureCurrencyCommandScope } from './scope'\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\nasync function loadCurrencySnapshot(\n em: EntityManager,\n id: string,\n ctx: Parameters<typeof buildCurrencyCommandWhere>[0],\n): Promise<CurrencySnapshot | null> {\n const record = await em.findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id }),\n )\n if (!record) return null\n ensureCurrencyCommandScope(ctx, record)\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\ntype CurrencyScope = {\n tenantId: string\n organizationId: string\n}\n\ntype BaseCurrencyTransition = {\n promoted: boolean\n demotedBaseCurrencyIds: string[]\n}\n\ntype CurrencyCommandResult = {\n currencyId: string\n base?: BaseCurrencyTransition\n}\n\ntype CurrencyUndoPayload = UndoPayload<CurrencySnapshot> & {\n base?: BaseCurrencyTransition\n}\n\nconst NO_BASE_TRANSITION: BaseCurrencyTransition = { promoted: false, demotedBaseCurrencyIds: [] }\n\nconst BASE_CURRENCY_LOCK_SQL = 'select pg_advisory_xact_lock(hashtextextended(?, 0))'\n\nconst UPDATABLE_CURRENCY_FIELDS = [\n 'code',\n 'name',\n 'symbol',\n 'decimalPlaces',\n 'thousandsSeparator',\n 'decimalSeparator',\n 'isBase',\n 'isActive',\n] as const\n\nfunction baseCurrencyLockKey(scope: CurrencyScope): string {\n return `currencies:base:${scope.tenantId}:${scope.organizationId}`\n}\n\n/**\n * Serialize every currency write inside one tenant and organization so the\n * \"exactly one live base currency\" invariant is decided on fresh rows. The\n * transaction owns the lock, so PostgreSQL releases it on commit/rollback.\n */\nasync function lockBaseCurrencyScope(em: EntityManager, scope: CurrencyScope): Promise<void> {\n if (!em.isInTransaction()) {\n throw new Error('[internal] Currency base lock requires an active transaction')\n }\n await em.execute(BASE_CURRENCY_LOCK_SQL, [baseCurrencyLockKey(scope)])\n}\n\nasync function findLiveBaseCurrencyIds(\n em: EntityManager,\n scope: CurrencyScope,\n excludeId?: string,\n): Promise<string[]> {\n const rows = await em.find(\n Currency,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n isBase: true,\n deletedAt: null,\n ...(excludeId ? { id: { $ne: excludeId } } : {}),\n },\n { orderBy: { createdAt: 'asc', id: 'asc' } },\n )\n return rows.map((row) => row.id)\n}\n\nasync function setBaseFlag(\n em: EntityManager,\n scope: CurrencyScope,\n ids: string[],\n isBase: boolean,\n): Promise<void> {\n if (ids.length === 0) return\n await em.nativeUpdate(\n Currency,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n id: { $in: ids },\n },\n { isBase, updatedAt: new Date() },\n )\n}\n\nasync function findFirstLivePredecessorId(\n em: EntityManager,\n scope: CurrencyScope,\n candidateIds: string[],\n): Promise<string | null> {\n if (candidateIds.length === 0) return null\n const rows = await em.find(Currency, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n id: { $in: candidateIds },\n deletedAt: null,\n })\n const liveIds = new Set(rows.map((row) => row.id))\n return candidateIds.find((id) => liveIds.has(id)) ?? null\n}\n\nasync function translatedHttpError(status: number, key: string, fallback: string): Promise<CrudHttpError> {\n const { translate } = await resolveTranslations()\n return new CrudHttpError(status, { error: translate(key, fallback) })\n}\n\nasync function resolvePredecessorOrRefuse(\n em: EntityManager,\n scope: CurrencyScope,\n candidateIds: string[],\n): Promise<string> {\n const predecessorId = await findFirstLivePredecessorId(em, scope, candidateIds)\n if (!predecessorId) {\n throw await translatedHttpError(\n 409,\n 'currencies.errors.undoWouldClearBase',\n \"This change can't be undone because it would leave the organization without a base currency.\",\n )\n }\n return predecessorId\n}\n\nfunction countActiveRatesForCode(em: EntityManager, scope: CurrencyScope, code: string): Promise<number> {\n return em.count(ExchangeRate, {\n $or: [\n { fromCurrencyCode: code },\n { toCurrencyCode: code },\n ],\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n isActive: true,\n })\n}\n\nfunction readScopedSnapshotRecord(em: EntityManager, snapshot: CurrencySnapshot): Promise<Currency | null> {\n return em.findOne(Currency, {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n })\n}\n\nfunction scopeOf(record: CurrencyScope): CurrencyScope {\n return { tenantId: record.tenantId, organizationId: record.organizationId }\n}\n\nfunction omitIsBase(input: CurrencyUpdateInput): Partial<CurrencyUpdateInput> {\n const rest: Partial<CurrencyUpdateInput> = { ...input }\n delete rest.isBase\n return rest\n}\n\nfunction isPromotionByLegacyLog(payload: CurrencyUndoPayload | null): boolean {\n return payload?.before?.isBase === false && payload?.after?.isBase === true\n}\n\nfunction isDemotionByLegacyLog(payload: CurrencyUndoPayload | null): boolean {\n return !payload?.base && payload?.before?.isBase === true && payload?.after?.isBase === false\n}\n\nconst createCurrencyCommand: CommandHandler<CurrencyCreateInput, CurrencyCommandResult> = {\n id: 'currencies.currencies.create',\n async execute(input, ctx) {\n const parsed = currencyCreateSchema.parse(input)\n ensureCurrencyCommandScope(ctx, parsed)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = scopeOf(parsed)\n let demotedBaseCurrencyIds: string[] = []\n let record!: Currency\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\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 conflict('Currency code already exists for this organization.')\n }\n if (parsed.isBase) {\n demotedBaseCurrencyIds = await findLiveBaseCurrencyIds(em, scope)\n await setBaseFlag(em, scope, demotedBaseCurrencyIds, false)\n }\n },\n () => {\n const now = new Date()\n 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 ],\n { transaction: true },\n )\n } catch (err) {\n if (isUniqueViolation(err, 'currencies_code_scope_unique')) {\n throw conflict('Currency code already exists for this organization.')\n }\n throw err\n }\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 record.isBase\n ? { currencyId: record.id, base: { promoted: true, demotedBaseCurrencyIds } }\n : { 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, ctx)\n },\n buildLog: async ({ snapshots, result }) => {\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, base: result?.base ?? NO_BASE_TRANSITION } },\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 scope = scopeOf(after)\n const promoted = payload?.base ? payload.base.promoted : after.isBase\n const predecessorIds = payload?.base?.demotedBaseCurrencyIds ?? []\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record: Currency | null = null\n let restoredBaseId: string | null = null\n\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n const current = await readScopedSnapshotRecord(em, after)\n if (!current || current.deletedAt) return\n const activeRatesCount = await countActiveRatesForCode(em, scope, current.code)\n if (activeRatesCount > 0) {\n throw await translatedHttpError(\n 409,\n 'currencies.errors.undoHasRates',\n \"This change can't be undone because exchange rates now use this currency. Delete or deactivate those exchange rates first.\",\n )\n }\n if (current.isBase) {\n restoredBaseId = await resolvePredecessorOrRefuse(em, scope, promoted ? predecessorIds : [])\n }\n record = current\n },\n () => {\n if (!record) return\n record.deletedAt = new Date()\n record.isActive = false\n record.isBase = false\n },\n () => (restoredBaseId ? setBaseFlag(em, scope, [restoredBaseId], true) : undefined),\n ],\n { transaction: true },\n )\n },\n redo: async ({ ctx, logEntry }) => {\n const snapshot = resolveRedoSnapshot<CurrencySnapshot>(logEntry)\n if (!snapshot || !snapshot.id) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for create command' })\n }\n const scope = scopeOf(snapshot)\n const isBase = snapshot.isBase === true\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let demotedBaseCurrencyIds: string[] = []\n let record!: Currency\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n if (isBase) {\n demotedBaseCurrencyIds = await findLiveBaseCurrencyIds(em, scope, snapshot.id)\n await setBaseFlag(em, scope, demotedBaseCurrencyIds, false)\n }\n },\n async () => {\n record = await restoreCreatedRow(\n em,\n Currency,\n snapshot.id,\n () => reviveSnapshotSeed(snapshot as unknown as Record<string, unknown>),\n (lookupEm) => readScopedSnapshotRecord(lookupEm, snapshot),\n )\n record.isBase = isBase\n },\n ],\n { transaction: true },\n )\n } catch (err) {\n if (isUniqueViolation(err)) {\n throw conflict('[internal] Cannot redo: a record with the same unique key already exists.')\n }\n throw err\n }\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 isBase\n ? { currencyId: record.id, base: { promoted: true, demotedBaseCurrencyIds } }\n : { currencyId: record.id }\n },\n}\n\nconst updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, CurrencyCommandResult> = {\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, ctx)\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 scopeRecord = await (ctx.container.resolve('em') as EntityManager).fork().findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!scopeRecord) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, scopeRecord)\n const scope = scopeOf(scopeRecord)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record!: Currency\n let changes: Record<string, { from: unknown; to: unknown }> = {}\n let promoted = false\n let demotedBaseCurrencyIds: string[] = []\n\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n const current = await em.findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!current) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, current)\n record = current\n\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 conflict('Currency code already exists for this organization.')\n }\n }\n\n if (parsed.isBase === false && record.isBase) {\n throw await translatedHttpError(\n 400,\n 'currencies.errors.baseCurrencyUnset',\n 'Choose another currency as the base instead of clearing it.',\n )\n }\n\n const allChanges = buildChanges(\n record as unknown as Record<string, unknown>,\n parsed,\n UPDATABLE_CURRENCY_FIELDS,\n )\n changes = Object.fromEntries(\n Object.entries(allChanges).filter(([, change]) => change.to !== undefined),\n ) as Record<string, { from: unknown; to: unknown }>\n\n promoted = changes.isBase?.to === true\n if (promoted) {\n demotedBaseCurrencyIds = await findLiveBaseCurrencyIds(em, scope, record.id)\n }\n },\n () => {\n if (Object.keys(changes).length === 0) return\n const assignments = Object.fromEntries(\n Object.entries(changes).map(([key, change]) => [key, change.to]),\n )\n Object.assign(record, assignments, { updatedAt: new Date() })\n },\n () => setBaseFlag(em, scope, demotedBaseCurrencyIds, false),\n ],\n { transaction: true },\n )\n\n const result: CurrencyCommandResult = promoted\n ? { currencyId: record.id, base: { promoted: true, demotedBaseCurrencyIds } }\n : { currencyId: record.id }\n if (Object.keys(changes).length === 0) {\n return result\n }\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 result\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadCurrencySnapshot(em, result.currencyId, ctx)\n },\n buildLog: async ({ input, 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 const base = result?.base ?? NO_BASE_TRANSITION\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, base } },\n redoInput: base.promoted ? input : omitIsBase(input),\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 scope = scopeOf(before)\n const promoted = payload?.base ? payload.base.promoted : isPromotionByLegacyLog(payload)\n const predecessorIds = payload?.base?.demotedBaseCurrencyIds ?? []\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const demotedByLegacyLog = isDemotionByLegacyLog(payload)\n let record: Currency | null = null\n let restoredBaseId: string | null = null\n let repromote = false\n\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n const current = await readScopedSnapshotRecord(em, before)\n if (!current) return\n if (promoted && current.isBase && !current.deletedAt) {\n restoredBaseId = await resolvePredecessorOrRefuse(em, scope, predecessorIds)\n }\n if (demotedByLegacyLog && !current.isBase && !current.deletedAt) {\n repromote = (await findLiveBaseCurrencyIds(em, scope)).length === 0\n }\n record = current\n },\n () => {\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 isActive: before.isActive,\n updatedAt: new Date(),\n })\n if (restoredBaseId) record.isBase = false\n if (repromote) record.isBase = true\n },\n () => (restoredBaseId ? setBaseFlag(em, scope, [restoredBaseId], true) : undefined),\n ],\n { transaction: true },\n )\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, ctx)\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 scopeRecord = await (ctx.container.resolve('em') as EntityManager).fork().findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!scopeRecord) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, scopeRecord)\n const scope = scopeOf(scopeRecord)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record!: Currency\n\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n const current = await em.findOne(\n Currency,\n buildCurrencyCommandWhere<Currency>(ctx, { id: parsed.id }),\n )\n if (!current) {\n throw new CrudHttpError(404, { error: 'Currency not found' })\n }\n ensureCurrencyCommandScope(ctx, current)\n\n if (current.isBase) {\n throw new CrudHttpError(400, { error: 'Cannot delete the base currency' })\n }\n\n const activeRatesCount = await countActiveRatesForCode(em, scope, current.code)\n if (activeRatesCount > 0) {\n throw new CrudHttpError(400, {\n error: `Cannot delete currency ${current.code} because it has ${activeRatesCount} active exchange rate(s). Please delete or deactivate the exchange rates first.`,\n })\n }\n record = current\n },\n () => {\n record.deletedAt = new Date()\n record.isActive = false\n },\n ],\n { transaction: true },\n )\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 }) => {\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 scope = scopeOf(before)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record: Currency | null = null\n let reviveAsNonBase = false\n\n await withAtomicFlush(\n em,\n [\n async () => {\n await lockBaseCurrencyScope(em, scope)\n const current = await readScopedSnapshotRecord(em, before)\n if (!current) return\n if (current.isBase) {\n const otherBaseIds = await findLiveBaseCurrencyIds(em, scope, current.id)\n reviveAsNonBase = otherBaseIds.length > 0\n }\n record = current\n },\n () => {\n if (!record) return\n record.deletedAt = null\n record.isActive = before.isActive\n if (reviveAsNonBase) record.isBase = false\n record.updatedAt = new Date()\n },\n ],\n { transaction: true },\n )\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
  }
@@ -110,6 +110,7 @@ async function seedExampleCurrencies(em, scope) {
110
110
  for (const entry of existingEntries) {
111
111
  existingMap.set(entry.code, entry);
112
112
  }
113
+ const hasLiveBase = existingEntries.some((entry) => entry.isBase && !entry.deletedAt);
113
114
  let touched = false;
114
115
  for (const curr of SEED_CURRENCIES) {
115
116
  const current = existingMap.get(curr.code);
@@ -126,6 +127,7 @@ async function seedExampleCurrencies(em, scope) {
126
127
  tenantId: scope.tenantId,
127
128
  organizationId: scope.organizationId,
128
129
  ...curr,
130
+ isBase: curr.isBase && !hasLiveBase,
129
131
  createdAt: /* @__PURE__ */ new Date(),
130
132
  updatedAt: /* @__PURE__ */ new Date()
131
133
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/currencies/lib/seeds.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Currency } from '../data/entities'\n\nexport type CurrencySeedScope = { tenantId: string; organizationId: string }\n\ntype CurrencySeed = {\n code: string\n name: string\n decimalPlaces: number\n symbol: string\n decimalSeparator: string\n thousandsSeparator: string\n isBase: boolean\n isActive: boolean\n}\n\nconst SEED_CURRENCIES: CurrencySeed[] = [\n {\n code: 'USD',\n name: 'US Dollar',\n decimalPlaces: 2,\n symbol: '$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: true,\n isActive: true,\n },\n {\n code: 'EUR',\n name: 'Euro',\n decimalPlaces: 2,\n symbol: '\u20AC',\n decimalSeparator: ',',\n thousandsSeparator: '.',\n isBase: false,\n isActive: true,\n },\n {\n code: 'JPY',\n name: 'Japanese Yen',\n decimalPlaces: 0,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'GBP',\n name: 'British Pound',\n decimalPlaces: 2,\n symbol: '\u00A3',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CHF',\n name: 'Swiss Franc',\n decimalPlaces: 2,\n symbol: 'Fr',\n decimalSeparator: '.',\n thousandsSeparator: \"'\",\n isBase: false,\n isActive: true,\n },\n {\n code: 'CAD',\n name: 'Canadian Dollar',\n decimalPlaces: 2,\n symbol: 'C$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'AUD',\n name: 'Australian Dollar',\n decimalPlaces: 2,\n symbol: 'A$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CNY',\n name: 'Chinese Yuan',\n decimalPlaces: 2,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CNH',\n name: 'Chinese Yuan (Offshore)',\n decimalPlaces: 2,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'PLN',\n name: 'Polish Zloty',\n decimalPlaces: 2,\n symbol: 'z\u0142',\n decimalSeparator: ',',\n thousandsSeparator: ' ',\n isBase: false,\n isActive: true,\n },\n]\n\nexport async function seedExampleCurrencies(em: EntityManager, scope: CurrencySeedScope): Promise<boolean> {\n const existingEntries = await em.find(Currency, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingMap = new Map<string, Currency>()\n for (const entry of existingEntries) {\n existingMap.set(entry.code, entry)\n }\n\n let touched = false\n for (const curr of SEED_CURRENCIES) {\n const current = existingMap.get(curr.code)\n if (current) {\n if (current.name !== curr.name) {\n current.name = curr.name\n current.updatedAt = new Date()\n em.persist(current)\n touched = true\n }\n continue\n }\n const entry = em.create(Currency, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n ...curr,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n touched = true\n }\n\n if (touched) {\n await em.flush()\n }\n return touched\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { Currency } from '../data/entities'\n\nexport type CurrencySeedScope = { tenantId: string; organizationId: string }\n\ntype CurrencySeed = {\n code: string\n name: string\n decimalPlaces: number\n symbol: string\n decimalSeparator: string\n thousandsSeparator: string\n isBase: boolean\n isActive: boolean\n}\n\nconst SEED_CURRENCIES: CurrencySeed[] = [\n {\n code: 'USD',\n name: 'US Dollar',\n decimalPlaces: 2,\n symbol: '$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: true,\n isActive: true,\n },\n {\n code: 'EUR',\n name: 'Euro',\n decimalPlaces: 2,\n symbol: '\u20AC',\n decimalSeparator: ',',\n thousandsSeparator: '.',\n isBase: false,\n isActive: true,\n },\n {\n code: 'JPY',\n name: 'Japanese Yen',\n decimalPlaces: 0,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'GBP',\n name: 'British Pound',\n decimalPlaces: 2,\n symbol: '\u00A3',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CHF',\n name: 'Swiss Franc',\n decimalPlaces: 2,\n symbol: 'Fr',\n decimalSeparator: '.',\n thousandsSeparator: \"'\",\n isBase: false,\n isActive: true,\n },\n {\n code: 'CAD',\n name: 'Canadian Dollar',\n decimalPlaces: 2,\n symbol: 'C$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'AUD',\n name: 'Australian Dollar',\n decimalPlaces: 2,\n symbol: 'A$',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CNY',\n name: 'Chinese Yuan',\n decimalPlaces: 2,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'CNH',\n name: 'Chinese Yuan (Offshore)',\n decimalPlaces: 2,\n symbol: '\u00A5',\n decimalSeparator: '.',\n thousandsSeparator: ',',\n isBase: false,\n isActive: true,\n },\n {\n code: 'PLN',\n name: 'Polish Zloty',\n decimalPlaces: 2,\n symbol: 'z\u0142',\n decimalSeparator: ',',\n thousandsSeparator: ' ',\n isBase: false,\n isActive: true,\n },\n]\n\nexport async function seedExampleCurrencies(em: EntityManager, scope: CurrencySeedScope): Promise<boolean> {\n const existingEntries = await em.find(Currency, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n const existingMap = new Map<string, Currency>()\n for (const entry of existingEntries) {\n existingMap.set(entry.code, entry)\n }\n const hasLiveBase = existingEntries.some((entry) => entry.isBase && !entry.deletedAt)\n\n let touched = false\n for (const curr of SEED_CURRENCIES) {\n const current = existingMap.get(curr.code)\n if (current) {\n if (current.name !== curr.name) {\n current.name = curr.name\n current.updatedAt = new Date()\n em.persist(current)\n touched = true\n }\n continue\n }\n const entry = em.create(Currency, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n ...curr,\n isBase: curr.isBase && !hasLiveBase,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(entry)\n touched = true\n }\n\n if (touched) {\n await em.flush()\n }\n return touched\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -22,6 +22,11 @@ import {
22
22
  CustomerPersonProfile,
23
23
  CustomerTagAssignment
24
24
  } from "../data/entities.js";
25
+ import {
26
+ captureInteractionRestoreDetails,
27
+ omitInteractionRestoreDetails,
28
+ restoreInteractionRestoreDetails
29
+ } from "./interactionRestoreDetails.js";
25
30
  import {
26
31
  companyCreateSchema,
27
32
  companyUpdateSchema
@@ -98,7 +103,7 @@ function companyEntityIndexEntry(entity) {
98
103
  organizationId: entity.organizationId
99
104
  };
100
105
  }
101
- async function loadCompanySnapshot(em, id) {
106
+ async function loadCompanySnapshot(em, id, options = {}) {
102
107
  const entity = await em.findOne(CustomerEntity, { id, deletedAt: null });
103
108
  if (!entity || entity.kind !== "company") return null;
104
109
  const profile = await em.findOne(CustomerCompanyProfile, { entity });
@@ -248,6 +253,7 @@ async function loadCompanySnapshot(em, id) {
248
253
  createdAt: interaction.createdAt,
249
254
  updatedAt: interaction.updatedAt,
250
255
  deletedAt: interaction.deletedAt ?? null,
256
+ ...options.includeInteractionRestoreDetails ? captureInteractionRestoreDetails(interaction) : {},
251
257
  custom: await loadCustomFieldSnapshot(em, {
252
258
  entityId: INTERACTION_ENTITY_ID,
253
259
  recordId: interaction.id,
@@ -792,7 +798,7 @@ const deleteCompanyCommand = {
792
798
  async prepare(input, ctx) {
793
799
  const id = requireId(input, "Company id required");
794
800
  const em = ctx.container.resolve("em");
795
- const snapshot = await loadCompanySnapshot(em, id);
801
+ const snapshot = await loadCompanySnapshot(em, id, { includeInteractionRestoreDetails: true });
796
802
  return snapshot ? { before: snapshot } : {};
797
803
  },
798
804
  async execute(input, ctx) {
@@ -966,7 +972,7 @@ const deleteCompanyCommand = {
966
972
  resourceId: before.entity.id,
967
973
  tenantId: before.entity.tenantId,
968
974
  organizationId: before.entity.organizationId,
969
- snapshotBefore: before,
975
+ snapshotBefore: omitInteractionRestoreDetails(before),
970
976
  payload: {
971
977
  undo: {
972
978
  before
@@ -1193,6 +1199,7 @@ const deleteCompanyCommand = {
1193
1199
  source: interaction.source,
1194
1200
  appearanceIcon: interaction.appearanceIcon,
1195
1201
  appearanceColor: interaction.appearanceColor,
1202
+ ...restoreInteractionRestoreDetails(interaction),
1196
1203
  createdAt: interaction.createdAt,
1197
1204
  updatedAt: interaction.updatedAt,
1198
1205
  deletedAt: interaction.deletedAt