@open-mercato/core 0.6.8-develop.7012.1.1dbd6f5fbd → 0.6.8-develop.7015.1.af90a2ddc7
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/customers/api/companies/[id]/people/route.js +19 -63
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +9 -85
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/data/validators.js +10 -2
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +5 -0
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +100 -2
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +79 -3
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/api/companies/[id]/people/route.ts +20 -72
- package/src/modules/customers/api/companies/[id]/route.ts +11 -97
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
- package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
- package/src/modules/customers/data/validators.ts +18 -3
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/lib/personCompanies.ts +136 -1
- package/src/modules/query_index/lib/search-tokens.ts +135 -3
|
@@ -16,15 +16,45 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
findDeletedPersonCompanyLink,
|
|
18
18
|
loadPersonCompanyLinks,
|
|
19
|
-
promoteFallbackPrimaryLink
|
|
19
|
+
promoteFallbackPrimaryLink,
|
|
20
|
+
removePersonCompanyLink
|
|
20
21
|
} from "../lib/personCompanies.js";
|
|
21
22
|
import {
|
|
23
|
+
emitQueryIndexUpsertEvents,
|
|
22
24
|
ensureOrganizationScope,
|
|
23
25
|
ensureTenantScope,
|
|
24
26
|
extractUndoPayload
|
|
25
27
|
} from "./shared.js";
|
|
28
|
+
import { E } from "../../../generated/entities.ids.generated.js";
|
|
26
29
|
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
27
30
|
import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
|
|
31
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
32
|
+
const logger = createLogger("customers").child({ component: "personCompanyLinks" });
|
|
33
|
+
const PROFILE_ONLY_DETACHED_EVENT = "customers.person.company_assignment.detached";
|
|
34
|
+
function isProfileAssignmentSnapshot(snapshot) {
|
|
35
|
+
return Boolean(snapshot) && snapshot.kind === "profile-only";
|
|
36
|
+
}
|
|
37
|
+
function resolveProfileOnlyTarget(parsed) {
|
|
38
|
+
if (parsed.linkId) return null;
|
|
39
|
+
if (!parsed.personEntityId || !parsed.companyEntityId) return null;
|
|
40
|
+
return { personEntityId: parsed.personEntityId, companyEntityId: parsed.companyEntityId };
|
|
41
|
+
}
|
|
42
|
+
function personQueryIndexEntries(person, profile) {
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
entityType: E.customers.customer_entity,
|
|
46
|
+
recordId: person.id,
|
|
47
|
+
tenantId: person.tenantId,
|
|
48
|
+
organizationId: person.organizationId
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
entityType: E.customers.customer_person_profile,
|
|
52
|
+
recordId: profile.id,
|
|
53
|
+
tenantId: profile.tenantId,
|
|
54
|
+
organizationId: profile.organizationId
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
}
|
|
28
58
|
const personCompanyLinkCrudEvents = {
|
|
29
59
|
module: "customers",
|
|
30
60
|
entity: "person_company_link",
|
|
@@ -495,10 +525,99 @@ const updatePersonCompanyLinkCommand = {
|
|
|
495
525
|
});
|
|
496
526
|
}
|
|
497
527
|
};
|
|
528
|
+
async function detachProfileOnlyCompany(em, ctx, parsed, target) {
|
|
529
|
+
const person = await requirePersonEntity(em, target.personEntityId, parsed.tenantId, parsed.organizationId);
|
|
530
|
+
const profile = await requirePersonProfile(em, person);
|
|
531
|
+
const assignedCompany = profile.company;
|
|
532
|
+
const assignedCompanyId = typeof assignedCompany === "string" ? assignedCompany : assignedCompany?.id ?? null;
|
|
533
|
+
if (assignedCompanyId !== target.companyEntityId) {
|
|
534
|
+
throw notFound("Company link not found");
|
|
535
|
+
}
|
|
536
|
+
await withAtomicFlush(em, [
|
|
537
|
+
() => removePersonCompanyLink(em, person, profile, target.companyEntityId)
|
|
538
|
+
], { transaction: true });
|
|
539
|
+
await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile));
|
|
540
|
+
await emitProfileOnlyDetachedEvent(ctx, person, target.companyEntityId);
|
|
541
|
+
return { linkId: null, personEntityId: person.id, companyEntityId: target.companyEntityId };
|
|
542
|
+
}
|
|
543
|
+
async function emitProfileOnlyDetachedEvent(ctx, person, companyEntityId) {
|
|
544
|
+
try {
|
|
545
|
+
const bus = ctx.container.resolve("eventBus");
|
|
546
|
+
await bus.emitEvent(
|
|
547
|
+
PROFILE_ONLY_DETACHED_EVENT,
|
|
548
|
+
{
|
|
549
|
+
linkId: null,
|
|
550
|
+
personEntityId: person.id,
|
|
551
|
+
companyEntityId,
|
|
552
|
+
tenantId: person.tenantId,
|
|
553
|
+
organizationId: person.organizationId
|
|
554
|
+
},
|
|
555
|
+
{ tenantId: person.tenantId, organizationId: person.organizationId }
|
|
556
|
+
);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
logger.warn("Profile-only company detach broadcast failed", {
|
|
559
|
+
command: "customers.personCompanyLinks.delete",
|
|
560
|
+
personEntityId: person.id,
|
|
561
|
+
companyEntityId,
|
|
562
|
+
err
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function restoreProfileOnlyCompany(ctx, before) {
|
|
567
|
+
const em = ctx.container.resolve("em").fork();
|
|
568
|
+
let person = null;
|
|
569
|
+
let profile = null;
|
|
570
|
+
let company = null;
|
|
571
|
+
await withAtomicFlush(em, [
|
|
572
|
+
async () => {
|
|
573
|
+
person = await findOneWithDecryption(
|
|
574
|
+
em,
|
|
575
|
+
CustomerEntity,
|
|
576
|
+
{ id: before.personEntityId, kind: "person", tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },
|
|
577
|
+
void 0,
|
|
578
|
+
{ tenantId: before.tenantId, organizationId: before.organizationId }
|
|
579
|
+
);
|
|
580
|
+
if (!person) return;
|
|
581
|
+
profile = await findOneWithDecryption(
|
|
582
|
+
em,
|
|
583
|
+
CustomerPersonProfile,
|
|
584
|
+
{ entity: person },
|
|
585
|
+
{ populate: ["company"] },
|
|
586
|
+
{ tenantId: before.tenantId, organizationId: before.organizationId }
|
|
587
|
+
);
|
|
588
|
+
if (!profile) return;
|
|
589
|
+
company = await findOneWithDecryption(
|
|
590
|
+
em,
|
|
591
|
+
CustomerEntity,
|
|
592
|
+
{ id: before.companyEntityId, kind: "company", tenantId: before.tenantId, organizationId: before.organizationId, deletedAt: null },
|
|
593
|
+
void 0,
|
|
594
|
+
{ tenantId: before.tenantId, organizationId: before.organizationId }
|
|
595
|
+
);
|
|
596
|
+
},
|
|
597
|
+
() => {
|
|
598
|
+
if (profile && company) profile.company = company;
|
|
599
|
+
}
|
|
600
|
+
], { transaction: true });
|
|
601
|
+
if (person && profile) {
|
|
602
|
+
await emitQueryIndexUpsertEvents(ctx, personQueryIndexEntries(person, profile));
|
|
603
|
+
}
|
|
604
|
+
}
|
|
498
605
|
const deletePersonCompanyLinkCommand = {
|
|
499
606
|
id: "customers.personCompanyLinks.delete",
|
|
500
607
|
async prepare(rawInput, ctx) {
|
|
501
608
|
const parsed = personCompanyLinkDeleteSchema.parse(rawInput);
|
|
609
|
+
const profileOnlyTarget = resolveProfileOnlyTarget(parsed);
|
|
610
|
+
if (profileOnlyTarget) {
|
|
611
|
+
return {
|
|
612
|
+
before: {
|
|
613
|
+
kind: "profile-only",
|
|
614
|
+
personEntityId: profileOnlyTarget.personEntityId,
|
|
615
|
+
companyEntityId: profileOnlyTarget.companyEntityId,
|
|
616
|
+
tenantId: parsed.tenantId,
|
|
617
|
+
organizationId: parsed.organizationId
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
502
621
|
const em = ctx.container.resolve("em").fork();
|
|
503
622
|
const snapshot = await loadPersonCompanyLinkSnapshot(em, parsed.linkId, {
|
|
504
623
|
tenantId: ctx.auth?.tenantId ?? null,
|
|
@@ -511,6 +630,10 @@ const deletePersonCompanyLinkCommand = {
|
|
|
511
630
|
ensureTenantScope(ctx, parsed.tenantId);
|
|
512
631
|
ensureOrganizationScope(ctx, parsed.organizationId);
|
|
513
632
|
const em = ctx.container.resolve("em").fork();
|
|
633
|
+
const profileOnlyTarget = resolveProfileOnlyTarget(parsed);
|
|
634
|
+
if (profileOnlyTarget) {
|
|
635
|
+
return await detachProfileOnlyCompany(em, ctx, parsed, profileOnlyTarget);
|
|
636
|
+
}
|
|
514
637
|
const link = await findOneWithDecryption(
|
|
515
638
|
em,
|
|
516
639
|
CustomerPersonCompanyLink,
|
|
@@ -560,13 +683,15 @@ const deletePersonCompanyLinkCommand = {
|
|
|
560
683
|
events: personCompanyLinkCrudEvents,
|
|
561
684
|
indexer: { entityType: "customers:customer_person_company_link" }
|
|
562
685
|
});
|
|
563
|
-
return { linkId: link.id };
|
|
686
|
+
return { linkId: link.id, personEntityId: personId, companyEntityId: companyId };
|
|
564
687
|
},
|
|
565
688
|
buildLog: async ({ result, snapshots }) => {
|
|
566
689
|
const { translate } = await resolveTranslations();
|
|
567
690
|
const before = snapshots.before;
|
|
568
691
|
return {
|
|
569
692
|
actionLabel: translate("customers.audit.personCompanyLinks.delete", "Remove company link"),
|
|
693
|
+
// Kept as `customers.personCompanyLink` for the profile-only shape too: the company
|
|
694
|
+
// detail cache tags this resource kind, so both detach shapes must invalidate it.
|
|
570
695
|
resourceKind: "customers.personCompanyLink",
|
|
571
696
|
resourceId: result.linkId,
|
|
572
697
|
parentResourceKind: "customers.person",
|
|
@@ -585,6 +710,10 @@ const deletePersonCompanyLinkCommand = {
|
|
|
585
710
|
const payload = extractUndoPayload(logEntry);
|
|
586
711
|
const before = payload?.before;
|
|
587
712
|
if (!before) return;
|
|
713
|
+
if (isProfileAssignmentSnapshot(before)) {
|
|
714
|
+
await restoreProfileOnlyCompany(ctx, before);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
588
717
|
const em = ctx.container.resolve("em").fork();
|
|
589
718
|
const link = await findOneWithDecryption(
|
|
590
719
|
em,
|
|
@@ -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 } 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} from '../lib/personCompanies'\nimport {\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n} from './shared'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\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\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\nconst deletePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkDeleteInput, { linkId: string }> = {\n id: 'customers.personCompanyLinks.delete',\n async prepare(rawInput, ctx) {\n const parsed = personCompanyLinkDeleteSchema.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 = 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 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 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 }\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as PersonCompanyLinkSnapshot | undefined\n return {\n actionLabel: translate('customers.audit.personCompanyLinks.delete', 'Remove company link'),\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 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 || !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}\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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",
|
|
6
6
|
"names": ["link"]
|
|
7
7
|
}
|
|
@@ -202,12 +202,14 @@ function CompanyPeopleSection({
|
|
|
202
202
|
React.useEffect(() => {
|
|
203
203
|
void loadVisiblePeople();
|
|
204
204
|
}, [loadVisiblePeople]);
|
|
205
|
-
|
|
205
|
+
const reloadOnCompanyDetach = React.useCallback((event) => {
|
|
206
206
|
const payload = event.payload;
|
|
207
207
|
if (payload && payload.companyEntityId === companyId) {
|
|
208
208
|
void loadVisiblePeople();
|
|
209
209
|
}
|
|
210
210
|
}, [companyId, loadVisiblePeople]);
|
|
211
|
+
useAppEvent("customers.person_company_link.deleted", reloadOnCompanyDetach, [reloadOnCompanyDetach]);
|
|
212
|
+
useAppEvent("customers.person.company_assignment.detached", reloadOnCompanyDetach, [reloadOnCompanyDetach]);
|
|
211
213
|
React.useEffect(() => {
|
|
212
214
|
setListPage(1);
|
|
213
215
|
}, [searchQuery, sortMode]);
|