@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +5 -0
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +40 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +6 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/system-user.js +8 -3
- package/dist/modules/communication_channels/lib/system-user.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +13 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +5 -0
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +5 -0
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +5 -0
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +5 -0
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +13 -0
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +5 -0
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +5 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +5 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +13 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +5 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +5 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +5 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +9 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/token/[token]/route.js +5 -0
- package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +5 -0
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +5 -0
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +5 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +25 -18
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +5 -0
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +16 -12
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +6 -1
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +24 -27
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js +8 -10
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js +4 -9
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +17 -13
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +19 -15
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/registrations/route.js +9 -4
- package/dist/modules/warranty_claims/api/registrations/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +16 -12
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +25 -19
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -1
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +16 -12
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +5 -0
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js +22 -1
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/commands/shared.js +2 -2
- package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js +31 -0
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js.map +7 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/package.json +7 -7
- package/src/helpers/integration/communicationChannelsFixtures.ts +13 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
- package/src/modules/auth/api/profile/route.ts +5 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +47 -2
- package/src/modules/communication_channels/lib/access-control.ts +28 -0
- package/src/modules/communication_channels/lib/system-user.ts +40 -22
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
- package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
- package/src/modules/customers/api/interactions/complete/route.ts +5 -0
- package/src/modules/customers/api/labels/assign/route.ts +5 -0
- package/src/modules/customers/api/labels/route.ts +5 -0
- package/src/modules/customers/api/labels/unassign/route.ts +5 -0
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
- package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
- package/src/modules/customers/api/pipelines/route.ts +13 -0
- package/src/modules/customers/api/settings/address-format/route.ts +5 -0
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
- package/src/modules/customers/api/tags/assign/route.ts +5 -0
- package/src/modules/customers/api/tags/unassign/route.ts +5 -0
- package/src/modules/customers/api/todos/route.ts +13 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -0
- package/src/modules/eudr/api/plots/import/route.ts +7 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
- package/src/modules/messages/api/[id]/forward/route.ts +5 -0
- package/src/modules/messages/api/[id]/reply/route.ts +5 -0
- package/src/modules/messages/api/[id]/route.ts +9 -0
- package/src/modules/messages/api/token/[token]/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
- package/src/modules/sales/api/quotes/accept/route.ts +5 -0
- package/src/modules/sales/api/quotes/convert/route.ts +5 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
- package/src/modules/staff/api/team-members/self/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
- package/src/modules/warranty_claims/api/ai/assess/route.ts +28 -17
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +19 -13
- package/src/modules/warranty_claims/api/events/route.ts +6 -1
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +29 -27
- package/src/modules/warranty_claims/api/portal/claims/[id]/route.ts +9 -10
- package/src/modules/warranty_claims/api/portal/claims/[id]/shared.ts +5 -9
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/route.ts +10 -9
- package/src/modules/warranty_claims/api/portal/events/route.ts +18 -13
- package/src/modules/warranty_claims/api/receiving/route.ts +22 -15
- package/src/modules/warranty_claims/api/registrations/route.ts +9 -3
- package/src/modules/warranty_claims/api/replacement-order/route.ts +19 -13
- package/src/modules/warranty_claims/api/return-label/route.ts +26 -17
- package/src/modules/warranty_claims/api/risk/route.ts +3 -1
- package/src/modules/warranty_claims/api/sales-return/route.ts +19 -13
- package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
- package/src/modules/warranty_claims/api/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/transition/route.ts +5 -0
- package/src/modules/warranty_claims/api/troubleshooting-guides/route.ts +23 -1
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +3 -1
- package/src/modules/warranty_claims/commands/shared.ts +7 -1
- package/src/modules/warranty_claims/lib/portalAuthGuard.ts +30 -0
- 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/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\
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,oBAAoB;AAC7B,SAAS,8BAA6D;AACtE,SAAS,yBAAyB;AAElC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,yCAAyC;AAClD,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,iBAAiB;AAa7C,MAAM,OAAO,EAAE,OAAO,EAAE,KAAK;AAC7B,MAAM,4BAA4B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;AAE5E,MAAM,oBAAoB,EACvB,OAAO;AAAA,EACN,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EACtD,WAAW;AACb,CAAC,EACA,OAAO;AAEV,MAAM,oBAAoB,EACvB,OAAO;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,EAAE,QAAQ,SAAS;AAAA,EAC3B,WAAW;AACb,CAAC,EACA,OAAO;AAEV,MAAM,sBAAsB,EAAE,MAAM,CAAC,mBAAmB,iBAAiB,CAAC;AAEnE,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,kCAAkC,EAAE;AACnF;AAEA,SAAS,SAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,CAAC;AAC3G;AAEA,
|
|
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 { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\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\nasync function resolveActionContext(\n req: Request,\n translate: ActionRouteContext['translate'],\n): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', '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: translate('warranty_claims.errors.organization_required', 'Organization context is 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,\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: 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_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 translate: ActionRouteContext['translate'],\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: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') })\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: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') })\n return result\n}\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const context = await resolveActionContext(req, translate)\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, translate)\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: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.receiving.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { 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
|
}
|
|
@@ -3,6 +3,7 @@ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
|
|
|
3
3
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
4
4
|
import { buildIlikeTerm } from "@open-mercato/shared/lib/db/buildIlikeTerm";
|
|
5
5
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
6
7
|
import { E } from "../../../../generated/entities.ids.generated.js";
|
|
7
8
|
import * as F from "../../../../generated/entities/warranty_claim_registration/index.js";
|
|
8
9
|
import { WarrantyClaimRegistration } from "../../data/entities.js";
|
|
@@ -67,7 +68,7 @@ function parseUpdateInput(input, ctx) {
|
|
|
67
68
|
function hasOwn(input, key) {
|
|
68
69
|
return Object.prototype.hasOwnProperty.call(input, key);
|
|
69
70
|
}
|
|
70
|
-
async function assertRegistrationSerialUnique(em, scope, serialNumber, excludeId) {
|
|
71
|
+
async function assertRegistrationSerialUnique(em, scope, serialNumber, excludeId, translate) {
|
|
71
72
|
if (!serialNumber) return;
|
|
72
73
|
const where = {
|
|
73
74
|
serialNumber,
|
|
@@ -78,7 +79,9 @@ async function assertRegistrationSerialUnique(em, scope, serialNumber, excludeId
|
|
|
78
79
|
if (excludeId) where.id = { $ne: excludeId };
|
|
79
80
|
const existing = await em.count(WarrantyClaimRegistration, where);
|
|
80
81
|
if (existing > 0) {
|
|
81
|
-
throw new CrudHttpError(400, {
|
|
82
|
+
throw new CrudHttpError(400, {
|
|
83
|
+
error: translate("warranty_claims.errors.duplicateSerial", "A registration with this serial number already exists.")
|
|
84
|
+
});
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
function parseDate(value) {
|
|
@@ -278,8 +281,9 @@ const crud = makeCrudRoute({
|
|
|
278
281
|
const result = registrationCreateSchema.safeParse({ ...input, ...scope });
|
|
279
282
|
if (!result.success) return;
|
|
280
283
|
const em = ctx.container.resolve("em").fork();
|
|
284
|
+
const { translate } = await resolveTranslations();
|
|
281
285
|
await assertOrderBelongsToCustomer(em, scope, result.data.orderId ?? null, result.data.customerId ?? null);
|
|
282
|
-
await assertRegistrationSerialUnique(em, scope, result.data.serialNumber ?? null, null);
|
|
286
|
+
await assertRegistrationSerialUnique(em, scope, result.data.serialNumber ?? null, null, translate);
|
|
283
287
|
},
|
|
284
288
|
beforeUpdate: async (input, ctx) => {
|
|
285
289
|
const scope = scopeFromContext(ctx);
|
|
@@ -304,7 +308,8 @@ const crud = makeCrudRoute({
|
|
|
304
308
|
const effectiveOrderId = hasOwn(parsed, "orderId") ? parsed.orderId ?? null : existing.orderId ?? null;
|
|
305
309
|
await assertOrderBelongsToCustomer(em, scope, effectiveOrderId, effectiveCustomerId);
|
|
306
310
|
if (hasOwn(parsed, "serialNumber") && (parsed.serialNumber ?? null) !== (existing.serialNumber ?? null)) {
|
|
307
|
-
|
|
311
|
+
const { translate } = await resolveTranslations();
|
|
312
|
+
await assertRegistrationSerialUnique(em, scope, parsed.serialNumber ?? null, existing.id, translate);
|
|
308
313
|
}
|
|
309
314
|
},
|
|
310
315
|
afterCreate: async (entity) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/registrations/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/warranty_claim_registration'\nimport { WarrantyClaimRegistration } from '../../data/entities'\nimport { assertOrderBelongsToCustomer } from '../../lib/orderOwnership'\nimport { emitWarrantyClaimsEvent } from '../../events'\nimport {\n registrationCreateSchema,\n registrationUpdateSchema,\n registrationCoverageTypeSchema,\n registrationSourceSchema,\n type RegistrationCreateInput,\n type RegistrationUpdateInput,\n} from '../../data/validators'\nimport { addWarrantyMonths } from '../../lib/warrantyPreview'\nimport {\n createPagedListResponseSchema,\n createWarrantyClaimsCrudOpenApi,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst rawBodySchema = z.object({}).passthrough()\nconst uuid = z.string().uuid()\n\nconst expiryWindowSchema = z.enum(['expired', 'expiring_30d', 'active'])\n\nconst EXPIRING_WINDOW_MS = 30 * 24 * 60 * 60 * 1000\n\nconst listSchema = z\n .object({\n id: uuid.optional(),\n serialNumber: z.string().trim().max(191).optional(),\n customerId: uuid.optional(),\n coverageType: registrationCoverageTypeSchema.optional(),\n source: registrationSourceSchema.optional(),\n expiry: expiryWindowSchema.optional(),\n search: z.string().trim().max(300).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n sortField: z.enum(['serialNumber', 'productName', 'sku', 'warrantyExpiresAt', 'updatedAt', 'createdAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\ntype RegistrationListQuery = z.infer<typeof listSchema>\ntype RawRegistrationInput = z.infer<typeof rawBodySchema>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.registration.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nfunction scopeFromContext(ctx: CrudCtx): { organizationId: string; tenantId: string } {\n const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n const tenantId = ctx.auth?.tenantId ?? null\n if (!organizationId || !tenantId) {\n throw new CrudHttpError(400, { error: '[internal] warranty registration scope is missing' })\n }\n return { organizationId, tenantId }\n}\n\nfunction parseCreateInput(input: RawRegistrationInput, ctx: CrudCtx): RegistrationCreateInput {\n return registrationCreateSchema.parse({\n ...input,\n ...scopeFromContext(ctx),\n })\n}\n\nfunction parseUpdateInput(input: RawRegistrationInput, ctx: CrudCtx): RegistrationUpdateInput {\n return registrationUpdateSchema.parse({\n ...input,\n ...scopeFromContext(ctx),\n })\n}\n\nfunction hasOwn(input: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(input, key)\n}\n\nasync function assertRegistrationSerialUnique(\n em: EntityManager,\n scope: { organizationId: string; tenantId: string },\n serialNumber: string | null | undefined,\n excludeId: string | null,\n): Promise<void> {\n if (!serialNumber) return\n const where: FilterQuery<WarrantyClaimRegistration> = {\n serialNumber,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n }\n if (excludeId) where.id = { $ne: excludeId }\n const existing = await em.count(WarrantyClaimRegistration, where)\n if (existing > 0) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.duplicateSerial' })\n }\n}\n\nfunction parseDate(value: string | Date | null | undefined): Date | null {\n if (value === null || value === undefined) return null\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction resolveWarrantyExpiresAt(input: RegistrationCreateInput): Date | null {\n if (hasOwn(input, 'warrantyExpiresAt')) return parseDate(input.warrantyExpiresAt)\n const purchaseDate = parseDate(input.purchaseDate)\n return purchaseDate && input.warrantyMonths !== null && input.warrantyMonths !== undefined\n ? addWarrantyMonths(purchaseDate, input.warrantyMonths)\n : null\n}\n\nfunction resolveUpdatedWarrantyExpiresAt(\n entity: WarrantyClaimRegistration,\n input: RegistrationUpdateInput,\n): Date | null | undefined {\n if (hasOwn(input, 'warrantyExpiresAt')) return parseDate(input.warrantyExpiresAt)\n if (!hasOwn(input, 'purchaseDate') && !hasOwn(input, 'warrantyMonths')) return undefined\n const purchaseDate = hasOwn(input, 'purchaseDate') ? parseDate(input.purchaseDate) : entity.purchaseDate ?? null\n const warrantyMonths = hasOwn(input, 'warrantyMonths') ? input.warrantyMonths ?? null : entity.warrantyMonths ?? null\n return purchaseDate && warrantyMonths !== null ? addWarrantyMonths(purchaseDate, warrantyMonths) : null\n}\n\nfunction toRegistrationEntityData(input: RegistrationCreateInput): Record<string, unknown> {\n return {\n serialNumber: input.serialNumber ?? null,\n productId: input.productId ?? null,\n variantId: input.variantId ?? null,\n sku: input.sku ?? null,\n productName: input.productName ?? null,\n customerId: input.customerId ?? null,\n orderId: input.orderId ?? null,\n purchaseDate: parseDate(input.purchaseDate),\n warrantyMonths: input.warrantyMonths ?? null,\n warrantyExpiresAt: resolveWarrantyExpiresAt(input),\n coverageType: input.coverageType ?? null,\n source: input.source ?? null,\n proofAttachmentId: input.proofAttachmentId ?? null,\n notes: input.notes ?? null,\n }\n}\n\nfunction applyRegistrationUpdate(entity: WarrantyClaimRegistration, input: RegistrationUpdateInput): void {\n if (hasOwn(input, 'serialNumber')) entity.serialNumber = input.serialNumber ?? null\n if (hasOwn(input, 'productId')) entity.productId = input.productId ?? null\n if (hasOwn(input, 'variantId')) entity.variantId = input.variantId ?? null\n if (hasOwn(input, 'sku')) entity.sku = input.sku ?? null\n if (hasOwn(input, 'productName')) entity.productName = input.productName ?? null\n if (hasOwn(input, 'customerId')) entity.customerId = input.customerId ?? null\n if (hasOwn(input, 'orderId')) entity.orderId = input.orderId ?? null\n if (hasOwn(input, 'purchaseDate')) entity.purchaseDate = parseDate(input.purchaseDate)\n if (hasOwn(input, 'warrantyMonths')) entity.warrantyMonths = input.warrantyMonths ?? null\n const warrantyExpiresAt = resolveUpdatedWarrantyExpiresAt(entity, input)\n if (warrantyExpiresAt !== undefined) entity.warrantyExpiresAt = warrantyExpiresAt\n if (hasOwn(input, 'coverageType')) entity.coverageType = input.coverageType ?? null\n if (hasOwn(input, 'source')) entity.source = input.source ?? null\n if (hasOwn(input, 'proofAttachmentId')) entity.proofAttachmentId = input.proofAttachmentId ?? null\n if (hasOwn(input, 'notes')) entity.notes = input.notes ?? null\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\nexport async function emitRegistrationCreatedEvent(entity: unknown): Promise<void> {\n const record = toRecord(entity)\n const registrationId = typeof record.id === 'string' ? record.id : null\n const tenantId = typeof record.tenantId === 'string' ? record.tenantId : null\n const organizationId = typeof record.organizationId === 'string' ? record.organizationId : null\n if (!registrationId || !tenantId || !organizationId) return\n await emitWarrantyClaimsEvent('warranty_claims.registration.created', {\n registrationId,\n tenantId,\n organizationId,\n }, { persistent: true, tenantId, organizationId })\n}\n\nfunction readString(record: Record<string, unknown>, snakeKey: string, camelKey: string): string | null {\n const value = record[snakeKey] ?? record[camelKey]\n return typeof value === 'string' ? value : null\n}\n\nfunction readNumber(record: Record<string, unknown>, snakeKey: string, camelKey: string): number | null {\n const value = record[snakeKey] ?? record[camelKey]\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return 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 transformRegistrationItem(item: unknown): unknown {\n const record = toRecord(item)\n if (!Object.keys(record).length) return item\n return {\n id: readString(record, 'id', 'id'),\n organizationId: readString(record, 'organization_id', 'organizationId'),\n tenantId: readString(record, 'tenant_id', 'tenantId'),\n serialNumber: readString(record, 'serial_number', 'serialNumber'),\n productId: readString(record, 'product_id', 'productId'),\n variantId: readString(record, 'variant_id', 'variantId'),\n sku: readString(record, 'sku', 'sku'),\n productName: readString(record, 'product_name', 'productName'),\n customerId: readString(record, 'customer_id', 'customerId'),\n orderId: readString(record, 'order_id', 'orderId'),\n purchaseDate: toIso(record.purchase_date ?? record.purchaseDate),\n warrantyMonths: readNumber(record, 'warranty_months', 'warrantyMonths'),\n warrantyExpiresAt: toIso(record.warranty_expires_at ?? record.warrantyExpiresAt),\n coverageType: readString(record, 'coverage_type', 'coverageType'),\n source: readString(record, 'source', 'source'),\n proofAttachmentId: readString(record, 'proof_attachment_id', 'proofAttachmentId'),\n notes: readString(record, 'notes', 'notes'),\n createdAt: toIso(record.created_at ?? record.createdAt),\n updatedAt: toIso(record.updated_at ?? record.updatedAt),\n deletedAt: toIso(record.deleted_at ?? record.deletedAt),\n }\n}\n\nconst crud = makeCrudRoute<RawRegistrationInput, RawRegistrationInput, RegistrationListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: WarrantyClaimRegistration,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.warranty_claims.warranty_claim_registration },\n list: {\n schema: listSchema,\n entityId: E.warranty_claims.warranty_claim_registration,\n fields: [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.serial_number,\n F.product_id,\n F.variant_id,\n F.sku,\n F.product_name,\n F.customer_id,\n F.order_id,\n F.purchase_date,\n F.warranty_months,\n F.warranty_expires_at,\n F.coverage_type,\n F.source,\n F.proof_attachment_id,\n F.notes,\n F.created_at,\n F.updated_at,\n F.deleted_at,\n ],\n sortFieldMap: {\n serialNumber: F.serial_number,\n productName: F.product_name,\n sku: F.sku,\n warrantyExpiresAt: F.warranty_expires_at,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query.serialNumber) filters.serial_number = { $eq: query.serialNumber }\n if (query.customerId) filters.customer_id = { $eq: query.customerId }\n if (query.coverageType) filters.coverage_type = { $eq: query.coverageType }\n if (query.source) filters.source = { $eq: query.source }\n if (query.expiry) {\n const now = new Date()\n if (query.expiry === 'expired') {\n filters.warranty_expires_at = { $lt: now }\n } else if (query.expiry === 'expiring_30d') {\n filters.warranty_expires_at = { $gte: now, $lte: new Date(now.getTime() + EXPIRING_WINDOW_MS) }\n } else {\n filters.warranty_expires_at = { $gte: now }\n }\n }\n if (query.search) {\n const pattern = buildIlikeTerm(query.search)\n filters.$or = [\n { serial_number: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { product_name: { $ilike: pattern } },\n ]\n }\n return filters\n },\n transformItem: transformRegistrationItem,\n },\n create: {\n schema: rawBodySchema,\n mapToEntity: (input, ctx) => toRegistrationEntityData(parseCreateInput(input, ctx)),\n },\n hooks: {\n beforeCreate: async (input, ctx) => {\n const scope = scopeFromContext(ctx)\n const result = registrationCreateSchema.safeParse({ ...input, ...scope })\n if (!result.success) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await assertOrderBelongsToCustomer(em, scope, result.data.orderId ?? null, result.data.customerId ?? null)\n await assertRegistrationSerialUnique(em, scope, result.data.serialNumber ?? null, null)\n },\n beforeUpdate: async (input, ctx) => {\n const scope = scopeFromContext(ctx)\n const result = registrationUpdateSchema.safeParse({ ...input, ...scope })\n if (!result.success) return\n const parsed = result.data\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await findOneWithDecryption(\n em,\n WarrantyClaimRegistration,\n {\n id: parsed.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (!existing) return\n const effectiveCustomerId = hasOwn(parsed, 'customerId') ? (parsed.customerId ?? null) : (existing.customerId ?? null)\n const effectiveOrderId = hasOwn(parsed, 'orderId') ? (parsed.orderId ?? null) : (existing.orderId ?? null)\n await assertOrderBelongsToCustomer(em, scope, effectiveOrderId, effectiveCustomerId)\n if (hasOwn(parsed, 'serialNumber') && (parsed.serialNumber ?? null) !== (existing.serialNumber ?? null)) {\n await assertRegistrationSerialUnique(em, scope, parsed.serialNumber ?? null, existing.id)\n }\n },\n afterCreate: async (entity) => {\n await emitRegistrationCreatedEvent(entity)\n },\n },\n update: {\n schema: rawBodySchema,\n getId: (input) => {\n const id = input.id\n return typeof id === 'string' ? id : ''\n },\n applyToEntity: (entity, input, ctx) => {\n applyRegistrationUpdate(entity as WarrantyClaimRegistration, parseUpdateInput(input, ctx))\n },\n response: () => ({ ok: true }),\n },\n del: { idFrom: 'query', softDelete: true, response: () => ({ ok: true }) },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst registrationListItemSchema = z.object({\n id: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable(),\n tenantId: z.string().uuid().nullable(),\n serialNumber: z.string().nullable(),\n productId: z.string().uuid().nullable(),\n variantId: z.string().uuid().nullable(),\n sku: z.string().nullable(),\n productName: z.string().nullable(),\n customerId: z.string().uuid().nullable(),\n orderId: z.string().uuid().nullable(),\n purchaseDate: z.string().nullable(),\n warrantyMonths: z.number().nullable(),\n warrantyExpiresAt: z.string().nullable(),\n coverageType: registrationCoverageTypeSchema.nullable(),\n source: registrationSourceSchema.nullable(),\n proofAttachmentId: z.string().uuid().nullable(),\n notes: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n}).passthrough()\n\nexport const openApi = createWarrantyClaimsCrudOpenApi({\n resourceName: 'WarrantyRegistration',\n pluralName: 'WarrantyRegistrations',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(registrationListItemSchema),\n create: {\n schema: registrationCreateSchema,\n responseSchema: z.object({ id: z.string().uuid() }),\n description: 'Creates a product warranty registration for entitlement resolution.',\n },\n update: {\n schema: registrationUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a product warranty registration.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a product warranty registration.',\n },\n})\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/warranty_claim_registration'\nimport { WarrantyClaimRegistration } from '../../data/entities'\nimport { assertOrderBelongsToCustomer } from '../../lib/orderOwnership'\nimport { emitWarrantyClaimsEvent } from '../../events'\nimport {\n registrationCreateSchema,\n registrationUpdateSchema,\n registrationCoverageTypeSchema,\n registrationSourceSchema,\n type RegistrationCreateInput,\n type RegistrationUpdateInput,\n} from '../../data/validators'\nimport { addWarrantyMonths } from '../../lib/warrantyPreview'\nimport {\n createPagedListResponseSchema,\n createWarrantyClaimsCrudOpenApi,\n defaultOkResponseSchema,\n} from '../openapi'\n\nconst rawBodySchema = z.object({}).passthrough()\nconst uuid = z.string().uuid()\n\nconst expiryWindowSchema = z.enum(['expired', 'expiring_30d', 'active'])\n\nconst EXPIRING_WINDOW_MS = 30 * 24 * 60 * 60 * 1000\n\nconst listSchema = z\n .object({\n id: uuid.optional(),\n serialNumber: z.string().trim().max(191).optional(),\n customerId: uuid.optional(),\n coverageType: registrationCoverageTypeSchema.optional(),\n source: registrationSourceSchema.optional(),\n expiry: expiryWindowSchema.optional(),\n search: z.string().trim().max(300).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n sortField: z.enum(['serialNumber', 'productName', 'sku', 'warrantyExpiresAt', 'updatedAt', 'createdAt']).optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\ntype RegistrationListQuery = z.infer<typeof listSchema>\ntype RawRegistrationInput = z.infer<typeof rawBodySchema>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.registration.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['warranty_claims.registration.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nfunction scopeFromContext(ctx: CrudCtx): { organizationId: string; tenantId: string } {\n const organizationId = ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n const tenantId = ctx.auth?.tenantId ?? null\n if (!organizationId || !tenantId) {\n throw new CrudHttpError(400, { error: '[internal] warranty registration scope is missing' })\n }\n return { organizationId, tenantId }\n}\n\nfunction parseCreateInput(input: RawRegistrationInput, ctx: CrudCtx): RegistrationCreateInput {\n return registrationCreateSchema.parse({\n ...input,\n ...scopeFromContext(ctx),\n })\n}\n\nfunction parseUpdateInput(input: RawRegistrationInput, ctx: CrudCtx): RegistrationUpdateInput {\n return registrationUpdateSchema.parse({\n ...input,\n ...scopeFromContext(ctx),\n })\n}\n\nfunction hasOwn(input: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(input, key)\n}\n\nasync function assertRegistrationSerialUnique(\n em: EntityManager,\n scope: { organizationId: string; tenantId: string },\n serialNumber: string | null | undefined,\n excludeId: string | null,\n translate: (key: string, fallback?: string) => string,\n): Promise<void> {\n if (!serialNumber) return\n const where: FilterQuery<WarrantyClaimRegistration> = {\n serialNumber,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n }\n if (excludeId) where.id = { $ne: excludeId }\n const existing = await em.count(WarrantyClaimRegistration, where)\n if (existing > 0) {\n throw new CrudHttpError(400, {\n error: translate('warranty_claims.errors.duplicateSerial', 'A registration with this serial number already exists.'),\n })\n }\n}\n\nfunction parseDate(value: string | Date | null | undefined): Date | null {\n if (value === null || value === undefined) return null\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction resolveWarrantyExpiresAt(input: RegistrationCreateInput): Date | null {\n if (hasOwn(input, 'warrantyExpiresAt')) return parseDate(input.warrantyExpiresAt)\n const purchaseDate = parseDate(input.purchaseDate)\n return purchaseDate && input.warrantyMonths !== null && input.warrantyMonths !== undefined\n ? addWarrantyMonths(purchaseDate, input.warrantyMonths)\n : null\n}\n\nfunction resolveUpdatedWarrantyExpiresAt(\n entity: WarrantyClaimRegistration,\n input: RegistrationUpdateInput,\n): Date | null | undefined {\n if (hasOwn(input, 'warrantyExpiresAt')) return parseDate(input.warrantyExpiresAt)\n if (!hasOwn(input, 'purchaseDate') && !hasOwn(input, 'warrantyMonths')) return undefined\n const purchaseDate = hasOwn(input, 'purchaseDate') ? parseDate(input.purchaseDate) : entity.purchaseDate ?? null\n const warrantyMonths = hasOwn(input, 'warrantyMonths') ? input.warrantyMonths ?? null : entity.warrantyMonths ?? null\n return purchaseDate && warrantyMonths !== null ? addWarrantyMonths(purchaseDate, warrantyMonths) : null\n}\n\nfunction toRegistrationEntityData(input: RegistrationCreateInput): Record<string, unknown> {\n return {\n serialNumber: input.serialNumber ?? null,\n productId: input.productId ?? null,\n variantId: input.variantId ?? null,\n sku: input.sku ?? null,\n productName: input.productName ?? null,\n customerId: input.customerId ?? null,\n orderId: input.orderId ?? null,\n purchaseDate: parseDate(input.purchaseDate),\n warrantyMonths: input.warrantyMonths ?? null,\n warrantyExpiresAt: resolveWarrantyExpiresAt(input),\n coverageType: input.coverageType ?? null,\n source: input.source ?? null,\n proofAttachmentId: input.proofAttachmentId ?? null,\n notes: input.notes ?? null,\n }\n}\n\nfunction applyRegistrationUpdate(entity: WarrantyClaimRegistration, input: RegistrationUpdateInput): void {\n if (hasOwn(input, 'serialNumber')) entity.serialNumber = input.serialNumber ?? null\n if (hasOwn(input, 'productId')) entity.productId = input.productId ?? null\n if (hasOwn(input, 'variantId')) entity.variantId = input.variantId ?? null\n if (hasOwn(input, 'sku')) entity.sku = input.sku ?? null\n if (hasOwn(input, 'productName')) entity.productName = input.productName ?? null\n if (hasOwn(input, 'customerId')) entity.customerId = input.customerId ?? null\n if (hasOwn(input, 'orderId')) entity.orderId = input.orderId ?? null\n if (hasOwn(input, 'purchaseDate')) entity.purchaseDate = parseDate(input.purchaseDate)\n if (hasOwn(input, 'warrantyMonths')) entity.warrantyMonths = input.warrantyMonths ?? null\n const warrantyExpiresAt = resolveUpdatedWarrantyExpiresAt(entity, input)\n if (warrantyExpiresAt !== undefined) entity.warrantyExpiresAt = warrantyExpiresAt\n if (hasOwn(input, 'coverageType')) entity.coverageType = input.coverageType ?? null\n if (hasOwn(input, 'source')) entity.source = input.source ?? null\n if (hasOwn(input, 'proofAttachmentId')) entity.proofAttachmentId = input.proofAttachmentId ?? null\n if (hasOwn(input, 'notes')) entity.notes = input.notes ?? null\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\nexport async function emitRegistrationCreatedEvent(entity: unknown): Promise<void> {\n const record = toRecord(entity)\n const registrationId = typeof record.id === 'string' ? record.id : null\n const tenantId = typeof record.tenantId === 'string' ? record.tenantId : null\n const organizationId = typeof record.organizationId === 'string' ? record.organizationId : null\n if (!registrationId || !tenantId || !organizationId) return\n await emitWarrantyClaimsEvent('warranty_claims.registration.created', {\n registrationId,\n tenantId,\n organizationId,\n }, { persistent: true, tenantId, organizationId })\n}\n\nfunction readString(record: Record<string, unknown>, snakeKey: string, camelKey: string): string | null {\n const value = record[snakeKey] ?? record[camelKey]\n return typeof value === 'string' ? value : null\n}\n\nfunction readNumber(record: Record<string, unknown>, snakeKey: string, camelKey: string): number | null {\n const value = record[snakeKey] ?? record[camelKey]\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return 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 transformRegistrationItem(item: unknown): unknown {\n const record = toRecord(item)\n if (!Object.keys(record).length) return item\n return {\n id: readString(record, 'id', 'id'),\n organizationId: readString(record, 'organization_id', 'organizationId'),\n tenantId: readString(record, 'tenant_id', 'tenantId'),\n serialNumber: readString(record, 'serial_number', 'serialNumber'),\n productId: readString(record, 'product_id', 'productId'),\n variantId: readString(record, 'variant_id', 'variantId'),\n sku: readString(record, 'sku', 'sku'),\n productName: readString(record, 'product_name', 'productName'),\n customerId: readString(record, 'customer_id', 'customerId'),\n orderId: readString(record, 'order_id', 'orderId'),\n purchaseDate: toIso(record.purchase_date ?? record.purchaseDate),\n warrantyMonths: readNumber(record, 'warranty_months', 'warrantyMonths'),\n warrantyExpiresAt: toIso(record.warranty_expires_at ?? record.warrantyExpiresAt),\n coverageType: readString(record, 'coverage_type', 'coverageType'),\n source: readString(record, 'source', 'source'),\n proofAttachmentId: readString(record, 'proof_attachment_id', 'proofAttachmentId'),\n notes: readString(record, 'notes', 'notes'),\n createdAt: toIso(record.created_at ?? record.createdAt),\n updatedAt: toIso(record.updated_at ?? record.updatedAt),\n deletedAt: toIso(record.deleted_at ?? record.deletedAt),\n }\n}\n\nconst crud = makeCrudRoute<RawRegistrationInput, RawRegistrationInput, RegistrationListQuery>({\n metadata: routeMetadata,\n orm: {\n entity: WarrantyClaimRegistration,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.warranty_claims.warranty_claim_registration },\n list: {\n schema: listSchema,\n entityId: E.warranty_claims.warranty_claim_registration,\n fields: [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.serial_number,\n F.product_id,\n F.variant_id,\n F.sku,\n F.product_name,\n F.customer_id,\n F.order_id,\n F.purchase_date,\n F.warranty_months,\n F.warranty_expires_at,\n F.coverage_type,\n F.source,\n F.proof_attachment_id,\n F.notes,\n F.created_at,\n F.updated_at,\n F.deleted_at,\n ],\n sortFieldMap: {\n serialNumber: F.serial_number,\n productName: F.product_name,\n sku: F.sku,\n warrantyExpiresAt: F.warranty_expires_at,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query.serialNumber) filters.serial_number = { $eq: query.serialNumber }\n if (query.customerId) filters.customer_id = { $eq: query.customerId }\n if (query.coverageType) filters.coverage_type = { $eq: query.coverageType }\n if (query.source) filters.source = { $eq: query.source }\n if (query.expiry) {\n const now = new Date()\n if (query.expiry === 'expired') {\n filters.warranty_expires_at = { $lt: now }\n } else if (query.expiry === 'expiring_30d') {\n filters.warranty_expires_at = { $gte: now, $lte: new Date(now.getTime() + EXPIRING_WINDOW_MS) }\n } else {\n filters.warranty_expires_at = { $gte: now }\n }\n }\n if (query.search) {\n const pattern = buildIlikeTerm(query.search)\n filters.$or = [\n { serial_number: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { product_name: { $ilike: pattern } },\n ]\n }\n return filters\n },\n transformItem: transformRegistrationItem,\n },\n create: {\n schema: rawBodySchema,\n mapToEntity: (input, ctx) => toRegistrationEntityData(parseCreateInput(input, ctx)),\n },\n hooks: {\n beforeCreate: async (input, ctx) => {\n const scope = scopeFromContext(ctx)\n const result = registrationCreateSchema.safeParse({ ...input, ...scope })\n if (!result.success) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const { translate } = await resolveTranslations()\n await assertOrderBelongsToCustomer(em, scope, result.data.orderId ?? null, result.data.customerId ?? null)\n await assertRegistrationSerialUnique(em, scope, result.data.serialNumber ?? null, null, translate)\n },\n beforeUpdate: async (input, ctx) => {\n const scope = scopeFromContext(ctx)\n const result = registrationUpdateSchema.safeParse({ ...input, ...scope })\n if (!result.success) return\n const parsed = result.data\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const existing = await findOneWithDecryption(\n em,\n WarrantyClaimRegistration,\n {\n id: parsed.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (!existing) return\n const effectiveCustomerId = hasOwn(parsed, 'customerId') ? (parsed.customerId ?? null) : (existing.customerId ?? null)\n const effectiveOrderId = hasOwn(parsed, 'orderId') ? (parsed.orderId ?? null) : (existing.orderId ?? null)\n await assertOrderBelongsToCustomer(em, scope, effectiveOrderId, effectiveCustomerId)\n if (hasOwn(parsed, 'serialNumber') && (parsed.serialNumber ?? null) !== (existing.serialNumber ?? null)) {\n const { translate } = await resolveTranslations()\n await assertRegistrationSerialUnique(em, scope, parsed.serialNumber ?? null, existing.id, translate)\n }\n },\n afterCreate: async (entity) => {\n await emitRegistrationCreatedEvent(entity)\n },\n },\n update: {\n schema: rawBodySchema,\n getId: (input) => {\n const id = input.id\n return typeof id === 'string' ? id : ''\n },\n applyToEntity: (entity, input, ctx) => {\n applyRegistrationUpdate(entity as WarrantyClaimRegistration, parseUpdateInput(input, ctx))\n },\n response: () => ({ ok: true }),\n },\n del: { idFrom: 'query', softDelete: true, response: () => ({ ok: true }) },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst registrationListItemSchema = z.object({\n id: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable(),\n tenantId: z.string().uuid().nullable(),\n serialNumber: z.string().nullable(),\n productId: z.string().uuid().nullable(),\n variantId: z.string().uuid().nullable(),\n sku: z.string().nullable(),\n productName: z.string().nullable(),\n customerId: z.string().uuid().nullable(),\n orderId: z.string().uuid().nullable(),\n purchaseDate: z.string().nullable(),\n warrantyMonths: z.number().nullable(),\n warrantyExpiresAt: z.string().nullable(),\n coverageType: registrationCoverageTypeSchema.nullable(),\n source: registrationSourceSchema.nullable(),\n proofAttachmentId: z.string().uuid().nullable(),\n notes: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n}).passthrough()\n\nexport const openApi = createWarrantyClaimsCrudOpenApi({\n resourceName: 'WarrantyRegistration',\n pluralName: 'WarrantyRegistrations',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(registrationListItemSchema),\n create: {\n schema: registrationCreateSchema,\n responseSchema: z.object({ id: z.string().uuid() }),\n description: 'Creates a product warranty registration for entitlement resolution.',\n },\n update: {\n schema: registrationUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a product warranty registration.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a product warranty registration.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,9 @@ 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";
|
|
9
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
11
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
10
12
|
import {
|
|
@@ -28,22 +30,19 @@ const errorResponseSchema = z.object({
|
|
|
28
30
|
const metadata = {
|
|
29
31
|
POST: { requireAuth: true, requireFeatures: ["warranty_claims.claim.manage", "sales.orders.manage"] }
|
|
30
32
|
};
|
|
31
|
-
function translateKey(key) {
|
|
32
|
-
return key;
|
|
33
|
-
}
|
|
34
33
|
function toRecord(value) {
|
|
35
34
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
36
35
|
}
|
|
37
|
-
async function resolveActionContext(req) {
|
|
36
|
+
async function resolveActionContext(req, translate) {
|
|
38
37
|
const container = await createRequestContainer();
|
|
39
38
|
const auth = await getAuthFromRequest(req);
|
|
40
39
|
if (!auth || !auth.tenantId) {
|
|
41
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
40
|
+
throw new CrudHttpError(401, { error: translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
42
41
|
}
|
|
43
42
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
44
43
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
45
44
|
if (!organizationId) {
|
|
46
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.organization_required" });
|
|
45
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.organization_required", "Organization context is required.") });
|
|
47
46
|
}
|
|
48
47
|
return {
|
|
49
48
|
ctx: {
|
|
@@ -56,7 +55,7 @@ async function resolveActionContext(req) {
|
|
|
56
55
|
},
|
|
57
56
|
tenantId: auth.tenantId,
|
|
58
57
|
organizationId,
|
|
59
|
-
translate
|
|
58
|
+
translate
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
61
|
function toReplacementOrderRequest(payload) {
|
|
@@ -73,7 +72,7 @@ function toCommandInput(input, context) {
|
|
|
73
72
|
async function runGuard(req, context, input) {
|
|
74
73
|
const userId = context.ctx.auth?.sub;
|
|
75
74
|
if (!userId) {
|
|
76
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
75
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
77
76
|
}
|
|
78
77
|
return runRouteMutationGuards({
|
|
79
78
|
container: context.ctx.container,
|
|
@@ -92,8 +91,9 @@ async function runGuard(req, context, input) {
|
|
|
92
91
|
});
|
|
93
92
|
}
|
|
94
93
|
async function POST(req) {
|
|
94
|
+
const { translate } = await resolveTranslations();
|
|
95
95
|
try {
|
|
96
|
-
const context = await resolveActionContext(req);
|
|
96
|
+
const context = await resolveActionContext(req, translate);
|
|
97
97
|
const requestInput = toReplacementOrderRequest(toRecord(await readJsonSafe(req, {})));
|
|
98
98
|
const guarded = await runGuard(req, context, requestInput);
|
|
99
99
|
if (!guarded.ok) {
|
|
@@ -105,7 +105,7 @@ async function POST(req) {
|
|
|
105
105
|
"warranty_claims.claim.create_replacement_order",
|
|
106
106
|
{ input: toCommandInput(guardedInput, context), ctx: context.ctx }
|
|
107
107
|
);
|
|
108
|
-
if (!result) throw new CrudHttpError(400, { error: "warranty_claims.errors.save_failed" });
|
|
108
|
+
if (!result) throw new CrudHttpError(400, { error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") });
|
|
109
109
|
await guarded.runAfterSuccess();
|
|
110
110
|
return NextResponse.json({
|
|
111
111
|
replacementOrderId: result.replacementOrderId,
|
|
@@ -113,11 +113,15 @@ async function POST(req) {
|
|
|
113
113
|
});
|
|
114
114
|
} catch (err) {
|
|
115
115
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
116
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
117
|
+
if (interceptorRejection) {
|
|
118
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
119
|
+
}
|
|
116
120
|
if (err instanceof z.ZodError) {
|
|
117
|
-
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
121
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
|
|
118
122
|
}
|
|
119
123
|
logger.error("warranty_claims.replacement-order.post failed", { err });
|
|
120
|
-
return NextResponse.json({ error: "warranty_claims.errors.save_failed" }, { status: 500 });
|
|
124
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") }, { status: 500 });
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/replacement-order/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 claimCreateReplacementOrderSchema,\n type ClaimCreateReplacementOrderInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_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 ReplacementOrderCommandResult = {\n claimId: string\n replacementOrderId: string\n replacementOrderUpdatedAt: string | null\n skippedLineIds: string[]\n pricing: 'zero' | 'original'\n}\n\nconst replacementOrderRequestSchema = z\n .object({\n claimId: z.string().uuid(),\n pricing: z.enum(['zero', 'original']).optional(),\n updatedAt: z.string().datetime().nullable().optional(),\n })\n .strict()\n\ntype ReplacementOrderRequest = z.infer<typeof replacementOrderRequestSchema>\n\nconst replacementOrderResponseSchema = z.object({\n replacementOrderId: z.string().uuid(),\n skippedLineIds: z.array(z.string().uuid()),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage', 'sales.orders.manage'] },\n}\n\nfunction
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,oBAAoB;AAC7B,SAAS,8BAA6D;AACtE,SAAS,yBAAyB;AAElC;AAAA,EACE;AAAA,OAEK;AACP,SAAS,oCAAoC;AAC7C,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,iBAAiB;AAiB7C,MAAM,gCAAgC,EACnC,OAAO;AAAA,EACN,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,SAAS,EAAE,KAAK,CAAC,QAAQ,UAAU,CAAC,EAAE,SAAS;AAAA,EAC/C,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;AACvD,CAAC,EACA,OAAO;AAIV,MAAM,iCAAiC,EAAE,OAAO;AAAA,EAC9C,oBAAoB,EAAE,OAAO,EAAE,KAAK;AAAA,EACpC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAC3C,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,gCAAgC,qBAAqB,EAAE;AACtG;AAEA,SAAS,
|
|
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 { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\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 claimCreateReplacementOrderSchema,\n type ClaimCreateReplacementOrderInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_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 ReplacementOrderCommandResult = {\n claimId: string\n replacementOrderId: string\n replacementOrderUpdatedAt: string | null\n skippedLineIds: string[]\n pricing: 'zero' | 'original'\n}\n\nconst replacementOrderRequestSchema = z\n .object({\n claimId: z.string().uuid(),\n pricing: z.enum(['zero', 'original']).optional(),\n updatedAt: z.string().datetime().nullable().optional(),\n })\n .strict()\n\ntype ReplacementOrderRequest = z.infer<typeof replacementOrderRequestSchema>\n\nconst replacementOrderResponseSchema = z.object({\n replacementOrderId: z.string().uuid(),\n skippedLineIds: z.array(z.string().uuid()),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage', 'sales.orders.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\nasync function resolveActionContext(\n req: Request,\n translate: ActionRouteContext['translate'],\n): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', '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: translate('warranty_claims.errors.organization_required', 'Organization context is 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,\n }\n}\n\nfunction toReplacementOrderRequest(payload: Record<string, unknown>): ReplacementOrderRequest {\n return replacementOrderRequestSchema.parse(payload)\n}\n\nfunction toCommandInput(\n input: ReplacementOrderRequest,\n context: ActionRouteContext,\n): ClaimCreateReplacementOrderInput {\n const scopedPayload = withScopedPayload({\n id: input.claimId,\n pricing: input.pricing,\n updatedAt: input.updatedAt,\n }, context.ctx, context.translate)\n return claimCreateReplacementOrderSchema.parse(scopedPayload)\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: ReplacementOrderRequest,\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 POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const context = await resolveActionContext(req, translate)\n const requestInput = toReplacementOrderRequest(toRecord(await readJsonSafe(req, {})))\n const guarded = await runGuard(req, context, requestInput)\n if (!guarded.ok) {\n return guarded.response\n }\n const guardedInput = guarded.modifiedPayload\n ? toReplacementOrderRequest(guarded.modifiedPayload)\n : requestInput\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<ClaimCreateReplacementOrderInput, ReplacementOrderCommandResult>(\n 'warranty_claims.claim.create_replacement_order',\n { input: toCommandInput(guardedInput, context), ctx: context.ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') })\n await guarded.runAfterSuccess()\n return NextResponse.json({\n replacementOrderId: result.replacementOrderId,\n skippedLineIds: result.skippedLineIds,\n })\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: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.replacement-order.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Create a replacement sales order from a warranty claim',\n methods: {\n POST: {\n summary: 'Create and link a replacement order from eligible replace-disposition claim lines',\n description: 'Zero pricing is the default. Original pricing copies the source order lines\\' pre-discount unit prices.',\n requestBody: { contentType: 'application/json', schema: replacementOrderRequestSchema },\n responses: [\n {\n status: 200,\n description: 'Replacement order created and linked; ineligible or unresolvable claim lines are reported as skipped',\n schema: replacementOrderResponseSchema,\n },\n { status: 400, description: 'Invalid request or claim is not eligible for replacement-order creation', schema: errorResponseSchema },\n { status: 401, description: 'Authentication required', schema: errorResponseSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorResponseSchema },\n { status: 409, description: 'Optimistic lock conflict', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,9 @@ 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";
|
|
9
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
11
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
10
12
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
@@ -40,22 +42,19 @@ const returnLabelResponseSchema = z.discriminatedUnion("status", [
|
|
|
40
42
|
const metadata = {
|
|
41
43
|
POST: { requireAuth: true, requireFeatures: ["warranty_claims.claim.manage"] }
|
|
42
44
|
};
|
|
43
|
-
function translateKey(key) {
|
|
44
|
-
return key;
|
|
45
|
-
}
|
|
46
45
|
function toRecord(value) {
|
|
47
46
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
48
47
|
}
|
|
49
|
-
async function resolveActionContext(req) {
|
|
48
|
+
async function resolveActionContext(req, translate) {
|
|
50
49
|
const container = await createRequestContainer();
|
|
51
50
|
const auth = await getAuthFromRequest(req);
|
|
52
51
|
if (!auth || !auth.tenantId) {
|
|
53
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
52
|
+
throw new CrudHttpError(401, { error: translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
54
53
|
}
|
|
55
54
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
56
55
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
57
56
|
if (!organizationId) {
|
|
58
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.organization_required" });
|
|
57
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.organization_required", "Organization context is required.") });
|
|
59
58
|
}
|
|
60
59
|
return {
|
|
61
60
|
ctx: {
|
|
@@ -68,7 +67,7 @@ async function resolveActionContext(req) {
|
|
|
68
67
|
},
|
|
69
68
|
tenantId: auth.tenantId,
|
|
70
69
|
organizationId,
|
|
71
|
-
translate
|
|
70
|
+
translate
|
|
72
71
|
};
|
|
73
72
|
}
|
|
74
73
|
function toReturnLabelRequest(payload) {
|
|
@@ -97,7 +96,7 @@ function toSetReturnLabelInput(input, context) {
|
|
|
97
96
|
async function runGuard(req, context, input) {
|
|
98
97
|
const userId = context.ctx.auth?.sub;
|
|
99
98
|
if (!userId) {
|
|
100
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
99
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
101
100
|
}
|
|
102
101
|
return runRouteMutationGuards({
|
|
103
102
|
container: context.ctx.container,
|
|
@@ -118,7 +117,7 @@ async function runGuard(req, context, input) {
|
|
|
118
117
|
async function loadClaimAndLines(context, claimId) {
|
|
119
118
|
const scope = { tenantId: context.tenantId, organizationId: context.organizationId };
|
|
120
119
|
const em = context.ctx.container.resolve("em").fork();
|
|
121
|
-
const claim = await requireScopedClaim(em, claimId, scope);
|
|
120
|
+
const claim = await requireScopedClaim(em, claimId, scope, {}, context.translate("warranty_claims.errors.notFound", "Claim not found."));
|
|
122
121
|
const lines = await findWithDecryption(
|
|
123
122
|
em,
|
|
124
123
|
WarrantyClaimLine,
|
|
@@ -128,17 +127,17 @@ async function loadClaimAndLines(context, claimId) {
|
|
|
128
127
|
);
|
|
129
128
|
return { claim, lines };
|
|
130
129
|
}
|
|
131
|
-
function assertGeneratableStatus(claim) {
|
|
130
|
+
function assertGeneratableStatus(claim, translate) {
|
|
132
131
|
if (claim.status !== "approved" && claim.status !== "awaiting_return") {
|
|
133
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.returnLabelInvalidStatus" });
|
|
132
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.returnLabelInvalidStatus", "Return labels can only be created for approved claims that are awaiting return.") });
|
|
134
133
|
}
|
|
135
134
|
}
|
|
136
|
-
async function executeSetReturnLabel(commandBus, input, ctx) {
|
|
135
|
+
async function executeSetReturnLabel(commandBus, input, ctx, translate) {
|
|
137
136
|
const { result } = await commandBus.execute(
|
|
138
137
|
"warranty_claims.claim.set_return_label",
|
|
139
138
|
{ input, ctx }
|
|
140
139
|
);
|
|
141
|
-
if (!result) throw new CrudHttpError(400, { error: "warranty_claims.errors.save_failed" });
|
|
140
|
+
if (!result) throw new CrudHttpError(400, { error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") });
|
|
142
141
|
return {
|
|
143
142
|
status: "created",
|
|
144
143
|
labelUrl: result.labelUrl,
|
|
@@ -153,8 +152,9 @@ function firstTranslatableIssueKey(err) {
|
|
|
153
152
|
return keyed ?? "warranty_claims.errors.invalidInput";
|
|
154
153
|
}
|
|
155
154
|
async function POST(req) {
|
|
155
|
+
const { translate } = await resolveTranslations();
|
|
156
156
|
try {
|
|
157
|
-
const context = await resolveActionContext(req);
|
|
157
|
+
const context = await resolveActionContext(req, translate);
|
|
158
158
|
const requestInput = toReturnLabelRequest(toRecord(await readJsonSafe(req, {})));
|
|
159
159
|
const guarded = await runGuard(req, context, requestInput);
|
|
160
160
|
if (!guarded.ok) {
|
|
@@ -166,13 +166,14 @@ async function POST(req) {
|
|
|
166
166
|
const response2 = await executeSetReturnLabel(
|
|
167
167
|
commandBus,
|
|
168
168
|
toSetReturnLabelInput(guardedInput, context),
|
|
169
|
-
context.ctx
|
|
169
|
+
context.ctx,
|
|
170
|
+
translate
|
|
170
171
|
);
|
|
171
172
|
await guarded.runAfterSuccess();
|
|
172
173
|
return NextResponse.json(response2);
|
|
173
174
|
}
|
|
174
175
|
const { claim, lines } = await loadClaimAndLines(context, guardedInput.claimId);
|
|
175
|
-
assertGeneratableStatus(claim);
|
|
176
|
+
assertGeneratableStatus(claim, translate);
|
|
176
177
|
await enforceWarrantyClaimOptimisticLock(
|
|
177
178
|
context.ctx,
|
|
178
179
|
claim,
|
|
@@ -196,17 +197,22 @@ async function POST(req) {
|
|
|
196
197
|
trackingNumber: result.trackingNumber,
|
|
197
198
|
carrier: result.carrier
|
|
198
199
|
}, context),
|
|
199
|
-
context.ctx
|
|
200
|
+
context.ctx,
|
|
201
|
+
translate
|
|
200
202
|
);
|
|
201
203
|
await guarded.runAfterSuccess();
|
|
202
204
|
return NextResponse.json(response);
|
|
203
205
|
} catch (err) {
|
|
204
206
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
207
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
208
|
+
if (interceptorRejection) {
|
|
209
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
210
|
+
}
|
|
205
211
|
if (err instanceof z.ZodError) {
|
|
206
|
-
return NextResponse.json({ error: firstTranslatableIssueKey(err) }, { status: 400 });
|
|
212
|
+
return NextResponse.json({ error: translate(firstTranslatableIssueKey(err), "Invalid input") }, { status: 400 });
|
|
207
213
|
}
|
|
208
214
|
logger.error("warranty_claims.return-label.post failed", { err });
|
|
209
|
-
return NextResponse.json({ error: "warranty_claims.errors.save_failed" }, { status: 500 });
|
|
215
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") }, { status: 500 });
|
|
210
216
|
}
|
|
211
217
|
}
|
|
212
218
|
const openApi = {
|