@open-mercato/core 0.6.6-develop.6458.1.113a54fb91 → 0.6.6-develop.6465.1.019f0fb26f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/auth.js +148 -82
  3. package/dist/helpers/integration/auth.js.map +2 -2
  4. package/dist/modules/query_index/api/status.js +21 -1
  5. package/dist/modules/query_index/api/status.js.map +2 -2
  6. package/dist/modules/sales/api/document-addresses/route.js +96 -6
  7. package/dist/modules/sales/api/document-addresses/route.js.map +2 -2
  8. package/dist/modules/sales/commands/documentAddresses.js +7 -0
  9. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  10. package/dist/modules/staff/commands/activities.js +23 -12
  11. package/dist/modules/staff/commands/activities.js.map +2 -2
  12. package/dist/modules/staff/commands/addresses.js +20 -12
  13. package/dist/modules/staff/commands/addresses.js.map +2 -2
  14. package/dist/modules/staff/commands/comments.js +18 -11
  15. package/dist/modules/staff/commands/comments.js.map +2 -2
  16. package/dist/modules/staff/commands/job-histories.js +23 -12
  17. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  18. package/dist/modules/staff/commands/leave-requests.js +37 -19
  19. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  20. package/dist/modules/staff/commands/shared.js +28 -1
  21. package/dist/modules/staff/commands/shared.js.map +2 -2
  22. package/dist/modules/staff/commands/tag-assignments.js +20 -3
  23. package/dist/modules/staff/commands/tag-assignments.js.map +2 -2
  24. package/dist/modules/staff/commands/team-members.js +21 -11
  25. package/dist/modules/staff/commands/team-members.js.map +2 -2
  26. package/dist/modules/staff/commands/team-roles.js +21 -11
  27. package/dist/modules/staff/commands/team-roles.js.map +2 -2
  28. package/dist/modules/staff/commands/teams.js +21 -11
  29. package/dist/modules/staff/commands/teams.js.map +2 -2
  30. package/dist/modules/staff/commands/timesheets-entries.js +24 -14
  31. package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
  32. package/dist/modules/staff/commands/timesheets-projects.js +34 -18
  33. package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
  34. package/dist/modules/workflows/api/instances/route.js +1 -1
  35. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  36. package/package.json +7 -7
  37. package/src/helpers/integration/auth.ts +178 -86
  38. package/src/modules/query_index/api/status.ts +32 -4
  39. package/src/modules/sales/api/document-addresses/route.ts +140 -7
  40. package/src/modules/sales/commands/documentAddresses.ts +8 -0
  41. package/src/modules/staff/commands/activities.ts +23 -11
  42. package/src/modules/staff/commands/addresses.ts +20 -11
  43. package/src/modules/staff/commands/comments.ts +18 -10
  44. package/src/modules/staff/commands/job-histories.ts +27 -11
  45. package/src/modules/staff/commands/leave-requests.ts +38 -19
  46. package/src/modules/staff/commands/shared.ts +38 -0
  47. package/src/modules/staff/commands/tag-assignments.ts +19 -2
  48. package/src/modules/staff/commands/team-members.ts +21 -10
  49. package/src/modules/staff/commands/team-roles.ts +21 -10
  50. package/src/modules/staff/commands/teams.ts +21 -10
  51. package/src/modules/staff/commands/timesheets-entries.ts +24 -13
  52. package/src/modules/staff/commands/timesheets-projects.ts +34 -17
  53. package/src/modules/workflows/api/instances/route.ts +2 -2
@@ -2,9 +2,10 @@ import { z } from "zod";
2
2
  import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
3
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
4
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
5
6
  import { createSalesCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from "../openapi.js";
6
7
  import { withScopedPayload } from "../utils.js";
7
- import { SalesDocumentAddress } from "../../data/entities.js";
8
+ import { SalesDocumentAddress, SalesOrder, SalesQuote } from "../../data/entities.js";
8
9
  import {
9
10
  documentAddressCreateSchema,
10
11
  documentAddressDeleteSchema,
@@ -27,8 +28,90 @@ const routeMetadata = {
27
28
  DELETE: { requireAuth: true }
28
29
  };
29
30
  const metadata = routeMetadata;
31
+ const DOCUMENT_ADDRESS_FEATURES = {
32
+ order: { view: "sales.orders.view", manage: "sales.orders.manage" },
33
+ quote: { view: "sales.quotes.view", manage: "sales.quotes.manage" }
34
+ };
35
+ function toDocumentKind(value) {
36
+ return value === "order" || value === "quote" ? value : null;
37
+ }
38
+ async function resolveDocumentKindForRead(query, ctx) {
39
+ if (query.documentKind) return query.documentKind;
40
+ const tenantId = ctx.auth?.tenantId ?? null;
41
+ const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? (Array.isArray(ctx.organizationIds) && ctx.organizationIds.length === 1 ? ctx.organizationIds[0] : null);
42
+ if (!query.documentId || !tenantId || !organizationId) return null;
43
+ const em = ctx.container.resolve("em");
44
+ const scope = { tenantId, organizationId };
45
+ const order = await findOneWithDecryption(em, SalesOrder, { id: query.documentId, tenantId, organizationId }, {}, scope);
46
+ if (order) return "order";
47
+ const quote = await findOneWithDecryption(em, SalesQuote, { id: query.documentId, tenantId, organizationId }, {}, scope);
48
+ if (quote) return "quote";
49
+ return null;
50
+ }
51
+ async function ensureDocumentAddressReadAccess(query, ctx) {
52
+ const documentKind = await resolveDocumentKindForRead(query, ctx);
53
+ const requiredFeatures = documentKind ? [DOCUMENT_ADDRESS_FEATURES[documentKind].view] : [DOCUMENT_ADDRESS_FEATURES.order.view, DOCUMENT_ADDRESS_FEATURES.quote.view];
54
+ await ensureDocumentAddressAccess(ctx, requiredFeatures);
55
+ return documentKind;
56
+ }
57
+ async function resolveExistingAddressKind(ctx, id, scope) {
58
+ const em = ctx.container.resolve("em");
59
+ const address = await findOneWithDecryption(
60
+ em,
61
+ SalesDocumentAddress,
62
+ { id, tenantId: scope.tenantId, organizationId: scope.organizationId },
63
+ {},
64
+ scope
65
+ );
66
+ return toDocumentKind(address?.documentKind);
67
+ }
68
+ async function ensureDocumentAddressAccess(ctx, requiredFeatures, translate) {
69
+ const resolvedTranslate = translate ?? (await resolveTranslations()).translate;
70
+ const auth = ctx.auth;
71
+ if (!auth?.sub) {
72
+ throw new CrudHttpError(401, { error: resolvedTranslate("api.errors.unauthorized", "Unauthorized") });
73
+ }
74
+ let rbac = null;
75
+ try {
76
+ rbac = ctx.container.resolve("rbacService");
77
+ } catch {
78
+ rbac = null;
79
+ }
80
+ const ok = rbac ? await rbac.userHasAllFeatures(auth.sub, requiredFeatures, {
81
+ tenantId: auth.tenantId ?? null,
82
+ organizationId: ctx.selectedOrganizationId ?? auth.orgId ?? null
83
+ }) : false;
84
+ if (!ok) {
85
+ throw new CrudHttpError(403, {
86
+ error: resolvedTranslate("api.errors.forbidden", "Forbidden"),
87
+ requiredFeatures
88
+ });
89
+ }
90
+ }
91
+ async function ensureDocumentAddressKindAccess(ctx, documentKind, access, translate) {
92
+ await ensureDocumentAddressAccess(ctx, [DOCUMENT_ADDRESS_FEATURES[documentKind][access]], translate);
93
+ }
94
+ async function ensureDocumentAddressMutationAccess(ctx, input, options) {
95
+ const existingKind = await resolveExistingAddressKind(ctx, input.id, {
96
+ tenantId: input.tenantId,
97
+ organizationId: input.organizationId
98
+ });
99
+ const featureKinds = /* @__PURE__ */ new Set();
100
+ if (existingKind) featureKinds.add(existingKind);
101
+ if (!existingKind || options.includeTargetKind) featureKinds.add(input.documentKind);
102
+ await ensureDocumentAddressAccess(
103
+ ctx,
104
+ [...featureKinds].map((kind) => DOCUMENT_ADDRESS_FEATURES[kind].manage),
105
+ options.translate
106
+ );
107
+ }
30
108
  const crud = makeCrudRoute({
31
109
  metadata: routeMetadata,
110
+ hooks: {
111
+ beforeList: async (query, ctx) => {
112
+ await ensureDocumentAddressReadAccess(query, ctx);
113
+ }
114
+ },
32
115
  orm: {
33
116
  entity: SalesDocumentAddress,
34
117
  idField: "id",
@@ -69,11 +152,12 @@ const crud = makeCrudRoute({
69
152
  createdAt: "created_at",
70
153
  updatedAt: "updated_at"
71
154
  },
72
- buildFilters: async (query) => {
155
+ buildFilters: async (query, ctx) => {
156
+ const documentKind = await resolveDocumentKindForRead(query, ctx);
73
157
  const filters = {
74
158
  document_id: { $eq: query.documentId }
75
159
  };
76
- if (query.documentKind) filters.document_kind = { $eq: query.documentKind };
160
+ if (query.documentKind ?? documentKind) filters.document_kind = { $eq: query.documentKind ?? documentKind };
77
161
  return filters;
78
162
  }
79
163
  },
@@ -83,7 +167,9 @@ const crud = makeCrudRoute({
83
167
  schema: rawBodySchema,
84
168
  mapInput: async ({ raw, ctx }) => {
85
169
  const { translate } = await resolveTranslations();
86
- return documentAddressCreateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate));
170
+ const input = documentAddressCreateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate));
171
+ await ensureDocumentAddressKindAccess(ctx, input.documentKind, "manage", translate);
172
+ return input;
87
173
  },
88
174
  response: ({ result }) => ({ id: result?.id ?? null }),
89
175
  status: 201
@@ -93,7 +179,9 @@ const crud = makeCrudRoute({
93
179
  schema: rawBodySchema,
94
180
  mapInput: async ({ raw, ctx }) => {
95
181
  const { translate } = await resolveTranslations();
96
- return documentAddressUpdateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate));
182
+ const input = documentAddressUpdateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate));
183
+ await ensureDocumentAddressMutationAccess(ctx, input, { includeTargetKind: true, translate });
184
+ return input;
97
185
  },
98
186
  response: () => ({ ok: true })
99
187
  },
@@ -110,7 +198,9 @@ const crud = makeCrudRoute({
110
198
  error: translate("sales.documents.detail.error", "Document not found or inaccessible.")
111
199
  });
112
200
  }
