@things-factory/operato-hub 4.3.744 → 4.3.745
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/dist-server/routers/api/restful-apis/v1/company/add-contact-points.js +71 -2
- package/dist-server/routers/api/restful-apis/v1/company/add-contact-points.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/company/index.js +1 -0
- package/dist-server/routers/api/restful-apis/v1/company/index.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/company/update-contact-points.js +243 -0
- package/dist-server/routers/api/restful-apis/v1/company/update-contact-points.js.map +1 -0
- package/dist-server/routers/api/restful-apis/v1/utils/params.js +109 -28
- package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/index.js +1 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/index.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.js +365 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.js.map +1 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js +946 -19
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js.map +1 -1
- package/openapi/v1/contact-point.yaml +266 -0
- package/openapi/v1/inbound.yaml +349 -0
- package/openapi/v1/outbound.yaml +256 -150
- package/package.json +18 -18
- package/server/routers/api/restful-apis/v1/company/add-contact-points.ts +91 -2
- package/server/routers/api/restful-apis/v1/company/index.ts +1 -0
- package/server/routers/api/restful-apis/v1/company/update-contact-points.ts +267 -0
- package/server/routers/api/restful-apis/v1/utils/params.ts +109 -28
- package/server/routers/api/restful-apis/v1/warehouse/index.ts +1 -0
- package/server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.ts +429 -0
- package/server/routers/api/restful-apis/v1/warehouse/update-release-order-details.ts +1122 -29
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { EntityManager, getConnection, SelectQueryBuilder, In } from 'typeorm'
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
3
|
+
|
|
4
|
+
import { restfulApiRouter as router } from '@things-factory/api'
|
|
5
|
+
import { Bizplace, ContactPoint, getCompanyBizplace } from '@things-factory/biz-base'
|
|
6
|
+
import { ProductDetail } from '@things-factory/product-base'
|
|
7
|
+
import {
|
|
8
|
+
ArrivalNotice,
|
|
9
|
+
ORDER_PRODUCT_STATUS,
|
|
10
|
+
ORDER_STATUS,
|
|
11
|
+
ORDER_TYPES,
|
|
12
|
+
OrderProduct
|
|
13
|
+
} from '@things-factory/sales-base'
|
|
14
|
+
import { WorksheetDetail } from '@things-factory/worksheet-base'
|
|
15
|
+
import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
|
|
16
|
+
import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
|
|
17
|
+
|
|
18
|
+
// Allowed statuses for update
|
|
19
|
+
const ALLOWED_STATUSES = [
|
|
20
|
+
ORDER_STATUS.PENDING,
|
|
21
|
+
ORDER_STATUS.PENDING_RECEIVE,
|
|
22
|
+
ORDER_STATUS.INTRANSIT,
|
|
23
|
+
ORDER_STATUS.ARRIVED,
|
|
24
|
+
ORDER_STATUS.READY_TO_UNLOAD
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
router.post(
|
|
28
|
+
'/v1/warehouse/update-arrival-notice',
|
|
29
|
+
businessMiddleware,
|
|
30
|
+
validationMiddleware,
|
|
31
|
+
loggingMiddleware,
|
|
32
|
+
async (context, next) => {
|
|
33
|
+
try {
|
|
34
|
+
const { domain } = context.state
|
|
35
|
+
const bodyReq = context.request.body.data
|
|
36
|
+
|
|
37
|
+
await getConnection().transaction(async (tx: EntityManager) => {
|
|
38
|
+
// Validate arrival notice exists and get it
|
|
39
|
+
if (!bodyReq.arrivalNoticeId && !bodyReq.ganNo) {
|
|
40
|
+
throw new ApiError('E01', 'arrivalNoticeId or ganNo is required')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Build where clause: arrivalNoticeId maps to id, ganNo maps to name
|
|
44
|
+
const whereClause: any = { domain }
|
|
45
|
+
if (bodyReq.arrivalNoticeId) {
|
|
46
|
+
whereClause.id = bodyReq.arrivalNoticeId
|
|
47
|
+
}
|
|
48
|
+
if (bodyReq.ganNo) {
|
|
49
|
+
whereClause.name = bodyReq.ganNo
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const foundArrivalNotice: ArrivalNotice = await tx.getRepository(ArrivalNotice).findOne({
|
|
53
|
+
where: whereClause,
|
|
54
|
+
relations: ['bizplace', 'orderProducts', 'orderProducts.product', 'orderProducts.productDetail']
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (!foundArrivalNotice) {
|
|
58
|
+
const searchCriteria = []
|
|
59
|
+
if (bodyReq.arrivalNoticeId) searchCriteria.push(`id: ${bodyReq.arrivalNoticeId}`)
|
|
60
|
+
if (bodyReq.ganNo) searchCriteria.push(`ganNo: ${bodyReq.ganNo}`)
|
|
61
|
+
throw new ApiError('E04', `Arrival notice not found: ${searchCriteria.join(', ')}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validate status
|
|
65
|
+
if (!ALLOWED_STATUSES.includes(foundArrivalNotice.status)) {
|
|
66
|
+
throw new ApiError(
|
|
67
|
+
'E04',
|
|
68
|
+
`Arrival notice status "${
|
|
69
|
+
foundArrivalNotice.status
|
|
70
|
+
}" does not allow updates. Allowed statuses: ${ALLOWED_STATUSES.join(', ')}`
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update arrival notice fields if provided
|
|
75
|
+
const updateData: any = {
|
|
76
|
+
updater: context.state.user
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (bodyReq.hasOwnProperty('refNo')) updateData.refNo = bodyReq.refNo
|
|
80
|
+
if (bodyReq.hasOwnProperty('refNo2')) updateData.refNo2 = bodyReq.refNo2
|
|
81
|
+
if (bodyReq.hasOwnProperty('refNo3')) updateData.refNo3 = bodyReq.refNo3
|
|
82
|
+
if (bodyReq.hasOwnProperty('etaDate')) updateData.etaDate = bodyReq.etaDate
|
|
83
|
+
if (bodyReq.hasOwnProperty('ownTransport')) updateData.ownTransport = bodyReq.ownTransport
|
|
84
|
+
if (bodyReq.hasOwnProperty('importCargo')) updateData.importCargo = bodyReq.importCargo
|
|
85
|
+
if (bodyReq.hasOwnProperty('container')) updateData.container = bodyReq.container
|
|
86
|
+
if (bodyReq.hasOwnProperty('containerNo')) updateData.containerNo = bodyReq.containerNo
|
|
87
|
+
if (bodyReq.hasOwnProperty('containerSize')) updateData.containerSize = bodyReq.containerSize
|
|
88
|
+
if (bodyReq.hasOwnProperty('truckNo')) updateData.truckNo = bodyReq.truckNo
|
|
89
|
+
if (bodyReq.hasOwnProperty('deliveryOrderNo')) updateData.deliveryOrderNo = bodyReq.deliveryOrderNo
|
|
90
|
+
if (bodyReq.hasOwnProperty('remark')) updateData.remark = bodyReq.remark
|
|
91
|
+
if (bodyReq.hasOwnProperty('description')) updateData.description = bodyReq.description
|
|
92
|
+
|
|
93
|
+
// Update supplier if provided
|
|
94
|
+
if (bodyReq.hasOwnProperty('supplierId')) {
|
|
95
|
+
if (bodyReq.supplierId) {
|
|
96
|
+
const foundSupplier: ContactPoint = await tx.getRepository(ContactPoint).findOne({
|
|
97
|
+
where: { id: bodyReq.supplierId }
|
|
98
|
+
})
|
|
99
|
+
if (!foundSupplier) {
|
|
100
|
+
throw new ApiError('E04', `Supplier not found: ${bodyReq.supplierId}`)
|
|
101
|
+
}
|
|
102
|
+
updateData.supplier = foundSupplier
|
|
103
|
+
} else {
|
|
104
|
+
updateData.supplier = null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Handle order products: add, update, remove
|
|
109
|
+
if (bodyReq.orderProducts && Array.isArray(bodyReq.orderProducts)) {
|
|
110
|
+
const existingOrderProducts = foundArrivalNotice.orderProducts || []
|
|
111
|
+
const orderProductsToAdd = []
|
|
112
|
+
const orderProductsToUpdate = []
|
|
113
|
+
const orderProductIdsToRemove = []
|
|
114
|
+
|
|
115
|
+
// Helper function to find matching order product by id, sku, refCode, batchId, packingType, or refItemId
|
|
116
|
+
const findMatchingOrderProduct = (orderProductReq: any): OrderProduct | null => {
|
|
117
|
+
// Match by ID if provided
|
|
118
|
+
if (orderProductReq.id) {
|
|
119
|
+
const found = existingOrderProducts.find(op => op.id === orderProductReq.id)
|
|
120
|
+
if (found) return found
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Extract matching fields
|
|
124
|
+
const sku = orderProductReq.product?.sku || orderProductReq.sku
|
|
125
|
+
const refCode =
|
|
126
|
+
orderProductReq.product?.refCode || orderProductReq.productDetail?.refCode || orderProductReq.refCode
|
|
127
|
+
const batchId = orderProductReq.batchId
|
|
128
|
+
const packingType = orderProductReq.packingType
|
|
129
|
+
const refItemId = orderProductReq.refItemId
|
|
130
|
+
|
|
131
|
+
// Need at least one matching field (besides id)
|
|
132
|
+
if (!sku && !refCode && !batchId && !packingType && !refItemId) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Match by provided fields (all provided fields must match)
|
|
137
|
+
return (
|
|
138
|
+
existingOrderProducts.find(op => {
|
|
139
|
+
const matchesSku = !sku || op.product?.sku === sku
|
|
140
|
+
const matchesRefCode = !refCode || (op.productDetail as any)?.refCode === refCode
|
|
141
|
+
const matchesBatchId = !batchId || op.batchId === batchId
|
|
142
|
+
const matchesPackingType = !packingType || op.packingType === packingType
|
|
143
|
+
const matchesRefItemId = !refItemId || op.refItemId === refItemId
|
|
144
|
+
|
|
145
|
+
return matchesSku && matchesRefCode && matchesBatchId && matchesPackingType && matchesRefItemId
|
|
146
|
+
}) || null
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Process each order product in the request
|
|
151
|
+
for (const orderProductReq of bodyReq.orderProducts) {
|
|
152
|
+
// Handle removal request
|
|
153
|
+
if (orderProductReq._action === 'remove') {
|
|
154
|
+
const matchingOP = findMatchingOrderProduct(orderProductReq)
|
|
155
|
+
if (!matchingOP) {
|
|
156
|
+
throw new ApiError(
|
|
157
|
+
'E04',
|
|
158
|
+
`Order product not found for removal. Provide id, sku, refCode, batchId, packingType, or refItemId to identify the item.`
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
orderProductIdsToRemove.push(matchingOP.id)
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Try to find existing order product for update
|
|
166
|
+
const matchingOP = findMatchingOrderProduct(orderProductReq)
|
|
167
|
+
|
|
168
|
+
if (matchingOP) {
|
|
169
|
+
// Update existing order product
|
|
170
|
+
// Validate and prepare update data
|
|
171
|
+
const opUpdateData: any = {
|
|
172
|
+
updater: context.state.user
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Update quantity if provided (accepts 'qty' or 'packQty')
|
|
176
|
+
if (orderProductReq.hasOwnProperty('qty') || orderProductReq.hasOwnProperty('packQty')) {
|
|
177
|
+
const qty = orderProductReq.qty !== undefined ? orderProductReq.qty : orderProductReq.packQty
|
|
178
|
+
if (qty <= 0) {
|
|
179
|
+
throw new ApiError('E01', 'Quantity must be greater than 0')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Round quantity based on product's decimal support
|
|
183
|
+
if (!matchingOP.product.isInventoryDecimal) {
|
|
184
|
+
opUpdateData.packQty = Math.round(qty)
|
|
185
|
+
} else {
|
|
186
|
+
opUpdateData.packQty = parseFloat(qty.toFixed(3))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Recalculate total UOM value
|
|
190
|
+
if (matchingOP.productDetail) {
|
|
191
|
+
opUpdateData.totalUomValue = `${(
|
|
192
|
+
opUpdateData.packQty * (matchingOP.productDetail.uomValue || 1)
|
|
193
|
+
).toFixed(2)} ${matchingOP.productDetail.uom}`
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Update other fields if provided
|
|
198
|
+
if (orderProductReq.hasOwnProperty('batchId')) opUpdateData.batchId = orderProductReq.batchId
|
|
199
|
+
if (orderProductReq.hasOwnProperty('unitPrice'))
|
|
200
|
+
opUpdateData.unitPrice = orderProductReq.unitPrice ? parseFloat(orderProductReq.unitPrice) : null
|
|
201
|
+
if (orderProductReq.hasOwnProperty('manufactureDate'))
|
|
202
|
+
opUpdateData.manufactureDate = orderProductReq.manufactureDate || null
|
|
203
|
+
if (orderProductReq.hasOwnProperty('remark')) opUpdateData.remark = orderProductReq.remark || null
|
|
204
|
+
if (orderProductReq.hasOwnProperty('refItemId')) opUpdateData.refItemId = orderProductReq.refItemId
|
|
205
|
+
|
|
206
|
+
orderProductsToUpdate.push({
|
|
207
|
+
id: matchingOP.id,
|
|
208
|
+
...opUpdateData
|
|
209
|
+
})
|
|
210
|
+
} else {
|
|
211
|
+
// No matching order product found - add as new
|
|
212
|
+
orderProductsToAdd.push(orderProductReq)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Remove order products
|
|
217
|
+
// First, delete any worksheet details that reference these order products
|
|
218
|
+
if (orderProductIdsToRemove.length > 0) {
|
|
219
|
+
// Find and delete worksheet details that reference the order products to be deleted
|
|
220
|
+
const worksheetDetailsToDelete: WorksheetDetail[] = await tx.getRepository(WorksheetDetail).find({
|
|
221
|
+
where: {
|
|
222
|
+
targetProduct: In(orderProductIdsToRemove),
|
|
223
|
+
domain
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if (worksheetDetailsToDelete.length > 0) {
|
|
228
|
+
const worksheetDetailIds = worksheetDetailsToDelete.map(wsd => wsd.id)
|
|
229
|
+
await tx.getRepository(WorksheetDetail).delete({
|
|
230
|
+
id: In(worksheetDetailIds)
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Now delete the order products
|
|
235
|
+
await tx.getRepository(OrderProduct).delete({
|
|
236
|
+
id: In(orderProductIdsToRemove),
|
|
237
|
+
arrivalNotice: foundArrivalNotice
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Update existing order products
|
|
242
|
+
for (const opUpdate of orderProductsToUpdate) {
|
|
243
|
+
await tx.getRepository(OrderProduct).update(opUpdate.id, opUpdate)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add new order products
|
|
247
|
+
if (orderProductsToAdd.length > 0) {
|
|
248
|
+
const massagedData = await massageOrderItems(foundArrivalNotice, orderProductsToAdd, context, tx)
|
|
249
|
+
for (const newOrderProduct of massagedData.orderProducts) {
|
|
250
|
+
const orderProduct = Object.assign(new OrderProduct(), {
|
|
251
|
+
...newOrderProduct,
|
|
252
|
+
name: uuidv4(),
|
|
253
|
+
domain: domain,
|
|
254
|
+
bizplace: foundArrivalNotice.bizplace,
|
|
255
|
+
arrivalNotice: foundArrivalNotice,
|
|
256
|
+
type: ORDER_TYPES.ARRIVAL_NOTICE,
|
|
257
|
+
status:
|
|
258
|
+
existingOrderProducts.length > 0
|
|
259
|
+
? existingOrderProducts[0].status
|
|
260
|
+
: ORDER_PRODUCT_STATUS.PENDING_RECEIVE,
|
|
261
|
+
creator: context.state.user,
|
|
262
|
+
updater: context.state.user
|
|
263
|
+
})
|
|
264
|
+
await tx.getRepository(OrderProduct).save(orderProduct)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Save arrival notice updates
|
|
270
|
+
await tx.getRepository(ArrivalNotice).update(foundArrivalNotice.id, updateData)
|
|
271
|
+
|
|
272
|
+
// Fetch updated arrival notice with relations
|
|
273
|
+
const updatedArrivalNotice: ArrivalNotice = await tx.getRepository(ArrivalNotice).findOne({
|
|
274
|
+
where: { id: foundArrivalNotice.id },
|
|
275
|
+
relations: ['bizplace', 'orderProducts', 'orderProducts.product', 'orderProducts.productDetail', 'supplier']
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// Format response
|
|
279
|
+
const resultOrderProducts = (updatedArrivalNotice.orderProducts || []).map(op => ({
|
|
280
|
+
id: op.id,
|
|
281
|
+
product: { name: op.product.name, sku: op.product.sku },
|
|
282
|
+
batchId: op.batchId,
|
|
283
|
+
packingType: op.packingType,
|
|
284
|
+
packingSize: op.packingSize,
|
|
285
|
+
qty: op.packQty,
|
|
286
|
+
palletQty: op.palletQty,
|
|
287
|
+
uom: op.uom,
|
|
288
|
+
uomValue: op.uomValue,
|
|
289
|
+
refItemId: op?.refItemId,
|
|
290
|
+
unitPrice: op.unitPrice,
|
|
291
|
+
manufactureDate: op.manufactureDate,
|
|
292
|
+
remark: op.remark
|
|
293
|
+
}))
|
|
294
|
+
|
|
295
|
+
const data = {
|
|
296
|
+
id: updatedArrivalNotice.id,
|
|
297
|
+
ganNo: updatedArrivalNotice.name,
|
|
298
|
+
refNo: updatedArrivalNotice.refNo,
|
|
299
|
+
refNo2: updatedArrivalNotice.refNo2,
|
|
300
|
+
refNo3: updatedArrivalNotice.refNo3,
|
|
301
|
+
etaDate: updatedArrivalNotice.etaDate,
|
|
302
|
+
ownTransport: updatedArrivalNotice.ownTransport,
|
|
303
|
+
importCargo: updatedArrivalNotice.importCargo,
|
|
304
|
+
container: updatedArrivalNotice.container,
|
|
305
|
+
containerNo: updatedArrivalNotice.containerNo,
|
|
306
|
+
containerSize: updatedArrivalNotice.containerSize,
|
|
307
|
+
truckNo: updatedArrivalNotice.truckNo,
|
|
308
|
+
deliveryOrderNo: updatedArrivalNotice.deliveryOrderNo,
|
|
309
|
+
remark: updatedArrivalNotice.remark,
|
|
310
|
+
description: updatedArrivalNotice.description,
|
|
311
|
+
status: updatedArrivalNotice.status,
|
|
312
|
+
orderProducts: resultOrderProducts,
|
|
313
|
+
supplier: updatedArrivalNotice?.supplier
|
|
314
|
+
? {
|
|
315
|
+
id: updatedArrivalNotice.supplier.id,
|
|
316
|
+
name: updatedArrivalNotice.supplier.name,
|
|
317
|
+
email: updatedArrivalNotice.supplier.email,
|
|
318
|
+
phone: updatedArrivalNotice.supplier.phone
|
|
319
|
+
}
|
|
320
|
+
: null
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
context.body = {
|
|
324
|
+
responseCode: '200',
|
|
325
|
+
message: 'success',
|
|
326
|
+
data
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
} catch (e) {
|
|
330
|
+
if (e instanceof ApiError) ApiErrorHandler(context, e)
|
|
331
|
+
else throwInternalServerError(context, e)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
async function massageOrderItems(
|
|
337
|
+
arrivalNotice: ArrivalNotice,
|
|
338
|
+
inputOrderProducts: any[],
|
|
339
|
+
context: any,
|
|
340
|
+
tx: EntityManager
|
|
341
|
+
): Promise<{ orderProducts: OrderProduct[] }> {
|
|
342
|
+
let orderProducts: OrderProduct[] = []
|
|
343
|
+
|
|
344
|
+
// Get company bizplace for product lookup
|
|
345
|
+
const customerCompanyBizplace: Bizplace = await getCompanyBizplace(null, null, arrivalNotice.bizplaceId, tx)
|
|
346
|
+
|
|
347
|
+
await Promise.all(
|
|
348
|
+
inputOrderProducts.map(async inputOrderItem => {
|
|
349
|
+
const sku: string = inputOrderItem.product?.sku || inputOrderItem.sku
|
|
350
|
+
const refCode: string = inputOrderItem.product?.refCode || inputOrderItem.refCode
|
|
351
|
+
const packingType: string = inputOrderItem.packingType
|
|
352
|
+
const packingSize: string = inputOrderItem.packingSize
|
|
353
|
+
const packQty = inputOrderItem.qty !== undefined ? inputOrderItem.qty : inputOrderItem.packQty
|
|
354
|
+
|
|
355
|
+
if (!sku && !refCode) {
|
|
356
|
+
throw new ApiError('E01', 'sku or refCode is required')
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!packQty || packQty <= 0) {
|
|
360
|
+
throw new ApiError('E01', 'Quantity must be greater than 0')
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const qb: SelectQueryBuilder<ProductDetail> = tx
|
|
364
|
+
.getRepository(ProductDetail)
|
|
365
|
+
.createQueryBuilder('pd')
|
|
366
|
+
.innerJoinAndSelect('pd.product', 'prod')
|
|
367
|
+
.where('prod.bizplace_id = :bizplaceId', { bizplaceId: customerCompanyBizplace.id })
|
|
368
|
+
|
|
369
|
+
if (refCode) {
|
|
370
|
+
qb.andWhere('pd.ref_code = :refCode', { refCode })
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (sku) {
|
|
374
|
+
qb.andWhere('prod.sku = :sku', { sku })
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (packingType) {
|
|
378
|
+
qb.andWhere('pd.packing_type = :packingType', { packingType })
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (packingSize) {
|
|
382
|
+
qb.andWhere('pd.packing_size = :packingSize', { packingSize })
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!refCode && !packingType && !packingSize) {
|
|
386
|
+
qb.andWhere('pd.is_default = :isDefault', { isDefault: true })
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const productDetail: ProductDetail = await qb.getOne()
|
|
390
|
+
|
|
391
|
+
if (productDetail) {
|
|
392
|
+
// Validate packQty based on product's isInventoryDecimal
|
|
393
|
+
let validatedPackQty: number
|
|
394
|
+
if (!productDetail.product.isInventoryDecimal) {
|
|
395
|
+
validatedPackQty = Math.round(packQty)
|
|
396
|
+
} else {
|
|
397
|
+
validatedPackQty = parseFloat(packQty.toFixed(3))
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
let newOrderProduct: OrderProduct = {
|
|
401
|
+
product: productDetail.product,
|
|
402
|
+
productDetail: productDetail,
|
|
403
|
+
packingType: productDetail.packingType,
|
|
404
|
+
packingSize: productDetail.packingSize,
|
|
405
|
+
uom: productDetail.uom,
|
|
406
|
+
uomValue: productDetail.uomValue,
|
|
407
|
+
packQty: validatedPackQty,
|
|
408
|
+
totalUomValue: `${(validatedPackQty * (productDetail.uomValue || 1)).toFixed(2)} ${productDetail.uom}`,
|
|
409
|
+
batchId: inputOrderItem.batchId || null,
|
|
410
|
+
status: ORDER_PRODUCT_STATUS.PENDING_RECEIVE,
|
|
411
|
+
unitPrice: inputOrderItem?.unitPrice ? parseFloat(inputOrderItem.unitPrice) : null,
|
|
412
|
+
manufactureDate: inputOrderItem?.manufactureDate || null,
|
|
413
|
+
remark: inputOrderItem?.remark || null,
|
|
414
|
+
refItemId: inputOrderItem?.refItemId || null
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
orderProducts.push(newOrderProduct)
|
|
418
|
+
} else {
|
|
419
|
+
// Product not found - provide clear error message
|
|
420
|
+
const identifier = sku ? `sku: ${sku}` : `refCode: ${refCode}`
|
|
421
|
+
const packingInfo = packingType ? `, packingType: ${packingType}` : ''
|
|
422
|
+
const packingSizeInfo = packingSize ? `, packingSize: ${packingSize}` : ''
|
|
423
|
+
throw new ApiError('E04', `Product not found: ${identifier}${packingInfo}${packingSizeInfo}`)
|
|
424
|
+
}
|
|
425
|
+
})
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
return { orderProducts }
|
|
429
|
+
}
|