@open-mercato/core 0.10.1-develop.7974.1.c98f9304d9 → 0.10.1-develop.7976.1.c0ee1c7c28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/auth/api/users/acl/route.js +5 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js +68 -23
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +101 -6
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/acl/route.ts +5 -1
- package/src/modules/auth/i18n/de.json +16 -0
- package/src/modules/auth/i18n/en.json +16 -0
- package/src/modules/auth/i18n/es.json +16 -0
- package/src/modules/auth/i18n/ko.json +16 -0
- package/src/modules/auth/i18n/pl.json +16 -0
- package/src/modules/auth/lib/grantChecks.ts +71 -25
- package/src/modules/customers/commands/personCompanyLinks.ts +132 -7
|
@@ -92,6 +92,9 @@ async function loadPersonCompanyLinkSnapshot(em, id, scope) {
|
|
|
92
92
|
}
|
|
93
93
|
);
|
|
94
94
|
if (!link) return null;
|
|
95
|
+
return snapshotPersonCompanyLink(link);
|
|
96
|
+
}
|
|
97
|
+
function snapshotPersonCompanyLink(link) {
|
|
95
98
|
const personId = typeof link.person === "string" ? link.person : link.person.id;
|
|
96
99
|
const companyId = typeof link.company === "string" ? link.company : link.company.id;
|
|
97
100
|
return {
|
|
@@ -150,8 +153,77 @@ async function clearPrimaryFlagsForPerson(em, person) {
|
|
|
150
153
|
{ isPrimary: false }
|
|
151
154
|
);
|
|
152
155
|
}
|
|
156
|
+
async function restoreCreatePersonCompanyLink(ctx, after, before) {
|
|
157
|
+
const em = ctx.container.resolve("em").fork();
|
|
158
|
+
const scope = { tenantId: after.tenantId, organizationId: after.organizationId };
|
|
159
|
+
const link = await findOneWithDecryption(
|
|
160
|
+
em,
|
|
161
|
+
CustomerPersonCompanyLink,
|
|
162
|
+
{ id: after.id, ...scope },
|
|
163
|
+
void 0,
|
|
164
|
+
scope
|
|
165
|
+
);
|
|
166
|
+
if (!link) return;
|
|
167
|
+
const person = await requirePersonEntity(em, after.personEntityId, after.tenantId, after.organizationId);
|
|
168
|
+
const profile = await requirePersonProfile(em, person);
|
|
169
|
+
const profileCompanyId = typeof profile.company === "string" ? profile.company : profile.company?.id ?? null;
|
|
170
|
+
const restorePrimaryAssignment = after.isPrimary && link.isPrimary && profileCompanyId === after.companyEntityId;
|
|
171
|
+
const company = restorePrimaryAssignment && before.profileCompanyId ? await requireCompanyEntity(em, before.profileCompanyId, after.tenantId, after.organizationId) : null;
|
|
172
|
+
const primaryLinkIds = before.primaryLinkIds.filter((id) => id !== link.id);
|
|
173
|
+
await withAtomicFlush(em, [
|
|
174
|
+
async () => {
|
|
175
|
+
if (restorePrimaryAssignment) {
|
|
176
|
+
await em.nativeUpdate(
|
|
177
|
+
CustomerPersonCompanyLink,
|
|
178
|
+
{ person, ...scope, id: { $ne: link.id }, deletedAt: null, isPrimary: true },
|
|
179
|
+
{ isPrimary: false }
|
|
180
|
+
);
|
|
181
|
+
if (primaryLinkIds.length) {
|
|
182
|
+
await em.nativeUpdate(
|
|
183
|
+
CustomerPersonCompanyLink,
|
|
184
|
+
{ person, ...scope, id: { $in: primaryLinkIds }, deletedAt: null },
|
|
185
|
+
{ isPrimary: true }
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
profile.company = company;
|
|
189
|
+
}
|
|
190
|
+
link.isPrimary = before.link?.isPrimary ?? false;
|
|
191
|
+
link.deletedAt = before.link ? before.link.deletedAt ? new Date(before.link.deletedAt) : null : /* @__PURE__ */ new Date();
|
|
192
|
+
}
|
|
193
|
+
], { transaction: true });
|
|
194
|
+
const dataEngine = ctx.container.resolve("dataEngine");
|
|
195
|
+
await emitCrudUndoSideEffects({
|
|
196
|
+
dataEngine,
|
|
197
|
+
action: link.deletedAt ? "deleted" : "updated",
|
|
198
|
+
entity: link,
|
|
199
|
+
identifiers: getLinkIdentifiers(link),
|
|
200
|
+
syncOrigin: ctx.syncOrigin,
|
|
201
|
+
events: personCompanyLinkCrudEvents,
|
|
202
|
+
indexer: { entityType: "customers:customer_person_company_link" }
|
|
203
|
+
});
|
|
204
|
+
}
|
|
153
205
|
const createPersonCompanyLinkCommand = {
|
|
154
206
|
id: "customers.personCompanyLinks.create",
|
|
207
|
+
async prepare(rawInput, ctx) {
|
|
208
|
+
const parsed = personCompanyLinkCreateSchema.parse(rawInput);
|
|
209
|
+
ensureTenantScope(ctx, parsed.tenantId);
|
|
210
|
+
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
211
|
+
const em = ctx.container.resolve("em").fork();
|
|
212
|
+
const person = await requirePersonEntity(em, parsed.personEntityId, parsed.tenantId, parsed.organizationId);
|
|
213
|
+
const company = await requireCompanyEntity(em, parsed.companyEntityId, parsed.tenantId, parsed.organizationId);
|
|
214
|
+
const profile = await requirePersonProfile(em, person);
|
|
215
|
+
const links = await loadPersonCompanyLinks(em, person);
|
|
216
|
+
const link = links.find(
|
|
217
|
+
(entry) => (typeof entry.company === "string" ? entry.company : entry.company.id) === company.id
|
|
218
|
+
) ?? await findDeletedPersonCompanyLink(em, person, company);
|
|
219
|
+
return {
|
|
220
|
+
before: {
|
|
221
|
+
link: link ? await loadPersonCompanyLinkSnapshot(em, link.id, parsed) : null,
|
|
222
|
+
primaryLinkIds: links.filter((entry) => entry.isPrimary).map((entry) => entry.id),
|
|
223
|
+
profileCompanyId: typeof profile.company === "string" ? profile.company : profile.company?.id ?? null
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
},
|
|
155
227
|
async execute(rawInput, ctx) {
|
|
156
228
|
const parsed = personCompanyLinkCreateSchema.parse(rawInput);
|
|
157
229
|
ensureTenantScope(ctx, parsed.tenantId);
|
|
@@ -163,8 +235,14 @@ const createPersonCompanyLinkCommand = {
|
|
|
163
235
|
const existingLinks = await loadPersonCompanyLinks(em, person);
|
|
164
236
|
const makePrimary = Boolean(parsed.isPrimary) || existingLinks.length === 0;
|
|
165
237
|
const existingLive = existingLinks.find((link2) => (typeof link2.company === "string" ? link2.company : link2.company.id) === company.id) ?? null;
|
|
238
|
+
const createBefore = {
|
|
239
|
+
link: existingLive ? snapshotPersonCompanyLink(existingLive) : null,
|
|
240
|
+
primaryLinkIds: existingLinks.filter((link2) => link2.isPrimary).map((link2) => link2.id),
|
|
241
|
+
profileCompanyId: typeof profile.company === "string" ? profile.company : profile.company?.id ?? null
|
|
242
|
+
};
|
|
166
243
|
if (existingLive) {
|
|
167
|
-
|
|
244
|
+
const changed = makePrimary && !existingLive.isPrimary;
|
|
245
|
+
if (changed) {
|
|
168
246
|
await withAtomicFlush(em, [
|
|
169
247
|
() => clearPrimaryFlagsForPerson(em, person),
|
|
170
248
|
() => {
|
|
@@ -173,13 +251,14 @@ const createPersonCompanyLinkCommand = {
|
|
|
173
251
|
}
|
|
174
252
|
], { transaction: true });
|
|
175
253
|
}
|
|
176
|
-
return { linkId: existingLive.id, created: false, undeleted: false };
|
|
254
|
+
return { linkId: existingLive.id, created: false, undeleted: false, changed, createBefore };
|
|
177
255
|
}
|
|
178
256
|
let link;
|
|
179
257
|
let undeleted = false;
|
|
180
258
|
await withAtomicFlush(em, [
|
|
181
259
|
async () => {
|
|
182
260
|
const deletedLink = await findDeletedPersonCompanyLink(em, person, company);
|
|
261
|
+
if (deletedLink) createBefore.link = snapshotPersonCompanyLink(deletedLink);
|
|
183
262
|
if (makePrimary) {
|
|
184
263
|
await clearPrimaryFlagsForPerson(em, person);
|
|
185
264
|
}
|
|
@@ -219,18 +298,22 @@ const createPersonCompanyLinkCommand = {
|
|
|
219
298
|
events: personCompanyLinkCrudEvents,
|
|
220
299
|
indexer: { entityType: "customers:customer_person_company_link" }
|
|
221
300
|
});
|
|
222
|
-
return { linkId: link.id, created: !undeleted, undeleted };
|
|
301
|
+
return { linkId: link.id, created: !undeleted, undeleted, changed: true, createBefore };
|
|
223
302
|
},
|
|
224
303
|
captureAfter: async (_input, result, ctx) => {
|
|
225
|
-
const em = ctx.container.resolve("em");
|
|
304
|
+
const em = ctx.container.resolve("em").fork();
|
|
226
305
|
return loadPersonCompanyLinkSnapshot(em, result.linkId, {
|
|
227
306
|
tenantId: ctx.auth?.tenantId ?? null,
|
|
228
307
|
organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
|
|
229
308
|
});
|
|
230
309
|
},
|
|
231
310
|
buildLog: async ({ result, snapshots }) => {
|
|
232
|
-
const
|
|
311
|
+
const before = result.createBefore;
|
|
233
312
|
const after = snapshots.after;
|
|
313
|
+
if (!result.changed) {
|
|
314
|
+
return { skipLog: true };
|
|
315
|
+
}
|
|
316
|
+
const { translate } = await resolveTranslations();
|
|
234
317
|
return {
|
|
235
318
|
actionLabel: translate("customers.audit.personCompanyLinks.create", "Link company to person"),
|
|
236
319
|
resourceKind: "customers.personCompanyLink",
|
|
@@ -239,10 +322,12 @@ const createPersonCompanyLinkCommand = {
|
|
|
239
322
|
parentResourceId: after?.personEntityId ?? null,
|
|
240
323
|
tenantId: after?.tenantId ?? null,
|
|
241
324
|
organizationId: after?.organizationId ?? null,
|
|
325
|
+
snapshotBefore: before?.link ?? null,
|
|
242
326
|
snapshotAfter: after ?? null,
|
|
243
327
|
payload: {
|
|
244
328
|
undo: {
|
|
245
329
|
after: after ?? null,
|
|
330
|
+
...before ? { createBefore: before } : {},
|
|
246
331
|
...result.undeleted ? { before: null } : {}
|
|
247
332
|
}
|
|
248
333
|
}
|
|
@@ -252,6 +337,10 @@ const createPersonCompanyLinkCommand = {
|
|
|
252
337
|
const payload = extractUndoPayload(logEntry);
|
|
253
338
|
const after = payload?.after;
|
|
254
339
|
if (!after) return;
|
|
340
|
+
if (payload.createBefore) {
|
|
341
|
+
await restoreCreatePersonCompanyLink(ctx, after, payload.createBefore);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
255
344
|
const em = ctx.container.resolve("em").fork();
|
|
256
345
|
const link = await findOneWithDecryption(
|
|
257
346
|
em,
|
|
@@ -322,6 +411,12 @@ const createPersonCompanyLinkCommand = {
|
|
|
322
411
|
void 0,
|
|
323
412
|
{ tenantId: after.tenantId, organizationId: after.organizationId }
|
|
324
413
|
);
|
|
414
|
+
const existingLinks = await loadPersonCompanyLinks(em, person);
|
|
415
|
+
const createBefore = {
|
|
416
|
+
link: link ? snapshotPersonCompanyLink(link) : null,
|
|
417
|
+
primaryLinkIds: existingLinks.filter((entry) => entry.isPrimary).map((entry) => entry.id),
|
|
418
|
+
profileCompanyId: typeof profile.company === "string" ? profile.company : profile.company?.id ?? null
|
|
419
|
+
};
|
|
325
420
|
await withAtomicFlush(em, [
|
|
326
421
|
async () => {
|
|
327
422
|
if (after.isPrimary) {
|
|
@@ -359,7 +454,7 @@ const createPersonCompanyLinkCommand = {
|
|
|
359
454
|
events: personCompanyLinkCrudEvents,
|
|
360
455
|
indexer: { entityType: "customers:customer_person_company_link" }
|
|
361
456
|
});
|
|
362
|
-
return { linkId: link.id, created: true, undeleted: false };
|
|
457
|
+
return { linkId: link.id, created: true, undeleted: false, changed: true, createBefore };
|
|
363
458
|
}
|
|
364
459
|
};
|
|
365
460
|
const updatePersonCompanyLinkCommand = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customers/commands/personCompanyLinks.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../data/entities'\nimport {\n personCompanyLinkCreateSchema,\n personCompanyLinkDeleteSchema,\n personCompanyLinkUpdateSchema,\n type PersonCompanyLinkCreateInput,\n type PersonCompanyLinkDeleteInput,\n type PersonCompanyLinkUpdateInput,\n} from '../data/validators'\nimport {\n findDeletedPersonCompanyLink,\n loadPersonCompanyLinks,\n promoteFallbackPrimaryLink,\n removePersonCompanyLink,\n} from '../lib/personCompanies'\nimport {\n emitQueryIndexUpsertEvents,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n type QueryIndexEventEntry,\n} from './shared'\nimport type { CustomersEventId } from '../events'\nimport { E } from '#generated/entities.ids.generated'\nimport type { EventBus } from '@open-mercato/events'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers').child({ component: 'personCompanyLinks' })\n\n// Typed against the module's declared event ids so removing or renaming the declaration in\n// `events.ts` fails the build here rather than emitting an undeclared event at runtime.\nconst PROFILE_ONLY_DETACHED_EVENT: CustomersEventId = 'customers.person.company_assignment.detached'\n\ntype PersonCompanyLinkSnapshot = {\n id: string\n personEntityId: string\n companyEntityId: string\n isPrimary: boolean\n tenantId: string\n organizationId: string\n deletedAt: string | null\n}\n\ntype PersonCompanyLinkUndoPayload = {\n before?: PersonCompanyLinkSnapshot | null\n after?: PersonCompanyLinkSnapshot | null\n}\n\n/**\n * Snapshot of a legacy profile-only company assignment \u2014 `customer_person_profiles.company_id`\n * set with no backing link row (#5114). It carries no link id because none exists; `kind`\n * discriminates it from `PersonCompanyLinkSnapshot` in the delete command's undo payload.\n */\ntype PersonCompanyProfileAssignmentSnapshot = {\n kind: 'profile-only'\n personEntityId: string\n companyEntityId: string\n tenantId: string\n organizationId: string\n}\n\ntype PersonCompanyLinkDeleteSnapshot = PersonCompanyLinkSnapshot | PersonCompanyProfileAssignmentSnapshot\n\ntype PersonCompanyLinkDeleteUndoPayload = {\n before?: PersonCompanyLinkDeleteSnapshot | null\n}\n\ntype PersonCompanyLinkDeleteResult = {\n linkId: string | null\n personEntityId: string | null\n companyEntityId: string | null\n}\n\nfunction isProfileAssignmentSnapshot(\n snapshot: PersonCompanyLinkDeleteSnapshot | null | undefined,\n): snapshot is PersonCompanyProfileAssignmentSnapshot {\n return Boolean(snapshot) && (snapshot as PersonCompanyProfileAssignmentSnapshot).kind === 'profile-only'\n}\n\nfunction resolveProfileOnlyTarget(\n parsed: PersonCompanyLinkDeleteInput,\n): { personEntityId: string; companyEntityId: string } | null {\n if (parsed.linkId) return null\n if (!parsed.personEntityId || !parsed.companyEntityId) return null\n return { personEntityId: parsed.personEntityId, companyEntityId: parsed.companyEntityId }\n}\n\nfunction personQueryIndexEntries(\n person: CustomerEntity,\n profile: CustomerPersonProfile,\n): QueryIndexEventEntry[] {\n return [\n {\n entityType: E.customers.customer_entity,\n recordId: person.id,\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n {\n entityType: E.customers.customer_person_profile,\n recordId: profile.id,\n tenantId: profile.tenantId,\n organizationId: profile.organizationId,\n },\n ]\n}\n\nconst personCompanyLinkCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'person_company_link',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n ...(ctx.entity && typeof ctx.entity === 'object' && 'person' in (ctx.entity as Record<string, unknown>)\n ? {\n personEntityId:\n typeof (ctx.entity as CustomerPersonCompanyLink).person === 'string'\n ? (ctx.entity as any).person\n : (ctx.entity as CustomerPersonCompanyLink).person?.id ?? null,\n companyEntityId:\n typeof (ctx.entity as CustomerPersonCompanyLink).company === 'string'\n ? (ctx.entity as any).company\n : (ctx.entity as CustomerPersonCompanyLink).company?.id ?? null,\n }\n : {}),\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n }),\n}\n\nfunction getLinkIdentifiers(link: CustomerPersonCompanyLink) {\n return {\n id: link.id,\n organizationId: link.organizationId,\n tenantId: link.tenantId,\n }\n}\n\nasync function loadPersonCompanyLinkSnapshot(\n em: EntityManager,\n id: string,\n scope?: { tenantId?: string | null; organizationId?: string | null },\n): Promise<PersonCompanyLinkSnapshot | null> {\n const filter: Record<string, unknown> = { id }\n if (scope?.tenantId) filter.tenantId = scope.tenantId\n if (scope?.organizationId) filter.organizationId = scope.organizationId\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n filter,\n undefined,\n {\n tenantId: scope?.tenantId ?? null,\n organizationId: scope?.organizationId ?? null,\n },\n )\n if (!link) return null\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n return {\n id: link.id,\n personEntityId: personId,\n companyEntityId: companyId,\n isPrimary: Boolean(link.isPrimary),\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n deletedAt: link.deletedAt ? link.deletedAt.toISOString() : null,\n }\n}\n\nasync function requirePersonEntity(\n em: EntityManager,\n entityId: string,\n tenantId: string,\n organizationId: string,\n): Promise<CustomerEntity> {\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: 'person', tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!person) {\n throw notFound('Person not found')\n }\n return person\n}\n\nasync function requireCompanyEntity(\n em: EntityManager,\n entityId: string,\n tenantId: string,\n organizationId: string,\n): Promise<CustomerEntity> {\n const company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: 'company', tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!company) {\n throw notFound('Company not found')\n }\n return company\n}\n\nasync function requirePersonProfile(\n em: EntityManager,\n person: CustomerEntity,\n): Promise<CustomerPersonProfile> {\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) {\n throw notFound('Person profile not found')\n }\n return profile\n}\n\nasync function clearPrimaryFlagsForPerson(em: EntityManager, person: CustomerEntity): Promise<void> {\n await em.nativeUpdate(\n CustomerPersonCompanyLink,\n { person, organizationId: person.organizationId, tenantId: person.tenantId, isPrimary: true },\n { isPrimary: false },\n )\n}\n\nconst createPersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkCreateInput, { linkId: string; created: boolean; undeleted: boolean }> = {\n id: 'customers.personCompanyLinks.create',\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const person = await requirePersonEntity(em, parsed.personEntityId, parsed.tenantId, parsed.organizationId)\n const company = await requireCompanyEntity(em, parsed.companyEntityId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n\n const existingLinks = await loadPersonCompanyLinks(em, person)\n const makePrimary = Boolean(parsed.isPrimary) || existingLinks.length === 0\n const existingLive =\n existingLinks.find((link) => (typeof link.company === 'string' ? link.company : link.company.id) === company.id) ?? null\n\n if (existingLive) {\n if (makePrimary && !existingLive.isPrimary) {\n await withAtomicFlush(em, [\n () => clearPrimaryFlagsForPerson(em, person),\n () => {\n existingLive.isPrimary = true\n profile.company = company\n },\n ], { transaction: true })\n }\n return { linkId: existingLive.id, created: false, undeleted: false }\n }\n\n let link!: CustomerPersonCompanyLink\n let undeleted = false\n await withAtomicFlush(em, [\n async () => {\n const deletedLink = await findDeletedPersonCompanyLink(em, person, company)\n if (makePrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n }\n if (deletedLink) {\n deletedLink.deletedAt = null\n deletedLink.isPrimary = makePrimary\n em.persist(deletedLink)\n link = deletedLink\n undeleted = true\n } else {\n link = em.create(CustomerPersonCompanyLink, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n person,\n company,\n isPrimary: makePrimary,\n })\n em.persist(link)\n }\n },\n () => {\n if (makePrimary) {\n profile.company = company\n } else if (!profile.company && existingLinks.length === 0) {\n profile.company = company\n link.isPrimary = true\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: undeleted ? 'updated' : 'created',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id, created: !undeleted, undeleted }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = ctx.container.resolve('em') as EntityManager\n return loadPersonCompanyLinkSnapshot(em, result.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as PersonCompanyLinkSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.create', 'Link company to person'),\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: after?.personEntityId ?? null,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n after: after ?? null,\n ...(result.undeleted ? { before: null } : {}),\n } satisfies PersonCompanyLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: after.id },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let remainingLinks: CustomerPersonCompanyLink[] = []\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: after.personEntityId, kind: 'person', tenantId: after.tenantId, organizationId: after.organizationId, deletedAt: null },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) return\n remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)\n },\n async () => {\n link.isPrimary = false\n link.deletedAt = new Date()\n if (!person || !profile) return\n if (after.isPrimary) {\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, after.companyEntityId)\n } else if (profile.company && typeof profile.company !== 'string' && profile.company.id === after.companyEntityId) {\n profile.company = null\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<PersonCompanyLinkSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for person-company link create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const person = await requirePersonEntity(em, after.personEntityId, after.tenantId, after.organizationId)\n const company = await requireCompanyEntity(em, after.companyEntityId, after.tenantId, after.organizationId)\n const profile = await requirePersonProfile(em, person)\n\n let link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: after.id },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n\n await withAtomicFlush(em, [\n async () => {\n if (after.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n }\n if (!link) {\n link = em.create(CustomerPersonCompanyLink, {\n id: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n person,\n company,\n isPrimary: after.isPrimary,\n })\n em.persist(link)\n } else {\n link.deletedAt = null\n link.isPrimary = after.isPrimary\n em.persist(link)\n }\n },\n () => {\n if (after.isPrimary) {\n profile.company = company\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: link!,\n identifiers: getLinkIdentifiers(link!),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link!.id, created: true, undeleted: false }\n },\n}\n\nconst updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInput, { linkId: string }> = {\n id: 'customers.personCompanyLinks.update',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadPersonCompanyLinkSnapshot(em, parsed.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkUpdateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n {\n id: parsed.linkId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!link) {\n throw notFound('Company link not found')\n }\n\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n const person = await requirePersonEntity(em, personId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const linkedCompany = await requireCompanyEntity(em, companyId, parsed.tenantId, parsed.organizationId)\n\n const linkWasPrimary = link.isPrimary\n await withAtomicFlush(em, [\n async () => {\n if (parsed.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n profile.company = linkedCompany\n } else if (!parsed.isPrimary) {\n link.isPrimary = false\n if (!linkWasPrimary && profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {\n profile.company = null\n }\n }\n },\n async () => {\n if (!parsed.isPrimary && linkWasPrimary) {\n const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = ctx.container.resolve('em') as EntityManager\n return loadPersonCompanyLinkSnapshot(em, result.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as PersonCompanyLinkSnapshot | undefined\n const after = snapshots.after as PersonCompanyLinkSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.update', 'Update company link'),\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: after?.personEntityId ?? before?.personEntityId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies PersonCompanyLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: before.id },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile || !before.isPrimary) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n async () => {\n if (!person || !profile) return\n if (before.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n if (company) profile.company = company\n } else {\n link.isPrimary = false\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n}\n\n/**\n * Detach a legacy profile-only company assignment: `customer_person_profiles.company_id`\n * points at the company but no `customer_person_company_links` row was ever created, so\n * there is no link row for the id-based branch to soft-delete (#5114). Running it as part\n * of the same command keeps the audit entry, the undo token and the CRUD cache\n * invalidation identical to a link-backed detach.\n */\nasync function detachProfileOnlyCompany(\n em: EntityManager,\n ctx: CommandRuntimeContext,\n parsed: PersonCompanyLinkDeleteInput,\n target: { personEntityId: string; companyEntityId: string },\n): Promise<PersonCompanyLinkDeleteResult> {\n const person = await requirePersonEntity(em, target.personEntityId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const assignedCompany = profile.company\n const assignedCompanyId = typeof assignedCompany === 'string' ? assignedCompany : assignedCompany?.id ?? null\n if (assignedCompanyId !== target.companyEntityId) {\n throw notFound('Company link not found')\n }\n\n await withAtomicFlush(em, [\n () => removePersonCompanyLink(em, person, profile, target.companyEntityId),\n ], { transaction: true })\n\n // There is no link entity to hand to `emitCrudSideEffects`; what changed is the\n // person's own company assignment, so reindex the person and its profile instead.\n await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile))\n await emitProfileOnlyDetachedEvent(ctx, person, target.companyEntityId)\n\n return { linkId: null, personEntityId: person.id, companyEntityId: target.companyEntityId }\n}\n\n/**\n * Live-refresh signal for the company People tab and the person Companies tab, which the\n * link-backed branch gets from `customers.person_company_link.deleted` via `emitCrudSideEffects`.\n * The profile-only branch has no link row to emit that for, so it publishes the sibling\n * `clientBroadcast` event instead (#5114). `tenantId` must travel in the payload: the event bus\n * only forwards a broadcast event cross-process when the payload carries a tenant scope. The\n * write is already committed, so a failed refresh signal must never fail the detach.\n */\nasync function emitProfileOnlyDetachedEvent(\n ctx: CommandRuntimeContext,\n person: CustomerEntity,\n companyEntityId: string,\n): Promise<void> {\n try {\n const bus = ctx.container.resolve<EventBus>('eventBus')\n await bus.emitEvent(\n PROFILE_ONLY_DETACHED_EVENT,\n {\n linkId: null,\n personEntityId: person.id,\n companyEntityId,\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n } catch (err) {\n logger.warn('Profile-only company detach broadcast failed', {\n command: 'customers.personCompanyLinks.delete',\n personEntityId: person.id,\n companyEntityId,\n err,\n })\n }\n}\n\n/**\n * Undo counterpart of `detachProfileOnlyCompany`: re-point the person's legacy\n * `profile.company` at the company it was detached from. The lookups run in the first\n * flush phase and the assignment in the second so the encryption subscriber's\n * re-baseline on find cannot drop the pending change (#2507).\n */\nasync function restoreProfileOnlyCompany(\n ctx: CommandRuntimeContext,\n before: PersonCompanyProfileAssignmentSnapshot,\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!profile) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n () => {\n if (profile && company) profile.company = company\n },\n ], { transaction: true })\n\n if (person && profile) {\n await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile))\n }\n}\n\nconst deletePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkDeleteInput, PersonCompanyLinkDeleteResult> = {\n id: 'customers.personCompanyLinks.delete',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkDeleteSchema.parse(rawInput)\n const profileOnlyTarget = resolveProfileOnlyTarget(parsed)\n if (profileOnlyTarget) {\n return {\n before: {\n kind: 'profile-only',\n personEntityId: profileOnlyTarget.personEntityId,\n companyEntityId: profileOnlyTarget.companyEntityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n } satisfies PersonCompanyProfileAssignmentSnapshot,\n }\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadPersonCompanyLinkSnapshot(em, parsed.linkId as string, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkDeleteSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const profileOnlyTarget = resolveProfileOnlyTarget(parsed)\n if (profileOnlyTarget) {\n return await detachProfileOnlyCompany(em, ctx, parsed, profileOnlyTarget)\n }\n\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n {\n id: parsed.linkId as string,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!link) {\n throw notFound('Company link not found')\n }\n\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n const person = await requirePersonEntity(em, personId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const linkWasPrimary = link.isPrimary\n\n const existingLinks = await loadPersonCompanyLinks(em, person)\n const remainingLinks = existingLinks.filter((entry) => entry.id !== link.id)\n\n await withAtomicFlush(em, [\n () => {\n link.isPrimary = false\n link.deletedAt = new Date()\n },\n async () => {\n if (linkWasPrimary) {\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)\n } else if (profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {\n const primary = remainingLinks.find((entry) => entry.isPrimary) ?? null\n const primaryCompany = primary && typeof primary.company !== 'string' ? primary.company : null\n if (primaryCompany) {\n profile.company = primaryCompany\n }\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id, personEntityId: personId, companyEntityId: companyId }\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as PersonCompanyLinkDeleteSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.delete', 'Remove company link'),\n // Kept as `customers.personCompanyLink` for the profile-only shape too: the company\n // detail cache tags this resource kind, so both detach shapes must invalidate it.\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: before?.personEntityId ?? null,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies PersonCompanyLinkDeleteUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkDeleteUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n\n if (isProfileAssignmentSnapshot(before)) {\n await restoreProfileOnlyCompany(ctx, before)\n return\n }\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: before.id },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person || !before.isPrimary) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n async () => {\n link.deletedAt = null\n link.isPrimary = before.isPrimary\n if (person && before.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n if (profile && company) profile.company = company\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n}\n\nregisterCommand(createPersonCompanyLinkCommand)\nregisterCommand(updatePersonCompanyLinkCommand)\nregisterCommand(deletePersonCompanyLinkCommand)\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../data/entities'\nimport {\n personCompanyLinkCreateSchema,\n personCompanyLinkDeleteSchema,\n personCompanyLinkUpdateSchema,\n type PersonCompanyLinkCreateInput,\n type PersonCompanyLinkDeleteInput,\n type PersonCompanyLinkUpdateInput,\n} from '../data/validators'\nimport {\n findDeletedPersonCompanyLink,\n loadPersonCompanyLinks,\n promoteFallbackPrimaryLink,\n removePersonCompanyLink,\n} from '../lib/personCompanies'\nimport {\n emitQueryIndexUpsertEvents,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n type QueryIndexEventEntry,\n} from './shared'\nimport type { CustomersEventId } from '../events'\nimport { E } from '#generated/entities.ids.generated'\nimport type { EventBus } from '@open-mercato/events'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers').child({ component: 'personCompanyLinks' })\n\n// Typed against the module's declared event ids so removing or renaming the declaration in\n// `events.ts` fails the build here rather than emitting an undeclared event at runtime.\nconst PROFILE_ONLY_DETACHED_EVENT: CustomersEventId = 'customers.person.company_assignment.detached'\n\ntype PersonCompanyLinkSnapshot = {\n id: string\n personEntityId: string\n companyEntityId: string\n isPrimary: boolean\n tenantId: string\n organizationId: string\n deletedAt: string | null\n}\n\ntype PersonCompanyLinkUndoPayload = {\n before?: PersonCompanyLinkSnapshot | null\n after?: PersonCompanyLinkSnapshot | null\n createBefore?: PersonCompanyLinkCreateBefore\n}\n\ntype PersonCompanyLinkCreateBefore = {\n link: PersonCompanyLinkSnapshot | null\n primaryLinkIds: string[]\n profileCompanyId: string | null\n}\n\ntype PersonCompanyLinkCreateResult = {\n linkId: string\n created: boolean\n undeleted: boolean\n changed: boolean\n createBefore: PersonCompanyLinkCreateBefore\n}\n\n/**\n * Snapshot of a legacy profile-only company assignment \u2014 `customer_person_profiles.company_id`\n * set with no backing link row (#5114). It carries no link id because none exists; `kind`\n * discriminates it from `PersonCompanyLinkSnapshot` in the delete command's undo payload.\n */\ntype PersonCompanyProfileAssignmentSnapshot = {\n kind: 'profile-only'\n personEntityId: string\n companyEntityId: string\n tenantId: string\n organizationId: string\n}\n\ntype PersonCompanyLinkDeleteSnapshot = PersonCompanyLinkSnapshot | PersonCompanyProfileAssignmentSnapshot\n\ntype PersonCompanyLinkDeleteUndoPayload = {\n before?: PersonCompanyLinkDeleteSnapshot | null\n}\n\ntype PersonCompanyLinkDeleteResult = {\n linkId: string | null\n personEntityId: string | null\n companyEntityId: string | null\n}\n\nfunction isProfileAssignmentSnapshot(\n snapshot: PersonCompanyLinkDeleteSnapshot | null | undefined,\n): snapshot is PersonCompanyProfileAssignmentSnapshot {\n return Boolean(snapshot) && (snapshot as PersonCompanyProfileAssignmentSnapshot).kind === 'profile-only'\n}\n\nfunction resolveProfileOnlyTarget(\n parsed: PersonCompanyLinkDeleteInput,\n): { personEntityId: string; companyEntityId: string } | null {\n if (parsed.linkId) return null\n if (!parsed.personEntityId || !parsed.companyEntityId) return null\n return { personEntityId: parsed.personEntityId, companyEntityId: parsed.companyEntityId }\n}\n\nfunction personQueryIndexEntries(\n person: CustomerEntity,\n profile: CustomerPersonProfile,\n): QueryIndexEventEntry[] {\n return [\n {\n entityType: E.customers.customer_entity,\n recordId: person.id,\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n {\n entityType: E.customers.customer_person_profile,\n recordId: profile.id,\n tenantId: profile.tenantId,\n organizationId: profile.organizationId,\n },\n ]\n}\n\nconst personCompanyLinkCrudEvents: CrudEventsConfig = {\n module: 'customers',\n entity: 'person_company_link',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n ...(ctx.entity && typeof ctx.entity === 'object' && 'person' in (ctx.entity as Record<string, unknown>)\n ? {\n personEntityId:\n typeof (ctx.entity as CustomerPersonCompanyLink).person === 'string'\n ? (ctx.entity as any).person\n : (ctx.entity as CustomerPersonCompanyLink).person?.id ?? null,\n companyEntityId:\n typeof (ctx.entity as CustomerPersonCompanyLink).company === 'string'\n ? (ctx.entity as any).company\n : (ctx.entity as CustomerPersonCompanyLink).company?.id ?? null,\n }\n : {}),\n ...(ctx.syncOrigin ? { syncOrigin: ctx.syncOrigin } : {}),\n }),\n}\n\nfunction getLinkIdentifiers(link: CustomerPersonCompanyLink) {\n return {\n id: link.id,\n organizationId: link.organizationId,\n tenantId: link.tenantId,\n }\n}\n\nasync function loadPersonCompanyLinkSnapshot(\n em: EntityManager,\n id: string,\n scope?: { tenantId?: string | null; organizationId?: string | null },\n): Promise<PersonCompanyLinkSnapshot | null> {\n const filter: Record<string, unknown> = { id }\n if (scope?.tenantId) filter.tenantId = scope.tenantId\n if (scope?.organizationId) filter.organizationId = scope.organizationId\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n filter,\n undefined,\n {\n tenantId: scope?.tenantId ?? null,\n organizationId: scope?.organizationId ?? null,\n },\n )\n if (!link) return null\n return snapshotPersonCompanyLink(link)\n}\n\nfunction snapshotPersonCompanyLink(link: CustomerPersonCompanyLink): PersonCompanyLinkSnapshot {\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n return {\n id: link.id,\n personEntityId: personId,\n companyEntityId: companyId,\n isPrimary: Boolean(link.isPrimary),\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n deletedAt: link.deletedAt ? link.deletedAt.toISOString() : null,\n }\n}\n\nasync function requirePersonEntity(\n em: EntityManager,\n entityId: string,\n tenantId: string,\n organizationId: string,\n): Promise<CustomerEntity> {\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: 'person', tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!person) {\n throw notFound('Person not found')\n }\n return person\n}\n\nasync function requireCompanyEntity(\n em: EntityManager,\n entityId: string,\n tenantId: string,\n organizationId: string,\n): Promise<CustomerEntity> {\n const company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: 'company', tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!company) {\n throw notFound('Company not found')\n }\n return company\n}\n\nasync function requirePersonProfile(\n em: EntityManager,\n person: CustomerEntity,\n): Promise<CustomerPersonProfile> {\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) {\n throw notFound('Person profile not found')\n }\n return profile\n}\n\nasync function clearPrimaryFlagsForPerson(em: EntityManager, person: CustomerEntity): Promise<void> {\n await em.nativeUpdate(\n CustomerPersonCompanyLink,\n { person, organizationId: person.organizationId, tenantId: person.tenantId, isPrimary: true },\n { isPrimary: false },\n )\n}\n\nasync function restoreCreatePersonCompanyLink(\n ctx: CommandRuntimeContext,\n after: PersonCompanyLinkSnapshot,\n before: PersonCompanyLinkCreateBefore,\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = { tenantId: after.tenantId, organizationId: after.organizationId }\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: after.id, ...scope },\n undefined,\n scope,\n )\n if (!link) return\n const person = await requirePersonEntity(em, after.personEntityId, after.tenantId, after.organizationId)\n const profile = await requirePersonProfile(em, person)\n const profileCompanyId = typeof profile.company === 'string' ? profile.company : profile.company?.id ?? null\n const restorePrimaryAssignment = after.isPrimary && link.isPrimary && profileCompanyId === after.companyEntityId\n const company = restorePrimaryAssignment && before.profileCompanyId\n ? await requireCompanyEntity(em, before.profileCompanyId, after.tenantId, after.organizationId)\n : null\n const primaryLinkIds = before.primaryLinkIds.filter((id) => id !== link.id)\n\n await withAtomicFlush(em, [\n async () => {\n if (restorePrimaryAssignment) {\n await em.nativeUpdate(\n CustomerPersonCompanyLink,\n { person, ...scope, id: { $ne: link.id }, deletedAt: null, isPrimary: true },\n { isPrimary: false },\n )\n if (primaryLinkIds.length) {\n await em.nativeUpdate(\n CustomerPersonCompanyLink,\n { person, ...scope, id: { $in: primaryLinkIds }, deletedAt: null },\n { isPrimary: true },\n )\n }\n profile.company = company\n }\n link.isPrimary = before.link?.isPrimary ?? false\n link.deletedAt = before.link\n ? before.link.deletedAt ? new Date(before.link.deletedAt) : null\n : new Date()\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: link.deletedAt ? 'deleted' : 'updated',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n}\n\nconst createPersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkCreateInput, PersonCompanyLinkCreateResult> = {\n id: 'customers.personCompanyLinks.create',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const person = await requirePersonEntity(em, parsed.personEntityId, parsed.tenantId, parsed.organizationId)\n const company = await requireCompanyEntity(em, parsed.companyEntityId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const links = await loadPersonCompanyLinks(em, person)\n const link = links.find((entry) =>\n (typeof entry.company === 'string' ? entry.company : entry.company.id) === company.id,\n ) ?? await findDeletedPersonCompanyLink(em, person, company)\n return {\n before: {\n link: link ? await loadPersonCompanyLinkSnapshot(em, link.id, parsed) : null,\n primaryLinkIds: links.filter((entry) => entry.isPrimary).map((entry) => entry.id),\n profileCompanyId: typeof profile.company === 'string' ? profile.company : profile.company?.id ?? null,\n } satisfies PersonCompanyLinkCreateBefore,\n }\n },\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const person = await requirePersonEntity(em, parsed.personEntityId, parsed.tenantId, parsed.organizationId)\n const company = await requireCompanyEntity(em, parsed.companyEntityId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n\n const existingLinks = await loadPersonCompanyLinks(em, person)\n const makePrimary = Boolean(parsed.isPrimary) || existingLinks.length === 0\n const existingLive =\n existingLinks.find((link) => (typeof link.company === 'string' ? link.company : link.company.id) === company.id) ?? null\n // This state must come from execute's decision, not prepare's earlier read: another\n // request may have created the link between prepare and execute.\n const createBefore: PersonCompanyLinkCreateBefore = {\n link: existingLive ? snapshotPersonCompanyLink(existingLive) : null,\n primaryLinkIds: existingLinks.filter((link) => link.isPrimary).map((link) => link.id),\n profileCompanyId: typeof profile.company === 'string' ? profile.company : profile.company?.id ?? null,\n }\n\n if (existingLive) {\n const changed = makePrimary && !existingLive.isPrimary\n if (changed) {\n await withAtomicFlush(em, [\n () => clearPrimaryFlagsForPerson(em, person),\n () => {\n existingLive.isPrimary = true\n profile.company = company\n },\n ], { transaction: true })\n }\n return { linkId: existingLive.id, created: false, undeleted: false, changed, createBefore }\n }\n\n let link!: CustomerPersonCompanyLink\n let undeleted = false\n await withAtomicFlush(em, [\n async () => {\n const deletedLink = await findDeletedPersonCompanyLink(em, person, company)\n if (deletedLink) createBefore.link = snapshotPersonCompanyLink(deletedLink)\n if (makePrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n }\n if (deletedLink) {\n deletedLink.deletedAt = null\n deletedLink.isPrimary = makePrimary\n em.persist(deletedLink)\n link = deletedLink\n undeleted = true\n } else {\n link = em.create(CustomerPersonCompanyLink, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n person,\n company,\n isPrimary: makePrimary,\n })\n em.persist(link)\n }\n },\n () => {\n if (makePrimary) {\n profile.company = company\n } else if (!profile.company && existingLinks.length === 0) {\n profile.company = company\n link.isPrimary = true\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: undeleted ? 'updated' : 'created',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id, created: !undeleted, undeleted, changed: true, createBefore }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadPersonCompanyLinkSnapshot(em, result.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n },\n buildLog: async ({ result, snapshots }) => {\n const before = result.createBefore\n const after = snapshots.after as PersonCompanyLinkSnapshot | undefined\n if (!result.changed) {\n return { skipLog: true }\n }\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.create', 'Link company to person'),\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: after?.personEntityId ?? null,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotBefore: before?.link ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n after: after ?? null,\n ...(before ? { createBefore: before } : {}),\n ...(result.undeleted ? { before: null } : {}),\n } satisfies PersonCompanyLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n\n if (payload.createBefore) {\n await restoreCreatePersonCompanyLink(ctx, after, payload.createBefore)\n return\n }\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: after.id },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let remainingLinks: CustomerPersonCompanyLink[] = []\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: after.personEntityId, kind: 'person', tenantId: after.tenantId, organizationId: after.organizationId, deletedAt: null },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) return\n remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)\n },\n async () => {\n link.isPrimary = false\n link.deletedAt = new Date()\n if (!person || !profile) return\n if (after.isPrimary) {\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, after.companyEntityId)\n } else if (profile.company && typeof profile.company !== 'string' && profile.company.id === after.companyEntityId) {\n profile.company = null\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'deleted',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<PersonCompanyLinkSnapshot>(logEntry)\n if (!after) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for person-company link create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const person = await requirePersonEntity(em, after.personEntityId, after.tenantId, after.organizationId)\n const company = await requireCompanyEntity(em, after.companyEntityId, after.tenantId, after.organizationId)\n const profile = await requirePersonProfile(em, person)\n\n let link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: after.id },\n undefined,\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const existingLinks = await loadPersonCompanyLinks(em, person)\n const createBefore: PersonCompanyLinkCreateBefore = {\n link: link ? snapshotPersonCompanyLink(link) : null,\n primaryLinkIds: existingLinks.filter((entry) => entry.isPrimary).map((entry) => entry.id),\n profileCompanyId: typeof profile.company === 'string' ? profile.company : profile.company?.id ?? null,\n }\n\n await withAtomicFlush(em, [\n async () => {\n if (after.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n }\n if (!link) {\n link = em.create(CustomerPersonCompanyLink, {\n id: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n person,\n company,\n isPrimary: after.isPrimary,\n })\n em.persist(link)\n } else {\n link.deletedAt = null\n link.isPrimary = after.isPrimary\n em.persist(link)\n }\n },\n () => {\n if (after.isPrimary) {\n profile.company = company\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: link!,\n identifiers: getLinkIdentifiers(link!),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link!.id, created: true, undeleted: false, changed: true, createBefore }\n },\n}\n\nconst updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInput, { linkId: string }> = {\n id: 'customers.personCompanyLinks.update',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkUpdateSchema.parse(rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadPersonCompanyLinkSnapshot(em, parsed.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkUpdateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n {\n id: parsed.linkId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!link) {\n throw notFound('Company link not found')\n }\n\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n const person = await requirePersonEntity(em, personId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const linkedCompany = await requireCompanyEntity(em, companyId, parsed.tenantId, parsed.organizationId)\n\n const linkWasPrimary = link.isPrimary\n await withAtomicFlush(em, [\n async () => {\n if (parsed.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n profile.company = linkedCompany\n } else if (!parsed.isPrimary) {\n link.isPrimary = false\n if (!linkWasPrimary && profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {\n profile.company = null\n }\n }\n },\n async () => {\n if (!parsed.isPrimary && linkWasPrimary) {\n const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = ctx.container.resolve('em') as EntityManager\n return loadPersonCompanyLinkSnapshot(em, result.linkId, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as PersonCompanyLinkSnapshot | undefined\n const after = snapshots.after as PersonCompanyLinkSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.update', 'Update company link'),\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: after?.personEntityId ?? before?.personEntityId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies PersonCompanyLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: before.id },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile || !before.isPrimary) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n async () => {\n if (!person || !profile) return\n if (before.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n if (company) profile.company = company\n } else {\n link.isPrimary = false\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'updated',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n}\n\n/**\n * Detach a legacy profile-only company assignment: `customer_person_profiles.company_id`\n * points at the company but no `customer_person_company_links` row was ever created, so\n * there is no link row for the id-based branch to soft-delete (#5114). Running it as part\n * of the same command keeps the audit entry, the undo token and the CRUD cache\n * invalidation identical to a link-backed detach.\n */\nasync function detachProfileOnlyCompany(\n em: EntityManager,\n ctx: CommandRuntimeContext,\n parsed: PersonCompanyLinkDeleteInput,\n target: { personEntityId: string; companyEntityId: string },\n): Promise<PersonCompanyLinkDeleteResult> {\n const person = await requirePersonEntity(em, target.personEntityId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const assignedCompany = profile.company\n const assignedCompanyId = typeof assignedCompany === 'string' ? assignedCompany : assignedCompany?.id ?? null\n if (assignedCompanyId !== target.companyEntityId) {\n throw notFound('Company link not found')\n }\n\n await withAtomicFlush(em, [\n () => removePersonCompanyLink(em, person, profile, target.companyEntityId),\n ], { transaction: true })\n\n // There is no link entity to hand to `emitCrudSideEffects`; what changed is the\n // person's own company assignment, so reindex the person and its profile instead.\n await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile))\n await emitProfileOnlyDetachedEvent(ctx, person, target.companyEntityId)\n\n return { linkId: null, personEntityId: person.id, companyEntityId: target.companyEntityId }\n}\n\n/**\n * Live-refresh signal for the company People tab and the person Companies tab, which the\n * link-backed branch gets from `customers.person_company_link.deleted` via `emitCrudSideEffects`.\n * The profile-only branch has no link row to emit that for, so it publishes the sibling\n * `clientBroadcast` event instead (#5114). `tenantId` must travel in the payload: the event bus\n * only forwards a broadcast event cross-process when the payload carries a tenant scope. The\n * write is already committed, so a failed refresh signal must never fail the detach.\n */\nasync function emitProfileOnlyDetachedEvent(\n ctx: CommandRuntimeContext,\n person: CustomerEntity,\n companyEntityId: string,\n): Promise<void> {\n try {\n const bus = ctx.container.resolve<EventBus>('eventBus')\n await bus.emitEvent(\n PROFILE_ONLY_DETACHED_EVENT,\n {\n linkId: null,\n personEntityId: person.id,\n companyEntityId,\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n } catch (err) {\n logger.warn('Profile-only company detach broadcast failed', {\n command: 'customers.personCompanyLinks.delete',\n personEntityId: person.id,\n companyEntityId,\n err,\n })\n }\n}\n\n/**\n * Undo counterpart of `detachProfileOnlyCompany`: re-point the person's legacy\n * `profile.company` at the company it was detached from. The lookups run in the first\n * flush phase and the assignment in the second so the encryption subscriber's\n * re-baseline on find cannot drop the pending change (#2507).\n */\nasync function restoreProfileOnlyCompany(\n ctx: CommandRuntimeContext,\n before: PersonCompanyProfileAssignmentSnapshot,\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!profile) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n () => {\n if (profile && company) profile.company = company\n },\n ], { transaction: true })\n\n if (person && profile) {\n await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile))\n }\n}\n\nconst deletePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkDeleteInput, PersonCompanyLinkDeleteResult> = {\n id: 'customers.personCompanyLinks.delete',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkDeleteSchema.parse(rawInput)\n const profileOnlyTarget = resolveProfileOnlyTarget(parsed)\n if (profileOnlyTarget) {\n return {\n before: {\n kind: 'profile-only',\n personEntityId: profileOnlyTarget.personEntityId,\n companyEntityId: profileOnlyTarget.companyEntityId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n } satisfies PersonCompanyProfileAssignmentSnapshot,\n }\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadPersonCompanyLinkSnapshot(em, parsed.linkId as string, {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n })\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = personCompanyLinkDeleteSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const profileOnlyTarget = resolveProfileOnlyTarget(parsed)\n if (profileOnlyTarget) {\n return await detachProfileOnlyCompany(em, ctx, parsed, profileOnlyTarget)\n }\n\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n {\n id: parsed.linkId as string,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!link) {\n throw notFound('Company link not found')\n }\n\n const personId = typeof link.person === 'string' ? link.person : link.person.id\n const companyId = typeof link.company === 'string' ? link.company : link.company.id\n const person = await requirePersonEntity(em, personId, parsed.tenantId, parsed.organizationId)\n const profile = await requirePersonProfile(em, person)\n const linkWasPrimary = link.isPrimary\n\n const existingLinks = await loadPersonCompanyLinks(em, person)\n const remainingLinks = existingLinks.filter((entry) => entry.id !== link.id)\n\n await withAtomicFlush(em, [\n () => {\n link.isPrimary = false\n link.deletedAt = new Date()\n },\n async () => {\n if (linkWasPrimary) {\n await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)\n } else if (profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {\n const primary = remainingLinks.find((entry) => entry.isPrimary) ?? null\n const primaryCompany = primary && typeof primary.company !== 'string' ? primary.company : null\n if (primaryCompany) {\n profile.company = primaryCompany\n }\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n\n return { linkId: link.id, personEntityId: personId, companyEntityId: companyId }\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as PersonCompanyLinkDeleteSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.delete', 'Remove company link'),\n // Kept as `customers.personCompanyLink` for the profile-only shape too: the company\n // detail cache tags this resource kind, so both detach shapes must invalidate it.\n resourceKind: 'customers.personCompanyLink',\n resourceId: result.linkId,\n parentResourceKind: 'customers.person',\n parentResourceId: before?.personEntityId ?? null,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies PersonCompanyLinkDeleteUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PersonCompanyLinkDeleteUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n\n if (isProfileAssignmentSnapshot(before)) {\n await restoreProfileOnlyCompany(ctx, before)\n return\n }\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n { id: before.id },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!link) return\n\n let person: CustomerEntity | null = null\n let profile: CustomerPersonProfile | null = null\n let company: CustomerEntity | null = null\n\n await withAtomicFlush(em, [\n async () => {\n person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.personEntityId, kind: 'person', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!person || !before.isPrimary) return\n profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person },\n { populate: ['company'] },\n { tenantId: person.tenantId, organizationId: person.organizationId },\n )\n if (!profile) return\n company = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: before.companyEntityId, kind: 'company', tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },\n undefined,\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n },\n async () => {\n link.deletedAt = null\n link.isPrimary = before.isPrimary\n if (person && before.isPrimary) {\n await clearPrimaryFlagsForPerson(em, person)\n link.isPrimary = true\n if (profile && company) profile.company = company\n }\n },\n ], { transaction: true })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action: 'created',\n entity: link,\n identifiers: getLinkIdentifiers(link),\n syncOrigin: ctx.syncOrigin,\n events: personCompanyLinkCrudEvents,\n indexer: { entityType: 'customers:customer_person_company_link' },\n })\n },\n}\n\nregisterCommand(createPersonCompanyLinkCommand)\nregisterCommand(updatePersonCompanyLinkCommand)\nregisterCommand(deletePersonCompanyLinkCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["link"]
|
|
7
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.7976.1.c0ee1c7c28",
|
|
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.7976.1.c0ee1c7c28",
|
|
270
|
+
"@open-mercato/shared": "0.10.1-develop.7976.1.c0ee1c7c28",
|
|
271
|
+
"@open-mercato/ui": "0.10.1-develop.7976.1.c0ee1c7c28",
|
|
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.7976.1.c0ee1c7c28",
|
|
277
|
+
"@open-mercato/shared": "0.10.1-develop.7976.1.c0ee1c7c28",
|
|
278
|
+
"@open-mercato/ui": "0.10.1-develop.7976.1.c0ee1c7c28",
|
|
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",
|
|
@@ -338,7 +338,11 @@ export async function PUT(req: Request) {
|
|
|
338
338
|
|
|
339
339
|
if (!actorIsSuperAdmin) {
|
|
340
340
|
if (requestedIsSuperAdmin && !existingIsSuperAdmin) {
|
|
341
|
-
|
|
341
|
+
const { translate } = await resolveTranslations()
|
|
342
|
+
throw forbidden(translate(
|
|
343
|
+
'auth.acl.errors.superAdminGrantDenied',
|
|
344
|
+
'Only super administrators can grant super admin access.',
|
|
345
|
+
))
|
|
342
346
|
}
|
|
343
347
|
if (existingIsSuperAdmin && requestedIsSuperAdmin === false) {
|
|
344
348
|
effectiveIsSuperAdmin = false
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
"auth.acl.deps.orphaned.restore": "\"{dependency}\" wiederherstellen",
|
|
14
14
|
"auth.acl.deps.orphaned.title": "Entfernung einer Berechtigung, die andere gewährte Berechtigungen benötigen:",
|
|
15
15
|
"auth.acl.deps.unknown.title": "Einige deklarierte Abhängigkeiten passen zu keiner bekannten Berechtigung:",
|
|
16
|
+
"auth.acl.errors.aclGrantTenantRequired": "Zum Vergeben von ACL-Berechtigungen ist ein Mandantenkontext erforderlich.",
|
|
17
|
+
"auth.acl.errors.actorRequired": "Keine Berechtigung zum Vergeben von ACL-Rechten.",
|
|
18
|
+
"auth.acl.errors.featureGrantDenied": "Die Berechtigung {feature} kann nicht vergeben werden.",
|
|
19
|
+
"auth.acl.errors.featureWildcardGrantDenied": "Die Platzhalter-Berechtigung {feature} kann nicht vergeben werden.",
|
|
20
|
+
"auth.acl.errors.globalWildcardGrantDenied": "Nur Superadministratoren können globalen Platzhalterzugriff vergeben.",
|
|
21
|
+
"auth.acl.errors.organizationOutsideScope": "Zugriff auf Organisationen außerhalb Ihres Zugriffsbereichs kann nicht vergeben werden.",
|
|
22
|
+
"auth.acl.errors.roleGrantTenantRequired": "Zum Vergeben von Rollen ist ein Mandantenkontext erforderlich.",
|
|
23
|
+
"auth.acl.errors.roleOutsideTargetTenant": "Eine Rolle außerhalb des Zielmandanten kann nicht vergeben werden.",
|
|
24
|
+
"auth.acl.errors.superAdminGrantDenied": "Nur Superadministratoren können Superadministrator-Zugriff vergeben.",
|
|
25
|
+
"auth.acl.errors.unrestrictedOrganizationsGrantDenied": "Uneingeschränkter Zugriff auf Organisationen kann nicht vergeben werden.",
|
|
16
26
|
"auth.acl.features.agent_orchestrator.agents.manage": "Manage agent presentation settings",
|
|
17
27
|
"auth.acl.features.agent_orchestrator.agents.run": "Run agents directly (Playground, evals, diagnostics)",
|
|
18
28
|
"auth.acl.features.agent_orchestrator.agents.view": "View agents and runs",
|
|
@@ -615,6 +625,9 @@
|
|
|
615
625
|
"auth.roles.admin": "Administrator",
|
|
616
626
|
"auth.roles.employee": "Mitarbeitende",
|
|
617
627
|
"auth.roles.errors.nameExists": "Eine Rolle mit diesem Namen existiert bereits",
|
|
628
|
+
"auth.roles.errors.notFound": "Rolle nicht gefunden",
|
|
629
|
+
"auth.roles.errors.rolesNotFound": "Rolle(n) nicht gefunden: {roles}",
|
|
630
|
+
"auth.roles.errors.superAdminTargetProtected": "Nur Superadministratoren können Superadministrator-Rollen ändern.",
|
|
618
631
|
"auth.roles.flash.created": "Rolle erstellt",
|
|
619
632
|
"auth.roles.flash.deleted": "Rolle gelöscht",
|
|
620
633
|
"auth.roles.flash.updated": "Rolle gespeichert",
|
|
@@ -667,8 +680,11 @@
|
|
|
667
680
|
"auth.users.errors.emailExists": "E-Mail-Adresse wird bereits verwendet",
|
|
668
681
|
"auth.users.errors.invalidRoleAssignment": "Der Benutzer hat eine ungültige Rollenzuweisung.",
|
|
669
682
|
"auth.users.errors.lastHolderOfCriticalRole": "Der letzte aktive Inhaber der Rolle \"{roleName}\" kann nicht entfernt werden",
|
|
683
|
+
"auth.users.errors.notFound": "Benutzer nicht gefunden",
|
|
670
684
|
"auth.users.errors.organizationNotFound": "Organisation nicht gefunden",
|
|
671
685
|
"auth.users.errors.roleOutsideDestinationTenant": "Eine Rolle außerhalb des Zielmandanten kann weder beibehalten noch zugewiesen werden.",
|
|
686
|
+
"auth.users.errors.superAdminTargetProtected": "Nur Superadministratoren können Superadministrator-Konten ändern.",
|
|
687
|
+
"auth.users.errors.targetAccessDenied": "Keine Berechtigung für den Zugriff auf diesen Benutzer.",
|
|
672
688
|
"auth.users.flash.created": "Benutzer erstellt",
|
|
673
689
|
"auth.users.flash.createdEmailFailed": "Benutzer erstellt, aber die Einladungs-E-Mail konnte nicht gesendet werden. Sie können sie über die Benutzerseite erneut senden.",
|
|
674
690
|
"auth.users.flash.createdWithInvite": "Benutzer erstellt und Einladung gesendet",
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
"auth.acl.deps.orphaned.restore": "Restore \"{dependency}\"",
|
|
14
14
|
"auth.acl.deps.orphaned.title": "Removing a permission that other granted permissions need:",
|
|
15
15
|
"auth.acl.deps.unknown.title": "Some declared dependencies do not match any known permission:",
|
|
16
|
+
"auth.acl.errors.aclGrantTenantRequired": "Tenant context is required to grant ACL features.",
|
|
17
|
+
"auth.acl.errors.actorRequired": "Not authorized to grant ACL privileges.",
|
|
18
|
+
"auth.acl.errors.featureGrantDenied": "Cannot grant feature {feature}.",
|
|
19
|
+
"auth.acl.errors.featureWildcardGrantDenied": "Cannot grant feature wildcard {feature}.",
|
|
20
|
+
"auth.acl.errors.globalWildcardGrantDenied": "Only super administrators can grant global wildcard access.",
|
|
21
|
+
"auth.acl.errors.organizationOutsideScope": "Cannot grant organization access outside actor scope.",
|
|
22
|
+
"auth.acl.errors.roleGrantTenantRequired": "Tenant context is required to grant roles.",
|
|
23
|
+
"auth.acl.errors.roleOutsideTargetTenant": "Cannot grant a role outside the target tenant.",
|
|
24
|
+
"auth.acl.errors.superAdminGrantDenied": "Only super administrators can grant super admin access.",
|
|
25
|
+
"auth.acl.errors.unrestrictedOrganizationsGrantDenied": "Cannot grant unrestricted organization access.",
|
|
16
26
|
"auth.acl.features.agent_orchestrator.agents.manage": "Manage agent presentation settings",
|
|
17
27
|
"auth.acl.features.agent_orchestrator.agents.run": "Run agents directly (Playground, evals, diagnostics)",
|
|
18
28
|
"auth.acl.features.agent_orchestrator.agents.view": "View agents and runs",
|
|
@@ -615,6 +625,9 @@
|
|
|
615
625
|
"auth.roles.admin": "Admin",
|
|
616
626
|
"auth.roles.employee": "Employee",
|
|
617
627
|
"auth.roles.errors.nameExists": "A role with this name already exists",
|
|
628
|
+
"auth.roles.errors.notFound": "Role not found",
|
|
629
|
+
"auth.roles.errors.rolesNotFound": "Role(s) not found: {roles}",
|
|
630
|
+
"auth.roles.errors.superAdminTargetProtected": "Only super administrators can modify super administrator roles.",
|
|
618
631
|
"auth.roles.flash.created": "Role created",
|
|
619
632
|
"auth.roles.flash.deleted": "Role deleted",
|
|
620
633
|
"auth.roles.flash.updated": "Role saved",
|
|
@@ -667,8 +680,11 @@
|
|
|
667
680
|
"auth.users.errors.emailExists": "Email already in use",
|
|
668
681
|
"auth.users.errors.invalidRoleAssignment": "User has an invalid role assignment",
|
|
669
682
|
"auth.users.errors.lastHolderOfCriticalRole": "Cannot remove the last active holder of role \"{roleName}\"",
|
|
683
|
+
"auth.users.errors.notFound": "User not found",
|
|
670
684
|
"auth.users.errors.organizationNotFound": "Organization not found",
|
|
671
685
|
"auth.users.errors.roleOutsideDestinationTenant": "Cannot retain or assign a role outside the destination tenant.",
|
|
686
|
+
"auth.users.errors.superAdminTargetProtected": "Only super administrators can modify super administrator accounts.",
|
|
687
|
+
"auth.users.errors.targetAccessDenied": "Not authorized to access this user.",
|
|
672
688
|
"auth.users.flash.created": "User created",
|
|
673
689
|
"auth.users.flash.createdEmailFailed": "User created but invitation email could not be sent. You can resend it from the user page.",
|
|
674
690
|
"auth.users.flash.createdWithInvite": "User created and invitation sent",
|