@open-mercato/core 0.6.4-develop.4015.1.efaafadf79 → 0.6.4-develop.4038.1.91ce075c8a
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.
|
@@ -44,6 +44,139 @@ const toNumber = (value) => {
|
|
|
44
44
|
}
|
|
45
45
|
return 0;
|
|
46
46
|
};
|
|
47
|
+
async function enrichShipmentListResponse(payload, ctx) {
|
|
48
|
+
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
49
|
+
if (!items.length) return;
|
|
50
|
+
const snapshotMap = /* @__PURE__ */ new Map();
|
|
51
|
+
items.forEach((item) => {
|
|
52
|
+
if (!item || typeof item !== "object") return;
|
|
53
|
+
const id = item.id;
|
|
54
|
+
if (typeof id !== "string") return;
|
|
55
|
+
const snapshot = readShipmentItemsSnapshot(
|
|
56
|
+
item.items_snapshot ?? item.itemsSnapshot ?? null
|
|
57
|
+
);
|
|
58
|
+
if (snapshot.length) {
|
|
59
|
+
snapshotMap.set(id, snapshot);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const shipmentIds = items.map((item) => {
|
|
63
|
+
if (!item || typeof item !== "object") return null;
|
|
64
|
+
const raw = item.id;
|
|
65
|
+
return typeof raw === "string" ? raw : null;
|
|
66
|
+
}).filter((value) => typeof value === "string");
|
|
67
|
+
if (!shipmentIds.length) return;
|
|
68
|
+
const em = ctx.container.resolve("em");
|
|
69
|
+
const statusIds = Array.from(
|
|
70
|
+
new Set(
|
|
71
|
+
items.map((item) => {
|
|
72
|
+
if (!item || typeof item !== "object") return null;
|
|
73
|
+
const raw = item.status_entry_id;
|
|
74
|
+
return typeof raw === "string" ? raw : null;
|
|
75
|
+
}).filter((value) => typeof value === "string" && value.length > 0)
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
const [shipmentItems, shippingMethods, statusEntries] = await Promise.all([
|
|
79
|
+
findWithDecryption(
|
|
80
|
+
em,
|
|
81
|
+
SalesShipmentItem,
|
|
82
|
+
{ shipment: { $in: shipmentIds } },
|
|
83
|
+
{ populate: ["orderLine"] },
|
|
84
|
+
{ tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
|
|
85
|
+
),
|
|
86
|
+
(async () => {
|
|
87
|
+
const ids = Array.from(
|
|
88
|
+
new Set(
|
|
89
|
+
items.map((item) => {
|
|
90
|
+
if (!item || typeof item !== "object") return null;
|
|
91
|
+
const raw = item.shipping_method_id;
|
|
92
|
+
return typeof raw === "string" ? raw : null;
|
|
93
|
+
}).filter((value) => typeof value === "string" && value.length > 0)
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
if (!ids.length) return [];
|
|
97
|
+
return em.find(SalesShippingMethod, { id: { $in: ids } });
|
|
98
|
+
})(),
|
|
99
|
+
statusIds.length ? em.find(DictionaryEntry, { id: { $in: statusIds } }) : []
|
|
100
|
+
]);
|
|
101
|
+
const orderLineIds = Array.from(
|
|
102
|
+
new Set(
|
|
103
|
+
shipmentItems.map(
|
|
104
|
+
(entry) => typeof entry.orderLine === "string" ? entry.orderLine : entry.orderLine?.id ?? entry.orderLineId ?? null
|
|
105
|
+
).filter((value) => typeof value === "string")
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
const orderLines = orderLineIds.length ? await em.find(SalesOrderLine, { id: { $in: orderLineIds } }) : [];
|
|
109
|
+
const lineMap = new Map(
|
|
110
|
+
orderLines.map((line) => [
|
|
111
|
+
line.id,
|
|
112
|
+
{
|
|
113
|
+
lineNumber: line.lineNumber ?? null,
|
|
114
|
+
name: line.name ?? (typeof line.catalogSnapshot === "object" && line.catalogSnapshot ? line.catalogSnapshot.name ?? null : null)
|
|
115
|
+
}
|
|
116
|
+
])
|
|
117
|
+
);
|
|
118
|
+
const grouped = shipmentItems.reduce((acc, entry) => {
|
|
119
|
+
const shipmentId = typeof entry.shipment === "string" ? entry.shipment : entry.shipment?.id ?? entry.shipment_id ?? null;
|
|
120
|
+
const lineId = typeof entry.orderLine === "string" ? entry.orderLine : entry.orderLine?.id ?? entry.order_line_id ?? null;
|
|
121
|
+
if (!shipmentId || !lineId) return acc;
|
|
122
|
+
const line = lineMap.get(lineId);
|
|
123
|
+
const list = acc.get(shipmentId) ?? [];
|
|
124
|
+
list.push({
|
|
125
|
+
id: entry.id,
|
|
126
|
+
orderLineId: lineId,
|
|
127
|
+
orderLineName: line?.name ?? null,
|
|
128
|
+
orderLineNumber: line?.lineNumber ?? null,
|
|
129
|
+
quantity: toNumber(entry.quantity),
|
|
130
|
+
metadata: entry.metadata ?? null
|
|
131
|
+
});
|
|
132
|
+
acc.set(shipmentId, list);
|
|
133
|
+
return acc;
|
|
134
|
+
}, /* @__PURE__ */ new Map());
|
|
135
|
+
const shippingMap = new Map(
|
|
136
|
+
shippingMethods.map((method) => [
|
|
137
|
+
method.id,
|
|
138
|
+
{
|
|
139
|
+
code: method.code ?? null,
|
|
140
|
+
name: method.name ?? method.code ?? null
|
|
141
|
+
}
|
|
142
|
+
])
|
|
143
|
+
);
|
|
144
|
+
const statusMap = /* @__PURE__ */ new Map();
|
|
145
|
+
statusEntries.forEach(
|
|
146
|
+
(entry) => statusMap.set(entry.id, {
|
|
147
|
+
value: entry.value ?? null,
|
|
148
|
+
label: entry.label ?? entry.value ?? null
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
items.forEach((item) => {
|
|
152
|
+
if (!item || typeof item !== "object") return;
|
|
153
|
+
const id = item.id;
|
|
154
|
+
if (typeof id !== "string") return;
|
|
155
|
+
const snapshot = snapshotMap.get(id);
|
|
156
|
+
if (snapshot?.length) {
|
|
157
|
+
;
|
|
158
|
+
item.items_snapshot = snapshot;
|
|
159
|
+
}
|
|
160
|
+
;
|
|
161
|
+
item.items = snapshot?.length ? snapshot : grouped.get(id) ?? [];
|
|
162
|
+
const shippingId = item.shipping_method_id;
|
|
163
|
+
if (typeof shippingId === "string" && shippingMap.has(shippingId)) {
|
|
164
|
+
const method = shippingMap.get(shippingId);
|
|
165
|
+
item.shipping_method_code = method?.code ?? null;
|
|
166
|
+
item.shipping_method_name = method?.name ?? null;
|
|
167
|
+
}
|
|
168
|
+
const statusId = item.status_entry_id;
|
|
169
|
+
if (typeof statusId === "string" && statusMap.has(statusId)) {
|
|
170
|
+
const status = statusMap.get(statusId);
|
|
171
|
+
if (!item.status) {
|
|
172
|
+
;
|
|
173
|
+
item.status = status?.value ?? null;
|
|
174
|
+
}
|
|
175
|
+
;
|
|
176
|
+
item.status_label = status?.label ?? status?.value ?? null;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
47
180
|
const crud = makeCrudRoute({
|
|
48
181
|
metadata: routeMetadata,
|
|
49
182
|
orm: {
|
|
@@ -144,137 +277,7 @@ const crud = makeCrudRoute({
|
|
|
144
277
|
}
|
|
145
278
|
},
|
|
146
279
|
hooks: {
|
|
147
|
-
afterList:
|
|
148
|
-
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
149
|
-
if (!items.length) return;
|
|
150
|
-
const snapshotMap = /* @__PURE__ */ new Map();
|
|
151
|
-
items.forEach((item) => {
|
|
152
|
-
if (!item || typeof item !== "object") return;
|
|
153
|
-
const id = item.id;
|
|
154
|
-
if (typeof id !== "string") return;
|
|
155
|
-
const snapshot = readShipmentItemsSnapshot(
|
|
156
|
-
item.items_snapshot ?? item.itemsSnapshot ?? null
|
|
157
|
-
);
|
|
158
|
-
if (snapshot.length) {
|
|
159
|
-
snapshotMap.set(id, snapshot);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
const shipmentIds = items.map((item) => item && typeof item === "object" ? item.id : null).filter((value) => typeof value === "string");
|
|
163
|
-
if (!shipmentIds.length) return;
|
|
164
|
-
const em = ctx.container.resolve("em");
|
|
165
|
-
const [shipmentItems, shippingMethods] = await Promise.all([
|
|
166
|
-
findWithDecryption(
|
|
167
|
-
em,
|
|
168
|
-
SalesShipmentItem,
|
|
169
|
-
{ shipment: { $in: shipmentIds } },
|
|
170
|
-
{ populate: ["orderLine"] },
|
|
171
|
-
{ tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null }
|
|
172
|
-
),
|
|
173
|
-
(async () => {
|
|
174
|
-
const ids = Array.from(
|
|
175
|
-
new Set(
|
|
176
|
-
items.map((item) => {
|
|
177
|
-
if (!item || typeof item !== "object") return null;
|
|
178
|
-
const raw = item.shipping_method_id;
|
|
179
|
-
return typeof raw === "string" ? raw : null;
|
|
180
|
-
}).filter((value) => typeof value === "string" && value.length > 0)
|
|
181
|
-
)
|
|
182
|
-
);
|
|
183
|
-
if (!ids.length) return [];
|
|
184
|
-
return em.find(SalesShippingMethod, { id: { $in: ids } });
|
|
185
|
-
})()
|
|
186
|
-
]);
|
|
187
|
-
const orderLineIds = Array.from(
|
|
188
|
-
new Set(
|
|
189
|
-
shipmentItems.map(
|
|
190
|
-
(entry) => typeof entry.orderLine === "string" ? entry.orderLine : entry.orderLine?.id ?? entry.orderLineId ?? null
|
|
191
|
-
).filter((value) => typeof value === "string")
|
|
192
|
-
)
|
|
193
|
-
);
|
|
194
|
-
const orderLines = orderLineIds.length ? await em.find(SalesOrderLine, { id: { $in: orderLineIds } }) : [];
|
|
195
|
-
const lineMap = new Map(
|
|
196
|
-
orderLines.map((line) => [
|
|
197
|
-
line.id,
|
|
198
|
-
{
|
|
199
|
-
lineNumber: line.lineNumber ?? null,
|
|
200
|
-
name: line.name ?? (typeof line.catalogSnapshot === "object" && line.catalogSnapshot ? line.catalogSnapshot.name ?? null : null)
|
|
201
|
-
}
|
|
202
|
-
])
|
|
203
|
-
);
|
|
204
|
-
const grouped = shipmentItems.reduce((acc, entry) => {
|
|
205
|
-
const shipmentId = typeof entry.shipment === "string" ? entry.shipment : entry.shipment?.id ?? entry.shipment_id ?? null;
|
|
206
|
-
const lineId = typeof entry.orderLine === "string" ? entry.orderLine : entry.orderLine?.id ?? entry.order_line_id ?? null;
|
|
207
|
-
if (!shipmentId || !lineId) return acc;
|
|
208
|
-
const line = lineMap.get(lineId);
|
|
209
|
-
const list = acc.get(shipmentId) ?? [];
|
|
210
|
-
list.push({
|
|
211
|
-
id: entry.id,
|
|
212
|
-
orderLineId: lineId,
|
|
213
|
-
orderLineName: line?.name ?? null,
|
|
214
|
-
orderLineNumber: line?.lineNumber ?? null,
|
|
215
|
-
quantity: toNumber(entry.quantity),
|
|
216
|
-
metadata: entry.metadata ?? null
|
|
217
|
-
});
|
|
218
|
-
acc.set(shipmentId, list);
|
|
219
|
-
return acc;
|
|
220
|
-
}, /* @__PURE__ */ new Map());
|
|
221
|
-
const shippingMap = new Map(
|
|
222
|
-
shippingMethods.map((method) => [
|
|
223
|
-
method.id,
|
|
224
|
-
{
|
|
225
|
-
code: method.code ?? null,
|
|
226
|
-
name: method.name ?? method.code ?? null
|
|
227
|
-
}
|
|
228
|
-
])
|
|
229
|
-
);
|
|
230
|
-
const statusIds = Array.from(
|
|
231
|
-
new Set(
|
|
232
|
-
items.map((item) => {
|
|
233
|
-
if (!item || typeof item !== "object") return null;
|
|
234
|
-
const raw = item.status_entry_id;
|
|
235
|
-
return typeof raw === "string" ? raw : null;
|
|
236
|
-
}).filter((value) => typeof value === "string" && value.length > 0)
|
|
237
|
-
)
|
|
238
|
-
);
|
|
239
|
-
const statusMap = /* @__PURE__ */ new Map();
|
|
240
|
-
if (statusIds.length) {
|
|
241
|
-
const entries = await em.find(DictionaryEntry, { id: { $in: statusIds } });
|
|
242
|
-
entries.forEach(
|
|
243
|
-
(entry) => statusMap.set(entry.id, {
|
|
244
|
-
value: entry.value ?? null,
|
|
245
|
-
label: entry.label ?? entry.value ?? null
|
|
246
|
-
})
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
items.forEach((item) => {
|
|
250
|
-
if (!item || typeof item !== "object") return;
|
|
251
|
-
const id = item.id;
|
|
252
|
-
if (typeof id !== "string") return;
|
|
253
|
-
const snapshot = snapshotMap.get(id);
|
|
254
|
-
if (snapshot?.length) {
|
|
255
|
-
;
|
|
256
|
-
item.items_snapshot = snapshot;
|
|
257
|
-
}
|
|
258
|
-
;
|
|
259
|
-
item.items = snapshot?.length ? snapshot : grouped.get(id) ?? [];
|
|
260
|
-
const shippingId = item.shipping_method_id;
|
|
261
|
-
if (typeof shippingId === "string" && shippingMap.has(shippingId)) {
|
|
262
|
-
const method = shippingMap.get(shippingId);
|
|
263
|
-
item.shipping_method_code = method?.code ?? null;
|
|
264
|
-
item.shipping_method_name = method?.name ?? null;
|
|
265
|
-
}
|
|
266
|
-
const statusId = item.status_entry_id;
|
|
267
|
-
if (typeof statusId === "string" && statusMap.has(statusId)) {
|
|
268
|
-
const status = statusMap.get(statusId);
|
|
269
|
-
if (!item.status) {
|
|
270
|
-
;
|
|
271
|
-
item.status = status?.value ?? null;
|
|
272
|
-
}
|
|
273
|
-
;
|
|
274
|
-
item.status_label = status?.label ?? status?.value ?? null;
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
|
280
|
+
afterList: (payload, ctx) => enrichShipmentListResponse(payload, ctx)
|
|
278
281
|
}
|
|
279
282
|
});
|
|
280
283
|
const { GET, POST, PUT, DELETE } = crud;
|
|
@@ -343,6 +346,7 @@ export {
|
|
|
343
346
|
GET,
|
|
344
347
|
POST,
|
|
345
348
|
PUT,
|
|
349
|
+
enrichShipmentListResponse,
|
|
346
350
|
metadata,
|
|
347
351
|
openApi
|
|
348
352
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/api/shipments/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { SalesOrderLine, SalesShipment, SalesShipmentItem, SalesShippingMethod } from '../../data/entities'\nimport { shipmentCreateSchema, shipmentUpdateSchema } from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { readShipmentItemsSnapshot } from '../../lib/shipments/snapshots'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/sales_shipment'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n orderId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.orders.view'] },\n POST: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n}\n\nconst deleteSchema = shipmentUpdateSchema.pick({\n id: true,\n orderId: true,\n organizationId: true,\n tenantId: true,\n})\n\nconst toNumber = (value: unknown): number => {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return 0\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesShipment,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.sales.sales_shipment,\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_shipment,\n fields: [\n F.id,\n 'order_id',\n F.shipment_number,\n F.shipping_method_id,\n F.status_entry_id,\n F.status,\n F.carrier_name,\n F.tracking_numbers,\n F.shipped_at,\n F.delivered_at,\n F.weight_value,\n F.weight_unit,\n F.declared_value_net,\n F.declared_value_gross,\n F.currency_code,\n F.notes,\n F.metadata,\n F.items_snapshot,\n F.created_at,\n F.updated_at,\n ],\n decorateCustomFields: { entityIds: [E.sales.sales_shipment] },\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n shippedAt: F.shipped_at,\n },\n buildFilters: async (query: any) => {\n const filters: Record<string, any> = {}\n if (query.orderId) filters.order_id = { $eq: query.orderId }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'sales.shipments.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n return shipmentCreateSchema.parse({\n ...base,\n ...(Object.keys(custom).length ? { customFields: custom } : {}),\n })\n },\n response: ({ result }) => ({ id: result?.shipmentId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.shipments.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n return shipmentUpdateSchema.parse({\n ...base,\n ...(Object.keys(custom).length ? { customFields: custom } : {}),\n })\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'sales.shipments.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const merged = {\n ...(raw?.body ?? {}),\n ...(raw?.query ?? {}),\n }\n const payload = deleteSchema.parse(withScopedPayload(merged, ctx, translate))\n if (!payload.id || !payload.orderId) {\n throw new CrudHttpError(400, {\n error: translate('sales.shipments.not_found', 'Shipment not found'),\n })\n }\n return payload\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const snapshotMap = new Map<string, ReturnType<typeof readShipmentItemsSnapshot>>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const id = (item as Record<string, unknown>).id\n if (typeof id !== 'string') return\n const snapshot = readShipmentItemsSnapshot(\n (item as any).items_snapshot ?? (item as any).itemsSnapshot ?? null\n )\n if (snapshot.length) {\n snapshotMap.set(id, snapshot)\n }\n })\n const shipmentIds = items\n .map((item: unknown) => (item && typeof item === 'object' ? (item as Record<string, unknown>).id : null))\n .filter((value: string | null): value is string => typeof value === 'string')\n if (!shipmentIds.length) return\n const em = ctx.container.resolve('em') as EntityManager\n const [shipmentItems, shippingMethods] = await Promise.all([\n findWithDecryption(\n em,\n SalesShipmentItem,\n { shipment: { $in: shipmentIds } },\n { populate: ['orderLine'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n ),\n (async () => {\n const ids: string[] = Array.from(\n new Set(\n items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const raw = (item as Record<string, unknown>).shipping_method_id\n return typeof raw === 'string' ? raw : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n )\n )\n if (!ids.length) return []\n return em.find(SalesShippingMethod, { id: { $in: ids } })\n })(),\n ])\n const orderLineIds = Array.from(\n new Set(\n shipmentItems\n .map((entry) =>\n typeof entry.orderLine === 'string'\n ? entry.orderLine\n : entry.orderLine?.id ?? (entry as any).orderLineId ?? null\n )\n .filter((value): value is string => typeof value === 'string')\n )\n )\n const orderLines = orderLineIds.length\n ? await em.find(SalesOrderLine, { id: { $in: orderLineIds } })\n : []\n const lineMap = new Map(\n orderLines.map((line) => [\n line.id,\n {\n lineNumber: line.lineNumber ?? null,\n name:\n line.name ??\n (typeof line.catalogSnapshot === 'object' && line.catalogSnapshot\n ? ((line.catalogSnapshot as any).name as string | undefined) ?? null\n : null),\n },\n ])\n )\n const grouped = shipmentItems.reduce<Map<string, Array<Record<string, unknown>>>>((acc, entry) => {\n const shipmentId =\n typeof entry.shipment === 'string'\n ? entry.shipment\n : entry.shipment?.id ?? (entry as any).shipment_id ?? null\n const lineId =\n typeof entry.orderLine === 'string'\n ? entry.orderLine\n : entry.orderLine?.id ?? (entry as any).order_line_id ?? null\n if (!shipmentId || !lineId) return acc\n const line = lineMap.get(lineId)\n const list = acc.get(shipmentId) ?? []\n list.push({\n id: entry.id,\n orderLineId: lineId,\n orderLineName: line?.name ?? null,\n orderLineNumber: line?.lineNumber ?? null,\n quantity: toNumber(entry.quantity),\n metadata: entry.metadata ?? null,\n })\n acc.set(shipmentId, list)\n return acc\n }, new Map())\n const shippingMap = new Map(\n shippingMethods.map((method) => [\n method.id,\n {\n code: method.code ?? null,\n name: method.name ?? method.code ?? null,\n },\n ])\n )\n const statusIds: string[] = Array.from(\n new Set(\n items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const raw = (item as Record<string, unknown>).status_entry_id\n return typeof raw === 'string' ? raw : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n )\n )\n const statusMap = new Map<string, { value: string | null; label: string | null }>()\n if (statusIds.length) {\n const entries = await em.find(DictionaryEntry, { id: { $in: statusIds } })\n entries.forEach((entry) =>\n statusMap.set(entry.id, {\n value: entry.value ?? null,\n label: entry.label ?? entry.value ?? null,\n })\n )\n }\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const id = (item as Record<string, unknown>).id\n if (typeof id !== 'string') return\n const snapshot = snapshotMap.get(id)\n if (snapshot?.length) {\n ;(item as Record<string, unknown>).items_snapshot = snapshot\n }\n ;(item as Record<string, unknown>).items = snapshot?.length ? snapshot : grouped.get(id) ?? []\n const shippingId = (item as Record<string, unknown>).shipping_method_id\n if (typeof shippingId === 'string' && shippingMap.has(shippingId)) {\n const method = shippingMap.get(shippingId)\n ;(item as Record<string, unknown>).shipping_method_code = method?.code ?? null\n ;(item as Record<string, unknown>).shipping_method_name = method?.name ?? null\n }\n const statusId = (item as Record<string, unknown>).status_entry_id\n if (typeof statusId === 'string' && statusMap.has(statusId)) {\n const status = statusMap.get(statusId)\n if (!(item as Record<string, unknown>).status) {\n ;(item as Record<string, unknown>).status = status?.value ?? null\n }\n ;(item as Record<string, unknown>).status_label = status?.label ?? status?.value ?? null\n }\n })\n },\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport const metadata = routeMetadata\nexport { GET, POST, PUT, DELETE }\n\nconst shipmentItemSchema = z.object({\n id: z.string().uuid(),\n orderLineId: z.string().uuid(),\n orderLineName: z.string().nullable().optional(),\n orderLineNumber: z.number().nullable().optional(),\n quantity: z.number(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n})\n\nconst shipmentSchema = z\n .object({\n id: z.string().uuid(),\n order_id: z.string().uuid(),\n shipment_number: z.string().nullable().optional(),\n shipping_method_id: z.string().uuid().nullable().optional(),\n shipping_method_code: z.string().nullable().optional(),\n shipping_method_name: z.string().nullable().optional(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n status_label: z.string().nullable().optional(),\n carrier_name: z.string().nullable().optional(),\n tracking_numbers: z.array(z.string()).nullable().optional(),\n shipped_at: z.string().nullable().optional(),\n delivered_at: z.string().nullable().optional(),\n weight_value: z.number().nullable().optional(),\n weight_unit: z.string().nullable().optional(),\n declared_value_net: z.number().nullable().optional(),\n declared_value_gross: z.number().nullable().optional(),\n currency_code: z.string().nullable().optional(),\n notes: z.string().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_values: z.record(z.string(), z.unknown()).nullable().optional(),\n customValues: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_fields: z.array(z.record(z.string(), z.unknown())).nullable().optional(),\n customFields: z.array(z.record(z.string(), z.unknown())).nullable().optional(),\n items_snapshot: z.array(shipmentItemSchema).nullable().optional(),\n itemsSnapshot: z.array(shipmentItemSchema).nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n items: z.array(shipmentItemSchema).optional(),\n })\n .passthrough()\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Shipment',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(shipmentSchema),\n create: {\n schema: shipmentCreateSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable() }),\n description: 'Creates a shipment for an order.',\n },\n update: {\n schema: shipmentUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a shipment.',\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a shipment.',\n },\n})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { SalesOrderLine, SalesShipment, SalesShipmentItem, SalesShippingMethod } from '../../data/entities'\nimport { shipmentCreateSchema, shipmentUpdateSchema } from '../../data/validators'\nimport { withScopedPayload } from '../utils'\nimport { readShipmentItemsSnapshot } from '../../lib/shipments/snapshots'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../openapi'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/sales_shipment'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n orderId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.orders.view'] },\n POST: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.shipments.manage'] },\n}\n\nconst deleteSchema = shipmentUpdateSchema.pick({\n id: true,\n orderId: true,\n organizationId: true,\n tenantId: true,\n})\n\nconst toNumber = (value: unknown): number => {\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return 0\n}\n\nexport async function enrichShipmentListResponse(\n payload: { items?: unknown[] },\n ctx: CrudCtx,\n): Promise<void> {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const snapshotMap = new Map<string, ReturnType<typeof readShipmentItemsSnapshot>>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const id = (item as Record<string, unknown>).id\n if (typeof id !== 'string') return\n const snapshot = readShipmentItemsSnapshot(\n (item as any).items_snapshot ?? (item as any).itemsSnapshot ?? null\n )\n if (snapshot.length) {\n snapshotMap.set(id, snapshot)\n }\n })\n const shipmentIds = items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const raw = (item as Record<string, unknown>).id\n return typeof raw === 'string' ? raw : null\n })\n .filter((value: string | null): value is string => typeof value === 'string')\n if (!shipmentIds.length) return\n const em = ctx.container.resolve('em') as EntityManager\n const statusIds: string[] = Array.from(\n new Set(\n items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const raw = (item as Record<string, unknown>).status_entry_id\n return typeof raw === 'string' ? raw : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n )\n )\n const [shipmentItems, shippingMethods, statusEntries] = await Promise.all([\n findWithDecryption(\n em,\n SalesShipmentItem,\n { shipment: { $in: shipmentIds } },\n { populate: ['orderLine'] },\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },\n ),\n (async () => {\n const ids: string[] = Array.from(\n new Set(\n items\n .map((item: unknown) => {\n if (!item || typeof item !== 'object') return null\n const raw = (item as Record<string, unknown>).shipping_method_id\n return typeof raw === 'string' ? raw : null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n )\n )\n if (!ids.length) return []\n return em.find(SalesShippingMethod, { id: { $in: ids } })\n })(),\n statusIds.length ? em.find(DictionaryEntry, { id: { $in: statusIds } }) : [],\n ])\n const orderLineIds = Array.from(\n new Set(\n shipmentItems\n .map((entry) =>\n typeof entry.orderLine === 'string'\n ? entry.orderLine\n : entry.orderLine?.id ?? (entry as any).orderLineId ?? null\n )\n .filter((value): value is string => typeof value === 'string')\n )\n )\n const orderLines = orderLineIds.length\n ? await em.find(SalesOrderLine, { id: { $in: orderLineIds } })\n : []\n const lineMap = new Map(\n orderLines.map((line) => [\n line.id,\n {\n lineNumber: line.lineNumber ?? null,\n name:\n line.name ??\n (typeof line.catalogSnapshot === 'object' && line.catalogSnapshot\n ? ((line.catalogSnapshot as any).name as string | undefined) ?? null\n : null),\n },\n ])\n )\n const grouped = shipmentItems.reduce<Map<string, Array<Record<string, unknown>>>>((acc, entry) => {\n const shipmentId =\n typeof entry.shipment === 'string'\n ? entry.shipment\n : entry.shipment?.id ?? (entry as any).shipment_id ?? null\n const lineId =\n typeof entry.orderLine === 'string'\n ? entry.orderLine\n : entry.orderLine?.id ?? (entry as any).order_line_id ?? null\n if (!shipmentId || !lineId) return acc\n const line = lineMap.get(lineId)\n const list = acc.get(shipmentId) ?? []\n list.push({\n id: entry.id,\n orderLineId: lineId,\n orderLineName: line?.name ?? null,\n orderLineNumber: line?.lineNumber ?? null,\n quantity: toNumber(entry.quantity),\n metadata: entry.metadata ?? null,\n })\n acc.set(shipmentId, list)\n return acc\n }, new Map())\n const shippingMap = new Map(\n shippingMethods.map((method) => [\n method.id,\n {\n code: method.code ?? null,\n name: method.name ?? method.code ?? null,\n },\n ])\n )\n const statusMap = new Map<string, { value: string | null; label: string | null }>()\n statusEntries.forEach((entry) =>\n statusMap.set(entry.id, {\n value: entry.value ?? null,\n label: entry.label ?? entry.value ?? null,\n })\n )\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const id = (item as Record<string, unknown>).id\n if (typeof id !== 'string') return\n const snapshot = snapshotMap.get(id)\n if (snapshot?.length) {\n ;(item as Record<string, unknown>).items_snapshot = snapshot\n }\n ;(item as Record<string, unknown>).items = snapshot?.length ? snapshot : grouped.get(id) ?? []\n const shippingId = (item as Record<string, unknown>).shipping_method_id\n if (typeof shippingId === 'string' && shippingMap.has(shippingId)) {\n const method = shippingMap.get(shippingId)\n ;(item as Record<string, unknown>).shipping_method_code = method?.code ?? null\n ;(item as Record<string, unknown>).shipping_method_name = method?.name ?? null\n }\n const statusId = (item as Record<string, unknown>).status_entry_id\n if (typeof statusId === 'string' && statusMap.has(statusId)) {\n const status = statusMap.get(statusId)\n if (!(item as Record<string, unknown>).status) {\n ;(item as Record<string, unknown>).status = status?.value ?? null\n }\n ;(item as Record<string, unknown>).status_label = status?.label ?? status?.value ?? null\n }\n })\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesShipment,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.sales.sales_shipment,\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_shipment,\n fields: [\n F.id,\n 'order_id',\n F.shipment_number,\n F.shipping_method_id,\n F.status_entry_id,\n F.status,\n F.carrier_name,\n F.tracking_numbers,\n F.shipped_at,\n F.delivered_at,\n F.weight_value,\n F.weight_unit,\n F.declared_value_net,\n F.declared_value_gross,\n F.currency_code,\n F.notes,\n F.metadata,\n F.items_snapshot,\n F.created_at,\n F.updated_at,\n ],\n decorateCustomFields: { entityIds: [E.sales.sales_shipment] },\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n shippedAt: F.shipped_at,\n },\n buildFilters: async (query: any) => {\n const filters: Record<string, any> = {}\n if (query.orderId) filters.order_id = { $eq: query.orderId }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'sales.shipments.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n return shipmentCreateSchema.parse({\n ...base,\n ...(Object.keys(custom).length ? { customFields: custom } : {}),\n })\n },\n response: ({ result }) => ({ id: result?.shipmentId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.shipments.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n return shipmentUpdateSchema.parse({\n ...base,\n ...(Object.keys(custom).length ? { customFields: custom } : {}),\n })\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'sales.shipments.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const merged = {\n ...(raw?.body ?? {}),\n ...(raw?.query ?? {}),\n }\n const payload = deleteSchema.parse(withScopedPayload(merged, ctx, translate))\n if (!payload.id || !payload.orderId) {\n throw new CrudHttpError(400, {\n error: translate('sales.shipments.not_found', 'Shipment not found'),\n })\n }\n return payload\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: (payload, ctx) => enrichShipmentListResponse(payload, ctx),\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport const metadata = routeMetadata\nexport { GET, POST, PUT, DELETE }\n\nconst shipmentItemSchema = z.object({\n id: z.string().uuid(),\n orderLineId: z.string().uuid(),\n orderLineName: z.string().nullable().optional(),\n orderLineNumber: z.number().nullable().optional(),\n quantity: z.number(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n})\n\nconst shipmentSchema = z\n .object({\n id: z.string().uuid(),\n order_id: z.string().uuid(),\n shipment_number: z.string().nullable().optional(),\n shipping_method_id: z.string().uuid().nullable().optional(),\n shipping_method_code: z.string().nullable().optional(),\n shipping_method_name: z.string().nullable().optional(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n status_label: z.string().nullable().optional(),\n carrier_name: z.string().nullable().optional(),\n tracking_numbers: z.array(z.string()).nullable().optional(),\n shipped_at: z.string().nullable().optional(),\n delivered_at: z.string().nullable().optional(),\n weight_value: z.number().nullable().optional(),\n weight_unit: z.string().nullable().optional(),\n declared_value_net: z.number().nullable().optional(),\n declared_value_gross: z.number().nullable().optional(),\n currency_code: z.string().nullable().optional(),\n notes: z.string().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_values: z.record(z.string(), z.unknown()).nullable().optional(),\n customValues: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_fields: z.array(z.record(z.string(), z.unknown())).nullable().optional(),\n customFields: z.array(z.record(z.string(), z.unknown())).nullable().optional(),\n items_snapshot: z.array(shipmentItemSchema).nullable().optional(),\n itemsSnapshot: z.array(shipmentItemSchema).nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n items: z.array(shipmentItemSchema).optional(),\n })\n .passthrough()\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Shipment',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(shipmentSchema),\n create: {\n schema: shipmentCreateSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable() }),\n description: 'Creates a shipment for an order.',\n },\n update: {\n schema: shipmentUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a shipment.',\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a shipment.',\n },\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.4-develop.
|
|
3
|
+
"version": "0.6.4-develop.4038.1.91ce075c8a",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -243,16 +243,16 @@
|
|
|
243
243
|
"zod": "^4.4.3"
|
|
244
244
|
},
|
|
245
245
|
"peerDependencies": {
|
|
246
|
-
"@open-mercato/ai-assistant": "0.6.4-develop.
|
|
247
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
248
|
-
"@open-mercato/ui": "0.6.4-develop.
|
|
246
|
+
"@open-mercato/ai-assistant": "0.6.4-develop.4038.1.91ce075c8a",
|
|
247
|
+
"@open-mercato/shared": "0.6.4-develop.4038.1.91ce075c8a",
|
|
248
|
+
"@open-mercato/ui": "0.6.4-develop.4038.1.91ce075c8a",
|
|
249
249
|
"react": "^19.0.0",
|
|
250
250
|
"react-dom": "^19.0.0"
|
|
251
251
|
},
|
|
252
252
|
"devDependencies": {
|
|
253
|
-
"@open-mercato/ai-assistant": "0.6.4-develop.
|
|
254
|
-
"@open-mercato/shared": "0.6.4-develop.
|
|
255
|
-
"@open-mercato/ui": "0.6.4-develop.
|
|
253
|
+
"@open-mercato/ai-assistant": "0.6.4-develop.4038.1.91ce075c8a",
|
|
254
|
+
"@open-mercato/shared": "0.6.4-develop.4038.1.91ce075c8a",
|
|
255
|
+
"@open-mercato/ui": "0.6.4-develop.4038.1.91ce075c8a",
|
|
256
256
|
"@testing-library/dom": "^10.4.1",
|
|
257
257
|
"@testing-library/jest-dom": "^6.9.1",
|
|
258
258
|
"@testing-library/react": "^16.3.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
-
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
|
+
import { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'
|
|
4
4
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
5
5
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
6
6
|
import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'
|
|
@@ -53,6 +53,161 @@ const toNumber = (value: unknown): number => {
|
|
|
53
53
|
return 0
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export async function enrichShipmentListResponse(
|
|
57
|
+
payload: { items?: unknown[] },
|
|
58
|
+
ctx: CrudCtx,
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
const items = Array.isArray(payload.items) ? payload.items : []
|
|
61
|
+
if (!items.length) return
|
|
62
|
+
const snapshotMap = new Map<string, ReturnType<typeof readShipmentItemsSnapshot>>()
|
|
63
|
+
items.forEach((item: unknown) => {
|
|
64
|
+
if (!item || typeof item !== 'object') return
|
|
65
|
+
const id = (item as Record<string, unknown>).id
|
|
66
|
+
if (typeof id !== 'string') return
|
|
67
|
+
const snapshot = readShipmentItemsSnapshot(
|
|
68
|
+
(item as any).items_snapshot ?? (item as any).itemsSnapshot ?? null
|
|
69
|
+
)
|
|
70
|
+
if (snapshot.length) {
|
|
71
|
+
snapshotMap.set(id, snapshot)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
const shipmentIds = items
|
|
75
|
+
.map((item: unknown) => {
|
|
76
|
+
if (!item || typeof item !== 'object') return null
|
|
77
|
+
const raw = (item as Record<string, unknown>).id
|
|
78
|
+
return typeof raw === 'string' ? raw : null
|
|
79
|
+
})
|
|
80
|
+
.filter((value: string | null): value is string => typeof value === 'string')
|
|
81
|
+
if (!shipmentIds.length) return
|
|
82
|
+
const em = ctx.container.resolve('em') as EntityManager
|
|
83
|
+
const statusIds: string[] = Array.from(
|
|
84
|
+
new Set(
|
|
85
|
+
items
|
|
86
|
+
.map((item: unknown) => {
|
|
87
|
+
if (!item || typeof item !== 'object') return null
|
|
88
|
+
const raw = (item as Record<string, unknown>).status_entry_id
|
|
89
|
+
return typeof raw === 'string' ? raw : null
|
|
90
|
+
})
|
|
91
|
+
.filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
const [shipmentItems, shippingMethods, statusEntries] = await Promise.all([
|
|
95
|
+
findWithDecryption(
|
|
96
|
+
em,
|
|
97
|
+
SalesShipmentItem,
|
|
98
|
+
{ shipment: { $in: shipmentIds } },
|
|
99
|
+
{ populate: ['orderLine'] },
|
|
100
|
+
{ tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },
|
|
101
|
+
),
|
|
102
|
+
(async () => {
|
|
103
|
+
const ids: string[] = Array.from(
|
|
104
|
+
new Set(
|
|
105
|
+
items
|
|
106
|
+
.map((item: unknown) => {
|
|
107
|
+
if (!item || typeof item !== 'object') return null
|
|
108
|
+
const raw = (item as Record<string, unknown>).shipping_method_id
|
|
109
|
+
return typeof raw === 'string' ? raw : null
|
|
110
|
+
})
|
|
111
|
+
.filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
if (!ids.length) return []
|
|
115
|
+
return em.find(SalesShippingMethod, { id: { $in: ids } })
|
|
116
|
+
})(),
|
|
117
|
+
statusIds.length ? em.find(DictionaryEntry, { id: { $in: statusIds } }) : [],
|
|
118
|
+
])
|
|
119
|
+
const orderLineIds = Array.from(
|
|
120
|
+
new Set(
|
|
121
|
+
shipmentItems
|
|
122
|
+
.map((entry) =>
|
|
123
|
+
typeof entry.orderLine === 'string'
|
|
124
|
+
? entry.orderLine
|
|
125
|
+
: entry.orderLine?.id ?? (entry as any).orderLineId ?? null
|
|
126
|
+
)
|
|
127
|
+
.filter((value): value is string => typeof value === 'string')
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
const orderLines = orderLineIds.length
|
|
131
|
+
? await em.find(SalesOrderLine, { id: { $in: orderLineIds } })
|
|
132
|
+
: []
|
|
133
|
+
const lineMap = new Map(
|
|
134
|
+
orderLines.map((line) => [
|
|
135
|
+
line.id,
|
|
136
|
+
{
|
|
137
|
+
lineNumber: line.lineNumber ?? null,
|
|
138
|
+
name:
|
|
139
|
+
line.name ??
|
|
140
|
+
(typeof line.catalogSnapshot === 'object' && line.catalogSnapshot
|
|
141
|
+
? ((line.catalogSnapshot as any).name as string | undefined) ?? null
|
|
142
|
+
: null),
|
|
143
|
+
},
|
|
144
|
+
])
|
|
145
|
+
)
|
|
146
|
+
const grouped = shipmentItems.reduce<Map<string, Array<Record<string, unknown>>>>((acc, entry) => {
|
|
147
|
+
const shipmentId =
|
|
148
|
+
typeof entry.shipment === 'string'
|
|
149
|
+
? entry.shipment
|
|
150
|
+
: entry.shipment?.id ?? (entry as any).shipment_id ?? null
|
|
151
|
+
const lineId =
|
|
152
|
+
typeof entry.orderLine === 'string'
|
|
153
|
+
? entry.orderLine
|
|
154
|
+
: entry.orderLine?.id ?? (entry as any).order_line_id ?? null
|
|
155
|
+
if (!shipmentId || !lineId) return acc
|
|
156
|
+
const line = lineMap.get(lineId)
|
|
157
|
+
const list = acc.get(shipmentId) ?? []
|
|
158
|
+
list.push({
|
|
159
|
+
id: entry.id,
|
|
160
|
+
orderLineId: lineId,
|
|
161
|
+
orderLineName: line?.name ?? null,
|
|
162
|
+
orderLineNumber: line?.lineNumber ?? null,
|
|
163
|
+
quantity: toNumber(entry.quantity),
|
|
164
|
+
metadata: entry.metadata ?? null,
|
|
165
|
+
})
|
|
166
|
+
acc.set(shipmentId, list)
|
|
167
|
+
return acc
|
|
168
|
+
}, new Map())
|
|
169
|
+
const shippingMap = new Map(
|
|
170
|
+
shippingMethods.map((method) => [
|
|
171
|
+
method.id,
|
|
172
|
+
{
|
|
173
|
+
code: method.code ?? null,
|
|
174
|
+
name: method.name ?? method.code ?? null,
|
|
175
|
+
},
|
|
176
|
+
])
|
|
177
|
+
)
|
|
178
|
+
const statusMap = new Map<string, { value: string | null; label: string | null }>()
|
|
179
|
+
statusEntries.forEach((entry) =>
|
|
180
|
+
statusMap.set(entry.id, {
|
|
181
|
+
value: entry.value ?? null,
|
|
182
|
+
label: entry.label ?? entry.value ?? null,
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
items.forEach((item: unknown) => {
|
|
186
|
+
if (!item || typeof item !== 'object') return
|
|
187
|
+
const id = (item as Record<string, unknown>).id
|
|
188
|
+
if (typeof id !== 'string') return
|
|
189
|
+
const snapshot = snapshotMap.get(id)
|
|
190
|
+
if (snapshot?.length) {
|
|
191
|
+
;(item as Record<string, unknown>).items_snapshot = snapshot
|
|
192
|
+
}
|
|
193
|
+
;(item as Record<string, unknown>).items = snapshot?.length ? snapshot : grouped.get(id) ?? []
|
|
194
|
+
const shippingId = (item as Record<string, unknown>).shipping_method_id
|
|
195
|
+
if (typeof shippingId === 'string' && shippingMap.has(shippingId)) {
|
|
196
|
+
const method = shippingMap.get(shippingId)
|
|
197
|
+
;(item as Record<string, unknown>).shipping_method_code = method?.code ?? null
|
|
198
|
+
;(item as Record<string, unknown>).shipping_method_name = method?.name ?? null
|
|
199
|
+
}
|
|
200
|
+
const statusId = (item as Record<string, unknown>).status_entry_id
|
|
201
|
+
if (typeof statusId === 'string' && statusMap.has(statusId)) {
|
|
202
|
+
const status = statusMap.get(statusId)
|
|
203
|
+
if (!(item as Record<string, unknown>).status) {
|
|
204
|
+
;(item as Record<string, unknown>).status = status?.value ?? null
|
|
205
|
+
}
|
|
206
|
+
;(item as Record<string, unknown>).status_label = status?.label ?? status?.value ?? null
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
56
211
|
const crud = makeCrudRoute({
|
|
57
212
|
metadata: routeMetadata,
|
|
58
213
|
orm: {
|
|
@@ -153,155 +308,7 @@ const crud = makeCrudRoute({
|
|
|
153
308
|
},
|
|
154
309
|
},
|
|
155
310
|
hooks: {
|
|
156
|
-
afterList:
|
|
157
|
-
const items = Array.isArray(payload.items) ? payload.items : []
|
|
158
|
-
if (!items.length) return
|
|
159
|
-
const snapshotMap = new Map<string, ReturnType<typeof readShipmentItemsSnapshot>>()
|
|
160
|
-
items.forEach((item: unknown) => {
|
|
161
|
-
if (!item || typeof item !== 'object') return
|
|
162
|
-
const id = (item as Record<string, unknown>).id
|
|
163
|
-
if (typeof id !== 'string') return
|
|
164
|
-
const snapshot = readShipmentItemsSnapshot(
|
|
165
|
-
(item as any).items_snapshot ?? (item as any).itemsSnapshot ?? null
|
|
166
|
-
)
|
|
167
|
-
if (snapshot.length) {
|
|
168
|
-
snapshotMap.set(id, snapshot)
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
const shipmentIds = items
|
|
172
|
-
.map((item: unknown) => (item && typeof item === 'object' ? (item as Record<string, unknown>).id : null))
|
|
173
|
-
.filter((value: string | null): value is string => typeof value === 'string')
|
|
174
|
-
if (!shipmentIds.length) return
|
|
175
|
-
const em = ctx.container.resolve('em') as EntityManager
|
|
176
|
-
const [shipmentItems, shippingMethods] = await Promise.all([
|
|
177
|
-
findWithDecryption(
|
|
178
|
-
em,
|
|
179
|
-
SalesShipmentItem,
|
|
180
|
-
{ shipment: { $in: shipmentIds } },
|
|
181
|
-
{ populate: ['orderLine'] },
|
|
182
|
-
{ tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.auth?.orgId ?? null },
|
|
183
|
-
),
|
|
184
|
-
(async () => {
|
|
185
|
-
const ids: string[] = Array.from(
|
|
186
|
-
new Set(
|
|
187
|
-
items
|
|
188
|
-
.map((item: unknown) => {
|
|
189
|
-
if (!item || typeof item !== 'object') return null
|
|
190
|
-
const raw = (item as Record<string, unknown>).shipping_method_id
|
|
191
|
-
return typeof raw === 'string' ? raw : null
|
|
192
|
-
})
|
|
193
|
-
.filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
if (!ids.length) return []
|
|
197
|
-
return em.find(SalesShippingMethod, { id: { $in: ids } })
|
|
198
|
-
})(),
|
|
199
|
-
])
|
|
200
|
-
const orderLineIds = Array.from(
|
|
201
|
-
new Set(
|
|
202
|
-
shipmentItems
|
|
203
|
-
.map((entry) =>
|
|
204
|
-
typeof entry.orderLine === 'string'
|
|
205
|
-
? entry.orderLine
|
|
206
|
-
: entry.orderLine?.id ?? (entry as any).orderLineId ?? null
|
|
207
|
-
)
|
|
208
|
-
.filter((value): value is string => typeof value === 'string')
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
const orderLines = orderLineIds.length
|
|
212
|
-
? await em.find(SalesOrderLine, { id: { $in: orderLineIds } })
|
|
213
|
-
: []
|
|
214
|
-
const lineMap = new Map(
|
|
215
|
-
orderLines.map((line) => [
|
|
216
|
-
line.id,
|
|
217
|
-
{
|
|
218
|
-
lineNumber: line.lineNumber ?? null,
|
|
219
|
-
name:
|
|
220
|
-
line.name ??
|
|
221
|
-
(typeof line.catalogSnapshot === 'object' && line.catalogSnapshot
|
|
222
|
-
? ((line.catalogSnapshot as any).name as string | undefined) ?? null
|
|
223
|
-
: null),
|
|
224
|
-
},
|
|
225
|
-
])
|
|
226
|
-
)
|
|
227
|
-
const grouped = shipmentItems.reduce<Map<string, Array<Record<string, unknown>>>>((acc, entry) => {
|
|
228
|
-
const shipmentId =
|
|
229
|
-
typeof entry.shipment === 'string'
|
|
230
|
-
? entry.shipment
|
|
231
|
-
: entry.shipment?.id ?? (entry as any).shipment_id ?? null
|
|
232
|
-
const lineId =
|
|
233
|
-
typeof entry.orderLine === 'string'
|
|
234
|
-
? entry.orderLine
|
|
235
|
-
: entry.orderLine?.id ?? (entry as any).order_line_id ?? null
|
|
236
|
-
if (!shipmentId || !lineId) return acc
|
|
237
|
-
const line = lineMap.get(lineId)
|
|
238
|
-
const list = acc.get(shipmentId) ?? []
|
|
239
|
-
list.push({
|
|
240
|
-
id: entry.id,
|
|
241
|
-
orderLineId: lineId,
|
|
242
|
-
orderLineName: line?.name ?? null,
|
|
243
|
-
orderLineNumber: line?.lineNumber ?? null,
|
|
244
|
-
quantity: toNumber(entry.quantity),
|
|
245
|
-
metadata: entry.metadata ?? null,
|
|
246
|
-
})
|
|
247
|
-
acc.set(shipmentId, list)
|
|
248
|
-
return acc
|
|
249
|
-
}, new Map())
|
|
250
|
-
const shippingMap = new Map(
|
|
251
|
-
shippingMethods.map((method) => [
|
|
252
|
-
method.id,
|
|
253
|
-
{
|
|
254
|
-
code: method.code ?? null,
|
|
255
|
-
name: method.name ?? method.code ?? null,
|
|
256
|
-
},
|
|
257
|
-
])
|
|
258
|
-
)
|
|
259
|
-
const statusIds: string[] = Array.from(
|
|
260
|
-
new Set(
|
|
261
|
-
items
|
|
262
|
-
.map((item: unknown) => {
|
|
263
|
-
if (!item || typeof item !== 'object') return null
|
|
264
|
-
const raw = (item as Record<string, unknown>).status_entry_id
|
|
265
|
-
return typeof raw === 'string' ? raw : null
|
|
266
|
-
})
|
|
267
|
-
.filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)
|
|
268
|
-
)
|
|
269
|
-
)
|
|
270
|
-
const statusMap = new Map<string, { value: string | null; label: string | null }>()
|
|
271
|
-
if (statusIds.length) {
|
|
272
|
-
const entries = await em.find(DictionaryEntry, { id: { $in: statusIds } })
|
|
273
|
-
entries.forEach((entry) =>
|
|
274
|
-
statusMap.set(entry.id, {
|
|
275
|
-
value: entry.value ?? null,
|
|
276
|
-
label: entry.label ?? entry.value ?? null,
|
|
277
|
-
})
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
items.forEach((item: unknown) => {
|
|
281
|
-
if (!item || typeof item !== 'object') return
|
|
282
|
-
const id = (item as Record<string, unknown>).id
|
|
283
|
-
if (typeof id !== 'string') return
|
|
284
|
-
const snapshot = snapshotMap.get(id)
|
|
285
|
-
if (snapshot?.length) {
|
|
286
|
-
;(item as Record<string, unknown>).items_snapshot = snapshot
|
|
287
|
-
}
|
|
288
|
-
;(item as Record<string, unknown>).items = snapshot?.length ? snapshot : grouped.get(id) ?? []
|
|
289
|
-
const shippingId = (item as Record<string, unknown>).shipping_method_id
|
|
290
|
-
if (typeof shippingId === 'string' && shippingMap.has(shippingId)) {
|
|
291
|
-
const method = shippingMap.get(shippingId)
|
|
292
|
-
;(item as Record<string, unknown>).shipping_method_code = method?.code ?? null
|
|
293
|
-
;(item as Record<string, unknown>).shipping_method_name = method?.name ?? null
|
|
294
|
-
}
|
|
295
|
-
const statusId = (item as Record<string, unknown>).status_entry_id
|
|
296
|
-
if (typeof statusId === 'string' && statusMap.has(statusId)) {
|
|
297
|
-
const status = statusMap.get(statusId)
|
|
298
|
-
if (!(item as Record<string, unknown>).status) {
|
|
299
|
-
;(item as Record<string, unknown>).status = status?.value ?? null
|
|
300
|
-
}
|
|
301
|
-
;(item as Record<string, unknown>).status_label = status?.label ?? status?.value ?? null
|
|
302
|
-
}
|
|
303
|
-
})
|
|
304
|
-
},
|
|
311
|
+
afterList: (payload, ctx) => enrichShipmentListResponse(payload, ctx),
|
|
305
312
|
},
|
|
306
313
|
})
|
|
307
314
|
|