@open-mercato/core 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7135.1.19bf969756

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 (242) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  3. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
  4. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  5. package/dist/modules/auth/api/profile/route.js +5 -0
  6. package/dist/modules/auth/api/profile/route.js.map +2 -2
  7. package/dist/modules/communication_channels/api/post/test-seed/route.js +40 -2
  8. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  9. package/dist/modules/communication_channels/lib/access-control.js +6 -1
  10. package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
  11. package/dist/modules/communication_channels/lib/system-user.js +8 -3
  12. package/dist/modules/communication_channels/lib/system-user.js.map +2 -2
  13. package/dist/modules/customers/api/activities/route.js +13 -0
  14. package/dist/modules/customers/api/activities/route.js.map +2 -2
  15. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
  16. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  17. package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
  18. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  19. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
  20. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
  21. package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
  22. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  23. package/dist/modules/customers/api/interactions/complete/route.js +5 -0
  24. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  25. package/dist/modules/customers/api/labels/assign/route.js +5 -0
  26. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  27. package/dist/modules/customers/api/labels/route.js +5 -0
  28. package/dist/modules/customers/api/labels/route.js.map +2 -2
  29. package/dist/modules/customers/api/labels/unassign/route.js +5 -0
  30. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  31. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
  32. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  33. package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
  34. package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
  35. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
  36. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  37. package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
  38. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  39. package/dist/modules/customers/api/pipelines/route.js +13 -0
  40. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  41. package/dist/modules/customers/api/settings/address-format/route.js +5 -0
  42. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  43. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
  44. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
  45. package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
  46. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
  47. package/dist/modules/customers/api/tags/assign/route.js +5 -0
  48. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  49. package/dist/modules/customers/api/tags/unassign/route.js +5 -0
  50. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  51. package/dist/modules/customers/api/todos/route.js +13 -0
  52. package/dist/modules/customers/api/todos/route.js.map +2 -2
  53. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
  54. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  55. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
  56. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
  57. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
  58. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  59. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
  60. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
  61. package/dist/modules/directory/api/organization-branding/route.js +5 -0
  62. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  63. package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
  64. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
  65. package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
  66. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  67. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
  68. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
  69. package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
  70. package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
  71. package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
  72. package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
  73. package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
  74. package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
  75. package/dist/modules/messages/api/[id]/forward/route.js +5 -0
  76. package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
  77. package/dist/modules/messages/api/[id]/reply/route.js +5 -0
  78. package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
  79. package/dist/modules/messages/api/[id]/route.js +9 -0
  80. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  81. package/dist/modules/messages/api/token/[token]/route.js +5 -0
  82. package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
  83. package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
  84. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  85. package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
  86. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  87. package/dist/modules/sales/api/quotes/accept/route.js +5 -0
  88. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  89. package/dist/modules/sales/api/quotes/convert/route.js +5 -0
  90. package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
  91. package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
  92. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  93. package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
  94. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  95. package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
  96. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  97. package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
  98. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  99. package/dist/modules/staff/api/team-members/self/route.js +5 -0
  100. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  101. package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
  102. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  103. package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
  104. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  105. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
  106. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  107. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
  108. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  109. package/dist/modules/warranty_claims/api/ai/assess/route.js +25 -18
  110. package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
  111. package/dist/modules/warranty_claims/api/assign/route.js +5 -0
  112. package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
  113. package/dist/modules/warranty_claims/api/credit-memo/route.js +16 -12
  114. package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
  115. package/dist/modules/warranty_claims/api/events/route.js +6 -1
  116. package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
  117. package/dist/modules/warranty_claims/api/portal/attachments/route.js +24 -27
  118. package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
  119. package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js +8 -10
  120. package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js.map +2 -2
  121. package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js +4 -9
  122. package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js.map +2 -2
  123. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +8 -3
  124. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
  125. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +8 -3
  126. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
  127. package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -9
  128. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  129. package/dist/modules/warranty_claims/api/portal/events/route.js +17 -13
  130. package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
  131. package/dist/modules/warranty_claims/api/receiving/route.js +19 -15
  132. package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
  133. package/dist/modules/warranty_claims/api/registrations/route.js +9 -4
  134. package/dist/modules/warranty_claims/api/registrations/route.js.map +2 -2
  135. package/dist/modules/warranty_claims/api/replacement-order/route.js +16 -12
  136. package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
  137. package/dist/modules/warranty_claims/api/return-label/route.js +25 -19
  138. package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
  139. package/dist/modules/warranty_claims/api/risk/route.js +2 -1
  140. package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
  141. package/dist/modules/warranty_claims/api/sales-return/route.js +16 -12
  142. package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
  143. package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
  144. package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
  145. package/dist/modules/warranty_claims/api/submit/route.js +5 -0
  146. package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
  147. package/dist/modules/warranty_claims/api/transition/route.js +5 -0
  148. package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
  149. package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js +22 -1
  150. package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js.map +2 -2
  151. package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
  152. package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
  153. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
  154. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
  155. package/dist/modules/warranty_claims/commands/shared.js +2 -2
  156. package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
  157. package/dist/modules/warranty_claims/lib/portalAuthGuard.js +31 -0
  158. package/dist/modules/warranty_claims/lib/portalAuthGuard.js.map +7 -0
  159. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
  160. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
  161. package/package.json +7 -7
  162. package/src/helpers/integration/communicationChannelsFixtures.ts +13 -0
  163. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
  164. package/src/modules/auth/api/profile/route.ts +5 -0
  165. package/src/modules/communication_channels/api/post/test-seed/route.ts +47 -2
  166. package/src/modules/communication_channels/lib/access-control.ts +28 -0
  167. package/src/modules/communication_channels/lib/system-user.ts +40 -22
  168. package/src/modules/customers/api/activities/route.ts +13 -0
  169. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
  170. package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
  171. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
  172. package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
  173. package/src/modules/customers/api/interactions/complete/route.ts +5 -0
  174. package/src/modules/customers/api/labels/assign/route.ts +5 -0
  175. package/src/modules/customers/api/labels/route.ts +5 -0
  176. package/src/modules/customers/api/labels/unassign/route.ts +5 -0
  177. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
  178. package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
  179. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
  180. package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
  181. package/src/modules/customers/api/pipelines/route.ts +13 -0
  182. package/src/modules/customers/api/settings/address-format/route.ts +5 -0
  183. package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
  184. package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
  185. package/src/modules/customers/api/tags/assign/route.ts +5 -0
  186. package/src/modules/customers/api/tags/unassign/route.ts +5 -0
  187. package/src/modules/customers/api/todos/route.ts +13 -0
  188. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
  189. package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
  190. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
  191. package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
  192. package/src/modules/directory/api/organization-branding/route.ts +5 -0
  193. package/src/modules/eudr/api/plots/import/route.ts +7 -0
  194. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
  195. package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
  196. package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
  197. package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
  198. package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
  199. package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
  200. package/src/modules/messages/api/[id]/forward/route.ts +5 -0
  201. package/src/modules/messages/api/[id]/reply/route.ts +5 -0
  202. package/src/modules/messages/api/[id]/route.ts +9 -0
  203. package/src/modules/messages/api/token/[token]/route.ts +5 -0
  204. package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
  205. package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
  206. package/src/modules/sales/api/quotes/accept/route.ts +5 -0
  207. package/src/modules/sales/api/quotes/convert/route.ts +5 -0
  208. package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
  209. package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
  210. package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
  211. package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
  212. package/src/modules/staff/api/team-members/self/route.ts +5 -0
  213. package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
  214. package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
  215. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
  216. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
  217. package/src/modules/warranty_claims/api/ai/assess/route.ts +28 -17
  218. package/src/modules/warranty_claims/api/assign/route.ts +5 -0
  219. package/src/modules/warranty_claims/api/credit-memo/route.ts +19 -13
  220. package/src/modules/warranty_claims/api/events/route.ts +6 -1
  221. package/src/modules/warranty_claims/api/portal/attachments/route.ts +29 -27
  222. package/src/modules/warranty_claims/api/portal/claims/[id]/route.ts +9 -10
  223. package/src/modules/warranty_claims/api/portal/claims/[id]/shared.ts +5 -9
  224. package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +8 -3
  225. package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +8 -3
  226. package/src/modules/warranty_claims/api/portal/claims/route.ts +10 -9
  227. package/src/modules/warranty_claims/api/portal/events/route.ts +18 -13
  228. package/src/modules/warranty_claims/api/receiving/route.ts +22 -15
  229. package/src/modules/warranty_claims/api/registrations/route.ts +9 -3
  230. package/src/modules/warranty_claims/api/replacement-order/route.ts +19 -13
  231. package/src/modules/warranty_claims/api/return-label/route.ts +26 -17
  232. package/src/modules/warranty_claims/api/risk/route.ts +3 -1
  233. package/src/modules/warranty_claims/api/sales-return/route.ts +19 -13
  234. package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
  235. package/src/modules/warranty_claims/api/submit/route.ts +5 -0
  236. package/src/modules/warranty_claims/api/transition/route.ts +5 -0
  237. package/src/modules/warranty_claims/api/troubleshooting-guides/route.ts +23 -1
  238. package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
  239. package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +3 -1
  240. package/src/modules/warranty_claims/commands/shared.ts +7 -1
  241. package/src/modules/warranty_claims/lib/portalAuthGuard.ts +30 -0
  242. package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
