@things-factory/operato-hub 4.3.677 → 4.3.679

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.
@@ -40,13 +40,19 @@ import {
40
40
  } from '@things-factory/sales-base'
41
41
  import { Setting } from '@things-factory/setting-base'
42
42
  import { Domain } from '@things-factory/shell'
43
- import { Inventory, INVENTORY_STATUS, LOCATION_TYPE, ProductDetailStock } from '@things-factory/warehouse-base'
43
+ import {
44
+ Inventory,
45
+ INVENTORY_STATUS,
46
+ LOCATION_TYPE,
47
+ ProductDetailStock,
48
+ Warehouse
49
+ } from '@things-factory/warehouse-base'
44
50
 
45
51
  import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
46
52
  import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
47
53
  import { getTmsService } from '@things-factory/integration-lmd'
48
54
 
49
- const debug = require('debug')('things-factory:operato-hub:restful-api:unstable:add-release-order')
55
+ const debug = require('debug')('things-factory:operato-hub:restful-api:v1:add-release-order')
50
56
  const apiVersion = 'v1'
51
57
 
52
58
  router.post(
@@ -202,6 +208,28 @@ router.post(
202
208
  throw new ApiError('E01', 'Please provide batch id for all inventories')
203
209
  }
204
210
 
211
+ // validate warehouseCode (name in warehouses table) if provided on any line
212
+ const providedWarehouseCodes = Array.from(
213
+ new Set(
214
+ (bodyReq.orderInventories || [])
215
+ .map((it: any) => it?.warehouseCode && String(it.warehouseCode).trim())
216
+ .filter(Boolean)
217
+ )
218
+ ) as string[]
219
+
220
+ if (providedWarehouseCodes.length > 0) {
221
+ const warehouses: Warehouse[] = await tx.getRepository(Warehouse).find({
222
+ where: { domain, name: In(providedWarehouseCodes) }
223
+ })
224
+
225
+ const foundNames = new Set(warehouses.map(w => w.name))
226
+ const missing = providedWarehouseCodes.filter(code => !foundNames.has(code))
227
+
228
+ if (missing.length > 0) {
229
+ throw new ApiError('E04', `warehouseCode not exist in master: ${missing.join(', ')}`)
230
+ }
231
+ }
232
+
205
233
  // massage data
206
234
  massagedData = await massageOrderItems(releaseGood, bodyReq.orderInventories, context) //double check this function
207
235
 
@@ -212,36 +240,185 @@ router.post(
212
240
  // validation
213
241
  await Promise.all(
214
242
  massagedData.combinedItems.map(async item => {
215
- let itemList = await tx.query(
243
+ const hasWarehouse = !!item.warehouseCode
244
+ if (hasWarehouse) {
245
+ // Validate availability within the specific warehouse across pickable locations (exclude Q/R/D/STORAGE)
246
+ const rows = await tx.query(
247
+ `
248
+ select
249
+ coalesce(sum(
250
+ case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
251
+ then 0
252
+ else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
253
+ end
254
+ ), 0) as total_available_qty,
255
+ coalesce(sum(
256
+ case when (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0)) < 0
257
+ then 0
258
+ else (iv.uom_value - greatest(coalesce(iv.locked_uom_value,0),0) - greatest(coalesce(pds.unassigned_uom_value,0),0))
259
+ end
260
+ ), 0) as total_available_uom_value
261
+ from inventories iv
262
+ left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
263
+ inner join locations loc on loc.id = iv.location_id
264
+ inner join warehouses w on w.id = loc.warehouse_id
265
+ inner join products p on p.id = iv.product_id
266
+ where iv.domain_id = $1
267
+ and iv.bizplace_id = $2
268
+ and iv.product_id = $3
269
+ and iv.packing_type = $4
270
+ and iv.packing_size = $5
271
+ and iv.uom = $6
272
+ and iv.status = 'STORED'
273
+ and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
274
+ and iv.obsolete = false
275
+ and (case when iv.expiration_date is not null and p.min_outbound_shelf_life is not null then CURRENT_DATE < iv.expiration_date - p.min_outbound_shelf_life else true end)
276
+ and w.name = $7
277
+ and not exists (
278
+ select 1 from inventories i
279
+ where i.domain_id = $1 and i.bizplace_id = $2 and i.product_id = $3 and i.packing_type = $4 and i.packing_size = $5 and i.uom = $6 and i.status = 'STORED' and i.lock_inventory is true
280
+ )
281
+ `,
282
+ [
283
+ domain.id,
284
+ customerBizplace.id,
285
+ item.product.id,
286
+ item.packingType,
287
+ item.packingSize,
288
+ item.uom,
289
+ String(item.warehouseCode).trim()
290
+ ]
291
+ )
292
+
293
+ const totalQty = parseFloat(rows?.[0]?.total_available_qty || '0')
294
+ const totalUomVal = parseFloat(rows?.[0]?.total_available_uom_value || '0')
295
+
296
+ let reqQty = item.releaseQty
297
+ let reqUomVal = item.releaseUomValue
298
+
299
+ if (!item.product.isInventoryDecimal) {
300
+ reqQty = Math.round(reqQty)
301
+ } else {
302
+ reqQty = Math.round(reqQty * 1000) / 1000
303
+ }
304
+
305
+ if (totalQty + 1e-6 < reqQty || totalUomVal + 1e-6 < reqUomVal) {
306
+ throw new ApiError('E01', 'INSUFFICIENT_STOCK')
307
+ }
308
+ } else {
309
+ // Fallback to existing global view-based validation when no warehouseCode provided
310
+ let itemList = await tx.query(
311
+ `
312
+ select wboi.*
313
+ from warehouse_bizplace_onhand_inventories wboi
314
+ left join (
315
+ select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom,
316
+ sum(i2.qty) as storage_qty,
317
+ sum(i2.uom_value) as storage_uom_value
318
+ from inventories i2
319
+ inner join locations l2 on l2.id = i2.location_id
320
+ inner join warehouses w on w.id = l2.warehouse_id
321
+ where i2.domain_id = $1
322
+ and i2.bizplace_id = $2
323
+ and i2.product_id = $3
324
+ and i2.packing_type = $4
325
+ and i2.packing_size = $5
326
+ and i2.uom = $6
327
+ and i2.status = 'STORED'
328
+ and l2.type = 'STORAGE'
329
+ group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
330
+ ) storageInv
331
+ on storageInv.product_id = wboi.product_id
332
+ and storageInv.domain_id = wboi.domain_id
333
+ and storageInv.bizplace_id = wboi.bizplace_id
334
+ and storageInv.packing_type = wboi.packing_type
335
+ and storageInv.packing_size = wboi.packing_size
336
+ and storageInv.uom = wboi.uom
337
+ left join (
338
+ select i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
339
+ from inventories i
340
+ where i.domain_id = $1
341
+ and i.bizplace_id = $2
342
+ and i.product_id = $3
343
+ and i.packing_type = $4
344
+ and i.packing_size = $5
345
+ and i.uom = $6
346
+ and i.status = 'STORED'
347
+ group by i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
348
+ ) lockInv
349
+ on lockInv.product_id = wboi.product_id
350
+ and lockInv.domain_id = wboi.domain_id
351
+ and lockInv.bizplace_id = wboi.bizplace_id
352
+ and lockInv.packing_type = wboi.packing_type
353
+ and lockInv.packing_size = wboi.packing_size
354
+ and lockInv.uom = wboi.uom
355
+ where wboi.domain_id = $1
356
+ and wboi.bizplace_id = $2
357
+ and wboi.group_type = 'SINGLE'
358
+ and wboi.product_id = $3
359
+ and wboi.packing_type = $4
360
+ and wboi.packing_size = $5
361
+ and wboi.uom = $6
362
+ and lockInv.lock_inventory is not true
363
+ and (wboi.remain_qty - wboi.transfer_qty - coalesce(storageInv.storage_qty, 0)) >= $7
364
+ and (wboi.remain_uom_value - wboi.transfer_uom_value - coalesce(storageInv.storage_uom_value, 0)) >= $8
365
+ `,
366
+ [
367
+ domain.id,
368
+ customerBizplace.id,
369
+ item.product.id,
370
+ item.packingType,
371
+ item.packingSize,
372
+ item.uom,
373
+ item.releaseQty,
374
+ item.releaseUomValue
375
+ ]
376
+ )
377
+ if (itemList.length <= 0) {
378
+ throw new ApiError('E01', 'INSUFFICIENT_STOCK')
379
+ }
380
+ console.log()
381
+ }
382
+ })
383
+ )
384
+ }
385
+
386
+ // inv with batchId and assignment enabled: validate availability by batch before creating order
387
+ if (!invWithoutBatchId && worksheetPickingAssignment?.value === 'true') {
388
+ await Promise.all(
389
+ massagedData.combinedItems.map(async item => {
390
+ const batchId = item?.batchId && item.batchId !== '-' ? String(item.batchId).trim() : null
391
+ if (!batchId) return
392
+
393
+ const warehouseName = item?.warehouseCode ? String(item.warehouseCode).trim() : null
394
+
395
+ const rows = await tx.query(
216
396
  `
217
- select wboi.* from warehouse_bizplace_onhand_inventories wboi
218
- left join (
219
- select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom, sum(i2.qty) as storage_qty, sum(i2.uom_value) as storage_uom_value from inventories i2
220
- inner join locations l2 on l2.id = i2.location_id
221
- where i2.domain_id = $1 and i2.bizplace_id = $2
222
- and i2.product_id = $3 and i2.packing_type= $4 and i2.packing_size= $5 and i2.uom = $6 and i2.status='STORED'
223
- and l2.type='STORAGE'
224
- group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
225
- ) storageInv on storageInv.product_id = wboi.product_id and storageInv.domain_id=wboi.domain_id and storageInv.bizplace_id=wboi.bizplace_id
226
- and storageInv.packing_type=wboi.packing_type and storageInv.packing_size=wboi.packing_size and storageInv.uom=wboi.uom
227
- left join (
228
- select i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory from inventories i
229
- where i.domain_id = $1 and i.bizplace_id = $2
230
- and i.product_id = $3 and i.packing_type= $4 and i.packing_size= $5 and i.uom = $6 and i.status='STORED'
231
- group by i.product_id, i.domain_id, i.bizplace_id, i.packing_type, i.packing_size, i.uom, i.lock_inventory
232
- ) lockInv on lockInv.product_id = wboi.product_id and lockInv.domain_id=wboi.domain_id and lockInv.bizplace_id=wboi.bizplace_id
233
- and lockInv.packing_type=wboi.packing_type and lockInv.packing_size=wboi.packing_size and lockInv.uom=wboi.uom
234
- where wboi."domain_id"= $1 and
235
- wboi."bizplace_id" = $2 and
236
- wboi."group_type" = 'SINGLE' and
237
- wboi."product_id" = $3 and
238
- wboi."packing_type" = $4 and
239
- wboi."packing_size" = $5 and
240
- wboi."uom" = $6 and
241
- lockInv.lock_inventory is not true and
242
- (wboi."remain_qty" - wboi."transfer_qty" - coalesce(storageInv.storage_qty, 0)) >= $7 and
243
- (wboi."remain_uom_value" - wboi."transfer_uom_value" - coalesce(storageInv.storage_uom_value, 0)) >= $8
244
- `,
397
+ select
398
+ coalesce(sum(
399
+ case when (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0)) < 0
400
+ then 0
401
+ else (iv.qty - greatest(coalesce(iv.locked_qty,0),0) - greatest(coalesce(pds.unassigned_qty,0),0))
402
+ end
403
+ ), 0) as total_available_qty
404
+ from inventories iv
405
+ left join product_detail_stocks pds on pds.product_detail_id = iv.product_detail_id
406
+ inner join locations loc on loc.id = iv.location_id
407
+ inner join warehouses w on w.id = loc.warehouse_id
408
+ inner join products p on p.id = iv.product_id
409
+ where iv.domain_id = $1
410
+ and iv.bizplace_id = $2
411
+ and iv.product_id = $3
412
+ and iv.packing_type = $4
413
+ and iv.packing_size = $5
414
+ and iv.uom = $6
415
+ and iv.status = 'STORED'
416
+ and loc.type not in ('QUARANTINE','RESERVE','DAMAGE','STORAGE')
417
+ and iv.obsolete = false
418
+ and (case when iv.expiration_date is not null and p.min_outbound_shelf_life is not null then CURRENT_DATE < iv.expiration_date - p.min_outbound_shelf_life else true end)
419
+ and iv.batch_id = $7
420
+ and ( $8::text is null or w.name = $8::text )
421
+ `,
245
422
  [
246
423
  domain.id,
247
424
  customerBizplace.id,
@@ -249,14 +426,25 @@ router.post(
249
426
  item.packingType,
250
427
  item.packingSize,
251
428
  item.uom,
252
- item.releaseQty,
253
- item.releaseUomValue
429
+ batchId,
430
+ warehouseName
254
431
  ]
255
432
  )
256
- if (itemList.length <= 0) {
433
+
434
+ const totalQty = parseFloat(rows?.[0]?.total_available_qty || '0')
435
+
436
+ let reqQty = item.releaseQty
437
+
438
+ // normalize comparison for decimal vs non-decimal products
439
+ if (!item.product.isInventoryDecimal) {
440
+ reqQty = Math.round(reqQty)
441
+ } else {
442
+ reqQty = Math.round(reqQty * 1000) / 1000
443
+ }
444
+
445
+ if (totalQty + 1e-6 < reqQty) {
257
446
  throw new ApiError('E01', 'INSUFFICIENT_STOCK')
258
447
  }
259
- console.log()
260
448
  })
261
449
  )
262
450
  }
@@ -645,7 +833,7 @@ async function createReleaseGood(
645
833
  let resultReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
646
834
  let combinedOrderInventories: OrderInventory[] = []
647
835
  let combinedOrderProducts: OrderProduct[] = []
648
- let savedOrderProducts = await Promise.all(
836
+ await Promise.all(
649
837
  orderProducts.map(async orderProduct => {
650
838
  let orderInventories: OrderInventory[] = orderProduct.orderInventories
651
839
  orderProduct = Object.assign(new OrderProduct(), {
@@ -655,7 +843,12 @@ async function createReleaseGood(
655
843
  bizplace: bizplace,
656
844
  releaseGood: resultReleaseGood,
657
845
  type: ORDER_TYPES.RELEASE_OF_GOODS,
658
- status: ORDER_PRODUCT_STATUS.ASSIGNED
846
+ status: ORDER_PRODUCT_STATUS.ASSIGNED,
847
+ // ensure batchId is persisted on order product even when assignment is enabled
848
+ batchId:
849
+ (orderProduct as any)?.batchId !== undefined && (orderProduct as any)?.batchId !== null
850
+ ? (orderProduct as any).batchId
851
+ : orderInventories?.[0]?.batchId || ''
659
852
  })
660
853
  let newOrderProduct = await tx.getRepository(OrderProduct).save(orderProduct)
661
854
 
@@ -665,6 +858,7 @@ async function createReleaseGood(
665
858
  packingSize: orderProduct.packingSize,
666
859
  releaseQty: orderProduct.releaseQty,
667
860
  releaseUomValue: orderProduct.releaseUomValue,
861
+ warehouseCode: orderProduct.warehouseCode,
668
862
  refItemId: orderProduct.refItemId
669
863
  })
670
864
 
@@ -688,7 +882,8 @@ async function createReleaseGood(
688
882
  packingType: orderInventory.packingType,
689
883
  packingSize: orderInventory.packingSize,
690
884
  releaseQty: orderInventory.releaseQty,
691
- releaseUomValue: orderInventory.releaseUomValue
885
+ releaseUomValue: orderInventory.releaseUomValue,
886
+ warehouseCode: orderInventory.warehouseCode
692
887
  })
693
888
  }
694
889
  }
@@ -720,7 +915,7 @@ async function massageOrderItems(
720
915
  inputOrderProducts = await inputOrderProducts.reduce(async (accPromise, itm, idx) => {
721
916
  const acc = await accPromise
722
917
  let { sku, refCode } = itm.product
723
- let { packingType, packingSize, batchId, releaseQty, sellingPrice, paidAmount, uom, refItemId } = itm
918
+ let { packingType, packingSize, batchId, releaseQty, sellingPrice, paidAmount, uom, refItemId, warehouseCode } = itm
724
919
 
725
920
  if (!sku && !refCode) {
726
921
  throw new ApiError('E01', 'sku or refCode not found')
@@ -730,14 +925,16 @@ async function massageOrderItems(
730
925
  throw new ApiError('E01', 'negative stock request')
731
926
  }
732
927
 
928
+ const normalizedWarehouseCode = warehouseCode ? String(warehouseCode).trim() : ''
733
929
  let existingIndex = acc.findIndex(
734
- itm =>
735
- itm?.sku == sku?.trim() &&
736
- itm?.refCode == refCode?.trim() &&
737
- itm?.packingType == packingType?.trim() &&
738
- itm?.packingSize == packingSize &&
739
- itm?.uom == uom &&
740
- itm?.batchId == (batchId?.trim() || '')
930
+ it =>
931
+ it?.sku == sku?.trim() &&
932
+ it?.refCode == refCode?.trim() &&
933
+ it?.packingType == packingType?.trim() &&
934
+ it?.packingSize == packingSize &&
935
+ it?.uom == uom &&
936
+ it?.batchId == (batchId?.trim() || '') &&
937
+ (it as any)?.warehouseCode == normalizedWarehouseCode
741
938
  )
742
939
 
743
940
  if (existingIndex >= 0 && releaseQty > 0) {
@@ -753,7 +950,8 @@ async function massageOrderItems(
753
950
  paidAmount: paidAmount,
754
951
  uom: uom,
755
952
  batchId: batchId?.trim() || '',
756
- refItemId: refItemId ?? ''
953
+ refItemId: refItemId ?? '',
954
+ warehouseCode: normalizedWarehouseCode
757
955
  })
758
956
  }
759
957
 
@@ -764,7 +962,6 @@ async function massageOrderItems(
764
962
  await Promise.all(
765
963
  inputOrderProducts.map(async inputOrderItem => {
766
964
  const sku: string = inputOrderItem.sku
767
- const uom: string = inputOrderItem?.uom
768
965
  const refCode: string = inputOrderItem.refCode
769
966
  const packingType: string = inputOrderItem.packingType
770
967
  const packingSize: string = inputOrderItem.packingSize
@@ -825,6 +1022,7 @@ async function massageOrderItems(
825
1022
  palletQty: 0,
826
1023
  actualPalletQty: 0,
827
1024
  type: 'RELEASE_OF_GOODS',
1025
+ warehouseCode: inputOrderItem?.warehouseCode || null,
828
1026
  status: ORDER_PRODUCT_STATUS.ASSIGNED,
829
1027
  batchId: inputOrderItem?.batchId || ''
830
1028
  }
@@ -837,10 +1035,16 @@ async function massageOrderItems(
837
1035
  palletQty: 0,
838
1036
  actualPalletQty: 0,
839
1037
  type: 'RELEASE_OF_GOODS',
1038
+ warehouseCode: inputOrderItem?.warehouseCode || null,
840
1039
  status: ORDER_INVENTORY_STATUS.PENDING_WORKSHEET
841
1040
  }
842
1041
  ]
843
1042
 
1043
+ // ensure warehouseCode is normalized
1044
+ ;(newOrderProduct as any).warehouseCode = newOrderProduct?.warehouseCode
1045
+ ? String(newOrderProduct.warehouseCode).trim()
1046
+ : null
1047
+
844
1048
  orderProducts.push(newOrderProduct)
845
1049
  }
846
1050
 
@@ -888,6 +1092,7 @@ async function massageOrderItems(
888
1092
  actualPalletQty: 0,
889
1093
  orderInventories: [],
890
1094
  type: 'RELEASE_OF_GOODS',
1095
+ warehouseCode: inputOrderItem?.warehouseCode || null,
891
1096
  status: ORDER_PRODUCT_STATUS.ASSIGNED,
892
1097
  batchId: ''
893
1098
  }
@@ -913,6 +1118,7 @@ async function massageOrderItems(
913
1118
  bundleProductSetting.bundleQty *
914
1119
  (productDetailBundle.uomValue <= 0 ? 1 : productDetailBundle.uomValue),
915
1120
  type: 'RELEASE_OF_GOODS',
1121
+ warehouseCode: inputOrderItem?.warehouseCode || null,
916
1122
  status: ORDER_INVENTORY_STATUS.PENDING_WORKSHEET
917
1123
  }
918
1124
 
@@ -941,7 +1147,7 @@ async function massageOrderItems(
941
1147
  let oi = op?.orderInventories || []
942
1148
  oi.forEach(itm => {
943
1149
  let { sku, refCode } = itm.product
944
- let { packingType, packingSize, batchId, releaseQty, product, productDetail, uom } = itm
1150
+ let { packingType, packingSize, batchId, releaseQty, product, productDetail, uom, warehouseCode } = itm
945
1151
 
946
1152
  if (!sku && !refCode) {
947
1153
  throw new ApiError('E04', t('error.sku or refCode not found'))
@@ -953,7 +1159,8 @@ async function massageOrderItems(
953
1159
  itm?.packingType == packingType?.trim() &&
954
1160
  itm?.packingSize == packingSize &&
955
1161
  itm?.batchId == batchId?.trim() &&
956
- itm?.uom == uom?.trim()
1162
+ itm?.uom == uom?.trim() &&
1163
+ (itm as any)?.warehouseCode == (warehouseCode ? String(warehouseCode).trim() : warehouseCode)
957
1164
  )
958
1165
 
959
1166
  if (existingIndex >= 0 && releaseQty > 0) {
@@ -961,7 +1168,7 @@ async function massageOrderItems(
961
1168
  acc[existingIndex].releaseUomValue =
962
1169
  acc[existingIndex].releaseUomValue + releaseQty * (productDetail.uomValue <= 0 ? 1 : productDetail.uomValue)
963
1170
  } else {
964
- let updateObj = {
1171
+ let updateObj: any = {
965
1172
  sku: sku?.trim(),
966
1173
  packingType: packingType?.trim(),
967
1174
  packingSize: packingSize,
@@ -974,6 +1181,7 @@ async function massageOrderItems(
974
1181
 
975
1182
  if (refCode) updateObj['refCode'] = refCode.trim()
976
1183
  updateObj['batchId'] = batchId ? batchId?.trim() : '-'
1184
+ if (warehouseCode) updateObj['warehouseCode'] = String(warehouseCode).trim()
977
1185
 
978
1186
  acc.push(updateObj)
979
1187
  }
@@ -1033,7 +1241,7 @@ async function assignToInventory(
1033
1241
  tx,
1034
1242
  worksheetPickingAssignment
1035
1243
  ) {
1036
- const { client, domain, user } = context.state
1244
+ const { domain, user } = context.state
1037
1245
 
1038
1246
  const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
1039
1247
  where: { domain, name: 'rule-for-picking-product' }
@@ -1158,9 +1366,14 @@ async function assignToInventory(
1158
1366
  LOCATION_TYPE.STORAGE
1159
1367
  ]
1160
1368
  let batchId = orderInventory[oiIdx].batchId
1161
- if (batchId) params.push(batchId)
1369
+ if (batchId) params.push(String(batchId).trim())
1162
1370
  params = [...params, ...queryStrings.values]
1163
1371
 
1372
+ const warehouseNameFilter = orderInventory[oiIdx]?.warehouseCode ? `AND w.name = $${params.length + 1}` : ''
1373
+ if (orderInventory[oiIdx]?.warehouseCode) {
1374
+ params.push(String(orderInventory[oiIdx].warehouseCode).trim())
1375
+ }
1376
+
1164
1377
  let query = `
1165
1378
  update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
1166
1379
  locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
@@ -1201,11 +1414,13 @@ async function assignToInventory(
1201
1414
  FROM "inventories" "iv"
1202
1415
  LEFT JOIN "product_detail_stocks" "pds" ON "pds"."product_detail_id" = "iv"."product_detail_id"
1203
1416
  INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
1417
+ INNER JOIN "warehouses" "w" ON "w"."id" = "loc"."warehouse_id"
1204
1418
  INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
1205
1419
  INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
1206
1420
  WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
1207
1421
  "iv"."domain_id" = $2 AND "iv"."bizplace_id" = $3 AND "iv"."packing_type" = $4 AND "iv"."packing_size" = $5 AND "iv"."product_id" = $6 AND "iv"."status" = $7 AND "loc"."type" NOT IN ($8, $9, $10)
1208
1422
  AND ${batchId ? `"iv"."batch_id" = $11` : `1=1`}
1423
+ ${warehouseNameFilter}
1209
1424
  AND "iv"."obsolete" = false AND case when "iv"."expiration_date" is not null and "p"."min_outbound_shelf_life" is not null then CURRENT_DATE < "iv"."expiration_date" - "p"."min_outbound_shelf_life" else true end
1210
1425
  ${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
1211
1426
  ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
@@ -1231,7 +1446,7 @@ async function assignToInventory(
1231
1446
  productId: orderInventory[oiIdx].product.id || ''
1232
1447
  },
1233
1448
  result: updatedInventories[0] || '',
1234
- location: 'add-release-order unstable assignInventory',
1449
+ location: 'add-release-order v1 assignInventory',
1235
1450
  time: new Date()
1236
1451
  })
1237
1452