@open-mercato/core 0.6.6-develop.6429.1.2fba7258d1 → 0.6.6-develop.6450.1.b493fb430c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/customers/api/people/[id]/route.js +73 -2
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/planner/commands/shared.js +1 -8
- package/dist/modules/planner/commands/shared.js.map +2 -2
- package/dist/modules/resources/commands/shared.js +1 -7
- package/dist/modules/resources/commands/shared.js.map +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/points/route.js +2 -2
- package/dist/modules/shipping_carriers/api/points/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/rates/route.js +2 -2
- package/dist/modules/shipping_carriers/api/rates/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/route.js +2 -2
- package/dist/modules/shipping_carriers/api/tracking/route.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/upstream-error-response.js +13 -0
- package/dist/modules/shipping_carriers/lib/upstream-error-response.js.map +7 -0
- package/dist/modules/staff/commands/activities.js +46 -8
- package/dist/modules/staff/commands/activities.js.map +2 -2
- package/dist/modules/staff/commands/addresses.js +40 -7
- package/dist/modules/staff/commands/addresses.js.map +2 -2
- package/dist/modules/staff/commands/comments.js +46 -8
- package/dist/modules/staff/commands/comments.js.map +2 -2
- package/dist/modules/staff/commands/job-histories.js +40 -7
- package/dist/modules/staff/commands/job-histories.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +31 -9
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/dist/modules/staff/commands/shared.js +56 -7
- package/dist/modules/staff/commands/shared.js.map +2 -2
- package/dist/modules/staff/commands/tag-assignments.js +18 -7
- package/dist/modules/staff/commands/tag-assignments.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +16 -5
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/commands/team-roles.js +16 -5
- package/dist/modules/staff/commands/team-roles.js.map +2 -2
- package/dist/modules/staff/commands/teams.js +16 -5
- package/dist/modules/staff/commands/teams.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +17 -5
- package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-projects.js +22 -9
- package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
- package/dist/modules/sync_excel/lib/upload-storage.js +9 -6
- package/dist/modules/sync_excel/lib/upload-storage.js.map +2 -2
- package/dist/modules/translations/api/get/locales.js +2 -1
- package/dist/modules/translations/api/get/locales.js.map +2 -2
- package/dist/modules/translations/api/put/locales.js +1 -1
- package/dist/modules/translations/api/put/locales.js.map +2 -2
- package/dist/modules/workflows/examples/sales-pipeline-definition.json +8 -8
- package/dist/modules/workflows/lib/activity-executor.js +39 -3
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/api/people/[id]/route.ts +107 -2
- package/src/modules/planner/commands/shared.ts +2 -11
- package/src/modules/resources/commands/shared.ts +2 -10
- package/src/modules/shipping_carriers/api/cancel/route.ts +2 -2
- package/src/modules/shipping_carriers/api/points/route.ts +2 -2
- package/src/modules/shipping_carriers/api/rates/route.ts +2 -2
- package/src/modules/shipping_carriers/api/shipments/route.ts +2 -2
- package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +2 -2
- package/src/modules/shipping_carriers/api/tracking/route.ts +2 -2
- package/src/modules/shipping_carriers/i18n/de.json +1 -0
- package/src/modules/shipping_carriers/i18n/en.json +1 -0
- package/src/modules/shipping_carriers/i18n/es.json +1 -0
- package/src/modules/shipping_carriers/i18n/pl.json +1 -0
- package/src/modules/shipping_carriers/lib/upstream-error-response.ts +11 -0
- package/src/modules/staff/commands/activities.ts +46 -8
- package/src/modules/staff/commands/addresses.ts +40 -7
- package/src/modules/staff/commands/comments.ts +46 -8
- package/src/modules/staff/commands/job-histories.ts +40 -7
- package/src/modules/staff/commands/leave-requests.ts +36 -9
- package/src/modules/staff/commands/shared.ts +71 -8
- package/src/modules/staff/commands/tag-assignments.ts +18 -7
- package/src/modules/staff/commands/team-members.ts +16 -5
- package/src/modules/staff/commands/team-roles.ts +16 -5
- package/src/modules/staff/commands/teams.ts +16 -5
- package/src/modules/staff/commands/timesheets-entries.ts +17 -5
- package/src/modules/staff/commands/timesheets-projects.ts +22 -9
- package/src/modules/sync_excel/lib/upload-storage.ts +8 -6
- package/src/modules/translations/api/get/locales.ts +1 -0
- package/src/modules/translations/api/put/locales.ts +1 -1
- package/src/modules/workflows/AGENTS.md +2 -1
- package/src/modules/workflows/examples/sales-pipeline-definition.json +8 -8
- package/src/modules/workflows/lib/activity-executor.ts +49 -5
|
@@ -42,6 +42,8 @@ import { loadPersonCompanyLinks, summarizePersonCompanies } from '../../../lib/p
|
|
|
42
42
|
import { normalizeCustomerDetailCustomFields } from '../../detailCustomFields'
|
|
43
43
|
import { buildEmailVisibilityMikroFilter } from '../../../lib/visibilityFilter'
|
|
44
44
|
import { resolveCustomerDetailTenantScope } from '../../../lib/detailTenantScope'
|
|
45
|
+
import { runWithCacheTenant } from '@open-mercato/cache'
|
|
46
|
+
import { buildCollectionTags, canonicalizeResourceTag, isCrudCacheEnabled, resolveCrudCache } from '@open-mercato/shared/lib/crud/cache'
|
|
45
47
|
|
|
46
48
|
export const metadata = {
|
|
47
49
|
GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },
|
|
@@ -51,6 +53,63 @@ const paramsSchema = z.object({
|
|
|
51
53
|
id: z.string().uuid(),
|
|
52
54
|
})
|
|
53
55
|
|
|
56
|
+
// Person detail is one of the heaviest custom GETs (decryption sweeps across
|
|
57
|
+
// addresses, tags, labels, interactions, custom fields, plus per-link query
|
|
58
|
+
// engine enrichment). Mirror the gated get-then-set cache from the roles list
|
|
59
|
+
// (#3143) so a hit skips every sweep below. Writes flow through the customers
|
|
60
|
+
// command bus, which already flushes the matching crud:<resourceKind> collection
|
|
61
|
+
// tags post-commit — these set tags reuse the exact same shapes (see
|
|
62
|
+
// invalidateCrudCache), so no new invalidation wiring is needed (#3663).
|
|
63
|
+
// The resource ids below are canonicalized with canonicalizeResourceTag (just
|
|
64
|
+
// like invalidateCrudCache does via expandResourceAliases) before building the
|
|
65
|
+
// collection tags, so camelCase ids (tagAssignment, labelAssignment,
|
|
66
|
+
// personCompanyLink) produce the SAME tag the command bus deletes on
|
|
67
|
+
// write/undo/redo — otherwise the cache would never be invalidated for them.
|
|
68
|
+
const PERSON_DETAIL_TTL_MS = 60_000
|
|
69
|
+
const PERSON_DETAIL_TAG_RESOURCES = [
|
|
70
|
+
'customers.person',
|
|
71
|
+
'customers.address',
|
|
72
|
+
'customers.tagAssignment',
|
|
73
|
+
'customers.labelAssignment',
|
|
74
|
+
'customers.personCompanyLink',
|
|
75
|
+
'customers.interaction',
|
|
76
|
+
'customers.activity',
|
|
77
|
+
] as const
|
|
78
|
+
|
|
79
|
+
function buildPersonDetailCacheKey(params: {
|
|
80
|
+
personId: string
|
|
81
|
+
tenantId: string | null
|
|
82
|
+
organizationId: string | null
|
|
83
|
+
callerId: string | null
|
|
84
|
+
selectedOrganizationId: string | null
|
|
85
|
+
scopedOrganizationIds: string[]
|
|
86
|
+
interactionMode: 'canonical' | 'legacy'
|
|
87
|
+
includeTokens: string[]
|
|
88
|
+
}): string {
|
|
89
|
+
const include = [...params.includeTokens].sort((a, b) => a.localeCompare(b)).join('|') || 'none'
|
|
90
|
+
const scoped = [...params.scopedOrganizationIds].sort((a, b) => a.localeCompare(b)).join('|') || 'null'
|
|
91
|
+
return [
|
|
92
|
+
'customers:person-detail',
|
|
93
|
+
params.personId,
|
|
94
|
+
`tenant=${params.tenantId ?? 'null'}`,
|
|
95
|
+
`org=${params.organizationId ?? 'null'}`,
|
|
96
|
+
`caller=${params.callerId ?? 'null'}`,
|
|
97
|
+
`selOrg=${params.selectedOrganizationId ?? 'null'}`,
|
|
98
|
+
`scope=${scoped}`,
|
|
99
|
+
`mode=${params.interactionMode}`,
|
|
100
|
+
`include=${include}`,
|
|
101
|
+
].join(':')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildPersonDetailCacheTags(tenantId: string | null, organizationId: string | null): string[] {
|
|
105
|
+
const tags: string[] = []
|
|
106
|
+
for (const resource of PERSON_DETAIL_TAG_RESOURCES) {
|
|
107
|
+
const canonical = canonicalizeResourceTag(resource) ?? resource
|
|
108
|
+
tags.push(...buildCollectionTags(canonical, tenantId, [organizationId]))
|
|
109
|
+
}
|
|
110
|
+
return tags
|
|
111
|
+
}
|
|
112
|
+
|
|
54
113
|
function parseIncludeParams(request: Request): Set<string> {
|
|
55
114
|
const url = new URL(request.url)
|
|
56
115
|
const raw = url.searchParams.getAll('include')
|
|
@@ -485,6 +544,39 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
485
544
|
return forbidden('Access denied')
|
|
486
545
|
}
|
|
487
546
|
|
|
547
|
+
// Gated get-then-set cache. Key on the resolved person plus the effective
|
|
548
|
+
// RBAC scope (caller identity, selected/visible orgs) and the payload-shaping
|
|
549
|
+
// inputs (interaction mode + include set). The lookup sits before the
|
|
550
|
+
// expensive sweeps below so a hit returns without touching the DB.
|
|
551
|
+
const personDetailTenantId = person.tenantId ?? auth.tenantId ?? null
|
|
552
|
+
const personDetailOrganizationId = person.organizationId ?? null
|
|
553
|
+
const personDetailCache = isCrudCacheEnabled() ? resolveCrudCache(container) : null
|
|
554
|
+
const personDetailCacheKey = personDetailCache
|
|
555
|
+
? buildPersonDetailCacheKey({
|
|
556
|
+
personId: person.id,
|
|
557
|
+
tenantId: personDetailTenantId,
|
|
558
|
+
organizationId: personDetailOrganizationId,
|
|
559
|
+
callerId: auth.sub ?? null,
|
|
560
|
+
selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,
|
|
561
|
+
scopedOrganizationIds: Array.isArray(scope?.filterIds) ? scope.filterIds : [],
|
|
562
|
+
interactionMode,
|
|
563
|
+
includeTokens: Array.from(includeTokens),
|
|
564
|
+
})
|
|
565
|
+
: null
|
|
566
|
+
const personDetailCacheTags = personDetailCache
|
|
567
|
+
? buildPersonDetailCacheTags(personDetailTenantId, personDetailOrganizationId)
|
|
568
|
+
: []
|
|
569
|
+
|
|
570
|
+
if (personDetailCache && personDetailCacheKey) {
|
|
571
|
+
const cached = await runWithCacheTenant(personDetailTenantId, () => personDetailCache.get(personDetailCacheKey))
|
|
572
|
+
if (cached) {
|
|
573
|
+
statusCode = 200
|
|
574
|
+
profileMeta = { cacheHit: true, include: Array.from(includeTokens), interactionMode }
|
|
575
|
+
profiler.mark('cache_hit')
|
|
576
|
+
return NextResponse.json(cached)
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
488
580
|
const [profileResult, personCompanyLinks] = await Promise.all([
|
|
489
581
|
findOneWithDecryption(em, CustomerPersonProfile, { entity: person }, { populate: ['company'] }, { tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null }),
|
|
490
582
|
loadPersonCompanyLinks(em, person),
|
|
@@ -801,7 +893,7 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
801
893
|
companies: companies.length,
|
|
802
894
|
}
|
|
803
895
|
|
|
804
|
-
const
|
|
896
|
+
const payload = {
|
|
805
897
|
interactionMode,
|
|
806
898
|
person: {
|
|
807
899
|
id: person.id,
|
|
@@ -1011,7 +1103,20 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
1011
1103
|
name: viewerUserIdFinal ? userMap.get(viewerUserIdFinal)?.name ?? null : null,
|
|
1012
1104
|
email: viewerUserIdFinal ? userMap.get(viewerUserIdFinal)?.email ?? auth.email ?? null : auth.email ?? null,
|
|
1013
1105
|
},
|
|
1014
|
-
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (personDetailCache && personDetailCacheKey) {
|
|
1109
|
+
try {
|
|
1110
|
+
await runWithCacheTenant(personDetailTenantId, () =>
|
|
1111
|
+
personDetailCache.set(personDetailCacheKey, payload, {
|
|
1112
|
+
ttl: PERSON_DETAIL_TTL_MS,
|
|
1113
|
+
tags: personDetailCacheTags,
|
|
1114
|
+
}),
|
|
1115
|
+
)
|
|
1116
|
+
} catch {}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const response = NextResponse.json(payload)
|
|
1015
1120
|
statusCode = 200
|
|
1016
1121
|
profileMeta = {
|
|
1017
1122
|
include: Array.from(includeTokens),
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
3
|
-
import { ensureOrganizationScope } from '@open-mercato/shared/lib/commands/scope'
|
|
1
|
+
import { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'
|
|
4
2
|
import { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
|
|
5
3
|
|
|
6
|
-
export
|
|
7
|
-
const currentTenant = ctx.auth?.tenantId ?? null
|
|
8
|
-
if (currentTenant && currentTenant !== tenantId) {
|
|
9
|
-
throw new CrudHttpError(403, { error: 'Forbidden' })
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export { ensureOrganizationScope, extractUndoPayload }
|
|
4
|
+
export { ensureOrganizationScope, ensureTenantScope, extractUndoPayload }
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
2
1
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
3
|
-
import { ensureOrganizationScope } from '@open-mercato/shared/lib/commands/scope'
|
|
2
|
+
import { ensureOrganizationScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'
|
|
4
3
|
import { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
|
|
5
4
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
5
|
import { ResourcesResource } from '../data/entities'
|
|
7
6
|
|
|
8
|
-
export
|
|
9
|
-
const currentTenant = ctx.auth?.tenantId ?? null
|
|
10
|
-
if (currentTenant && currentTenant !== tenantId) {
|
|
11
|
-
throw new CrudHttpError(403, { error: 'Forbidden' })
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export { ensureOrganizationScope, extractUndoPayload }
|
|
7
|
+
export { ensureOrganizationScope, ensureTenantScope, extractUndoPayload }
|
|
16
8
|
|
|
17
9
|
export async function requireResource(
|
|
18
10
|
em: EntityManager,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
9
9
|
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
10
10
|
import { isShipmentCancelNotAllowedError } from '../../lib/status-sync'
|
|
11
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../lib/upstream-error-response'
|
|
11
12
|
import { cancelShipmentSchema } from '../../data/validators'
|
|
12
13
|
import { shippingCarriersTag } from '../openapi'
|
|
13
14
|
|
|
@@ -81,8 +82,7 @@ export async function POST(req: Request) {
|
|
|
81
82
|
if (isShipmentCancelNotAllowedError(error)) {
|
|
82
83
|
return NextResponse.json({ error: error.message }, { status: 422 })
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
85
|
+
return shippingCarrierUpstreamErrorResponse('cancel.post', error)
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
|
|
2
2
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
3
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
4
|
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
5
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../lib/upstream-error-response'
|
|
5
6
|
import { searchDropOffPointsQuerySchema } from '../../data/validators'
|
|
6
7
|
import { shippingCarriersTag } from '../openapi'
|
|
7
8
|
|
|
@@ -35,8 +36,7 @@ export async function GET(req: Request) {
|
|
|
35
36
|
})
|
|
36
37
|
return NextResponse.json({ points })
|
|
37
38
|
} catch (error: unknown) {
|
|
38
|
-
|
|
39
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
39
|
+
return shippingCarrierUpstreamErrorResponse('points.search', error)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -3,6 +3,7 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
|
3
3
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
4
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
5
|
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
6
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../lib/upstream-error-response'
|
|
6
7
|
import { calculateRatesSchema } from '../../data/validators'
|
|
7
8
|
import { shippingCarriersTag } from '../openapi'
|
|
8
9
|
|
|
@@ -31,8 +32,7 @@ export async function POST(req: Request) {
|
|
|
31
32
|
})
|
|
32
33
|
return NextResponse.json({ rates })
|
|
33
34
|
} catch (error: unknown) {
|
|
34
|
-
|
|
35
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
35
|
+
return shippingCarrierUpstreamErrorResponse('rates.calculate', error)
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
9
9
|
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
10
10
|
import { isShipmentIdempotencyConflictError } from '../../lib/shipment-idempotency'
|
|
11
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../lib/upstream-error-response'
|
|
11
12
|
import { createShipmentSchema } from '../../data/validators'
|
|
12
13
|
import { shippingCarriersTag } from '../openapi'
|
|
13
14
|
|
|
@@ -90,8 +91,7 @@ export async function POST(req: Request) {
|
|
|
90
91
|
{ status: 409 },
|
|
91
92
|
)
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
94
|
+
return shippingCarrierUpstreamErrorResponse('shipments.create', error)
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -3,6 +3,7 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
|
3
3
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
4
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
5
5
|
import type { ShippingCarrierService } from '../../../lib/shipping-service'
|
|
6
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../../lib/upstream-error-response'
|
|
6
7
|
import { trackingRefreshSchema } from '../../../data/validators'
|
|
7
8
|
import { shippingCarriersTag } from '../../openapi'
|
|
8
9
|
|
|
@@ -31,8 +32,7 @@ export async function POST(req: Request) {
|
|
|
31
32
|
})
|
|
32
33
|
return NextResponse.json(tracking)
|
|
33
34
|
} catch (error: unknown) {
|
|
34
|
-
|
|
35
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
35
|
+
return shippingCarrierUpstreamErrorResponse('tracking.refresh', error)
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
|
|
2
2
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
3
3
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
4
|
import type { ShippingCarrierService } from '../../lib/shipping-service'
|
|
5
|
+
import { shippingCarrierUpstreamErrorResponse } from '../../lib/upstream-error-response'
|
|
5
6
|
import { trackingQuerySchema } from '../../data/validators'
|
|
6
7
|
import { shippingCarriersTag } from '../openapi'
|
|
7
8
|
|
|
@@ -34,8 +35,7 @@ export async function GET(req: Request) {
|
|
|
34
35
|
})
|
|
35
36
|
return NextResponse.json(tracking)
|
|
36
37
|
} catch (error: unknown) {
|
|
37
|
-
|
|
38
|
-
return NextResponse.json({ error: message }, { status: 502 })
|
|
38
|
+
return shippingCarrierUpstreamErrorResponse('tracking.get', error)
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"shipping_carriers.create.summary.targetPoint": "Paketbox",
|
|
70
70
|
"shipping_carriers.create.summary.to": "Nach",
|
|
71
71
|
"shipping_carriers.create.title": "Sendung beim Transporteur erstellen",
|
|
72
|
+
"shipping_carriers.errors.upstreamFailed": "Anfrage an den Transporteur fehlgeschlagen. Versuchen Sie es spaeter erneut.",
|
|
72
73
|
"shipping_carriers.feature.manage": "Versanddienstleister-Vorgaenge verwalten",
|
|
73
74
|
"shipping_carriers.feature.view": "Sendungen der Versanddienstleister anzeigen",
|
|
74
75
|
"shipping_carriers.status.cancelled": "Storniert",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"shipping_carriers.create.summary.targetPoint": "Locker point",
|
|
70
70
|
"shipping_carriers.create.summary.to": "To",
|
|
71
71
|
"shipping_carriers.create.title": "Create carrier shipment",
|
|
72
|
+
"shipping_carriers.errors.upstreamFailed": "Carrier provider request failed. Try again later.",
|
|
72
73
|
"shipping_carriers.feature.manage": "Manage shipping carrier operations",
|
|
73
74
|
"shipping_carriers.feature.view": "View shipping carrier shipments",
|
|
74
75
|
"shipping_carriers.status.cancelled": "Cancelled",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"shipping_carriers.create.summary.targetPoint": "Casillero",
|
|
70
70
|
"shipping_carriers.create.summary.to": "Hasta",
|
|
71
71
|
"shipping_carriers.create.title": "Crear envio con transportista",
|
|
72
|
+
"shipping_carriers.errors.upstreamFailed": "Error en la solicitud al transportista. Intentalo de nuevo mas tarde.",
|
|
72
73
|
"shipping_carriers.feature.manage": "Gestionar operaciones de transportistas",
|
|
73
74
|
"shipping_carriers.feature.view": "Ver envios de transportistas",
|
|
74
75
|
"shipping_carriers.status.cancelled": "Cancelado",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"shipping_carriers.create.summary.targetPoint": "Paczkomat",
|
|
70
70
|
"shipping_carriers.create.summary.to": "Odbiór",
|
|
71
71
|
"shipping_carriers.create.title": "Utwórz przesyłkę u przewoźnika",
|
|
72
|
+
"shipping_carriers.errors.upstreamFailed": "Żądanie do przewoźnika nie powiodło się. Spróbuj ponownie później.",
|
|
72
73
|
"shipping_carriers.feature.manage": "Zarzadzanie operacjami przewoznikow",
|
|
73
74
|
"shipping_carriers.feature.view": "Podglad przesylek przewoznikow",
|
|
74
75
|
"shipping_carriers.status.cancelled": "Anulowano",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
3
|
+
|
|
4
|
+
const upstreamFailedKey = 'shipping_carriers.errors.upstreamFailed'
|
|
5
|
+
const upstreamFailedFallback = 'Carrier provider request failed. Try again later.'
|
|
6
|
+
|
|
7
|
+
export async function shippingCarrierUpstreamErrorResponse(routeId: string, error: unknown) {
|
|
8
|
+
console.error(`[shipping_carriers.${routeId}] provider upstream error`, error)
|
|
9
|
+
const { translate } = await resolveTranslations()
|
|
10
|
+
return NextResponse.json({ error: translate(upstreamFailedKey, upstreamFailedFallback) }, { status: 502 })
|
|
11
|
+
}
|
|
@@ -22,7 +22,16 @@ import {
|
|
|
22
22
|
type StaffTeamMemberActivityUpdateInput,
|
|
23
23
|
} from '../data/validators'
|
|
24
24
|
import { staffTeamMemberActivityCrudEvents } from '../lib/crud'
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
applyScopeToWhere,
|
|
27
|
+
commandActorScope,
|
|
28
|
+
commandInputScope,
|
|
29
|
+
ensureOrganizationScope,
|
|
30
|
+
ensureTenantScope,
|
|
31
|
+
explicitStaffCommandScope,
|
|
32
|
+
extractUndoPayload,
|
|
33
|
+
requireTeamMember,
|
|
34
|
+
} from './shared'
|
|
26
35
|
import { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'
|
|
27
36
|
import { E } from '#generated/entities.ids.generated'
|
|
28
37
|
import {
|
|
@@ -118,10 +127,16 @@ const createActivityCommand: CommandHandler<
|
|
|
118
127
|
const { parsed, custom } = parseWithCustomFields(staffTeamMemberActivityCreateSchema, rawInput)
|
|
119
128
|
ensureTenantScope(ctx, parsed.tenantId)
|
|
120
129
|
ensureOrganizationScope(ctx, parsed.organizationId)
|
|
130
|
+
const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)
|
|
121
131
|
const normalizedAuthor = normalizeAuthorUserId(parsed.authorUserId, ctx.auth)
|
|
122
132
|
|
|
123
133
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
124
|
-
const member = await requireTeamMember(
|
|
134
|
+
const member = await requireTeamMember(
|
|
135
|
+
em,
|
|
136
|
+
parsed.entityId,
|
|
137
|
+
scope,
|
|
138
|
+
'Team member not found',
|
|
139
|
+
)
|
|
125
140
|
ensureTenantScope(ctx, member.tenantId)
|
|
126
141
|
ensureOrganizationScope(ctx, member.organizationId)
|
|
127
142
|
|
|
@@ -199,7 +214,12 @@ const createActivityCommand: CommandHandler<
|
|
|
199
214
|
throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for activity create' })
|
|
200
215
|
}
|
|
201
216
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
202
|
-
const member = await requireTeamMember(
|
|
217
|
+
const member = await requireTeamMember(
|
|
218
|
+
em,
|
|
219
|
+
after.activity.memberId,
|
|
220
|
+
explicitStaffCommandScope(after.activity.tenantId, after.activity.organizationId),
|
|
221
|
+
'Team member not found',
|
|
222
|
+
)
|
|
203
223
|
let activity = await em.findOne(StaffTeamMemberActivity, { id: after.activity.id })
|
|
204
224
|
if (!activity) {
|
|
205
225
|
activity = em.create(StaffTeamMemberActivity, {
|
|
@@ -261,13 +281,17 @@ const updateActivityCommand: CommandHandler<StaffTeamMemberActivityUpdateInput,
|
|
|
261
281
|
async execute(rawInput, ctx) {
|
|
262
282
|
const { parsed, custom } = parseWithCustomFields(staffTeamMemberActivityUpdateSchema, rawInput)
|
|
263
283
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
264
|
-
const
|
|
284
|
+
const scope = commandActorScope(ctx)
|
|
285
|
+
const activity = await em.findOne(
|
|
286
|
+
StaffTeamMemberActivity,
|
|
287
|
+
applyScopeToWhere<StaffTeamMemberActivity>({ id: parsed.id }, scope),
|
|
288
|
+
)
|
|
265
289
|
if (!activity) throw new CrudHttpError(404, { error: 'Activity not found' })
|
|
266
290
|
ensureTenantScope(ctx, activity.tenantId)
|
|
267
291
|
ensureOrganizationScope(ctx, activity.organizationId)
|
|
268
292
|
|
|
269
293
|
if (parsed.entityId !== undefined) {
|
|
270
|
-
const member = await requireTeamMember(em, parsed.entityId, 'Team member not found')
|
|
294
|
+
const member = await requireTeamMember(em, parsed.entityId, scope, 'Team member not found')
|
|
271
295
|
ensureTenantScope(ctx, member.tenantId)
|
|
272
296
|
ensureOrganizationScope(ctx, member.organizationId)
|
|
273
297
|
activity.member = member
|
|
@@ -354,7 +378,12 @@ const updateActivityCommand: CommandHandler<StaffTeamMemberActivityUpdateInput,
|
|
|
354
378
|
if (!before) return
|
|
355
379
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
356
380
|
let activity = await em.findOne(StaffTeamMemberActivity, { id: before.activity.id })
|
|
357
|
-
const member = await requireTeamMember(
|
|
381
|
+
const member = await requireTeamMember(
|
|
382
|
+
em,
|
|
383
|
+
before.activity.memberId,
|
|
384
|
+
explicitStaffCommandScope(before.activity.tenantId, before.activity.organizationId),
|
|
385
|
+
'Team member not found',
|
|
386
|
+
)
|
|
358
387
|
|
|
359
388
|
if (!activity) {
|
|
360
389
|
activity = em.create(StaffTeamMemberActivity, {
|
|
@@ -426,7 +455,11 @@ const deleteActivityCommand: CommandHandler<{ body?: Record<string, unknown>; qu
|
|
|
426
455
|
async execute(input, ctx) {
|
|
427
456
|
const id = requireId(input, 'Activity id required')
|
|
428
457
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
429
|
-
const
|
|
458
|
+
const scope = commandActorScope(ctx)
|
|
459
|
+
const activity = await em.findOne(
|
|
460
|
+
StaffTeamMemberActivity,
|
|
461
|
+
applyScopeToWhere<StaffTeamMemberActivity>({ id }, scope),
|
|
462
|
+
)
|
|
430
463
|
if (!activity) throw new CrudHttpError(404, { error: 'Activity not found' })
|
|
431
464
|
ensureTenantScope(ctx, activity.tenantId)
|
|
432
465
|
ensureOrganizationScope(ctx, activity.organizationId)
|
|
@@ -473,7 +506,12 @@ const deleteActivityCommand: CommandHandler<{ body?: Record<string, unknown>; qu
|
|
|
473
506
|
const before = payload?.before
|
|
474
507
|
if (!before) return
|
|
475
508
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
476
|
-
const member = await requireTeamMember(
|
|
509
|
+
const member = await requireTeamMember(
|
|
510
|
+
em,
|
|
511
|
+
before.activity.memberId,
|
|
512
|
+
explicitStaffCommandScope(before.activity.tenantId, before.activity.organizationId),
|
|
513
|
+
'Team member not found',
|
|
514
|
+
)
|
|
477
515
|
let activity = await em.findOne(StaffTeamMemberActivity, { id: before.activity.id })
|
|
478
516
|
if (!activity) {
|
|
479
517
|
activity = em.create(StaffTeamMemberActivity, {
|
|
@@ -12,8 +12,12 @@ import {
|
|
|
12
12
|
} from '../data/validators'
|
|
13
13
|
import { staffTeamMemberAddressCrudEvents } from '../lib/crud'
|
|
14
14
|
import {
|
|
15
|
+
applyScopeToWhere,
|
|
16
|
+
commandActorScope,
|
|
17
|
+
commandInputScope,
|
|
15
18
|
ensureOrganizationScope,
|
|
16
19
|
ensureTenantScope,
|
|
20
|
+
explicitStaffCommandScope,
|
|
17
21
|
extractUndoPayload,
|
|
18
22
|
requireTeamMember,
|
|
19
23
|
} from './shared'
|
|
@@ -92,9 +96,15 @@ const createAddressCommand: CommandHandler<StaffTeamMemberAddressCreateInput, {
|
|
|
92
96
|
const parsed = staffTeamMemberAddressCreateSchema.parse(rawInput)
|
|
93
97
|
ensureTenantScope(ctx, parsed.tenantId)
|
|
94
98
|
ensureOrganizationScope(ctx, parsed.organizationId)
|
|
99
|
+
const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)
|
|
95
100
|
|
|
96
101
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
97
|
-
const member = await requireTeamMember(
|
|
102
|
+
const member = await requireTeamMember(
|
|
103
|
+
em,
|
|
104
|
+
parsed.entityId,
|
|
105
|
+
scope,
|
|
106
|
+
'Team member not found',
|
|
107
|
+
)
|
|
98
108
|
ensureTenantScope(ctx, member.tenantId)
|
|
99
109
|
ensureOrganizationScope(ctx, member.organizationId)
|
|
100
110
|
|
|
@@ -182,7 +192,12 @@ const createAddressCommand: CommandHandler<StaffTeamMemberAddressCreateInput, {
|
|
|
182
192
|
throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for address create' })
|
|
183
193
|
}
|
|
184
194
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
185
|
-
const member = await requireTeamMember(
|
|
195
|
+
const member = await requireTeamMember(
|
|
196
|
+
em,
|
|
197
|
+
after.memberId,
|
|
198
|
+
explicitStaffCommandScope(after.tenantId, after.organizationId),
|
|
199
|
+
'Team member not found',
|
|
200
|
+
)
|
|
186
201
|
let address = await em.findOne(StaffTeamMemberAddress, { id: after.id })
|
|
187
202
|
if (!address) {
|
|
188
203
|
address = em.create(StaffTeamMemberAddress, {
|
|
@@ -260,13 +275,17 @@ const updateAddressCommand: CommandHandler<StaffTeamMemberAddressUpdateInput, {
|
|
|
260
275
|
async execute(rawInput, ctx) {
|
|
261
276
|
const parsed = staffTeamMemberAddressUpdateSchema.parse(rawInput)
|
|
262
277
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
263
|
-
const
|
|
278
|
+
const scope = commandActorScope(ctx)
|
|
279
|
+
const address = await em.findOne(
|
|
280
|
+
StaffTeamMemberAddress,
|
|
281
|
+
applyScopeToWhere<StaffTeamMemberAddress>({ id: parsed.id }, scope),
|
|
282
|
+
)
|
|
264
283
|
if (!address) throw new CrudHttpError(404, { error: 'Address not found' })
|
|
265
284
|
ensureTenantScope(ctx, address.tenantId)
|
|
266
285
|
ensureOrganizationScope(ctx, address.organizationId)
|
|
267
286
|
|
|
268
287
|
if (parsed.entityId !== undefined) {
|
|
269
|
-
const member = await requireTeamMember(em, parsed.entityId, 'Team member not found')
|
|
288
|
+
const member = await requireTeamMember(em, parsed.entityId, scope, 'Team member not found')
|
|
270
289
|
ensureTenantScope(ctx, member.tenantId)
|
|
271
290
|
ensureOrganizationScope(ctx, member.organizationId)
|
|
272
291
|
address.member = member
|
|
@@ -367,7 +386,12 @@ const updateAddressCommand: CommandHandler<StaffTeamMemberAddressUpdateInput, {
|
|
|
367
386
|
if (!before) return
|
|
368
387
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
369
388
|
let address = await em.findOne(StaffTeamMemberAddress, { id: before.id })
|
|
370
|
-
const member = await requireTeamMember(
|
|
389
|
+
const member = await requireTeamMember(
|
|
390
|
+
em,
|
|
391
|
+
before.memberId,
|
|
392
|
+
explicitStaffCommandScope(before.tenantId, before.organizationId),
|
|
393
|
+
'Team member not found',
|
|
394
|
+
)
|
|
371
395
|
|
|
372
396
|
if (!address) {
|
|
373
397
|
address = em.create(StaffTeamMemberAddress, {
|
|
@@ -444,7 +468,11 @@ const deleteAddressCommand: CommandHandler<{ body?: Record<string, unknown>; que
|
|
|
444
468
|
async execute(input, ctx) {
|
|
445
469
|
const id = requireId(input, 'Address id required')
|
|
446
470
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
447
|
-
const
|
|
471
|
+
const scope = commandActorScope(ctx)
|
|
472
|
+
const address = await em.findOne(
|
|
473
|
+
StaffTeamMemberAddress,
|
|
474
|
+
applyScopeToWhere<StaffTeamMemberAddress>({ id }, scope),
|
|
475
|
+
)
|
|
448
476
|
if (!address) throw new CrudHttpError(404, { error: 'Address not found' })
|
|
449
477
|
ensureTenantScope(ctx, address.tenantId)
|
|
450
478
|
ensureOrganizationScope(ctx, address.organizationId)
|
|
@@ -491,7 +519,12 @@ const deleteAddressCommand: CommandHandler<{ body?: Record<string, unknown>; que
|
|
|
491
519
|
const before = payload?.before
|
|
492
520
|
if (!before) return
|
|
493
521
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
494
|
-
const member = await requireTeamMember(
|
|
522
|
+
const member = await requireTeamMember(
|
|
523
|
+
em,
|
|
524
|
+
before.memberId,
|
|
525
|
+
explicitStaffCommandScope(before.tenantId, before.organizationId),
|
|
526
|
+
'Team member not found',
|
|
527
|
+
)
|
|
495
528
|
let address = await em.findOne(StaffTeamMemberAddress, { id: before.id })
|
|
496
529
|
if (!address) {
|
|
497
530
|
address = em.create(StaffTeamMemberAddress, {
|