@open-mercato/core 0.7.1-develop.7123.1.25365e718d → 0.7.1-develop.7131.1.20abf9e885
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- 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/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/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +5 -117
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -3
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +33 -9
- package/dist/modules/customers/commands/deals.js.map +3 -3
- package/dist/modules/customers/components/AddressEditor.js +71 -1
- package/dist/modules/customers/components/AddressEditor.js.map +2 -2
- package/dist/modules/customers/utils/addressFormat.js +18 -2
- package/dist/modules/customers/utils/addressFormat.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/sales/components/documents/AddressesSection.js +17 -2
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +7 -24
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js +29 -0
- package/dist/modules/sales/components/documents/normalizeAddressDraft.js.map +7 -0
- 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 +5 -0
- 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 +5 -0
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +5 -0
- 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 +5 -0
- 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 +5 -0
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +5 -0
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +5 -0
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +5 -0
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +5 -0
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +5 -0
- 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/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- 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/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/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/customers/backend/customers/deals/[id]/hooks/types.ts +0 -23
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +11 -172
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -1
- package/src/modules/customers/commands/deals.ts +84 -9
- package/src/modules/customers/components/AddressEditor.tsx +116 -1
- package/src/modules/customers/i18n/de.json +6 -0
- package/src/modules/customers/i18n/en.json +6 -0
- package/src/modules/customers/i18n/es.json +6 -0
- package/src/modules/customers/i18n/ko.json +6 -0
- package/src/modules/customers/i18n/pl.json +6 -0
- package/src/modules/customers/utils/addressFormat.tsx +59 -1
- 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/sales/components/documents/AddressesSection.tsx +48 -5
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +5 -23
- package/src/modules/sales/components/documents/normalizeAddressDraft.ts +29 -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 +5 -0
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +5 -0
- package/src/modules/warranty_claims/api/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/claims/route.ts +5 -0
- package/src/modules/warranty_claims/api/portal/events/route.ts +5 -0
- package/src/modules/warranty_claims/api/receiving/route.ts +5 -0
- package/src/modules/warranty_claims/api/replacement-order/route.ts +5 -0
- package/src/modules/warranty_claims/api/return-label/route.ts +5 -0
- package/src/modules/warranty_claims/api/sales-return/route.ts +5 -0
- 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/vendor-recovery/route.ts +5 -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/portal/claims/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../../data/entities'\nimport {\n claimStatusSchema,\n portalIntakeInputSchema,\n type ClaimCreateInput,\n type PortalIntakeInput,\n} from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { portalClaimStatusesForStateGroup } from '../../../lib/listSegments'\nimport { buildPortalOwnedClaimWhere } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst logger = createLogger('warranty_claims')\n\nconst emptyQueryValueToUndefined = (value: unknown): unknown => {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nconst optionalTrimmedQueryString = (max: number) =>\n z.preprocess(emptyQueryValueToUndefined, z.string().trim().min(1).max(max).optional())\n\nconst listQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: optionalTrimmedQueryString(190),\n status: z.preprocess(emptyQueryValueToUndefined, claimStatusSchema.optional()),\n stateGroup: z.enum(['open', 'resolved']).optional(),\n serialNumber: optionalTrimmedQueryString(191),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\ntype LineSummary = {\n count: number\n creditTotal: string\n statuses: Record<string, number>\n}\n\ntype OrderValidationResult = {\n orderId: string | null\n currencyCode: string | null\n}\n\ntype PortalOrderLookup = {\n id: string\n currencyCode: string | null\n}\n\ntype PortalOrderLineLookup = {\n id: string\n orderId: string\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction stringField(record: Record<string, unknown>, key: string): string | null {\n const value = record[key]\n return typeof value === 'string' ? value : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nconst CREDIT_SCALE = 10_000n\n\nfunction decimalUnits(value: unknown): bigint {\n const normalized = typeof value === 'number'\n ? (Number.isFinite(value) ? value.toFixed(4) : '')\n : typeof value === 'string' ? value.trim() : ''\n const match = /^([+-]?)(\\d+)(?:\\.(\\d+))?$/.exec(normalized)\n if (!match) return 0n\n const [, sign, whole, fraction = ''] = match\n let units = BigInt(whole) * CREDIT_SCALE + BigInt(fraction.slice(0, 4).padEnd(4, '0'))\n if (fraction.length > 4 && fraction[4] >= '5') units += 1n\n return sign === '-' ? -units : units\n}\n\nfunction decimalString(value: bigint): string {\n const sign = value < 0n ? '-' : ''\n const absolute = value < 0n ? -value : value\n const whole = absolute / CREDIT_SCALE\n const fraction = String(absolute % CREDIT_SCALE).padStart(4, '0').replace(/0+$/, '')\n return `${sign}${whole}${fraction ? `.${fraction}` : ''}`\n}\n\nfunction summarizeLines(lines: WarrantyClaimLine[]): LineSummary {\n let creditTotal = 0n\n const statuses: Record<string, number> = {}\n for (const line of lines) {\n creditTotal += decimalUnits(line.creditAmount)\n statuses[line.lineStatus] = (statuses[line.lineStatus] ?? 0) + 1\n }\n return {\n count: lines.length,\n creditTotal: decimalString(creditTotal),\n statuses,\n }\n}\n\nfunction serializePortalClaim(claim: WarrantyClaim, lines: WarrantyClaimLine[]) {\n return {\n id: claim.id,\n claimNumber: claim.claimNumber,\n claimType: claim.claimType,\n status: claim.status,\n orderId: claim.orderId ?? null,\n orderNumber: claim.orderNumber ?? null,\n reasonCode: claim.reasonCode ?? null,\n resolutionSummary: claim.resolutionSummary ?? null,\n createdAt: toIso(claim.createdAt),\n updatedAt: toIso(claim.updatedAt),\n lines: summarizeLines(lines),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const { translate } = await resolveTranslations()\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.customerAccountNotLinked', 'Customer account is not linked to a customer record') }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\ntype PortalSalesDb = {\n sales_orders: {\n id: string\n currency_code: string\n customer_entity_id: string | null\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n sales_order_lines: {\n id: string\n order_id: string\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n}\n\nfunction isMissingSalesTableError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false\n const candidate = err as { code?: unknown; message?: unknown }\n return candidate.code === '42P01'\n || (typeof candidate.message === 'string'\n && candidate.message.includes('does not exist')\n && (candidate.message.includes('sales_orders') || candidate.message.includes('sales_order_lines')))\n}\n\nasync function loadOwnedOrders(\n context: PortalContext,\n orderIds: string[],\n): Promise<Map<string, PortalOrderLookup>> {\n if (orderIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_orders')\n .select(['id', 'currency_code'])\n .where('id', 'in', orderIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('customer_entity_id', '=', context.customerId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, currencyCode: row.currency_code ?? null }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function loadOwnedOrderLines(\n context: PortalContext,\n orderLineIds: string[],\n): Promise<Map<string, PortalOrderLineLookup>> {\n if (orderLineIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_order_lines')\n .select(['id', 'order_id'])\n .where('id', 'in', orderLineIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, orderId: row.order_id }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function validatePortalOrderOwnership(\n context: PortalContext,\n input: PortalIntakeInput,\n): Promise<OrderValidationResult | Response> {\n const { translate } = await resolveTranslations()\n const orderLineIds = Array.from(new Set(input.lines.flatMap((line) => line.orderLineId ? [line.orderLineId] : [])))\n const orderLinesById = await loadOwnedOrderLines(context, orderLineIds)\n const orderIds = new Set<string>()\n if (input.orderId) orderIds.add(input.orderId)\n for (const orderLine of orderLinesById.values()) orderIds.add(orderLine.orderId)\n const ordersById = await loadOwnedOrders(context, Array.from(orderIds))\n let resolvedOrder = input.orderId ? (ordersById.get(input.orderId) ?? null) : null\n if (input.orderId) {\n if (!resolvedOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderNotOwned', 'Order not found.') }, { status: 404 })\n }\n }\n\n for (const line of input.lines) {\n if (!line.orderLineId) continue\n const resolvedLine = orderLinesById.get(line.orderLineId)\n const lineOrder = resolvedLine ? ordersById.get(resolvedLine.orderId) : null\n if (!resolvedLine || !lineOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') }, { status: 404 })\n }\n if (resolvedOrder && lineOrder.id !== resolvedOrder.id) {\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') },\n { status: 400 },\n )\n }\n if (!resolvedOrder) resolvedOrder = lineOrder\n }\n\n return {\n orderId: resolvedOrder?.id ?? input.orderId ?? null,\n currencyCode: resolvedOrder?.currencyCode ?? null,\n }\n}\n\nasync function runPortalCreateGuard(\n req: Request,\n context: PortalContext,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: null,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const query = listQuerySchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n stateGroup: url.searchParams.get('stateGroup') ?? undefined,\n serialNumber: url.searchParams.get('serialNumber') ?? undefined,\n })\n if (!query.success) {\n const { translate } = await resolveTranslations()\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const { page, pageSize, search, status, stateGroup, serialNumber } = query.data\n const where = buildPortalOwnedClaimWhere(context)\n if (status) where.status = status\n else if (stateGroup) where.status = { $in: portalClaimStatusesForStateGroup(stateGroup) }\n if (search) {\n const pattern = `%${escapeLikePattern(search)}%`\n where.$or = [\n { claimNumber: { $ilike: pattern } },\n { orderNumber: { $ilike: pattern } },\n ]\n }\n if (serialNumber) {\n const matchingLines = await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n {\n serialNumber,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n deletedAt: null,\n claim: buildPortalOwnedClaimWhere(context),\n },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const matchingClaimIds = Array.from(new Set(\n matchingLines.map((line) => relationId(line.claim)).filter((id): id is string => Boolean(id)),\n ))\n if (matchingClaimIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 1 })\n }\n where.id = { $in: matchingClaimIds }\n }\n const total = await context.em.count(WarrantyClaim, where)\n const claims = await findWithDecryption(\n context.em,\n WarrantyClaim,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const claimIds = claims.map((claim) => claim.id)\n const lines = claimIds.length\n ? await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n { claim: { $in: claimIds }, tenantId: context.tenantId, organizationId: context.organizationId, deletedAt: null },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n : []\n const linesByClaim = new Map<string, WarrantyClaimLine[]>()\n for (const line of lines) {\n const claimId = relationId(line.claim)\n if (!claimId) continue\n const bucket = linesByClaim.get(claimId) ?? []\n bucket.push(line)\n linesByClaim.set(claimId, bucket)\n }\n return NextResponse.json({\n items: claims.map((claim) => serializePortalClaim(claim, linesByClaim.get(claim.id) ?? [])),\n total,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(total / pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const parsed = portalIntakeInputSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const ownership = await validatePortalOrderOwnership(context, parsed.data)\n if (ownership instanceof Response) return ownership\n\n const createInput: ClaimCreateInput = {\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n claimType: 'warranty',\n channel: 'portal',\n priority: 'normal',\n customerId: context.customerId,\n customerName: context.auth.displayName || null,\n orderId: ownership.orderId,\n // A resolved order supplies its own number; otherwise persist the typed reference (WQA-002).\n orderNumber: ownership.orderId ? null : (parsed.data.orderReference ?? null),\n reasonCode: parsed.data.reasonCode,\n notes: parsed.data.notes ?? null,\n currencyCode: ownership.currencyCode,\n lines: parsed.data.lines.map((line, index) => ({\n lineNo: index + 1,\n orderLineId: line.orderLineId ?? null,\n productId: line.productId ?? null,\n productName: line.productName ?? null,\n sku: line.sku ?? null,\n serialNumber: line.serialNumber ?? null,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription,\n qtyClaimed: line.qtyClaimed ?? 1,\n // Carry warranty metadata so order-backed portal lines keep their entitlement basis\n // instead of persisting warrantyStatus='unknown' (WQA-004 / LINE-06).\n purchaseDate: line.purchaseDate ?? null,\n warrantyMonths: line.warrantyMonths ?? null,\n })),\n }\n\n const guarded = await runPortalCreateGuard(req, context, { ...createInput })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: ClaimCreateInput = guarded.modifiedPayload\n ? {\n ...createInput,\n ...guarded.modifiedPayload,\n tenantId: createInput.tenantId,\n organizationId: createInput.organizationId,\n customerId: createInput.customerId,\n channel: createInput.channel,\n claimType: createInput.claimType,\n orderId: createInput.orderId,\n orderNumber: createInput.orderNumber,\n currencyCode: createInput.currencyCode,\n lines: createInput.lines,\n }\n : createInput\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n const createResult = await commandBus.execute<ClaimCreateInput, { claimId: string }>(\n 'warranty_claims.claim.create',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n const claimId = createResult.result?.claimId\n if (typeof claimId !== 'string') {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claimId, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (submitError) {\n try {\n await commandBus.execute<{ id: string }, { claimId: string }>(\n 'warranty_claims.claim.delete',\n { input: { id: claimId }, ctx: context.commandCtx },\n )\n } catch (compensationError) {\n logger.error('warranty_claims.portal.claim.submit compensation failed \u2014 orphaned draft claim', {\n err: compensationError,\n claimId,\n customerId: context.customerId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n }\n throw submitError\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId }, { status: 201 })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n}\n\nconst lineSummarySchema = z.object({\n count: z.number().int().nonnegative(),\n creditTotal: z.string(),\n statuses: z.record(z.string(), z.number().int().nonnegative()),\n})\n\nconst portalClaimSchema = z.object({\n id: z.string().uuid(),\n claimNumber: z.string(),\n claimType: z.string(),\n status: z.string(),\n orderId: z.string().uuid().nullable(),\n orderNumber: z.string().nullable(),\n reasonCode: z.string().nullable(),\n resolutionSummary: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n lines: lineSummarySchema,\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claims',\n methods: {\n GET: {\n summary: 'List the authenticated customer account claims',\n query: listQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer claims',\n schema: z.object({\n items: z.array(portalClaimSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n POST: {\n summary: 'Submit a customer portal claim intake',\n requestBody: { contentType: 'application/json', schema: portalIntakeInputSchema },\n responses: [\n {\n status: 201,\n description: 'Claim created and submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../../data/entities'\nimport {\n claimStatusSchema,\n portalIntakeInputSchema,\n type ClaimCreateInput,\n type PortalIntakeInput,\n} from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { portalClaimStatusesForStateGroup } from '../../../lib/listSegments'\nimport { buildPortalOwnedClaimWhere } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst logger = createLogger('warranty_claims')\n\nconst emptyQueryValueToUndefined = (value: unknown): unknown => {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length ? trimmed : undefined\n}\n\nconst optionalTrimmedQueryString = (max: number) =>\n z.preprocess(emptyQueryValueToUndefined, z.string().trim().min(1).max(max).optional())\n\nconst listQuerySchema = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: optionalTrimmedQueryString(190),\n status: z.preprocess(emptyQueryValueToUndefined, claimStatusSchema.optional()),\n stateGroup: z.enum(['open', 'resolved']).optional(),\n serialNumber: optionalTrimmedQueryString(191),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\ntype LineSummary = {\n count: number\n creditTotal: string\n statuses: Record<string, number>\n}\n\ntype OrderValidationResult = {\n orderId: string | null\n currencyCode: string | null\n}\n\ntype PortalOrderLookup = {\n id: string\n currencyCode: string | null\n}\n\ntype PortalOrderLineLookup = {\n id: string\n orderId: string\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction stringField(record: Record<string, unknown>, key: string): string | null {\n const value = record[key]\n return typeof value === 'string' ? value : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nconst CREDIT_SCALE = 10_000n\n\nfunction decimalUnits(value: unknown): bigint {\n const normalized = typeof value === 'number'\n ? (Number.isFinite(value) ? value.toFixed(4) : '')\n : typeof value === 'string' ? value.trim() : ''\n const match = /^([+-]?)(\\d+)(?:\\.(\\d+))?$/.exec(normalized)\n if (!match) return 0n\n const [, sign, whole, fraction = ''] = match\n let units = BigInt(whole) * CREDIT_SCALE + BigInt(fraction.slice(0, 4).padEnd(4, '0'))\n if (fraction.length > 4 && fraction[4] >= '5') units += 1n\n return sign === '-' ? -units : units\n}\n\nfunction decimalString(value: bigint): string {\n const sign = value < 0n ? '-' : ''\n const absolute = value < 0n ? -value : value\n const whole = absolute / CREDIT_SCALE\n const fraction = String(absolute % CREDIT_SCALE).padStart(4, '0').replace(/0+$/, '')\n return `${sign}${whole}${fraction ? `.${fraction}` : ''}`\n}\n\nfunction summarizeLines(lines: WarrantyClaimLine[]): LineSummary {\n let creditTotal = 0n\n const statuses: Record<string, number> = {}\n for (const line of lines) {\n creditTotal += decimalUnits(line.creditAmount)\n statuses[line.lineStatus] = (statuses[line.lineStatus] ?? 0) + 1\n }\n return {\n count: lines.length,\n creditTotal: decimalString(creditTotal),\n statuses,\n }\n}\n\nfunction serializePortalClaim(claim: WarrantyClaim, lines: WarrantyClaimLine[]) {\n return {\n id: claim.id,\n claimNumber: claim.claimNumber,\n claimType: claim.claimType,\n status: claim.status,\n orderId: claim.orderId ?? null,\n orderNumber: claim.orderNumber ?? null,\n reasonCode: claim.reasonCode ?? null,\n resolutionSummary: claim.resolutionSummary ?? null,\n createdAt: toIso(claim.createdAt),\n updatedAt: toIso(claim.updatedAt),\n lines: summarizeLines(lines),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const { translate } = await resolveTranslations()\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.customerAccountNotLinked', 'Customer account is not linked to a customer record') }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\ntype PortalSalesDb = {\n sales_orders: {\n id: string\n currency_code: string\n customer_entity_id: string | null\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n sales_order_lines: {\n id: string\n order_id: string\n tenant_id: string | null\n organization_id: string | null\n deleted_at: Date | null\n }\n}\n\nfunction isMissingSalesTableError(err: unknown): boolean {\n if (typeof err !== 'object' || err === null) return false\n const candidate = err as { code?: unknown; message?: unknown }\n return candidate.code === '42P01'\n || (typeof candidate.message === 'string'\n && candidate.message.includes('does not exist')\n && (candidate.message.includes('sales_orders') || candidate.message.includes('sales_order_lines')))\n}\n\nasync function loadOwnedOrders(\n context: PortalContext,\n orderIds: string[],\n): Promise<Map<string, PortalOrderLookup>> {\n if (orderIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_orders')\n .select(['id', 'currency_code'])\n .where('id', 'in', orderIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('customer_entity_id', '=', context.customerId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, currencyCode: row.currency_code ?? null }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function loadOwnedOrderLines(\n context: PortalContext,\n orderLineIds: string[],\n): Promise<Map<string, PortalOrderLineLookup>> {\n if (orderLineIds.length === 0) return new Map()\n try {\n const db = context.em.fork().getKysely<PortalSalesDb>()\n const rows = await db\n .selectFrom('sales_order_lines')\n .select(['id', 'order_id'])\n .where('id', 'in', orderLineIds)\n .where('tenant_id', '=', context.tenantId)\n .where('organization_id', '=', context.organizationId)\n .where('deleted_at', 'is', null)\n .execute()\n return new Map(rows.map((row) => [row.id, { id: row.id, orderId: row.order_id }]))\n } catch (err) {\n if (isMissingSalesTableError(err)) return new Map()\n throw err\n }\n}\n\nasync function validatePortalOrderOwnership(\n context: PortalContext,\n input: PortalIntakeInput,\n): Promise<OrderValidationResult | Response> {\n const { translate } = await resolveTranslations()\n const orderLineIds = Array.from(new Set(input.lines.flatMap((line) => line.orderLineId ? [line.orderLineId] : [])))\n const orderLinesById = await loadOwnedOrderLines(context, orderLineIds)\n const orderIds = new Set<string>()\n if (input.orderId) orderIds.add(input.orderId)\n for (const orderLine of orderLinesById.values()) orderIds.add(orderLine.orderId)\n const ordersById = await loadOwnedOrders(context, Array.from(orderIds))\n let resolvedOrder = input.orderId ? (ordersById.get(input.orderId) ?? null) : null\n if (input.orderId) {\n if (!resolvedOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderNotOwned', 'Order not found.') }, { status: 404 })\n }\n }\n\n for (const line of input.lines) {\n if (!line.orderLineId) continue\n const resolvedLine = orderLinesById.get(line.orderLineId)\n const lineOrder = resolvedLine ? ordersById.get(resolvedLine.orderId) : null\n if (!resolvedLine || !lineOrder) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') }, { status: 404 })\n }\n if (resolvedOrder && lineOrder.id !== resolvedOrder.id) {\n return NextResponse.json(\n { ok: false, error: translate('warranty_claims.errors.orderLineMismatch', 'Order line does not belong to the selected order') },\n { status: 400 },\n )\n }\n if (!resolvedOrder) resolvedOrder = lineOrder\n }\n\n return {\n orderId: resolvedOrder?.id ?? input.orderId ?? null,\n currencyCode: resolvedOrder?.currencyCode ?? null,\n }\n}\n\nasync function runPortalCreateGuard(\n req: Request,\n context: PortalContext,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: null,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const query = listQuerySchema.safeParse({\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n search: url.searchParams.get('search') ?? undefined,\n status: url.searchParams.get('status') ?? undefined,\n stateGroup: url.searchParams.get('stateGroup') ?? undefined,\n serialNumber: url.searchParams.get('serialNumber') ?? undefined,\n })\n if (!query.success) {\n const { translate } = await resolveTranslations()\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const { page, pageSize, search, status, stateGroup, serialNumber } = query.data\n const where = buildPortalOwnedClaimWhere(context)\n if (status) where.status = status\n else if (stateGroup) where.status = { $in: portalClaimStatusesForStateGroup(stateGroup) }\n if (search) {\n const pattern = `%${escapeLikePattern(search)}%`\n where.$or = [\n { claimNumber: { $ilike: pattern } },\n { orderNumber: { $ilike: pattern } },\n ]\n }\n if (serialNumber) {\n const matchingLines = await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n {\n serialNumber,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n deletedAt: null,\n claim: buildPortalOwnedClaimWhere(context),\n },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const matchingClaimIds = Array.from(new Set(\n matchingLines.map((line) => relationId(line.claim)).filter((id): id is string => Boolean(id)),\n ))\n if (matchingClaimIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 1 })\n }\n where.id = { $in: matchingClaimIds }\n }\n const total = await context.em.count(WarrantyClaim, where)\n const claims = await findWithDecryption(\n context.em,\n WarrantyClaim,\n where,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const claimIds = claims.map((claim) => claim.id)\n const lines = claimIds.length\n ? await findWithDecryption(\n context.em,\n WarrantyClaimLine,\n { claim: { $in: claimIds }, tenantId: context.tenantId, organizationId: context.organizationId, deletedAt: null },\n {},\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n : []\n const linesByClaim = new Map<string, WarrantyClaimLine[]>()\n for (const line of lines) {\n const claimId = relationId(line.claim)\n if (!claimId) continue\n const bucket = linesByClaim.get(claimId) ?? []\n bucket.push(line)\n linesByClaim.set(claimId, bucket)\n }\n return NextResponse.json({\n items: claims.map((claim) => serializePortalClaim(claim, linesByClaim.get(claim.id) ?? [])),\n total,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(total / pageSize)),\n })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const parsed = portalIntakeInputSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const ownership = await validatePortalOrderOwnership(context, parsed.data)\n if (ownership instanceof Response) return ownership\n\n const createInput: ClaimCreateInput = {\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n claimType: 'warranty',\n channel: 'portal',\n priority: 'normal',\n customerId: context.customerId,\n customerName: context.auth.displayName || null,\n orderId: ownership.orderId,\n // A resolved order supplies its own number; otherwise persist the typed reference (WQA-002).\n orderNumber: ownership.orderId ? null : (parsed.data.orderReference ?? null),\n reasonCode: parsed.data.reasonCode,\n notes: parsed.data.notes ?? null,\n currencyCode: ownership.currencyCode,\n lines: parsed.data.lines.map((line, index) => ({\n lineNo: index + 1,\n orderLineId: line.orderLineId ?? null,\n productId: line.productId ?? null,\n productName: line.productName ?? null,\n sku: line.sku ?? null,\n serialNumber: line.serialNumber ?? null,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription,\n qtyClaimed: line.qtyClaimed ?? 1,\n // Carry warranty metadata so order-backed portal lines keep their entitlement basis\n // instead of persisting warrantyStatus='unknown' (WQA-004 / LINE-06).\n purchaseDate: line.purchaseDate ?? null,\n warrantyMonths: line.warrantyMonths ?? null,\n })),\n }\n\n const guarded = await runPortalCreateGuard(req, context, { ...createInput })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: ClaimCreateInput = guarded.modifiedPayload\n ? {\n ...createInput,\n ...guarded.modifiedPayload,\n tenantId: createInput.tenantId,\n organizationId: createInput.organizationId,\n customerId: createInput.customerId,\n channel: createInput.channel,\n claimType: createInput.claimType,\n orderId: createInput.orderId,\n orderNumber: createInput.orderNumber,\n currencyCode: createInput.currencyCode,\n lines: createInput.lines,\n }\n : createInput\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n const createResult = await commandBus.execute<ClaimCreateInput, { claimId: string }>(\n 'warranty_claims.claim.create',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n const claimId = createResult.result?.claimId\n if (typeof claimId !== 'string') {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n try {\n await commandBus.execute<{ id: string; actorCustomerId: string }, { claimId: string }>(\n 'warranty_claims.claim.submit',\n { input: { id: claimId, actorCustomerId: context.customerId }, ctx: context.commandCtx },\n )\n } catch (submitError) {\n try {\n await commandBus.execute<{ id: string }, { claimId: string }>(\n 'warranty_claims.claim.delete',\n { input: { id: claimId }, ctx: context.commandCtx },\n )\n } catch (compensationError) {\n logger.error('warranty_claims.portal.claim.submit compensation failed \u2014 orphaned draft claim', {\n err: compensationError,\n claimId,\n customerId: context.customerId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n }\n throw submitError\n }\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId }, { status: 201 })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n}\n\nconst lineSummarySchema = z.object({\n count: z.number().int().nonnegative(),\n creditTotal: z.string(),\n statuses: z.record(z.string(), z.number().int().nonnegative()),\n})\n\nconst portalClaimSchema = z.object({\n id: z.string().uuid(),\n claimNumber: z.string(),\n claimType: z.string(),\n status: z.string(),\n orderId: z.string().uuid().nullable(),\n orderNumber: z.string().nullable(),\n reasonCode: z.string().nullable(),\n resolutionSummary: z.string().nullable(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n lines: lineSummarySchema,\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claims',\n methods: {\n GET: {\n summary: 'List the authenticated customer account claims',\n query: listQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer claims',\n schema: z.object({\n items: z.array(portalClaimSchema),\n total: z.number().int().nonnegative(),\n page: z.number().int().min(1),\n pageSize: z.number().int().min(1),\n totalPages: z.number().int().min(1),\n }),\n },\n ],\n },\n POST: {\n summary: 'Submit a customer portal claim intake',\n requestBody: { contentType: 'application/json', schema: portalIntakeInputSchema },\n responses: [\n {\n status: 201,\n description: 'Claim created and submitted',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
5
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
5
6
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
6
7
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
7
8
|
import { getCustomerAuthFromRequest } from "@open-mercato/core/modules/customer_accounts/lib/customerAuth";
|
|
@@ -177,6 +178,10 @@ async function POST(req) {
|
|
|
177
178
|
if (isCrudHttpError(err)) {
|
|
178
179
|
return NextResponse.json(err.body, { status: err.status });
|
|
179
180
|
}
|
|
181
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
182
|
+
if (interceptorRejection) {
|
|
183
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
184
|
+
}
|
|
180
185
|
throw err;
|
|
181
186
|
}
|
|
182
187
|
return NextResponse.json({ ok: true, claimId: claim.id });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/warranty_claims/api/portal/events/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { WarrantyClaim, WarrantyClaimEvent } from '../../../data/entities'\nimport type { CommentClaimInput } from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst eventQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\nconst portalCommentSchema = z\n .object({\n claimId: z.string().uuid(),\n body: z.string().trim().min(1).max(8000),\n })\n .strict()\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.unauthorized' }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.customerAccountNotLinked' }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalCommentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const parsed = eventQuerySchema.safeParse({ claimId: url.searchParams.get('claimId') ?? undefined })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, visibility: 'customer', tenantId: context.tenantId, organizationId: context.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const parsed = portalCommentSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const input: CommentClaimInput = {\n claimId: claim.id,\n body: parsed.data.body,\n visibility: 'customer',\n actorCustomerId: context.customerId,\n }\n const guarded = await runPortalCommentGuard(req, context, claim.id, { ...input })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: CommentClaimInput = guarded.modifiedPayload\n ? {\n ...input,\n ...guarded.modifiedPayload,\n claimId: input.claimId,\n visibility: input.visibility,\n actorCustomerId: input.actorCustomerId,\n }\n : input\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n await guarded.runAfterSuccess()\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n\n return NextResponse.json({ ok: true, claimId: claim.id })\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.literal('customer'),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim timeline',\n methods: {\n GET: {\n summary: 'List customer-visible timeline events for an owned claim',\n query: eventQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer-visible timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a customer-visible customer comment',\n requestBody: { contentType: 'application/json', schema: portalCommentSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getCustomerAuthFromRequest, type CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { WarrantyClaim, WarrantyClaimEvent } from '../../../data/entities'\nimport type { CommentClaimInput } from '../../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n}\n\nconst eventQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\nconst portalCommentSchema = z\n .object({\n claimId: z.string().uuid(),\n body: z.string().trim().min(1).max(8000),\n })\n .strict()\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n commandCtx: CommandRuntimeContext\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const auth = await getCustomerAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.unauthorized' }, { status: 401 })\n }\n if (!auth.customerEntityId) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.customerAccountNotLinked' }, { status: 403 })\n }\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const commandAuth: NonNullable<AuthContext> = {\n sub: auth.sub,\n sid: auth.sid,\n tenantId: auth.tenantId,\n orgId: auth.orgId,\n email: auth.email,\n customerEntityId: auth.customerEntityId ?? null,\n personEntityId: auth.personEntityId ?? null,\n }\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n commandCtx: {\n container,\n auth: commandAuth,\n organizationScope: null,\n selectedOrganizationId: auth.orgId,\n organizationIds: [auth.orgId],\n request: req,\n },\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalCommentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.commandCtx.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation: 'create',\n mutationPayload,\n },\n })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const url = new URL(req.url)\n const parsed = eventQuerySchema.safeParse({ claimId: url.searchParams.get('claimId') ?? undefined })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, visibility: 'customer', tenantId: context.tenantId, organizationId: context.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n}\n\nexport async function POST(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const parsed = portalCommentSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: 'warranty_claims.errors.notFound' }, { status: 404 })\n }\n const input: CommentClaimInput = {\n claimId: claim.id,\n body: parsed.data.body,\n visibility: 'customer',\n actorCustomerId: context.customerId,\n }\n const guarded = await runPortalCommentGuard(req, context, claim.id, { ...input })\n if (!guarded.ok) {\n return guarded.response\n }\n const effectiveInput: CommentClaimInput = guarded.modifiedPayload\n ? {\n ...input,\n ...guarded.modifiedPayload,\n claimId: input.claimId,\n visibility: input.visibility,\n actorCustomerId: input.actorCustomerId,\n }\n : input\n\n const commandBus = context.commandCtx.container.resolve('commandBus') as CommandBus\n try {\n await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: effectiveInput, ctx: context.commandCtx },\n )\n await guarded.runAfterSuccess()\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n throw err\n }\n\n return NextResponse.json({ ok: true, claimId: claim.id })\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.literal('customer'),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim timeline',\n methods: {\n GET: {\n summary: 'List customer-visible timeline events for an owned claim',\n query: eventQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Customer-visible timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a customer-visible customer comment',\n requestBody: { contentType: 'application/json', schema: portalCommentSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
9
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
10
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
@@ -140,6 +141,10 @@ async function POST(req) {
|
|
|
140
141
|
return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId });
|
|
141
142
|
} catch (err) {
|
|
142
143
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
144
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
145
|
+
if (interceptorRejection) {
|
|
146
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
147
|
+
}
|
|
143
148
|
if (err instanceof z.ZodError) {
|
|
144
149
|
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
145
150
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/receiving/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n claimConditionGradeSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_LINE_RESOURCE_KIND } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\ntype ActionRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n}\n\ntype ReceivingAction =\n | { kind: 'receive'; input: ClaimLineReceiveInput }\n | { kind: 'release'; input: ClaimLineReleaseQuarantineInput }\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst receiveBodySchema = z\n .object({\n lineId: uuid,\n conditionGrade: claimConditionGradeSchema,\n inspectionNotes: z.string().trim().max(4000).optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst releaseBodySchema = z\n .object({\n lineId: uuid,\n action: z.literal('release'),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst receivingBodySchema = z.union([releaseBodySchema, receiveBodySchema])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.receiving.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction translateKey(key: string): string {\n return key\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReceivingAction(payload: Record<string, unknown>, context: ActionRouteContext): ReceivingAction {\n const releaseBody = releaseBodySchema.safeParse(payload)\n if (releaseBody.success) {\n const scopedPayload = withScopedPayload({\n id: releaseBody.data.lineId,\n updatedAt: releaseBody.data.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'release', input: claimLineReleaseQuarantineSchema.parse(scopedPayload) }\n }\n const body = receiveBodySchema.parse(payload)\n const scopedPayload = withScopedPayload({\n id: body.lineId,\n conditionGrade: body.conditionGrade,\n inspectionNotes: body.inspectionNotes,\n updatedAt: body.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'receive', input: claimLineReceiveSchema.parse(scopedPayload) }\n}\n\nfunction toGuardedAction(kind: ReceivingAction['kind'], payload: Record<string, unknown>): ReceivingAction {\n if (kind === 'release') {\n return { kind, input: claimLineReleaseQuarantineSchema.parse(payload) }\n }\n return { kind, input: claimLineReceiveSchema.parse(payload) }\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n action: ReceivingAction,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId: action.input.id,\n operation: 'custom',\n mutationPayload: { ...action.input },\n },\n })\n}\n\nasync function executeReceivingAction(\n commandBus: CommandBus,\n action: ReceivingAction,\n ctx: CommandRuntimeContext,\n): Promise<{ lineId: string; claimId: string }> {\n if (action.kind === 'release') {\n const { result } = await commandBus.execute<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.release_quarantine',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n }\n const { result } = await commandBus.execute<ClaimLineReceiveInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.receive',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const action = toReceivingAction(payload, context)\n const guarded = await runGuard(req, context, action)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandAction = guarded.modifiedPayload ? toGuardedAction(action.kind, guarded.modifiedPayload) : action\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const result = await executeReceivingAction(commandBus, commandAction, context.ctx)\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.receiving.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Receive or release a warranty claim line',\n methods: {\n POST: {\n summary: 'Grade a received line or release its quarantine hold',\n requestBody: { contentType: 'application/json', schema: receivingBodySchema },\n responses: [\n {\n status: 200,\n description: 'Receiving action applied',\n schema: z.object({\n ok: z.boolean(),\n lineId: z.string().uuid(),\n claimId: z.string().uuid(),\n }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n claimConditionGradeSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_LINE_RESOURCE_KIND } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\ntype ActionRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n translate: (key: string, fallback?: string) => string\n}\n\ntype ReceivingAction =\n | { kind: 'receive'; input: ClaimLineReceiveInput }\n | { kind: 'release'; input: ClaimLineReleaseQuarantineInput }\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst receiveBodySchema = z\n .object({\n lineId: uuid,\n conditionGrade: claimConditionGradeSchema,\n inspectionNotes: z.string().trim().max(4000).optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst releaseBodySchema = z\n .object({\n lineId: uuid,\n action: z.literal('release'),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\nconst receivingBodySchema = z.union([releaseBodySchema, receiveBodySchema])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.receiving.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction translateKey(key: string): string {\n return key\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReceivingAction(payload: Record<string, unknown>, context: ActionRouteContext): ReceivingAction {\n const releaseBody = releaseBodySchema.safeParse(payload)\n if (releaseBody.success) {\n const scopedPayload = withScopedPayload({\n id: releaseBody.data.lineId,\n updatedAt: releaseBody.data.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'release', input: claimLineReleaseQuarantineSchema.parse(scopedPayload) }\n }\n const body = receiveBodySchema.parse(payload)\n const scopedPayload = withScopedPayload({\n id: body.lineId,\n conditionGrade: body.conditionGrade,\n inspectionNotes: body.inspectionNotes,\n updatedAt: body.updatedAt,\n }, context.ctx, context.translate)\n return { kind: 'receive', input: claimLineReceiveSchema.parse(scopedPayload) }\n}\n\nfunction toGuardedAction(kind: ReceivingAction['kind'], payload: Record<string, unknown>): ReceivingAction {\n if (kind === 'release') {\n return { kind, input: claimLineReleaseQuarantineSchema.parse(payload) }\n }\n return { kind, input: claimLineReceiveSchema.parse(payload) }\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n action: ReceivingAction,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId: action.input.id,\n operation: 'custom',\n mutationPayload: { ...action.input },\n },\n })\n}\n\nasync function executeReceivingAction(\n commandBus: CommandBus,\n action: ReceivingAction,\n ctx: CommandRuntimeContext,\n): Promise<{ lineId: string; claimId: string }> {\n if (action.kind === 'release') {\n const { result } = await commandBus.execute<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.release_quarantine',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n }\n const { result } = await commandBus.execute<ClaimLineReceiveInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.receive',\n { input: action.input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return result\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const action = toReceivingAction(payload, context)\n const guarded = await runGuard(req, context, action)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandAction = guarded.modifiedPayload ? toGuardedAction(action.kind, guarded.modifiedPayload) : action\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const result = await executeReceivingAction(commandBus, commandAction, context.ctx)\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, lineId: result.lineId, claimId: result.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.receiving.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Receive or release a warranty claim line',\n methods: {\n POST: {\n summary: 'Grade a received line or release its quarantine hold',\n requestBody: { contentType: 'application/json', schema: receivingBodySchema },\n responses: [\n {\n status: 200,\n description: 'Receiving action applied',\n schema: z.object({\n ok: z.boolean(),\n lineId: z.string().uuid(),\n claimId: z.string().uuid(),\n }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["scopedPayload", "result"]
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
9
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
10
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
@@ -113,6 +114,10 @@ async function POST(req) {
|
|
|
113
114
|
});
|
|
114
115
|
} catch (err) {
|
|
115
116
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
117
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
118
|
+
if (interceptorRejection) {
|
|
119
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
120
|
+
}
|
|
116
121
|
if (err instanceof z.ZodError) {
|
|
117
122
|
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
118
123
|
}
|
|
@@ -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 translateKey(key: string): string {\n return key\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(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction 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: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\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: 'warranty_claims.errors.save_failed' })\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 if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.replacement-order.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: '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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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n 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 translateKey(key: string): string {\n return key\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(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction 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: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\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: 'warranty_claims.errors.save_failed' })\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: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.replacement-order.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: '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,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
9
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
10
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
@@ -202,6 +203,10 @@ async function POST(req) {
|
|
|
202
203
|
return NextResponse.json(response);
|
|
203
204
|
} catch (err) {
|
|
204
205
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
206
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
207
|
+
if (interceptorRejection) {
|
|
208
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
209
|
+
}
|
|
205
210
|
if (err instanceof z.ZodError) {
|
|
206
211
|
return NextResponse.json({ error: firstTranslatableIssueKey(err) }, { status: 400 });
|
|
207
212
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/return-label/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { 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 { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../data/entities'\nimport { claimSetReturnLabelSchema, type ClaimSetReturnLabelInput } from '../../data/validators'\nimport type { WarrantyReturnLabelProvider } from '../../services/returnLabelProvider'\nimport {\n WARRANTY_CLAIM_RESOURCE_KIND,\n enforceWarrantyClaimOptimisticLock,\n requireScopedClaim,\n} 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 ReturnLabelCreatedResponse = {\n status: 'created'\n labelUrl: string | null\n trackingNumber: string | null\n carrier: string | null\n}\ntype ReturnLabelCommandResult = Omit<ReturnLabelCreatedResponse, 'status'> & { claimId: string }\n\nconst uuid = z.string().uuid()\nconst returnLabelText = (max: number) => z.string().trim().min(1).max(max).optional()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst returnLabelRequestSchema = z\n .object({\n claimId: uuid,\n manual: z.boolean().optional(),\n labelUrl: returnLabelText(2048),\n trackingNumber: returnLabelText(191),\n carrier: returnLabelText(120),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\ntype ReturnLabelRequest = z.infer<typeof returnLabelRequestSchema>\n\nconst returnLabelResponseSchema = z.discriminatedUnion('status', [\n z.object({\n status: z.literal('created'),\n labelUrl: z.string().nullable(),\n trackingNumber: z.string().nullable(),\n carrier: z.string().nullable(),\n }),\n z.object({ status: z.literal('notConfigured') }),\n])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction translateKey(key: string): string {\n return key\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(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReturnLabelRequest(payload: Record<string, unknown>): ReturnLabelRequest {\n return returnLabelRequestSchema.parse({\n claimId: payload.claimId ?? payload.id,\n manual: payload.manual,\n labelUrl: payload.labelUrl,\n trackingNumber: payload.trackingNumber,\n carrier: payload.carrier,\n updatedAt: payload.updatedAt,\n })\n}\n\nfunction isManualRequest(input: ReturnLabelRequest): boolean {\n return input.manual === true\n || input.labelUrl !== undefined\n || input.trackingNumber !== undefined\n || input.carrier !== undefined\n}\n\nfunction toSetReturnLabelInput(\n input: ReturnLabelRequest,\n context: ActionRouteContext,\n): ClaimSetReturnLabelInput {\n const scopedPayload = withScopedPayload({\n id: input.claimId,\n labelUrl: input.labelUrl,\n trackingNumber: input.trackingNumber,\n carrier: input.carrier,\n updatedAt: input.updatedAt,\n }, context.ctx, context.translate)\n return claimSetReturnLabelSchema.parse(scopedPayload)\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: ReturnLabelRequest,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nasync function loadClaimAndLines(\n context: ActionRouteContext,\n claimId: string,\n): Promise<{ claim: WarrantyClaim; lines: WarrantyClaimLine[] }> {\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n const em = (context.ctx.container.resolve('em') as EntityManager).fork()\n const claim = await requireScopedClaim(em, claimId, scope)\n const lines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n { claim: claim.id, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n {},\n scope,\n )\n return { claim, lines }\n}\n\nfunction assertGeneratableStatus(claim: WarrantyClaim): void {\n if (claim.status !== 'approved' && claim.status !== 'awaiting_return') {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.returnLabelInvalidStatus' })\n }\n}\n\nasync function executeSetReturnLabel(\n commandBus: CommandBus,\n input: ClaimSetReturnLabelInput,\n ctx: CommandRuntimeContext,\n): Promise<ReturnLabelCreatedResponse> {\n const { result } = await commandBus.execute<ClaimSetReturnLabelInput, ReturnLabelCommandResult>(\n 'warranty_claims.claim.set_return_label',\n { input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return {\n status: 'created',\n labelUrl: result.labelUrl,\n trackingNumber: result.trackingNumber,\n carrier: result.carrier,\n }\n}\n\nexport function firstTranslatableIssueKey(err: z.ZodError): string {\n const keyed = err.issues.find(\n (issue) => typeof issue.message === 'string' && issue.message.startsWith('warranty_claims.'),\n )?.message\n return keyed ?? 'warranty_claims.errors.invalidInput'\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const requestInput = toReturnLabelRequest(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 ? toReturnLabelRequest(guarded.modifiedPayload) : requestInput\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n\n if (isManualRequest(guardedInput)) {\n const response = await executeSetReturnLabel(\n commandBus,\n toSetReturnLabelInput(guardedInput, context),\n context.ctx,\n )\n await guarded.runAfterSuccess()\n return NextResponse.json(response)\n }\n\n const { claim, lines } = await loadClaimAndLines(context, guardedInput.claimId)\n assertGeneratableStatus(claim)\n await enforceWarrantyClaimOptimisticLock(\n context.ctx,\n claim,\n WARRANTY_CLAIM_RESOURCE_KIND,\n guardedInput.updatedAt ?? undefined,\n )\n\n const provider = context.ctx.container.resolve<WarrantyReturnLabelProvider>('warrantyReturnLabelProvider')\n const result = await provider.createReturnLabel(\n { claim, lines },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n context.ctx.container,\n )\n\n if (result.status === 'notConfigured') {\n return NextResponse.json({ status: 'notConfigured' })\n }\n\n const response = await executeSetReturnLabel(\n commandBus,\n toSetReturnLabelInput({\n ...guardedInput,\n labelUrl: result.labelUrl,\n trackingNumber: result.trackingNumber,\n carrier: result.carrier,\n }, context),\n context.ctx,\n )\n await guarded.runAfterSuccess()\n return NextResponse.json(response)\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: firstTranslatableIssueKey(err) }, { status: 400 })\n }\n logger.error('warranty_claims.return-label.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Create or record a return label for a warranty claim',\n methods: {\n POST: {\n summary: 'Generate a carrier return label or record manual label details',\n requestBody: { contentType: 'application/json', schema: returnLabelRequestSchema },\n responses: [\n {\n status: 200,\n description: 'Return label created or provider not configured',\n schema: returnLabelResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { WarrantyClaim, WarrantyClaimLine } from '../../data/entities'\nimport { claimSetReturnLabelSchema, type ClaimSetReturnLabelInput } from '../../data/validators'\nimport type { WarrantyReturnLabelProvider } from '../../services/returnLabelProvider'\nimport {\n WARRANTY_CLAIM_RESOURCE_KIND,\n enforceWarrantyClaimOptimisticLock,\n requireScopedClaim,\n} 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 ReturnLabelCreatedResponse = {\n status: 'created'\n labelUrl: string | null\n trackingNumber: string | null\n carrier: string | null\n}\ntype ReturnLabelCommandResult = Omit<ReturnLabelCreatedResponse, 'status'> & { claimId: string }\n\nconst uuid = z.string().uuid()\nconst returnLabelText = (max: number) => z.string().trim().min(1).max(max).optional()\nconst optimisticLockTokenSchema = z.string().datetime().nullable().optional()\n\nconst returnLabelRequestSchema = z\n .object({\n claimId: uuid,\n manual: z.boolean().optional(),\n labelUrl: returnLabelText(2048),\n trackingNumber: returnLabelText(191),\n carrier: returnLabelText(120),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\ntype ReturnLabelRequest = z.infer<typeof returnLabelRequestSchema>\n\nconst returnLabelResponseSchema = z.discriminatedUnion('status', [\n z.object({\n status: z.literal('created'),\n labelUrl: z.string().nullable(),\n trackingNumber: z.string().nullable(),\n carrier: z.string().nullable(),\n }),\n z.object({ status: z.literal('notConfigured') }),\n])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction translateKey(key: string): string {\n return key\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(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.organization_required' })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate: translateKey,\n }\n}\n\nfunction toReturnLabelRequest(payload: Record<string, unknown>): ReturnLabelRequest {\n return returnLabelRequestSchema.parse({\n claimId: payload.claimId ?? payload.id,\n manual: payload.manual,\n labelUrl: payload.labelUrl,\n trackingNumber: payload.trackingNumber,\n carrier: payload.carrier,\n updatedAt: payload.updatedAt,\n })\n}\n\nfunction isManualRequest(input: ReturnLabelRequest): boolean {\n return input.manual === true\n || input.labelUrl !== undefined\n || input.trackingNumber !== undefined\n || input.carrier !== undefined\n}\n\nfunction toSetReturnLabelInput(\n input: ReturnLabelRequest,\n context: ActionRouteContext,\n): ClaimSetReturnLabelInput {\n const scopedPayload = withScopedPayload({\n id: input.claimId,\n labelUrl: input.labelUrl,\n trackingNumber: input.trackingNumber,\n carrier: input.carrier,\n updatedAt: input.updatedAt,\n }, context.ctx, context.translate)\n return claimSetReturnLabelSchema.parse(scopedPayload)\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: ReturnLabelRequest,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nasync function loadClaimAndLines(\n context: ActionRouteContext,\n claimId: string,\n): Promise<{ claim: WarrantyClaim; lines: WarrantyClaimLine[] }> {\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n const em = (context.ctx.container.resolve('em') as EntityManager).fork()\n const claim = await requireScopedClaim(em, claimId, scope)\n const lines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n { claim: claim.id, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n {},\n scope,\n )\n return { claim, lines }\n}\n\nfunction assertGeneratableStatus(claim: WarrantyClaim): void {\n if (claim.status !== 'approved' && claim.status !== 'awaiting_return') {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.returnLabelInvalidStatus' })\n }\n}\n\nasync function executeSetReturnLabel(\n commandBus: CommandBus,\n input: ClaimSetReturnLabelInput,\n ctx: CommandRuntimeContext,\n): Promise<ReturnLabelCreatedResponse> {\n const { result } = await commandBus.execute<ClaimSetReturnLabelInput, ReturnLabelCommandResult>(\n 'warranty_claims.claim.set_return_label',\n { input, ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: 'warranty_claims.errors.save_failed' })\n return {\n status: 'created',\n labelUrl: result.labelUrl,\n trackingNumber: result.trackingNumber,\n carrier: result.carrier,\n }\n}\n\nexport function firstTranslatableIssueKey(err: z.ZodError): string {\n const keyed = err.issues.find(\n (issue) => typeof issue.message === 'string' && issue.message.startsWith('warranty_claims.'),\n )?.message\n return keyed ?? 'warranty_claims.errors.invalidInput'\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveActionContext(req)\n const requestInput = toReturnLabelRequest(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 ? toReturnLabelRequest(guarded.modifiedPayload) : requestInput\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n\n if (isManualRequest(guardedInput)) {\n const response = await executeSetReturnLabel(\n commandBus,\n toSetReturnLabelInput(guardedInput, context),\n context.ctx,\n )\n await guarded.runAfterSuccess()\n return NextResponse.json(response)\n }\n\n const { claim, lines } = await loadClaimAndLines(context, guardedInput.claimId)\n assertGeneratableStatus(claim)\n await enforceWarrantyClaimOptimisticLock(\n context.ctx,\n claim,\n WARRANTY_CLAIM_RESOURCE_KIND,\n guardedInput.updatedAt ?? undefined,\n )\n\n const provider = context.ctx.container.resolve<WarrantyReturnLabelProvider>('warrantyReturnLabelProvider')\n const result = await provider.createReturnLabel(\n { claim, lines },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n context.ctx.container,\n )\n\n if (result.status === 'notConfigured') {\n return NextResponse.json({ status: 'notConfigured' })\n }\n\n const response = await executeSetReturnLabel(\n commandBus,\n toSetReturnLabelInput({\n ...guardedInput,\n labelUrl: result.labelUrl,\n trackingNumber: result.trackingNumber,\n carrier: result.carrier,\n }, context),\n context.ctx,\n )\n await guarded.runAfterSuccess()\n return NextResponse.json(response)\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: firstTranslatableIssueKey(err) }, { status: 400 })\n }\n logger.error('warranty_claims.return-label.post failed', { err })\n return NextResponse.json({ error: 'warranty_claims.errors.save_failed' }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Create or record a return label for a warranty claim',\n methods: {\n POST: {\n summary: 'Generate a carrier return label or record manual label details',\n requestBody: { contentType: 'application/json', schema: returnLabelRequestSchema },\n responses: [\n {\n status: 200,\n description: 'Return label created or provider not configured',\n schema: returnLabelResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["response"]
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
8
9
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
10
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
@@ -111,6 +112,10 @@ async function POST(req) {
|
|
|
111
112
|
return NextResponse.json({ salesReturnId: result.salesReturnId, skippedLineIds: result.skippedLineIds });
|
|
112
113
|
} catch (err) {
|
|
113
114
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
115
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
116
|
+
if (interceptorRejection) {
|
|
117
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
118
|
+
}
|
|
114
119
|
if (err instanceof z.ZodError) {
|
|
115
120
|
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
116
121
|
}
|