113
- return documentAddressDeleteSchema.parse(withScopedPayload({ id, documentId, documentKind }, ctx, translate));
201
+ const input = documentAddressDeleteSchema.parse(withScopedPayload({ id, documentId, documentKind }, ctx, translate));
202
+ await ensureDocumentAddressMutationAccess(ctx, input, { includeTargetKind: false, translate });
203
+ return input;
114
204
  },
115
205
  response: () => ({ ok: true })
116
206
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/api/document-addresses/route.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createSalesCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport { withScopedPayload } from '../utils'\nimport { SalesDocumentAddress } from '../../data/entities'\nimport {\n documentAddressCreateSchema,\n documentAddressDeleteSchema,\n documentAddressUpdateSchema,\n} from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n documentId: z.string().uuid(),\n documentKind: z.enum(['order', 'quote']).optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true },\n PUT: { requireAuth: true },\n DELETE: { requireAuth: true },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesDocumentAddress,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.sales.sales_document_address },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_document_address,\n fields: [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'document_id',\n 'document_kind',\n 'customer_address_id',\n 'name',\n 'purpose',\n 'company_name',\n 'address_line1',\n 'address_line2',\n 'building_number',\n 'flat_number',\n 'city',\n 'region',\n 'postal_code',\n 'country',\n 'latitude',\n 'longitude',\n 'order_id',\n 'quote_id',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query: any) => {\n const filters: Record<string, any> = {\n document_id: { $eq: query.documentId },\n }\n\n if (query.documentKind) filters.document_kind = { $eq: query.documentKind }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'sales.document-addresses.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return documentAddressCreateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate))\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.document-addresses.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return documentAddressUpdateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate))\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'sales.document-addresses.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n const documentId =\n parsed?.body?.documentId ??\n parsed?.query?.documentId ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('documentId') : null)\n const documentKind =\n parsed?.body?.documentKind ??\n parsed?.query?.documentKind ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('documentKind') : null)\n if (!id || !documentId || !documentKind) {\n throw new CrudHttpError(400, {\n error: translate('sales.documents.detail.error', 'Document not found or inaccessible.'),\n })\n }\n return documentAddressDeleteSchema.parse(withScopedPayload({ id, documentId, documentKind }, ctx, translate))\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport { GET, POST, PUT, DELETE }\n\nconst documentAddressSchema = z.object({\n id: z.string().uuid(),\n document_id: z.string().uuid(),\n document_kind: z.enum(['order', 'quote']),\n customer_address_id: z.string().uuid().nullable().optional(),\n name: z.string().nullable().optional(),\n purpose: z.string().nullable().optional(),\n company_name: z.string().nullable().optional(),\n address_line1: z.string(),\n address_line2: z.string().nullable().optional(),\n building_number: z.string().nullable().optional(),\n flat_number: z.string().nullable().optional(),\n city: z.string().nullable().optional(),\n region: z.string().nullable().optional(),\n postal_code: z.string().nullable().optional(),\n country: z.string().nullable().optional(),\n latitude: z.number().nullable().optional(),\n longitude: z.number().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Document address',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(documentAddressSchema),\n create: {\n schema: documentAddressCreateSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable() }),\n description: 'Creates a sales document address linked to an order or quote.',\n },\n update: {\n schema: documentAddressUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a sales document address.',\n },\n del: {\n schema: documentAddressDeleteSchema.pick({ id: true, documentId: true, documentKind: true }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a sales document address.',\n },\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createSalesCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\nimport { withScopedPayload } from '../utils'\nimport { SalesDocumentAddress, SalesOrder, SalesQuote } from '../../data/entities'\nimport {\n documentAddressCreateSchema,\n documentAddressDeleteSchema,\n documentAddressUpdateSchema,\n} from '../../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n documentId: z.string().uuid(),\n documentKind: z.enum(['order', 'quote']).optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true },\n PUT: { requireAuth: true },\n DELETE: { requireAuth: true },\n}\n\nexport const metadata = routeMetadata\n\ntype DocumentKind = 'order' | 'quote'\ntype DocumentAddressAccess = 'view' | 'manage'\ntype Translate = (key: string, fallback?: string) => string\n\nconst DOCUMENT_ADDRESS_FEATURES: Record<DocumentKind, Record<DocumentAddressAccess, string>> = {\n order: { view: 'sales.orders.view', manage: 'sales.orders.manage' },\n quote: { view: 'sales.quotes.view', manage: 'sales.quotes.manage' },\n}\n\nfunction toDocumentKind(value: unknown): DocumentKind | null {\n return value === 'order' || value === 'quote' ? value : null\n}\n\nasync function resolveDocumentKindForRead(query: { documentId?: string; documentKind?: DocumentKind }, ctx: CrudCtx) {\n if (query.documentKind) return query.documentKind\n\n const tenantId = ctx.auth?.tenantId ?? null\n const organizationId =\n ctx.selectedOrganizationId ??\n ctx.auth?.orgId ??\n (Array.isArray(ctx.organizationIds) && ctx.organizationIds.length === 1 ? ctx.organizationIds[0] : null)\n if (!query.documentId || !tenantId || !organizationId) return null\n\n const em = ctx.container.resolve('em') as EntityManager\n const scope = { tenantId, organizationId }\n const order = await findOneWithDecryption(em, SalesOrder, { id: query.documentId, tenantId, organizationId }, {}, scope)\n if (order) return 'order'\n const quote = await findOneWithDecryption(em, SalesQuote, { id: query.documentId, tenantId, organizationId }, {}, scope)\n if (quote) return 'quote'\n return null\n}\n\nasync function ensureDocumentAddressReadAccess(query: { documentId?: string; documentKind?: DocumentKind }, ctx: CrudCtx) {\n const documentKind = await resolveDocumentKindForRead(query, ctx)\n const requiredFeatures = documentKind\n ? [DOCUMENT_ADDRESS_FEATURES[documentKind].view]\n : [DOCUMENT_ADDRESS_FEATURES.order.view, DOCUMENT_ADDRESS_FEATURES.quote.view]\n await ensureDocumentAddressAccess(ctx, requiredFeatures)\n return documentKind\n}\n\nasync function resolveExistingAddressKind(\n ctx: CrudCtx,\n id: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<DocumentKind | null> {\n const em = ctx.container.resolve('em') as EntityManager\n const address = await findOneWithDecryption(\n em,\n SalesDocumentAddress,\n { id, tenantId: scope.tenantId, organizationId: scope.organizationId },\n {},\n scope,\n )\n return toDocumentKind(address?.documentKind)\n}\n\nasync function ensureDocumentAddressAccess(\n ctx: CrudCtx,\n requiredFeatures: string[],\n translate?: Translate,\n): Promise<void> {\n const resolvedTranslate = translate ?? (await resolveTranslations()).translate\n const auth = ctx.auth\n if (!auth?.sub) {\n throw new CrudHttpError(401, { error: resolvedTranslate('api.errors.unauthorized', 'Unauthorized') })\n }\n\n let rbac: RbacService | null = null\n try {\n rbac = ctx.container.resolve('rbacService') as RbacService\n } catch {\n rbac = null\n }\n\n const ok = rbac\n ? await rbac.userHasAllFeatures(auth.sub, requiredFeatures, {\n tenantId: auth.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? auth.orgId ?? null,\n })\n : false\n if (!ok) {\n throw new CrudHttpError(403, {\n error: resolvedTranslate('api.errors.forbidden', 'Forbidden'),\n requiredFeatures,\n })\n }\n}\n\nasync function ensureDocumentAddressKindAccess(\n ctx: CrudCtx,\n documentKind: DocumentKind,\n access: DocumentAddressAccess,\n translate?: Translate,\n): Promise<void> {\n await ensureDocumentAddressAccess(ctx, [DOCUMENT_ADDRESS_FEATURES[documentKind][access]], translate)\n}\n\nasync function ensureDocumentAddressMutationAccess(\n ctx: CrudCtx,\n input: { id: string; documentKind: DocumentKind; tenantId: string; organizationId: string },\n options: { includeTargetKind: boolean; translate?: Translate },\n): Promise<void> {\n const existingKind = await resolveExistingAddressKind(ctx, input.id, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n const featureKinds = new Set<DocumentKind>()\n if (existingKind) featureKinds.add(existingKind)\n if (!existingKind || options.includeTargetKind) featureKinds.add(input.documentKind)\n\n await ensureDocumentAddressAccess(\n ctx,\n [...featureKinds].map((kind) => DOCUMENT_ADDRESS_FEATURES[kind].manage),\n options.translate,\n )\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n hooks: {\n beforeList: async (query, ctx) => {\n await ensureDocumentAddressReadAccess(query, ctx)\n },\n },\n orm: {\n entity: SalesDocumentAddress,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.sales.sales_document_address },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_document_address,\n fields: [\n 'id',\n 'organization_id',\n 'tenant_id',\n 'document_id',\n 'document_kind',\n 'customer_address_id',\n 'name',\n 'purpose',\n 'company_name',\n 'address_line1',\n 'address_line2',\n 'building_number',\n 'flat_number',\n 'city',\n 'region',\n 'postal_code',\n 'country',\n 'latitude',\n 'longitude',\n 'order_id',\n 'quote_id',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query: any, ctx) => {\n const documentKind = await resolveDocumentKindForRead(query, ctx)\n const filters: Record<string, any> = {\n document_id: { $eq: query.documentId },\n }\n\n if (query.documentKind ?? documentKind) filters.document_kind = { $eq: query.documentKind ?? documentKind }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'sales.document-addresses.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const input = documentAddressCreateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate))\n await ensureDocumentAddressKindAccess(ctx, input.documentKind, 'manage', translate)\n return input\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.document-addresses.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const input = documentAddressUpdateSchema.parse(withScopedPayload(raw ?? {}, ctx, translate))\n await ensureDocumentAddressMutationAccess(ctx, input, { includeTargetKind: true, translate })\n return input\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'sales.document-addresses.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n const documentId =\n parsed?.body?.documentId ??\n parsed?.query?.documentId ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('documentId') : null)\n const documentKind =\n parsed?.body?.documentKind ??\n parsed?.query?.documentKind ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('documentKind') : null)\n if (!id || !documentId || !documentKind) {\n throw new CrudHttpError(400, {\n error: translate('sales.documents.detail.error', 'Document not found or inaccessible.'),\n })\n }\n const input = documentAddressDeleteSchema.parse(withScopedPayload({ id, documentId, documentKind }, ctx, translate))\n await ensureDocumentAddressMutationAccess(ctx, input, { includeTargetKind: false, translate })\n return input\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport { GET, POST, PUT, DELETE }\n\nconst documentAddressSchema = z.object({\n id: z.string().uuid(),\n document_id: z.string().uuid(),\n document_kind: z.enum(['order', 'quote']),\n customer_address_id: z.string().uuid().nullable().optional(),\n name: z.string().nullable().optional(),\n purpose: z.string().nullable().optional(),\n company_name: z.string().nullable().optional(),\n address_line1: z.string(),\n address_line2: z.string().nullable().optional(),\n building_number: z.string().nullable().optional(),\n flat_number: z.string().nullable().optional(),\n city: z.string().nullable().optional(),\n region: z.string().nullable().optional(),\n postal_code: z.string().nullable().optional(),\n country: z.string().nullable().optional(),\n latitude: z.number().nullable().optional(),\n longitude: z.number().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Document address',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(documentAddressSchema),\n create: {\n schema: documentAddressCreateSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable() }),\n description: 'Creates a sales document address linked to an order or quote.',\n },\n update: {\n schema: documentAddressUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a sales document address.',\n },\n del: {\n schema: documentAddressDeleteSchema.pick({ id: true, documentId: true, documentKind: true }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a sales document address.',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAmC;AAC5C,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AAGpC,SAAS,6BAA6B;AACtC,SAAS,wBAAwB,+BAA+B,+BAA+B;AAC/F,SAAS,yBAAyB;AAClC,SAAS,sBAAsB,YAAY,kBAAkB;AAC7D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;AAElB,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,aAAa,EAChB,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,YAAY,EAAE,OAAO,EAAE,KAAK;AAAA,EAC5B,cAAc,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC,EAAE,SAAS;AAAA,EAClD,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;AAEf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,KAAK;AAAA,EACzB,MAAM,EAAE,aAAa,KAAK;AAAA,EAC1B,KAAK,EAAE,aAAa,KAAK;AAAA,EACzB,QAAQ,EAAE,aAAa,KAAK;AAC9B;AAEO,MAAM,WAAW;AAMxB,MAAM,4BAAyF;AAAA,EAC7F,OAAO,EAAE,MAAM,qBAAqB,QAAQ,sBAAsB;AAAA,EAClE,OAAO,EAAE,MAAM,qBAAqB,QAAQ,sBAAsB;AACpE;AAEA,SAAS,eAAe,OAAqC;AAC3D,SAAO,UAAU,WAAW,UAAU,UAAU,QAAQ;AAC1D;AAEA,eAAe,2BAA2B,OAA6D,KAAc;AACnH,MAAI,MAAM,aAAc,QAAO,MAAM;AAErC,QAAM,WAAW,IAAI,MAAM,YAAY;AACvC,QAAM,iBACJ,IAAI,0BACJ,IAAI,MAAM,UACT,MAAM,QAAQ,IAAI,eAAe,KAAK,IAAI,gBAAgB,WAAW,IAAI,IAAI,gBAAgB,CAAC,IAAI;AACrG,MAAI,CAAC,MAAM,cAAc,CAAC,YAAY,CAAC,eAAgB,QAAO;AAE9D,QAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,QAAM,QAAQ,EAAE,UAAU,eAAe;AACzC,QAAM,QAAQ,MAAM,sBAAsB,IAAI,YAAY,EAAE,IAAI,MAAM,YAAY,UAAU,eAAe,GAAG,CAAC,GAAG,KAAK;AACvH,MAAI,MAAO,QAAO;AAClB,QAAM,QAAQ,MAAM,sBAAsB,IAAI,YAAY,EAAE,IAAI,MAAM,YAAY,UAAU,eAAe,GAAG,CAAC,GAAG,KAAK;AACvH,MAAI,MAAO,QAAO;AAClB,SAAO;AACT;AAEA,eAAe,gCAAgC,OAA6D,KAAc;AACxH,QAAM,eAAe,MAAM,2BAA2B,OAAO,GAAG;AAChE,QAAM,mBAAmB,eACrB,CAAC,0BAA0B,YAAY,EAAE,IAAI,IAC7C,CAAC,0BAA0B,MAAM,MAAM,0BAA0B,MAAM,IAAI;AAC/E,QAAM,4BAA4B,KAAK,gBAAgB;AACvD,SAAO;AACT;AAEA,eAAe,2BACb,KACA,IACA,OAC8B;AAC9B,QAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACrE,CAAC;AAAA,IACD;AAAA,EACF;AACA,SAAO,eAAe,SAAS,YAAY;AAC7C;AAEA,eAAe,4BACb,KACA,kBACA,WACe;AACf,QAAM,oBAAoB,cAAc,MAAM,oBAAoB,GAAG;AACrE,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,MAAM,KAAK;AACd,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kBAAkB,2BAA2B,cAAc,EAAE,CAAC;AAAA,EACtG;AAEA,MAAI,OAA2B;AAC/B,MAAI;AACF,WAAO,IAAI,UAAU,QAAQ,aAAa;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,KAAK,OACP,MAAM,KAAK,mBAAmB,KAAK,KAAK,kBAAkB;AAAA,IACxD,UAAU,KAAK,YAAY;AAAA,IAC3B,gBAAgB,IAAI,0BAA0B,KAAK,SAAS;AAAA,EAC9D,CAAC,IACD;AACJ,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,cAAc,KAAK;AAAA,MAC3B,OAAO,kBAAkB,wBAAwB,WAAW;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,eAAe,gCACb,KACA,cACA,QACA,WACe;AACf,QAAM,4BAA4B,KAAK,CAAC,0BAA0B,YAAY,EAAE,MAAM,CAAC,GAAG,SAAS;AACrG;AAEA,eAAe,oCACb,KACA,OACA,SACe;AACf,QAAM,eAAe,MAAM,2BAA2B,KAAK,MAAM,IAAI;AAAA,IACnE,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB,CAAC;AACD,QAAM,eAAe,oBAAI,IAAkB;AAC3C,MAAI,aAAc,cAAa,IAAI,YAAY;AAC/C,MAAI,CAAC,gBAAgB,QAAQ,kBAAmB,cAAa,IAAI,MAAM,YAAY;AAEnF,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,GAAG,YAAY,EAAE,IAAI,CAAC,SAAS,0BAA0B,IAAI,EAAE,MAAM;AAAA,IACtE,QAAQ;AAAA,EACV;AACF;AAEA,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,OAAO;AAAA,IACL,YAAY,OAAO,OAAO,QAAQ;AAChC,YAAM,gCAAgC,OAAO,GAAG;AAAA,IAClD;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB;AAAA,EACtD,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,MAAM;AAAA,IAClB,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,IACA,cAAc,OAAO,OAAY,QAAQ;AACvC,YAAM,eAAe,MAAM,2BAA2B,OAAO,GAAG;AAChE,YAAM,UAA+B;AAAA,QACnC,aAAa,EAAE,KAAK,MAAM,WAAW;AAAA,MACvC;AAEA,UAAI,MAAM,gBAAgB,aAAc,SAAQ,gBAAgB,EAAE,KAAK,MAAM,gBAAgB,aAAa;AAC1G,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,QAAQ,4BAA4B,MAAM,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC;AAC5F,cAAM,gCAAgC,KAAK,MAAM,cAAc,UAAU,SAAS;AAClF,eAAO;AAAA,MACT;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO,EAAE,IAAI,QAAQ,MAAM,KAAK;AAAA,MACpD,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,QAAQ,4BAA4B,MAAM,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC;AAC5F,cAAM,oCAAoC,KAAK,OAAO,EAAE,mBAAmB,MAAM,UAAU,CAAC;AAC5F,eAAO;AAAA,MACT;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,KACJ,QAAQ,MAAM,MACd,QAAQ,MACR,QAAQ,OAAO,OACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,IAAI,IAAI;AACnE,cAAM,aACJ,QAAQ,MAAM,cACd,QAAQ,OAAO,eACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,YAAY,IAAI;AAC3E,cAAM,eACJ,QAAQ,MAAM,gBACd,QAAQ,OAAO,iBACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,cAAc,IAAI;AAC7E,YAAI,CAAC,MAAM,CAAC,cAAc,CAAC,cAAc;AACvC,gBAAM,IAAI,cAAc,KAAK;AAAA,YAC3B,OAAO,UAAU,gCAAgC,qCAAqC;AAAA,UACxF,CAAC;AAAA,QACH;AACA,cAAM,QAAQ,4BAA4B,MAAM,kBAAkB,EAAE,IAAI,YAAY,aAAa,GAAG,KAAK,SAAS,CAAC;AACnH,cAAM,oCAAoC,KAAK,OAAO,EAAE,mBAAmB,OAAO,UAAU,CAAC;AAC7F,eAAO;AAAA,MACT;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AACF,CAAC;AAED,MAAM,EAAE,KAAK,MAAM,KAAK,OAAO,IAAI;AAInC,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,aAAa,EAAE,OAAO,EAAE,KAAK;AAAA,EAC7B,eAAe,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC;AAAA,EACxC,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACrC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,eAAe,EAAE,OAAO;AAAA,EACxB,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACrC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AACvB,CAAC;AAEM,MAAM,UAAU,uBAAuB;AAAA,EAC5C,cAAc;AAAA,EACd,aAAa;AAAA,EACb,oBAAoB,8BAA8B,qBAAqB;AAAA,EACvE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAAA,IAC7D,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,4BAA4B,KAAK,EAAE,IAAI,MAAM,YAAY,MAAM,cAAc,KAAK,CAAC;AAAA,IAC3F,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -62,6 +62,11 @@ function applyDocumentAddressSnapshot(em, entity, snapshot) {
62
62
  entity.order = snapshot.documentKind === "order" ? em.getReference(SalesOrder, snapshot.documentId) : null;
63
63
  entity.quote = snapshot.documentKind === "quote" ? em.getReference(SalesQuote, snapshot.documentId) : null;
64
64
  }
65
+ function assertDocumentAddressParent(entity, input) {
66
+ if (entity.documentId !== input.documentId || entity.documentKind !== input.documentKind) {
67
+ throw new CrudHttpError(404, { error: "sales.document.address.not_found" });
68
+ }
69
+ }
65
70
  async function emitDocumentAddressIndexSideEffects(ctx, action, snapshot) {
66
71
  let dataEngine = null;
67
72
  try {
@@ -196,6 +201,7 @@ const updateDocumentAddress = {
196
201
  "sales.document.address.not_found"
197
202
  );
198
203
  ensureSameScope(entity, input.organizationId, input.tenantId);
204
+ assertDocumentAddressParent(entity, input);
199
205
  const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId);
200
206
  if (input.documentKind === "order") {
201
207
  await assertAddressEditable(em, {
@@ -276,6 +282,7 @@ const deleteDocumentAddress = {
276
282
  "sales.document.address.not_found"
277
283
  );
278
284
  ensureSameScope(entity, input.organizationId, input.tenantId);
285
+ assertDocumentAddressParent(entity, input);
279
286
  const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId);
280
287
  if (input.documentKind === "order") {
281
288
  await assertAddressEditable(em, {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/commands/documentAddresses.ts"],
4
- "sourcesContent": ["// @ts-nocheck\n\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n documentAddressCreateSchema,\n documentAddressDeleteSchema,\n documentAddressUpdateSchema,\n type DocumentAddressCreateInput,\n type DocumentAddressDeleteInput,\n type DocumentAddressUpdateInput,\n} from '../data/validators'\nimport { SalesDocumentAddress, SalesOrder, SalesQuote } from '../data/entities'\nimport { ensureOrganizationScope, ensureSameScope, ensureTenantScope, assertFound, extractUndoPayload } from './shared'\nimport { loadSalesSettings } from './settings'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { E } from '#generated/entities.ids.generated'\n\nconst DOCUMENT_ADDRESS_ENTITY_TYPE = E.sales.sales_document_address\n\ntype DocumentAddressSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n documentId: string\n documentKind: 'order' | 'quote'\n customerAddressId: string | null\n name: string | null\n purpose: string | null\n companyName: string | null\n addressLine1: string\n addressLine2: string | null\n buildingNumber: string | null\n flatNumber: string | null\n city: string | null\n region: string | null\n postalCode: string | null\n country: string | null\n latitude: number | null\n longitude: number | null\n}\n\ntype DocumentAddressUndoPayload = {\n before?: DocumentAddressSnapshot | null\n after?: DocumentAddressSnapshot | null\n}\n\nfunction snapshotDocumentAddress(entity: SalesDocumentAddress): DocumentAddressSnapshot {\n return {\n id: entity.id,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n documentId: entity.documentId,\n documentKind: entity.documentKind as 'order' | 'quote',\n customerAddressId: entity.customerAddressId ?? null,\n name: entity.name ?? null,\n purpose: entity.purpose ?? null,\n companyName: entity.companyName ?? null,\n addressLine1: entity.addressLine1,\n addressLine2: entity.addressLine2 ?? null,\n buildingNumber: entity.buildingNumber ?? null,\n flatNumber: entity.flatNumber ?? null,\n city: entity.city ?? null,\n region: entity.region ?? null,\n postalCode: entity.postalCode ?? null,\n country: entity.country ?? null,\n latitude: entity.latitude ?? null,\n longitude: entity.longitude ?? null,\n }\n}\n\nasync function loadDocumentAddressSnapshot(\n em: EntityManager,\n id: string\n): Promise<DocumentAddressSnapshot | null> {\n const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id }, {})\n return entity ? snapshotDocumentAddress(entity) : null\n}\n\nfunction applyDocumentAddressSnapshot(em: EntityManager, entity: SalesDocumentAddress, snapshot: DocumentAddressSnapshot): void {\n entity.organizationId = snapshot.organizationId\n entity.tenantId = snapshot.tenantId\n entity.documentId = snapshot.documentId\n entity.documentKind = snapshot.documentKind\n entity.customerAddressId = snapshot.customerAddressId\n entity.name = snapshot.name\n entity.purpose = snapshot.purpose\n entity.companyName = snapshot.companyName\n entity.addressLine1 = snapshot.addressLine1\n entity.addressLine2 = snapshot.addressLine2\n entity.buildingNumber = snapshot.buildingNumber\n entity.flatNumber = snapshot.flatNumber\n entity.city = snapshot.city\n entity.region = snapshot.region\n entity.postalCode = snapshot.postalCode\n entity.country = snapshot.country\n entity.latitude = snapshot.latitude\n entity.longitude = snapshot.longitude\n entity.order = snapshot.documentKind === 'order' ? em.getReference(SalesOrder, snapshot.documentId) : null\n entity.quote = snapshot.documentKind === 'quote' ? em.getReference(SalesQuote, snapshot.documentId) : null\n}\n\nasync function emitDocumentAddressIndexSideEffects(\n ctx: { container: { resolve: (name: string) => unknown } },\n action: 'created' | 'updated' | 'deleted',\n snapshot: DocumentAddressSnapshot\n): Promise<void> {\n let dataEngine: DataEngine | null = null\n try {\n dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n } catch {\n dataEngine = null\n }\n if (!dataEngine) return\n await emitCrudSideEffects({\n dataEngine,\n action,\n entity: snapshot,\n identifiers: {\n id: snapshot.id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n },\n indexer: { entityType: DOCUMENT_ADDRESS_ENTITY_TYPE },\n })\n}\n\nasync function requireDocument(\n em: EntityManager,\n kind: 'order' | 'quote',\n id: string,\n organizationId: string,\n tenantId: string\n): Promise<SalesOrder | SalesQuote> {\n const repo = kind === 'order' ? SalesOrder : SalesQuote\n const doc = await findOneWithDecryption(em, repo, { id, organizationId, tenantId }, {}, { tenantId, organizationId })\n if (!doc) {\n throw new CrudHttpError(404, { error: 'sales.document.not_found' })\n }\n return doc\n}\n\nasync function assertAddressEditable(\n em: EntityManager,\n params: { organizationId: string; tenantId: string; status: string | null }\n): Promise<void> {\n const settings = await loadSalesSettings(em, {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n })\n const allowed = settings?.orderAddressEditableStatuses ?? null\n if (!Array.isArray(allowed)) return\n const { translate } = await resolveTranslations()\n if (allowed.length === 0) {\n throw new CrudHttpError(400, { error: translate('sales.orders.edit_addresses_blocked', 'Addresses cannot be changed for the current status.') })\n }\n if (!params.status || !allowed.includes(params.status)) {\n throw new CrudHttpError(400, { error: translate('sales.orders.edit_addresses_blocked', 'Addresses cannot be changed for the current status.') })\n }\n}\n\nconst createDocumentAddress: CommandHandler<DocumentAddressCreateInput, { id: string }> = {\n id: 'sales.document-addresses.create',\n async execute(rawInput, ctx) {\n const input = documentAddressCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n\n const entity = em.create(SalesDocumentAddress, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n documentId: input.documentId,\n documentKind: input.documentKind,\n customerAddressId: input.customerAddressId ?? null,\n name: input.name ?? null,\n purpose: input.purpose ?? null,\n companyName: input.companyName ?? null,\n addressLine1: input.addressLine1,\n addressLine2: input.addressLine2 ?? null,\n buildingNumber: input.buildingNumber ?? null,\n flatNumber: input.flatNumber ?? null,\n city: input.city ?? null,\n region: input.region ?? null,\n postalCode: input.postalCode ?? null,\n country: input.country ?? null,\n latitude: input.latitude ?? null,\n longitude: input.longitude ?? null,\n order: input.documentKind === 'order' ? (document as SalesOrder) : null,\n quote: input.documentKind === 'quote' ? (document as SalesQuote) : null,\n })\n await em.persist(entity).flush()\n return { id: entity.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as DocumentAddressSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.create', 'Add document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: after.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: after.documentId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n ensureTenantScope(ctx, after.tenantId)\n ensureOrganizationScope(ctx, after.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id: after.id }, {}, { tenantId: after.tenantId, organizationId: after.organizationId })\n if (!entity) return\n await em.remove(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'deleted', after)\n },\n}\n\nconst updateDocumentAddress: CommandHandler<DocumentAddressUpdateInput, { id: string }> = {\n id: 'sales.document-addresses.update',\n async prepare(rawInput, ctx) {\n const parsed = documentAddressUpdateSchema.parse(rawInput)\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadDocumentAddressSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = documentAddressUpdateSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = assertFound(\n await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),\n 'sales.document.address.not_found'\n )\n ensureSameScope(entity, input.organizationId, input.tenantId)\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n\n entity.documentId = input.documentId\n entity.documentKind = input.documentKind\n entity.customerAddressId = input.customerAddressId ?? null\n entity.name = input.name ?? null\n entity.purpose = input.purpose ?? null\n entity.companyName = input.companyName ?? null\n entity.addressLine1 = input.addressLine1\n entity.addressLine2 = input.addressLine2 ?? null\n entity.buildingNumber = input.buildingNumber ?? null\n entity.flatNumber = input.flatNumber ?? null\n entity.city = input.city ?? null\n entity.region = input.region ?? null\n entity.postalCode = input.postalCode ?? null\n entity.country = input.country ?? null\n entity.latitude = input.latitude ?? null\n entity.longitude = input.longitude ?? null\n\n await em.flush()\n return { id: entity.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as DocumentAddressSnapshot | undefined\n const after = snapshots.after as DocumentAddressSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.update', 'Update document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: after.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: after.documentId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: { undo: { before: before ?? null, after } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity =\n (await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })) ??\n em.create(SalesDocumentAddress, { id: before.id } as Partial<SalesDocumentAddress>)\n applyDocumentAddressSnapshot(em, entity, before)\n await em.persist(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'updated', before)\n },\n}\n\nconst deleteDocumentAddress: CommandHandler<\n DocumentAddressDeleteInput,\n { ok: true; id: string }\n> = {\n id: 'sales.document-addresses.delete',\n async prepare(rawInput, ctx) {\n const parsed = documentAddressDeleteSchema.parse(rawInput)\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadDocumentAddressSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = documentAddressDeleteSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = assertFound(\n await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),\n 'sales.document.address.not_found'\n )\n ensureSameScope(entity, input.organizationId, input.tenantId)\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n await em.remove(entity).flush()\n return { ok: true, id: input.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as DocumentAddressSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.delete', 'Remove document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: before.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: before.documentId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })\n const entity = existing ?? em.create(SalesDocumentAddress, { id: before.id } as Partial<SalesDocumentAddress>)\n applyDocumentAddressSnapshot(em, entity, before)\n await em.persist(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'created', before)\n },\n}\n\nexport const documentAddressCommands = [createDocumentAddress, updateDocumentAddress, deleteDocumentAddress]\n\nregisterCommand(createDocumentAddress)\nregisterCommand(updateDocumentAddress)\nregisterCommand(deleteDocumentAddress)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// @ts-nocheck\n\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n documentAddressCreateSchema,\n documentAddressDeleteSchema,\n documentAddressUpdateSchema,\n type DocumentAddressCreateInput,\n type DocumentAddressDeleteInput,\n type DocumentAddressUpdateInput,\n} from '../data/validators'\nimport { SalesDocumentAddress, SalesOrder, SalesQuote } from '../data/entities'\nimport { ensureOrganizationScope, ensureSameScope, ensureTenantScope, assertFound, extractUndoPayload } from './shared'\nimport { loadSalesSettings } from './settings'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { E } from '#generated/entities.ids.generated'\n\nconst DOCUMENT_ADDRESS_ENTITY_TYPE = E.sales.sales_document_address\n\ntype DocumentAddressSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n documentId: string\n documentKind: 'order' | 'quote'\n customerAddressId: string | null\n name: string | null\n purpose: string | null\n companyName: string | null\n addressLine1: string\n addressLine2: string | null\n buildingNumber: string | null\n flatNumber: string | null\n city: string | null\n region: string | null\n postalCode: string | null\n country: string | null\n latitude: number | null\n longitude: number | null\n}\n\ntype DocumentAddressUndoPayload = {\n before?: DocumentAddressSnapshot | null\n after?: DocumentAddressSnapshot | null\n}\n\nfunction snapshotDocumentAddress(entity: SalesDocumentAddress): DocumentAddressSnapshot {\n return {\n id: entity.id,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n documentId: entity.documentId,\n documentKind: entity.documentKind as 'order' | 'quote',\n customerAddressId: entity.customerAddressId ?? null,\n name: entity.name ?? null,\n purpose: entity.purpose ?? null,\n companyName: entity.companyName ?? null,\n addressLine1: entity.addressLine1,\n addressLine2: entity.addressLine2 ?? null,\n buildingNumber: entity.buildingNumber ?? null,\n flatNumber: entity.flatNumber ?? null,\n city: entity.city ?? null,\n region: entity.region ?? null,\n postalCode: entity.postalCode ?? null,\n country: entity.country ?? null,\n latitude: entity.latitude ?? null,\n longitude: entity.longitude ?? null,\n }\n}\n\nasync function loadDocumentAddressSnapshot(\n em: EntityManager,\n id: string\n): Promise<DocumentAddressSnapshot | null> {\n const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id }, {})\n return entity ? snapshotDocumentAddress(entity) : null\n}\n\nfunction applyDocumentAddressSnapshot(em: EntityManager, entity: SalesDocumentAddress, snapshot: DocumentAddressSnapshot): void {\n entity.organizationId = snapshot.organizationId\n entity.tenantId = snapshot.tenantId\n entity.documentId = snapshot.documentId\n entity.documentKind = snapshot.documentKind\n entity.customerAddressId = snapshot.customerAddressId\n entity.name = snapshot.name\n entity.purpose = snapshot.purpose\n entity.companyName = snapshot.companyName\n entity.addressLine1 = snapshot.addressLine1\n entity.addressLine2 = snapshot.addressLine2\n entity.buildingNumber = snapshot.buildingNumber\n entity.flatNumber = snapshot.flatNumber\n entity.city = snapshot.city\n entity.region = snapshot.region\n entity.postalCode = snapshot.postalCode\n entity.country = snapshot.country\n entity.latitude = snapshot.latitude\n entity.longitude = snapshot.longitude\n entity.order = snapshot.documentKind === 'order' ? em.getReference(SalesOrder, snapshot.documentId) : null\n entity.quote = snapshot.documentKind === 'quote' ? em.getReference(SalesQuote, snapshot.documentId) : null\n}\n\nfunction assertDocumentAddressParent(entity: SalesDocumentAddress, input: { documentId: string; documentKind: 'order' | 'quote' }): void {\n if (entity.documentId !== input.documentId || entity.documentKind !== input.documentKind) {\n throw new CrudHttpError(404, { error: 'sales.document.address.not_found' })\n }\n}\n\nasync function emitDocumentAddressIndexSideEffects(\n ctx: { container: { resolve: (name: string) => unknown } },\n action: 'created' | 'updated' | 'deleted',\n snapshot: DocumentAddressSnapshot\n): Promise<void> {\n let dataEngine: DataEngine | null = null\n try {\n dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n } catch {\n dataEngine = null\n }\n if (!dataEngine) return\n await emitCrudSideEffects({\n dataEngine,\n action,\n entity: snapshot,\n identifiers: {\n id: snapshot.id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n },\n indexer: { entityType: DOCUMENT_ADDRESS_ENTITY_TYPE },\n })\n}\n\nasync function requireDocument(\n em: EntityManager,\n kind: 'order' | 'quote',\n id: string,\n organizationId: string,\n tenantId: string\n): Promise<SalesOrder | SalesQuote> {\n const repo = kind === 'order' ? SalesOrder : SalesQuote\n const doc = await findOneWithDecryption(em, repo, { id, organizationId, tenantId }, {}, { tenantId, organizationId })\n if (!doc) {\n throw new CrudHttpError(404, { error: 'sales.document.not_found' })\n }\n return doc\n}\n\nasync function assertAddressEditable(\n em: EntityManager,\n params: { organizationId: string; tenantId: string; status: string | null }\n): Promise<void> {\n const settings = await loadSalesSettings(em, {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n })\n const allowed = settings?.orderAddressEditableStatuses ?? null\n if (!Array.isArray(allowed)) return\n const { translate } = await resolveTranslations()\n if (allowed.length === 0) {\n throw new CrudHttpError(400, { error: translate('sales.orders.edit_addresses_blocked', 'Addresses cannot be changed for the current status.') })\n }\n if (!params.status || !allowed.includes(params.status)) {\n throw new CrudHttpError(400, { error: translate('sales.orders.edit_addresses_blocked', 'Addresses cannot be changed for the current status.') })\n }\n}\n\nconst createDocumentAddress: CommandHandler<DocumentAddressCreateInput, { id: string }> = {\n id: 'sales.document-addresses.create',\n async execute(rawInput, ctx) {\n const input = documentAddressCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n\n const entity = em.create(SalesDocumentAddress, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n documentId: input.documentId,\n documentKind: input.documentKind,\n customerAddressId: input.customerAddressId ?? null,\n name: input.name ?? null,\n purpose: input.purpose ?? null,\n companyName: input.companyName ?? null,\n addressLine1: input.addressLine1,\n addressLine2: input.addressLine2 ?? null,\n buildingNumber: input.buildingNumber ?? null,\n flatNumber: input.flatNumber ?? null,\n city: input.city ?? null,\n region: input.region ?? null,\n postalCode: input.postalCode ?? null,\n country: input.country ?? null,\n latitude: input.latitude ?? null,\n longitude: input.longitude ?? null,\n order: input.documentKind === 'order' ? (document as SalesOrder) : null,\n quote: input.documentKind === 'quote' ? (document as SalesQuote) : null,\n })\n await em.persist(entity).flush()\n return { id: entity.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as DocumentAddressSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.create', 'Add document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: after.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: after.documentId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: { undo: { after } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n ensureTenantScope(ctx, after.tenantId)\n ensureOrganizationScope(ctx, after.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = await findOneWithDecryption(em, SalesDocumentAddress, { id: after.id }, {}, { tenantId: after.tenantId, organizationId: after.organizationId })\n if (!entity) return\n await em.remove(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'deleted', after)\n },\n}\n\nconst updateDocumentAddress: CommandHandler<DocumentAddressUpdateInput, { id: string }> = {\n id: 'sales.document-addresses.update',\n async prepare(rawInput, ctx) {\n const parsed = documentAddressUpdateSchema.parse(rawInput)\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadDocumentAddressSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = documentAddressUpdateSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = assertFound(\n await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),\n 'sales.document.address.not_found'\n )\n ensureSameScope(entity, input.organizationId, input.tenantId)\n assertDocumentAddressParent(entity, input)\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n\n entity.documentId = input.documentId\n entity.documentKind = input.documentKind\n entity.customerAddressId = input.customerAddressId ?? null\n entity.name = input.name ?? null\n entity.purpose = input.purpose ?? null\n entity.companyName = input.companyName ?? null\n entity.addressLine1 = input.addressLine1\n entity.addressLine2 = input.addressLine2 ?? null\n entity.buildingNumber = input.buildingNumber ?? null\n entity.flatNumber = input.flatNumber ?? null\n entity.city = input.city ?? null\n entity.region = input.region ?? null\n entity.postalCode = input.postalCode ?? null\n entity.country = input.country ?? null\n entity.latitude = input.latitude ?? null\n entity.longitude = input.longitude ?? null\n\n await em.flush()\n return { id: entity.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return result?.id ? loadDocumentAddressSnapshot(em, result.id) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as DocumentAddressSnapshot | undefined\n const after = snapshots.after as DocumentAddressSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.update', 'Update document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: after.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: after.documentId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: { undo: { before: before ?? null, after } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity =\n (await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })) ??\n em.create(SalesDocumentAddress, { id: before.id } as Partial<SalesDocumentAddress>)\n applyDocumentAddressSnapshot(em, entity, before)\n await em.persist(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'updated', before)\n },\n}\n\nconst deleteDocumentAddress: CommandHandler<\n DocumentAddressDeleteInput,\n { ok: true; id: string }\n> = {\n id: 'sales.document-addresses.delete',\n async prepare(rawInput, ctx) {\n const parsed = documentAddressDeleteSchema.parse(rawInput)\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadDocumentAddressSnapshot(em, parsed.id)\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = documentAddressDeleteSchema.parse(rawInput)\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entity = assertFound(\n await findOneWithDecryption(em, SalesDocumentAddress, { id: input.id }, {}, { tenantId: input.tenantId, organizationId: input.organizationId }),\n 'sales.document.address.not_found'\n )\n ensureSameScope(entity, input.organizationId, input.tenantId)\n assertDocumentAddressParent(entity, input)\n const document = await requireDocument(em, input.documentKind, input.documentId, input.organizationId, input.tenantId)\n if (input.documentKind === 'order') {\n await assertAddressEditable(em, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n status: (document as SalesOrder).status ?? null,\n })\n }\n await em.remove(entity).flush()\n return { ok: true, id: input.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as DocumentAddressSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.document_addresses.delete', 'Remove document address'),\n resourceKind: 'sales.document_address',\n resourceId: result.id,\n parentResourceKind: before.documentKind === 'order' ? 'sales.order' : 'sales.quote',\n parentResourceId: before.documentId,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: { undo: { before } satisfies DocumentAddressUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<DocumentAddressUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await findOneWithDecryption(em, SalesDocumentAddress, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })\n const entity = existing ?? em.create(SalesDocumentAddress, { id: before.id } as Partial<SalesDocumentAddress>)\n applyDocumentAddressSnapshot(em, entity, before)\n await em.persist(entity).flush()\n await emitDocumentAddressIndexSideEffects(ctx, 'created', before)\n },\n}\n\nexport const documentAddressCommands = [createDocumentAddress, updateDocumentAddress, deleteDocumentAddress]\n\nregisterCommand(createDocumentAddress)\nregisterCommand(updateDocumentAddress)\nregisterCommand(deleteDocumentAddress)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -24,7 +24,10 @@ import {
24
24
  ensureTenantScope,
25
25
  explicitStaffCommandScope,
26
26
  extractUndoPayload,
27
- requireTeamMember
27
+ requireTeamMember,
28
+ scopedStaffSnapshotWhere,
29
+ staffSnapshotScopeFromContext,
30
+ staffSnapshotScopeFromSnapshot
28
31
  } from "./shared.js";
29
32
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
30
33
  import { E } from "../../../generated/entities.ids.generated.js";
@@ -36,8 +39,8 @@ import {
36
39
  const activityCrudIndexer = {
37
40
  entityType: E.staff.staff_team_member_activity
38
41
  };
39
- async function loadActivitySnapshot(em, id) {
40
- const activity = await em.findOne(StaffTeamMemberActivity, { id });
42
+ async function loadActivitySnapshot(em, id, scope) {
43
+ const activity = await em.findOne(StaffTeamMemberActivity, scopedStaffSnapshotWhere(id, scope));
41
44
  if (!activity) return null;
42
45
  const custom = await loadCustomFieldSnapshot(em, {
43
46
  entityId: E.staff.staff_team_member_activity,
@@ -126,7 +129,7 @@ const createActivityCommand = {
126
129
  },
127
130
  captureAfter: async (_input, result, ctx) => {
128
131
  const em = ctx.container.resolve("em").fork();
129
- return await loadActivitySnapshot(em, result.activityId);
132
+ return await loadActivitySnapshot(em, result.activityId, staffSnapshotScopeFromContext(ctx));
130
133
  },
131
134
  buildLog: async ({ result, snapshots }) => {
132
135
  const { translate } = await resolveTranslations();
@@ -148,10 +151,15 @@ const createActivityCommand = {
148
151
  };
149
152
  },
150
153
  undo: async ({ logEntry, ctx }) => {
151
- const activityId = logEntry?.resourceId ?? null;
154
+ const payload = extractUndoPayload(logEntry);
155
+ const after = payload?.after;
156
+ const activityId = after?.activity.id ?? logEntry?.resourceId ?? null;
152
157
  if (!activityId) return;
153
158
  const em = ctx.container.resolve("em").fork();
154
- const existing = await em.findOne(StaffTeamMemberActivity, { id: activityId });
159
+ const existing = await em.findOne(
160
+ StaffTeamMemberActivity,
161
+ scopedStaffSnapshotWhere(activityId, staffSnapshotScopeFromSnapshot(after?.activity))
162
+ );
155
163
  if (existing) {
156
164
  em.remove(existing);
157
165
  await em.flush();
@@ -163,13 +171,14 @@ const createActivityCommand = {
163
171
  throw new CrudHttpError(400, { error: "[internal] redo snapshot unavailable for activity create" });
164
172
  }
165
173
  const em = ctx.container.resolve("em").fork();
174
+ const snapshotScope = staffSnapshotScopeFromSnapshot(after.activity);
166
175
  const member = await requireTeamMember(
167
176
  em,
168
177
  after.activity.memberId,
169
178
  explicitStaffCommandScope(after.activity.tenantId, after.activity.organizationId),
170
179
  "Team member not found"
171
180
  );
172
- let activity = await em.findOne(StaffTeamMemberActivity, { id: after.activity.id });
181
+ let activity = await em.findOne(StaffTeamMemberActivity, scopedStaffSnapshotWhere(after.activity.id, snapshotScope));
173
182
  if (!activity) {
174
183
  activity = em.create(StaffTeamMemberActivity, {
175
184
  id: after.activity.id,
@@ -220,7 +229,7 @@ const updateActivityCommand = {
220
229
  async prepare(rawInput, ctx) {
221
230
  const parsed = staffTeamMemberActivityUpdateSchema.parse(rawInput);
222
231
  const em = ctx.container.resolve("em");
223
- const snapshot = await loadActivitySnapshot(em, parsed.id);
232
+ const snapshot = await loadActivitySnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx));
224
233
  return snapshot ? { before: snapshot } : {};
225
234
  },
226
235
  async execute(rawInput, ctx) {
@@ -266,7 +275,7 @@ const updateActivityCommand = {
266
275
  },
267
276
  captureAfter: async (_input, result, ctx) => {
268
277
  const em = ctx.container.resolve("em").fork();
269
- return await loadActivitySnapshot(em, result.activityId);
278
+ return await loadActivitySnapshot(em, result.activityId, staffSnapshotScopeFromContext(ctx));
270
279
  },
271
280
  buildLog: async ({ snapshots }) => {
272
281
  const { translate } = await resolveTranslations();
@@ -314,7 +323,8 @@ const updateActivityCommand = {
314
323
  const before = payload?.before;
315
324
  if (!before) return;
316
325
  const em = ctx.container.resolve("em").fork();
317
- let activity = await em.findOne(StaffTeamMemberActivity, { id: before.activity.id });
326
+ const snapshotScope = staffSnapshotScopeFromSnapshot(before.activity);
327
+ let activity = await em.findOne(StaffTeamMemberActivity, scopedStaffSnapshotWhere(before.activity.id, snapshotScope));
318
328
  const member = await requireTeamMember(
319
329
  em,
320
330
  before.activity.memberId,
@@ -381,7 +391,7 @@ const deleteActivityCommand = {
381
391
  async prepare(input, ctx) {
382
392
  const id = requireId(input, "Activity id required");
383
393
  const em = ctx.container.resolve("em");
384
- const snapshot = await loadActivitySnapshot(em, id);
394
+ const snapshot = await loadActivitySnapshot(em, id, staffSnapshotScopeFromContext(ctx));
385
395
  return snapshot ? { before: snapshot } : {};
386
396
  },
387
397
  async execute(input, ctx) {
@@ -437,13 +447,14 @@ const deleteActivityCommand = {
437
447
  const before = payload?.before;
438
448
  if (!before) return;
439
449
  const em = ctx.container.resolve("em").fork();
450
+ const snapshotScope = staffSnapshotScopeFromSnapshot(before.activity);
440
451
  const member = await requireTeamMember(
441
452
  em,
442
453
  before.activity.memberId,
443
454
  explicitStaffCommandScope(before.activity.tenantId, before.activity.organizationId),
444
455
  "Team member not found"
445
456
  );
446
- let activity = await em.findOne(StaffTeamMemberActivity, { id: before.activity.id });
457
+ let activity = await em.findOne(StaffTeamMemberActivity, scopedStaffSnapshotWhere(before.activity.id, snapshotScope));
447
458
  if (!activity) {
448
459
  activity = em.create(StaffTeamMemberActivity, {
449
460
  id: before.activity.id,