@open-mercato/core 0.6.7-develop.6553.1.2ec7246e04 → 0.6.7-develop.6565.1.1026d85fff

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 (138) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/attachments/lib/reconcileOrganization.js +9 -7
  3. package/dist/modules/attachments/lib/reconcileOrganization.js.map +2 -2
  4. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -2
  5. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  6. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -2
  7. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  8. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -2
  9. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  10. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +4 -4
  11. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  12. package/dist/modules/customers/api/entity-roles-factory.js +3 -3
  13. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  14. package/dist/modules/customers/api/labels/assign/route.js +2 -2
  15. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  16. package/dist/modules/customers/api/labels/unassign/route.js +2 -2
  17. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  18. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +3 -7
  19. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  20. package/dist/modules/customers/api/people/[id]/companies/context.js +3 -3
  21. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  22. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -2
  23. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  24. package/dist/modules/customers/api/todos/route.js +2 -2
  25. package/dist/modules/customers/api/todos/route.js.map +2 -2
  26. package/dist/modules/customers/commands/addresses.js +3 -3
  27. package/dist/modules/customers/commands/addresses.js.map +2 -2
  28. package/dist/modules/customers/commands/comments.js +3 -3
  29. package/dist/modules/customers/commands/comments.js.map +2 -2
  30. package/dist/modules/customers/commands/companies.js +2 -2
  31. package/dist/modules/customers/commands/companies.js.map +2 -2
  32. package/dist/modules/customers/commands/deals.js +3 -3
  33. package/dist/modules/customers/commands/deals.js.map +2 -2
  34. package/dist/modules/customers/commands/dictionaries.js +3 -3
  35. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  36. package/dist/modules/customers/commands/entity-roles.js +3 -3
  37. package/dist/modules/customers/commands/entity-roles.js.map +2 -2
  38. package/dist/modules/customers/commands/interactions.js +6 -6
  39. package/dist/modules/customers/commands/interactions.js.map +2 -2
  40. package/dist/modules/customers/commands/labels.js +7 -7
  41. package/dist/modules/customers/commands/labels.js.map +2 -2
  42. package/dist/modules/customers/commands/people.js +3 -3
  43. package/dist/modules/customers/commands/people.js.map +2 -2
  44. package/dist/modules/customers/commands/personCompanyLinks.js +6 -6
  45. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  46. package/dist/modules/customers/commands/pipeline-stages.js +3 -3
  47. package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
  48. package/dist/modules/customers/commands/pipelines.js +3 -3
  49. package/dist/modules/customers/commands/pipelines.js.map +2 -2
  50. package/dist/modules/customers/commands/shared.js +3 -3
  51. package/dist/modules/customers/commands/shared.js.map +2 -2
  52. package/dist/modules/customers/commands/tags.js +7 -7
  53. package/dist/modules/customers/commands/tags.js.map +2 -2
  54. package/dist/modules/customers/commands/todos.js +3 -3
  55. package/dist/modules/customers/commands/todos.js.map +2 -2
  56. package/dist/modules/customers/lib/personCompanies.js +4 -4
  57. package/dist/modules/customers/lib/personCompanies.js.map +2 -2
  58. package/dist/modules/perspectives/data/validators.js +4 -0
  59. package/dist/modules/perspectives/data/validators.js.map +2 -2
  60. package/dist/modules/sales/api/notes/route.js +2 -4
  61. package/dist/modules/sales/api/notes/route.js.map +2 -2
  62. package/dist/modules/sales/api/quotes/accept/route.js +2 -2
  63. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  64. package/dist/modules/sales/api/quotes/public/[token]/route.js +3 -7
  65. package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
  66. package/dist/modules/sales/api/quotes/send/route.js +2 -2
  67. package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
  68. package/dist/modules/sales/api/returns/[id]/route.js +2 -2
  69. package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
  70. package/dist/modules/sales/commands/configuration.js +11 -11
  71. package/dist/modules/sales/commands/configuration.js.map +2 -2
  72. package/dist/modules/sales/commands/documentAddresses.js +3 -3
  73. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  74. package/dist/modules/sales/commands/documents.js +31 -39
  75. package/dist/modules/sales/commands/documents.js.map +2 -2
  76. package/dist/modules/sales/commands/notes.js +7 -7
  77. package/dist/modules/sales/commands/notes.js.map +2 -2
  78. package/dist/modules/sales/commands/payments.js +2 -2
  79. package/dist/modules/sales/commands/payments.js.map +2 -2
  80. package/dist/modules/sales/commands/returns.js +8 -8
  81. package/dist/modules/sales/commands/returns.js.map +2 -2
  82. package/dist/modules/sales/commands/shared.js +2 -2
  83. package/dist/modules/sales/commands/shared.js.map +2 -2
  84. package/dist/modules/sales/commands/shipments.js +5 -5
  85. package/dist/modules/sales/commands/shipments.js.map +2 -2
  86. package/dist/modules/sales/commands/statuses.js +2 -2
  87. package/dist/modules/sales/commands/statuses.js.map +2 -2
  88. package/dist/modules/sales/commands/tags.js +3 -3
  89. package/dist/modules/sales/commands/tags.js.map +2 -2
  90. package/package.json +7 -7
  91. package/src/modules/attachments/lib/reconcileOrganization.ts +16 -7
  92. package/src/modules/customer_accounts/i18n/de.json +12 -0
  93. package/src/modules/customer_accounts/i18n/en.json +12 -0
  94. package/src/modules/customer_accounts/i18n/es.json +34 -22
  95. package/src/modules/customer_accounts/i18n/pl.json +32 -20
  96. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -2
  97. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -2
  98. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -2
  99. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +4 -4
  100. package/src/modules/customers/api/entity-roles-factory.ts +3 -3
  101. package/src/modules/customers/api/labels/assign/route.ts +2 -2
  102. package/src/modules/customers/api/labels/unassign/route.ts +2 -2
  103. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +3 -7
  104. package/src/modules/customers/api/people/[id]/companies/context.ts +3 -3
  105. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -2
  106. package/src/modules/customers/api/todos/route.ts +2 -2
  107. package/src/modules/customers/commands/addresses.ts +3 -3
  108. package/src/modules/customers/commands/comments.ts +3 -3
  109. package/src/modules/customers/commands/companies.ts +2 -2
  110. package/src/modules/customers/commands/deals.ts +3 -3
  111. package/src/modules/customers/commands/dictionaries.ts +3 -3
  112. package/src/modules/customers/commands/entity-roles.ts +3 -3
  113. package/src/modules/customers/commands/interactions.ts +6 -6
  114. package/src/modules/customers/commands/labels.ts +7 -7
  115. package/src/modules/customers/commands/people.ts +3 -3
  116. package/src/modules/customers/commands/personCompanyLinks.ts +6 -6
  117. package/src/modules/customers/commands/pipeline-stages.ts +3 -3
  118. package/src/modules/customers/commands/pipelines.ts +3 -3
  119. package/src/modules/customers/commands/shared.ts +3 -3
  120. package/src/modules/customers/commands/tags.ts +7 -7
  121. package/src/modules/customers/commands/todos.ts +3 -3
  122. package/src/modules/customers/lib/personCompanies.ts +4 -4
  123. package/src/modules/perspectives/data/validators.ts +4 -0
  124. package/src/modules/sales/api/notes/route.ts +2 -4
  125. package/src/modules/sales/api/quotes/accept/route.ts +2 -2
  126. package/src/modules/sales/api/quotes/public/[token]/route.ts +3 -7
  127. package/src/modules/sales/api/quotes/send/route.ts +2 -2
  128. package/src/modules/sales/api/returns/[id]/route.ts +2 -2
  129. package/src/modules/sales/commands/configuration.ts +11 -11
  130. package/src/modules/sales/commands/documentAddresses.ts +3 -3
  131. package/src/modules/sales/commands/documents.ts +23 -31
  132. package/src/modules/sales/commands/notes.ts +7 -7
  133. package/src/modules/sales/commands/payments.ts +2 -2
  134. package/src/modules/sales/commands/returns.ts +8 -8
  135. package/src/modules/sales/commands/shared.ts +2 -2
  136. package/src/modules/sales/commands/shipments.ts +5 -5
  137. package/src/modules/sales/commands/statuses.ts +2 -2
  138. package/src/modules/sales/commands/tags.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/api/entity-roles-factory.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerEntity, CustomerEntityRole } from '../data/entities'\nimport { entityRoleCreateSchema, entityRoleUpdateSchema, entityRoleDeleteSchema, type EntityRoleCreateInput, type EntityRoleUpdateInput, type EntityRoleDeleteInput } from '../data/validators'\nimport { withScopedPayload } from './utils'\nimport { resolveCustomersRequestContext, resolveAuthActorId } from '../lib/interactionRequestContext'\nimport { deriveDisplayNameFromEmail } from '../lib/displayName'\nimport { withOperationMetadata } from '../lib/operationMetadata'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\nconst roleIdQuerySchema = z.object({ roleId: z.string().uuid() })\n\nconst createBodySchema = z.object({\n roleType: z.string().trim().min(1).max(100),\n userId: z.string().uuid(),\n})\nconst updateBodySchema = z.object({\n userId: z.string().uuid(),\n})\n\nconst listItemSchema = z.object({\n id: z.string().uuid(),\n entityType: z.enum(['company', 'person']),\n entityId: z.string().uuid(),\n userId: z.string().uuid(),\n userName: z.string().nullable().optional(),\n userEmail: z.string().nullable().optional(),\n userPhone: z.string().nullable().optional(),\n roleType: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst listResponseSchema = z.object({ items: z.array(listItemSchema) })\nconst okResponseSchema = z.object({ ok: z.boolean() })\nconst createResponseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\ntype EntityType = 'company' | 'person'\n\ntype Translator = Awaited<ReturnType<typeof resolveTranslations>>['translate']\n\nfunction getRoleContext(entityType: EntityType, entityId: string) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n return { entityType, entityId, resourceKind, resourceId: entityId }\n}\n\nfunction buildValidationErrorResponse(error: z.ZodError, translate: Translator) {\n return NextResponse.json(\n { error: translate('customers.errors.validationFailed', 'Validation failed'), fieldErrors: error.flatten().fieldErrors },\n { status: 400 },\n )\n}\n\nasync function buildContext(request: Request) {\n const context = await resolveCustomersRequestContext(request)\n return {\n ...context,\n ctx: context.commandContext,\n }\n}\n\nfunction ensureRouteOrganizationAccess(\n organizationId: string,\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n translate: Translator,\n) {\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function ensureFeatureOnOrganization(\n container: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['container'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n feature: string,\n organizationId: string,\n translate: Translator,\n) {\n const actorId = resolveAuthActorId(auth)\n if (!actorId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n let rbac: RbacService | undefined\n try {\n rbac = container.resolve('rbacService') as RbacService | undefined\n } catch (err) {\n logger.error('rbacService resolve failed', { component: 'entity-roles-factory', err })\n rbac = undefined\n }\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const hasFeature = await rbac.userHasAllFeatures(actorId, [feature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function resolveEntityRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n translate: Translator,\n) {\n const entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: entityType, tenantId: auth.tenantId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (!entity || entity.tenantId !== auth.tenantId) {\n throw new CrudHttpError(404, { error: translate('customers.errors.customer_not_found', 'Customer not found') })\n }\n ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate)\n return {\n entity,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n }\n}\n\nasync function resolveRoleRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n roleId: string,\n translate: Translator,\n) {\n const role = await findOneWithDecryption(\n em,\n CustomerEntityRole,\n { id: roleId, tenantId: auth.tenantId, entityType, entityId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (\n !role ||\n role.tenantId !== auth.tenantId ||\n role.entityType !== entityType ||\n role.entityId !== entityId\n ) {\n throw new CrudHttpError(404, { error: translate('customers.errors.role_not_found', 'Role not found') })\n }\n ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate)\n return {\n role,\n organizationId: role.organizationId,\n tenantId: role.tenantId,\n }\n}\n\nfunction createScopedCommandContext(\n ctx: Awaited<ReturnType<typeof buildContext>>['ctx'],\n organizationId: string,\n) {\n return {\n ...ctx,\n selectedOrganizationId: organizationId,\n organizationIds: [organizationId],\n }\n}\n\nexport const entityRolesMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.roles.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n}\n\nexport function buildEntityRolesOpenApi(entityType: EntityType): OpenApiRouteDoc {\n const label = entityType === 'company' ? 'company' : 'person'\n return {\n tag: 'Customers',\n summary: `${label.charAt(0).toUpperCase() + label.slice(1)} role assignments`,\n pathParams: paramsSchema,\n methods: {\n GET: {\n summary: `List roles for a ${label}`,\n responses: [{ status: 200, description: 'Role assignments', schema: listResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: `Assign a role to a ${label}`,\n requestBody: { contentType: 'application/json', schema: createBodySchema },\n responses: [{ status: 201, description: 'Role created', schema: createResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Role already assigned', schema: errorSchema },\n ],\n },\n PUT: {\n summary: `Update a ${label} role assignment`,\n query: roleIdQuerySchema,\n requestBody: { contentType: 'application/json', schema: updateBodySchema },\n responses: [{ status: 200, description: 'Role updated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: `Remove a ${label} role assignment`,\n query: roleIdQuerySchema,\n responses: [{ status: 200, description: 'Role deleted', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n },\n }\n}\n\nexport function createEntityRolesHandlers(entityType: EntityType) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const logPrefix = entityType === 'company' ? 'customers.company.roles' : 'customers.person.roles'\n\n async function GET(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.view', targetScope.organizationId, translate)\n\n const roles = await findWithDecryption(\n em,\n CustomerEntityRole,\n {\n entityType,\n entityId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n deletedAt: null,\n },\n { orderBy: { roleType: 'asc' } },\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n\n const userIds = Array.from(new Set(roles.map((role) => role.userId).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const users = userIds.length\n ? await findWithDecryption(\n em,\n User,\n {\n id: { $in: userIds },\n deletedAt: null,\n ...(targetScope.tenantId ? { tenantId: targetScope.tenantId } : {}),\n },\n undefined,\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n : []\n const userMap = new Map(users.map((user) => [user.id, {\n name: user.name ?? deriveDisplayNameFromEmail(user.email) ?? null,\n email: user.email ?? null,\n phone: null,\n }]))\n\n return NextResponse.json({\n items: roles.map((role) => ({\n ...(userMap.has(role.userId)\n ? {\n userName: userMap.get(role.userId)?.name ?? null,\n userEmail: userMap.get(role.userId)?.email ?? null,\n userPhone: userMap.get(role.userId)?.phone ?? null,\n }\n : {}),\n id: role.id,\n entityType: role.entityType,\n entityId: role.entityId,\n userId: role.userId,\n roleType: role.roleType,\n createdAt: role.createdAt.toISOString(),\n updatedAt: role.updatedAt.toISOString(),\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles GET failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_load_roles', 'Failed to load roles') }, { status: 500 })\n }\n }\n\n async function POST(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n ...getRoleContext(entityType, entityId),\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleCreateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: rawBody,\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<EntityRoleCreateInput, { roleId: string }>(\n 'customers.entityRoles.create',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ id: result?.roleId ?? null }, { status: 201 }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles POST failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_assign_role', 'Failed to assign role') }, { status: 500 })\n }\n }\n\n async function PUT(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleUpdateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId, ...rawBody },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleUpdateInput, { roleId: string }>(\n 'customers.entityRoles.update',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles PUT failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_update_role', 'Failed to update role') }, { status: 500 })\n }\n }\n\n async function DELETE(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const parsed = entityRoleDeleteSchema.parse(\n withScopedPayload(\n {\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n ),\n )\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleDeleteInput, { roleId: string }>(\n 'customers.entityRoles.delete',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles DELETE failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_delete_role', 'Failed to delete role') }, { status: 500 })\n }\n }\n\n return { GET, POST, PUT, DELETE }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerEntity, CustomerEntityRole } from '../data/entities'\nimport { entityRoleCreateSchema, entityRoleUpdateSchema, entityRoleDeleteSchema, type EntityRoleCreateInput, type EntityRoleUpdateInput, type EntityRoleDeleteInput } from '../data/validators'\nimport { withScopedPayload } from './utils'\nimport { resolveCustomersRequestContext, resolveAuthActorId } from '../lib/interactionRequestContext'\nimport { deriveDisplayNameFromEmail } from '../lib/displayName'\nimport { withOperationMetadata } from '../lib/operationMetadata'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\nconst roleIdQuerySchema = z.object({ roleId: z.string().uuid() })\n\nconst createBodySchema = z.object({\n roleType: z.string().trim().min(1).max(100),\n userId: z.string().uuid(),\n})\nconst updateBodySchema = z.object({\n userId: z.string().uuid(),\n})\n\nconst listItemSchema = z.object({\n id: z.string().uuid(),\n entityType: z.enum(['company', 'person']),\n entityId: z.string().uuid(),\n userId: z.string().uuid(),\n userName: z.string().nullable().optional(),\n userEmail: z.string().nullable().optional(),\n userPhone: z.string().nullable().optional(),\n roleType: z.string(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst listResponseSchema = z.object({ items: z.array(listItemSchema) })\nconst okResponseSchema = z.object({ ok: z.boolean() })\nconst createResponseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\ntype EntityType = 'company' | 'person'\n\ntype Translator = Awaited<ReturnType<typeof resolveTranslations>>['translate']\n\nfunction getRoleContext(entityType: EntityType, entityId: string) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n return { entityType, entityId, resourceKind, resourceId: entityId }\n}\n\nfunction buildValidationErrorResponse(error: z.ZodError, translate: Translator) {\n return NextResponse.json(\n { error: translate('customers.errors.validationFailed', 'Validation failed'), fieldErrors: error.flatten().fieldErrors },\n { status: 400 },\n )\n}\n\nasync function buildContext(request: Request) {\n const context = await resolveCustomersRequestContext(request)\n return {\n ...context,\n ctx: context.commandContext,\n }\n}\n\nfunction ensureRouteOrganizationAccess(\n organizationId: string,\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n translate: Translator,\n) {\n if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function ensureFeatureOnOrganization(\n container: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['container'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n feature: string,\n organizationId: string,\n translate: Translator,\n) {\n const actorId = resolveAuthActorId(auth)\n if (!actorId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n let rbac: RbacService | undefined\n try {\n rbac = container.resolve('rbacService') as RbacService | undefined\n } catch (err) {\n logger.error('rbacService resolve failed', { component: 'entity-roles-factory', err })\n rbac = undefined\n }\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const hasFeature = await rbac.userHasAllFeatures(actorId, [feature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n}\n\nasync function resolveEntityRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n translate: Translator,\n) {\n const entity = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: entityId, kind: entityType, tenantId: auth.tenantId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (!entity || entity.tenantId !== auth.tenantId) {\n throw notFound(translate('customers.errors.customer_not_found', 'Customer not found'))\n }\n ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate)\n return {\n entity,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n }\n}\n\nasync function resolveRoleRouteScope(\n em: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['em'],\n auth: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['auth'],\n scope: Awaited<ReturnType<typeof resolveCustomersRequestContext>>['scope'],\n entityType: EntityType,\n entityId: string,\n roleId: string,\n translate: Translator,\n) {\n const role = await findOneWithDecryption(\n em,\n CustomerEntityRole,\n { id: roleId, tenantId: auth.tenantId, entityType, entityId, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },\n )\n if (\n !role ||\n role.tenantId !== auth.tenantId ||\n role.entityType !== entityType ||\n role.entityId !== entityId\n ) {\n throw notFound(translate('customers.errors.role_not_found', 'Role not found'))\n }\n ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate)\n return {\n role,\n organizationId: role.organizationId,\n tenantId: role.tenantId,\n }\n}\n\nfunction createScopedCommandContext(\n ctx: Awaited<ReturnType<typeof buildContext>>['ctx'],\n organizationId: string,\n) {\n return {\n ...ctx,\n selectedOrganizationId: organizationId,\n organizationIds: [organizationId],\n }\n}\n\nexport const entityRolesMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.roles.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.roles.manage'] },\n}\n\nexport function buildEntityRolesOpenApi(entityType: EntityType): OpenApiRouteDoc {\n const label = entityType === 'company' ? 'company' : 'person'\n return {\n tag: 'Customers',\n summary: `${label.charAt(0).toUpperCase() + label.slice(1)} role assignments`,\n pathParams: paramsSchema,\n methods: {\n GET: {\n summary: `List roles for a ${label}`,\n responses: [{ status: 200, description: 'Role assignments', schema: listResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: `Assign a role to a ${label}`,\n requestBody: { contentType: 'application/json', schema: createBodySchema },\n responses: [{ status: 201, description: 'Role created', schema: createResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 409, description: 'Role already assigned', schema: errorSchema },\n ],\n },\n PUT: {\n summary: `Update a ${label} role assignment`,\n query: roleIdQuerySchema,\n requestBody: { contentType: 'application/json', schema: updateBodySchema },\n responses: [{ status: 200, description: 'Role updated', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: `Remove a ${label} role assignment`,\n query: roleIdQuerySchema,\n responses: [{ status: 200, description: 'Role deleted', schema: okResponseSchema }],\n errors: [\n { status: 400, description: 'Invalid request', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 404, description: 'Role not found', schema: errorSchema },\n ],\n },\n },\n }\n}\n\nexport function createEntityRolesHandlers(entityType: EntityType) {\n const resourceKind = entityType === 'company' ? 'customers.company' : 'customers.person'\n const logPrefix = entityType === 'company' ? 'customers.company.roles' : 'customers.person.roles'\n\n async function GET(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.view', targetScope.organizationId, translate)\n\n const roles = await findWithDecryption(\n em,\n CustomerEntityRole,\n {\n entityType,\n entityId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n deletedAt: null,\n },\n { orderBy: { roleType: 'asc' } },\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n\n const userIds = Array.from(new Set(roles.map((role) => role.userId).filter((value): value is string => typeof value === 'string' && value.length > 0)))\n const users = userIds.length\n ? await findWithDecryption(\n em,\n User,\n {\n id: { $in: userIds },\n deletedAt: null,\n ...(targetScope.tenantId ? { tenantId: targetScope.tenantId } : {}),\n },\n undefined,\n {\n tenantId: targetScope.tenantId,\n organizationId: targetScope.organizationId,\n },\n )\n : []\n const userMap = new Map(users.map((user) => [user.id, {\n name: user.name ?? deriveDisplayNameFromEmail(user.email) ?? null,\n email: user.email ?? null,\n phone: null,\n }]))\n\n return NextResponse.json({\n items: roles.map((role) => ({\n ...(userMap.has(role.userId)\n ? {\n userName: userMap.get(role.userId)?.name ?? null,\n userEmail: userMap.get(role.userId)?.email ?? null,\n userPhone: userMap.get(role.userId)?.phone ?? null,\n }\n : {}),\n id: role.id,\n entityType: role.entityType,\n entityId: role.entityId,\n userId: role.userId,\n roleType: role.roleType,\n createdAt: role.createdAt.toISOString(),\n updatedAt: role.updatedAt.toISOString(),\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles GET failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_load_roles', 'Failed to load roles') }, { status: 500 })\n }\n }\n\n async function POST(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveEntityRouteScope(em, auth, scope, entityType, entityId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n ...getRoleContext(entityType, entityId),\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleCreateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: rawBody,\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<EntityRoleCreateInput, { roleId: string }>(\n 'customers.entityRoles.create',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ id: result?.roleId ?? null }, { status: 201 }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles POST failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_assign_role', 'Failed to assign role') }, { status: 500 })\n }\n }\n\n async function PUT(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const rawBody = await readJsonSafe<Record<string, unknown>>(request, {})\n const scoped = withScopedPayload(\n {\n ...rawBody,\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n )\n const parsed = entityRoleUpdateSchema.parse(scoped)\n\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId, ...rawBody },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleUpdateInput, { roleId: string }>(\n 'customers.entityRoles.update',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles PUT failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_update_role', 'Failed to update role') }, { status: 500 })\n }\n }\n\n async function DELETE(request: Request, { params }: { params: { id: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id: entityId } = paramsSchema.parse(params)\n const { roleId } = roleIdQuerySchema.parse(Object.fromEntries(new URL(request.url).searchParams))\n const { container, em, auth, scope, ctx } = await buildContext(request)\n const targetScope = await resolveRoleRouteScope(em, auth, scope, entityType, entityId, roleId, translate)\n await ensureFeatureOnOrganization(container, auth, 'customers.roles.manage', targetScope.organizationId, translate)\n const commandCtx = createScopedCommandContext(ctx, targetScope.organizationId)\n\n const parsed = entityRoleDeleteSchema.parse(\n withScopedPayload(\n {\n id: roleId,\n organizationId: targetScope.organizationId,\n tenantId: targetScope.tenantId,\n },\n commandCtx,\n translate,\n ),\n )\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, mutationPayload: { roleId },\n })\n if (guardResult && !guardResult.ok) return NextResponse.json(guardResult.body, { status: guardResult.status })\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<EntityRoleDeleteInput, { roleId: string }>(\n 'customers.entityRoles.delete',\n { input: parsed, ctx: commandCtx },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: targetScope.tenantId, organizationId: targetScope.organizationId, userId: guardUserId,\n resourceKind, resourceId: entityId, operation: 'custom',\n requestMethod: request.method, requestHeaders: request.headers, metadata: guardResult.metadata ?? null,\n })\n }\n\n return withOperationMetadata(\n NextResponse.json({ ok: true }),\n logEntry,\n { resourceKind, resourceId: entityId },\n )\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) return buildValidationErrorResponse(err, translate)\n logger.error('Entity roles DELETE failed', { component: logPrefix, err })\n return NextResponse.json({ error: translate('customers.errors.failed_to_delete_role', 'Failed to delete role') }, { status: 500 })\n }\n }\n\n return { GET, POST, PUT, DELETE }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { labelAssignCommandSchema, labelAssignmentSchema } from "../../../data/v
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
6
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
- import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { CrudHttpError, isCrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
8
8
  import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
9
9
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
10
10
  import { z } from "zod";
@@ -68,7 +68,7 @@ async function POST(req) {
68
68
  { tenantId: auth.tenantId, organizationId }
69
69
  );
70
70
  if (!targetEntity) {
71
- throw new CrudHttpError(404, { error: translate("customers.errors.entity_not_found", "Entity not found") });
71
+ throw notFound(translate("customers.errors.entity_not_found", "Entity not found"));
72
72
  }
73
73
  const entityKind = targetEntity.kind === "person" || targetEntity.kind === "company" ? targetEntity.kind : null;
74
74
  const resourceKind = resolveResourceKind(entityKind);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/api/labels/assign/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { CustomerEntity } from '../../../data/entities'\nimport { labelAssignCommandSchema, labelAssignmentSchema, type LabelAssignCommandInput } from '../../../data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport { resolveLabelActorUserId } from '../auth'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n createMissingCustomerLabelTablesError,\n isMissingCustomerLabelTable,\n} from '../table-errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nexport const metadata = {\n POST: { requireAuth: true },\n}\n\nconst labelAssignmentRequestSchema = labelAssignmentSchema.extend({\n organizationId: z.string().uuid().optional(),\n})\n\nfunction resolveResourceKind(kind: 'person' | 'company' | null | undefined): string {\n if (kind === 'company') return 'customers.company'\n return 'customers.person'\n}\n\nfunction resolveRequiredFeature(kind: 'person' | 'company' | null | undefined): string {\n return kind === 'company' ? 'customers.companies.manage' : 'customers.people.manage'\n}\n\nexport async function POST(req: Request) {\n try {\n const { translate } = await resolveTranslations()\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveLabelActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('customers.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const body = labelAssignmentRequestSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: body.organizationId ?? undefined,\n })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const targetEntity = await findOneWithDecryption(\n em,\n CustomerEntity,\n {\n id: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: auth.tenantId, organizationId },\n )\n if (!targetEntity) {\n throw new CrudHttpError(404, { error: translate('customers.errors.entity_not_found', 'Entity not found') })\n }\n const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null\n const resourceKind = resolveResourceKind(entityKind)\n\n const rbac = container.resolve('rbacService') as RbacService | undefined\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const requiredFeature = resolveRequiredFeature(entityKind)\n const hasFeature = await rbac.userHasAllFeatures(actorUserId, [requiredFeature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = labelAssignCommandSchema.parse({\n labelId: body.labelId,\n entityId: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n } satisfies LabelAssignCommandInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<LabelAssignCommandInput, { assignmentId: string; created: boolean; entityKind: 'person' | 'company' | null }>(\n 'customers.labels.assign',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ id: result.assignmentId }, { status: result.created ? 201 : 200 })\n if (result.created && logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.labelAssignment',\n resourceId: logEntry.resourceId ?? result.assignmentId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isMissingCustomerLabelTable(err)) {\n const migrationError = await createMissingCustomerLabelTablesError()\n return NextResponse.json(migrationError.body, { status: migrationError.status })\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('customers/labels/assign.POST', { err })\n const { translate: fallbackTranslate } = await resolveTranslations()\n return NextResponse.json(\n { error: fallbackTranslate('customers.errors.failed_to_assign_label', 'Failed to assign label') },\n { status: 500 },\n )\n }\n}\n\nconst responseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Assign label to entity',\n methods: {\n POST: {\n summary: 'Assign label',\n requestBody: { contentType: 'application/json', schema: labelAssignmentRequestSchema },\n responses: [\n { status: 201, description: 'Assigned', schema: responseSchema },\n { status: 200, description: 'Already assigned', schema: responseSchema },\n ],\n errors: [\n { status: 404, description: 'Label or entity not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B,6BAA2D;AAC9F,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,eAAe,uBAAuB;AAK/C,SAAS,kCAAkC;AAC3C,SAAS,6BAA6B;AACtC,SAAS,SAAS;AAClB,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,WAAW;AAEhC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,KAAK;AAC5B;AAEA,MAAM,+BAA+B,sBAAsB,OAAO;AAAA,EAChE,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAC7C,CAAC;AAED,SAAS,oBAAoB,MAAuD;AAClF,MAAI,SAAS,UAAW,QAAO;AAC/B,SAAO;AACT;AAEA,SAAS,uBAAuB,MAAuD;AACrF,SAAO,SAAS,YAAY,+BAA+B;AAC7D;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,UAAM,cAAc,wBAAwB,IAAI;AAChD,QAAI,CAAC,QAAQ,CAAC,KAAK,YAAY,CAAC,aAAa;AAC3C,aAAO,aAAa,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjH;AAEA,UAAM,OAAO,6BAA6B,MAAM,MAAM,aAAa,KAAK,CAAC,CAAC,CAAC;AAC3E,UAAM,QAAQ,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,YAAY,KAAK,kBAAkB;AAAA,IACrC,CAAC;AACD,UAAM,iBAAiB,OAAO,cAAc,KAAK,SAAS;AAC1D,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,0CAA0C,kCAAkC,EAAE,CAAC;AAAA,IACjI;AAEA,UAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,UAAM,eAAe,MAAM;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI,KAAK;AAAA,QACT,UAAU,KAAK;AAAA,QACf;AAAA,QACA,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA,EAAE,UAAU,KAAK,UAAU,eAAe;AAAA,IAC5C;AACA,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,qCAAqC,kBAAkB,EAAE,CAAC;AAAA,IAC5G;AACA,UAAM,aAAa,aAAa,SAAS,YAAY,aAAa,SAAS,YAAY,aAAa,OAAO;AAC3G,UAAM,eAAe,oBAAoB,UAAU;AAEnD,UAAM,OAAO,UAAU,QAAQ,aAAa;AAC5C,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,gBAAgB,EAAE,CAAC;AAAA,IAClG;AACA,UAAM,kBAAkB,uBAAuB,UAAU;AACzD,UAAM,aAAa,MAAM,KAAK,mBAAmB,aAAa,CAAC,eAAe,GAAG;AAAA,MAC/E,UAAU,KAAK;AAAA,MACf;AAAA,IACF,CAAC;AACD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,eAAe,EAAE,CAAC;AAAA,IACtG;AAEA,UAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,MAC7D,UAAU,KAAK;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB;AAAA,IACnB,CAAC;AACD,QAAI,eAAe,CAAC,YAAY,IAAI;AAClC,aAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,IAC3E;AAEA,UAAM,eAAe,yBAAyB,MAAM;AAAA,MAClD,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf;AAAA,IACF,CAAmC;AAEnC,UAAM,aAAa,UAAU,QAAQ,YAAY;AACjD,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW;AAAA,MAC5C;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,KAAK;AAAA,UACH;AAAA,UACA;AAAA,UACA,mBAAmB;AAAA,UACnB,wBAAwB;AAAA,UACxB,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa,MAAM,YAAY,uBAAuB;AACxD,YAAM,iCAAiC,WAAW;AAAA,QAChD,UAAU,KAAK;AAAA,QACf;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,aAAa,KAAK,EAAE,IAAI,OAAO,aAAa,GAAG,EAAE,QAAQ,OAAO,UAAU,MAAM,IAAI,CAAC;AACtG,QAAI,OAAO,WAAW,UAAU,aAAa,SAAS,MAAM,SAAS,WAAW;AAC9E,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc,OAAO;AAAA,UAC1C,YAAY,SAAS,qBAAqB,OAAO,SAAS,UAAU,YAAY,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC7G,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,4BAA4B,GAAG,GAAG;AACpC,YAAM,iBAAiB,MAAM,sCAAsC;AACnE,aAAO,aAAa,KAAK,eAAe,MAAM,EAAE,QAAQ,eAAe,OAAO,CAAC;AAAA,IACjF;AACA,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,WAAO,MAAM,gCAAgC,EAAE,IAAI,CAAC;AACpD,UAAM,EAAE,WAAW,kBAAkB,IAAI,MAAM,oBAAoB;AACnE,WAAO,aAAa;AAAA,MAClB,EAAE,OAAO,kBAAkB,2CAA2C,wBAAwB,EAAE;AAAA,MAChG,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEA,MAAM,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACzD,MAAM,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAE3C,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa,EAAE,aAAa,oBAAoB,QAAQ,6BAA6B;AAAA,MACrF,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,YAAY,QAAQ,eAAe;AAAA,QAC/D,EAAE,QAAQ,KAAK,aAAa,oBAAoB,QAAQ,eAAe;AAAA,MACzE;AAAA,MACA,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,6BAA6B,QAAQ,YAAY;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { CustomerEntity } from '../../../data/entities'\nimport { labelAssignCommandSchema, labelAssignmentSchema, type LabelAssignCommandInput } from '../../../data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport { resolveLabelActorUserId } from '../auth'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n createMissingCustomerLabelTablesError,\n isMissingCustomerLabelTable,\n} from '../table-errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nexport const metadata = {\n POST: { requireAuth: true },\n}\n\nconst labelAssignmentRequestSchema = labelAssignmentSchema.extend({\n organizationId: z.string().uuid().optional(),\n})\n\nfunction resolveResourceKind(kind: 'person' | 'company' | null | undefined): string {\n if (kind === 'company') return 'customers.company'\n return 'customers.person'\n}\n\nfunction resolveRequiredFeature(kind: 'person' | 'company' | null | undefined): string {\n return kind === 'company' ? 'customers.companies.manage' : 'customers.people.manage'\n}\n\nexport async function POST(req: Request) {\n try {\n const { translate } = await resolveTranslations()\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveLabelActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('customers.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const body = labelAssignmentRequestSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: body.organizationId ?? undefined,\n })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const targetEntity = await findOneWithDecryption(\n em,\n CustomerEntity,\n {\n id: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: auth.tenantId, organizationId },\n )\n if (!targetEntity) {\n throw notFound(translate('customers.errors.entity_not_found', 'Entity not found'))\n }\n const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null\n const resourceKind = resolveResourceKind(entityKind)\n\n const rbac = container.resolve('rbacService') as RbacService | undefined\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const requiredFeature = resolveRequiredFeature(entityKind)\n const hasFeature = await rbac.userHasAllFeatures(actorUserId, [requiredFeature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = labelAssignCommandSchema.parse({\n labelId: body.labelId,\n entityId: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n } satisfies LabelAssignCommandInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<LabelAssignCommandInput, { assignmentId: string; created: boolean; entityKind: 'person' | 'company' | null }>(\n 'customers.labels.assign',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ id: result.assignmentId }, { status: result.created ? 201 : 200 })\n if (result.created && logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.labelAssignment',\n resourceId: logEntry.resourceId ?? result.assignmentId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isMissingCustomerLabelTable(err)) {\n const migrationError = await createMissingCustomerLabelTablesError()\n return NextResponse.json(migrationError.body, { status: migrationError.status })\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('customers/labels/assign.POST', { err })\n const { translate: fallbackTranslate } = await resolveTranslations()\n return NextResponse.json(\n { error: fallbackTranslate('customers.errors.failed_to_assign_label', 'Failed to assign label') },\n { status: 500 },\n )\n }\n}\n\nconst responseSchema = z.object({ id: z.string().uuid() })\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Assign label to entity',\n methods: {\n POST: {\n summary: 'Assign label',\n requestBody: { contentType: 'application/json', schema: labelAssignmentRequestSchema },\n responses: [\n { status: 201, description: 'Assigned', schema: responseSchema },\n { status: 200, description: 'Already assigned', schema: responseSchema },\n ],\n errors: [\n { status: 404, description: 'Label or entity not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { labelUnassignCommandSchema, labelAssignmentSchema } from "../../../data
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
6
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
- import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { CrudHttpError, isCrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
8
8
  import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
9
9
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
10
10
  import { z } from "zod";
@@ -68,7 +68,7 @@ async function POST(req) {
68
68
  { tenantId: auth.tenantId, organizationId }
69
69
  );
70
70
  if (!targetEntity) {
71
- throw new CrudHttpError(404, { error: translate("customers.errors.entity_not_found", "Entity not found") });
71
+ throw notFound(translate("customers.errors.entity_not_found", "Entity not found"));
72
72
  }
73
73
  const entityKind = targetEntity.kind === "person" || targetEntity.kind === "company" ? targetEntity.kind : null;
74
74
  const resourceKind = resolveResourceKind(entityKind);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/api/labels/unassign/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { CustomerEntity } from '../../../data/entities'\nimport { labelUnassignCommandSchema, labelAssignmentSchema, type LabelUnassignCommandInput } from '../../../data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport { resolveLabelActorUserId } from '../auth'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n createMissingCustomerLabelTablesError,\n isMissingCustomerLabelTable,\n} from '../table-errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nexport const metadata = {\n POST: { requireAuth: true },\n}\n\nconst labelAssignmentRequestSchema = labelAssignmentSchema.extend({\n organizationId: z.string().uuid().optional(),\n})\n\nfunction resolveResourceKind(kind: 'person' | 'company' | null | undefined): string {\n if (kind === 'company') return 'customers.company'\n return 'customers.person'\n}\n\nfunction resolveRequiredFeature(kind: 'person' | 'company' | null | undefined): string {\n return kind === 'company' ? 'customers.companies.manage' : 'customers.people.manage'\n}\n\nexport async function POST(req: Request) {\n try {\n const { translate } = await resolveTranslations()\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveLabelActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('customers.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const body = labelAssignmentRequestSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: body.organizationId ?? undefined,\n })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const targetEntity = await findOneWithDecryption(\n em,\n CustomerEntity,\n {\n id: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: auth.tenantId, organizationId },\n )\n if (!targetEntity) {\n throw new CrudHttpError(404, { error: translate('customers.errors.entity_not_found', 'Entity not found') })\n }\n const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null\n const resourceKind = resolveResourceKind(entityKind)\n\n const rbac = container.resolve('rbacService') as RbacService | undefined\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const requiredFeature = resolveRequiredFeature(entityKind)\n const hasFeature = await rbac.userHasAllFeatures(actorUserId, [requiredFeature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = labelUnassignCommandSchema.parse({\n labelId: body.labelId,\n entityId: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n } satisfies LabelUnassignCommandInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<LabelUnassignCommandInput, { assignmentId: string | null; entityKind: 'person' | 'company' | null }>(\n 'customers.labels.unassign',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ id: result.assignmentId })\n if (result.assignmentId && logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.labelAssignment',\n resourceId: logEntry.resourceId ?? result.assignmentId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isMissingCustomerLabelTable(err)) {\n const migrationError = await createMissingCustomerLabelTablesError()\n return NextResponse.json(migrationError.body, { status: migrationError.status })\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('customers/labels/unassign.POST', { err })\n return NextResponse.json({ error: 'Failed to unassign label' }, { status: 500 })\n }\n}\n\nconst responseSchema = z.object({ id: z.string().uuid().nullable() })\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Unassign label from entity',\n methods: {\n POST: {\n summary: 'Unassign label',\n requestBody: { contentType: 'application/json', schema: labelAssignmentRequestSchema },\n responses: [\n { status: 200, description: 'Unassigned', schema: responseSchema },\n ],\n errors: [\n { status: 404, description: 'Label or entity not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { CustomerEntity } from '../../../data/entities'\nimport { labelUnassignCommandSchema, labelAssignmentSchema, type LabelUnassignCommandInput } from '../../../data/validators'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { z } from 'zod'\nimport { resolveLabelActorUserId } from '../auth'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport {\n createMissingCustomerLabelTablesError,\n isMissingCustomerLabelTable,\n} from '../table-errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customers')\n\nexport const metadata = {\n POST: { requireAuth: true },\n}\n\nconst labelAssignmentRequestSchema = labelAssignmentSchema.extend({\n organizationId: z.string().uuid().optional(),\n})\n\nfunction resolveResourceKind(kind: 'person' | 'company' | null | undefined): string {\n if (kind === 'company') return 'customers.company'\n return 'customers.person'\n}\n\nfunction resolveRequiredFeature(kind: 'person' | 'company' | null | undefined): string {\n return kind === 'company' ? 'customers.companies.manage' : 'customers.people.manage'\n}\n\nexport async function POST(req: Request) {\n try {\n const { translate } = await resolveTranslations()\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const actorUserId = resolveLabelActorUserId(auth)\n if (!auth || !auth.tenantId || !actorUserId) {\n return NextResponse.json({ error: translate('customers.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n\n const body = labelAssignmentRequestSchema.parse(await readJsonSafe(req, {}))\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n selectedId: body.organizationId ?? undefined,\n })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const targetEntity = await findOneWithDecryption(\n em,\n CustomerEntity,\n {\n id: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: auth.tenantId, organizationId },\n )\n if (!targetEntity) {\n throw notFound(translate('customers.errors.entity_not_found', 'Entity not found'))\n }\n const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null\n const resourceKind = resolveResourceKind(entityKind)\n\n const rbac = container.resolve('rbacService') as RbacService | undefined\n if (!rbac) {\n throw new CrudHttpError(500, { error: translate('customers.errors.internal', 'Internal error') })\n }\n const requiredFeature = resolveRequiredFeature(entityKind)\n const hasFeature = await rbac.userHasAllFeatures(actorUserId, [requiredFeature], {\n tenantId: auth.tenantId,\n organizationId,\n })\n if (!hasFeature) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: body,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const commandInput = labelUnassignCommandSchema.parse({\n labelId: body.labelId,\n entityId: body.entityId,\n tenantId: auth.tenantId,\n organizationId,\n } satisfies LabelUnassignCommandInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<LabelUnassignCommandInput, { assignmentId: string | null; entityKind: 'person' | 'company' | null }>(\n 'customers.labels.unassign',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId,\n userId: actorUserId,\n resourceKind,\n resourceId: body.entityId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ id: result.assignmentId })\n if (result.assignmentId && logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.labelAssignment',\n resourceId: logEntry.resourceId ?? result.assignmentId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isMissingCustomerLabelTable(err)) {\n const migrationError = await createMissingCustomerLabelTablesError()\n return NextResponse.json(migrationError.body, { status: migrationError.status })\n }\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('customers/labels/unassign.POST', { err })\n return NextResponse.json({ error: 'Failed to unassign label' }, { status: 500 })\n }\n}\n\nconst responseSchema = z.object({ id: z.string().uuid().nullable() })\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Unassign label from entity',\n methods: {\n POST: {\n summary: 'Unassign label',\n requestBody: { contentType: 'application/json', schema: labelAssignmentRequestSchema },\n responses: [\n { status: 200, description: 'Unassigned', schema: responseSchema },\n ],\n errors: [\n { status: 404, description: 'Label or entity not found', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
- import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
+ import { CrudHttpError, isCrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
4
4
  import {
5
5
  runCrudMutationGuardAfterSuccess,
6
6
  validateCrudMutationGuard
@@ -113,9 +113,7 @@ async function PATCH(req, ctx) {
113
113
  selectedOrganizationId
114
114
  );
115
115
  if (!resolvedLinkId) {
116
- throw new CrudHttpError(404, {
117
- error: translate("customers.errors.person_company_link_not_found", "Person-company link not found")
118
- });
116
+ throw notFound(translate("customers.errors.person_company_link_not_found", "Person-company link not found"));
119
117
  }
120
118
  const commandInput = personCompanyLinkUpdateSchema.parse({
121
119
  linkId: resolvedLinkId,
@@ -223,9 +221,7 @@ async function DELETE(req, ctx) {
223
221
  selectedOrganizationId
224
222
  );
225
223
  if (!resolvedLinkId) {
226
- throw new CrudHttpError(404, {
227
- error: translate("customers.errors.person_company_link_not_found", "Person-company link not found")
228
- });
224
+ throw notFound(translate("customers.errors.person_company_link_not_found", "Person-company link not found"));
229
225
  }
230
226
  const commandInput = personCompanyLinkDeleteSchema.parse({
231
227
  linkId: resolvedLinkId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/customers/api/people/%5Bid%5D/companies/%5BlinkId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveAuthActorId } from '@open-mercato/core/modules/customers/lib/interactionRequestContext'\nimport { CustomerPersonCompanyLink } from '@open-mercato/core/modules/customers/data/entities'\nimport {\n personCompanyLinkDeleteSchema,\n personCompanyLinkUpdateSchema,\n type PersonCompanyLinkDeleteInput,\n type PersonCompanyLinkUpdateInput,\n} from '@open-mercato/core/modules/customers/data/validators'\nimport { loadPersonContext } from '../context'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n linkId: z.string().uuid(),\n})\n\n/**\n * Resolve the second path segment to a concrete `customer_person_company_links` row id.\n * The UI on company/person detail pages calls this route with the company entity id as\n * the last segment (symmetric with the other company-centric endpoints); older callers\n * may pass the actual link row id. We try a direct id lookup first, then fall back to\n * resolving via `(person, company)` pair.\n */\nasync function resolveLinkId(\n em: EntityManager,\n rawLinkId: string,\n personEntityId: string,\n tenantId: string,\n organizationId: string | null,\n): Promise<string | null> {\n const decryptionScope = { tenantId, organizationId }\n const directFilter: FilterQuery<CustomerPersonCompanyLink> = organizationId\n ? { id: rawLinkId, tenantId, organizationId, deletedAt: null }\n : { id: rawLinkId, tenantId, deletedAt: null }\n const direct = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n directFilter,\n undefined,\n decryptionScope,\n )\n if (direct) return direct.id\n\n const companyFilter: FilterQuery<CustomerPersonCompanyLink> = organizationId\n ? {\n person: personEntityId,\n company: rawLinkId,\n tenantId,\n organizationId,\n deletedAt: null,\n }\n : { person: personEntityId, company: rawLinkId, tenantId, deletedAt: null }\n const viaCompany = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n companyFilter,\n undefined,\n decryptionScope,\n )\n return viaCompany?.id ?? null\n}\n\nconst updateSchema = z.object({\n isPrimary: z.boolean().optional(),\n})\n\nconst updateResponseSchema = z.object({\n ok: z.literal(true),\n result: z\n .object({\n id: z.string().uuid(),\n companyId: z.string().uuid(),\n displayName: z.string(),\n isPrimary: z.boolean(),\n })\n .nullable(),\n})\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n PATCH: {\n summary: 'Update a linked company for a person',\n requestBody: { schema: updateSchema },\n responses: [{ status: 200, description: 'Updated company link', schema: updateResponseSchema }],\n },\n DELETE: {\n summary: 'Remove a linked company from a person',\n responses: [{ status: 200, description: 'Deletion result', schema: z.object({ ok: z.literal(true) }) }],\n },\n },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { id?: string; linkId?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id, linkId } = paramsSchema.parse({ id: ctx.params?.id, linkId: ctx.params?.linkId })\n const payload = updateSchema.parse(await readJsonSafe(req, {}))\n const { container, auth, selectedOrganizationId, person } = await loadPersonContext(req, id)\n if (!selectedOrganizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (payload.isPrimary === undefined) {\n return NextResponse.json({ ok: true as const, result: null })\n }\n\n const resolveEm = (container.resolve('em') as EntityManager).fork()\n const resolvedLinkId = await resolveLinkId(\n resolveEm,\n linkId,\n person.id,\n auth.tenantId,\n selectedOrganizationId,\n )\n if (!resolvedLinkId) {\n throw new CrudHttpError(404, {\n error: translate('customers.errors.person_company_link_not_found', 'Person-company link not found'),\n })\n }\n\n const commandInput = personCompanyLinkUpdateSchema.parse({\n linkId: resolvedLinkId,\n isPrimary: payload.isPrimary,\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n } satisfies PersonCompanyLinkUpdateInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<PersonCompanyLinkUpdateInput, { linkId: string }>(\n 'customers.personCompanyLinks.update',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId,\n organizationIds: [selectedOrganizationId],\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const freshEm = (container.resolve('em') as EntityManager).fork()\n const linkRecord = await findOneWithDecryption(\n freshEm,\n CustomerPersonCompanyLink,\n { id: result.linkId, tenantId: auth.tenantId, organizationId: selectedOrganizationId },\n { populate: ['company'] },\n { tenantId: auth.tenantId, organizationId: selectedOrganizationId },\n )\n const company = linkRecord && typeof linkRecord.company !== 'string' ? linkRecord.company : null\n const response = NextResponse.json({\n ok: true as const,\n result: linkRecord\n ? {\n id: linkRecord.id,\n companyId: company?.id ?? '',\n displayName: company?.displayName ?? '',\n isPrimary: Boolean(linkRecord.isPrimary),\n }\n : null,\n })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.personCompanyLink',\n resourceId: logEntry.resourceId ?? result.linkId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { id?: string; linkId?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id, linkId } = paramsSchema.parse({ id: ctx.params?.id, linkId: ctx.params?.linkId })\n const { container, auth, selectedOrganizationId, person } = await loadPersonContext(req, id)\n if (!selectedOrganizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { id: linkId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const resolveEm = (container.resolve('em') as EntityManager).fork()\n const resolvedLinkId = await resolveLinkId(\n resolveEm,\n linkId,\n person.id,\n auth.tenantId,\n selectedOrganizationId,\n )\n if (!resolvedLinkId) {\n throw new CrudHttpError(404, {\n error: translate('customers.errors.person_company_link_not_found', 'Person-company link not found'),\n })\n }\n\n const commandInput = personCompanyLinkDeleteSchema.parse({\n linkId: resolvedLinkId,\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n } satisfies PersonCompanyLinkDeleteInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<PersonCompanyLinkDeleteInput, { linkId: string }>(\n 'customers.personCompanyLinks.delete',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId,\n organizationIds: [selectedOrganizationId],\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ ok: true as const })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.personCompanyLink',\n resourceId: logEntry.resourceId ?? result.linkId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveAuthActorId } from '@open-mercato/core/modules/customers/lib/interactionRequestContext'\nimport { CustomerPersonCompanyLink } from '@open-mercato/core/modules/customers/data/entities'\nimport {\n personCompanyLinkDeleteSchema,\n personCompanyLinkUpdateSchema,\n type PersonCompanyLinkDeleteInput,\n type PersonCompanyLinkUpdateInput,\n} from '@open-mercato/core/modules/customers/data/validators'\nimport { loadPersonContext } from '../context'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\n\nconst paramsSchema = z.object({\n id: z.string().uuid(),\n linkId: z.string().uuid(),\n})\n\n/**\n * Resolve the second path segment to a concrete `customer_person_company_links` row id.\n * The UI on company/person detail pages calls this route with the company entity id as\n * the last segment (symmetric with the other company-centric endpoints); older callers\n * may pass the actual link row id. We try a direct id lookup first, then fall back to\n * resolving via `(person, company)` pair.\n */\nasync function resolveLinkId(\n em: EntityManager,\n rawLinkId: string,\n personEntityId: string,\n tenantId: string,\n organizationId: string | null,\n): Promise<string | null> {\n const decryptionScope = { tenantId, organizationId }\n const directFilter: FilterQuery<CustomerPersonCompanyLink> = organizationId\n ? { id: rawLinkId, tenantId, organizationId, deletedAt: null }\n : { id: rawLinkId, tenantId, deletedAt: null }\n const direct = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n directFilter,\n undefined,\n decryptionScope,\n )\n if (direct) return direct.id\n\n const companyFilter: FilterQuery<CustomerPersonCompanyLink> = organizationId\n ? {\n person: personEntityId,\n company: rawLinkId,\n tenantId,\n organizationId,\n deletedAt: null,\n }\n : { person: personEntityId, company: rawLinkId, tenantId, deletedAt: null }\n const viaCompany = await findOneWithDecryption(\n em,\n CustomerPersonCompanyLink,\n companyFilter,\n undefined,\n decryptionScope,\n )\n return viaCompany?.id ?? null\n}\n\nconst updateSchema = z.object({\n isPrimary: z.boolean().optional(),\n})\n\nconst updateResponseSchema = z.object({\n ok: z.literal(true),\n result: z\n .object({\n id: z.string().uuid(),\n companyId: z.string().uuid(),\n displayName: z.string(),\n isPrimary: z.boolean(),\n })\n .nullable(),\n})\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n methods: {\n PATCH: {\n summary: 'Update a linked company for a person',\n requestBody: { schema: updateSchema },\n responses: [{ status: 200, description: 'Updated company link', schema: updateResponseSchema }],\n },\n DELETE: {\n summary: 'Remove a linked company from a person',\n responses: [{ status: 200, description: 'Deletion result', schema: z.object({ ok: z.literal(true) }) }],\n },\n },\n}\n\nexport async function PATCH(req: Request, ctx: { params?: { id?: string; linkId?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id, linkId } = paramsSchema.parse({ id: ctx.params?.id, linkId: ctx.params?.linkId })\n const payload = updateSchema.parse(await readJsonSafe(req, {}))\n const { container, auth, selectedOrganizationId, person } = await loadPersonContext(req, id)\n if (!selectedOrganizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: payload,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n if (payload.isPrimary === undefined) {\n return NextResponse.json({ ok: true as const, result: null })\n }\n\n const resolveEm = (container.resolve('em') as EntityManager).fork()\n const resolvedLinkId = await resolveLinkId(\n resolveEm,\n linkId,\n person.id,\n auth.tenantId,\n selectedOrganizationId,\n )\n if (!resolvedLinkId) {\n throw notFound(translate('customers.errors.person_company_link_not_found', 'Person-company link not found'))\n }\n\n const commandInput = personCompanyLinkUpdateSchema.parse({\n linkId: resolvedLinkId,\n isPrimary: payload.isPrimary,\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n } satisfies PersonCompanyLinkUpdateInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<PersonCompanyLinkUpdateInput, { linkId: string }>(\n 'customers.personCompanyLinks.update',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId,\n organizationIds: [selectedOrganizationId],\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const freshEm = (container.resolve('em') as EntityManager).fork()\n const linkRecord = await findOneWithDecryption(\n freshEm,\n CustomerPersonCompanyLink,\n { id: result.linkId, tenantId: auth.tenantId, organizationId: selectedOrganizationId },\n { populate: ['company'] },\n { tenantId: auth.tenantId, organizationId: selectedOrganizationId },\n )\n const company = linkRecord && typeof linkRecord.company !== 'string' ? linkRecord.company : null\n const response = NextResponse.json({\n ok: true as const,\n result: linkRecord\n ? {\n id: linkRecord.id,\n companyId: company?.id ?? '',\n displayName: company?.displayName ?? '',\n isPrimary: Boolean(linkRecord.isPrimary),\n }\n : null,\n })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.personCompanyLink',\n resourceId: logEntry.resourceId ?? result.linkId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { id?: string; linkId?: string } }) {\n const { translate } = await resolveTranslations()\n try {\n const { id, linkId } = paramsSchema.parse({ id: ctx.params?.id, linkId: ctx.params?.linkId })\n const { container, auth, selectedOrganizationId, person } = await loadPersonContext(req, id)\n if (!selectedOrganizationId) {\n throw new CrudHttpError(400, { error: translate('customers.errors.organization_required', 'Organization context is required') })\n }\n const guardUserId = resolveAuthActorId(auth)\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { id: linkId },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const resolveEm = (container.resolve('em') as EntityManager).fork()\n const resolvedLinkId = await resolveLinkId(\n resolveEm,\n linkId,\n person.id,\n auth.tenantId,\n selectedOrganizationId,\n )\n if (!resolvedLinkId) {\n throw notFound(translate('customers.errors.person_company_link_not_found', 'Person-company link not found'))\n }\n\n const commandInput = personCompanyLinkDeleteSchema.parse({\n linkId: resolvedLinkId,\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n } satisfies PersonCompanyLinkDeleteInput)\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<PersonCompanyLinkDeleteInput, { linkId: string }>(\n 'customers.personCompanyLinks.delete',\n {\n input: commandInput,\n ctx: {\n container,\n auth,\n organizationScope: null,\n selectedOrganizationId,\n organizationIds: [selectedOrganizationId],\n request: req,\n },\n },\n )\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: selectedOrganizationId,\n userId: guardUserId,\n resourceKind: 'customers.person',\n resourceId: person.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = NextResponse.json({ ok: true as const })\n if (logEntry?.undoToken && logEntry.id && logEntry.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'customers.personCompanyLink',\n resourceId: logEntry.resourceId ?? result.linkId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : new Date().toISOString(),\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n return NextResponse.json({ error: translate('customers.errors.internal', 'Internal server error') }, { status: 500 })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
1
+ import { CrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
2
2
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
3
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
4
  import {
@@ -31,7 +31,7 @@ async function loadPersonContext(req, personId) {
31
31
  decryptionScope
32
32
  );
33
33
  if (!person) {
34
- throw new CrudHttpError(404, { error: translate("customers.errors.person_not_found", "Person not found") });
34
+ throw notFound(translate("customers.errors.person_not_found", "Person not found"));
35
35
  }
36
36
  if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {
37
37
  throw new CrudHttpError(403, { error: translate("customers.errors.access_denied", "Access denied") });
@@ -47,7 +47,7 @@ async function loadPersonContext(req, personId) {
47
47
  }
48
48
  );
49
49
  if (!profile) {
50
- throw new CrudHttpError(404, { error: translate("customers.errors.person_profile_not_found", "Person profile not found") });
50
+ throw notFound(translate("customers.errors.person_profile_not_found", "Person profile not found"));
51
51
  }
52
52
  return {
53
53
  container,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/customers/api/people/%5Bid%5D/companies/context.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n CustomerEntity,\n CustomerPersonProfile,\n} from '@open-mercato/core/modules/customers/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport async function loadPersonContext(req: Request, personId: string) {\n const { translate } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const authenticatedAuth = auth as typeof auth & { tenantId: string }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth: authenticatedAuth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n const decryptionScope = {\n tenantId: authenticatedAuth.tenantId,\n organizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n }\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: personId, kind: 'person', tenantId: authenticatedAuth.tenantId, deletedAt: null },\n {},\n decryptionScope,\n )\n\n if (!person) {\n throw new CrudHttpError(404, { error: translate('customers.errors.person_not_found', 'Person not found') })\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person, tenantId: person.tenantId, organizationId: person.organizationId },\n { populate: ['company'] },\n {\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n )\n if (!profile) {\n throw new CrudHttpError(404, { error: translate('customers.errors.person_profile_not_found', 'Person profile not found') })\n }\n\n return {\n container,\n auth: authenticatedAuth,\n selectedOrganizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n em,\n person,\n profile,\n }\n}\n"],
5
- "mappings": "AACA,SAAS,qBAAqB;AAC9B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AACnD,SAAS,uCAAuC;AAChD,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AAEtC,eAAsB,kBAAkB,KAAc,UAAkB;AACtE,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAAA,EACpG;AACA,QAAM,oBAAoB;AAE1B,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,mBAAmB,SAAS,IAAI,CAAC;AAC3G,QAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,QAAM,kBAAkB;AAAA,IACtB,UAAU,kBAAkB;AAAA,IAC5B,gBAAgB,OAAO,cAAc,kBAAkB,SAAS;AAAA,EAClE;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,UAAU,MAAM,UAAU,UAAU,kBAAkB,UAAU,WAAW,KAAK;AAAA,IACtF,CAAC;AAAA,IACD;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,qCAAqC,kBAAkB,EAAE,CAAC;AAAA,EAC5G;AAEA,MAAI,CAAC,gCAAgC,EAAE,OAAO,MAAM,mBAAmB,gBAAgB,OAAO,eAAe,CAAC,GAAG;AAC/G,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,eAAe,EAAE,CAAC;AAAA,EACtG;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,QAAQ,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,IACnF,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,IACxB;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6CAA6C,0BAA0B,EAAE,CAAC;AAAA,EAC5H;AAEA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,wBAAwB,OAAO,cAAc,kBAAkB,SAAS;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n CustomerEntity,\n CustomerPersonProfile,\n} from '@open-mercato/core/modules/customers/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport async function loadPersonContext(req: Request, personId: string) {\n const { translate } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n throw new CrudHttpError(401, { error: translate('customers.errors.unauthorized', 'Unauthorized') })\n }\n const authenticatedAuth = auth as typeof auth & { tenantId: string }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth: authenticatedAuth, request: req })\n const em = (container.resolve('em') as EntityManager).fork()\n const decryptionScope = {\n tenantId: authenticatedAuth.tenantId,\n organizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n }\n const person = await findOneWithDecryption(\n em,\n CustomerEntity,\n { id: personId, kind: 'person', tenantId: authenticatedAuth.tenantId, deletedAt: null },\n {},\n decryptionScope,\n )\n\n if (!person) {\n throw notFound(translate('customers.errors.person_not_found', 'Person not found'))\n }\n\n if (!isOrganizationReadAccessAllowed({ scope, auth: authenticatedAuth, organizationId: person.organizationId })) {\n throw new CrudHttpError(403, { error: translate('customers.errors.access_denied', 'Access denied') })\n }\n\n const profile = await findOneWithDecryption(\n em,\n CustomerPersonProfile,\n { entity: person, tenantId: person.tenantId, organizationId: person.organizationId },\n { populate: ['company'] },\n {\n tenantId: person.tenantId,\n organizationId: person.organizationId,\n },\n )\n if (!profile) {\n throw notFound(translate('customers.errors.person_profile_not_found', 'Person profile not found'))\n }\n\n return {\n container,\n auth: authenticatedAuth,\n selectedOrganizationId: scope?.selectedId ?? authenticatedAuth.orgId ?? null,\n em,\n person,\n profile,\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,eAAe,gBAAgB;AACxC,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0CAA0C;AACnD,SAAS,uCAAuC;AAChD,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AAEtC,eAAsB,kBAAkB,KAAc,UAAkB;AACtE,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,iCAAiC,cAAc,EAAE,CAAC;AAAA,EACpG;AACA,QAAM,oBAAoB;AAE1B,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,mBAAmB,SAAS,IAAI,CAAC;AAC3G,QAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,QAAM,kBAAkB;AAAA,IACtB,UAAU,kBAAkB;AAAA,IAC5B,gBAAgB,OAAO,cAAc,kBAAkB,SAAS;AAAA,EAClE;AACA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,UAAU,MAAM,UAAU,UAAU,kBAAkB,UAAU,WAAW,KAAK;AAAA,IACtF,CAAC;AAAA,IACD;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,UAAM,SAAS,UAAU,qCAAqC,kBAAkB,CAAC;AAAA,EACnF;AAEA,MAAI,CAAC,gCAAgC,EAAE,OAAO,MAAM,mBAAmB,gBAAgB,OAAO,eAAe,CAAC,GAAG;AAC/G,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,eAAe,EAAE,CAAC;AAAA,EACtG;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,QAAQ,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,IACnF,EAAE,UAAU,CAAC,SAAS,EAAE;AAAA,IACxB;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AACA,MAAI,CAAC,SAAS;AACZ,UAAM,SAAS,UAAU,6CAA6C,0BAA0B,CAAC;AAAA,EACnG;AAEA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,wBAAwB,OAAO,cAAc,kBAAkB,SAAS;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
- import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
+ import { CrudHttpError, isCrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
6
6
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
@@ -149,7 +149,7 @@ async function GET(req, ctx) {
149
149
  const decryptionScope = { tenantId: auth.tenantId, organizationId: auth.orgId ?? null };
150
150
  const person = await findOneWithDecryption(em, CustomerEntity, { id, kind: "person", tenantId: auth.tenantId, deletedAt: null }, {}, decryptionScope);
151
151
  if (!person) {
152
- throw new CrudHttpError(404, { error: translate("customers.errors.person_not_found", "Person not found") });
152
+ throw notFound(translate("customers.errors.person_not_found", "Person not found"));
153
153
  }
154
154
  if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: person.organizationId })) {
155
155
  throw new CrudHttpError(403, { error: translate("customers.errors.access_denied", "Access denied") });