@@ -5,7 +5,9 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
6
6
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
7
7
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
8
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
8
9
  import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
10
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
11
  import { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'
10
12
  import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
11
13
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
@@ -54,24 +56,23 @@ export const metadata = {
54
56
  POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage', 'sales.returns.create'] },
55
57
  }
56
58
 
57
- function translateKey(key: string): string {
58
- return key
59
- }
60
-
61
59
  function toRecord(value: unknown): Record<string, unknown> {
62
60
  return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}
63
61
  }
64
62
 
65
- async function resolveActionContext(req: Request): Promise<ActionRouteContext> {
63
+ async function resolveActionContext(
64
+ req: Request,
65
+ translate: ActionRouteContext['translate'],
66
+ ): Promise<ActionRouteContext> {
66
67
  const container = await createRequestContainer()
67
68
  const auth = await getAuthFromRequest(req)
68
69
  if (!auth || !auth.tenantId) {
69
- throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })
70
+ throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })
70
71
  }
71
72
  const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
72
73
  const organizationId = scope?.selectedId ?? auth.orgId ?? null
73
74
  if (!organizationId) {
74
- throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })
75
+ throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required.') })
75
76
  }
76
77
  return {
77
78
  ctx: {
@@ -84,7 +85,7 @@ async function resolveActionContext(req: Request): Promise<ActionRouteContext> {
84
85
  },
85
86
  tenantId: auth.tenantId,
86
87
  organizationId,
87
- translate: translateKey,
88
+ translate,
88
89
  }
89
90
  }
