@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/integrations/lib/state-service.js +1 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/resources/commands/resource-types.js +42 -15
- package/dist/modules/resources/commands/resource-types.js.map +2 -2
- package/dist/modules/resources/commands/resources.js +54 -3
- package/dist/modules/resources/commands/resources.js.map +2 -2
- package/dist/modules/resources/lib/resourceTypeLock.js +15 -0
- package/dist/modules/resources/lib/resourceTypeLock.js.map +7 -0
- package/package.json +7 -7
- package/src/modules/integrations/lib/state-service.ts +1 -1
- package/src/modules/resources/commands/resource-types.ts +47 -15
- package/src/modules/resources/commands/resources.ts +74 -3
- package/src/modules/resources/i18n/de.json +3 -0
- package/src/modules/resources/i18n/en.json +3 -0
- package/src/modules/resources/i18n/es.json +3 -0
- package/src/modules/resources/i18n/ko.json +3 -0
- package/src/modules/resources/i18n/pl.json +3 -0
- package/src/modules/resources/lib/resourceTypeLock.ts +14 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -66,7 +66,7 @@ function createIntegrationStateService(em) {
|
|
|
66
66
|
}
|
|
67
67
|
const created = em.create(IntegrationState, {
|
|
68
68
|
integrationId,
|
|
69
|
-
isEnabled: input.isEnabled ??
|
|
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 ??
|
|
5
|
-
"mappings": "AACA,SAAS,6BAA6B;AACtC,SAAS,sBAA6C;AACtD,SAAS,wBAAwB;AAa1B,SAAS,8BAA8B,IAAmB;AAC/D,WAAS,0BAA0B,eAAiD;AAClF,UAAM,aAAa,eAAe,aAAa;AAE/C,WAAO;AAAA,MACL,WAAW,YAAY,cAAc,aAAa;AAAA,MAClD,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,MACrB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,eAAuB,OAA2D;AAC1F,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,aAAa,eAAuB,OAA4D;AACpG,YAAM,QAAQ,MAAM,KAAK,IAAI,eAAe,KAAK;AACjD,YAAM,WAAW,0BAA0B,aAAa;AAExD,aAAO;AAAA,QACL,WAAW,OAAO,aAAa,SAAS;AAAA,QACxC,YAAY,OAAO,cAAc;AAAA,QACjC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,qBAAqB,OAAO,uBAAuB;AAAA,QACnD,qBAAqB,OAAO,uBAAuB;AAAA,QACnD,WAAW,OAAO,aAAa;AAAA,QAC/B,WAAW,OAAO,aAAa;AAAA,MACjC;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,eAAuB,OAA2C;AAChF,YAAM,QAAQ,MAAM,KAAK,aAAa,eAAe,KAAK;AAC1D,aAAO,MAAM;AAAA,IACf;AAAA,IAEA,MAAM,OACJ,eACA,OAYA,OAC2B;AAC3B,YAAM,UAAU,MAAM,KAAK,IAAI,eAAe,KAAK;AACnD,YAAM,iBAAiB,MAAM,KAAK,aAAa,eAAe,KAAK;AACnE,YAAM,mBACJ,MAAM,cAAc,QAAQ,eAAe,cAAc;AAE3D,UAAI,SAAS;AACX,YAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAC7D,YAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,YAAI,MAAM,mBAAmB,OAAW,SAAQ,iBAAiB,MAAM;AACvE,YAAI,MAAM,qBAAqB,OAAW,SAAQ,mBAAmB,MAAM;AAC3E,YAAI,MAAM,wBAAwB,OAAW,SAAQ,sBAAsB,MAAM;AACjF,YAAI,MAAM,wBAAwB,OAAW,SAAQ,sBAAsB,MAAM;AACjF,YAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAAA,iBACpD,iBAAkB,SAAQ,YAAY,oBAAI,KAAK;AACxD,cAAM,GAAG,MAAM;AACf,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,GAAG,OAAO,kBAAkB;AAAA,QAC1C;AAAA,QACA,WAAW,MAAM,aAAa;AAAA,
|
|
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": "AACA,SAAS,6BAA6B;AACtC,SAAS,sBAA6C;AACtD,SAAS,wBAAwB;AAa1B,SAAS,8BAA8B,IAAmB;AAC/D,WAAS,0BAA0B,eAAiD;AAClF,UAAM,aAAa,eAAe,aAAa;AAE/C,WAAO;AAAA,MACL,WAAW,YAAY,cAAc,aAAa;AAAA,MAClD,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,MACrB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,IAAI,eAAuB,OAA2D;AAC1F,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,UACE;AAAA,UACA,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,aAAa,eAAuB,OAA4D;AACpG,YAAM,QAAQ,MAAM,KAAK,IAAI,eAAe,KAAK;AACjD,YAAM,WAAW,0BAA0B,aAAa;AAExD,aAAO;AAAA,QACL,WAAW,OAAO,aAAa,SAAS;AAAA,QACxC,YAAY,OAAO,cAAc;AAAA,QACjC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,qBAAqB,OAAO,uBAAuB;AAAA,QACnD,qBAAqB,OAAO,uBAAuB;AAAA,QACnD,WAAW,OAAO,aAAa;AAAA,QAC/B,WAAW,OAAO,aAAa;AAAA,MACjC;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,eAAuB,OAA2C;AAChF,YAAM,QAAQ,MAAM,KAAK,aAAa,eAAe,KAAK;AAC1D,aAAO,MAAM;AAAA,IACf;AAAA,IAEA,MAAM,OACJ,eACA,OAYA,OAC2B;AAC3B,YAAM,UAAU,MAAM,KAAK,IAAI,eAAe,KAAK;AACnD,YAAM,iBAAiB,MAAM,KAAK,aAAa,eAAe,KAAK;AACnE,YAAM,mBACJ,MAAM,cAAc,QAAQ,eAAe,cAAc;AAE3D,UAAI,SAAS;AACX,YAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAC7D,YAAI,MAAM,eAAe,OAAW,SAAQ,aAAa,MAAM;AAC/D,YAAI,MAAM,mBAAmB,OAAW,SAAQ,iBAAiB,MAAM;AACvE,YAAI,MAAM,qBAAqB,OAAW,SAAQ,mBAAmB,MAAM;AAC3E,YAAI,MAAM,wBAAwB,OAAW,SAAQ,sBAAsB,MAAM;AACjF,YAAI,MAAM,wBAAwB,OAAW,SAAQ,sBAAsB,MAAM;AACjF,YAAI,MAAM,cAAc,OAAW,SAAQ,YAAY,MAAM;AAAA,iBACpD,iBAAkB,SAAQ,YAAY,oBAAI,KAAK;AACxD,cAAM,GAAG,MAAM;AACf,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,GAAG,OAAO,kBAAkB;AAAA,QAC1C;AAAA,QACA,WAAW,MAAM,aAAa,eAAe;AAAA,QAC7C,YAAY,MAAM;AAAA,QAClB,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,kBAAkB,MAAM;AAAA,QACxB,qBAAqB,MAAM;AAAA,QAC3B,qBAAqB,MAAM;AAAA,QAC3B,WAAW,MAAM,cAAc,SAAY,MAAM,YAAY,mBAAmB,oBAAI,KAAK,IAAI;AAAA,QAC7F,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,MAClB,CAAC;AACD,YAAM,GAAG,QAAQ,OAAO,EAAE,MAAM;AAChC,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,kBAAkB,eAAuB,OAAsD;AACnG,YAAM,QAAQ,MAAM,KAAK,IAAI,eAAe,KAAK;AACjD,aAAO,OAAO,cAAc;AAAA,IAC9B;AAAA,IAEA,MAAM,kBAAkB,eAAuB,UAAmB,OAAoD;AACpH,aAAO,KAAK,OAAO,eAAe,EAAE,gBAAgB,SAAS,GAAG,KAAK;AAAA,IACvE;AAAA,EACF;AACF;",
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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": "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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 { 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": "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",
|
|
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": "AACA,SAAS,uBAAuB;AAEhC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,cAAc,qBAAqB,yBAAyB,uBAAuB,4BAA4B;AACxH,SAAS,uBAAuB;AAChC,SAAS,0BAA0B,wBAAwB,+BAAyD;AACpH,SAAS,2BAA2B;AAEpC,SAAS,2BAA2B;AAEpC,SAAS,YAAY,uBAAuB;AAC5C,SAAS,mBAAmB,sBAAsB,sCAAsC;AACxF;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,mCAAmC;AAC5C,SAAS,yBAAyB,mBAAmB,0BAA0B;AAC/E,SAAS,8CAA8C;AACvD,SAAS,SAAS;AAElB,MAAM,sBAA4D;AAAA,EAChE,YAAY,EAAE,UAAU;AAC1B;AAsCA,eAAe,oBACb,IACA,OACA,UAC+B;AAC/B,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,6BAA6B,CAAC;AAAA,EACtE;AACA,QAAM,aAAa,MAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,MACE,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,KAAK;AAAA,MACL,WAAW;AAAA,MACX,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,8CAA8C,CAAC;AAAA,EACvF;AACA,QAAM,kBAAkB,QAAQ,YAAY;AAC5C,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,IACA,EAAE,UAAU,CAAC,YAAY,EAAE;AAAA,IAC3B;AAAA,EACF;AACA,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAAA,EACpE;AACA,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,MAAM,MAAM,OAAO,KAAK,EAAE,SAAS,MAAM,QAAQ,MAAM;AAAA,IACvD,OAAO,MAAM,SAAS;AAAA,IACtB,MAAM,MAAM,QAAQ;AAAA,EACtB;AACF;AAEA,SAAS,gBAAgB,MAAmD;AAC1E,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAClC,QAAM,MAAM,oBAAI,IAAY;AAC5B,OAAK,QAAQ,CAAC,OAAO;AACnB,QAAI,OAAO,OAAO,YAAY,GAAG,KAAK,EAAE,SAAS,EAAG,KAAI,IAAI,GAAG,KAAK,CAAC;AAAA,EACvE,CAAC;AACD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,eAAe,qBAAqB,IAAmB,IAA8C;AACnG,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA,EAAE,GAAG;AAAA,IACL;AAAA,IACA,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,EACzC;AACA,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,cAAc,MAAM,GAAG;AAAA,IAC3B;AAAA,IACA,EAAE,UAAU,SAAS,GAAG;AAAA,IACxB,EAAE,UAAU,CAAC,KAAK,EAAE;AAAA,EACtB;AACA,QAAM,OAAO,YACV,IAAI,CAAC,eAAgB,WAAW,KAA0C,MAAM,IAAI,EACpF,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,EAChF,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACpC,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,IACzB,MAAM,SAAS;AAAA,IACf,aAAa,SAAS,eAAe;AAAA,IACrC,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,UAAU,SAAS,YAAY;AAAA,IAC/B,mBAAmB,SAAS,qBAAqB;AAAA,IACjD,kBAAkB,SAAS,oBAAoB;AAAA,IAC/C,mBAAmB,SAAS,qBAAqB;AAAA,IACjD,kBAAkB,SAAS,oBAAoB;AAAA,IAC/C,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,iBAAiB,SAAS,mBAAmB;AAAA,IAC7C,UAAU,SAAS;AAAA,IACnB,uBAAuB,SAAS,yBAAyB;AAAA,IACzD,oBAAoB,SAAS,sBAAsB;AAAA,IACnD;AAAA,IACA,WAAW,SAAS,YAAY,SAAS,UAAU,YAAY,IAAI;AAAA,EACrE;AACF;AAEA,eAAe,2BAA2B,IAAmB,UAA0D;AACrH,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,0BAA0B,IAAmB,QAMzD;AACD,MAAI,OAAO,WAAW,OAAW;AACjC,QAAM,SAAS,gBAAgB,OAAO,UAAU,CAAC,CAAC;AAClD,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,GAAG,aAAa,gCAAgC,EAAE,UAAU,OAAO,WAAW,CAAC;AACrF;AAAA,EACF;AACA,QAAM,cAAc,MAAM,GAAG,KAAK,sBAAsB;AAAA,IACtD,IAAI,EAAE,KAAK,OAAO;AAAA,IAClB,gBAAgB,OAAO;AAAA,IACvB,UAAU,OAAO;AAAA,EACnB,CAAC;AACD,MAAI,CAAC,OAAO,iBAAiB,YAAY,WAAW,OAAO,QAAQ;AACjE,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,4CAA4C,CAAC;AAAA,EACrF;AACA,QAAM,OAAO,IAAI,IAAI,YAAY,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AAC5D,QAAM,GAAG,aAAa,gCAAgC,EAAE,UAAU,OAAO,WAAW,CAAC;AACrF,QAAM,WAAW,GAAG,aAAa,mBAAmB,OAAO,UAAU;AACrE,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,QAAI,CAAC,IAAK;AACV,UAAM,aAAa,GAAG,OAAO,gCAAgC;AAAA,MAC3D,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AACD,OAAG,QAAQ,UAAU;AAAA,EACvB;AACF;AAEA,MAAM,wBAA8F;AAAA,EAClG,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,+BAA+B,QAAQ;AACxF,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,YACJ,OAAO,OAAO,sBAAsB,WAAW,OAAO,kBAAkB,KAAK,IAAI;AACnF,UAAM,eAAe,YACjB,MAAM,oBAAoB,IAAI,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe,GAAG,SAAS,IAC7G;AACJ,UAAM,SAAS,GAAG,OAAO,mBAAmB;AAAA,MAC1C,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO,eAAe;AAAA,MACnC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,UAAU,OAAO,YAAY;AAAA,MAC7B,mBAAmB,cAAc,SAAS;AAAA,MAC1C,kBAAkB,cAAc,QAAQ;AAAA,MACxC,mBAAmB,cAAc,SAAS;AAAA,MAC1C,kBAAkB,cAAc,QAAQ;AAAA,MACxC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,UAAU,OAAO,YAAY;AAAA,MAC7B,uBAAuB,OAAO,yBAAyB;AAAA,MACvD,oBAAoB,OAAO,sBAAsB;AAAA,MACjD,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,WAAG,QAAQ,MAAM;AACjB,cAAM,GAAG,MAAM;AAAA,MACjB;AAAA,MACA,MAAM,0BAA0B,IAAI;AAAA,QAClC,YAAY,OAAO;AAAA,QACnB,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH,GAAG,EAAE,aAAa,KAAK,CAAC;AACxB,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;AACD,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,YAAY,OAAO,GAAG;AAAA,EACjC;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,WAAW,MAAM,qBAAqB,IAAI,OAAO,UAAU;AACjE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,2BAA2B,IAAI,QAAQ;AAC5D,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,qBAAqB,IAAI,QAAQ,cAAc,EAAE;AACxE,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,SAAS,MAAM,2BAA2B,IAAI,QAAQ;AAC5D,UAAM,qBAAuC,SACzC,EAAE,GAAG,UAAU,cAAc,OAAO,IACpC;AACJ,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,oCAAoC,iBAAiB;AAAA,MAC5E,cAAc;AAAA,MACd,YAAY,mBAAmB;AAAA,MAC/B,UAAU,mBAAmB;AAAA,MAC7B,gBAAgB,mBAAmB;AAAA,MACnC,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,mBAAwC,QAAQ;AAChE,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,mBAAmB,EAAE,IAAI,MAAM,GAAG,CAAC;AACnE,QAAI,QAAQ;AACV,aAAO,YAAY,oBAAI,KAAK;AAC5B,aAAO,YAAY,oBAAI,KAAK;AAC5B,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,OAAO;AAAA,UACX,gBAAgB,OAAO;AAAA,UACvB,UAAU,OAAO;AAAA,QACnB;AAAA,QACA,QAAQ;AAAA,QACV,SAAS;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,QAAQ,oBAAsC,QAAQ;AAC5D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2DAA2D,CAAC;AAAA,IACpG;AACA,UAAM,sBAAuB,MAA2B,gBAAgB;AACxE,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,SAAS,MAAM,GAAG,QAAQ,mBAAmB,EAAE,IAAI,MAAM,GAAG,CAAC;AACjE,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,YAAI,CAAC,QAAQ;AACX,mBAAS,GAAG,OAAO,mBAAmB;AAAA,YACpC,IAAI,MAAM;AAAA,YACV,UAAU,MAAM;AAAA,YAChB,gBAAgB,MAAM;AAAA,YACtB,MAAM,MAAM;AAAA,YACZ,aAAa,MAAM,eAAe;AAAA,YAClC,gBAAgB,MAAM,kBAAkB;AAAA,YACxC,UAAU,MAAM,YAAY;AAAA,YAC5B,mBAAmB,MAAM,qBAAqB;AAAA,YAC9C,kBAAkB,MAAM,oBAAoB;AAAA,YAC5C,mBAAmB,MAAM,qBAAqB;AAAA,YAC9C,kBAAkB,MAAM,oBAAoB;AAAA,YAC5C,gBAAgB,MAAM,kBAAkB;AAAA,YACxC,iBAAiB,MAAM,mBAAmB;AAAA,YAC1C,UAAU,MAAM;AAAA,YAChB,uBAAuB,MAAM,yBAAyB;AAAA,YACtD,oBAAoB,MAAM,sBAAsB;AAAA,YAChD,WAAW;AAAA,YACX,WAAW,oBAAI,KAAK;AAAA,YACpB,WAAW,oBAAI,KAAK;AAAA,UACtB,CAAC;AACD,aAAG,QAAQ,MAAM;AAAA,QACnB,OAAO;AACL,iBAAO,OAAO,MAAM;AACpB,iBAAO,cAAc,MAAM,eAAe;AAC1C,iBAAO,iBAAiB,MAAM,kBAAkB;AAChD,iBAAO,WAAW,MAAM,YAAY;AACpC,iBAAO,oBAAoB,MAAM,qBAAqB;AACtD,iBAAO,mBAAmB,MAAM,oBAAoB;AACpD,iBAAO,oBAAoB,MAAM,qBAAqB;AACtD,iBAAO,mBAAmB,MAAM,oBAAoB;AACpD,iBAAO,iBAAiB,MAAM,kBAAkB;AAChD,iBAAO,kBAAkB,MAAM,mBAAmB;AAClD,iBAAO,WAAW,MAAM;AACxB,iBAAO,wBAAwB,MAAM,yBAAyB;AAC9D,iBAAO,qBAAqB,MAAM,sBAAsB;AACxD,iBAAO,YAAY;AACnB,iBAAO,YAAY,oBAAI,KAAK;AAAA,QAC9B;AACA,cAAM,GAAG,MAAM;AAAA,MACjB;AAAA,MACA,MAAM,0BAA0B,IAAI;AAAA,QAClC,YAAa,OAA6B;AAAA,QAC1C,gBAAiB,OAA6B;AAAA,QAC9C,UAAW,OAA6B;AAAA,QACxC,QAAQ,MAAM;AAAA,QACd,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,GAAG,EAAE,aAAa,KAAK,CAAC;AACxB,UAAM,iBAAiB;AAEvB,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,cAAc,SAAS,eAAe,uBAAuB;AACnE,QAAI,aAAa;AACf,YAAM,QAAQ,yBAAyB,aAAa,MAAS;AAC7D,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,eAAe;AAAA,QACzB,UAAU,eAAe;AAAA,QACzB,gBAAgB,eAAe;AAAA,QAC/B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,eAAe;AAAA,QACnB,gBAAgB,eAAe;AAAA,QAC/B,UAAU,eAAe;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO,EAAE,YAAY,eAAe,GAAG;AAAA,EACzC;AACF;AAEA,MAAM,wBAA8F;AAAA,EAClG,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,OAAO,IAAI,sBAAsB,+BAA+B,QAAQ;AAChF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,qBAAqB,IAAI,OAAO,EAAE;AACzD,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,2BAA2B,IAAI,QAAQ;AAC5D,WAAO,EAAE,QAAQ,UAAU,cAAc,OAAO;AAAA,EAClD;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,+BAA+B,QAAQ;AACxF,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,gCAAgC,CAAC;AACpF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAElD,QAAI;AACJ,QAAI,OAAO,sBAAsB,QAAW;AAC1C,YAAM,YACJ,OAAO,OAAO,sBAAsB,WAAW,OAAO,kBAAkB,KAAK,IAAI;AACnF,qBAAe,YACX,MAAM;AAAA,QACJ;AAAA,QACA,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,QACnE;AAAA,MACF,IACA;AAAA,IACN;AACA,UAAM,gBAAgB,IAAI;AAAA,MACxB,MAAM;AACJ,YAAI,OAAO,sBAAsB,QAAW;AAC1C,iBAAO,oBAAoB,cAAc,SAAS;AAClD,iBAAO,mBAAmB,cAAc,QAAQ;AAChD,iBAAO,oBAAoB,cAAc,SAAS;AAClD,iBAAO,mBAAmB,cAAc,QAAQ;AAAA,QAClD;AACA,YAAI,OAAO,SAAS,OAAW,QAAO,OAAO,OAAO;AACpD,YAAI,OAAO,gBAAgB,OAAW,QAAO,cAAc,OAAO,eAAe;AACjF,YAAI,OAAO,mBAAmB,OAAW,QAAO,iBAAiB,OAAO,kBAAkB;AAC1F,YAAI,OAAO,aAAa,OAAW,QAAO,WAAW,OAAO,YAAY;AACxE,YAAI,OAAO,mBAAmB,OAAW,QAAO,iBAAiB,OAAO,kBAAkB;AAC1F,YAAI,OAAO,oBAAoB,OAAW,QAAO,kBAAkB,OAAO,mBAAmB;AAC7F,YAAI,OAAO,0BAA0B,OAAW,QAAO,wBAAwB,OAAO,yBAAyB;AAC/G,YAAI,OAAO,uBAAuB,OAAW,QAAO,qBAAqB,OAAO,sBAAsB;AACtG,eAAO,YAAY,oBAAI,KAAK;AAC5B,YAAI,OAAO,aAAa,OAAW,QAAO,WAAW,OAAO;AAAA,MAC9D;AAAA,MACA,MAAM,0BAA0B,IAAI;AAAA,QAClC,YAAY,OAAO;AAAA,QACnB,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH,GAAG,EAAE,aAAa,KAAK,CAAC;AACxB,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;AACD,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,YAAY,OAAO,GAAG;AAAA,EACjC;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,qBAAqB,IAAI,OAAO,EAAE;AACtD,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,cAAc,MAAM,2BAA2B,IAAI,KAAK;AAC9D,UAAM,mBAAqC,eACvC,EAAE,GAAG,QAAQ,cAAc,aAAa,IACxC;AACJ,UAAM,kBAAoC,cACtC,EAAE,GAAG,OAAO,cAAc,YAAY,IACtC;AACJ,UAAM,UAAU,aAAa,QAA8C,OAA6C;AAAA,MACtH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,OAAO,KAAK,KAAK,GAAG,MAAM,MAAM,KAAK,KAAK,GAAG,GAAG;AAClD,cAAQ,OAAO,EAAE,MAAM,OAAO,MAAM,IAAI,MAAM,KAAK;AAAA,IACrD;AACA,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,oCAAoC,iBAAiB;AAAA,MAC5E,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,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,cAAc,gBAAgB;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,uBAAwB,OAA4B,gBAAgB;AAC1E,UAAM,sBAAuB,SAAS,OAA+C,gBAAgB;AACrG,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,SAAS,MAAM,GAAG,QAAQ,mBAAmB,EAAE,IAAI,OAAO,GAAG,CAAC;AACpE,QAAI,CAAC,OAAQ;AACb,UAAM,gBAAgB,IAAI;AAAA,MACxB,MAAM;AACJ,eAAO,OAAO,OAAO;AACrB,eAAO,cAAc,OAAO,eAAe;AAC3C,eAAO,iBAAiB,OAAO,kBAAkB;AACjD,eAAO,WAAW,OAAO,YAAY;AACrC,eAAO,oBAAoB,OAAO,qBAAqB;AACvD,eAAO,mBAAmB,OAAO,oBAAoB;AACrD,eAAO,oBAAoB,OAAO,qBAAqB;AACvD,eAAO,mBAAmB,OAAO,oBAAoB;AACrD,eAAO,iBAAiB,OAAO,kBAAkB;AACjD,eAAO,kBAAkB,OAAO,mBAAmB;AACnD,eAAO,WAAW,OAAO;AACzB,eAAO,wBAAwB,OAAO,yBAAyB;AAC/D,eAAO,qBAAqB,OAAO,sBAAsB;AACzD,eAAO,YAAY,OAAO,YAAY,IAAI,KAAK,OAAO,SAAS,IAAI;AACnE,eAAO,YAAY,oBAAI,KAAK;AAAA,MAC9B;AAAA,MACA,MAAM,0BAA0B,IAAI;AAAA,QAClC,YAAY,OAAO;AAAA,QACnB,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,QAAQ,OAAO;AAAA,QACf,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,GAAG,EAAE,aAAa,KAAK,CAAC;AAExB,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,eAAe,QAAQ,gBAAgB,wBAAwB;AACrE,UAAM,cAAc,QAAQ,eAAe,uBAAuB;AAClE,QAAI,gBAAgB,aAAa;AAC/B,YAAM,QAAQ,yBAAyB,cAAc,WAAW;AAChE,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;AAEA,UAAM,wBAAwB;AAAA,MAC5B;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;AAAA,EACH;AACF;AAEA,MAAM,wBAAiF;AAAA,EACrF,IAAI;AAAA,EACJ,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAC3E,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI;AACtC,UAAM,WAAW,MAAM,qBAAqB,IAAI,EAAE;AAClD,QAAI,CAAC,SAAU,QAAO,CAAC;AACvB,UAAM,SAAS,MAAM,2BAA2B,IAAI,QAAQ;AAC5D,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,2BAA2B,CAAC;AAC3E,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,gCAAgC,CAAC;AACpF,sBAAkB,KAAK,OAAO,QAAQ;AACtC,4BAAwB,KAAK,OAAO,cAAc;AAClD,WAAO,YAAY,oBAAI,KAAK;AAC5B,WAAO,YAAY,oBAAI,KAAK;AAC5B,UAAM,GAAG,MAAM;AACf,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,YAAY,OAAO,GAAG;AAAA,EACjC;AAAA,EACA,UAAU,OAAO,EAAE,UAAU,MAAM;AACjC,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,eAAgB,UAA4D,gBAAgB;AAClG,UAAM,mBAAqC,eACvC,EAAE,GAAG,QAAQ,cAAc,aAAa,IACxC;AACJ,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,oCAAoC,iBAAiB;AAAA,MAC5E,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB;AAAA,MAChB,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,cAAc,gBAAgB;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAwC,QAAQ;AAChE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,uBAAwB,OAA4B,gBAAgB;AAC1E,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI,SAAS,MAAM,GAAG,QAAQ,mBAAmB,EAAE,IAAI,OAAO,GAAG,CAAC;AAClE,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,YAAI,CAAC,QAAQ;AACX,mBAAS,GAAG,OAAO,mBAAmB;AAAA,YACpC,IAAI,OAAO;AAAA,YACX,UAAU,OAAO;AAAA,YACjB,gBAAgB,OAAO;AAAA,YACvB,MAAM,OAAO;AAAA,YACb,aAAa,OAAO,eAAe;AAAA,YACnC,gBAAgB,OAAO,kBAAkB;AAAA,YACzC,UAAU,OAAO,YAAY;AAAA,YAC7B,mBAAmB,OAAO,qBAAqB;AAAA,YAC/C,kBAAkB,OAAO,oBAAoB;AAAA,YAC7C,mBAAmB,OAAO,qBAAqB;AAAA,YAC/C,kBAAkB,OAAO,oBAAoB;AAAA,YAC7C,gBAAgB,OAAO,kBAAkB;AAAA,YACzC,iBAAiB,OAAO,mBAAmB;AAAA,YAC3C,UAAU,OAAO;AAAA,YACjB,uBAAuB,OAAO,yBAAyB;AAAA,YACvD,oBAAoB,OAAO,sBAAsB;AAAA,YACjD,WAAW;AAAA,YACX,WAAW,oBAAI,KAAK;AAAA,YACpB,WAAW,oBAAI,KAAK;AAAA,UACtB,CAAC;AACD,aAAG,QAAQ,MAAM;AAAA,QACnB,OAAO;AACL,iBAAO,OAAO,OAAO;AACrB,iBAAO,cAAc,OAAO,eAAe;AAC3C,iBAAO,iBAAiB,OAAO,kBAAkB;AACjD,iBAAO,WAAW,OAAO,YAAY;AACrC,iBAAO,oBAAoB,OAAO,qBAAqB;AACvD,iBAAO,mBAAmB,OAAO,oBAAoB;AACrD,iBAAO,oBAAoB,OAAO,qBAAqB;AACvD,iBAAO,mBAAmB,OAAO,oBAAoB;AACrD,iBAAO,iBAAiB,OAAO,kBAAkB;AACjD,iBAAO,kBAAkB,OAAO,mBAAmB;AACnD,iBAAO,WAAW,OAAO;AACzB,iBAAO,wBAAwB,OAAO,yBAAyB;AAC/D,iBAAO,qBAAqB,OAAO,sBAAsB;AACzD,iBAAO,YAAY;AACnB,iBAAO,YAAY,oBAAI,KAAK;AAAA,QAC9B;AACA,cAAM,GAAG,MAAM;AAAA,MACjB;AAAA,MACA,MAAM,0BAA0B,IAAI;AAAA,QAClC,YAAa,OAA6B;AAAA,QAC1C,gBAAiB,OAA6B;AAAA,QAC9C,UAAW,OAA6B;AAAA,QACxC,QAAQ,OAAO;AAAA,QACf,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,GAAG,EAAE,aAAa,KAAK,CAAC;AACxB,UAAM,iBAAiB;AAEvB,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,eAAe,QAAQ,gBAAgB,wBAAwB;AACrE,QAAI,cAAc;AAChB,YAAM,QAAQ,yBAAyB,cAAc,MAAS;AAC9D,YAAM,qBAAqB;AAAA,QACzB;AAAA,QACA,UAAU,EAAE,UAAU;AAAA,QACtB,UAAU,eAAe;AAAA,QACzB,UAAU,eAAe;AAAA,QACzB,gBAAgB,eAAe;AAAA,QAC/B,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa;AAAA,QACX,IAAI,eAAe;AAAA,QACnB,gBAAgB,eAAe;AAAA,QAC/B,UAAU,eAAe;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,gBAAgB,qBAAqB;AACrC,gBAAgB,qBAAqB;AACrC,gBAAgB,qBAAqB;",
|
|
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.
|
|
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.
|
|
270
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
271
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
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.
|
|
277
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
278
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
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 ??
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
+
}
|