@open-mercato/core 0.10.1-develop.7994.1.c9f31972aa → 0.10.1-develop.8004.1.a8a79ee43f

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.
@@ -1,4 +1,4 @@
1
- [build:core] found 6295 entry points
1
+ [build:core] found 6298 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 271 entry points
4
4
  [build:core:generated] built successfully
@@ -66,7 +66,7 @@ function createIntegrationStateService(em) {
66
66
  }
67
67
  const created = em.create(IntegrationState, {
68
68
  integrationId,
69
- isEnabled: input.isEnabled ?? false,
69
+ isEnabled: input.isEnabled ?? resolvedBefore.isEnabled,
70
70
  apiVersion: input.apiVersion,
71
71
  reauthRequired: input.reauthRequired ?? false,
72
72
  lastHealthStatus: input.lastHealthStatus,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/integrations/lib/state-service.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { getIntegration, type IntegrationScope } from '@open-mercato/shared/modules/integrations/types'\nimport { IntegrationState } from '../data/entities'\n\nexport type ResolvedIntegrationState = {\n isEnabled: boolean\n apiVersion: string | null\n reauthRequired: boolean\n lastHealthStatus: string | null\n lastHealthCheckedAt: Date | null\n lastHealthLatencyMs: number | null\n enabledAt: Date | null\n updatedAt: Date | null\n}\n\nexport function createIntegrationStateService(em: EntityManager) {\n function resolveDefinitionDefaults(integrationId: string): ResolvedIntegrationState {\n const definition = getIntegration(integrationId)\n\n return {\n isEnabled: definition?.defaultState?.isEnabled ?? false,\n apiVersion: null,\n reauthRequired: false,\n lastHealthStatus: null,\n lastHealthCheckedAt: null,\n lastHealthLatencyMs: null,\n enabledAt: null,\n updatedAt: null,\n }\n }\n\n return {\n async get(integrationId: string, scope: IntegrationScope): Promise<IntegrationState | null> {\n return findOneWithDecryption(\n em,\n IntegrationState,\n {\n integrationId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async resolveState(integrationId: string, scope: IntegrationScope): Promise<ResolvedIntegrationState> {\n const state = await this.get(integrationId, scope)\n const defaults = resolveDefinitionDefaults(integrationId)\n\n return {\n isEnabled: state?.isEnabled ?? defaults.isEnabled,\n apiVersion: state?.apiVersion ?? null,\n reauthRequired: state?.reauthRequired ?? false,\n lastHealthStatus: state?.lastHealthStatus ?? null,\n lastHealthCheckedAt: state?.lastHealthCheckedAt ?? null,\n lastHealthLatencyMs: state?.lastHealthLatencyMs ?? null,\n enabledAt: state?.enabledAt ?? null,\n updatedAt: state?.updatedAt ?? null,\n }\n },\n\n async isEnabled(integrationId: string, scope: IntegrationScope): Promise<boolean> {\n const state = await this.resolveState(integrationId, scope)\n return state.isEnabled\n },\n\n async upsert(\n integrationId: string,\n input: Partial<\n Pick<\n IntegrationState,\n | 'isEnabled'\n | 'apiVersion'\n | 'reauthRequired'\n | 'lastHealthStatus'\n | 'lastHealthCheckedAt'\n | 'lastHealthLatencyMs'\n | 'enabledAt'\n >\n >,\n scope: IntegrationScope,\n ): Promise<IntegrationState> {\n const current = await this.get(integrationId, scope)\n const resolvedBefore = await this.resolveState(integrationId, scope)\n const enableTransition =\n input.isEnabled === true && resolvedBefore.isEnabled === false\n\n if (current) {\n if (input.isEnabled !== undefined) current.isEnabled = input.isEnabled\n if (input.apiVersion !== undefined) current.apiVersion = input.apiVersion\n if (input.reauthRequired !== undefined) current.reauthRequired = input.reauthRequired\n if (input.lastHealthStatus !== undefined) current.lastHealthStatus = input.lastHealthStatus\n if (input.lastHealthCheckedAt !== undefined) current.lastHealthCheckedAt = input.lastHealthCheckedAt\n if (input.lastHealthLatencyMs !== undefined) current.lastHealthLatencyMs = input.lastHealthLatencyMs\n if (input.enabledAt !== undefined) current.enabledAt = input.enabledAt\n else if (enableTransition) current.enabledAt = new Date()\n await em.flush()\n return current\n }\n\n const created = em.create(IntegrationState, {\n integrationId,\n isEnabled: input.isEnabled ?? false,\n apiVersion: input.apiVersion,\n reauthRequired: input.reauthRequired ?? false,\n lastHealthStatus: input.lastHealthStatus,\n lastHealthCheckedAt: input.lastHealthCheckedAt,\n lastHealthLatencyMs: input.lastHealthLatencyMs,\n enabledAt: input.enabledAt !== undefined ? input.enabledAt : enableTransition ? new Date() : undefined,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.persist(created).flush()\n return created\n },\n\n async resolveApiVersion(integrationId: string, scope: IntegrationScope): Promise<string | undefined> {\n const state = await this.get(integrationId, scope)\n return state?.apiVersion ?? undefined\n },\n\n async setReauthRequired(integrationId: string, required: boolean, scope: IntegrationScope): Promise<IntegrationState> {\n return this.upsert(integrationId, { reauthRequired: required }, scope)\n },\n }\n}\n\nexport type IntegrationStateService = ReturnType<typeof createIntegrationStateService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { getIntegration, type IntegrationScope } from '@open-mercato/shared/modules/integrations/types'\nimport { IntegrationState } from '../data/entities'\n\nexport type ResolvedIntegrationState = {\n isEnabled: boolean\n apiVersion: string | null\n reauthRequired: boolean\n lastHealthStatus: string | null\n lastHealthCheckedAt: Date | null\n lastHealthLatencyMs: number | null\n enabledAt: Date | null\n updatedAt: Date | null\n}\n\nexport function createIntegrationStateService(em: EntityManager) {\n function resolveDefinitionDefaults(integrationId: string): ResolvedIntegrationState {\n const definition = getIntegration(integrationId)\n\n return {\n isEnabled: definition?.defaultState?.isEnabled ?? false,\n apiVersion: null,\n reauthRequired: false,\n lastHealthStatus: null,\n lastHealthCheckedAt: null,\n lastHealthLatencyMs: null,\n enabledAt: null,\n updatedAt: null,\n }\n }\n\n return {\n async get(integrationId: string, scope: IntegrationScope): Promise<IntegrationState | null> {\n return findOneWithDecryption(\n em,\n IntegrationState,\n {\n integrationId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async resolveState(integrationId: string, scope: IntegrationScope): Promise<ResolvedIntegrationState> {\n const state = await this.get(integrationId, scope)\n const defaults = resolveDefinitionDefaults(integrationId)\n\n return {\n isEnabled: state?.isEnabled ?? defaults.isEnabled,\n apiVersion: state?.apiVersion ?? null,\n reauthRequired: state?.reauthRequired ?? false,\n lastHealthStatus: state?.lastHealthStatus ?? null,\n lastHealthCheckedAt: state?.lastHealthCheckedAt ?? null,\n lastHealthLatencyMs: state?.lastHealthLatencyMs ?? null,\n enabledAt: state?.enabledAt ?? null,\n updatedAt: state?.updatedAt ?? null,\n }\n },\n\n async isEnabled(integrationId: string, scope: IntegrationScope): Promise<boolean> {\n const state = await this.resolveState(integrationId, scope)\n return state.isEnabled\n },\n\n async upsert(\n integrationId: string,\n input: Partial<\n Pick<\n IntegrationState,\n | 'isEnabled'\n | 'apiVersion'\n | 'reauthRequired'\n | 'lastHealthStatus'\n | 'lastHealthCheckedAt'\n | 'lastHealthLatencyMs'\n | 'enabledAt'\n >\n >,\n scope: IntegrationScope,\n ): Promise<IntegrationState> {\n const current = await this.get(integrationId, scope)\n const resolvedBefore = await this.resolveState(integrationId, scope)\n const enableTransition =\n input.isEnabled === true && resolvedBefore.isEnabled === false\n\n if (current) {\n if (input.isEnabled !== undefined) current.isEnabled = input.isEnabled\n if (input.apiVersion !== undefined) current.apiVersion = input.apiVersion\n if (input.reauthRequired !== undefined) current.reauthRequired = input.reauthRequired\n if (input.lastHealthStatus !== undefined) current.lastHealthStatus = input.lastHealthStatus\n if (input.lastHealthCheckedAt !== undefined) current.lastHealthCheckedAt = input.lastHealthCheckedAt\n if (input.lastHealthLatencyMs !== undefined) current.lastHealthLatencyMs = input.lastHealthLatencyMs\n if (input.enabledAt !== undefined) current.enabledAt = input.enabledAt\n else if (enableTransition) current.enabledAt = new Date()\n await em.flush()\n return current\n }\n\n const created = em.create(IntegrationState, {\n integrationId,\n isEnabled: input.isEnabled ?? resolvedBefore.isEnabled,\n apiVersion: input.apiVersion,\n reauthRequired: input.reauthRequired ?? false,\n lastHealthStatus: input.lastHealthStatus,\n lastHealthCheckedAt: input.lastHealthCheckedAt,\n lastHealthLatencyMs: input.lastHealthLatencyMs,\n enabledAt: input.enabledAt !== undefined ? input.enabledAt : enableTransition ? new Date() : undefined,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n await em.persist(created).flush()\n return created\n },\n\n async resolveApiVersion(integrationId: string, scope: IntegrationScope): Promise<string | undefined> {\n const state = await this.get(integrationId, scope)\n return state?.apiVersion ?? undefined\n },\n\n async setReauthRequired(integrationId: string, required: boolean, scope: IntegrationScope): Promise<IntegrationState> {\n return this.upsert(integrationId, { reauthRequired: required }, scope)\n },\n }\n}\n\nexport type IntegrationStateService = ReturnType<typeof createIntegrationStateService>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,12 +4,14 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
4
4
  import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from "@open-mercato/shared/lib/commands/helpers";
5
5
  import { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
6
6
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
7
+ import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
7
8
  import { ResourcesResource, ResourcesResourceType } from "../data/entities.js";
8
9
  import {
9
10
  resourcesResourceTypeCreateSchema,
10
11
  resourcesResourceTypeUpdateSchema
11
12
  } from "../data/validators.js";
12
13
  import { resourcesResourceTypeCrudEvents } from "../lib/crud.js";
14
+ import { lockResourceType } from "../lib/resourceTypeLock.js";
13
15
  import { makeCreateRedo } from "@open-mercato/shared/lib/commands/redo";
14
16
  import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from "./shared.js";
15
17
  import { E } from "../../../generated/entities.ids.generated.js";
@@ -44,6 +46,20 @@ async function loadResourceTypeCustomSnapshot(em, snapshot) {
44
46
  organizationId: snapshot.organizationId
45
47
  });
46
48
  }
49
+ async function assertNoAssignedResources(em, resourceType, status) {
50
+ const assignedCount = await em.count(ResourcesResource, {
51
+ resourceTypeId: resourceType.id,
52
+ deletedAt: null,
53
+ tenantId: resourceType.tenantId,
54
+ organizationId: resourceType.organizationId
55
+ });
56
+ if (assignedCount === 0) return;
57
+ const { translate } = await resolveTranslations();
58
+ throw new CrudHttpError(status, {
59
+ error: translate("resources.resourceTypes.errors.deleteAssigned", "Resource type has assigned resources."),
60
+ code: "resources.resourceTypes.errors.deleteAssigned"
61
+ });
62
+ }
47
63
  const createResourceTypeCommand = {
48
64
  id: "resources.resourceTypes.create",
49
65
  async execute(rawInput, ctx) {
@@ -122,9 +138,24 @@ const createResourceTypeCommand = {
122
138
  const em = ctx.container.resolve("em").fork();
123
139
  const resourceType = await em.findOne(ResourcesResourceType, { id: after.id });
124
140
  if (resourceType) {
125
- resourceType.deletedAt = /* @__PURE__ */ new Date();
126
- resourceType.updatedAt = /* @__PURE__ */ new Date();
127
- await em.flush();
141
+ let deleted = false;
142
+ await withAtomicFlush(em, [
143
+ async () => {
144
+ await lockResourceType(em, resourceType.id);
145
+ const isLive = await em.count(ResourcesResourceType, {
146
+ id: resourceType.id,
147
+ tenantId: resourceType.tenantId,
148
+ organizationId: resourceType.organizationId,
149
+ deletedAt: null
150
+ }) > 0;
151
+ if (!isLive) return;
152
+ await assertNoAssignedResources(em, resourceType, 409);
153
+ resourceType.deletedAt = /* @__PURE__ */ new Date();
154
+ resourceType.updatedAt = /* @__PURE__ */ new Date();
155
+ deleted = true;
156
+ }
157
+ ], { transaction: true });
158
+ if (!deleted) return;
128
159
  const dataEngine = ctx.container.resolve("dataEngine");
129
160
  await emitCrudUndoSideEffects({
130
161
  dataEngine,
@@ -323,18 +354,14 @@ const deleteResourceTypeCommand = {
323
354
  if (!record) throw new CrudHttpError(404, { error: "Resources resource type not found." });
324
355
  ensureTenantScope(ctx, record.tenantId);
325
356
  ensureOrganizationScope(ctx, record.organizationId);
326
- const assignedCount = await em.count(ResourcesResource, {
327
- resourceTypeId: record.id,
328
- deletedAt: null,
329
- tenantId: record.tenantId,
330
- organizationId: record.organizationId
331
- });
332
- if (assignedCount > 0) {
333
- throw new CrudHttpError(400, { error: "Resource type has assigned resources." });
334
- }
335
- record.deletedAt = /* @__PURE__ */ new Date();
336
- record.updatedAt = /* @__PURE__ */ new Date();
337
- await em.flush();
357
+ await withAtomicFlush(em, [
358
+ async () => {
359
+ await lockResourceType(em, record.id);
360
+ await assertNoAssignedResources(em, record, 400);
361
+ record.deletedAt = /* @__PURE__ */ new Date();
362
+ record.updatedAt = /* @__PURE__ */ new Date();
363
+ }
364
+ ], { transaction: true });
338
365
  const dataEngine = ctx.container.resolve("dataEngine");
339
366
  await emitCrudSideEffects({
340
367
  dataEngine,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/resources/commands/resource-types.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { ResourcesResource, ResourcesResourceType } from '../data/entities'\nimport {\n resourcesResourceTypeCreateSchema,\n resourcesResourceTypeUpdateSchema,\n type ResourcesResourceTypeCreateInput,\n type ResourcesResourceTypeUpdateInput,\n} from '../data/validators'\nimport { resourcesResourceTypeCrudEvents } from '../lib/crud'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\nimport { E } from '#generated/entities.ids.generated'\n\nconst resourceTypeCrudIndexer: CrudIndexerConfig<ResourcesResourceType> = {\n entityType: E.resources.resources_resource_type,\n}\n\ntype ResourceTypeSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n deletedAt: string | null\n}\n\ntype ResourceTypeUndoPayload = {\n before?: ResourceTypeSnapshot | null\n after?: ResourceTypeSnapshot | null\n customBefore?: CustomFieldSnapshot | null\n customAfter?: CustomFieldSnapshot | null\n}\n\nasync function loadResourceTypeSnapshot(em: EntityManager, id: string): Promise<ResourceTypeSnapshot | null> {\n const resourceType = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id },\n undefined,\n { tenantId: null, organizationId: null },\n )\n if (!resourceType) return null\n return {\n id: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n name: resourceType.name,\n description: resourceType.description ?? null,\n appearanceIcon: resourceType.appearanceIcon ?? null,\n appearanceColor: resourceType.appearanceColor ?? null,\n deletedAt: resourceType.deletedAt ? resourceType.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadResourceTypeCustomSnapshot(em: EntityManager, snapshot: ResourceTypeSnapshot): Promise<CustomFieldSnapshot> {\n return loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource_type,\n recordId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n })\n}\n\nconst createResourceTypeCommand: CommandHandler<ResourcesResourceTypeCreateInput, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const record = em.create(ResourcesResourceType, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId)\n if (!snapshot) return null\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { snapshot, custom }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId)\n if (!snapshot) return null\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.create', 'Create resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n customAfter: custom,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const resourceType = await em.findOne(ResourcesResourceType, { id: after.id })\n if (resourceType) {\n resourceType.deletedAt = new Date()\n resourceType.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<ResourcesResourceType, ResourceTypeSnapshot, ResourcesResourceTypeCreateInput, { resourceTypeId: string }>({\n entityClass: ResourcesResourceType,\n buildResult: (entity) => ({ resourceTypeId: entity.id }),\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n afterRestore: async ({ ctx, entity, logEntry }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n if (!payload?.customAfter) return\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const reset = buildCustomFieldResetMap(payload.customAfter, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: entity.id,\n tenantId: entity.tenantId,\n organizationId: entity.organizationId,\n values: reset,\n })\n },\n }),\n}\n\nconst updateResourceTypeCommand: CommandHandler<ResourcesResourceTypeUpdateInput, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceTypeSnapshot(em, parsed.id)\n if (!snapshot) return {}\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id: parsed.id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource type not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.appearanceIcon !== undefined) record.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) record.appearanceColor = parsed.appearanceColor ?? null\n record.updatedAt = new Date()\n\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as ResourceTypeSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadResourceTypeSnapshot(em, before.id)\n if (!after) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const customAfter = await loadResourceTypeCustomSnapshot(em, after)\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'appearanceIcon',\n 'appearanceColor',\n 'deletedAt',\n ])\n const customChanges = diffCustomFieldChanges(customBefore, customAfter)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: customBefore ?? null, to: customAfter ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.update', 'Update resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n customBefore: customBefore ?? null,\n customAfter,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const resourceType = await em.findOne(ResourcesResourceType, { id: before.id })\n if (!resourceType) return\n resourceType.name = before.name\n resourceType.description = before.description ?? null\n resourceType.appearanceIcon = before.appearanceIcon ?? null\n resourceType.appearanceColor = before.appearanceColor ?? null\n resourceType.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n resourceType.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (payload.customBefore || payload.customAfter) {\n const currentCustom = await loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n })\n const reset = buildCustomFieldResetMap(payload.customBefore ?? undefined, currentCustom ?? undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n },\n}\n\nconst deleteResourceTypeCommand: CommandHandler<{ id?: string }, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource type id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceTypeSnapshot(em, id)\n if (!snapshot) return {}\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource type id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource type not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const assignedCount = await em.count(ResourcesResource, {\n resourceTypeId: record.id,\n deletedAt: null,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n if (assignedCount > 0) {\n throw new CrudHttpError(400, { error: 'Resource type has assigned resources.' })\n }\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ResourceTypeSnapshot | undefined\n if (!before) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.delete', 'Delete resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n customBefore: customBefore ?? null,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let resourceType = await em.findOne(ResourcesResourceType, { id: before.id })\n if (!resourceType) {\n resourceType = em.create(ResourcesResourceType, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n appearanceIcon: before.appearanceIcon ?? null,\n appearanceColor: before.appearanceColor ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(resourceType)\n } else {\n resourceType.name = before.name\n resourceType.description = before.description ?? null\n resourceType.appearanceIcon = before.appearanceIcon ?? null\n resourceType.appearanceColor = before.appearanceColor ?? null\n resourceType.deletedAt = null\n resourceType.updatedAt = new Date()\n }\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (payload.customBefore) {\n const reset = buildCustomFieldResetMap(payload.customBefore, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n },\n}\n\nregisterCommand(createResourceTypeCommand)\nregisterCommand(updateResourceTypeCommand)\nregisterCommand(deleteResourceTypeCommand)\n"],
5
- "mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,cAAc,qBAAqB,yBAAyB,uBAAuB,4BAA4B;AACxH,SAAS,0BAA0B,wBAAwB,+BAAyD;AACpH,SAAS,2BAA2B;AAGpC,SAAS,mBAAmB,6BAA6B;AACzD;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,uCAAuC;AAChD,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB,mBAAmB,0BAA0B;AAC/E,SAAS,SAAS;AAElB,MAAM,0BAAoE;AAAA,EACxE,YAAY,EAAE,UAAU;AAC1B;AAoBA,eAAe,yBAAyB,IAAmB,IAAkD;AAC3G,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA,EAAE,GAAG;AAAA,IACL;AAAA,IACA,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO;AAAA,IACL,IAAI,aAAa;AAAA,IACjB,UAAU,aAAa;AAAA,IACvB,gBAAgB,aAAa;AAAA,IAC7B,MAAM,aAAa;AAAA,IACnB,aAAa,aAAa,eAAe;AAAA,IACzC,gBAAgB,aAAa,kBAAkB;AAAA,IAC/C,iBAAiB,aAAa,mBAAmB;AAAA,IACjD,WAAW,aAAa,YAAY,aAAa,UAAU,YAAY,IAAI;AAAA,EAC7E;AACF;AAEA,eAAe,+BAA+B,IAAmB,UAA8D;AAC7H,SAAO,wBAAwB,IAAI;AAAA,IACjC,UAAU,EAAE,UAAU;AAAA,IACtB,UAAU,SAAS;AAAA,IACnB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AACH;AAEA,MAAM,4BAA0G;AAAA,EAC9G,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AAC5F,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,SAAS,GAAG,OAAO,uBAAuB;AAAA,MAC9C,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO,eAAe;AAAA,MACnC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,OAAG,QAAQ,MAAM;AACjB,UAAM,GAAG,MAAM;AACf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,cAAc;AACzE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,UAAU,OAAO;AAAA,EAC5B;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,cAAc;AACzE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,SAAS;AAAA,MACrB,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,MACzB,eAAe;AAAA,MACf,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,MAAM,GAAG,CAAC;AAC7E,QAAI,cAAc;AAChB,mBAAa,YAAY,oBAAI,KAAK;AAClC,mBAAa,YAAY,oBAAI,KAAK;AAClC,YAAM,GAAG,MAAM;AAEf,YAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,YAAM,wBAAwB;AAAA,QAC5B;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,aAAa;AAAA,UACX,IAAI,aAAa;AAAA,UACjB,gBAAgB,aAAa;AAAA,UAC7B,UAAU,aAAa;AAAA,QACzB;AAAA,QACA,QAAQ;AAAA,QACV,SAAS;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EACA,MAAM,eAA0H;AAAA,IAC9H,aAAa;AAAA,IACb,aAAa,CAAC,YAAY,EAAE,gBAAgB,OAAO,GAAG;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc,OAAO,EAAE,KAAK,QAAQ,SAAS,MAAM;AACjD,YAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAI,CAAC,SAAS,YAAa;AAC3B,YAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,YAAM,QAAQ,yBAAyB,QAAQ,aAAa,MAAS;AACrE,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,gBAAgB,OAAO;AAAA,QACvB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,MAAM,4BAA0G;AAAA,EAC9G,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AACpF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,EAAE;AAC7D,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,QAAQ,UAAU,cAAc,OAAO;AAAA,EAClD;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AAC5F,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK;AAAA,MACjC;AAAA,MACA,EAAE,UAAU,IAAI,MAAM,YAAY,MAAM,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAAA,IAClF;AACA,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qCAAqC,CAAC;AACzF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,QAAI,OAAO,SAAS,OAAW,QAAO,OAAO,OAAO;AACpD,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,QAAI,OAAO,mBAAmB,OAAW,QAAO,iBAAiB,OAAO,kBAAkB;AAC1F,QAAI,OAAO,oBAAoB,OAAW,QAAO,kBAAkB,OAAO,mBAAmB;AAC7F,WAAO,YAAY,oBAAI,KAAK;AAE5B,UAAM,GAAG,MAAM;AACf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,IAAI,MAAM;AACtC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM,yBAAyB,IAAI,OAAO,EAAE;AAC1D,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,cAAc,MAAM,+BAA+B,IAAI,KAAK;AAClE,UAAM,UAAU,aAAa,QAA8C,OAA6C;AAAA,MACtH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,gBAAgB,uBAAuB,cAAc,WAAW;AACtE,QAAI,OAAO,KAAK,aAAa,EAAE,QAAQ;AACrC,cAAQ,eAAe,EAAE,MAAM,gBAAgB,MAAM,IAAI,eAAe,KAAK;AAAA,IAC/E;AACA,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,cAAc,gBAAgB;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AAC9E,QAAI,CAAC,aAAc;AACnB,iBAAa,OAAO,OAAO;AAC3B,iBAAa,cAAc,OAAO,eAAe;AACjD,iBAAa,iBAAiB,OAAO,kBAAkB;AACvD,iBAAa,kBAAkB,OAAO,mBAAmB;AACzD,iBAAa,YAAY,OAAO,YAAY,IAAI,KAAK,OAAO,SAAS,IAAI;AACzE,iBAAa,YAAY,oBAAI,KAAK;AAClC,UAAM,GAAG,MAAM;AAEf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,QAAI,QAAQ,gBAAgB,QAAQ,aAAa;AAC/C,YAAM,gBAAgB,MAAM,wBAAwB,IAAI;AAAA,QACtD,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,MAC/B,CAAC;AACD,YAAM,QAAQ,yBAAyB,QAAQ,gBAAgB,QAAW,iBAAiB,MAAS;AACpG,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,QAC7B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,aAAa;AAAA,QACjB,gBAAgB,aAAa;AAAA,QAC7B,UAAU,aAAa;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,MAAM,4BAAyF;AAAA,EAC7F,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gCAAgC,CAAC;AAChF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,yBAAyB,IAAI,EAAE;AACtD,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,QAAQ,UAAU,cAAc,OAAO;AAAA,EAClD;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gCAAgC,CAAC;AAChF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,WAAW,KAAK;AAAA,MACtB;AAAA,MACA,EAAE,UAAU,IAAI,MAAM,YAAY,MAAM,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAAA,IAClF;AACA,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qCAAqC,CAAC;AACzF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAClD,UAAM,gBAAgB,MAAM,GAAG,MAAM,mBAAmB;AAAA,MACtD,gBAAgB,OAAO;AAAA,MACvB,WAAW;AAAA,MACX,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AACD,QAAI,gBAAgB,GAAG;AACrB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,wCAAwC,CAAC;AAAA,IACjF;AACA,WAAO,YAAY,oBAAI,KAAK;AAC5B,WAAO,YAAY,oBAAI,KAAK;AAC5B,UAAM,GAAG,MAAM;AAEf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA,cAAc,gBAAgB;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AAC5E,QAAI,CAAC,cAAc;AACjB,qBAAe,GAAG,OAAO,uBAAuB;AAAA,QAC9C,IAAI,OAAO;AAAA,QACX,UAAU,OAAO;AAAA,QACjB,gBAAgB,OAAO;AAAA,QACvB,MAAM,OAAO;AAAA,QACb,aAAa,OAAO,eAAe;AAAA,QACnC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,iBAAiB,OAAO,mBAAmB;AAAA,QAC3C,WAAW;AAAA,QACX,WAAW,oBAAI,KAAK;AAAA,QACpB,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,YAAY;AAAA,IACzB,OAAO;AACL,mBAAa,OAAO,OAAO;AAC3B,mBAAa,cAAc,OAAO,eAAe;AACjD,mBAAa,iBAAiB,OAAO,kBAAkB;AACvD,mBAAa,kBAAkB,OAAO,mBAAmB;AACzD,mBAAa,YAAY;AACzB,mBAAa,YAAY,oBAAI,KAAK;AAAA,IACpC;AACA,UAAM,GAAG,MAAM;AAEf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,QAAI,QAAQ,cAAc;AACxB,YAAM,QAAQ,yBAAyB,QAAQ,cAAc,MAAS;AACtE,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,QAC7B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,aAAa;AAAA,QACjB,gBAAgB,aAAa;AAAA,QAC7B,UAAU,aAAa;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,gBAAgB,yBAAyB;AACzC,gBAAgB,yBAAyB;AACzC,gBAAgB,yBAAyB;",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { ResourcesResource, ResourcesResourceType } from '../data/entities'\nimport {\n resourcesResourceTypeCreateSchema,\n resourcesResourceTypeUpdateSchema,\n type ResourcesResourceTypeCreateInput,\n type ResourcesResourceTypeUpdateInput,\n} from '../data/validators'\nimport { resourcesResourceTypeCrudEvents } from '../lib/crud'\nimport { lockResourceType } from '../lib/resourceTypeLock'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\nimport { E } from '#generated/entities.ids.generated'\n\nconst resourceTypeCrudIndexer: CrudIndexerConfig<ResourcesResourceType> = {\n entityType: E.resources.resources_resource_type,\n}\n\ntype ResourceTypeSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n deletedAt: string | null\n}\n\ntype ResourceTypeUndoPayload = {\n before?: ResourceTypeSnapshot | null\n after?: ResourceTypeSnapshot | null\n customBefore?: CustomFieldSnapshot | null\n customAfter?: CustomFieldSnapshot | null\n}\n\nasync function loadResourceTypeSnapshot(em: EntityManager, id: string): Promise<ResourceTypeSnapshot | null> {\n const resourceType = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id },\n undefined,\n { tenantId: null, organizationId: null },\n )\n if (!resourceType) return null\n return {\n id: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n name: resourceType.name,\n description: resourceType.description ?? null,\n appearanceIcon: resourceType.appearanceIcon ?? null,\n appearanceColor: resourceType.appearanceColor ?? null,\n deletedAt: resourceType.deletedAt ? resourceType.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadResourceTypeCustomSnapshot(em: EntityManager, snapshot: ResourceTypeSnapshot): Promise<CustomFieldSnapshot> {\n return loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource_type,\n recordId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n })\n}\n\nasync function assertNoAssignedResources(\n em: EntityManager,\n resourceType: ResourcesResourceType,\n status: 400 | 409,\n): Promise<void> {\n const assignedCount = await em.count(ResourcesResource, {\n resourceTypeId: resourceType.id,\n deletedAt: null,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n })\n if (assignedCount === 0) return\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(status, {\n error: translate('resources.resourceTypes.errors.deleteAssigned', 'Resource type has assigned resources.'),\n code: 'resources.resourceTypes.errors.deleteAssigned',\n })\n}\n\nconst createResourceTypeCommand: CommandHandler<ResourcesResourceTypeCreateInput, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const record = em.create(ResourcesResourceType, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId)\n if (!snapshot) return null\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { snapshot, custom }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceTypeSnapshot(em, result.resourceTypeId)\n if (!snapshot) return null\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.create', 'Create resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: {\n after: snapshot,\n customAfter: custom,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const resourceType = await em.findOne(ResourcesResourceType, { id: after.id })\n if (resourceType) {\n let deleted = false\n await withAtomicFlush(em, [\n async () => {\n await lockResourceType(em, resourceType.id)\n const isLive = await em.count(ResourcesResourceType, {\n id: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n deletedAt: null,\n }) > 0\n if (!isLive) return\n await assertNoAssignedResources(em, resourceType, 409)\n resourceType.deletedAt = new Date()\n resourceType.updatedAt = new Date()\n deleted = true\n },\n ], { transaction: true })\n if (!deleted) return\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n }\n },\n redo: makeCreateRedo<ResourcesResourceType, ResourceTypeSnapshot, ResourcesResourceTypeCreateInput, { resourceTypeId: string }>({\n entityClass: ResourcesResourceType,\n buildResult: (entity) => ({ resourceTypeId: entity.id }),\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n afterRestore: async ({ ctx, entity, logEntry }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n if (!payload?.customAfter) return\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const reset = buildCustomFieldResetMap(payload.customAfter, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: entity.id,\n tenantId: entity.tenantId,\n organizationId: entity.organizationId,\n values: reset,\n })\n },\n }),\n}\n\nconst updateResourceTypeCommand: CommandHandler<ResourcesResourceTypeUpdateInput, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceTypeSnapshot(em, parsed.id)\n if (!snapshot) return {}\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceTypeUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id: parsed.id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource type not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.appearanceIcon !== undefined) record.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) record.appearanceColor = parsed.appearanceColor ?? null\n record.updatedAt = new Date()\n\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as ResourceTypeSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadResourceTypeSnapshot(em, before.id)\n if (!after) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const customAfter = await loadResourceTypeCustomSnapshot(em, after)\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'appearanceIcon',\n 'appearanceColor',\n 'deletedAt',\n ])\n const customChanges = diffCustomFieldChanges(customBefore, customAfter)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: customBefore ?? null, to: customAfter ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.update', 'Update resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: {\n before,\n after,\n customBefore: customBefore ?? null,\n customAfter,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const resourceType = await em.findOne(ResourcesResourceType, { id: before.id })\n if (!resourceType) return\n resourceType.name = before.name\n resourceType.description = before.description ?? null\n resourceType.appearanceIcon = before.appearanceIcon ?? null\n resourceType.appearanceColor = before.appearanceColor ?? null\n resourceType.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n resourceType.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (payload.customBefore || payload.customAfter) {\n const currentCustom = await loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n })\n const reset = buildCustomFieldResetMap(payload.customBefore ?? undefined, currentCustom ?? undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n },\n}\n\nconst deleteResourceTypeCommand: CommandHandler<{ id?: string }, { resourceTypeId: string }> = {\n id: 'resources.resourceTypes.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource type id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceTypeSnapshot(em, id)\n if (!snapshot) return {}\n const custom = await loadResourceTypeCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource type id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResourceType,\n { id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource type not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n await withAtomicFlush(em, [\n async () => {\n await lockResourceType(em, record.id)\n await assertNoAssignedResources(em, record, 400)\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n },\n ], { transaction: true })\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n return { resourceTypeId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ResourceTypeSnapshot | undefined\n if (!before) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resourceTypes.delete', 'Delete resource type'),\n resourceKind: 'resources.resourceType',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n customBefore: customBefore ?? null,\n } satisfies ResourceTypeUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceTypeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let resourceType = await em.findOne(ResourcesResourceType, { id: before.id })\n if (!resourceType) {\n resourceType = em.create(ResourcesResourceType, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n appearanceIcon: before.appearanceIcon ?? null,\n appearanceColor: before.appearanceColor ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(resourceType)\n } else {\n resourceType.name = before.name\n resourceType.description = before.description ?? null\n resourceType.appearanceIcon = before.appearanceIcon ?? null\n resourceType.appearanceColor = before.appearanceColor ?? null\n resourceType.deletedAt = null\n resourceType.updatedAt = new Date()\n }\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n if (payload.customBefore) {\n const reset = buildCustomFieldResetMap(payload.customBefore, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource_type,\n recordId: resourceType.id,\n tenantId: resourceType.tenantId,\n organizationId: resourceType.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: resourceType,\n identifiers: {\n id: resourceType.id,\n organizationId: resourceType.organizationId,\n tenantId: resourceType.tenantId,\n },\n events: resourcesResourceTypeCrudEvents,\n indexer: resourceTypeCrudIndexer,\n })\n },\n}\n\nregisterCommand(createResourceTypeCommand)\nregisterCommand(updateResourceTypeCommand)\nregisterCommand(deleteResourceTypeCommand)\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,cAAc,qBAAqB,yBAAyB,uBAAuB,4BAA4B;AACxH,SAAS,0BAA0B,wBAAwB,+BAAyD;AACpH,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAGhC,SAAS,mBAAmB,6BAA6B;AACzD;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB,mBAAmB,0BAA0B;AAC/E,SAAS,SAAS;AAElB,MAAM,0BAAoE;AAAA,EACxE,YAAY,EAAE,UAAU;AAC1B;AAoBA,eAAe,yBAAyB,IAAmB,IAAkD;AAC3G,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA,EAAE,GAAG;AAAA,IACL;AAAA,IACA,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO;AAAA,IACL,IAAI,aAAa;AAAA,IACjB,UAAU,aAAa;AAAA,IACvB,gBAAgB,aAAa;AAAA,IAC7B,MAAM,aAAa;AAAA,IACnB,aAAa,aAAa,eAAe;AAAA,IACzC,gBAAgB,aAAa,kBAAkB;AAAA,IAC/C,iBAAiB,aAAa,mBAAmB;AAAA,IACjD,WAAW,aAAa,YAAY,aAAa,UAAU,YAAY,IAAI;AAAA,EAC7E;AACF;AAEA,eAAe,+BAA+B,IAAmB,UAA8D;AAC7H,SAAO,wBAAwB,IAAI;AAAA,IACjC,UAAU,EAAE,UAAU;AAAA,IACtB,UAAU,SAAS;AAAA,IACnB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AACH;AAEA,eAAe,0BACb,IACA,cACA,QACe;AACf,QAAM,gBAAgB,MAAM,GAAG,MAAM,mBAAmB;AAAA,IACtD,gBAAgB,aAAa;AAAA,IAC7B,WAAW;AAAA,IACX,UAAU,aAAa;AAAA,IACvB,gBAAgB,aAAa;AAAA,EAC/B,CAAC;AACD,MAAI,kBAAkB,EAAG;AACzB,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,IAAI,cAAc,QAAQ;AAAA,IAC9B,OAAO,UAAU,iDAAiD,uCAAuC;AAAA,IACzG,MAAM;AAAA,EACR,CAAC;AACH;AAEA,MAAM,4BAA0G;AAAA,EAC9G,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AAC5F,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,SAAS,GAAG,OAAO,uBAAuB;AAAA,MAC9C,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO,eAAe;AAAA,MACnC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,OAAG,QAAQ,MAAM;AACjB,UAAM,GAAG,MAAM;AACf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,cAAc;AACzE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,UAAU,OAAO;AAAA,EAC5B;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,cAAc;AACzE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,SAAS;AAAA,MACrB,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,MACzB,eAAe;AAAA,MACf,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,MAAM,GAAG,CAAC;AAC7E,QAAI,cAAc;AAChB,UAAI,UAAU;AACd,YAAM,gBAAgB,IAAI;AAAA,QACxB,YAAY;AACV,gBAAM,iBAAiB,IAAI,aAAa,EAAE;AAC1C,gBAAM,SAAS,MAAM,GAAG,MAAM,uBAAuB;AAAA,YACnD,IAAI,aAAa;AAAA,YACjB,UAAU,aAAa;AAAA,YACvB,gBAAgB,aAAa;AAAA,YAC7B,WAAW;AAAA,UACb,CAAC,IAAI;AACL,cAAI,CAAC,OAAQ;AACb,gBAAM,0BAA0B,IAAI,cAAc,GAAG;AACrD,uBAAa,YAAY,oBAAI,KAAK;AAClC,uBAAa,YAAY,oBAAI,KAAK;AAClC,oBAAU;AAAA,QACZ;AAAA,MACF,GAAG,EAAE,aAAa,KAAK,CAAC;AACxB,UAAI,CAAC,QAAS;AAEd,YAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,YAAM,wBAAwB;AAAA,QAC5B;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,aAAa;AAAA,UACX,IAAI,aAAa;AAAA,UACjB,gBAAgB,aAAa;AAAA,UAC7B,UAAU,aAAa;AAAA,QACzB;AAAA,QACA,QAAQ;AAAA,QACV,SAAS;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EACA,MAAM,eAA0H;AAAA,IAC9H,aAAa;AAAA,IACb,aAAa,CAAC,YAAY,EAAE,gBAAgB,OAAO,GAAG;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc,OAAO,EAAE,KAAK,QAAQ,SAAS,MAAM;AACjD,YAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAI,CAAC,SAAS,YAAa;AAC3B,YAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,YAAM,QAAQ,yBAAyB,QAAQ,aAAa,MAAS;AACrE,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,gBAAgB,OAAO;AAAA,QACvB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,MAAM,4BAA0G;AAAA,EAC9G,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AACpF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,yBAAyB,IAAI,OAAO,EAAE;AAC7D,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,QAAQ,UAAU,cAAc,OAAO;AAAA,EAClD;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,mCAAmC,QAAQ;AAC5F,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,OAAO,IAAI,WAAW,KAAK;AAAA,MACjC;AAAA,MACA,EAAE,UAAU,IAAI,MAAM,YAAY,MAAM,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAAA,IAClF;AACA,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qCAAqC,CAAC;AACzF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,QAAI,OAAO,SAAS,OAAW,QAAO,OAAO,OAAO;AACpD,QAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,QAAI,OAAO,mBAAmB,OAAW,QAAO,iBAAiB,OAAO,kBAAkB;AAC1F,QAAI,OAAO,oBAAoB,OAAW,QAAO,kBAAkB,OAAO,mBAAmB;AAC7F,WAAO,YAAY,oBAAI,KAAK;AAE5B,UAAM,GAAG,MAAM;AACf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA,UAAU,EAAE,UAAU;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,WAAW,IAAI,MAAM;AACtC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM,yBAAyB,IAAI,OAAO,EAAE;AAC1D,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,cAAc,MAAM,+BAA+B,IAAI,KAAK;AAClE,UAAM,UAAU,aAAa,QAA8C,OAA6C;AAAA,MACtH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,gBAAgB,uBAAuB,cAAc,WAAW;AACtE,QAAI,OAAO,KAAK,aAAa,EAAE,QAAQ;AACrC,cAAQ,eAAe,EAAE,MAAM,gBAAgB,MAAM,IAAI,eAAe,KAAK;AAAA,IAC/E;AACA,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA,cAAc,gBAAgB;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AAC9E,QAAI,CAAC,aAAc;AACnB,iBAAa,OAAO,OAAO;AAC3B,iBAAa,cAAc,OAAO,eAAe;AACjD,iBAAa,iBAAiB,OAAO,kBAAkB;AACvD,iBAAa,kBAAkB,OAAO,mBAAmB;AACzD,iBAAa,YAAY,OAAO,YAAY,IAAI,KAAK,OAAO,SAAS,IAAI;AACzE,iBAAa,YAAY,oBAAI,KAAK;AAClC,UAAM,GAAG,MAAM;AAEf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,QAAI,QAAQ,gBAAgB,QAAQ,aAAa;AAC/C,YAAM,gBAAgB,MAAM,wBAAwB,IAAI;AAAA,QACtD,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,MAC/B,CAAC;AACD,YAAM,QAAQ,yBAAyB,QAAQ,gBAAgB,QAAW,iBAAiB,MAAS;AACpG,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,QAC7B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,aAAa;AAAA,QACjB,gBAAgB,aAAa;AAAA,QAC7B,UAAU,aAAa;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,MAAM,4BAAyF;AAAA,EAC7F,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gCAAgC,CAAC;AAChF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,yBAAyB,IAAI,EAAE;AACtD,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,+BAA+B,IAAI,QAAQ;AAChE,WAAO,EAAE,QAAQ,UAAU,cAAc,OAAO;AAAA,EAClD;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gCAAgC,CAAC;AAChF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,WAAW,KAAK;AAAA,MACtB;AAAA,MACA,EAAE,UAAU,IAAI,MAAM,YAAY,MAAM,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAAA,IAClF;AACA,QAAI,CAAC,OAAQ,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,qCAAqC,CAAC;AACzF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAClD,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,iBAAiB,IAAI,OAAO,EAAE;AACpC,cAAM,0BAA0B,IAAI,QAAQ,GAAG;AAC/C,eAAO,YAAY,oBAAI,KAAK;AAC5B,eAAO,YAAY,oBAAI,KAAK;AAAA,MAC9B;AAAA,IACF,GAAG,EAAE,aAAa,KAAK,CAAC;AAExB,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,gBAAgB,OAAO,GAAG;AAAA,EACrC;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,sBAAsB;AAAA,MACrF,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA,cAAc,gBAAgB;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA4C,QAAQ;AACpE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,eAAe,MAAM,GAAG,QAAQ,uBAAuB,EAAE,IAAI,OAAO,GAAG,CAAC;AAC5E,QAAI,CAAC,cAAc;AACjB,qBAAe,GAAG,OAAO,uBAAuB;AAAA,QAC9C,IAAI,OAAO;AAAA,QACX,UAAU,OAAO;AAAA,QACjB,gBAAgB,OAAO;AAAA,QACvB,MAAM,OAAO;AAAA,QACb,aAAa,OAAO,eAAe;AAAA,QACnC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,iBAAiB,OAAO,mBAAmB;AAAA,QAC3C,WAAW;AAAA,QACX,WAAW,oBAAI,KAAK;AAAA,QACpB,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,YAAY;AAAA,IACzB,OAAO;AACL,mBAAa,OAAO,OAAO;AAC3B,mBAAa,cAAc,OAAO,eAAe;AACjD,mBAAa,iBAAiB,OAAO,kBAAkB;AACvD,mBAAa,kBAAkB,OAAO,mBAAmB;AACzD,mBAAa,YAAY;AACzB,mBAAa,YAAY,oBAAI,KAAK;AAAA,IACpC;AACA,UAAM,GAAG,MAAM;AAEf,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,QAAI,QAAQ,cAAc;AACxB,YAAM,QAAQ,yBAAyB,QAAQ,cAAc,MAAS;AACtE,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,aAAa;AAAA,QACvB,UAAU,aAAa;AAAA,QACvB,gBAAgB,aAAa;AAAA,QAC7B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,aAAa;AAAA,QACjB,gBAAgB,aAAa;AAAA,QAC7B,UAAU,aAAa;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,gBAAgB,yBAAyB;AACzC,gBAAgB,yBAAyB;AACzC,gBAAgB,yBAAyB;",
6
6
  "names": []