90
91
 
@@ -110,7 +111,7 @@ async function runGuard(
110
111
  ): Promise<RouteMutationGuardResult> {
111
112
  const userId = context.ctx.auth?.sub
112
113
  if (!userId) {
113
- throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })
114
+ throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })
114
115
  }
115
116
  return runRouteMutationGuards({
116
117
  container: context.ctx.container,
@@ -130,8 +131,9 @@ async function runGuard(
130
131
  }
131
132
 
132
133
  export async function POST(req: Request) {
134
+ const { translate } = await resolveTranslations()
133
135
  try {
134
- const context = await resolveActionContext(req)
136
+ const context = await resolveActionContext(req, translate)
135
137
  const requestInput = toSalesReturnRequest(toRecord(await readJsonSafe(req, {})))
136
138
  const guarded = await runGuard(req, context, requestInput)
137
139
  if (!guarded.ok) {
@@ -143,16 +145,20 @@ export async function POST(req: Request) {
143
145
  'warranty_claims.claim.create_sales_return',
144
146
  { input: toCommandInput(guardedInput, context), ctx: context.ctx },
145
147
  )
146
- if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })
148
+ if (!result) throw new CrudHttpError(400, { error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') })
147
149
  await guarded.runAfterSuccess()
148
150
  return NextResponse.json({ salesReturnId: result.salesReturnId, skippedLineIds: result.skippedLineIds })
149
151
  } catch (err) {
150
152
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
153
+ const interceptorRejection = getCommandInterceptorHttpRejection(err)
154
+ if (interceptorRejection) {
155
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
156
+ }
151
157
  if (err instanceof z.ZodError) {
152
- return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })
158
+ return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })
153
159
  }
154
160
  logger.error('warranty_claims.sales-return.post failed', { err })
155
- return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })
161
+ return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })
156
162
  }
157
163
  }
158
164
 
@@ -7,6 +7,7 @@ import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/d
7
7
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
8
8
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
9
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
10
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
10
11
  import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
11
12
  import { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'
12
13
  import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
@@ -165,6 +166,10 @@ export async function PUT(req: Request) {
165
166
  })
166
167
  } catch (err) {
167
168
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
169
+ const interceptorRejection = getCommandInterceptorHttpRejection(err)
170
+ if (interceptorRejection) {
171
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
172
+ }
168
173
  const { translate } = await resolveTranslations()
169
174
  if (err instanceof z.ZodError) {
170
175
  return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })
