@open-mercato/core 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7135.1.19bf969756
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +5 -0
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +40 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/lib/access-control.js +6 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/system-user.js +8 -3
- package/dist/modules/communication_channels/lib/system-user.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +13 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +5 -0
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +5 -0
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +5 -0
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +5 -0
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
- package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +13 -0
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +5 -0
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +5 -0
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +5 -0
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +13 -0
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +5 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
- package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
- package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward/route.js +5 -0
- package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/reply/route.js +5 -0
- package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +9 -0
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/token/[token]/route.js +5 -0
- package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +5 -0
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +5 -0
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +5 -0
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +25 -18
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +5 -0
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +16 -12
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +6 -1
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +24 -27
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js +8 -10
- package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js +4 -9
- package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +8 -3
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +17 -13
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +19 -15
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/registrations/route.js +9 -4
- package/dist/modules/warranty_claims/api/registrations/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +16 -12
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +25 -19
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -1
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +16 -12
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +5 -0
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +5 -0
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js +22 -1
- package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/commands/shared.js +2 -2
- package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js +31 -0
- package/dist/modules/warranty_claims/lib/portalAuthGuard.js.map +7 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/package.json +7 -7
- package/src/helpers/integration/communicationChannelsFixtures.ts +13 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
- package/src/modules/auth/api/profile/route.ts +5 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +47 -2
- package/src/modules/communication_channels/lib/access-control.ts +28 -0
- package/src/modules/communication_channels/lib/system-user.ts +40 -22
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
- package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
- package/src/modules/customers/api/interactions/complete/route.ts +5 -0
- package/src/modules/customers/api/labels/assign/route.ts +5 -0
- package/src/modules/customers/api/labels/route.ts +5 -0
- package/src/modules/customers/api/labels/unassign/route.ts +5 -0
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
- package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
- package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
- package/src/modules/customers/api/pipelines/route.ts +13 -0
- package/src/modules/customers/api/settings/address-format/route.ts +5 -0
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
- package/src/modules/customers/api/tags/assign/route.ts +5 -0
- package/src/modules/customers/api/tags/unassign/route.ts +5 -0
- package/src/modules/customers/api/todos/route.ts +13 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -0
- package/src/modules/eudr/api/plots/import/route.ts +7 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
- package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
- package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
- package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
- package/src/modules/messages/api/[id]/forward/route.ts +5 -0
- package/src/modules/messages/api/[id]/reply/route.ts +5 -0
- package/src/modules/messages/api/[id]/route.ts +9 -0
- package/src/modules/messages/api/token/[token]/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
- package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
- package/src/modules/sales/api/quotes/accept/route.ts +5 -0
- package/src/modules/sales/api/quotes/convert/route.ts +5 -0
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
- package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
- package/src/modules/staff/api/team-members/self/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
- package/src/modules/warranty_claims/api/ai/assess/route.ts +28 -17
- package/src/modules/warranty_claims/api/assign/route.ts +5 -0
- package/src/modules/warranty_claims/api/credit-memo/route.ts +19 -13
- package/src/modules/warranty_claims/api/events/route.ts +6 -1
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +29 -27
- package/src/modules/warranty_claims/api/portal/claims/[id]/route.ts +9 -10
- package/src/modules/warranty_claims/api/portal/claims/[id]/shared.ts +5 -9
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +8 -3
- package/src/modules/warranty_claims/api/portal/claims/route.ts +10 -9
- package/src/modules/warranty_claims/api/portal/events/route.ts +18 -13
- package/src/modules/warranty_claims/api/receiving/route.ts +22 -15
- package/src/modules/warranty_claims/api/registrations/route.ts +9 -3
- package/src/modules/warranty_claims/api/replacement-order/route.ts +19 -13
- package/src/modules/warranty_claims/api/return-label/route.ts +26 -17
- package/src/modules/warranty_claims/api/risk/route.ts +3 -1
- package/src/modules/warranty_claims/api/sales-return/route.ts +19 -13
- package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
- package/src/modules/warranty_claims/api/submit/route.ts +5 -0
- package/src/modules/warranty_claims/api/transition/route.ts +5 -0
- package/src/modules/warranty_claims/api/troubleshooting-guides/route.ts +23 -1
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +3 -1
- package/src/modules/warranty_claims/commands/shared.ts +7 -1
- package/src/modules/warranty_claims/lib/portalAuthGuard.ts +30 -0
- package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
|
@@ -4,7 +4,9 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
9
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
11
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
10
12
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
@@ -71,7 +73,7 @@ function isSuperAdmin(auth) {
|
|
|
71
73
|
function buildAuthContext(context) {
|
|
72
74
|
const userId = context.ctx.auth?.sub;
|
|
73
75
|
if (!userId) {
|
|
74
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
76
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
75
77
|
}
|
|
76
78
|
return {
|
|
77
79
|
tenantId: context.tenantId,
|
|
@@ -84,7 +86,7 @@ function buildAuthContext(context) {
|
|
|
84
86
|
function buildAttachmentAuthContext(context) {
|
|
85
87
|
const auth = context.ctx.auth;
|
|
86
88
|
if (!auth?.sub) {
|
|
87
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
89
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
88
90
|
}
|
|
89
91
|
return {
|
|
90
92
|
...auth,
|
|
@@ -93,16 +95,16 @@ function buildAttachmentAuthContext(context) {
|
|
|
93
95
|
orgId: context.organizationId
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
|
-
async function resolveAssessContext(req) {
|
|
98
|
+
async function resolveAssessContext(req, translate) {
|
|
97
99
|
const container = await createRequestContainer();
|
|
98
100
|
const auth = await getAuthFromRequest(req);
|
|
99
101
|
if (!auth || !auth.tenantId) {
|
|
100
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
102
|
+
throw new CrudHttpError(401, { error: translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
101
103
|
}
|
|
102
104
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
103
105
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
104
106
|
if (!organizationId) {
|
|
105
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.organization_required" });
|
|
107
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.organization_required", "Organization context is required.") });
|
|
106
108
|
}
|
|
107
109
|
return {
|
|
108
110
|
ctx: {
|
|
@@ -117,7 +119,7 @@ async function resolveAssessContext(req) {
|
|
|
117
119
|
organizationId,
|
|
118
120
|
container,
|
|
119
121
|
em: container.resolve("em").fork(),
|
|
120
|
-
translate
|
|
122
|
+
translate
|
|
121
123
|
};
|
|
122
124
|
}
|
|
123
125
|
function toAssessInput(payload, context) {
|
|
@@ -126,7 +128,7 @@ function toAssessInput(payload, context) {
|
|
|
126
128
|
async function runGuard(req, context, input) {
|
|
127
129
|
const userId = context.ctx.auth?.sub;
|
|
128
130
|
if (!userId) {
|
|
129
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
131
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
130
132
|
}
|
|
131
133
|
return runRouteMutationGuards({
|
|
132
134
|
container: context.ctx.container,
|
|
@@ -144,7 +146,7 @@ async function runGuard(req, context, input) {
|
|
|
144
146
|
}
|
|
145
147
|
});
|
|
146
148
|
}
|
|
147
|
-
async function loadScopedLine(em, scope, claimId, lineId) {
|
|
149
|
+
async function loadScopedLine(em, scope, claimId, lineId, translate) {
|
|
148
150
|
const line = await findOneWithDecryption(
|
|
149
151
|
em,
|
|
150
152
|
WarrantyClaimLine,
|
|
@@ -159,7 +161,7 @@ async function loadScopedLine(em, scope, claimId, lineId) {
|
|
|
159
161
|
scope
|
|
160
162
|
);
|
|
161
163
|
if (!line) {
|
|
162
|
-
throw new CrudHttpError(404, { error: "warranty_claims.errors.notFound" });
|
|
164
|
+
throw new CrudHttpError(404, { error: translate("warranty_claims.errors.notFound", "Claim not found.") });
|
|
163
165
|
}
|
|
164
166
|
return line;
|
|
165
167
|
}
|
|
@@ -181,7 +183,7 @@ async function verifyAttachmentLinkedToTarget(context, input) {
|
|
|
181
183
|
]
|
|
182
184
|
}) : false;
|
|
183
185
|
if (!linked) {
|
|
184
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.attachmentNotLinked" });
|
|
186
|
+
throw new CrudHttpError(400, { error: context.translate("warranty_claims.errors.attachmentNotLinked", "The attachment is not linked to this claim.") });
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
function mergeAssessmentPayload(current, key, value) {
|
|
@@ -209,12 +211,13 @@ async function persistAssessmentPayload(context, line, key, value, updatedAt) {
|
|
|
209
211
|
);
|
|
210
212
|
}
|
|
211
213
|
async function POST(req) {
|
|
214
|
+
const { translate } = await resolveTranslations();
|
|
212
215
|
try {
|
|
213
|
-
const context = await resolveAssessContext(req);
|
|
216
|
+
const context = await resolveAssessContext(req, translate);
|
|
214
217
|
const payload = toRecord(await readJsonSafe(req, {}));
|
|
215
218
|
const parsed = toAssessInput(payload, context);
|
|
216
219
|
if (parsed.kind === "damage" && !parsed.lineId) {
|
|
217
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.invalidInput" });
|
|
220
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.invalidInput", "Invalid input") });
|
|
218
221
|
}
|
|
219
222
|
const guarded = parsed.lineId ? await runGuard(req, context, parsed) : null;
|
|
220
223
|
if (guarded && !guarded.ok) {
|
|
@@ -224,9 +227,9 @@ async function POST(req) {
|
|
|
224
227
|
const scope = { tenantId: context.tenantId, organizationId: context.organizationId };
|
|
225
228
|
if (input.kind === "damage") {
|
|
226
229
|
if (!input.lineId) {
|
|
227
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.invalidInput" });
|
|
230
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.invalidInput", "Invalid input") });
|
|
228
231
|
}
|
|
229
|
-
const line2 = await loadScopedLine(context.em, scope, input.claimId, input.lineId);
|
|
232
|
+
const line2 = await loadScopedLine(context.em, scope, input.claimId, input.lineId, translate);
|
|
230
233
|
await verifyAttachmentLinkedToTarget(context, {
|
|
231
234
|
attachmentId: input.attachmentId,
|
|
232
235
|
claimId: input.claimId,
|
|
@@ -245,8 +248,8 @@ async function POST(req) {
|
|
|
245
248
|
await guarded?.runAfterSuccess();
|
|
246
249
|
return NextResponse.json({ status: "ok", assessment });
|
|
247
250
|
}
|
|
248
|
-
await requireScopedClaim(context.em, input.claimId, scope);
|
|
249
|
-
const line = input.lineId ? await loadScopedLine(context.em, scope, input.claimId, input.lineId) : null;
|
|
251
|
+
await requireScopedClaim(context.em, input.claimId, scope, {}, translate("warranty_claims.errors.notFound", "Claim not found."));
|
|
252
|
+
const line = input.lineId ? await loadScopedLine(context.em, scope, input.claimId, input.lineId, translate) : null;
|
|
250
253
|
await verifyAttachmentLinkedToTarget(context, {
|
|
251
254
|
attachmentId: input.attachmentId,
|
|
252
255
|
claimId: input.claimId,
|
|
@@ -266,6 +269,10 @@ async function POST(req) {
|
|
|
266
269
|
return NextResponse.json({ status: "ok", extraction });
|
|
267
270
|
} catch (err) {
|
|
268
271
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
272
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
273
|
+
if (interceptorRejection) {
|
|
274
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
275
|
+
}
|
|
269
276
|
if (isWarrantyAiNotConfiguredError(err)) {
|
|
270
277
|
return NextResponse.json({ status: "notConfigured" });
|
|
271
278
|
}
|
|
@@ -273,10 +280,10 @@ async function POST(req) {
|
|
|
273
280
|
return NextResponse.json({ status: "aiUnavailable" });
|
|
274
281
|
}
|
|
275
282
|
if (err instanceof z.ZodError) {
|
|
276
|
-
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
283
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
|
|
277
284
|
}
|
|
278
285
|
logger.error("warranty_claims.ai.assess.post failed", { err });
|
|
279
|
-
return NextResponse.json({ error: "warranty_claims.errors.save_failed" }, { status: 500 });
|
|
286
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") }, { status: 500 });
|
|
280
287
|
}
|
|
281
288
|
}
|
|
282
289
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/warranty_claims/api/ai/assess/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest, type AuthContext } 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 type { AiChatRequestContext } from '@open-mercato/ai-assistant/modules/ai_assistant/lib/attachment-bridge-types'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { AttachmentTargetAccessService } from '@open-mercato/core/modules/attachments/lib/target-access-service'\nimport { WarrantyClaimLine } from '../../../data/entities'\nimport {\n assessDamagePhoto,\n extractProofOfPurchase,\n isWarrantyAiNotConfiguredError,\n isWarrantyAiUnavailableError,\n} from '../../../lib/aiAssist'\nimport {\n WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n requireScopedClaim,\n type WarrantyClaimScope,\n} from '../../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.union([z.string().datetime(), z.date()]).nullable().optional()\nconst CLAIM_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim'\nconst LINE_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim_line'\n\nconst assessBodySchema = z\n .object({\n claimId: uuid,\n lineId: uuid.optional(),\n attachmentId: uuid,\n kind: z.enum(['damage', 'proof']),\n organizationId: uuid.optional(),\n tenantId: uuid.optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\ntype AssessBodyInput = z.infer<typeof assessBodySchema>\n\ntype AssessRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n container: AwilixContainer\n em: EntityManager\n translate: (key: string, fallback?: string) => string\n}\n\ntype SetAssessmentCommandInput = {\n id: string\n organizationId: string\n tenantId: string\n assessmentPayload: Record<string, unknown>\n updatedAt?: string | Date | null\n}\n\nconst damageAssessmentSchema = z.object({\n damageType: z.string(),\n severity: z.enum(['minor', 'moderate', 'severe', 'unknown']),\n probableCause: z.string(),\n misuseSuspected: z.boolean(),\n confidence: z.number(),\n summary: z.string(),\n})\n\nconst proofExtractionSchema = z.object({\n purchaseDate: z.string().nullable(),\n serialNumber: z.string().nullable(),\n amount: z.string().nullable(),\n currency: z.string().nullable(),\n merchant: z.string().nullable(),\n confidence: z.number(),\n})\n\nconst assessResponseSchema = z.union([\n z.object({\n status: z.literal('ok'),\n assessment: damageAssessmentSchema.optional(),\n extraction: proofExtractionSchema.optional(),\n }),\n z.object({ status: z.literal('notConfigured') }),\n z.object({ status: z.literal('aiUnavailable') }),\n])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction isSuperAdmin(auth: CommandRuntimeContext['auth']): boolean {\n return Boolean(auth && toRecord(auth).isSuperAdmin === true)\n}\n\nfunction buildAuthContext(context: AssessRouteContext): AiChatRequestContext {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId,\n features: [],\n isSuperAdmin: isSuperAdmin(context.ctx.auth),\n }\n}\n\nfunction buildAttachmentAuthContext(context: AssessRouteContext): Exclude<AuthContext, null> {\n const auth = context.ctx.auth\n if (!auth?.sub) {\n throw new CrudHttpError(401, { error: 'warranty_claims.errors.unauthorized' })\n }\n return {\n ...auth,\n sub: auth.sub,\n tenantId: context.tenantId,\n orgId: context.organizationId,\n }\n}\n\nasync function resolveAssessContext(req: Request): Promise<AssessRouteContext> {\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 container,\n em: container.resolve<EntityManager>('em').fork(),\n translate: (key: string) => key,\n }\n}\n\nfunction toAssessInput(payload: Record<string, unknown>, context: AssessRouteContext): AssessBodyInput {\n return assessBodySchema.parse(withScopedPayload(payload, context.ctx, context.translate))\n}\n\nasync function runGuard(\n req: Request,\n context: AssessRouteContext,\n input: AssessBodyInput & { lineId: string },\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: input.lineId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nasync function loadScopedLine(\n em: EntityManager,\n scope: WarrantyClaimScope,\n claimId: string,\n lineId: string,\n): Promise<WarrantyClaimLine> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n {\n id: lineId,\n claim: claimId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (!line) {\n throw new CrudHttpError(404, { error: 'warranty_claims.errors.notFound' })\n }\n return line\n}\n\nasync function verifyAttachmentLinkedToTarget(\n context: AssessRouteContext,\n input: { attachmentId: string; claimId: string; lineId?: string | null },\n): Promise<void> {\n let service: AttachmentTargetAccessService | null = null\n try {\n service = context.container.resolve<AttachmentTargetAccessService>('attachmentTargetAccessService')\n } catch {\n service = null\n }\n const linked = service ? await service.canAccessLinkedTarget({\n attachmentId: input.attachmentId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n auth: buildAttachmentAuthContext(context),\n targets: [\n { entityId: CLAIM_ATTACHMENT_ENTITY_ID, recordId: input.claimId },\n ...(input.lineId ? [{ entityId: LINE_ATTACHMENT_ENTITY_ID, recordId: input.lineId }] : []),\n ],\n }) : false\n if (!linked) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.attachmentNotLinked' })\n }\n}\n\nfunction mergeAssessmentPayload(\n current: Record<string, unknown> | null | undefined,\n key: 'damage' | 'proof',\n value: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n ...(current ?? {}),\n [key]: value,\n generatedAt: new Date().toISOString(),\n }\n}\n\nasync function persistAssessmentPayload(\n context: AssessRouteContext,\n line: WarrantyClaimLine,\n key: 'damage' | 'proof',\n value: Record<string, unknown>,\n updatedAt: string | Date | null | undefined,\n): Promise<void> {\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const assessmentPayload = mergeAssessmentPayload(line.assessmentPayload ?? null, key, value)\n await commandBus.execute<SetAssessmentCommandInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.set_assessment',\n {\n input: {\n id: line.id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n assessmentPayload,\n updatedAt,\n },\n ctx: context.ctx,\n },\n )\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveAssessContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = toAssessInput(payload, context)\n if (parsed.kind === 'damage' && !parsed.lineId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.invalidInput' })\n }\n\n const guarded = parsed.lineId ? await runGuard(req, context, parsed as AssessBodyInput & { lineId: string }) : null\n if (guarded && !guarded.ok) {\n return guarded.response\n }\n const input = guarded?.modifiedPayload ? toAssessInput(guarded.modifiedPayload, context) : parsed\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n\n if (input.kind === 'damage') {\n if (!input.lineId) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.invalidInput' })\n }\n const line = await loadScopedLine(context.em, scope, input.claimId, input.lineId)\n await verifyAttachmentLinkedToTarget(context, {\n attachmentId: input.attachmentId,\n claimId: input.claimId,\n lineId: input.lineId,\n })\n const assessment = await assessDamagePhoto({\n em: context.em,\n container: context.container,\n scope,\n claimId: input.claimId,\n lineId: input.lineId,\n attachmentId: input.attachmentId,\n authContext: buildAuthContext(context),\n })\n await persistAssessmentPayload(context, line, 'damage', assessment, input.updatedAt)\n await guarded?.runAfterSuccess()\n return NextResponse.json({ status: 'ok', assessment })\n }\n\n await requireScopedClaim(context.em, input.claimId, scope)\n const line = input.lineId ? await loadScopedLine(context.em, scope, input.claimId, input.lineId) : null\n await verifyAttachmentLinkedToTarget(context, {\n attachmentId: input.attachmentId,\n claimId: input.claimId,\n lineId: input.lineId ?? null,\n })\n const extraction = await extractProofOfPurchase({\n em: context.em,\n container: context.container,\n scope,\n attachmentId: input.attachmentId,\n authContext: buildAuthContext(context),\n })\n if (line) {\n await persistAssessmentPayload(context, line, 'proof', extraction, input.updatedAt)\n await guarded?.runAfterSuccess()\n }\n return NextResponse.json({ status: 'ok', extraction })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n if (isWarrantyAiNotConfiguredError(err)) {\n return NextResponse.json({ status: 'notConfigured' })\n }\n if (isWarrantyAiUnavailableError(err)) {\n return NextResponse.json({ status: 'aiUnavailable' })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'warranty_claims.errors.invalidInput' }, { status: 400 })\n }\n logger.error('warranty_claims.ai.assess.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: 'Assess warranty claim attachment with AI',\n methods: {\n POST: {\n summary: 'Assess a damage photo or extract proof-of-purchase facts',\n requestBody: { contentType: 'application/json', schema: assessBodySchema },\n responses: [\n {\n status: 200,\n description: 'Assessment completed or AI gracefully degraded',\n schema: assessResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAGlB,SAAS,8BAA8B;AACvC,SAAS,0BAA4C;AACrD,SAAS,0CAA0C;AAEnD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,oBAAoB;AAC7B,SAAS,8BAA6D;AACtE,SAAS,yBAAyB;AAGlC,SAAS,6BAA6B;AAEtC,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,iBAAiB;AAE7C,MAAM,OAAO,EAAE,OAAO,EAAE,KAAK;AAC7B,MAAM,4BAA4B,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AACjG,MAAM,6BAA6B;AACnC,MAAM,4BAA4B;AAElC,MAAM,mBAAmB,EACtB,OAAO;AAAA,EACN,SAAS;AAAA,EACT,QAAQ,KAAK,SAAS;AAAA,EACtB,cAAc;AAAA,EACd,MAAM,EAAE,KAAK,CAAC,UAAU,OAAO,CAAC;AAAA,EAChC,gBAAgB,KAAK,SAAS;AAAA,EAC9B,UAAU,KAAK,SAAS;AAAA,EACxB,WAAW;AACb,CAAC,EACA,OAAO;AAqBV,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,YAAY,EAAE,OAAO;AAAA,EACrB,UAAU,EAAE,KAAK,CAAC,SAAS,YAAY,UAAU,SAAS,CAAC;AAAA,EAC3D,eAAe,EAAE,OAAO;AAAA,EACxB,iBAAiB,EAAE,QAAQ;AAAA,EAC3B,YAAY,EAAE,OAAO;AAAA,EACrB,SAAS,EAAE,OAAO;AACpB,CAAC;AAED,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,YAAY,EAAE,OAAO;AACvB,CAAC;AAED,MAAM,uBAAuB,EAAE,MAAM;AAAA,EACnC,EAAE,OAAO;AAAA,IACP,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACtB,YAAY,uBAAuB,SAAS;AAAA,IAC5C,YAAY,sBAAsB,SAAS;AAAA,EAC7C,CAAC;AAAA,EACD,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,eAAe,EAAE,CAAC;AAAA,EAC/C,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,eAAe,EAAE,CAAC;AACjD,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,8BAA8B,EAAE;AAC/E;AAEA,SAAS,SAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,CAAC;AAC3G;AAEA,SAAS,aAAa,MAA8C;AAClE,SAAO,QAAQ,QAAQ,SAAS,IAAI,EAAE,iBAAiB,IAAI;AAC7D;AAEA,SAAS,iBAAiB,SAAmD;AAC3E,QAAM,SAAS,QAAQ,IAAI,MAAM;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest, type AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { AiChatRequestContext } from '@open-mercato/ai-assistant/modules/ai_assistant/lib/attachment-bridge-types'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { AttachmentTargetAccessService } from '@open-mercato/core/modules/attachments/lib/target-access-service'\nimport { WarrantyClaimLine } from '../../../data/entities'\nimport {\n assessDamagePhoto,\n extractProofOfPurchase,\n isWarrantyAiNotConfiguredError,\n isWarrantyAiUnavailableError,\n} from '../../../lib/aiAssist'\nimport {\n WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n requireScopedClaim,\n type WarrantyClaimScope,\n} from '../../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst uuid = z.string().uuid()\nconst optimisticLockTokenSchema = z.union([z.string().datetime(), z.date()]).nullable().optional()\nconst CLAIM_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim'\nconst LINE_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim_line'\n\nconst assessBodySchema = z\n .object({\n claimId: uuid,\n lineId: uuid.optional(),\n attachmentId: uuid,\n kind: z.enum(['damage', 'proof']),\n organizationId: uuid.optional(),\n tenantId: uuid.optional(),\n updatedAt: optimisticLockTokenSchema,\n })\n .strict()\n\ntype AssessBodyInput = z.infer<typeof assessBodySchema>\n\ntype AssessRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n container: AwilixContainer\n em: EntityManager\n translate: (key: string, fallback?: string) => string\n}\n\ntype SetAssessmentCommandInput = {\n id: string\n organizationId: string\n tenantId: string\n assessmentPayload: Record<string, unknown>\n updatedAt?: string | Date | null\n}\n\nconst damageAssessmentSchema = z.object({\n damageType: z.string(),\n severity: z.enum(['minor', 'moderate', 'severe', 'unknown']),\n probableCause: z.string(),\n misuseSuspected: z.boolean(),\n confidence: z.number(),\n summary: z.string(),\n})\n\nconst proofExtractionSchema = z.object({\n purchaseDate: z.string().nullable(),\n serialNumber: z.string().nullable(),\n amount: z.string().nullable(),\n currency: z.string().nullable(),\n merchant: z.string().nullable(),\n confidence: z.number(),\n})\n\nconst assessResponseSchema = z.union([\n z.object({\n status: z.literal('ok'),\n assessment: damageAssessmentSchema.optional(),\n extraction: proofExtractionSchema.optional(),\n }),\n z.object({ status: z.literal('notConfigured') }),\n z.object({ status: z.literal('aiUnavailable') }),\n])\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction isSuperAdmin(auth: CommandRuntimeContext['auth']): boolean {\n return Boolean(auth && toRecord(auth).isSuperAdmin === true)\n}\n\nfunction buildAuthContext(context: AssessRouteContext): AiChatRequestContext {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId,\n features: [],\n isSuperAdmin: isSuperAdmin(context.ctx.auth),\n }\n}\n\nfunction buildAttachmentAuthContext(context: AssessRouteContext): Exclude<AuthContext, null> {\n const auth = context.ctx.auth\n if (!auth?.sub) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return {\n ...auth,\n sub: auth.sub,\n tenantId: context.tenantId,\n orgId: context.organizationId,\n }\n}\n\nasync function resolveAssessContext(\n req: Request,\n translate: AssessRouteContext['translate'],\n): Promise<AssessRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required.') })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n container,\n em: container.resolve<EntityManager>('em').fork(),\n translate,\n }\n}\n\nfunction toAssessInput(payload: Record<string, unknown>, context: AssessRouteContext): AssessBodyInput {\n return assessBodySchema.parse(withScopedPayload(payload, context.ctx, context.translate))\n}\n\nasync function runGuard(\n req: Request,\n context: AssessRouteContext,\n input: AssessBodyInput & { lineId: string },\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId: input.lineId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nasync function loadScopedLine(\n em: EntityManager,\n scope: WarrantyClaimScope,\n claimId: string,\n lineId: string,\n translate: AssessRouteContext['translate'],\n): Promise<WarrantyClaimLine> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n {\n id: lineId,\n claim: claimId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (!line) {\n throw new CrudHttpError(404, { error: translate('warranty_claims.errors.notFound', 'Claim not found.') })\n }\n return line\n}\n\nasync function verifyAttachmentLinkedToTarget(\n context: AssessRouteContext,\n input: { attachmentId: string; claimId: string; lineId?: string | null },\n): Promise<void> {\n let service: AttachmentTargetAccessService | null = null\n try {\n service = context.container.resolve<AttachmentTargetAccessService>('attachmentTargetAccessService')\n } catch {\n service = null\n }\n const linked = service ? await service.canAccessLinkedTarget({\n attachmentId: input.attachmentId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n auth: buildAttachmentAuthContext(context),\n targets: [\n { entityId: CLAIM_ATTACHMENT_ENTITY_ID, recordId: input.claimId },\n ...(input.lineId ? [{ entityId: LINE_ATTACHMENT_ENTITY_ID, recordId: input.lineId }] : []),\n ],\n }) : false\n if (!linked) {\n throw new CrudHttpError(400, { error: context.translate('warranty_claims.errors.attachmentNotLinked', 'The attachment is not linked to this claim.') })\n }\n}\n\nfunction mergeAssessmentPayload(\n current: Record<string, unknown> | null | undefined,\n key: 'damage' | 'proof',\n value: Record<string, unknown>,\n): Record<string, unknown> {\n return {\n ...(current ?? {}),\n [key]: value,\n generatedAt: new Date().toISOString(),\n }\n}\n\nasync function persistAssessmentPayload(\n context: AssessRouteContext,\n line: WarrantyClaimLine,\n key: 'damage' | 'proof',\n value: Record<string, unknown>,\n updatedAt: string | Date | null | undefined,\n): Promise<void> {\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const assessmentPayload = mergeAssessmentPayload(line.assessmentPayload ?? null, key, value)\n await commandBus.execute<SetAssessmentCommandInput, { lineId: string; claimId: string }>(\n 'warranty_claims.claim_line.set_assessment',\n {\n input: {\n id: line.id,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n assessmentPayload,\n updatedAt,\n },\n ctx: context.ctx,\n },\n )\n}\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const context = await resolveAssessContext(req, translate)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = toAssessInput(payload, context)\n if (parsed.kind === 'damage' && !parsed.lineId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.invalidInput', 'Invalid input') })\n }\n\n const guarded = parsed.lineId ? await runGuard(req, context, parsed as AssessBodyInput & { lineId: string }) : null\n if (guarded && !guarded.ok) {\n return guarded.response\n }\n const input = guarded?.modifiedPayload ? toAssessInput(guarded.modifiedPayload, context) : parsed\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n\n if (input.kind === 'damage') {\n if (!input.lineId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.invalidInput', 'Invalid input') })\n }\n const line = await loadScopedLine(context.em, scope, input.claimId, input.lineId, translate)\n await verifyAttachmentLinkedToTarget(context, {\n attachmentId: input.attachmentId,\n claimId: input.claimId,\n lineId: input.lineId,\n })\n const assessment = await assessDamagePhoto({\n em: context.em,\n container: context.container,\n scope,\n claimId: input.claimId,\n lineId: input.lineId,\n attachmentId: input.attachmentId,\n authContext: buildAuthContext(context),\n })\n await persistAssessmentPayload(context, line, 'damage', assessment, input.updatedAt)\n await guarded?.runAfterSuccess()\n return NextResponse.json({ status: 'ok', assessment })\n }\n\n await requireScopedClaim(context.em, input.claimId, scope, {}, translate('warranty_claims.errors.notFound', 'Claim not found.'))\n const line = input.lineId ? await loadScopedLine(context.em, scope, input.claimId, input.lineId, translate) : null\n await verifyAttachmentLinkedToTarget(context, {\n attachmentId: input.attachmentId,\n claimId: input.claimId,\n lineId: input.lineId ?? null,\n })\n const extraction = await extractProofOfPurchase({\n em: context.em,\n container: context.container,\n scope,\n attachmentId: input.attachmentId,\n authContext: buildAuthContext(context),\n })\n if (line) {\n await persistAssessmentPayload(context, line, 'proof', extraction, input.updatedAt)\n await guarded?.runAfterSuccess()\n }\n return NextResponse.json({ status: 'ok', extraction })\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 (isWarrantyAiNotConfiguredError(err)) {\n return NextResponse.json({ status: 'notConfigured' })\n }\n if (isWarrantyAiUnavailableError(err)) {\n return NextResponse.json({ status: 'aiUnavailable' })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.ai.assess.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Assess warranty claim attachment with AI',\n methods: {\n POST: {\n summary: 'Assess a damage photo or extract proof-of-purchase facts',\n requestBody: { contentType: 'application/json', schema: assessBodySchema },\n responses: [\n {\n status: 200,\n description: 'Assessment completed or AI gracefully degraded',\n schema: assessResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["line"]
|
|
7
7
|
}
|
|
@@ -5,6 +5,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
7
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
8
9
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
9
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
10
11
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
@@ -91,6 +92,10 @@ async function POST(req) {
|
|
|
91
92
|
return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.id });
|
|
92
93
|
} catch (err) {
|
|
93
94
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
95
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
96
|
+
if (interceptorRejection) {
|
|
97
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
98
|
+
}
|
|
94
99
|
const { translate } = await resolveTranslations();
|
|
95
100
|
if (err instanceof z.ZodError) {
|
|
96
101
|
return NextResponse.json({ error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/assign/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 { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { assignClaimInputSchema, type AssignClaimInput } 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\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate,\n }\n}\n\nfunction toAssignInput(scopedPayload: Record<string, unknown>): AssignClaimInput {\n return assignClaimInputSchema.parse({\n id: scopedPayload.id,\n assigneeUserId: scopedPayload.assigneeUserId,\n })\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: AssignClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.id,\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 payload = toRecord(await readJsonSafe(req, {}))\n const scopedPayload = withScopedPayload(payload, context.ctx, context.translate)\n const input = toAssignInput(scopedPayload)\n const guarded = await runGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload ? toAssignInput(guarded.modifiedPayload) : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<AssignClaimInput, { claimId: string }>(\n 'warranty_claims.claim.assign',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.id })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.assign.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Assign warranty claim',\n methods: {\n POST: {\n summary: 'Assign or unassign a claim',\n requestBody: { contentType: 'application/json', schema: assignClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Claim assigned',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,2BAA2B;AACpC,SAAS,eAAe,uBAAuB;AAC/C,SAAS,oBAAoB;AAC7B,SAAS,8BAA6D;AACtE,SAAS,yBAAyB;AAElC,SAAS,8BAAqD;AAC9D,SAAS,oCAAoC;AAC7C,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,iBAAiB;AAStC,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,8BAA8B,EAAE;AAC/E;AAEA,SAAS,SAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,CAAC;AAC3G;AAEA,eAAe,qBAAqB,KAA2C;AAC7E,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI,CAAC,QAAQ,CAAC,KAAK,UAAU;AAC3B,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,uCAAuC,cAAc,EAAE,CAAC;AAAA,EAC1G;AACA,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,iBAAiB,OAAO,cAAc,KAAK,SAAS;AAC1D,MAAI,CAAC,gBAAgB;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,gDAAgD,kCAAkC,EAAE,CAAC;AAAA,EACvI;AACA,SAAO;AAAA,IACL,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,wBAAwB;AAAA,MACxB,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,MAClE,SAAS;AAAA,IACX;AAAA,IACA,UAAU,KAAK;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,cAAc,eAA0D;AAC/E,SAAO,uBAAuB,MAAM;AAAA,IAClC,IAAI,cAAc;AAAA,IAClB,gBAAgB,cAAc;AAAA,EAChC,CAAC;AACH;AAEA,eAAe,SACb,KACA,SACA,OACmC;AACnC,QAAM,SAAS,QAAQ,IAAI,MAAM;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,QAAQ,UAAU,uCAAuC,cAAc,EAAE,CAAC;AAAA,EAClH;AACA,SAAO,uBAAuB;AAAA,IAC5B,WAAW,QAAQ,IAAI;AAAA,IACvB;AAAA,IACA,MAAM;AAAA,MACJ;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,MACL,cAAc;AAAA,MACd,YAAY,MAAM;AAAA,MAClB,WAAW;AAAA,MACX,iBAAiB,EAAE,GAAG,MAAM;AAAA,IAC9B;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,UAAM,UAAU,SAAS,MAAM,aAAa,KAAK,CAAC,CAAC,CAAC;AACpD,UAAM,gBAAgB,kBAAkB,SAAS,QAAQ,KAAK,QAAQ,SAAS;AAC/E,UAAM,QAAQ,cAAc,aAAa;AACzC,UAAM,UAAU,MAAM,SAAS,KAAK,SAAS,KAAK;AAClD,QAAI,CAAC,QAAQ,IAAI;AACf,aAAO,QAAQ;AAAA,IACjB;AACA,UAAM,eAAe,QAAQ,kBAAkB,cAAc,QAAQ,eAAe,IAAI;AAExF,UAAM,aAAa,QAAQ,IAAI,UAAU,QAAQ,YAAY;AAC7D,UAAM,EAAE,OAAO,IAAI,MAAM,WAAW;AAAA,MAClC;AAAA,MACA,EAAE,OAAO,cAAc,KAAK,QAAQ,IAAI;AAAA,IAC1C;AAEA,UAAM,QAAQ,gBAAgB;AAE9B,WAAO,aAAa,KAAK,EAAE,IAAI,MAAM,SAAS,QAAQ,WAAW,aAAa,GAAG,CAAC;AAAA,EACpF,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,EAAG,QAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AACnF,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,UAAU,uCAAuC,eAAe,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxH;AACA,WAAO,MAAM,sCAAsC,EAAE,IAAI,CAAC;AAC1D,WAAO,aAAa,KAAK,EAAE,OAAO,UAAU,sCAAsC,+BAA+B,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvI;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa,EAAE,aAAa,oBAAoB,QAAQ,uBAAuB;AAAA,MAC/E,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,QAClE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { assignClaimInputSchema, type AssignClaimInput } 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\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nasync function resolveActionContext(req: Request): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate,\n }\n}\n\nfunction toAssignInput(scopedPayload: Record<string, unknown>): AssignClaimInput {\n return assignClaimInputSchema.parse({\n id: scopedPayload.id,\n assigneeUserId: scopedPayload.assigneeUserId,\n })\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: AssignClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.id,\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 payload = toRecord(await readJsonSafe(req, {}))\n const scopedPayload = withScopedPayload(payload, context.ctx, context.translate)\n const input = toAssignInput(scopedPayload)\n const guarded = await runGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload ? toAssignInput(guarded.modifiedPayload) : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<AssignClaimInput, { claimId: string }>(\n 'warranty_claims.claim.assign',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.id })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.assign.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Assign warranty claim',\n methods: {\n POST: {\n summary: 'Assign or unassign a claim',\n requestBody: { contentType: 'application/json', schema: assignClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Claim assigned',\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,7 +4,9 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
7
8
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
9
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
11
|
import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
|
|
10
12
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
@@ -29,22 +31,19 @@ const errorResponseSchema = z.object({
|
|
|
29
31
|
const metadata = {
|
|
30
32
|
POST: { requireAuth: true, requireFeatures: ["warranty_claims.claim.manage", "sales.credit_memos.manage"] }
|
|
31
33
|
};
|
|
32
|
-
function translateKey(key) {
|
|
33
|
-
return key;
|
|
34
|
-
}
|
|
35
34
|
function toRecord(value) {
|
|
36
35
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
37
36
|
}
|
|
38
|
-
async function resolveActionContext(req) {
|
|
37
|
+
async function resolveActionContext(req, translate) {
|
|
39
38
|
const container = await createRequestContainer();
|
|
40
39
|
const auth = await getAuthFromRequest(req);
|
|
41
40
|
if (!auth || !auth.tenantId) {
|
|
42
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
41
|
+
throw new CrudHttpError(401, { error: translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
43
42
|
}
|
|
44
43
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
45
44
|
const organizationId = scope?.selectedId ?? auth.orgId ?? null;
|
|
46
45
|
if (!organizationId) {
|
|
47
|
-
throw new CrudHttpError(400, { error: "warranty_claims.errors.organization_required" });
|
|
46
|
+
throw new CrudHttpError(400, { error: translate("warranty_claims.errors.organization_required", "Organization context is required.") });
|
|
48
47
|
}
|
|
49
48
|
return {
|
|
50
49
|
ctx: {
|
|
@@ -57,7 +56,7 @@ async function resolveActionContext(req) {
|
|
|
57
56
|
},
|
|
58
57
|
tenantId: auth.tenantId,
|
|
59
58
|
organizationId,
|
|
60
|
-
translate
|
|
59
|
+
translate
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
62
|
function toCreditMemoRequest(payload) {
|
|
@@ -73,7 +72,7 @@ function toCommandInput(input, context) {
|
|
|
73
72
|
async function runGuard(req, context, input) {
|
|
74
73
|
const userId = context.ctx.auth?.sub;
|
|
75
74
|
if (!userId) {
|
|
76
|
-
throw new CrudHttpError(401, { error: "warranty_claims.errors.unauthorized" });
|
|
75
|
+
throw new CrudHttpError(401, { error: context.translate("warranty_claims.errors.unauthorized", "Unauthorized") });
|
|
77
76
|
}
|
|
78
77
|
return runRouteMutationGuards({
|
|
79
78
|
container: context.ctx.container,
|
|
@@ -92,8 +91,9 @@ async function runGuard(req, context, input) {
|
|
|
92
91
|
});
|
|
93
92
|
}
|
|
94
93
|
async function POST(req) {
|
|
94
|
+
const { translate } = await resolveTranslations();
|
|
95
95
|
try {
|
|
96
|
-
const context = await resolveActionContext(req);
|
|
96
|
+
const context = await resolveActionContext(req, translate);
|
|
97
97
|
const requestInput = toCreditMemoRequest(toRecord(await readJsonSafe(req, {})));
|
|
98
98
|
const guarded = await runGuard(req, context, requestInput);
|
|
99
99
|
if (!guarded.ok) {
|
|
@@ -105,7 +105,7 @@ async function POST(req) {
|
|
|
105
105
|
"warranty_claims.claim.create_credit_memo",
|
|
106
106
|
{ input: toCommandInput(guardedInput, context), ctx: context.ctx }
|
|
107
107
|
);
|
|
108
|
-
if (!result) throw new CrudHttpError(400, { error: "warranty_claims.errors.save_failed" });
|
|
108
|
+
if (!result) throw new CrudHttpError(400, { error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") });
|
|
109
109
|
await guarded.runAfterSuccess();
|
|
110
110
|
return NextResponse.json({
|
|
111
111
|
creditMemoId: result.creditMemoId,
|
|
@@ -115,11 +115,15 @@ async function POST(req) {
|
|
|
115
115
|
});
|
|
116
116
|
} catch (err) {
|
|
117
117
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
118
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
119
|
+
if (interceptorRejection) {
|
|
120
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
121
|
+
}
|
|
118
122
|
if (err instanceof z.ZodError) {
|
|
119
|
-
return NextResponse.json({ error: "warranty_claims.errors.invalidInput" }, { status: 400 });
|
|
123
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
|
|
120
124
|
}
|
|
121
125
|
logger.error("warranty_claims.credit-memo.post failed", { err });
|
|
122
|
-
return NextResponse.json({ error: "warranty_claims.errors.save_failed" }, { status: 500 });
|
|
126
|
+
return NextResponse.json({ error: translate("warranty_claims.errors.save_failed", "Failed to save warranty claim.") }, { status: 500 });
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
const openApi = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/credit-memo/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 { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n claimCreateCreditMemoSchema,\n type ClaimCreateCreditMemoInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../commands/shared'\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 CreditMemoCommandResult = {\n claimId: string\n creditMemoId: string\n creditMemoUpdatedAt: string | null\n skippedLineIds: string[]\n grandTotalGrossAmount: string\n currencyCode: string\n}\n\nconst creditMemoRequestSchema = z\n .object({\n claimId: z.string().uuid(),\n updatedAt: z.string().datetime().nullable().optional(),\n })\n .strict()\n\ntype CreditMemoRequest = z.infer<typeof creditMemoRequestSchema>\n\nconst creditMemoResponseSchema = z.object({\n creditMemoId: z.string().uuid(),\n skippedLineIds: z.array(z.string().uuid()),\n grandTotalGrossAmount: z.string(),\n currencyCode: z.string(),\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.credit_memos.manage'] },\n}\n\nfunction
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAEnD,SAAS,eAAe,uBAAuB;AAC/C,SAAS,oBAAoB;AAC7B,SAAS,8BAA6D;AACtE,SAAS,yBAAyB;AAElC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,OAEK;AACP,SAAS,oCAAoC;AAE7C,MAAM,SAAS,aAAa,iBAAiB;AAkB7C,MAAM,0BAA0B,EAC7B,OAAO;AAAA,EACN,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;AACvD,CAAC,EACA,OAAO;AAIV,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,cAAc,EAAE,OAAO,EAAE,KAAK;AAAA,EAC9B,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,EACzC,uBAAuB,EAAE,OAAO;AAAA,EAChC,cAAc,EAAE,OAAO;AACzB,CAAC;AAED,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,OAAO,EAAE,OAAO;AAClB,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,gCAAgC,2BAA2B,EAAE;AAC5G;AAEA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n claimCreateCreditMemoSchema,\n type ClaimCreateCreditMemoInput,\n} from '../../data/validators'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../commands/shared'\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 CreditMemoCommandResult = {\n claimId: string\n creditMemoId: string\n creditMemoUpdatedAt: string | null\n skippedLineIds: string[]\n grandTotalGrossAmount: string\n currencyCode: string\n}\n\nconst creditMemoRequestSchema = z\n .object({\n claimId: z.string().uuid(),\n updatedAt: z.string().datetime().nullable().optional(),\n })\n .strict()\n\ntype CreditMemoRequest = z.infer<typeof creditMemoRequestSchema>\n\nconst creditMemoResponseSchema = z.object({\n creditMemoId: z.string().uuid(),\n skippedLineIds: z.array(z.string().uuid()),\n grandTotalGrossAmount: z.string(),\n currencyCode: z.string(),\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.credit_memos.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nasync function resolveActionContext(\n req: Request,\n translate: ActionRouteContext['translate'],\n): Promise<ActionRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required.') })\n }\n return {\n ctx: {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: organizationId,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n translate,\n }\n}\n\nfunction toCreditMemoRequest(payload: Record<string, unknown>): CreditMemoRequest {\n return creditMemoRequestSchema.parse(payload)\n}\n\nfunction toCommandInput(\n input: CreditMemoRequest,\n context: ActionRouteContext,\n): ClaimCreateCreditMemoInput {\n const scopedPayload = withScopedPayload({\n id: input.claimId,\n updatedAt: input.updatedAt,\n }, context.ctx, context.translate)\n return claimCreateCreditMemoSchema.parse(scopedPayload)\n}\n\nasync function runGuard(\n req: Request,\n context: ActionRouteContext,\n input: CreditMemoRequest,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function POST(req: Request) {\n const { translate } = await resolveTranslations()\n try {\n const context = await resolveActionContext(req, translate)\n const requestInput = toCreditMemoRequest(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 ? toCreditMemoRequest(guarded.modifiedPayload)\n : requestInput\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<ClaimCreateCreditMemoInput, CreditMemoCommandResult>(\n 'warranty_claims.claim.create_credit_memo',\n { input: toCommandInput(guardedInput, context), ctx: context.ctx },\n )\n if (!result) throw new CrudHttpError(400, { error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') })\n await guarded.runAfterSuccess()\n return NextResponse.json({\n creditMemoId: result.creditMemoId,\n skippedLineIds: result.skippedLineIds,\n grandTotalGrossAmount: result.grandTotalGrossAmount,\n currencyCode: result.currencyCode,\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.credit-memo.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim.') }, { status: 500 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Create a credit memo from a warranty claim',\n methods: {\n POST: {\n summary: 'Create and link a credit memo from eligible received credit or refund lines',\n description: 'Amounts are prorated from source order-line totals. Version 1 does not prorate order-level adjustments.',\n requestBody: { contentType: 'application/json', schema: creditMemoRequestSchema },\n responses: [\n {\n status: 200,\n description: 'Credit memo created and linked; ineligible or unresolvable claim lines are reported as skipped',\n schema: creditMemoResponseSchema,\n },\n { status: 400, description: 'Invalid request or claim is not eligible for credit-memo 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
|
}
|
|
@@ -5,6 +5,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
6
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
7
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
|
+
import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
|
|
8
9
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
9
10
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
10
11
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
@@ -104,7 +105,7 @@ async function GET(req) {
|
|
|
104
105
|
const context = await resolveEventContext(req);
|
|
105
106
|
const url = new URL(req.url);
|
|
106
107
|
const query = eventListQuerySchema.parse({ claimId: url.searchParams.get("claimId") ?? void 0 });
|
|
107
|
-
const claim = await requireScopedClaim(context.em, query.claimId, context.scope);
|
|
108
|
+
const claim = await requireScopedClaim(context.em, query.claimId, context.scope, {}, context.translate("warranty_claims.errors.notFound", "Claim not found."));
|
|
108
109
|
const events = await findWithDecryption(
|
|
109
110
|
context.em,
|
|
110
111
|
WarrantyClaimEvent,
|
|
@@ -143,6 +144,10 @@ async function POST(req) {
|
|
|
143
144
|
return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.claimId });
|
|
144
145
|
} catch (err) {
|
|
145
146
|
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status });
|
|
147
|
+
const interceptorRejection = getCommandInterceptorHttpRejection(err);
|
|
148
|
+
if (interceptorRejection) {
|
|
149
|
+
return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
|
|
150
|
+
}
|
|
146
151
|
const { translate } = await resolveTranslations();
|
|
147
152
|
if (err instanceof z.ZodError) {
|
|
148
153
|
return NextResponse.json({ error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/warranty_claims/api/events/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaimEvent } from '../../data/entities'\nimport { commentClaimInputSchema, type CommentClaimInput } from '../../data/validators'\nimport { requireScopedClaim, WARRANTY_CLAIM_RESOURCE_KIND, type WarrantyClaimScope } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst eventListQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\ntype EventRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n scope: WarrantyClaimScope\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.claim.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n }\n return null\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorUserId: event.actorUserId ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolveEventContext(req: Request): Promise<EventRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = organizationScope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n const em = container.resolve('em') as EntityManager\n return {\n ctx: {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: organizationId,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n scope: { tenantId: auth.tenantId, organizationId },\n translate,\n em,\n }\n}\n\nasync function runCommentGuard(\n req: Request,\n context: EventRouteContext,\n input: CommentClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function GET(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const url = new URL(req.url)\n const query = eventListQuerySchema.parse({ claimId: url.searchParams.get('claimId') ?? undefined })\n const claim = await requireScopedClaim(context.em, query.claimId, context.scope)\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, tenantId: context.scope.tenantId, organizationId: context.scope.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n context.scope,\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.get failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.notFound', 'Warranty claim not found.') }, { status: 404 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = commentClaimInputSchema.parse(payload)\n const input: CommentClaimInput = { ...parsed, actorCustomerId: undefined }\n const guarded = await runCommentGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload\n ? { ...commentClaimInputSchema.parse(guarded.modifiedPayload), actorCustomerId: undefined }\n : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.string(),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorUserId: z.string().uuid().nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Warranty claim timeline events',\n methods: {\n GET: {\n summary: 'List timeline events for a claim',\n query: eventListQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a staff comment to a claim timeline',\n requestBody: { contentType: 'application/json', schema: commentClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaimEvent } from '../../data/entities'\nimport { commentClaimInputSchema, type CommentClaimInput } from '../../data/validators'\nimport { requireScopedClaim, WARRANTY_CLAIM_RESOURCE_KIND, type WarrantyClaimScope } from '../../commands/shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('warranty_claims')\n\nconst eventListQuerySchema = z.object({\n claimId: z.string().uuid(),\n})\n\ntype EventRouteContext = {\n ctx: CommandRuntimeContext\n tenantId: string\n organizationId: string\n scope: WarrantyClaimScope\n translate: (key: string, fallback?: string) => string\n em: EntityManager\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['warranty_claims.claim.view'] },\n POST: { requireAuth: true, requireFeatures: ['warranty_claims.claim.manage'] },\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction relationId(value: unknown): string | null {\n if (typeof value === 'string') return value\n const record = toRecord(value)\n return typeof record.id === 'string' ? record.id : null\n}\n\nfunction toIso(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n }\n return null\n}\n\nfunction serializeEvent(event: WarrantyClaimEvent) {\n return {\n id: event.id,\n claimId: relationId(event.claim),\n kind: event.kind,\n visibility: event.visibility,\n body: event.body ?? null,\n payload: event.payload ?? null,\n actorUserId: event.actorUserId ?? null,\n actorCustomerId: event.actorCustomerId ?? null,\n createdAt: toIso(event.createdAt),\n }\n}\n\nasync function resolveEventContext(req: Request): Promise<EventRouteContext> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth || !auth.tenantId) {\n throw new CrudHttpError(401, { error: translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const organizationId = organizationScope?.selectedId ?? auth.orgId ?? null\n if (!organizationId) {\n throw new CrudHttpError(400, { error: translate('warranty_claims.errors.organization_required', 'Organization context is required') })\n }\n const em = container.resolve('em') as EntityManager\n return {\n ctx: {\n container,\n auth,\n organizationScope,\n selectedOrganizationId: organizationId,\n organizationIds: organizationScope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n },\n tenantId: auth.tenantId,\n organizationId,\n scope: { tenantId: auth.tenantId, organizationId },\n translate,\n em,\n }\n}\n\nasync function runCommentGuard(\n req: Request,\n context: EventRouteContext,\n input: CommentClaimInput,\n): Promise<RouteMutationGuardResult> {\n const userId = context.ctx.auth?.sub\n if (!userId) {\n throw new CrudHttpError(401, { error: context.translate('warranty_claims.errors.unauthorized', 'Unauthorized') })\n }\n return runRouteMutationGuards({\n container: context.ctx.container,\n req,\n auth: {\n userId,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: input.claimId,\n operation: 'custom',\n mutationPayload: { ...input },\n },\n })\n}\n\nexport async function GET(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const url = new URL(req.url)\n const query = eventListQuerySchema.parse({ claimId: url.searchParams.get('claimId') ?? undefined })\n const claim = await requireScopedClaim(context.em, query.claimId, context.scope, {}, context.translate('warranty_claims.errors.notFound', 'Claim not found.'))\n const events = await findWithDecryption(\n context.em,\n WarrantyClaimEvent,\n { claim: claim.id, tenantId: context.scope.tenantId, organizationId: context.scope.organizationId },\n { orderBy: { createdAt: 'ASC' } },\n context.scope,\n )\n return NextResponse.json({ items: events.map(serializeEvent) })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.get failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.notFound', 'Warranty claim not found.') }, { status: 404 })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const context = await resolveEventContext(req)\n const payload = toRecord(await readJsonSafe(req, {}))\n const parsed = commentClaimInputSchema.parse(payload)\n const input: CommentClaimInput = { ...parsed, actorCustomerId: undefined }\n const guarded = await runCommentGuard(req, context, input)\n if (!guarded.ok) {\n return guarded.response\n }\n const commandInput = guarded.modifiedPayload\n ? { ...commentClaimInputSchema.parse(guarded.modifiedPayload), actorCustomerId: undefined }\n : input\n\n const commandBus = context.ctx.container.resolve('commandBus') as CommandBus\n const { result } = await commandBus.execute<CommentClaimInput, { claimId: string }>(\n 'warranty_claims.claim.comment',\n { input: commandInput, ctx: context.ctx },\n )\n\n await guarded.runAfterSuccess()\n\n return NextResponse.json({ ok: true, claimId: result?.claimId ?? commandInput.claimId })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n logger.error('warranty_claims.events.post failed', { err })\n return NextResponse.json({ error: translate('warranty_claims.errors.save_failed', 'Failed to save warranty claim') }, { status: 400 })\n }\n}\n\nconst eventSchema = z.object({\n id: z.string().uuid(),\n claimId: z.string().uuid().nullable(),\n kind: z.string(),\n visibility: z.string(),\n body: z.string().nullable(),\n payload: z.record(z.string(), z.unknown()).nullable(),\n actorUserId: z.string().uuid().nullable(),\n actorCustomerId: z.string().uuid().nullable(),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims',\n summary: 'Warranty claim timeline events',\n methods: {\n GET: {\n summary: 'List timeline events for a claim',\n query: eventListQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Timeline events',\n schema: z.object({ items: z.array(eventSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Append a staff comment to a claim timeline',\n requestBody: { contentType: 'application/json', schema: commentClaimInputSchema },\n responses: [\n {\n status: 200,\n description: 'Comment appended',\n schema: z.object({ ok: z.boolean(), claimId: z.string().uuid() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|