@open-mercato/core 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7131.1.20abf9e885

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 (203) hide show
  1. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
  2. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  3. package/dist/modules/auth/api/profile/route.js +5 -0
  4. package/dist/modules/auth/api/profile/route.js.map +2 -2
  5. package/dist/modules/customers/api/activities/route.js +13 -0
  6. package/dist/modules/customers/api/activities/route.js.map +2 -2
  7. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
  8. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  9. package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
  10. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  11. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
  12. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
  13. package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
  14. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  15. package/dist/modules/customers/api/interactions/complete/route.js +5 -0
  16. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  17. package/dist/modules/customers/api/labels/assign/route.js +5 -0
  18. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  19. package/dist/modules/customers/api/labels/route.js +5 -0
  20. package/dist/modules/customers/api/labels/route.js.map +2 -2
  21. package/dist/modules/customers/api/labels/unassign/route.js +5 -0
  22. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  23. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
  24. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  25. package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
  26. package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
  27. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
  28. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  29. package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
  30. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  31. package/dist/modules/customers/api/pipelines/route.js +13 -0
  32. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  33. package/dist/modules/customers/api/settings/address-format/route.js +5 -0
  34. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  35. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
  36. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
  37. package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
  38. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
  39. package/dist/modules/customers/api/tags/assign/route.js +5 -0
  40. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  41. package/dist/modules/customers/api/tags/unassign/route.js +5 -0
  42. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  43. package/dist/modules/customers/api/todos/route.js +13 -0
  44. package/dist/modules/customers/api/todos/route.js.map +2 -2
  45. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
  46. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  47. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
  48. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
  49. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
  50. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  51. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
  52. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
  53. package/dist/modules/directory/api/organization-branding/route.js +5 -0
  54. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  55. package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
  56. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
  57. package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
  58. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  59. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
  60. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
  61. package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
  62. package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
  63. package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
  64. package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
  65. package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
  66. package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
  67. package/dist/modules/messages/api/[id]/forward/route.js +5 -0
  68. package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
  69. package/dist/modules/messages/api/[id]/reply/route.js +5 -0
  70. package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
  71. package/dist/modules/messages/api/[id]/route.js +9 -0
  72. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  73. package/dist/modules/messages/api/token/[token]/route.js +5 -0
  74. package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
  75. package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
  76. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  77. package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
  78. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  79. package/dist/modules/sales/api/quotes/accept/route.js +5 -0
  80. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  81. package/dist/modules/sales/api/quotes/convert/route.js +5 -0
  82. package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
  83. package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
  84. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  85. package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
  86. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  87. package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
  88. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  89. package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
  90. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  91. package/dist/modules/staff/api/team-members/self/route.js +5 -0
  92. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  93. package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
  94. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  95. package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
  96. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  97. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
  98. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  99. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
  100. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  101. package/dist/modules/warranty_claims/api/ai/assess/route.js +5 -0
  102. package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
  103. package/dist/modules/warranty_claims/api/assign/route.js +5 -0
  104. package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
  105. package/dist/modules/warranty_claims/api/credit-memo/route.js +5 -0
  106. package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
  107. package/dist/modules/warranty_claims/api/events/route.js +5 -0
  108. package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
  109. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +5 -0
  110. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
  111. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +5 -0
  112. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
  113. package/dist/modules/warranty_claims/api/portal/claims/route.js +5 -0
  114. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  115. package/dist/modules/warranty_claims/api/portal/events/route.js +5 -0
  116. package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
  117. package/dist/modules/warranty_claims/api/receiving/route.js +5 -0
  118. package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
  119. package/dist/modules/warranty_claims/api/replacement-order/route.js +5 -0
  120. package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
  121. package/dist/modules/warranty_claims/api/return-label/route.js +5 -0
  122. package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
  123. package/dist/modules/warranty_claims/api/sales-return/route.js +5 -0
  124. package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
  125. package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
  126. package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
  127. package/dist/modules/warranty_claims/api/submit/route.js +5 -0
  128. package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
  129. package/dist/modules/warranty_claims/api/transition/route.js +5 -0
  130. package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
  131. package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
  132. package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
  133. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
  134. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
  135. package/package.json +7 -7
  136. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
  137. package/src/modules/auth/api/profile/route.ts +5 -0
  138. package/src/modules/customers/api/activities/route.ts +13 -0
  139. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
  140. package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
  141. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
  142. package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
  143. package/src/modules/customers/api/interactions/complete/route.ts +5 -0
  144. package/src/modules/customers/api/labels/assign/route.ts +5 -0
  145. package/src/modules/customers/api/labels/route.ts +5 -0
  146. package/src/modules/customers/api/labels/unassign/route.ts +5 -0
  147. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
  148. package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
  149. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
  150. package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
  151. package/src/modules/customers/api/pipelines/route.ts +13 -0
  152. package/src/modules/customers/api/settings/address-format/route.ts +5 -0
  153. package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
  154. package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
  155. package/src/modules/customers/api/tags/assign/route.ts +5 -0
  156. package/src/modules/customers/api/tags/unassign/route.ts +5 -0
  157. package/src/modules/customers/api/todos/route.ts +13 -0
  158. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
  159. package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
  160. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
  161. package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
  162. package/src/modules/directory/api/organization-branding/route.ts +5 -0
  163. package/src/modules/eudr/api/plots/import/route.ts +7 -0
  164. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
  165. package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
  166. package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
  167. package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
  168. package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
  169. package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
  170. package/src/modules/messages/api/[id]/forward/route.ts +5 -0
  171. package/src/modules/messages/api/[id]/reply/route.ts +5 -0
  172. package/src/modules/messages/api/[id]/route.ts +9 -0
  173. package/src/modules/messages/api/token/[token]/route.ts +5 -0
  174. package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
  175. package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
  176. package/src/modules/sales/api/quotes/accept/route.ts +5 -0
  177. package/src/modules/sales/api/quotes/convert/route.ts +5 -0
  178. package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
  179. package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
  180. package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
  181. package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
  182. package/src/modules/staff/api/team-members/self/route.ts +5 -0
  183. package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
  184. package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
  185. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
  186. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
  187. package/src/modules/warranty_claims/api/ai/assess/route.ts +5 -0
  188. package/src/modules/warranty_claims/api/assign/route.ts +5 -0
  189. package/src/modules/warranty_claims/api/credit-memo/route.ts +5 -0
  190. package/src/modules/warranty_claims/api/events/route.ts +5 -0
  191. package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +5 -0
  192. package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +5 -0
  193. package/src/modules/warranty_claims/api/portal/claims/route.ts +5 -0
  194. package/src/modules/warranty_claims/api/portal/events/route.ts +5 -0
  195. package/src/modules/warranty_claims/api/receiving/route.ts +5 -0
  196. package/src/modules/warranty_claims/api/replacement-order/route.ts +5 -0
  197. package/src/modules/warranty_claims/api/return-label/route.ts +5 -0
  198. package/src/modules/warranty_claims/api/sales-return/route.ts +5 -0
  199. package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
  200. package/src/modules/warranty_claims/api/submit/route.ts +5 -0
  201. package/src/modules/warranty_claims/api/transition/route.ts +5 -0
  202. package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
  203. package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/warranty_claims/api/events/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\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 type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaimEvent } from '../../data/entities'\nimport { commentClaimInputSchema, type CommentClaimInput } from '../../data/validators'\nimport { requireScopedClaim, WARRANTY_CLAIM_RESOURCE_KIND, type WarrantyClaimScope } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst eventListQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\ntype EventRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n scope: WarrantyClaimScope\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.claim.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n }\n return null\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorUserId: event.actorUserId ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolveEventContext(req: Request): Promise<EventRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = organizationScope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n const em = container.resolve('em') as EntityManager\n return {\n ctx: {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: organizationId,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n scope: { tenantId: auth.tenantId, organizationId },\n translate,\n em,\n }\n}\n\nasync function runCommentGuard(\n req: Request,\n context: EventRouteContext,\n input: CommentClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function GET(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const url = new URL(req.url)\n const query = eventListQuerySchema.parse({ claimId: url.searchParams.get('claimId') ?? undefined })\n const claim = await requireScopedClaim(context.em, query.claimId, context.scope)\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, tenantId: context.scope.tenantId, organizationId: context.scope.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n context.scope,\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.get failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.notFound', 'Warranty claim not found.') }, { status: 404 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = commentClaimInputSchema.parse(payload)\n const input: CommentClaimInput = { ...parsed, actorCustomerId: undefined }\n const guarded = await runCommentGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload\n ? { ...commentClaimInputSchema.parse(guarded.modifiedPayload), actorCustomerId: undefined }\n : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.string(),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorUserId: z.string().uuid().nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Warranty claim timeline events',\n methods: {\n GET: {\n summary: 'List timeline events for a claim',\n query: eventListQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a staff comment to a claim timeline',\n requestBody: { contentType: 'application/json', schema: commentClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\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 type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaimEvent } from '../../data/entities'\nimport { commentClaimInputSchema, type CommentClaimInput } from '../../data/validators'\nimport { requireScopedClaim, WARRANTY_CLAIM_RESOURCE_KIND, type WarrantyClaimScope } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst eventListQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\ntype EventRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n scope: WarrantyClaimScope\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.claim.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n }\n return null\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorUserId: event.actorUserId ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolveEventContext(req: Request): Promise<EventRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = organizationScope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n const em = container.resolve('em') as EntityManager\n return {\n ctx: {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: organizationId,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n scope: { tenantId: auth.tenantId, organizationId },\n translate,\n em,\n }\n}\n\nasync function runCommentGuard(\n req: Request,\n context: EventRouteContext,\n input: CommentClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function GET(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const url = new URL(req.url)\n const query = eventListQuerySchema.parse({ claimId: url.searchParams.get('claimId') ?? undefined })\n const claim = await requireScopedClaim(context.em, query.claimId, context.scope)\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, tenantId: context.scope.tenantId, organizationId: context.scope.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n context.scope,\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.get failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.notFound', 'Warranty claim not found.') }, { status: 404 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = commentClaimInputSchema.parse(payload)\n const input: CommentClaimInput = { ...parsed, actorCustomerId: undefined }\n const guarded = await runCommentGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload\n ? { ...commentClaimInputSchema.parse(guarded.modifiedPayload), actorCustomerId: undefined }\n : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.string(),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorUserId: z.string().uuid().nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Warranty claim timeline events',\n methods: {\n GET: {\n summary: 'List timeline events for a claim',\n query: eventListQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a staff comment to a claim timeline',\n requestBody: { contentType: 'application/json', schema: commentClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
4
5
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
6
  import {
6
7
  loadOwnedClaim,
@@ -48,6 +49,10 @@ async function POST(req, ctx) {
48
49
  if (isCrudHttpError(err)) {
49
50
  return NextResponse.json(err.body, { status: err.status });
50
51
  }
52
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
53
+ if (interceptorRejection) {
54
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
55
+ }
51
56
  throw err;
52
57
  }
53
58
  await guarded.runAfterSuccess();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/warranty_claims/api/portal/claims/%5Bid%5D/submit/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n loadOwnedClaim,\n loadOwnedClaimFresh,\n resolvePortalActionContext,\n resolvePortalClaimId,\n runPortalClaimActionGuard,\n type PortalClaimActionRouteContext,\n} from '../shared'\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nexport async function POST(req: Request, ctx: PortalClaimActionRouteContext) {\n const contextOrResponse = await resolvePortalActionContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const claimId = await resolvePortalClaimId(ctx)\n if (!claimId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const claim = await loadOwnedClaim(context, claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n if (claim.status !== 'draft') {\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.invalidTransition', 'That status change is not allowed.') },\n { status: 400 },\n )\n }\n\n const guarded = await runPortalClaimActionGuard(req, context, claim.id, {\n id: claim.id,\n action: 'submit',\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claim.id, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n await guarded.runAfterSuccess()\n\n const updated = await loadOwnedClaimFresh(context, claim.id)\n return NextResponse.json({ ok: true, claimId: claim.id, status: updated?.status ?? 'submitted' })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim submit action',\n methods: {\n POST: {\n summary: 'Submit an owned draft claim from the customer portal',\n responses: [\n {\n status: 200,\n description: 'Claim submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid(), status: z.string() }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM;AAC7B;AAEA,eAAsB,KAAK,KAAc,KAAoC;AAC3E,QAAM,oBAAoB,MAAM,2BAA2B,GAAG;AAC9D,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,MAAI,CAAC,SAAS;AACZ,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,QAAM,QAAQ,MAAM,eAAe,SAAS,OAAO;AACnD,MAAI,CAAC,OAAO;AACV,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,MAAI,MAAM,WAAW,SAAS;AAC5B,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa;AAAA,MAClB,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,oCAAoC,EAAE;AAAA,MAChH,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,0BAA0B,KAAK,SAAS,MAAM,IAAI;AAAA,IACtE,IAAI,MAAM;AAAA,IACV,QAAQ;AAAA,EACV,CAAC;AACD,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,aAAa,QAAQ,WAAW,UAAU,QAAQ,YAAY;AACpE,MAAI;AACF,UAAM,WAAW;AAAA,MACf;AAAA,MACA,EAAE,OAAO,EAAE,IAAI,MAAM,IAAI,iBAAiB,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW;AAAA,IAC1F;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,gBAAgB;AAE9B,QAAM,UAAU,MAAM,oBAAoB,SAAS,MAAM,EAAE;AAC3D,SAAO,aAAa,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,UAAU,YAAY,CAAC;AAClG;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n loadOwnedClaim,\n loadOwnedClaimFresh,\n resolvePortalActionContext,\n resolvePortalClaimId,\n runPortalClaimActionGuard,\n type PortalClaimActionRouteContext,\n} from '../shared'\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nexport async function POST(req: Request, ctx: PortalClaimActionRouteContext) {\n const contextOrResponse = await resolvePortalActionContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const claimId = await resolvePortalClaimId(ctx)\n if (!claimId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const claim = await loadOwnedClaim(context, claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n if (claim.status !== 'draft') {\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.invalidTransition', 'That status change is not allowed.') },\n { status: 400 },\n )\n }\n\n const guarded = await runPortalClaimActionGuard(req, context, claim.id, {\n id: claim.id,\n action: 'submit',\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claim.id, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n\n await guarded.runAfterSuccess()\n\n const updated = await loadOwnedClaimFresh(context, claim.id)\n return NextResponse.json({ ok: true, claimId: claim.id, status: updated?.status ?? 'submitted' })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim submit action',\n methods: {\n POST: {\n summary: 'Submit an owned draft claim from the customer portal',\n responses: [\n {\n status: 200,\n description: 'Claim submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid(), status: z.string() }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC,SAAS,0CAA0C;AACnD,SAAS,2BAA2B;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM;AAC7B;AAEA,eAAsB,KAAK,KAAc,KAAoC;AAC3E,QAAM,oBAAoB,MAAM,2BAA2B,GAAG;AAC9D,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,MAAI,CAAC,SAAS;AACZ,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,QAAM,QAAQ,MAAM,eAAe,SAAS,OAAO;AACnD,MAAI,CAAC,OAAO;AACV,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,MAAI,MAAM,WAAW,SAAS;AAC5B,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa;AAAA,MAClB,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,oCAAoC,EAAE;AAAA,MAChH,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,0BAA0B,KAAK,SAAS,MAAM,IAAI;AAAA,IACtE,IAAI,MAAM;AAAA,IACV,QAAQ;AAAA,EACV,CAAC;AACD,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,aAAa,QAAQ,WAAW,UAAU,QAAQ,YAAY;AACpE,MAAI;AACF,UAAM,WAAW;AAAA,MACf;AAAA,MACA,EAAE,OAAO,EAAE,IAAI,MAAM,IAAI,iBAAiB,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW;AAAA,IAC1F;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,uBAAuB,mCAAmC,GAAG;AACnE,QAAI,sBAAsB;AACxB,aAAO,aAAa,KAAK,qBAAqB,MAAM,EAAE,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7F;AACA,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,gBAAgB;AAE9B,QAAM,UAAU,MAAM,oBAAoB,SAAS,MAAM,EAAE;AAC3D,SAAO,aAAa,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS,UAAU,YAAY,CAAC;AAClG;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
4
5
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
6
  import {
6
7
  loadOwnedClaim,
@@ -52,6 +53,10 @@ async function POST(req, ctx) {
52
53
  if (isCrudHttpError(err)) {
53
54
  return NextResponse.json(err.body, { status: err.status });
54
55
  }
56
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
57
+ if (interceptorRejection) {
58
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
59
+ }
55
60
  throw err;
56
61
  }
57
62
  await guarded.runAfterSuccess();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/warranty_claims/api/portal/claims/%5Bid%5D/withdraw/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { TransitionClaimInput, WarrantyClaimStatus } from '../../../../../data/validators'\nimport {\n loadOwnedClaim,\n resolvePortalActionContext,\n resolvePortalClaimId,\n runPortalClaimActionGuard,\n type PortalClaimActionRouteContext,\n} from '../shared'\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nconst PORTAL_WITHDRAWABLE_STATUSES = new Set<WarrantyClaimStatus>(['draft', 'submitted'])\n\nexport async function POST(req: Request, ctx: PortalClaimActionRouteContext) {\n const contextOrResponse = await resolvePortalActionContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const claimId = await resolvePortalClaimId(ctx)\n if (!claimId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const claim = await loadOwnedClaim(context, claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n if (!PORTAL_WITHDRAWABLE_STATUSES.has(claim.status)) {\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.invalidTransition', 'That status change is not allowed.') },\n { status: 400 },\n )\n }\n\n const guarded = await runPortalClaimActionGuard(req, context, claim.id, {\n id: claim.id,\n action: 'withdraw',\n toStatus: 'cancelled',\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<TransitionClaimInput, { claimId: string }>(\n 'warranty_claims.claim.transition',\n {\n input: { id: claim.id, toStatus: 'cancelled', actorCustomerId: context.customerId },\n ctx: context.commandCtx,\n },\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: claim.id, status: 'cancelled' })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim withdraw action',\n methods: {\n POST: {\n summary: 'Withdraw (cancel) an owned claim while it is draft or submitted',\n responses: [\n {\n status: 200,\n description: 'Claim withdrawn',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid(), status: z.literal('cancelled') }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC,SAAS,2BAA2B;AAGpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM;AAC7B;AAEA,MAAM,+BAA+B,oBAAI,IAAyB,CAAC,SAAS,WAAW,CAAC;AAExF,eAAsB,KAAK,KAAc,KAAoC;AAC3E,QAAM,oBAAoB,MAAM,2BAA2B,GAAG;AAC9D,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,MAAI,CAAC,SAAS;AACZ,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,QAAM,QAAQ,MAAM,eAAe,SAAS,OAAO;AACnD,MAAI,CAAC,OAAO;AACV,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,MAAI,CAAC,6BAA6B,IAAI,MAAM,MAAM,GAAG;AACnD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa;AAAA,MAClB,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,oCAAoC,EAAE;AAAA,MAChH,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,0BAA0B,KAAK,SAAS,MAAM,IAAI;AAAA,IACtE,IAAI,MAAM;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,aAAa,QAAQ,WAAW,UAAU,QAAQ,YAAY;AACpE,MAAI;AACF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,QACE,OAAO,EAAE,IAAI,MAAM,IAAI,UAAU,aAAa,iBAAiB,QAAQ,WAAW;AAAA,QAClF,KAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,gBAAgB;AAE9B,SAAO,aAAa,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,IAAI,QAAQ,YAAY,CAAC;AAC/E;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,GAAG,QAAQ,EAAE,QAAQ,WAAW,EAAE,CAAC;AAAA,QAClG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { TransitionClaimInput, WarrantyClaimStatus } from '../../../../../data/validators'\nimport {\n loadOwnedClaim,\n resolvePortalActionContext,\n resolvePortalClaimId,\n runPortalClaimActionGuard,\n type PortalClaimActionRouteContext,\n} from '../shared'\n\nexport const metadata = {\n POST: { requireAuth: false },\n}\n\nconst PORTAL_WITHDRAWABLE_STATUSES = new Set<WarrantyClaimStatus>(['draft', 'submitted'])\n\nexport async function POST(req: Request, ctx: PortalClaimActionRouteContext) {\n const contextOrResponse = await resolvePortalActionContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const claimId = await resolvePortalClaimId(ctx)\n if (!claimId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const claim = await loadOwnedClaim(context, claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n if (!PORTAL_WITHDRAWABLE_STATUSES.has(claim.status)) {\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.invalidTransition', 'That status change is not allowed.') },\n { status: 400 },\n )\n }\n\n const guarded = await runPortalClaimActionGuard(req, context, claim.id, {\n id: claim.id,\n action: 'withdraw',\n toStatus: 'cancelled',\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<TransitionClaimInput, { claimId: string }>(\n 'warranty_claims.claim.transition',\n {\n input: { id: claim.id, toStatus: 'cancelled', actorCustomerId: context.customerId },\n ctx: context.commandCtx,\n },\n )\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: claim.id, status: 'cancelled' })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim withdraw action',\n methods: {\n POST: {\n summary: 'Withdraw (cancel) an owned claim while it is draft or submitted',\n responses: [\n {\n status: 200,\n description: 'Claim withdrawn',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid(), status: z.literal('cancelled') }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,uBAAuB;AAChC,SAAS,0CAA0C;AACnD,SAAS,2BAA2B;AAGpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM;AAC7B;AAEA,MAAM,+BAA+B,oBAAI,IAAyB,CAAC,SAAS,WAAW,CAAC;AAExF,eAAsB,KAAK,KAAc,KAAoC;AAC3E,QAAM,oBAAoB,MAAM,2BAA2B,GAAG;AAC9D,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,MAAI,CAAC,SAAS;AACZ,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,QAAM,QAAQ,MAAM,eAAe,SAAS,OAAO;AACnD,MAAI,CAAC,OAAO;AACV,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnG;AACA,MAAI,CAAC,6BAA6B,IAAI,MAAM,MAAM,GAAG;AACnD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa;AAAA,MAClB,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,oCAAoC,EAAE;AAAA,MAChH,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,0BAA0B,KAAK,SAAS,MAAM,IAAI;AAAA,IACtE,IAAI,MAAM;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,aAAa,QAAQ,WAAW,UAAU,QAAQ,YAAY;AACpE,MAAI;AACF,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,QACE,OAAO,EAAE,IAAI,MAAM,IAAI,UAAU,aAAa,iBAAiB,QAAQ,WAAW;AAAA,QAClF,KAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,uBAAuB,mCAAmC,GAAG;AACnE,QAAI,sBAAsB;AACxB,aAAO,aAAa,KAAK,qBAAqB,MAAM,EAAE,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7F;AACA,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,gBAAgB;AAE9B,SAAO,aAAa,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,IAAI,QAAQ,YAAY,CAAC;AAC/E;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,GAAG,QAAQ,EAAE,QAAQ,WAAW,EAAE,CAAC;AAAA,QAClG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
5
6
  import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
6
7
  import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
7
8
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
@@ -401,6 +402,10 @@ async function POST(req) {
401
402
  if (isCrudHttpError(err)) {
402
403
  return NextResponse.json(err.body, { status: err.status });
403
404
  }
405
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
406
+ if (interceptorRejection) {
407
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
408
+ }
404
409
  throw err;
405
410
  }
406
411
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/warranty_claims/api/portal/claims/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../../data/entities'\nimport {\n claimStatusSchema,\n portalIntakeInputSchema,\n type ClaimCreateInput,\n type PortalIntakeInput,\n} from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { portalClaimStatusesForStateGroup } from '../../../lib/listSegments'\nimport { buildPortalOwnedClaimWhere } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst logger = createLogger('warranty_claims')\n\nconst emptyQueryValueToUndefined = (value: unknown): unknown => {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nconst optionalTrimmedQueryString = (max: number) =>\n z.preprocess(emptyQueryValueToUndefined, z.string().trim().min(1).max(max).optional())\n\nconst listQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: optionalTrimmedQueryString(190),\n status: z.preprocess(emptyQueryValueToUndefined, claimStatusSchema.optional()),\n stateGroup: z.enum(['open', 'resolved']).optional(),\n serialNumber: optionalTrimmedQueryString(191),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\ntype LineSummary = {\n count: number\n creditTotal: string\n statuses: Record<string, number>\n}\n\ntype OrderValidationResult = {\n orderId: string | null\n currencyCode: string | null\n}\n\ntype PortalOrderLookup = {\n id: string\n currencyCode: string | null\n}\n\ntype PortalOrderLineLookup = {\n id: string\n orderId: string\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction stringField(record: Record<string, unknown>, key: string): string | null {\n const value = record[key]\n return typeof value === 'string' ? value : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nconst CREDIT_SCALE = 10_000n\n\nfunction decimalUnits(value: unknown): bigint {\n const normalized = typeof value === 'number'\n ? (Number.isFinite(value) ? value.toFixed(4) : '')\n : typeof value === 'string' ? value.trim() : ''\n const match = /^([+-]?)(\\d+)(?:\\.(\\d+))?$/.exec(normalized)\n if (!match) return 0n\n const [, sign, whole, fraction = ''] = match\n let units = BigInt(whole) * CREDIT_SCALE + BigInt(fraction.slice(0, 4).padEnd(4, '0'))\n if (fraction.length > 4 && fraction[4] >= '5') units += 1n\n return sign === '-' ? -units : units\n}\n\nfunction decimalString(value: bigint): string {\n const sign = value < 0n ? '-' : ''\n const absolute = value < 0n ? -value : value\n const whole = absolute / CREDIT_SCALE\n const fraction = String(absolute % CREDIT_SCALE).padStart(4, '0').replace(/0+$/, '')\n return `${sign}${whole}${fraction ? `.${fraction}` : ''}`\n}\n\nfunction summarizeLines(lines: WarrantyClaimLine[]): LineSummary {\n let creditTotal = 0n\n const statuses: Record<string, number> = {}\n for (const line of lines) {\n creditTotal += decimalUnits(line.creditAmount)\n statuses[line.lineStatus] = (statuses[line.lineStatus] ?? 0) + 1\n }\n return {\n count: lines.length,\n creditTotal: decimalString(creditTotal),\n statuses,\n }\n}\n\nfunction serializePortalClaim(claim: WarrantyClaim, lines: WarrantyClaimLine[]) {\n return {\n id: claim.id,\n claimNumber: claim.claimNumber,\n claimType: claim.claimType,\n status: claim.status,\n orderId: claim.orderId ?? null,\n orderNumber: claim.orderNumber ?? null,\n reasonCode: claim.reasonCode ?? null,\n resolutionSummary: claim.resolutionSummary ?? null,\n createdAt: toIso(claim.createdAt),\n updatedAt: toIso(claim.updatedAt),\n lines: summarizeLines(lines),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const { translate } = await resolveTranslations()\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.customerAccountNotLinked', 'Customer account is not linked to a customer record') }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\ntype PortalSalesDb = {\n sales_orders: {\n id: string\n currency_code: string\n customer_entity_id: string | null\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n sales_order_lines: {\n id: string\n order_id: string\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n}\n\nfunction isMissingSalesTableError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false\n const candidate = err as { code?: unknown; message?: unknown }\n return candidate.code === '42P01'\n || (typeof candidate.message === 'string'\n && candidate.message.includes('does not exist')\n && (candidate.message.includes('sales_orders') || candidate.message.includes('sales_order_lines')))\n}\n\nasync function loadOwnedOrders(\n context: PortalContext,\n orderIds: string[],\n): Promise<Map<string, PortalOrderLookup>> {\n if (orderIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_orders')\n .select(['id', 'currency_code'])\n .where('id', 'in', orderIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('customer_entity_id', '=', context.customerId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, currencyCode: row.currency_code ?? null }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function loadOwnedOrderLines(\n context: PortalContext,\n orderLineIds: string[],\n): Promise<Map<string, PortalOrderLineLookup>> {\n if (orderLineIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_order_lines')\n .select(['id', 'order_id'])\n .where('id', 'in', orderLineIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, orderId: row.order_id }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function validatePortalOrderOwnership(\n context: PortalContext,\n input: PortalIntakeInput,\n): Promise<OrderValidationResult | Response> {\n const { translate } = await resolveTranslations()\n const orderLineIds = Array.from(new Set(input.lines.flatMap((line) => line.orderLineId ? [line.orderLineId] : [])))\n const orderLinesById = await loadOwnedOrderLines(context, orderLineIds)\n const orderIds = new Set<string>()\n if (input.orderId) orderIds.add(input.orderId)\n for (const orderLine of orderLinesById.values()) orderIds.add(orderLine.orderId)\n const ordersById = await loadOwnedOrders(context, Array.from(orderIds))\n let resolvedOrder = input.orderId ? (ordersById.get(input.orderId) ?? null) : null\n if (input.orderId) {\n if (!resolvedOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderNotOwned', 'Order not found.') }, { status: 404 })\n }\n }\n\n for (const line of input.lines) {\n if (!line.orderLineId) continue\n const resolvedLine = orderLinesById.get(line.orderLineId)\n const lineOrder = resolvedLine ? ordersById.get(resolvedLine.orderId) : null\n if (!resolvedLine || !lineOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') }, { status: 404 })\n }\n if (resolvedOrder && lineOrder.id !== resolvedOrder.id) {\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') },\n { status: 400 },\n )\n }\n if (!resolvedOrder) resolvedOrder = lineOrder\n }\n\n return {\n orderId: resolvedOrder?.id ?? input.orderId ?? null,\n currencyCode: resolvedOrder?.currencyCode ?? null,\n }\n}\n\nasync function runPortalCreateGuard(\n req: Request,\n context: PortalContext,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: null,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const query = listQuerySchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n stateGroup: url.searchParams.get('stateGroup') ?? undefined,\n serialNumber: url.searchParams.get('serialNumber') ?? undefined,\n })\n if (!query.success) {\n const { translate } = await resolveTranslations()\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const { page, pageSize, search, status, stateGroup, serialNumber } = query.data\n const where = buildPortalOwnedClaimWhere(context)\n if (status) where.status = status\n else if (stateGroup) where.status = { $in: portalClaimStatusesForStateGroup(stateGroup) }\n if (search) {\n const pattern = `%${escapeLikePattern(search)}%`\n where.$or = [\n { claimNumber: { $ilike: pattern } },\n { orderNumber: { $ilike: pattern } },\n ]\n }\n if (serialNumber) {\n const matchingLines = await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n {\n serialNumber,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n deletedAt: null,\n claim: buildPortalOwnedClaimWhere(context),\n },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const matchingClaimIds = Array.from(new Set(\n matchingLines.map((line) => relationId(line.claim)).filter((id): id is string => Boolean(id)),\n ))\n if (matchingClaimIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 1 })\n }\n where.id = { $in: matchingClaimIds }\n }\n const total = await context.em.count(WarrantyClaim, where)\n const claims = await findWithDecryption(\n context.em,\n WarrantyClaim,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const claimIds = claims.map((claim) => claim.id)\n const lines = claimIds.length\n ? await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n { claim: { $in: claimIds }, tenantId: context.tenantId, organizationId: context.organizationId, deletedAt: null },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n : []\n const linesByClaim = new Map<string, WarrantyClaimLine[]>()\n for (const line of lines) {\n const claimId = relationId(line.claim)\n if (!claimId) continue\n const bucket = linesByClaim.get(claimId) ?? []\n bucket.push(line)\n linesByClaim.set(claimId, bucket)\n }\n return NextResponse.json({\n items: claims.map((claim) => serializePortalClaim(claim, linesByClaim.get(claim.id) ?? [])),\n total,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(total / pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const parsed = portalIntakeInputSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const ownership = await validatePortalOrderOwnership(context, parsed.data)\n if (ownership instanceof Response) return ownership\n\n const createInput: ClaimCreateInput = {\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n claimType: 'warranty',\n channel: 'portal',\n priority: 'normal',\n customerId: context.customerId,\n customerName: context.auth.displayName || null,\n orderId: ownership.orderId,\n // A resolved order supplies its own number; otherwise persist the typed reference (WQA-002).\n orderNumber: ownership.orderId ? null : (parsed.data.orderReference ?? null),\n reasonCode: parsed.data.reasonCode,\n notes: parsed.data.notes ?? null,\n currencyCode: ownership.currencyCode,\n lines: parsed.data.lines.map((line, index) => ({\n lineNo: index + 1,\n orderLineId: line.orderLineId ?? null,\n productId: line.productId ?? null,\n productName: line.productName ?? null,\n sku: line.sku ?? null,\n serialNumber: line.serialNumber ?? null,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription,\n qtyClaimed: line.qtyClaimed ?? 1,\n // Carry warranty metadata so order-backed portal lines keep their entitlement basis\n // instead of persisting warrantyStatus='unknown' (WQA-004 / LINE-06).\n purchaseDate: line.purchaseDate ?? null,\n warrantyMonths: line.warrantyMonths ?? null,\n })),\n }\n\n const guarded = await runPortalCreateGuard(req, context, { ...createInput })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: ClaimCreateInput = guarded.modifiedPayload\n ? {\n ...createInput,\n ...guarded.modifiedPayload,\n tenantId: createInput.tenantId,\n organizationId: createInput.organizationId,\n customerId: createInput.customerId,\n channel: createInput.channel,\n claimType: createInput.claimType,\n orderId: createInput.orderId,\n orderNumber: createInput.orderNumber,\n currencyCode: createInput.currencyCode,\n lines: createInput.lines,\n }\n : createInput\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n const createResult = await commandBus.execute<ClaimCreateInput, { claimId: string }>(\n 'warranty_claims.claim.create',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n const claimId = createResult.result?.claimId\n if (typeof claimId !== 'string') {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claimId, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (submitError) {\n try {\n await commandBus.execute<{ id: string }, { claimId: string }>(\n 'warranty_claims.claim.delete',\n { input: { id: claimId }, ctx: context.commandCtx },\n )\n } catch (compensationError) {\n logger.error('warranty_claims.portal.claim.submit compensation failed \u2014 orphaned draft claim', {\n err: compensationError,\n claimId,\n customerId: context.customerId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n }\n throw submitError\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId }, { status: 201 })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n}\n\nconst lineSummarySchema = z.object({\n count: z.number().int().nonnegative(),\n creditTotal: z.string(),\n statuses: z.record(z.string(), z.number().int().nonnegative()),\n})\n\nconst portalClaimSchema = z.object({\n id: z.string().uuid(),\n claimNumber: z.string(),\n claimType: z.string(),\n status: z.string(),\n orderId: z.string().uuid().nullable(),\n orderNumber: z.string().nullable(),\n reasonCode: z.string().nullable(),\n resolutionSummary: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n lines: lineSummarySchema,\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claims',\n methods: {\n GET: {\n summary: 'List the authenticated customer account claims',\n query: listQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer claims',\n schema: z.object({\n items: z.array(portalClaimSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n POST: {\n summary: 'Submit a customer portal claim intake',\n requestBody: { contentType: 'application/json', schema: portalIntakeInputSchema },\n responses: [\n {\n status: 201,\n description: 'Claim created and submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAGlB,SAAS,8BAA8B;AAEvC,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,8BAA6D;AACtE,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,kCAA4D;AAErE,SAAS,eAAe,yBAAyB;AACjD;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oCAAoC;AAC7C,SAAS,wCAAwC;AACjD,SAAS,kCAAkC;AAEpC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM;AAAA,EAC1B,MAAM,EAAE,aAAa,MAAM;AAC7B;AAEA,MAAM,SAAS,aAAa,iBAAiB;AAE7C,MAAM,6BAA6B,CAAC,UAA4B;AAC9D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,UAAU;AACpC;AAEA,MAAM,6BAA6B,CAAC,QAClC,EAAE,WAAW,4BAA4B,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,CAAC;AAEvF,MAAM,kBAAkB,EAAE,OAAO;AAAA,EAC/B,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC9C,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EAC5D,QAAQ,2BAA2B,GAAG;AAAA,EACtC,QAAQ,EAAE,WAAW,4BAA4B,kBAAkB,SAAS,CAAC;AAAA,EAC7E,YAAY,EAAE,KAAK,CAAC,QAAQ,UAAU,CAAC,EAAE,SAAS;AAAA,EAClD,cAAc,2BAA2B,GAAG;AAC9C,CAAC;AAgCD,SAAS,SAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,CAAC;AAC3G;AAEA,SAAS,YAAY,QAAiC,KAA4B;AAChF,QAAM,QAAQ,OAAO,GAAG;AACxB,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,MAAM,OAAwD;AACrE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,QAAQ,KAAK,YAAY;AACjE;AAEA,SAAS,WAAW,OAA+B;AACjD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,SAAS,KAAK;AAC7B,SAAO,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK;AACrD;AAEA,MAAM,eAAe;AAErB,SAAS,aAAa,OAAwB;AAC5C,QAAM,aAAa,OAAO,UAAU,WAC/B,OAAO,SAAS,KAAK,IAAI,MAAM,QAAQ,CAAC,IAAI,KAC7C,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AAC/C,QAAM,QAAQ,6BAA6B,KAAK,UAAU;AAC1D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,CAAC,EAAE,MAAM,OAAO,WAAW,EAAE,IAAI;AACvC,MAAI,QAAQ,OAAO,KAAK,IAAI,eAAe,OAAO,SAAS,MAAM,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,CAAC;AACrF,MAAI,SAAS,SAAS,KAAK,SAAS,CAAC,KAAK,IAAK,UAAS;AACxD,SAAO,SAAS,MAAM,CAAC,QAAQ;AACjC;AAEA,SAAS,cAAc,OAAuB;AAC5C,QAAM,OAAO,QAAQ,KAAK,MAAM;AAChC,QAAM,WAAW,QAAQ,KAAK,CAAC,QAAQ;AACvC,QAAM,QAAQ,WAAW;AACzB,QAAM,WAAW,OAAO,WAAW,YAAY,EAAE,SAAS,GAAG,GAAG,EAAE,QAAQ,OAAO,EAAE;AACnF,SAAO,GAAG,IAAI,GAAG,KAAK,GAAG,WAAW,IAAI,QAAQ,KAAK,EAAE;AACzD;AAEA,SAAS,eAAe,OAAyC;AAC/D,MAAI,cAAc;AAClB,QAAM,WAAmC,CAAC;AAC1C,aAAW,QAAQ,OAAO;AACxB,mBAAe,aAAa,KAAK,YAAY;AAC7C,aAAS,KAAK,UAAU,KAAK,SAAS,KAAK,UAAU,KAAK,KAAK;AAAA,EACjE;AACA,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,aAAa,cAAc,WAAW;AAAA,IACtC;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,OAAsB,OAA4B;AAC9E,SAAO;AAAA,IACL,IAAI,MAAM;AAAA,IACV,aAAa,MAAM;AAAA,IACnB,WAAW,MAAM;AAAA,IACjB,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM,WAAW;AAAA,IAC1B,aAAa,MAAM,eAAe;AAAA,IAClC,YAAY,MAAM,cAAc;AAAA,IAChC,mBAAmB,MAAM,qBAAqB;AAAA,IAC9C,WAAW,MAAM,MAAM,SAAS;AAAA,IAChC,WAAW,MAAM,MAAM,SAAS;AAAA,IAChC,OAAO,eAAe,KAAK;AAAA,EAC7B;AACF;AAEA,eAAe,qBAAqB,KAAiD;AACnF,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,OAAO,MAAM,2BAA2B,GAAG;AACjD,MAAI,CAAC,MAAM;AACT,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,uCAAuC,cAAc,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClI;AACA,MAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,mDAAmD,qDAAqD,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrL;AACA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,KAAK,UAAU,QAAQ,IAAI;AACjC,QAAM,cAAwC;AAAA,IAC5C,KAAK,KAAK;AAAA,IACV,KAAK,KAAK;AAAA,IACV,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK;AAAA,IACZ,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,gBAAgB,KAAK,kBAAkB;AAAA,EACzC;AACA,SAAO;AAAA,IACL;AAAA,IACA,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,IACrB;AAAA,IACA,YAAY;AAAA,MACV;AAAA,MACA,MAAM;AAAA,MACN,mBAAmB;AAAA,MACnB,wBAAwB,KAAK;AAAA,MAC7B,iBAAiB,CAAC,KAAK,KAAK;AAAA,MAC5B,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAoBA,SAAS,yBAAyB,KAAuB;AACvD,MAAI,OAAO,QAAQ,YAAY,QAAQ,KAAM,QAAO;AACpD,QAAM,YAAY;AAClB,SAAO,UAAU,SAAS,WACpB,OAAO,UAAU,YAAY,YAC5B,UAAU,QAAQ,SAAS,gBAAgB,MAC1C,UAAU,QAAQ,SAAS,cAAc,KAAK,UAAU,QAAQ,SAAS,mBAAmB;AACtG;AAEA,eAAe,gBACb,SACA,UACyC;AACzC,MAAI,SAAS,WAAW,EAAG,QAAO,oBAAI,IAAI;AAC1C,MAAI;AACF,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAyB;AACtD,UAAM,OAAO,MAAM,GAChB,WAAW,cAAc,EACzB,OAAO,CAAC,MAAM,eAAe,CAAC,EAC9B,MAAM,MAAM,MAAM,QAAQ,EAC1B,MAAM,aAAa,KAAK,QAAQ,QAAQ,EACxC,MAAM,mBAAmB,KAAK,QAAQ,cAAc,EACpD,MAAM,sBAAsB,KAAK,QAAQ,UAAU,EACnD,MAAM,cAAc,MAAM,IAAI,EAC9B,QAAQ;AACX,WAAO,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,cAAc,IAAI,iBAAiB,KAAK,CAAC,CAAC,CAAC;AAAA,EACrG,SAAS,KAAK;AACZ,QAAI,yBAAyB,GAAG,EAAG,QAAO,oBAAI,IAAI;AAClD,UAAM;AAAA,EACR;AACF;AAEA,eAAe,oBACb,SACA,cAC6C;AAC7C,MAAI,aAAa,WAAW,EAAG,QAAO,oBAAI,IAAI;AAC9C,MAAI;AACF,UAAM,KAAK,QAAQ,GAAG,KAAK,EAAE,UAAyB;AACtD,UAAM,OAAO,MAAM,GAChB,WAAW,mBAAmB,EAC9B,OAAO,CAAC,MAAM,UAAU,CAAC,EACzB,MAAM,MAAM,MAAM,YAAY,EAC9B,MAAM,aAAa,KAAK,QAAQ,QAAQ,EACxC,MAAM,mBAAmB,KAAK,QAAQ,cAAc,EACpD,MAAM,cAAc,MAAM,IAAI,EAC9B,QAAQ;AACX,WAAO,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC;AAAA,EACnF,SAAS,KAAK;AACZ,QAAI,yBAAyB,GAAG,EAAG,QAAO,oBAAI,IAAI;AAClD,UAAM;AAAA,EACR;AACF;AAEA,eAAe,6BACb,SACA,OAC2C;AAC3C,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,eAAe,MAAM,KAAK,IAAI,IAAI,MAAM,MAAM,QAAQ,CAAC,SAAS,KAAK,cAAc,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC;AAClH,QAAM,iBAAiB,MAAM,oBAAoB,SAAS,YAAY;AACtE,QAAM,WAAW,oBAAI,IAAY;AACjC,MAAI,MAAM,QAAS,UAAS,IAAI,MAAM,OAAO;AAC7C,aAAW,aAAa,eAAe,OAAO,EAAG,UAAS,IAAI,UAAU,OAAO;AAC/E,QAAM,aAAa,MAAM,gBAAgB,SAAS,MAAM,KAAK,QAAQ,CAAC;AACtE,MAAI,gBAAgB,MAAM,UAAW,WAAW,IAAI,MAAM,OAAO,KAAK,OAAQ;AAC9E,MAAI,MAAM,SAAS;AACjB,QAAI,CAAC,eAAe;AAClB,aAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,wCAAwC,kBAAkB,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACvI;AAAA,EACF;AAEA,aAAW,QAAQ,MAAM,OAAO;AAC9B,QAAI,CAAC,KAAK,YAAa;AACvB,UAAM,eAAe,eAAe,IAAI,KAAK,WAAW;AACxD,UAAM,YAAY,eAAe,WAAW,IAAI,aAAa,OAAO,IAAI;AACxE,QAAI,CAAC,gBAAgB,CAAC,WAAW;AAC/B,aAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,kDAAkD,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3K;AACA,QAAI,iBAAiB,UAAU,OAAO,cAAc,IAAI;AACtD,aAAO,aAAa;AAAA,QAClB,EAAE,IAAI,OAAO,OAAO,UAAU,4CAA4C,kDAAkD,EAAE;AAAA,QAC9H,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AACA,QAAI,CAAC,cAAe,iBAAgB;AAAA,EACtC;AAEA,SAAO;AAAA,IACL,SAAS,eAAe,MAAM,MAAM,WAAW;AAAA,IAC/C,cAAc,eAAe,gBAAgB;AAAA,EAC/C;AACF;AAEA,eAAe,qBACb,KACA,SACA,iBACmC;AACnC,SAAO,uBAAuB;AAAA,IAC5B,WAAW,QAAQ,WAAW;AAAA,IAC9B;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ,QAAQ,KAAK;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,cAAc,CAAC;AAAA,IACjB;AAAA,IACA,OAAO;AAAA,MACL,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,IAAI,KAAc;AACtC,QAAM,oBAAoB,MAAM,qBAAqB,GAAG;AACxD,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,QAAQ,gBAAgB,UAAU;AAAA,IACtC,MAAM,IAAI,aAAa,IAAI,MAAM,KAAK;AAAA,IACtC,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK;AAAA,IAC9C,QAAQ,IAAI,aAAa,IAAI,QAAQ,KAAK;AAAA,IAC1C,QAAQ,IAAI,aAAa,IAAI,QAAQ,KAAK;AAAA,IAC1C,YAAY,IAAI,aAAa,IAAI,YAAY,KAAK;AAAA,IAClD,cAAc,IAAI,aAAa,IAAI,cAAc,KAAK;AAAA,EACxD,CAAC;AACD,MAAI,CAAC,MAAM,SAAS;AAClB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,uCAAuC,eAAe,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnI;AACA,QAAM,EAAE,MAAM,UAAU,QAAQ,QAAQ,YAAY,aAAa,IAAI,MAAM;AAC3E,QAAM,QAAQ,2BAA2B,OAAO;AAChD,MAAI,OAAQ,OAAM,SAAS;AAAA,WAClB,WAAY,OAAM,SAAS,EAAE,KAAK,iCAAiC,UAAU,EAAE;AACxF,MAAI,QAAQ;AACV,UAAM,UAAU,IAAI,kBAAkB,MAAM,CAAC;AAC7C,UAAM,MAAM;AAAA,MACV,EAAE,aAAa,EAAE,QAAQ,QAAQ,EAAE;AAAA,MACnC,EAAE,aAAa,EAAE,QAAQ,QAAQ,EAAE;AAAA,IACrC;AAAA,EACF;AACA,MAAI,cAAc;AAChB,UAAM,gBAAgB,MAAM;AAAA,MAC1B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,QACE;AAAA,QACA,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,WAAW;AAAA,QACX,OAAO,2BAA2B,OAAO;AAAA,MAC3C;AAAA,MACA,CAAC;AAAA,MACD,EAAE,UAAU,QAAQ,UAAU,gBAAgB,QAAQ,eAAe;AAAA,IACvE;AACA,UAAM,mBAAmB,MAAM,KAAK,IAAI;AAAA,MACtC,cAAc,IAAI,CAAC,SAAS,WAAW,KAAK,KAAK,CAAC,EAAE,OAAO,CAAC,OAAqB,QAAQ,EAAE,CAAC;AAAA,IAC9F,CAAC;AACD,QAAI,iBAAiB,WAAW,GAAG;AACjC,aAAO,aAAa,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,MAAM,UAAU,YAAY,EAAE,CAAC;AAAA,IACjF;AACA,UAAM,KAAK,EAAE,KAAK,iBAAiB;AAAA,EACrC;AACA,QAAM,QAAQ,MAAM,QAAQ,GAAG,MAAM,eAAe,KAAK;AACzD,QAAM,SAAS,MAAM;AAAA,IACnB,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS,EAAE,WAAW,OAAO;AAAA,MAC7B,OAAO;AAAA,MACP,SAAS,OAAO,KAAK;AAAA,IACvB;AAAA,IACA,EAAE,UAAU,QAAQ,UAAU,gBAAgB,QAAQ,eAAe;AAAA,EACvE;AACA,QAAM,WAAW,OAAO,IAAI,CAAC,UAAU,MAAM,EAAE;AAC/C,QAAM,QAAQ,SAAS,SACnB,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR;AAAA,IACA,EAAE,OAAO,EAAE,KAAK,SAAS,GAAG,UAAU,QAAQ,UAAU,gBAAgB,QAAQ,gBAAgB,WAAW,KAAK;AAAA,IAChH,CAAC;AAAA,IACD,EAAE,UAAU,QAAQ,UAAU,gBAAgB,QAAQ,eAAe;AAAA,EACvE,IACA,CAAC;AACL,QAAM,eAAe,oBAAI,IAAiC;AAC1D,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,WAAW,KAAK,KAAK;AACrC,QAAI,CAAC,QAAS;AACd,UAAM,SAAS,aAAa,IAAI,OAAO,KAAK,CAAC;AAC7C,WAAO,KAAK,IAAI;AAChB,iBAAa,IAAI,SAAS,MAAM;AAAA,EAClC;AACA,SAAO,aAAa,KAAK;AAAA,IACvB,OAAO,OAAO,IAAI,CAAC,UAAU,qBAAqB,OAAO,aAAa,IAAI,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1F;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,QAAQ,CAAC;AAAA,EACrD,CAAC;AACH;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,oBAAoB,MAAM,qBAAqB,GAAG;AACxD,MAAI,6BAA6B,SAAU,QAAO;AAClD,QAAM,UAAU;AAChB,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,uCAAuC,eAAe,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnI;AACA,QAAM,SAAS,wBAAwB,UAAU,IAAI;AACrD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,uCAAuC,eAAe,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnI;AACA,QAAM,YAAY,MAAM,6BAA6B,SAAS,OAAO,IAAI;AACzE,MAAI,qBAAqB,SAAU,QAAO;AAE1C,QAAM,cAAgC;AAAA,IACpC,gBAAgB,QAAQ;AAAA,IACxB,UAAU,QAAQ;AAAA,IAClB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY,QAAQ;AAAA,IACpB,cAAc,QAAQ,KAAK,eAAe;AAAA,IAC1C,SAAS,UAAU;AAAA;AAAA,IAEnB,aAAa,UAAU,UAAU,OAAQ,OAAO,KAAK,kBAAkB;AAAA,IACvE,YAAY,OAAO,KAAK;AAAA,IACxB,OAAO,OAAO,KAAK,SAAS;AAAA,IAC5B,cAAc,UAAU;AAAA,IACxB,OAAO,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,MAC7C,QAAQ,QAAQ;AAAA,MAChB,aAAa,KAAK,eAAe;AAAA,MACjC,WAAW,KAAK,aAAa;AAAA,MAC7B,aAAa,KAAK,eAAe;AAAA,MACjC,KAAK,KAAK,OAAO;AAAA,MACjB,cAAc,KAAK,gBAAgB;AAAA,MACnC,WAAW,KAAK,aAAa;AAAA,MAC7B,kBAAkB,KAAK;AAAA,MACvB,YAAY,KAAK,cAAc;AAAA;AAAA;AAAA,MAG/B,cAAc,KAAK,gBAAgB;AAAA,MACnC,gBAAgB,KAAK,kBAAkB;AAAA,IACzC,EAAE;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM,qBAAqB,KAAK,SAAS,EAAE,GAAG,YAAY,CAAC;AAC3E,MAAI,CAAC,QAAQ,IAAI;AACf,WAAO,QAAQ;AAAA,EACjB;AACA,QAAM,iBAAmC,QAAQ,kBAC7C;AAAA,IACE,GAAG;AAAA,IACH,GAAG,QAAQ;AAAA,IACX,UAAU,YAAY;AAAA,IACtB,gBAAgB,YAAY;AAAA,IAC5B,YAAY,YAAY;AAAA,IACxB,SAAS,YAAY;AAAA,IACrB,WAAW,YAAY;AAAA,IACvB,SAAS,YAAY;AAAA,IACrB,aAAa,YAAY;AAAA,IACzB,cAAc,YAAY;AAAA,IAC1B,OAAO,YAAY;AAAA,EACrB,IACA;AAEJ,QAAM,aAAa,QAAQ,WAAW,UAAU,QAAQ,YAAY;AACpE,MAAI;AACF,UAAM,eAAe,MAAM,WAAW;AAAA,MACpC;AAAA,MACA,EAAE,OAAO,gBAAgB,KAAK,QAAQ,WAAW;AAAA,IACnD;AACA,UAAM,UAAU,aAAa,QAAQ;AACrC,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO,aAAa,KAAK,EAAE,IAAI,OAAO,OAAO,UAAU,sCAAsC,gCAAgC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACnJ;AACA,QAAI;AACF,YAAM,WAAW;AAAA,QACf;AAAA,QACA,EAAE,OAAO,EAAE,IAAI,SAAS,iBAAiB,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW;AAAA,MACzF;AAAA,IACF,SAAS,aAAa;AACpB,UAAI;AACF,cAAM,WAAW;AAAA,UACf;AAAA,UACA,EAAE,OAAO,EAAE,IAAI,QAAQ,GAAG,KAAK,QAAQ,WAAW;AAAA,QACpD;AAAA,MACF,SAAS,mBAAmB;AAC1B,eAAO,MAAM,uFAAkF;AAAA,UAC7F,KAAK;AAAA,UACL;AAAA,UACA,YAAY,QAAQ;AAAA,UACpB,UAAU,QAAQ;AAAA,UAClB,gBAAgB,QAAQ;AAAA,QAC1B,CAAC;AAAA,MACH;AACA,YAAM;AAAA,IACR;AAEA,UAAM,QAAQ,gBAAgB;AAE9B,WAAO,aAAa,KAAK,EAAE,IAAI,MAAM,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACjE,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM;AAAA,EACR;AACF;AAEA,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACpC,aAAa,EAAE,OAAO;AAAA,EACtB,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC;AAC/D,CAAC;AAED,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,WAAW,EAAE,OAAO;AAAA,EACpB,QAAQ,EAAE,OAAO;AAAA,EACjB,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,EACvC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,OAAO;AACT,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,MACH,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,YACf,OAAO,EAAE,MAAM,iBAAiB;AAAA,YAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,YACpC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,YAC5B,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,YAChC,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,UACpC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa,EAAE,aAAa,oBAAoB,QAAQ,wBAAwB;AAAA,MAChF,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,QAClE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../../data/entities'\nimport {\n claimStatusSchema,\n portalIntakeInputSchema,\n type ClaimCreateInput,\n type PortalIntakeInput,\n} from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { portalClaimStatusesForStateGroup } from '../../../lib/listSegments'\nimport { buildPortalOwnedClaimWhere } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst logger = createLogger('warranty_claims')\n\nconst emptyQueryValueToUndefined = (value: unknown): unknown => {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nconst optionalTrimmedQueryString = (max: number) =>\n z.preprocess(emptyQueryValueToUndefined, z.string().trim().min(1).max(max).optional())\n\nconst listQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: optionalTrimmedQueryString(190),\n status: z.preprocess(emptyQueryValueToUndefined, claimStatusSchema.optional()),\n stateGroup: z.enum(['open', 'resolved']).optional(),\n serialNumber: optionalTrimmedQueryString(191),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\ntype LineSummary = {\n count: number\n creditTotal: string\n statuses: Record<string, number>\n}\n\ntype OrderValidationResult = {\n orderId: string | null\n currencyCode: string | null\n}\n\ntype PortalOrderLookup = {\n id: string\n currencyCode: string | null\n}\n\ntype PortalOrderLineLookup = {\n id: string\n orderId: string\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction stringField(record: Record<string, unknown>, key: string): string | null {\n const value = record[key]\n return typeof value === 'string' ? value : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nconst CREDIT_SCALE = 10_000n\n\nfunction decimalUnits(value: unknown): bigint {\n const normalized = typeof value === 'number'\n ? (Number.isFinite(value) ? value.toFixed(4) : '')\n : typeof value === 'string' ? value.trim() : ''\n const match = /^([+-]?)(\\d+)(?:\\.(\\d+))?$/.exec(normalized)\n if (!match) return 0n\n const [, sign, whole, fraction = ''] = match\n let units = BigInt(whole) * CREDIT_SCALE + BigInt(fraction.slice(0, 4).padEnd(4, '0'))\n if (fraction.length > 4 && fraction[4] >= '5') units += 1n\n return sign === '-' ? -units : units\n}\n\nfunction decimalString(value: bigint): string {\n const sign = value < 0n ? '-' : ''\n const absolute = value < 0n ? -value : value\n const whole = absolute / CREDIT_SCALE\n const fraction = String(absolute % CREDIT_SCALE).padStart(4, '0').replace(/0+$/, '')\n return `${sign}${whole}${fraction ? `.${fraction}` : ''}`\n}\n\nfunction summarizeLines(lines: WarrantyClaimLine[]): LineSummary {\n let creditTotal = 0n\n const statuses: Record<string, number> = {}\n for (const line of lines) {\n creditTotal += decimalUnits(line.creditAmount)\n statuses[line.lineStatus] = (statuses[line.lineStatus] ?? 0) + 1\n }\n return {\n count: lines.length,\n creditTotal: decimalString(creditTotal),\n statuses,\n }\n}\n\nfunction serializePortalClaim(claim: WarrantyClaim, lines: WarrantyClaimLine[]) {\n return {\n id: claim.id,\n claimNumber: claim.claimNumber,\n claimType: claim.claimType,\n status: claim.status,\n orderId: claim.orderId ?? null,\n orderNumber: claim.orderNumber ?? null,\n reasonCode: claim.reasonCode ?? null,\n resolutionSummary: claim.resolutionSummary ?? null,\n createdAt: toIso(claim.createdAt),\n updatedAt: toIso(claim.updatedAt),\n lines: summarizeLines(lines),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const { translate } = await resolveTranslations()\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.customerAccountNotLinked', 'Customer account is not linked to a customer record') }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\ntype PortalSalesDb = {\n sales_orders: {\n id: string\n currency_code: string\n customer_entity_id: string | null\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n sales_order_lines: {\n id: string\n order_id: string\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n}\n\nfunction isMissingSalesTableError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false\n const candidate = err as { code?: unknown; message?: unknown }\n return candidate.code === '42P01'\n || (typeof candidate.message === 'string'\n && candidate.message.includes('does not exist')\n && (candidate.message.includes('sales_orders') || candidate.message.includes('sales_order_lines')))\n}\n\nasync function loadOwnedOrders(\n context: PortalContext,\n orderIds: string[],\n): Promise<Map<string, PortalOrderLookup>> {\n if (orderIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_orders')\n .select(['id', 'currency_code'])\n .where('id', 'in', orderIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('customer_entity_id', '=', context.customerId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, currencyCode: row.currency_code ?? null }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function loadOwnedOrderLines(\n context: PortalContext,\n orderLineIds: string[],\n): Promise<Map<string, PortalOrderLineLookup>> {\n if (orderLineIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_order_lines')\n .select(['id', 'order_id'])\n .where('id', 'in', orderLineIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, orderId: row.order_id }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function validatePortalOrderOwnership(\n context: PortalContext,\n input: PortalIntakeInput,\n): Promise<OrderValidationResult | Response> {\n const { translate } = await resolveTranslations()\n const orderLineIds = Array.from(new Set(input.lines.flatMap((line) => line.orderLineId ? [line.orderLineId] : [])))\n const orderLinesById = await loadOwnedOrderLines(context, orderLineIds)\n const orderIds = new Set<string>()\n if (input.orderId) orderIds.add(input.orderId)\n for (const orderLine of orderLinesById.values()) orderIds.add(orderLine.orderId)\n const ordersById = await loadOwnedOrders(context, Array.from(orderIds))\n let resolvedOrder = input.orderId ? (ordersById.get(input.orderId) ?? null) : null\n if (input.orderId) {\n if (!resolvedOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderNotOwned', 'Order not found.') }, { status: 404 })\n }\n }\n\n for (const line of input.lines) {\n if (!line.orderLineId) continue\n const resolvedLine = orderLinesById.get(line.orderLineId)\n const lineOrder = resolvedLine ? ordersById.get(resolvedLine.orderId) : null\n if (!resolvedLine || !lineOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') }, { status: 404 })\n }\n if (resolvedOrder && lineOrder.id !== resolvedOrder.id) {\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') },\n { status: 400 },\n )\n }\n if (!resolvedOrder) resolvedOrder = lineOrder\n }\n\n return {\n orderId: resolvedOrder?.id ?? input.orderId ?? null,\n currencyCode: resolvedOrder?.currencyCode ?? null,\n }\n}\n\nasync function runPortalCreateGuard(\n req: Request,\n context: PortalContext,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: null,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const query = listQuerySchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n stateGroup: url.searchParams.get('stateGroup') ?? undefined,\n serialNumber: url.searchParams.get('serialNumber') ?? undefined,\n })\n if (!query.success) {\n const { translate } = await resolveTranslations()\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const { page, pageSize, search, status, stateGroup, serialNumber } = query.data\n const where = buildPortalOwnedClaimWhere(context)\n if (status) where.status = status\n else if (stateGroup) where.status = { $in: portalClaimStatusesForStateGroup(stateGroup) }\n if (search) {\n const pattern = `%${escapeLikePattern(search)}%`\n where.$or = [\n { claimNumber: { $ilike: pattern } },\n { orderNumber: { $ilike: pattern } },\n ]\n }\n if (serialNumber) {\n const matchingLines = await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n {\n serialNumber,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n deletedAt: null,\n claim: buildPortalOwnedClaimWhere(context),\n },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const matchingClaimIds = Array.from(new Set(\n matchingLines.map((line) => relationId(line.claim)).filter((id): id is string => Boolean(id)),\n ))\n if (matchingClaimIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 1 })\n }\n where.id = { $in: matchingClaimIds }\n }\n const total = await context.em.count(WarrantyClaim, where)\n const claims = await findWithDecryption(\n context.em,\n WarrantyClaim,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const claimIds = claims.map((claim) => claim.id)\n const lines = claimIds.length\n ? await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n { claim: { $in: claimIds }, tenantId: context.tenantId, organizationId: context.organizationId, deletedAt: null },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n : []\n const linesByClaim = new Map<string, WarrantyClaimLine[]>()\n for (const line of lines) {\n const claimId = relationId(line.claim)\n if (!claimId) continue\n const bucket = linesByClaim.get(claimId) ?? []\n bucket.push(line)\n linesByClaim.set(claimId, bucket)\n }\n return NextResponse.json({\n items: claims.map((claim) => serializePortalClaim(claim, linesByClaim.get(claim.id) ?? [])),\n total,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(total / pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const parsed = portalIntakeInputSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const ownership = await validatePortalOrderOwnership(context, parsed.data)\n if (ownership instanceof Response) return ownership\n\n const createInput: ClaimCreateInput = {\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n claimType: 'warranty',\n channel: 'portal',\n priority: 'normal',\n customerId: context.customerId,\n customerName: context.auth.displayName || null,\n orderId: ownership.orderId,\n // A resolved order supplies its own number; otherwise persist the typed reference (WQA-002).\n orderNumber: ownership.orderId ? null : (parsed.data.orderReference ?? null),\n reasonCode: parsed.data.reasonCode,\n notes: parsed.data.notes ?? null,\n currencyCode: ownership.currencyCode,\n lines: parsed.data.lines.map((line, index) => ({\n lineNo: index + 1,\n orderLineId: line.orderLineId ?? null,\n productId: line.productId ?? null,\n productName: line.productName ?? null,\n sku: line.sku ?? null,\n serialNumber: line.serialNumber ?? null,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription,\n qtyClaimed: line.qtyClaimed ?? 1,\n // Carry warranty metadata so order-backed portal lines keep their entitlement basis\n // instead of persisting warrantyStatus='unknown' (WQA-004 / LINE-06).\n purchaseDate: line.purchaseDate ?? null,\n warrantyMonths: line.warrantyMonths ?? null,\n })),\n }\n\n const guarded = await runPortalCreateGuard(req, context, { ...createInput })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: ClaimCreateInput = guarded.modifiedPayload\n ? {\n ...createInput,\n ...guarded.modifiedPayload,\n tenantId: createInput.tenantId,\n organizationId: createInput.organizationId,\n customerId: createInput.customerId,\n channel: createInput.channel,\n claimType: createInput.claimType,\n orderId: createInput.orderId,\n orderNumber: createInput.orderNumber,\n currencyCode: createInput.currencyCode,\n lines: createInput.lines,\n }\n : createInput\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n const createResult = await commandBus.execute<ClaimCreateInput, { claimId: string }>(\n 'warranty_claims.claim.create',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n const claimId = createResult.result?.claimId\n if (typeof claimId !== 'string') {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claimId, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (submitError) {\n try {\n await commandBus.execute<{ id: string }, { claimId: string }>(\n 'warranty_claims.claim.delete',\n { input: { id: claimId }, ctx: context.commandCtx },\n )\n } catch (compensationError) {\n logger.error('warranty_claims.portal.claim.submit compensation failed \u2014 orphaned draft claim', {\n err: compensationError,\n claimId,\n customerId: context.customerId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n }\n throw submitError\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId }, { status: 201 })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n}\n\nconst lineSummarySchema = z.object({\n count: z.number().int().nonnegative(),\n creditTotal: z.string(),\n statuses: z.record(z.string(), z.number().int().nonnegative()),\n})\n\nconst portalClaimSchema = z.object({\n id: z.string().uuid(),\n claimNumber: z.string(),\n claimType: z.string(),\n status: z.string(),\n orderId: z.string().uuid().nullable(),\n orderNumber: z.string().nullable(),\n reasonCode: z.string().nullable(),\n resolutionSummary: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n lines: lineSummarySchema,\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claims',\n methods: {\n GET: {\n summary: 'List the authenticated customer account claims',\n query: listQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer claims',\n schema: z.object({\n items: z.array(portalClaimSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n POST: {\n summary: 'Submit a customer portal claim intake',\n requestBody: { contentType: 'application/json', schema: portalIntakeInputSchema },\n responses: [\n {\n status: 201,\n description: 'Claim created and submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
5
6
  import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
6
7
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
7
8
  import { getCustomerAuthFromRequest } from "@open-mercato/core/modules/customer_accounts/lib/customerAuth";
@@ -177,6 +178,10 @@ async function POST(req) {
177
178
  if (isCrudHttpError(err)) {
178
179
  return NextResponse.json(err.body, { status: err.status });
179
180
  }
181
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
182
+ if (interceptorRejection) {
183
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
184
+ }
180
185
  throw err;
181
186
  }
182
187
  return NextResponse.json({ ok: true, claimId: claim.id });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/warranty_claims/api/portal/events/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { WarrantyClaim, WarrantyClaimEvent } from '../../../data/entities'\nimport type { CommentClaimInput } from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst eventQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\nconst portalCommentSchema = z\n .object({\n claimId: z.string().uuid(),\n body: z.string().trim().min(1).max(8000),\n })\n .strict()\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.unauthorized' }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.customerAccountNotLinked' }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalCommentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const parsed = eventQuerySchema.safeParse({ claimId: url.searchParams.get('claimId') ?? undefined })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, visibility: 'customer', tenantId: context.tenantId, organizationId: context.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const parsed = portalCommentSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const input: CommentClaimInput = {\n claimId: claim.id,\n body: parsed.data.body,\n visibility: 'customer',\n actorCustomerId: context.customerId,\n }\n const guarded = await runPortalCommentGuard(req, context, claim.id, { ...input })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: CommentClaimInput = guarded.modifiedPayload\n ? {\n ...input,\n ...guarded.modifiedPayload,\n claimId: input.claimId,\n visibility: input.visibility,\n actorCustomerId: input.actorCustomerId,\n }\n : input\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n await guarded.runAfterSuccess()\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n return NextResponse.json({ ok: true, claimId: claim.id })\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.literal('customer'),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim timeline',\n methods: {\n GET: {\n summary: 'List customer-visible timeline events for an owned claim',\n query: eventQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer-visible timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a customer-visible customer comment',\n requestBody: { contentType: 'application/json', schema: portalCommentSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { WarrantyClaim, WarrantyClaimEvent } from '../../../data/entities'\nimport type { CommentClaimInput } from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst eventQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\nconst portalCommentSchema = z\n .object({\n claimId: z.string().uuid(),\n body: z.string().trim().min(1).max(8000),\n })\n .strict()\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.unauthorized' }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.customerAccountNotLinked' }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalCommentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const parsed = eventQuerySchema.safeParse({ claimId: url.searchParams.get('claimId') ?? undefined })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, visibility: 'customer', tenantId: context.tenantId, organizationId: context.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const parsed = portalCommentSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const input: CommentClaimInput = {\n claimId: claim.id,\n body: parsed.data.body,\n visibility: 'customer',\n actorCustomerId: context.customerId,\n }\n const guarded = await runPortalCommentGuard(req, context, claim.id, { ...input })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: CommentClaimInput = guarded.modifiedPayload\n ? {\n ...input,\n ...guarded.modifiedPayload,\n claimId: input.claimId,\n visibility: input.visibility,\n actorCustomerId: input.actorCustomerId,\n }\n : input\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n await guarded.runAfterSuccess()\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n\n return NextResponse.json({ ok: true, claimId: claim.id })\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.literal('customer'),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim timeline',\n methods: {\n GET: {\n summary: 'List customer-visible timeline events for an owned claim',\n query: eventQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer-visible timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a customer-visible customer comment',\n requestBody: { contentType: 'application/json', schema: portalCommentSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
6
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
7
8
  import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
8
9
  import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
9
10
  import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
@@ -140,6 +141,10 @@ async function POST(req) {
140
141
  return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId });
141
142
  } catch (err) {
142
143
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
144
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
145
+ if (interceptorRejection) {
146
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
147
+ }
143
148
  if (err instanceof z.ZodError) {
144
149
  return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
145
150
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/warranty_claims/api/receiving/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n claimConditionGradeSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_LINE_RESOURCE_KIND } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\ntype ActionRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n}\n\ntype ReceivingAction =\n | { kind: 'receive'; input: ClaimLineReceiveInput }\n | { kind: 'release'; input: ClaimLineReleaseQuarantineInput }\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst receiveBodySchema = z\n .object({\n lineId: uuid,\n conditionGrade: claimConditionGradeSchema,\n inspectionNotes: z.string().trim().max(4000).optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst releaseBodySchema = z\n .object({\n lineId: uuid,\n action: z.literal('release'),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst receivingBodySchema = z.union([releaseBodySchema, receiveBodySchema])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.receiving.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction translateKey(key: string): string {\n return key\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\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 tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReceivingAction(payload: Record<string, unknown>, context: ActionRouteContext): ReceivingAction {\n const releaseBody = releaseBodySchema.safeParse(payload)\n if (releaseBody.success) {\n const scopedPayload = withScopedPayload({\n id: releaseBody.data.lineId,\n updatedAt: releaseBody.data.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'release', input: claimLineReleaseQuarantineSchema.parse(scopedPayload) }\n }\n const body = receiveBodySchema.parse(payload)\n const scopedPayload = withScopedPayload({\n id: body.lineId,\n conditionGrade: body.conditionGrade,\n inspectionNotes: body.inspectionNotes,\n updatedAt: body.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'receive', input: claimLineReceiveSchema.parse(scopedPayload) }\n}\n\nfunction toGuardedAction(kind: ReceivingAction['kind'], payload: Record<string, unknown>): ReceivingAction {\n if (kind === 'release') {\n return { kind, input: claimLineReleaseQuarantineSchema.parse(payload) }\n }\n return { kind, input: claimLineReceiveSchema.parse(payload) }\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n action: ReceivingAction,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId: action.input.id,\n operation: 'custom',\n mutationPayload: { ...action.input },\n },\n })\n}\n\nasync function executeReceivingAction(\n commandBus: CommandBus,\n action: ReceivingAction,\n ctx: CommandRuntimeContext,\n): Promise<{ lineId: string; claimId: string }> {\n if (action.kind === 'release') {\n const { result } = await commandBus.execute<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.release_quarantine',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n }\n const { result } = await commandBus.execute<ClaimLineReceiveInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.receive',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const action = toReceivingAction(payload, context)\n const guarded = await runGuard(req, context, action)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandAction = guarded.modifiedPayload ? toGuardedAction(action.kind, guarded.modifiedPayload) : action\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const result = await executeReceivingAction(commandBus, commandAction, context.ctx)\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.receiving.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Receive or release a warranty claim line',\n methods: {\n POST: {\n summary: 'Grade a received line or release its quarantine hold',\n requestBody: { contentType: 'application/json', schema: receivingBodySchema },\n responses: [\n {\n status: 200,\n description: 'Receiving action applied',\n schema: z.object({\n ok: z.boolean(),\n lineId: z.string().uuid(),\n claimId: z.string().uuid(),\n }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\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 type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n claimConditionGradeSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_LINE_RESOURCE_KIND } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\ntype ActionRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n}\n\ntype ReceivingAction =\n | { kind: 'receive'; input: ClaimLineReceiveInput }\n | { kind: 'release'; input: ClaimLineReleaseQuarantineInput }\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst receiveBodySchema = z\n .object({\n lineId: uuid,\n conditionGrade: claimConditionGradeSchema,\n inspectionNotes: z.string().trim().max(4000).optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst releaseBodySchema = z\n .object({\n lineId: uuid,\n action: z.literal('release'),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst receivingBodySchema = z.union([releaseBodySchema, receiveBodySchema])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.receiving.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction translateKey(key: string): string {\n return key\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\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 tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReceivingAction(payload: Record<string, unknown>, context: ActionRouteContext): ReceivingAction {\n const releaseBody = releaseBodySchema.safeParse(payload)\n if (releaseBody.success) {\n const scopedPayload = withScopedPayload({\n id: releaseBody.data.lineId,\n updatedAt: releaseBody.data.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'release', input: claimLineReleaseQuarantineSchema.parse(scopedPayload) }\n }\n const body = receiveBodySchema.parse(payload)\n const scopedPayload = withScopedPayload({\n id: body.lineId,\n conditionGrade: body.conditionGrade,\n inspectionNotes: body.inspectionNotes,\n updatedAt: body.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'receive', input: claimLineReceiveSchema.parse(scopedPayload) }\n}\n\nfunction toGuardedAction(kind: ReceivingAction['kind'], payload: Record<string, unknown>): ReceivingAction {\n if (kind === 'release') {\n return { kind, input: claimLineReleaseQuarantineSchema.parse(payload) }\n }\n return { kind, input: claimLineReceiveSchema.parse(payload) }\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n action: ReceivingAction,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId: action.input.id,\n operation: 'custom',\n mutationPayload: { ...action.input },\n },\n })\n}\n\nasync function executeReceivingAction(\n commandBus: CommandBus,\n action: ReceivingAction,\n ctx: CommandRuntimeContext,\n): Promise<{ lineId: string; claimId: string }> {\n if (action.kind === 'release') {\n const { result } = await commandBus.execute<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.release_quarantine',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n }\n const { result } = await commandBus.execute<ClaimLineReceiveInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.receive',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const action = toReceivingAction(payload, context)\n const guarded = await runGuard(req, context, action)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandAction = guarded.modifiedPayload ? toGuardedAction(action.kind, guarded.modifiedPayload) : action\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const result = await executeReceivingAction(commandBus, commandAction, context.ctx)\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.receiving.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Receive or release a warranty claim line',\n methods: {\n POST: {\n summary: 'Grade a received line or release its quarantine hold',\n requestBody: { contentType: 'application/json', schema: receivingBodySchema },\n responses: [\n {\n status: 200,\n description: 'Receiving action applied',\n schema: z.object({\n ok: z.boolean(),\n lineId: z.string().uuid(),\n claimId: z.string().uuid(),\n }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["scopedPayload", "result"]
7
7
  }
@@ -4,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
5
  import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
6
  import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
7
8
  import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
8
9
  import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
9
10
  import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
@@ -113,6 +114,10 @@ async function POST(req) {
113
114
  });
114
115
  } catch (err) {
115
116
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
117
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
118
+ if (interceptorRejection) {
119
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
120
+ }
116
121
  if (err instanceof z.ZodError) {
117
122
  return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
118
123
  }