@@ -6,6 +6,7 @@ import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/d
6
6
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
7
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
8
8
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
9
10
  import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
10
11
  import { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'
11
12
  import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
@@ -118,6 +119,10 @@ export async function POST(req: Request) {
118
119
  return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.id })
119
120
  } catch (err) {
120
121
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
122
+ const interceptorRejection = getCommandInterceptorHttpRejection(err)
123
+ if (interceptorRejection) {
124
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
125
+ }
121
126
  const { translate } = await resolveTranslations()
122
127
  if (err instanceof z.ZodError) {
123
128
  return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })
@@ -6,6 +6,7 @@ import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/d
6
6
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
7
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
8
8
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
9
10
  import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
10
11
  import { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'
11
12
  import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
@@ -116,6 +117,10 @@ export async function POST(req: Request) {
116
117
  return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.id })
117
118
  } catch (err) {
118
119
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
120
+ const interceptorRejection = getCommandInterceptorHttpRejection(err)
121
+ if (interceptorRejection) {
122
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
123
+ }
119
124
  const { translate } = await resolveTranslations()
120
125
  if (err instanceof z.ZodError) {
121
126
  return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })
@@ -2,6 +2,7 @@ import { z } from 'zod'
2
2
  import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
3
3
  import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
4
4
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
5
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
5
6
  import { parseBooleanFromUnknown } from '@open-mercato/shared/lib/boolean'
6
7
  import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
7
8
  import { applyIdsFilter } from '../../lib/apiIdsFilter'
@@ -122,11 +123,24 @@ function toEntitySteps(value: unknown): Record<string, unknown> | null {
122
123
  if (value === undefined || value === null) return null
123
124
  const parsed = parseGuideSteps(value)
124
125
  if (!parsed) {
125
- throw new CrudHttpError(400, { error: 'warranty_claims.errors.invalidTroubleshootingSteps' })
126
+ throw new CrudHttpError(400, { error: '[internal] troubleshooting steps reached the entity mapper unvalidated' })
126
127
  }
127
128
  return { prompt: parsed.prompt, options: parsed.options }
128
129
  }
129
130
 