7
7
  }
@@ -7,12 +7,13 @@ import { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapsh
7
7
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
8
8
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
9
9
  import { Dictionary, DictionaryEntry } from "@open-mercato/core/modules/dictionaries/data/entities";
10
- import { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment } from "../data/entities.js";
10
+ import { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment, ResourcesResourceType } from "../data/entities.js";
11
11
  import {
12
12
  resourcesResourceCreateSchema,
13
13
  resourcesResourceUpdateSchema
14
14
  } from "../data/validators.js";
15
15
  import { resourcesResourceCrudEvents } from "../lib/crud.js";
16
+ import { lockResourceType } from "../lib/resourceTypeLock.js";
16
17
  import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from "./shared.js";
17
18
  import { RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from "../lib/capacityUnits.js";
18
19
  import { E } from "../../../generated/entities.ids.generated.js";
@@ -147,6 +148,50 @@ async function syncResourcesResourceTags(em, params) {
147
148
  em.persist(assignment);
148
149
  }
149
150
  }
151
+ async function countResourceTypes(em, resourceTypeId, scope, state) {
152
+ return em.count(ResourcesResourceType, {
153
+ id: resourceTypeId,
154
+ tenantId: scope.tenantId,
155
+ organizationId: scope.organizationId,
156
+ deletedAt: state === "live" ? null : { $ne: null }
157
+ });
158
+ }
159
+ async function assertResourceTypeAssignable(em, resourceTypeId, scope) {
160
+ if (!resourceTypeId) return;
161
+ await lockResourceType(em, resourceTypeId);
162
+ if (await countResourceTypes(em, resourceTypeId, scope, "live") > 0) return;
163
+ throw await resourceTypeUnavailableError(400);
164
+ }
165
+ async function resourceTypeUnavailableError(status) {
166
+ const { translate } = await resolveTranslations();
167
+ return new CrudHttpError(status, {
168
+ error: translate("resources.resources.errors.resourceTypeUnavailable", "The selected resource type is no longer available."),
169
+ code: "resources.resources.errors.resourceTypeUnavailable"
170
+ });
171
+ }
172
+ async function assertResourceTypeRestorable(em, record, resourceTypeId, scope) {
173
+ if (!resourceTypeId) return;
174
+ await lockResourceType(em, resourceTypeId);
175
+ if (record && await em.count(ResourcesResource, {
176
+ id: record.id,
177
+ tenantId: scope.tenantId,
178
+ organizationId: scope.organizationId,
179
+ resourceTypeId,
180
+ deletedAt: null
181
+ }) > 0) return;
182
+ if (await countResourceTypes(em, resourceTypeId, scope, "live") > 0) return;
183
+ if (await countResourceTypes(em, resourceTypeId, scope, "deleted") === 0) {
184
+ throw await resourceTypeUnavailableError(409);
185
+ }
186
+ const { translate } = await resolveTranslations();
187
+ throw new CrudHttpError(409, {
188
+ error: translate(
189
+ "resources.resources.errors.resourceTypeDeleted",
190
+ "This would put the resource on a deleted resource type. Restore the resource type first."
191
+ ),
192
+ code: "resources.resources.errors.resourceTypeDeleted"
193
+ });
194
+ }
150
195
  const createResourceCommand = {
151
196
  id: "resources.resources.create",
152
197
  async execute(rawInput, ctx) {
@@ -178,6 +223,7 @@ const createResourceCommand = {
178
223
  });
179
224
  await withAtomicFlush(em, [
180
225
  async () => {
226
+ await assertResourceTypeAssignable(em, parsed.resourceTypeId, parsed);
181
227
  em.persist(record);
182
228
  await em.flush();
183
229
  },
@@ -276,6 +322,7 @@ const createResourceCommand = {
276
322
  let record = await em.findOne(ResourcesResource, { id: after.id });
277
323
  await withAtomicFlush(em, [
278
324
  async () => {
325
+ await assertResourceTypeRestorable(em, record, after.resourceTypeId, after);
279
326
  if (!record) {
280
327
  record = em.create(ResourcesResource, {
281
328
  id: after.id,
@@ -378,6 +425,7 @@ const updateResourceCommand = {
378
425
  if (!record) throw new CrudHttpError(404, { error: "Resources resource not found." });
379
426
  ensureTenantScope(ctx, record.tenantId);
380
427
  ensureOrganizationScope(ctx, record.organizationId);
428
+ const resourceTypeChanged = parsed.resourceTypeId !== void 0 && parsed.resourceTypeId !== record.resourceTypeId;
381
429
  let capacityUnit;
382
430
  if (parsed.capacityUnitValue !== void 0) {
383
431
  const unitValue = typeof parsed.capacityUnitValue === "string" ? parsed.capacityUnitValue.trim() : "";
@@ -388,7 +436,8 @@ const updateResourceCommand = {
388
436
  ) : null;
389
437
  }
390
438
  await withAtomicFlush(em, [
391
- () => {
439
+ async () => {
440
+ if (resourceTypeChanged) await assertResourceTypeAssignable(em, parsed.resourceTypeId, record);
392
441
  if (parsed.capacityUnitValue !== void 0) {
393
442
  record.capacityUnitValue = capacityUnit?.value ?? null;
394
443
  record.capacityUnitName = capacityUnit?.name ?? null;
@@ -499,7 +548,8 @@ const updateResourceCommand = {
499
548
  const record = await em.findOne(ResourcesResource, { id: before.id });
500
549
  if (!record) return;
501
550
  await withAtomicFlush(em, [
502
- () => {
551
+ async () => {
552
+ await assertResourceTypeRestorable(em, record, before.resourceTypeId, before);
503
553
  record.name = before.name;
504
554
  record.description = before.description ?? null;
505
555
  record.resourceTypeId = before.resourceTypeId ?? null;
@@ -625,6 +675,7 @@ const deleteResourceCommand = {
625
675
  let record = await em.findOne(ResourcesResource, { id: before.id });
626
676
  await withAtomicFlush(em, [
627
677
  async () => {
678
+ await assertResourceTypeRestorable(em, record, before.resourceTypeId, before);
628
679
  if (!record) {
629
680
  record = em.create(ResourcesResource, {
630
681
  id: before.id,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/resources/commands/resources.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment } from '../data/entities'\nimport {\n resourcesResourceCreateSchema,\n resourcesResourceUpdateSchema,\n type ResourcesResourceCreateInput,\n type ResourcesResourceUpdateInput,\n} from '../data/validators'\nimport { resourcesResourceCrudEvents } from '../lib/crud'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\nimport { RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from '../lib/capacityUnits'\nimport { E } from '#generated/entities.ids.generated'\n\nconst resourceCrudIndexer: CrudIndexerConfig<ResourcesResource> = {\n entityType: E.resources.resources_resource,\n}\n\ntype CapacityUnitSnapshot = {\n value: string\n name: string\n color: string | null\n icon: string | null\n}\n\ntype ResourceSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n isActive: boolean\n availabilityRuleSetId: string | null\n customFieldsetCode: string | null\n tags: string[]\n deletedAt: string | null\n customFields?: CustomFieldSnapshot | null\n}\n\ntype ResourceUndoPayload = {\n before?: ResourceSnapshot | null\n after?: ResourceSnapshot | null\n customBefore?: CustomFieldSnapshot | null\n customAfter?: CustomFieldSnapshot | null\n}\n\nasync function resolveCapacityUnit(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n rawValue: string,\n): Promise<CapacityUnitSnapshot> {\n const trimmed = rawValue.trim()\n if (!trimmed) {\n throw new CrudHttpError(400, { error: 'Capacity unit is required.' })\n }\n const dictionary = await findOneWithDecryption(\n em,\n Dictionary,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n deletedAt: null,\n isActive: true,\n },\n undefined,\n scope,\n )\n if (!dictionary) {\n throw new CrudHttpError(400, { error: 'Capacity unit dictionary is not configured.' })\n }\n const normalizedValue = trimmed.toLowerCase()\n const entry = await findOneWithDecryption(\n em,\n DictionaryEntry,\n {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n normalizedValue,\n },\n { populate: ['dictionary'] },\n scope,\n )\n if (!entry) {\n throw new CrudHttpError(400, { error: 'Capacity unit not found.' })\n }\n return {\n value: entry.value,\n name: entry.label?.trim().length ? entry.label : entry.value,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }\n}\n\nfunction normalizeTagIds(tags?: Array<string | null | undefined>): string[] {\n if (!Array.isArray(tags)) return []\n const set = new Set<string>()\n tags.forEach((id) => {\n if (typeof id === 'string' && id.trim().length > 0) set.add(id.trim())\n })\n return Array.from(set)\n}\n\nasync function loadResourceSnapshot(em: EntityManager, id: string): Promise<ResourceSnapshot | null> {\n const resource = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id },\n undefined,\n { tenantId: null, organizationId: null },\n )\n if (!resource) return null\n const assignments = await em.find(\n ResourcesResourceTagAssignment,\n { resource: resource.id },\n { populate: ['tag'] },\n )\n const tags = assignments\n .map((assignment) => (assignment.tag as ResourcesResourceTag | undefined)?.id ?? null)\n .filter((tagId): tagId is string => typeof tagId === 'string' && tagId.length > 0)\n .sort((a, b) => a.localeCompare(b))\n return {\n id: resource.id,\n tenantId: resource.tenantId,\n organizationId: resource.organizationId,\n name: resource.name,\n description: resource.description ?? null,\n resourceTypeId: resource.resourceTypeId ?? null,\n capacity: resource.capacity ?? null,\n capacityUnitValue: resource.capacityUnitValue ?? null,\n capacityUnitName: resource.capacityUnitName ?? null,\n capacityUnitColor: resource.capacityUnitColor ?? null,\n capacityUnitIcon: resource.capacityUnitIcon ?? null,\n appearanceIcon: resource.appearanceIcon ?? null,\n appearanceColor: resource.appearanceColor ?? null,\n isActive: resource.isActive,\n availabilityRuleSetId: resource.availabilityRuleSetId ?? null,\n customFieldsetCode: resource.customFieldsetCode ?? null,\n tags,\n deletedAt: resource.deletedAt ? resource.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadResourceCustomSnapshot(em: EntityManager, snapshot: ResourceSnapshot): Promise<CustomFieldSnapshot> {\n return loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource,\n recordId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n })\n}\n\nasync function syncResourcesResourceTags(em: EntityManager, params: {\n resourceId: string\n organizationId: string\n tenantId: string\n tagIds?: Array<string | null | undefined> | null\n ignoreMissing?: boolean\n}) {\n if (params.tagIds === undefined) return\n const tagIds = normalizeTagIds(params.tagIds ?? [])\n if (tagIds.length === 0) {\n await em.nativeDelete(ResourcesResourceTagAssignment, { resource: params.resourceId })\n return\n }\n const tagsInScope = await em.find(ResourcesResourceTag, {\n id: { $in: tagIds },\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n })\n if (!params.ignoreMissing && tagsInScope.length !== tagIds.length) {\n throw new CrudHttpError(400, { error: 'One or more tags not found for this scope' })\n }\n const byId = new Map(tagsInScope.map((tag) => [tag.id, tag]))\n await em.nativeDelete(ResourcesResourceTagAssignment, { resource: params.resourceId })\n const resource = em.getReference(ResourcesResource, params.resourceId)\n for (const tagId of tagIds) {\n const tag = byId.get(tagId)\n if (!tag) continue\n const assignment = em.create(ResourcesResourceTagAssignment, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n resource,\n tag,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(assignment)\n }\n}\n\nconst createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { resourceId: string }> = {\n id: 'resources.resources.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const unitValue =\n typeof parsed.capacityUnitValue === 'string' ? parsed.capacityUnitValue.trim() : ''\n const unitSnapshot = unitValue\n ? await resolveCapacityUnit(em, { tenantId: parsed.tenantId, organizationId: parsed.organizationId }, unitValue)\n : null\n const record = em.create(ResourcesResource, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n resourceTypeId: parsed.resourceTypeId ?? null,\n capacity: parsed.capacity ?? null,\n capacityUnitValue: unitSnapshot?.value ?? null,\n capacityUnitName: unitSnapshot?.name ?? null,\n capacityUnitColor: unitSnapshot?.color ?? null,\n capacityUnitIcon: unitSnapshot?.icon ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n isActive: parsed.isActive ?? true,\n availabilityRuleSetId: parsed.availabilityRuleSetId ?? null,\n customFieldsetCode: parsed.customFieldsetCode ?? null,\n createdAt: now,\n updatedAt: now,\n })\n await withAtomicFlush(em, [\n async () => {\n em.persist(record)\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: parsed.tags,\n }),\n ], { transaction: true })\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceSnapshot(em, result.resourceId)\n if (!snapshot) return null\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { snapshot, custom }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceSnapshot(em, result?.resourceId ?? '')\n if (!snapshot) return null\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n const snapshotWithCustom: ResourceSnapshot = custom\n ? { ...snapshot, customFields: custom }\n : snapshot\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.create', 'Create resource'),\n resourceKind: 'resources.resource',\n resourceId: snapshotWithCustom.id,\n tenantId: snapshotWithCustom.tenantId,\n organizationId: snapshotWithCustom.organizationId,\n snapshotAfter: snapshotWithCustom,\n payload: {\n undo: {\n after: snapshotWithCustom,\n customAfter: custom,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ResourcesResource, { id: after.id })\n if (record) {\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const after = resolveRedoSnapshot<ResourceSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for resource create' })\n }\n const fallbackCustomAfter = (after as ResourceSnapshot).customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(ResourcesResource, { id: after.id })\n await withAtomicFlush(em, [\n async () => {\n if (!record) {\n record = em.create(ResourcesResource, {\n id: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n name: after.name,\n description: after.description ?? null,\n resourceTypeId: after.resourceTypeId ?? null,\n capacity: after.capacity ?? null,\n capacityUnitValue: after.capacityUnitValue ?? null,\n capacityUnitName: after.capacityUnitName ?? null,\n capacityUnitColor: after.capacityUnitColor ?? null,\n capacityUnitIcon: after.capacityUnitIcon ?? null,\n appearanceIcon: after.appearanceIcon ?? null,\n appearanceColor: after.appearanceColor ?? null,\n isActive: after.isActive,\n availabilityRuleSetId: after.availabilityRuleSetId ?? null,\n customFieldsetCode: after.customFieldsetCode ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.name = after.name\n record.description = after.description ?? null\n record.resourceTypeId = after.resourceTypeId ?? null\n record.capacity = after.capacity ?? null\n record.capacityUnitValue = after.capacityUnitValue ?? null\n record.capacityUnitName = after.capacityUnitName ?? null\n record.capacityUnitColor = after.capacityUnitColor ?? null\n record.capacityUnitIcon = after.capacityUnitIcon ?? null\n record.appearanceIcon = after.appearanceIcon ?? null\n record.appearanceColor = after.appearanceColor ?? null\n record.isActive = after.isActive\n record.availabilityRuleSetId = after.availabilityRuleSetId ?? null\n record.customFieldsetCode = after.customFieldsetCode ?? null\n record.deletedAt = null\n record.updatedAt = new Date()\n }\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: (record as ResourcesResource).id,\n organizationId: (record as ResourcesResource).organizationId,\n tenantId: (record as ResourcesResource).tenantId,\n tagIds: after.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n const resolvedRecord = record as ResourcesResource\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customAfter = payload?.customAfter ?? fallbackCustomAfter ?? undefined\n if (customAfter) {\n const reset = buildCustomFieldResetMap(customAfter, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: resolvedRecord.id,\n tenantId: resolvedRecord.tenantId,\n organizationId: resolvedRecord.organizationId,\n values: reset,\n })\n }\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: resolvedRecord,\n identifiers: {\n id: resolvedRecord.id,\n organizationId: resolvedRecord.organizationId,\n tenantId: resolvedRecord.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: resolvedRecord.id }\n },\n}\n\nconst updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { resourceId: string }> = {\n id: 'resources.resources.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(resourcesResourceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceSnapshot(em, parsed.id)\n if (!snapshot) return {}\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id: parsed.id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n\n let capacityUnit: CapacityUnitSnapshot | null | undefined\n if (parsed.capacityUnitValue !== undefined) {\n const unitValue =\n typeof parsed.capacityUnitValue === 'string' ? parsed.capacityUnitValue.trim() : ''\n capacityUnit = unitValue\n ? await resolveCapacityUnit(\n em,\n { tenantId: record.tenantId, organizationId: record.organizationId },\n unitValue,\n )\n : null\n }\n await withAtomicFlush(em, [\n () => {\n if (parsed.capacityUnitValue !== undefined) {\n record.capacityUnitValue = capacityUnit?.value ?? null\n record.capacityUnitName = capacityUnit?.name ?? null\n record.capacityUnitColor = capacityUnit?.color ?? null\n record.capacityUnitIcon = capacityUnit?.icon ?? null\n }\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.resourceTypeId !== undefined) record.resourceTypeId = parsed.resourceTypeId ?? null\n if (parsed.capacity !== undefined) record.capacity = parsed.capacity ?? null\n if (parsed.appearanceIcon !== undefined) record.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) record.appearanceColor = parsed.appearanceColor ?? null\n if (parsed.availabilityRuleSetId !== undefined) record.availabilityRuleSetId = parsed.availabilityRuleSetId ?? null\n if (parsed.customFieldsetCode !== undefined) record.customFieldsetCode = parsed.customFieldsetCode ?? null\n record.updatedAt = new Date()\n if (parsed.isActive !== undefined) record.isActive = parsed.isActive\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: parsed.tags,\n }),\n ], { transaction: true })\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as ResourceSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadResourceSnapshot(em, before.id)\n if (!after) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const customAfter = await loadResourceCustomSnapshot(em, after)\n const beforeWithCustom: ResourceSnapshot = customBefore\n ? { ...before, customFields: customBefore }\n : before\n const afterWithCustom: ResourceSnapshot = customAfter\n ? { ...after, customFields: customAfter }\n : after\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'resourceTypeId',\n 'capacity',\n 'capacityUnitValue',\n 'capacityUnitName',\n 'capacityUnitColor',\n 'capacityUnitIcon',\n 'appearanceIcon',\n 'appearanceColor',\n 'isActive',\n 'availabilityRuleSetId',\n 'customFieldsetCode',\n 'deletedAt',\n ])\n if (before.tags.join(',') !== after.tags.join(',')) {\n changes.tags = { from: before.tags, to: after.tags }\n }\n const customChanges = diffCustomFieldChanges(customBefore, customAfter)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: customBefore ?? null, to: customAfter ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.update', 'Update resource'),\n resourceKind: 'resources.resource',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: beforeWithCustom,\n snapshotAfter: afterWithCustom,\n changes,\n payload: {\n undo: {\n before: beforeWithCustom,\n after: afterWithCustom,\n customBefore: customBefore ?? null,\n customAfter,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const fallbackCustomBefore = (before as ResourceSnapshot).customFields ?? null\n const fallbackCustomAfter = (payload?.after as ResourceSnapshot | null | undefined)?.customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ResourcesResource, { id: before.id })\n if (!record) return\n await withAtomicFlush(em, [\n () => {\n record.name = before.name\n record.description = before.description ?? null\n record.resourceTypeId = before.resourceTypeId ?? null\n record.capacity = before.capacity ?? null\n record.capacityUnitValue = before.capacityUnitValue ?? null\n record.capacityUnitName = before.capacityUnitName ?? null\n record.capacityUnitColor = before.capacityUnitColor ?? null\n record.capacityUnitIcon = before.capacityUnitIcon ?? null\n record.appearanceIcon = before.appearanceIcon ?? null\n record.appearanceColor = before.appearanceColor ?? null\n record.isActive = before.isActive\n record.availabilityRuleSetId = before.availabilityRuleSetId ?? null\n record.customFieldsetCode = before.customFieldsetCode ?? null\n record.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n record.updatedAt = new Date()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: before.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customBefore = payload.customBefore ?? fallbackCustomBefore ?? undefined\n const customAfter = payload.customAfter ?? fallbackCustomAfter ?? undefined\n if (customBefore || customAfter) {\n const reset = buildCustomFieldResetMap(customBefore, customAfter)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n },\n}\n\nconst deleteResourceCommand: CommandHandler<{ id?: string }, { resourceId: string }> = {\n id: 'resources.resources.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceSnapshot(em, id)\n if (!snapshot) return {}\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ResourceSnapshot | undefined\n if (!before) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const beforeWithCustom: ResourceSnapshot = customBefore\n ? { ...before, customFields: customBefore }\n : before\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.delete', 'Delete resource'),\n resourceKind: 'resources.resource',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: beforeWithCustom,\n payload: {\n undo: {\n before: beforeWithCustom,\n customBefore: customBefore ?? null,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const fallbackCustomBefore = (before as ResourceSnapshot).customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(ResourcesResource, { id: before.id })\n await withAtomicFlush(em, [\n async () => {\n if (!record) {\n record = em.create(ResourcesResource, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n resourceTypeId: before.resourceTypeId ?? null,\n capacity: before.capacity ?? null,\n capacityUnitValue: before.capacityUnitValue ?? null,\n capacityUnitName: before.capacityUnitName ?? null,\n capacityUnitColor: before.capacityUnitColor ?? null,\n capacityUnitIcon: before.capacityUnitIcon ?? null,\n appearanceIcon: before.appearanceIcon ?? null,\n appearanceColor: before.appearanceColor ?? null,\n isActive: before.isActive,\n availabilityRuleSetId: before.availabilityRuleSetId ?? null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.name = before.name\n record.description = before.description ?? null\n record.resourceTypeId = before.resourceTypeId ?? null\n record.capacity = before.capacity ?? null\n record.capacityUnitValue = before.capacityUnitValue ?? null\n record.capacityUnitName = before.capacityUnitName ?? null\n record.capacityUnitColor = before.capacityUnitColor ?? null\n record.capacityUnitIcon = before.capacityUnitIcon ?? null\n record.appearanceIcon = before.appearanceIcon ?? null\n record.appearanceColor = before.appearanceColor ?? null\n record.isActive = before.isActive\n record.availabilityRuleSetId = before.availabilityRuleSetId ?? null\n record.customFieldsetCode = before.customFieldsetCode ?? null\n record.deletedAt = null\n record.updatedAt = new Date()\n }\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: (record as ResourcesResource).id,\n organizationId: (record as ResourcesResource).organizationId,\n tenantId: (record as ResourcesResource).tenantId,\n tagIds: before.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n const resolvedRecord = record as ResourcesResource\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customBefore = payload.customBefore ?? fallbackCustomBefore ?? undefined\n if (customBefore) {\n const reset = buildCustomFieldResetMap(customBefore, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: resolvedRecord.id,\n tenantId: resolvedRecord.tenantId,\n organizationId: resolvedRecord.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: resolvedRecord,\n identifiers: {\n id: resolvedRecord.id,\n organizationId: resolvedRecord.organizationId,\n tenantId: resolvedRecord.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n },\n}\n\nregisterCommand(createResourceCommand)\nregisterCommand(updateResourceCommand)\nregisterCommand(deleteResourceCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment, ResourcesResourceType } from '../data/entities'\nimport {\n resourcesResourceCreateSchema,\n resourcesResourceUpdateSchema,\n type ResourcesResourceCreateInput,\n type ResourcesResourceUpdateInput,\n} from '../data/validators'\nimport { resourcesResourceCrudEvents } from '../lib/crud'\nimport { lockResourceType } from '../lib/resourceTypeLock'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\nimport { RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from '../lib/capacityUnits'\nimport { E } from '#generated/entities.ids.generated'\n\nconst resourceCrudIndexer: CrudIndexerConfig<ResourcesResource> = {\n entityType: E.resources.resources_resource,\n}\n\ntype CapacityUnitSnapshot = {\n value: string\n name: string\n color: string | null\n icon: string | null\n}\n\ntype ResourceSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n name: string\n description: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n appearanceIcon: string | null\n appearanceColor: string | null\n isActive: boolean\n availabilityRuleSetId: string | null\n customFieldsetCode: string | null\n tags: string[]\n deletedAt: string | null\n customFields?: CustomFieldSnapshot | null\n}\n\ntype ResourceUndoPayload = {\n before?: ResourceSnapshot | null\n after?: ResourceSnapshot | null\n customBefore?: CustomFieldSnapshot | null\n customAfter?: CustomFieldSnapshot | null\n}\n\nasync function resolveCapacityUnit(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n rawValue: string,\n): Promise<CapacityUnitSnapshot> {\n const trimmed = rawValue.trim()\n if (!trimmed) {\n throw new CrudHttpError(400, { error: 'Capacity unit is required.' })\n }\n const dictionary = await findOneWithDecryption(\n em,\n Dictionary,\n {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n key: RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY,\n deletedAt: null,\n isActive: true,\n },\n undefined,\n scope,\n )\n if (!dictionary) {\n throw new CrudHttpError(400, { error: 'Capacity unit dictionary is not configured.' })\n }\n const normalizedValue = trimmed.toLowerCase()\n const entry = await findOneWithDecryption(\n em,\n DictionaryEntry,\n {\n dictionary,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n normalizedValue,\n },\n { populate: ['dictionary'] },\n scope,\n )\n if (!entry) {\n throw new CrudHttpError(400, { error: 'Capacity unit not found.' })\n }\n return {\n value: entry.value,\n name: entry.label?.trim().length ? entry.label : entry.value,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }\n}\n\nfunction normalizeTagIds(tags?: Array<string | null | undefined>): string[] {\n if (!Array.isArray(tags)) return []\n const set = new Set<string>()\n tags.forEach((id) => {\n if (typeof id === 'string' && id.trim().length > 0) set.add(id.trim())\n })\n return Array.from(set)\n}\n\nasync function loadResourceSnapshot(em: EntityManager, id: string): Promise<ResourceSnapshot | null> {\n const resource = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id },\n undefined,\n { tenantId: null, organizationId: null },\n )\n if (!resource) return null\n const assignments = await em.find(\n ResourcesResourceTagAssignment,\n { resource: resource.id },\n { populate: ['tag'] },\n )\n const tags = assignments\n .map((assignment) => (assignment.tag as ResourcesResourceTag | undefined)?.id ?? null)\n .filter((tagId): tagId is string => typeof tagId === 'string' && tagId.length > 0)\n .sort((a, b) => a.localeCompare(b))\n return {\n id: resource.id,\n tenantId: resource.tenantId,\n organizationId: resource.organizationId,\n name: resource.name,\n description: resource.description ?? null,\n resourceTypeId: resource.resourceTypeId ?? null,\n capacity: resource.capacity ?? null,\n capacityUnitValue: resource.capacityUnitValue ?? null,\n capacityUnitName: resource.capacityUnitName ?? null,\n capacityUnitColor: resource.capacityUnitColor ?? null,\n capacityUnitIcon: resource.capacityUnitIcon ?? null,\n appearanceIcon: resource.appearanceIcon ?? null,\n appearanceColor: resource.appearanceColor ?? null,\n isActive: resource.isActive,\n availabilityRuleSetId: resource.availabilityRuleSetId ?? null,\n customFieldsetCode: resource.customFieldsetCode ?? null,\n tags,\n deletedAt: resource.deletedAt ? resource.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadResourceCustomSnapshot(em: EntityManager, snapshot: ResourceSnapshot): Promise<CustomFieldSnapshot> {\n return loadCustomFieldSnapshot(em, {\n entityId: E.resources.resources_resource,\n recordId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n })\n}\n\nasync function syncResourcesResourceTags(em: EntityManager, params: {\n resourceId: string\n organizationId: string\n tenantId: string\n tagIds?: Array<string | null | undefined> | null\n ignoreMissing?: boolean\n}) {\n if (params.tagIds === undefined) return\n const tagIds = normalizeTagIds(params.tagIds ?? [])\n if (tagIds.length === 0) {\n await em.nativeDelete(ResourcesResourceTagAssignment, { resource: params.resourceId })\n return\n }\n const tagsInScope = await em.find(ResourcesResourceTag, {\n id: { $in: tagIds },\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n })\n if (!params.ignoreMissing && tagsInScope.length !== tagIds.length) {\n throw new CrudHttpError(400, { error: 'One or more tags not found for this scope' })\n }\n const byId = new Map(tagsInScope.map((tag) => [tag.id, tag]))\n await em.nativeDelete(ResourcesResourceTagAssignment, { resource: params.resourceId })\n const resource = em.getReference(ResourcesResource, params.resourceId)\n for (const tagId of tagIds) {\n const tag = byId.get(tagId)\n if (!tag) continue\n const assignment = em.create(ResourcesResourceTagAssignment, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n resource,\n tag,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(assignment)\n }\n}\n\ntype ResourceScope = { tenantId: string; organizationId: string }\n\nasync function countResourceTypes(\n em: EntityManager,\n resourceTypeId: string,\n scope: ResourceScope,\n state: 'live' | 'deleted',\n): Promise<number> {\n return em.count(ResourcesResourceType, {\n id: resourceTypeId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: state === 'live' ? null : { $ne: null },\n })\n}\n\nasync function assertResourceTypeAssignable(\n em: EntityManager,\n resourceTypeId: string | null | undefined,\n scope: ResourceScope,\n): Promise<void> {\n if (!resourceTypeId) return\n await lockResourceType(em, resourceTypeId)\n if (await countResourceTypes(em, resourceTypeId, scope, 'live') > 0) return\n throw await resourceTypeUnavailableError(400)\n}\n\nasync function resourceTypeUnavailableError(status: 400 | 409): Promise<CrudHttpError> {\n const { translate } = await resolveTranslations()\n return new CrudHttpError(status, {\n error: translate('resources.resources.errors.resourceTypeUnavailable', 'The selected resource type is no longer available.'),\n code: 'resources.resources.errors.resourceTypeUnavailable',\n })\n}\n\nasync function assertResourceTypeRestorable(\n em: EntityManager,\n record: ResourcesResource | null,\n resourceTypeId: string | null | undefined,\n scope: ResourceScope,\n): Promise<void> {\n if (!resourceTypeId) return\n await lockResourceType(em, resourceTypeId)\n if (record && await em.count(ResourcesResource, {\n id: record.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n resourceTypeId,\n deletedAt: null,\n }) > 0) return\n if (await countResourceTypes(em, resourceTypeId, scope, 'live') > 0) return\n if (await countResourceTypes(em, resourceTypeId, scope, 'deleted') === 0) {\n throw await resourceTypeUnavailableError(409)\n }\n const { translate } = await resolveTranslations()\n throw new CrudHttpError(409, {\n error: translate(\n 'resources.resources.errors.resourceTypeDeleted',\n 'This would put the resource on a deleted resource type. Restore the resource type first.',\n ),\n code: 'resources.resources.errors.resourceTypeDeleted',\n })\n}\n\nconst createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { resourceId: string }> = {\n id: 'resources.resources.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceCreateSchema, rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n const unitValue =\n typeof parsed.capacityUnitValue === 'string' ? parsed.capacityUnitValue.trim() : ''\n const unitSnapshot = unitValue\n ? await resolveCapacityUnit(em, { tenantId: parsed.tenantId, organizationId: parsed.organizationId }, unitValue)\n : null\n const record = em.create(ResourcesResource, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n name: parsed.name,\n description: parsed.description ?? null,\n resourceTypeId: parsed.resourceTypeId ?? null,\n capacity: parsed.capacity ?? null,\n capacityUnitValue: unitSnapshot?.value ?? null,\n capacityUnitName: unitSnapshot?.name ?? null,\n capacityUnitColor: unitSnapshot?.color ?? null,\n capacityUnitIcon: unitSnapshot?.icon ?? null,\n appearanceIcon: parsed.appearanceIcon ?? null,\n appearanceColor: parsed.appearanceColor ?? null,\n isActive: parsed.isActive ?? true,\n availabilityRuleSetId: parsed.availabilityRuleSetId ?? null,\n customFieldsetCode: parsed.customFieldsetCode ?? null,\n createdAt: now,\n updatedAt: now,\n })\n await withAtomicFlush(em, [\n async () => {\n await assertResourceTypeAssignable(em, parsed.resourceTypeId, parsed)\n em.persist(record)\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: parsed.tags,\n }),\n ], { transaction: true })\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceSnapshot(em, result.resourceId)\n if (!snapshot) return null\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { snapshot, custom }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadResourceSnapshot(em, result?.resourceId ?? '')\n if (!snapshot) return null\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n const snapshotWithCustom: ResourceSnapshot = custom\n ? { ...snapshot, customFields: custom }\n : snapshot\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.create', 'Create resource'),\n resourceKind: 'resources.resource',\n resourceId: snapshotWithCustom.id,\n tenantId: snapshotWithCustom.tenantId,\n organizationId: snapshotWithCustom.organizationId,\n snapshotAfter: snapshotWithCustom,\n payload: {\n undo: {\n after: snapshotWithCustom,\n customAfter: custom,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ResourcesResource, { id: after.id })\n if (record) {\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n }\n },\n redo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const after = resolveRedoSnapshot<ResourceSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for resource create' })\n }\n const fallbackCustomAfter = (after as ResourceSnapshot).customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(ResourcesResource, { id: after.id })\n await withAtomicFlush(em, [\n async () => {\n await assertResourceTypeRestorable(em, record, after.resourceTypeId, after)\n if (!record) {\n record = em.create(ResourcesResource, {\n id: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n name: after.name,\n description: after.description ?? null,\n resourceTypeId: after.resourceTypeId ?? null,\n capacity: after.capacity ?? null,\n capacityUnitValue: after.capacityUnitValue ?? null,\n capacityUnitName: after.capacityUnitName ?? null,\n capacityUnitColor: after.capacityUnitColor ?? null,\n capacityUnitIcon: after.capacityUnitIcon ?? null,\n appearanceIcon: after.appearanceIcon ?? null,\n appearanceColor: after.appearanceColor ?? null,\n isActive: after.isActive,\n availabilityRuleSetId: after.availabilityRuleSetId ?? null,\n customFieldsetCode: after.customFieldsetCode ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.name = after.name\n record.description = after.description ?? null\n record.resourceTypeId = after.resourceTypeId ?? null\n record.capacity = after.capacity ?? null\n record.capacityUnitValue = after.capacityUnitValue ?? null\n record.capacityUnitName = after.capacityUnitName ?? null\n record.capacityUnitColor = after.capacityUnitColor ?? null\n record.capacityUnitIcon = after.capacityUnitIcon ?? null\n record.appearanceIcon = after.appearanceIcon ?? null\n record.appearanceColor = after.appearanceColor ?? null\n record.isActive = after.isActive\n record.availabilityRuleSetId = after.availabilityRuleSetId ?? null\n record.customFieldsetCode = after.customFieldsetCode ?? null\n record.deletedAt = null\n record.updatedAt = new Date()\n }\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: (record as ResourcesResource).id,\n organizationId: (record as ResourcesResource).organizationId,\n tenantId: (record as ResourcesResource).tenantId,\n tagIds: after.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n const resolvedRecord = record as ResourcesResource\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customAfter = payload?.customAfter ?? fallbackCustomAfter ?? undefined\n if (customAfter) {\n const reset = buildCustomFieldResetMap(customAfter, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: resolvedRecord.id,\n tenantId: resolvedRecord.tenantId,\n organizationId: resolvedRecord.organizationId,\n values: reset,\n })\n }\n\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: resolvedRecord,\n identifiers: {\n id: resolvedRecord.id,\n organizationId: resolvedRecord.organizationId,\n tenantId: resolvedRecord.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: resolvedRecord.id }\n },\n}\n\nconst updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { resourceId: string }> = {\n id: 'resources.resources.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(resourcesResourceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceSnapshot(em, parsed.id)\n if (!snapshot) return {}\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(resourcesResourceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id: parsed.id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const resourceTypeChanged = parsed.resourceTypeId !== undefined && parsed.resourceTypeId !== record.resourceTypeId\n\n let capacityUnit: CapacityUnitSnapshot | null | undefined\n if (parsed.capacityUnitValue !== undefined) {\n const unitValue =\n typeof parsed.capacityUnitValue === 'string' ? parsed.capacityUnitValue.trim() : ''\n capacityUnit = unitValue\n ? await resolveCapacityUnit(\n em,\n { tenantId: record.tenantId, organizationId: record.organizationId },\n unitValue,\n )\n : null\n }\n await withAtomicFlush(em, [\n async () => {\n if (resourceTypeChanged) await assertResourceTypeAssignable(em, parsed.resourceTypeId, record)\n if (parsed.capacityUnitValue !== undefined) {\n record.capacityUnitValue = capacityUnit?.value ?? null\n record.capacityUnitName = capacityUnit?.name ?? null\n record.capacityUnitColor = capacityUnit?.color ?? null\n record.capacityUnitIcon = capacityUnit?.icon ?? null\n }\n if (parsed.name !== undefined) record.name = parsed.name\n if (parsed.description !== undefined) record.description = parsed.description ?? null\n if (parsed.resourceTypeId !== undefined) record.resourceTypeId = parsed.resourceTypeId ?? null\n if (parsed.capacity !== undefined) record.capacity = parsed.capacity ?? null\n if (parsed.appearanceIcon !== undefined) record.appearanceIcon = parsed.appearanceIcon ?? null\n if (parsed.appearanceColor !== undefined) record.appearanceColor = parsed.appearanceColor ?? null\n if (parsed.availabilityRuleSetId !== undefined) record.availabilityRuleSetId = parsed.availabilityRuleSetId ?? null\n if (parsed.customFieldsetCode !== undefined) record.customFieldsetCode = parsed.customFieldsetCode ?? null\n record.updatedAt = new Date()\n if (parsed.isActive !== undefined) record.isActive = parsed.isActive\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: parsed.tags,\n }),\n ], { transaction: true })\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: custom,\n })\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = snapshots.before as ResourceSnapshot | undefined\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadResourceSnapshot(em, before.id)\n if (!after) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const customAfter = await loadResourceCustomSnapshot(em, after)\n const beforeWithCustom: ResourceSnapshot = customBefore\n ? { ...before, customFields: customBefore }\n : before\n const afterWithCustom: ResourceSnapshot = customAfter\n ? { ...after, customFields: customAfter }\n : after\n const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [\n 'name',\n 'description',\n 'resourceTypeId',\n 'capacity',\n 'capacityUnitValue',\n 'capacityUnitName',\n 'capacityUnitColor',\n 'capacityUnitIcon',\n 'appearanceIcon',\n 'appearanceColor',\n 'isActive',\n 'availabilityRuleSetId',\n 'customFieldsetCode',\n 'deletedAt',\n ])\n if (before.tags.join(',') !== after.tags.join(',')) {\n changes.tags = { from: before.tags, to: after.tags }\n }\n const customChanges = diffCustomFieldChanges(customBefore, customAfter)\n if (Object.keys(customChanges).length) {\n changes.customFields = { from: customBefore ?? null, to: customAfter ?? null }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.update', 'Update resource'),\n resourceKind: 'resources.resource',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: beforeWithCustom,\n snapshotAfter: afterWithCustom,\n changes,\n payload: {\n undo: {\n before: beforeWithCustom,\n after: afterWithCustom,\n customBefore: customBefore ?? null,\n customAfter,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const fallbackCustomBefore = (before as ResourceSnapshot).customFields ?? null\n const fallbackCustomAfter = (payload?.after as ResourceSnapshot | null | undefined)?.customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(ResourcesResource, { id: before.id })\n if (!record) return\n await withAtomicFlush(em, [\n async () => {\n await assertResourceTypeRestorable(em, record, before.resourceTypeId, before)\n record.name = before.name\n record.description = before.description ?? null\n record.resourceTypeId = before.resourceTypeId ?? null\n record.capacity = before.capacity ?? null\n record.capacityUnitValue = before.capacityUnitValue ?? null\n record.capacityUnitName = before.capacityUnitName ?? null\n record.capacityUnitColor = before.capacityUnitColor ?? null\n record.capacityUnitIcon = before.capacityUnitIcon ?? null\n record.appearanceIcon = before.appearanceIcon ?? null\n record.appearanceColor = before.appearanceColor ?? null\n record.isActive = before.isActive\n record.availabilityRuleSetId = before.availabilityRuleSetId ?? null\n record.customFieldsetCode = before.customFieldsetCode ?? null\n record.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null\n record.updatedAt = new Date()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n tagIds: before.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customBefore = payload.customBefore ?? fallbackCustomBefore ?? undefined\n const customAfter = payload.customAfter ?? fallbackCustomAfter ?? undefined\n if (customBefore || customAfter) {\n const reset = buildCustomFieldResetMap(customBefore, customAfter)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n },\n}\n\nconst deleteResourceCommand: CommandHandler<{ id?: string }, { resourceId: string }> = {\n id: 'resources.resources.delete',\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadResourceSnapshot(em, id)\n if (!snapshot) return {}\n const custom = await loadResourceCustomSnapshot(em, snapshot)\n return { before: snapshot, customBefore: custom }\n },\n async execute(input, ctx) {\n const id = input?.id\n if (!id) throw new CrudHttpError(400, { error: 'Resource id is required.' })\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n ResourcesResource,\n { id, deletedAt: null },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Resources resource not found.' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n record.deletedAt = new Date()\n record.updatedAt = new Date()\n await em.flush()\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n return { resourceId: record.id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as ResourceSnapshot | undefined\n if (!before) return null\n const customBefore = (snapshots as { customBefore?: CustomFieldSnapshot | null }).customBefore ?? undefined\n const beforeWithCustom: ResourceSnapshot = customBefore\n ? { ...before, customFields: customBefore }\n : before\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('resources.audit.resources.delete', 'Delete resource'),\n resourceKind: 'resources.resource',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: beforeWithCustom,\n payload: {\n undo: {\n before: beforeWithCustom,\n customBefore: customBefore ?? null,\n } satisfies ResourceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ResourceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const fallbackCustomBefore = (before as ResourceSnapshot).customFields ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(ResourcesResource, { id: before.id })\n await withAtomicFlush(em, [\n async () => {\n await assertResourceTypeRestorable(em, record, before.resourceTypeId, before)\n if (!record) {\n record = em.create(ResourcesResource, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n name: before.name,\n description: before.description ?? null,\n resourceTypeId: before.resourceTypeId ?? null,\n capacity: before.capacity ?? null,\n capacityUnitValue: before.capacityUnitValue ?? null,\n capacityUnitName: before.capacityUnitName ?? null,\n capacityUnitColor: before.capacityUnitColor ?? null,\n capacityUnitIcon: before.capacityUnitIcon ?? null,\n appearanceIcon: before.appearanceIcon ?? null,\n appearanceColor: before.appearanceColor ?? null,\n isActive: before.isActive,\n availabilityRuleSetId: before.availabilityRuleSetId ?? null,\n customFieldsetCode: before.customFieldsetCode ?? null,\n deletedAt: null,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(record)\n } else {\n record.name = before.name\n record.description = before.description ?? null\n record.resourceTypeId = before.resourceTypeId ?? null\n record.capacity = before.capacity ?? null\n record.capacityUnitValue = before.capacityUnitValue ?? null\n record.capacityUnitName = before.capacityUnitName ?? null\n record.capacityUnitColor = before.capacityUnitColor ?? null\n record.capacityUnitIcon = before.capacityUnitIcon ?? null\n record.appearanceIcon = before.appearanceIcon ?? null\n record.appearanceColor = before.appearanceColor ?? null\n record.isActive = before.isActive\n record.availabilityRuleSetId = before.availabilityRuleSetId ?? null\n record.customFieldsetCode = before.customFieldsetCode ?? null\n record.deletedAt = null\n record.updatedAt = new Date()\n }\n await em.flush()\n },\n () => syncResourcesResourceTags(em, {\n resourceId: (record as ResourcesResource).id,\n organizationId: (record as ResourcesResource).organizationId,\n tenantId: (record as ResourcesResource).tenantId,\n tagIds: before.tags,\n ignoreMissing: true,\n }),\n ], { transaction: true })\n const resolvedRecord = record as ResourcesResource\n\n const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)\n const customBefore = payload.customBefore ?? fallbackCustomBefore ?? undefined\n if (customBefore) {\n const reset = buildCustomFieldResetMap(customBefore, undefined)\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.resources.resources_resource,\n recordId: resolvedRecord.id,\n tenantId: resolvedRecord.tenantId,\n organizationId: resolvedRecord.organizationId,\n values: reset,\n })\n }\n\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: resolvedRecord,\n identifiers: {\n id: resolvedRecord.id,\n organizationId: resolvedRecord.organizationId,\n tenantId: resolvedRecord.tenantId,\n },\n events: resourcesResourceCrudEvents,\n indexer: resourceCrudIndexer,\n })\n },\n}\n\nregisterCommand(createResourceCommand)\nregisterCommand(updateResourceCommand)\nregisterCommand(deleteResourceCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,15 @@
1
+ const RESOURCE_TYPE_LOCK_SQL = "select pg_advisory_xact_lock(hashtextextended(?, 0))";
2
+ function resourceTypeLockKey(resourceTypeId) {
3
+ return `resources:resource-type:${resourceTypeId.toLowerCase()}`;
4
+ }
5
+ async function lockResourceType(em, resourceTypeId) {
6
+ if (!em.isInTransaction()) {
7
+ throw new Error("[internal] lockResourceType requires an open transaction");
8
+ }
9
+ await em.execute(RESOURCE_TYPE_LOCK_SQL, [resourceTypeLockKey(resourceTypeId)]);
10
+ }
11
+ export {
12
+ lockResourceType,
13
+ resourceTypeLockKey
14
+ };
15
+ //# sourceMappingURL=resourceTypeLock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/resources/lib/resourceTypeLock.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\n\nconst RESOURCE_TYPE_LOCK_SQL = 'select pg_advisory_xact_lock(hashtextextended(?, 0))'\n\nexport function resourceTypeLockKey(resourceTypeId: string): string {\n return `resources:resource-type:${resourceTypeId.toLowerCase()}`\n}\n\nexport async function lockResourceType(em: EntityManager, resourceTypeId: string): Promise<void> {\n if (!em.isInTransaction()) {\n throw new Error('[internal] lockResourceType requires an open transaction')\n }\n await em.execute(RESOURCE_TYPE_LOCK_SQL, [resourceTypeLockKey(resourceTypeId)])\n}\n"],
5
+ "mappings": "AAEA,MAAM,yBAAyB;AAExB,SAAS,oBAAoB,gBAAgC;AAClE,SAAO,2BAA2B,eAAe,YAAY,CAAC;AAChE;AAEA,eAAsB,iBAAiB,IAAmB,gBAAuC;AAC/F,MAAI,CAAC,GAAG,gBAAgB,GAAG;AACzB,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,QAAM,GAAG,QAAQ,wBAAwB,CAAC,oBAAoB,cAAc,CAAC,CAAC;AAChF;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7994.1.c9f31972aa",
3
+ "version": "0.10.1-develop.8004.1.a8a79ee43f",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -266,16 +266,16 @@
266
266
  "zod": "^4.6.6"
267
267
  },
268
268
  "peerDependencies": {
269
- "@open-mercato/ai-assistant": "0.10.1-develop.7994.1.c9f31972aa",
270
- "@open-mercato/shared": "0.10.1-develop.7994.1.c9f31972aa",
271
- "@open-mercato/ui": "0.10.1-develop.7994.1.c9f31972aa",
269
+ "@open-mercato/ai-assistant": "0.10.1-develop.8004.1.a8a79ee43f",
270
+ "@open-mercato/shared": "0.10.1-develop.8004.1.a8a79ee43f",
271
+ "@open-mercato/ui": "0.10.1-develop.8004.1.a8a79ee43f",
272
272
  "react": "^19.0.0",
273
273
  "react-dom": "^19.0.0"
274
274
  },
275
275
  "devDependencies": {
276
- "@open-mercato/ai-assistant": "0.10.1-develop.7994.1.c9f31972aa",
277
- "@open-mercato/shared": "0.10.1-develop.7994.1.c9f31972aa",
278
- "@open-mercato/ui": "0.10.1-develop.7994.1.c9f31972aa",
276
+ "@open-mercato/ai-assistant": "0.10.1-develop.8004.1.a8a79ee43f",
277
+ "@open-mercato/shared": "0.10.1-develop.8004.1.a8a79ee43f",
278
+ "@open-mercato/ui": "0.10.1-develop.8004.1.a8a79ee43f",
279
279
  "@testing-library/dom": "^10.4.1",
280
280
  "@testing-library/jest-dom": "^7.0.1",
281
281
  "@testing-library/react": "^16.3.3",
@@ -103,7 +103,7 @@ export function createIntegrationStateService(em: EntityManager) {
103
103
 
104
104
  const created = em.create(IntegrationState, {
105
105
  integrationId,
106
- isEnabled: input.isEnabled ?? false,
106
+ isEnabled: input.isEnabled ?? resolvedBefore.isEnabled,
107
107
  apiVersion: input.apiVersion,
108
108
  reauthRequired: input.reauthRequired ?? false,
109
109
  lastHealthStatus: input.lastHealthStatus,
@@ -6,6 +6,7 @@ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, parseWithCustomFields, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'
7
7
  import { buildCustomFieldResetMap, diffCustomFieldChanges, loadCustomFieldSnapshot, type CustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
8
8
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
+ import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
9
10
  import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
10
11
  import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
11
12
  import { ResourcesResource, ResourcesResourceType } from '../data/entities'
@@ -16,6 +17,7 @@ import {
16
17
  type ResourcesResourceTypeUpdateInput,
17
18
  } from '../data/validators'
18
19
  import { resourcesResourceTypeCrudEvents } from '../lib/crud'
20
+ import { lockResourceType } from '../lib/resourceTypeLock'
19
21
  import { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'
20
22
  import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'
21
23
  import { E } from '#generated/entities.ids.generated'
@@ -72,6 +74,25 @@ async function loadResourceTypeCustomSnapshot(em: EntityManager, snapshot: Resou
72
74
  })
73
75
  }
74
76
 
77
+ async function assertNoAssignedResources(
78
+ em: EntityManager,
79
+ resourceType: ResourcesResourceType,
80
+ status: 400 | 409,
81
+ ): Promise<void> {
82
+ const assignedCount = await em.count(ResourcesResource, {
83
+ resourceTypeId: resourceType.id,
84
+ deletedAt: null,
85
+ tenantId: resourceType.tenantId,
86
+ organizationId: resourceType.organizationId,
87
+ })
88
+ if (assignedCount === 0) return
89
+ const { translate } = await resolveTranslations()
90
+ throw new CrudHttpError(status, {
91
+ error: translate('resources.resourceTypes.errors.deleteAssigned', 'Resource type has assigned resources.'),
92
+ code: 'resources.resourceTypes.errors.deleteAssigned',
93
+ })
94
+ }
95
+
75
96
  const createResourceTypeCommand: CommandHandler<ResourcesResourceTypeCreateInput, { resourceTypeId: string }> = {
76
97
  id: 'resources.resourceTypes.create',
77
98
  async execute(rawInput, ctx) {
@@ -152,9 +173,24 @@ const createResourceTypeCommand: CommandHandler<ResourcesResourceTypeCreateInput
152
173
  const em = (ctx.container.resolve('em') as EntityManager).fork()
153
174
  const resourceType = await em.findOne(ResourcesResourceType, { id: after.id })
154
175
  if (resourceType) {
155
- resourceType.deletedAt = new Date()
156
- resourceType.updatedAt = new Date()
157
- await em.flush()
176
+ let deleted = false
177
+ await withAtomicFlush(em, [
178
+ async () => {
179
+ await lockResourceType(em, resourceType.id)
180
+ const isLive = await em.count(ResourcesResourceType, {
181
+ id: resourceType.id,
182
+ tenantId: resourceType.tenantId,
183
+ organizationId: resourceType.organizationId,
184
+ deletedAt: null,
185
+ }) > 0
186
+ if (!isLive) return
187
+ await assertNoAssignedResources(em, resourceType, 409)
188
+ resourceType.deletedAt = new Date()
189
+ resourceType.updatedAt = new Date()
190
+ deleted = true
191
+ },
192
+ ], { transaction: true })
193
+ if (!deleted) return
158
194
 
159
195
  const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)
160
196
  await emitCrudUndoSideEffects({
@@ -361,18 +397,14 @@ const deleteResourceTypeCommand: CommandHandler<{ id?: string }, { resourceTypeI
361
397
  if (!record) throw new CrudHttpError(404, { error: 'Resources resource type not found.' })
362
398
  ensureTenantScope(ctx, record.tenantId)
363
399
  ensureOrganizationScope(ctx, record.organizationId)
364
- const assignedCount = await em.count(ResourcesResource, {
365
- resourceTypeId: record.id,
366
- deletedAt: null,
367
- tenantId: record.tenantId,
368
- organizationId: record.organizationId,
369
- })
370
- if (assignedCount > 0) {
371
- throw new CrudHttpError(400, { error: 'Resource type has assigned resources.' })
372
- }
373
- record.deletedAt = new Date()
374
- record.updatedAt = new Date()
375
- await em.flush()
400
+ await withAtomicFlush(em, [
401
+ async () => {
402
+ await lockResourceType(em, record.id)
403
+ await assertNoAssignedResources(em, record, 400)
404
+ record.deletedAt = new Date()
405
+ record.updatedAt = new Date()
406
+ },
407
+ ], { transaction: true })
376
408
 
377
409
  const dataEngine = (ctx.container.resolve('dataEngine') as DataEngine)
378
410
  await emitCrudSideEffects({
@@ -11,7 +11,7 @@ import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
11
11
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
12
12
  import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
13
13
  import { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'
14
- import { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment } from '../data/entities'
14
+ import { ResourcesResource, ResourcesResourceTag, ResourcesResourceTagAssignment, ResourcesResourceType } from '../data/entities'
15
15
  import {
16
16
  resourcesResourceCreateSchema,
17
17
  resourcesResourceUpdateSchema,
@@ -19,6 +19,7 @@ import {
19
19
  type ResourcesResourceUpdateInput,
20
20
  } from '../data/validators'
21
21
  import { resourcesResourceCrudEvents } from '../lib/crud'
22
+ import { lockResourceType } from '../lib/resourceTypeLock'
22
23
  import { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'
23
24
  import { RESOURCES_CAPACITY_UNIT_DICTIONARY_KEY } from '../lib/capacityUnits'
24
25
  import { E } from '#generated/entities.ids.generated'
@@ -209,6 +210,70 @@ async function syncResourcesResourceTags(em: EntityManager, params: {
209
210
  }
210
211
  }
211
212
 
213
+ type ResourceScope = { tenantId: string; organizationId: string }
214
+
215
+ async function countResourceTypes(
216
+ em: EntityManager,
217
+ resourceTypeId: string,
218
+ scope: ResourceScope,
219
+ state: 'live' | 'deleted',
220
+ ): Promise<number> {
221
+ return em.count(ResourcesResourceType, {
222
+ id: resourceTypeId,
223
+ tenantId: scope.tenantId,
224
+ organizationId: scope.organizationId,
225
+ deletedAt: state === 'live' ? null : { $ne: null },
226
+ })
227
+ }
228
+
229
+ async function assertResourceTypeAssignable(
230
+ em: EntityManager,
231
+ resourceTypeId: string | null | undefined,
232
+ scope: ResourceScope,
233
+ ): Promise<void> {
234
+ if (!resourceTypeId) return
235
+ await lockResourceType(em, resourceTypeId)
236
+ if (await countResourceTypes(em, resourceTypeId, scope, 'live') > 0) return
237
+ throw await resourceTypeUnavailableError(400)
238
+ }
239
+
240
+ async function resourceTypeUnavailableError(status: 400 | 409): Promise<CrudHttpError> {
241
+ const { translate } = await resolveTranslations()
242
+ return new CrudHttpError(status, {
243
+ error: translate('resources.resources.errors.resourceTypeUnavailable', 'The selected resource type is no longer available.'),
244
+ code: 'resources.resources.errors.resourceTypeUnavailable',
245
+ })
246
+ }
247
+
248
+ async function assertResourceTypeRestorable(
249
+ em: EntityManager,
250
+ record: ResourcesResource | null,
251
+ resourceTypeId: string | null | undefined,
252
+ scope: ResourceScope,
253
+ ): Promise<void> {
254
+ if (!resourceTypeId) return
255
+ await lockResourceType(em, resourceTypeId)
256
+ if (record && await em.count(ResourcesResource, {
257
+ id: record.id,
258
+ tenantId: scope.tenantId,
259
+ organizationId: scope.organizationId,
260
+ resourceTypeId,
261
+ deletedAt: null,
262
+ }) > 0) return
263
+ if (await countResourceTypes(em, resourceTypeId, scope, 'live') > 0) return
264
+ if (await countResourceTypes(em, resourceTypeId, scope, 'deleted') === 0) {
265
+ throw await resourceTypeUnavailableError(409)
266
+ }
267
+ const { translate } = await resolveTranslations()
268
+ throw new CrudHttpError(409, {
269
+ error: translate(
270
+ 'resources.resources.errors.resourceTypeDeleted',
271
+ 'This would put the resource on a deleted resource type. Restore the resource type first.',
272
+ ),
273
+ code: 'resources.resources.errors.resourceTypeDeleted',
274
+ })
275
+ }
276
+
212
277
  const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { resourceId: string }> = {
213
278
  id: 'resources.resources.create',
214
279
  async execute(rawInput, ctx) {
@@ -244,6 +309,7 @@ const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { reso
244
309
  })
245
310
  await withAtomicFlush(em, [
246
311
  async () => {
312
+ await assertResourceTypeAssignable(em, parsed.resourceTypeId, parsed)
247
313
  em.persist(record)
248
314
  await em.flush()
249
315
  },
@@ -345,6 +411,7 @@ const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { reso
345
411
  let record = await em.findOne(ResourcesResource, { id: after.id })
346
412
  await withAtomicFlush(em, [
347
413
  async () => {
414
+ await assertResourceTypeRestorable(em, record, after.resourceTypeId, after)
348
415
  if (!record) {
349
416
  record = em.create(ResourcesResource, {
350
417
  id: after.id,
@@ -450,6 +517,7 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
450
517
  if (!record) throw new CrudHttpError(404, { error: 'Resources resource not found.' })
451
518
  ensureTenantScope(ctx, record.tenantId)
452
519
  ensureOrganizationScope(ctx, record.organizationId)
520
+ const resourceTypeChanged = parsed.resourceTypeId !== undefined && parsed.resourceTypeId !== record.resourceTypeId
453
521
 
454
522
  let capacityUnit: CapacityUnitSnapshot | null | undefined
455
523
  if (parsed.capacityUnitValue !== undefined) {
@@ -464,7 +532,8 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
464
532
  : null
465
533
  }
466
534
  await withAtomicFlush(em, [
467
- () => {
535
+ async () => {
536
+ if (resourceTypeChanged) await assertResourceTypeAssignable(em, parsed.resourceTypeId, record)
468
537
  if (parsed.capacityUnitValue !== undefined) {
469
538
  record.capacityUnitValue = capacityUnit?.value ?? null
470
539
  record.capacityUnitName = capacityUnit?.name ?? null
@@ -579,7 +648,8 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
579
648
  const record = await em.findOne(ResourcesResource, { id: before.id })
580
649
  if (!record) return
581
650
  await withAtomicFlush(em, [
582
- () => {
651
+ async () => {
652
+ await assertResourceTypeRestorable(em, record, before.resourceTypeId, before)
583
653
  record.name = before.name
584
654
  record.description = before.description ?? null
585
655
  record.resourceTypeId = before.resourceTypeId ?? null
@@ -710,6 +780,7 @@ const deleteResourceCommand: CommandHandler<{ id?: string }, { resourceId: strin
710
780
  let record = await em.findOne(ResourcesResource, { id: before.id })
711
781
  await withAtomicFlush(em, [
712
782
  async () => {
783
+ await assertResourceTypeRestorable(em, record, before.resourceTypeId, before)
713
784
  if (!record) {
714
785
  record = em.create(ResourcesResource, {
715
786
  id: before.id,
@@ -259,6 +259,7 @@
259
259
  "resources.resources.availability.scope.label": "Anwenden auf",
260
260
  "resources.resources.availability.scope.weekday": "Wochentag:",
261
261
  "resources.resources.availability.scope.weekdayShort": "Alle {{weekday}}",
262
+ "resources.resources.availability.section.title": "Verfügbarkeit",
262
263
  "resources.resources.availability.timezone": "Zeitzone",
263
264
  "resources.resources.availability.timezone.placeholder": "Zeitzonen durchsuchen...",
264
265
  "resources.resources.availability.timezone.saveError": "Zeitzone konnte nicht gespeichert werden.",
@@ -443,6 +444,8 @@
443
444
  "resources.resources.detail.tabs.activities": "Aktivitäten",
444
445
  "resources.resources.detail.tabs.notes": "Notizen",
445
446
  "resources.resources.detail.untitled": "Unbenannte Ressource",
447
+ "resources.resources.errors.resourceTypeDeleted": "Dadurch würde die Ressource einem gelöschten Ressourcentyp zugeordnet. Stellen Sie zuerst den Ressourcentyp wieder her.",
448
+ "resources.resources.errors.resourceTypeUnavailable": "Der ausgewählte Ressourcentyp ist nicht mehr verfügbar.",
446
449
  "resources.resources.flash.createdAvailability": "Gespeichert. Du kannst jetzt die Verfügbarkeit festlegen.",
447
450
  "resources.resources.form.actions.create": "Erstellen",
448
451
  "resources.resources.form.appearance.colorClear": "Farbe entfernen",
@@ -259,6 +259,7 @@
259
259
  "resources.resources.availability.scope.label": "Apply to",
260
260
  "resources.resources.availability.scope.weekday": "Weekday:",
261
261
  "resources.resources.availability.scope.weekdayShort": "All {{weekday}}s",
262
+ "resources.resources.availability.section.title": "Availability",
262
263
  "resources.resources.availability.timezone": "Timezone",
263
264
  "resources.resources.availability.timezone.placeholder": "Search timezones...",
264
265
  "resources.resources.availability.timezone.saveError": "Failed to save timezone.",
@@ -443,6 +444,8 @@
443
444
  "resources.resources.detail.tabs.activities": "Activities",
444
445
  "resources.resources.detail.tabs.notes": "Notes",
445
446
  "resources.resources.detail.untitled": "Unnamed resource",
447
+ "resources.resources.errors.resourceTypeDeleted": "This would put the resource on a deleted resource type. Restore the resource type first.",
448
+ "resources.resources.errors.resourceTypeUnavailable": "The selected resource type is no longer available.",
446
449
  "resources.resources.flash.createdAvailability": "Saved. You can now set availability.",
447
450
  "resources.resources.form.actions.create": "Create",
448
451
  "resources.resources.form.appearance.colorClear": "Clear color",
@@ -259,6 +259,7 @@
259
259
  "resources.resources.availability.scope.label": "Aplicar a",
260
260
  "resources.resources.availability.scope.weekday": "Día de la semana:",
261
261
  "resources.resources.availability.scope.weekdayShort": "Todos los {{weekday}}",
262
+ "resources.resources.availability.section.title": "Disponibilidad",
262
263
  "resources.resources.availability.timezone": "Zona horaria",
263
264
  "resources.resources.availability.timezone.placeholder": "Buscar zonas horarias...",
264
265
  "resources.resources.availability.timezone.saveError": "No se pudo guardar la zona horaria.",
@@ -443,6 +444,8 @@
443
444
  "resources.resources.detail.tabs.activities": "Actividades",
444
445
  "resources.resources.detail.tabs.notes": "Notas",
445
446
  "resources.resources.detail.untitled": "Recurso sin nombre",
447
+ "resources.resources.errors.resourceTypeDeleted": "Esto asignaría el recurso a un tipo de recurso eliminado. Restaura primero el tipo de recurso.",
448
+ "resources.resources.errors.resourceTypeUnavailable": "El tipo de recurso seleccionado ya no está disponible.",
446
449
  "resources.resources.flash.createdAvailability": "Guardado. Ahora puedes establecer la disponibilidad.",
447
450
  "resources.resources.form.actions.create": "Crear",
448
451
  "resources.resources.form.appearance.colorClear": "Limpiar color",
@@ -259,6 +259,7 @@
259
259
  "resources.resources.availability.scope.label": "적용 대상",
260
260
  "resources.resources.availability.scope.weekday": "요일:",
261
261
  "resources.resources.availability.scope.weekdayShort": "매주 {{weekday}}",
262
+ "resources.resources.availability.section.title": "가용성",
262
263
  "resources.resources.availability.timezone": "시간대",
263
264
  "resources.resources.availability.timezone.placeholder": "시간대 검색...",
264
265
  "resources.resources.availability.timezone.saveError": "시간대를 저장하지 못했습니다.",
@@ -443,6 +444,8 @@
443
444
  "resources.resources.detail.tabs.activities": "활동",
444
445
  "resources.resources.detail.tabs.notes": "메모",
445
446
  "resources.resources.detail.untitled": "이름 없는 리소스",
447
+ "resources.resources.errors.resourceTypeDeleted": "이 작업은 리소스를 삭제된 리소스 유형에 연결합니다. 먼저 리소스 유형을 복원하세요.",
448
+ "resources.resources.errors.resourceTypeUnavailable": "선택한 리소스 유형을 더 이상 사용할 수 없습니다.",
446
449
  "resources.resources.flash.createdAvailability": "저장되었습니다. 이제 가용성을 설정할 수 있습니다.",
447
450
  "resources.resources.form.actions.create": "생성",
448
451
  "resources.resources.form.appearance.colorClear": "색상 지우기",
@@ -259,6 +259,7 @@
259
259
  "resources.resources.availability.scope.label": "Zastosuj do",
260
260
  "resources.resources.availability.scope.weekday": "Dzień tygodnia:",
261
261
  "resources.resources.availability.scope.weekdayShort": "Wszystkie {{weekday}}",
262
+ "resources.resources.availability.section.title": "Dostępność",
262
263
  "resources.resources.availability.timezone": "Strefa czasowa",
263
264
  "resources.resources.availability.timezone.placeholder": "Szukaj stref czasowych...",
264
265
  "resources.resources.availability.timezone.saveError": "Nie udało się zapisać strefy czasowej.",
@@ -443,6 +444,8 @@
443
444
  "resources.resources.detail.tabs.activities": "Działania",
444
445
  "resources.resources.detail.tabs.notes": "Notatki",
445
446
  "resources.resources.detail.untitled": "Nienazwany zasób",
447
+ "resources.resources.errors.resourceTypeDeleted": "Spowodowałoby to przypisanie zasobu do usuniętego typu zasobu. Najpierw przywróć typ zasobu.",
448
+ "resources.resources.errors.resourceTypeUnavailable": "Wybrany typ zasobu nie jest już dostępny.",
446
449
  "resources.resources.flash.createdAvailability": "Zapisano. Możesz teraz ustawić dostępność.",
447
450
  "resources.resources.form.actions.create": "Utwórz",
448
451
  "resources.resources.form.appearance.colorClear": "Wyczyść kolor",
@@ -0,0 +1,14 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+
3
+ const RESOURCE_TYPE_LOCK_SQL = 'select pg_advisory_xact_lock(hashtextextended(?, 0))'
4
+
5
+ export function resourceTypeLockKey(resourceTypeId: string): string {
6
+ return `resources:resource-type:${resourceTypeId.toLowerCase()}`
7
+ }
8
+
9
+ export async function lockResourceType(em: EntityManager, resourceTypeId: string): Promise<void> {
10
+ if (!em.isInTransaction()) {
11
+ throw new Error('[internal] lockResourceType requires an open transaction')
12
+ }
13
+ await em.execute(RESOURCE_TYPE_LOCK_SQL, [resourceTypeLockKey(resourceTypeId)])
14
+ }