131
+ async function assertParsableGuideSteps(input: object): Promise<void> {
132
+ if (!hasOwn(input, 'steps')) return
133
+ const value = (input as { steps?: unknown }).steps
134
+ if (value === undefined || value === null || parseGuideSteps(value)) return
135
+ const { translate } = await resolveTranslations()
136
+ throw new CrudHttpError(400, {
137
+ error: translate(
138
+ 'warranty_claims.errors.invalidTroubleshootingSteps',
139
+ 'Troubleshooting steps must include a prompt and valid option branches.',
140
+ ),
141
+ })
142
+ }
143
+
130
144
  function toTroubleshootingGuideEntityData(input: TroubleshootingGuideCreateInput): Record<string, unknown> {
131
145
  return {
132
146
  claimType: input.claimType ?? null,
@@ -238,6 +252,14 @@ const crud = makeCrudRoute<RawTroubleshootingGuideInput, RawTroubleshootingGuide
238
252
  schema: rawBodySchema,
239
253
  mapToEntity: (input, ctx) => toTroubleshootingGuideEntityData(parseCreateInput(input, ctx)),
240
254
  },
255
+ hooks: {
256
+ beforeCreate: async (input) => {
257
+ await assertParsableGuideSteps(input)
258
+ },
259
+ beforeUpdate: async (input) => {
260
+ await assertParsableGuideSteps(input)
261
+ },
262
+ },
241
263
  update: {
242
264
  schema: rawBodySchema,
243
265
  getId: (input) => {
@@ -6,6 +6,7 @@ import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/d
6
6
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
7
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
8
8
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
9
10
  import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
10
11
  import { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'
11
12
  import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
@@ -120,6 +121,10 @@ export async function POST(req: Request) {
120
121
  return NextResponse.json({ ok: true, claimId: createdClaimId })
121
122
  } catch (err) {
122
123
  if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
124
+ const interceptorRejection = getCommandInterceptorHttpRejection(err)
125
+ if (interceptorRejection) {
126
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
127
+ }
123
128
  const { translate } = await resolveTranslations()
124
129
  if (err instanceof z.ZodError) {
125
130
  return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })
@@ -43,6 +43,7 @@ type SuggestionsRouteContext = {
43
43
  tenantId: string
44
44
  organizationId: string
45
45
  scope: WarrantyClaimScope
46
+ translate: (key: string, fallback?: string) => string
46
47
  em: EntityManager
47
48
  }
48
49
 
@@ -79,6 +80,7 @@ async function resolveSuggestionsContext(req: Request): Promise<SuggestionsRoute
79
80
  tenantId: auth.tenantId,
80
81
  organizationId,
81
82
  scope: { tenantId: auth.tenantId, organizationId },
83
+ translate,
82
84
  em,
83
85
  }
84
86
  }
@@ -88,7 +90,7 @@ export async function GET(req: Request) {
88
90
  const context = await resolveSuggestionsContext(req)
89
91
  const url = new URL(req.url)
90
92
  const query = querySchema.parse(Object.fromEntries(url.searchParams))
91
- const claim = await requireScopedClaim(context.em, query.claimId, context.scope)
93
+ const claim = await requireScopedClaim(context.em, query.claimId, context.scope, {}, context.translate('warranty_claims.errors.notFound', 'Claim not found.'))
92
94
  if (claim.claimType !== 'warranty' || (claim.status !== 'resolved' && claim.status !== 'closed')) {
93
95
  return NextResponse.json({ ok: true, result: { claimId: claim.id, suggestions: [] } })
94
96
  }
@@ -80,14 +80,20 @@ export async function loadScopedClaim(
80
80
  )
81
81
  }
82
82
 
83
+ /**
84
+ * `notFoundMessage` is passed to `assertFound` verbatim, so request-scoped callers
85
+ * must hand in an already-translated sentence; the key default only suits the
86
+ * command surface, which runs without a request locale.
87
+ */
83
88
  export async function requireScopedClaim(
84
89
  em: EntityManager,
85
90
  id: string,
86
91
  scope: WarrantyClaimScope,
87
92
  options: FindOneOptions<WarrantyClaim> = {},
93
+ notFoundMessage = 'warranty_claims.errors.notFound',
88
94
  ): Promise<WarrantyClaim> {
89
95
  const claim = await loadScopedClaim(em, id, scope, options)
90
- return assertFound(claim, 'warranty_claims.errors.notFound')
96
+ return assertFound(claim, notFoundMessage)
91
97
  }
92
98
 
93
99
  export function appendClaimEvent(
@@ -0,0 +1,30 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
3
+ import { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'
4
+
5
+ export type LinkedCustomerAuthContext = CustomerAuthContext & { customerEntityId: string }
6
+
7
+ export async function resolveLinkedCustomerAuth(req: Request): Promise<LinkedCustomerAuthContext | Response> {
8
+ const auth = await getCustomerAuthFromRequest(req)
9
+ if (!auth) {
10
+ const { translate } = await resolveTranslations()
11
+ return NextResponse.json(
12
+ { ok: false, error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') },
13
+ { status: 401 },
14
+ )
15
+ }
16
+ if (!auth.customerEntityId) {
17
+ const { translate } = await resolveTranslations()
18
+ return NextResponse.json(
19
+ {
20
+ ok: false,
21
+ error: translate(
22
+ 'warranty_claims.errors.customerAccountNotLinked',
23
+ 'Customer account is not linked to a customer record',
24
+ ),
25
+ },
26
+ { status: 403 },
27
+ )
28
+ }
29
+ return auth as LinkedCustomerAuthContext
30
+ }
@@ -17,6 +17,7 @@ import { createLogger } from '@open-mercato/shared/lib/logger'
17
17
  import { salesOrderWarehouseAssignBodySchema } from '../../../../data/validators'
18
18
  import { loadSalesOrderWarehouseAssignmentView } from '../../../../lib/salesOrderWarehouseAssignment'
19
19
  import { executeWmsCustomPostRoute } from '../../../inventory/helpers'
20
+ import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
20
21
 
21
22
  const logger = createLogger('wms')
22
23
 
@@ -241,6 +242,10 @@ export async function DELETE(
241
242
  }
242
243
  return NextResponse.json(intercepted.body, { status: intercepted.statusCode })
243
244
  } catch (error) {
245
+ const interceptorRejection = getCommandInterceptorHttpRejection(error)
246
+ if (interceptorRejection) {
247
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })
248
+ }
244
249
  if (error instanceof CrudHttpError) {
245
250
  return NextResponse.json(error.body, { status: error.status })
246
251
  }