@things-factory/operato-hub 4.3.769 → 4.3.771

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.
Files changed (35) hide show
  1. package/config.development.js +107 -52
  2. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js +81 -45
  3. package/dist-server/routers/api/restful-apis/unstable/add-release-order.js.map +1 -1
  4. package/dist-server/routers/api/restful-apis/v1/utils/params.js +3 -0
  5. package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
  6. package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js +55 -44
  7. package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
  8. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inbound-order-details.js +4 -3
  9. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inbound-order-details.js.map +1 -1
  10. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inventory-warehouse-group-list.js +91 -0
  11. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inventory-warehouse-group-list.js.map +1 -0
  12. package/dist-server/routers/api/restful-apis/v1/warehouse/get-release-order-details.js +2 -1
  13. package/dist-server/routers/api/restful-apis/v1/warehouse/get-release-order-details.js.map +1 -1
  14. package/dist-server/routers/api/restful-apis/v1/warehouse/get-return-order-details.js +3 -2
  15. package/dist-server/routers/api/restful-apis/v1/warehouse/get-return-order-details.js.map +1 -1
  16. package/dist-server/routers/api/restful-apis/v1/warehouse/index.js +1 -0
  17. package/dist-server/routers/api/restful-apis/v1/warehouse/index.js.map +1 -1
  18. package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js +65 -36
  19. package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js.map +1 -1
  20. package/dist-server/routers/xilnex-router.js +7 -2
  21. package/dist-server/routers/xilnex-router.js.map +1 -1
  22. package/openapi/v1/inbound.yaml +3 -0
  23. package/openapi/v1/outbound.yaml +7 -0
  24. package/openapi/v1/return-order.yaml +3 -0
  25. package/package.json +25 -25
  26. package/server/routers/api/restful-apis/unstable/add-release-order.ts +90 -50
  27. package/server/routers/api/restful-apis/v1/utils/params.ts +3 -0
  28. package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +63 -48
  29. package/server/routers/api/restful-apis/v1/warehouse/get-inbound-order-details.ts +2 -1
  30. package/server/routers/api/restful-apis/v1/warehouse/get-inventory-warehouse-group-list.ts +114 -0
  31. package/server/routers/api/restful-apis/v1/warehouse/get-release-order-details.ts +2 -1
  32. package/server/routers/api/restful-apis/v1/warehouse/get-return-order-details.ts +2 -1
  33. package/server/routers/api/restful-apis/v1/warehouse/index.ts +1 -0
  34. package/server/routers/api/restful-apis/v1/warehouse/update-release-order-details.ts +69 -38
  35. package/server/routers/xilnex-router.ts +7 -2
@@ -4,15 +4,46 @@ module.exports = {
4
4
  useVirtualHostBasedDomain: false,
5
5
  fallbackRoute: '/',
6
6
  subdomainOffset: 2,
7
- port: 4445,
7
+ port: 3000,
8
8
  inspect: '9260',
9
- storage: {
10
- type: 's3',
11
- accessKeyId: 'AKIAUQEOPWEJHCE4MTH4',
12
- secretAccessKey: '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',
13
- bucketName: 'opa-one',
14
- region: 'ap-southeast-1'
9
+ //postgres
10
+ ormconfig: {
11
+ name: 'default',
12
+ type: 'postgres',
13
+ database: 'postgres',
14
+ username: 'postgres',
15
+ password: 't62dgT#Ns*GerhuZ9wnzm^',
16
+ host: 'my-operato-pg.cjcso4qmeuq0.ap-southeast-5.rds.amazonaws.com',
17
+ port: 55432,
18
+ synchronize: false,
19
+ logging: ['query', 'error'],
20
+ logger: 'advanced-console'
15
21
  },
22
+ //operato
23
+ // ormconfig: {
24
+ // name: 'default',
25
+ // type: 'postgres',
26
+ // database: 'operato',
27
+ // username: 'postgres',
28
+ // password: 'hatio',
29
+ // host: '192.168.0.151',
30
+ // port: 15432,
31
+ // synchronize: false,
32
+ // logging: true
33
+ // },
34
+ //eric2
35
+ // ormconfig: {
36
+ // name: 'default',
37
+ // type: 'postgres',
38
+ // database: 'eric2',
39
+ // username: 'postgres',
40
+ // password: 'hatio',
41
+ // host: '192.168.0.153',
42
+ // port: 15432,
43
+ // synchronize: false,
44
+ // logging: true
45
+ // },
46
+ // arif's
16
47
  // ormconfig: {
17
48
  // name: 'default',
18
49
  // type: 'postgres',
@@ -24,47 +55,30 @@ module.exports = {
24
55
  // synchronize: false,
25
56
  // logging: true
26
57
  // },
27
-
28
- // STAGING DATABASE
29
- ormconfig: {
30
- name: 'default',
31
- type: 'postgres',
32
- database: 'staging',
33
- username: 'postgres',
34
- password: 't62dgT#Ns*GerhuZ9wnzm^',
35
- host: 'my-operato-pg.cjcso4qmeuq0.ap-southeast-5.rds.amazonaws.com',
36
- port: 55432,
37
- synchronize: false,
38
- logging: true
39
- },
40
-
58
+ //EMS
41
59
  // ormconfig: {
42
60
  // name: 'default',
43
61
  // type: 'postgres',
44
- // database: 'operato',
62
+ // database: 'EMS',
45
63
  // username: 'postgres',
46
64
  // password: 'hatio',
47
- // host: '192.168.0.153',
65
+ // host: '192.168.0.161',
48
66
  // port: 15432,
49
67
  // synchronize: false,
50
68
  // logging: true
51
69
  // },
52
-
53
- //db izzah
70
+ //db nora
54
71
  // ormconfig: {
55
72
  // name: 'default',
56
73
  // type: 'postgres',
57
- // database: '06072023',
74
+ // database: 'postgres',
58
75
  // username: 'postgres',
59
76
  // password: 'hatio',
60
- // host: '192.168.0.153',
77
+ // host: '192.168.0.36',
61
78
  // port: 15432,
62
79
  // synchronize: true,
63
80
  // logging: true
64
81
  // },
65
- //postgres
66
-
67
- // //ERIC
68
82
  // ormconfig: {
69
83
  // name: 'default',
70
84
  // type: 'postgres',
@@ -76,20 +90,18 @@ module.exports = {
76
90
  // synchronize: true,
77
91
  // logging: true
78
92
  // },
79
-
80
- //eric2
93
+ //db izzah
81
94
  // ormconfig: {
82
95
  // name: 'default',
83
96
  // type: 'postgres',
84
- // database: 'eric2',
97
+ // database: '06072023',
85
98
  // username: 'postgres',
86
99
  // password: 'hatio',
87
100
  // host: '192.168.0.153',
88
101
  // port: 15432,
89
- // synchronize: false,
102
+ // synchronize: true,
90
103
  // logging: true
91
104
  // },
92
-
93
105
  // ormconfig: {
94
106
  // name: 'default',
95
107
  // type: 'postgres',
@@ -103,6 +115,7 @@ module.exports = {
103
115
  // connectTimeoutMS: 30000,
104
116
  // extra: { poolSize: 30 }
105
117
  // },
118
+
106
119
  // ormconfig: {
107
120
  // name: 'default',
108
121
  // type: 'postgres',
@@ -116,6 +129,7 @@ module.exports = {
116
129
  // connectTimeoutMS: 30000,
117
130
  // extra: { poolSize: 30 }
118
131
  // },
132
+
119
133
  password: {
120
134
  lowerCase: true,
121
135
  upperCase: false,
@@ -130,10 +144,23 @@ module.exports = {
130
144
  },
131
145
  sftpFileStorage: {
132
146
  type: 's3',
133
- accessKeyId: 'AKIAUQEOPWEJKL43OMZA',
134
- secretAccessKey: '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',
135
- bucketName: 'operato-sftp'
147
+ accessKeyId: 'AKIAUQEOPWEJAMXCXGB4',
148
+ secretAccessKey: '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',
149
+ bucketName: 'operato-sftp',
150
+ region: 'ap-southeast-1'
136
151
  },
152
+
153
+ // SFTP Configuration for external server
154
+ // sftpExternal: {
155
+ // type: 'sftp',
156
+ // host: '103.4.6.168',
157
+ // port: 2211,
158
+ // username: 'hatio_admin',
159
+ // password: 'Yltc@2025!',
160
+ // basePath: '/Staging',
161
+ // timeout: 30000,
162
+ // retries: 3
163
+ // },
137
164
  // storage: {
138
165
  // type: 's3',
139
166
  // accessKeyId: 'AKIAUQEOPWEJCDH6AR5H',
@@ -167,11 +194,6 @@ module.exports = {
167
194
  ciphers: 'SSLv3'
168
195
  }
169
196
  },
170
- jobQueue: {
171
- // For JobQueueService/JobWorker (FIFO queue is supported)
172
- queueUrl: 'https://sqs.ap-southeast-5.amazonaws.com/309536469266/operato-job-queue.fifo',
173
- sqsRegion: 'ap-southeast-5'
174
- },
175
197
  sender: 'noreply@hatiolab.com',
176
198
  notification: {
177
199
  // fcm: {
@@ -205,12 +227,20 @@ module.exports = {
205
227
  appSecret: '1c385935dc131c4b902b9bbf6a4798af',
206
228
  callback: 'http://192.168.0.161:5000/callback-operato'
207
229
  },
230
+
231
+ // Job queue (AWS SQS)
232
+ jobQueue: {
233
+ // For JobQueueService/JobWorker (FIFO queue is supported)
234
+ queueUrl: 'https://sqs.ap-southeast-5.amazonaws.com/309536469266/operato-job-queue.fifo',
235
+ sqsRegion: 'ap-southeast-5'
236
+ },
208
237
  marketplaceIntegrationShopee: {
209
238
  platform: 'shopee',
210
239
  isUAT: false,
211
240
  application: 'Operato MMS',
212
241
  partnerId: 846025,
213
- partnerKey: 'd34cfd85a603f196a0d74ebe08043280c1a27788bb36bdffd61e7e0bb1c90b64'
242
+ partnerKey: 'd34cfd85a603f196a0d74ebe08043280c1a27788bb36bdffd61e7e0bb1c90b64',
243
+ v2: true
214
244
  },
215
245
  marketplaceIntegrationLazada: {
216
246
  platform: 'lazada',
@@ -219,6 +249,16 @@ module.exports = {
219
249
  appSecret: 'HB3RTNEXHlVSlBr9SmWF8AjbSUT7a825',
220
250
  callback: 'https://maybank.operato-m.com/lazada-callback'
221
251
  },
252
+
253
+ //testinglazada
254
+ // marketplaceIntegrationLazada: {
255
+ // platform: 'lazada',
256
+ // application: 'powrup_bi',
257
+ // appKey: '117890',
258
+ // appSecret: 'tQVllnUa7irAHoNxAwXEVxoP1we1bUjE',
259
+ // callback: 'https://73c5-175-141-30-142.ngrok-free.app/lazada-callback'
260
+ // },
261
+
222
262
  accountingIntegrationXero: {
223
263
  platform: 'xero',
224
264
  application: 'Operato WMS',
@@ -227,21 +267,36 @@ module.exports = {
227
267
  callback: 'http://operato-h.com:3000/callback-xero',
228
268
  hostname: 'http://operato-h.com:3000/'
229
269
  },
230
- lambda: {
231
- region: 'ap-southeast-1',
232
- accessKeyId: 'AKIAUQEOPWEJKL43OMZA',
233
- secretAccessKey: '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'
270
+ lmdIntegrationNinjavan: {
271
+ clientId: 'P8WCEwMo0FHNlPECwTLetwN3diAmt5KF',
272
+ secretKey: '1D0yNZGseOjhxnwri29xmuZiiuRp131L',
273
+ refreshThreshold: 43200
234
274
  },
275
+
276
+ lmdIntegrationEms: { refreshThreshold: 43200 },
277
+
278
+ lmdIntegrationCitylink: { refreshThreshold: 172800000 },
235
279
  awbFileStorage: {
236
280
  type: 's3',
237
- accessKeyId: 'AKIAUQEOPWEJKL43OMZA',
238
- secretAccessKey: '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',
281
+ accessKeyId: 'AKIAUQEOPWEJPXIVER74',
282
+ secretAccessKey: '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',
239
283
  bucketName: 'operato-awb',
240
284
  region: 'ap-southeast-1'
241
285
  },
286
+ lambda: {
287
+ region: 'ap-southeast-1',
288
+ accessKeyId: 'AKIAUQEOPWEJPXIVER74',
289
+ secretAccessKey: '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'
290
+ },
291
+ lmdIntegrationConfig: {
292
+ version: {
293
+ v1: 'lmdMiddleware',
294
+ v2: 'lmdMiddlewareV2'
295
+ }
296
+ },
242
297
  awsSesEmail: {
243
- accessKeyId: 'AKIAUQEOPWEJKQDJSEOG',
244
- secretAccessKey: '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',
298
+ accessKeyId: 'AKIAUQEOPWEJPXIVER74',
299
+ secretAccessKey: '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',
245
300
  email: 'support@hatio.asia'
246
301
  },
247
302
  reportApiUrl:
@@ -816,6 +816,7 @@ async function massageOrderItems(releaseGood, inputOrderProducts, context) {
816
816
  }
817
817
  }
818
818
  async function assignToInventory(releaseGood, orderProducts, customerBizplace, context, tx, worksheetPickingAssignment) {
819
+ var _a;
819
820
  const { client, domain, user } = context.state;
820
821
  const pickingProductSetting = await tx.getRepository(setting_base_1.Setting).findOne({
821
822
  where: { domain, name: 'rule-for-picking-product' }
@@ -890,7 +891,7 @@ async function assignToInventory(releaseGood, orderProducts, customerBizplace, c
890
891
  switch (itm === null || itm === void 0 ? void 0 : itm.operator) {
891
892
  case 'notin':
892
893
  case 'in':
893
- acc.query.push(`${itm.query} ${itm.operator == 'notin' ? 'NOT IN' : 'IN'} (${itm.filtes
894
+ acc.query.push(`${itm.query} ${itm.operator == 'notin' ? 'NOT IN' : 'IN'} (${itm.filters
894
895
  .map((itm, idx) => {
895
896
  return `$${acc.values.length + 1}`;
896
897
  })
@@ -900,7 +901,7 @@ async function assignToInventory(releaseGood, orderProducts, customerBizplace, c
900
901
  acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
901
902
  break;
902
903
  }
903
- acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
904
+ return acc;
904
905
  }, {
905
906
  query: [],
906
907
  values: []
@@ -926,16 +927,25 @@ async function assignToInventory(releaseGood, orderProducts, customerBizplace, c
926
927
  params.push(orderInventory.batchId);
927
928
  }
928
929
  params = [...params, ...queryStrings.values];
929
- let updatedInventories = await tx.getRepository(warehouse_base_1.Inventory).query(`
930
- update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
930
+ // Add seed row parameters to avoid SQL injection
931
+ const seedUomIdx = params.length + 1;
932
+ const seedPackingTypeIdx = params.length + 2;
933
+ const seedPackingSizeIdx = params.length + 3;
934
+ const seedReleaseQtyIdx = params.length + 4;
935
+ const seedReleaseUomValueIdx = params.length + 5;
936
+ params.push(orderInventory.uom, orderInventory.packingType, orderInventory.packingSize, orderInventory.releaseQty, orderInventory.releaseUomValue);
937
+ const MAX_ASSIGN_RETRIES = 3;
938
+ const RETRY_DELAY_MS = 10;
939
+ const assignQuery = `
940
+ update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
931
941
  locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
932
942
  updated_at = NOW(),
933
943
  updater_id = $1
934
944
  from (
935
945
  select "id", "pallet_id","carton_id", "batch_id", "batch_id_ref", "unit", "uom", "packing_type", "packing_size", "manufacture_year",
936
- "reserve_qty", (("uom_value"/"qty") * "reserve_qty") as "reserve_uom_value" from (
937
- select "sort_seq", "id", "pallet_id", "batch_id", "batch_id_ref", "unit", "uom", "packing_type", "packing_size", "manufacture_year", "carton_id", "uom_value", "locked_uom_value", "qty", "locked_qty", "created_at",
938
- "release_qty", "release_uom_value", lock_amount,
946
+ "reserve_qty", (("uom_value"/"qty") * "reserve_qty") as "reserve_uom_value" from (
947
+ select "sort_seq", "id", "pallet_id", "batch_id", "batch_id_ref", "unit", "uom", "packing_type", "packing_size", "manufacture_year", "carton_id", "uom_value", "locked_uom_value", "qty", "locked_qty", "created_at",
948
+ "release_qty", "release_uom_value", lock_amount,
939
949
  case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end as "reserve_qty"
940
950
  from (
941
951
  select *,
@@ -945,57 +955,81 @@ async function assignToInventory(releaseGood, orderProducts, customerBizplace, c
945
955
  ) as available_qty,
946
956
  sum(qty - locked_qty - release_qty - unassigned_qty) over (order by sort_seq asc rows between unbounded preceding and current row) as lock_amount
947
957
  from (
948
- SELECT 0 as sort_seq, null as id, null as pallet_id, null as batch_id, null as batch_id_ref,
949
- null as unit, '${orderInventory.uom}' as uom, '${orderInventory.packingType}' as packing_type, '${orderInventory.packingSize}' as packing_size,
958
+ SELECT 0 as sort_seq, null as id, null as pallet_id, null as batch_id, null as batch_id_ref,
959
+ null as unit, $${seedUomIdx} as uom, $${seedPackingTypeIdx} as packing_type, $${seedPackingSizeIdx} as packing_size,
950
960
  null as manufacture_year, null as carton_id, 0 as uom_value, 0 as locked_uom_value, 0 as qty, 0 as locked_qty, 0 as unassigned_uom_value, 0 as unassigned_qty, null as created_at,
951
- '${orderInventory.releaseQty}' as release_qty, '${orderInventory.releaseUomValue}' as release_uom_value
961
+ $${seedReleaseQtyIdx}::numeric as release_qty, $${seedReleaseUomValueIdx}::numeric as release_uom_value
952
962
  UNION
953
963
  (
954
- SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}) as sort_seq,
955
- iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
956
- iv.unit, iv.uom, iv.packing_type, iv.packing_size,
957
- iv.manufacture_year, iv.carton_id, iv.uom_value,
964
+ SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}) as sort_seq,
965
+ iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
966
+ iv.unit, iv.uom, iv.packing_type, iv.packing_size,
967
+ iv.manufacture_year, iv.carton_id, iv.uom_value,
958
968
  coalesce(iv.locked_uom_value,0) as locked_uom_value, iv.qty, greatest(coalesce(iv.locked_qty,0),0) as locked_qty, coalesce(pds.unassigned_uom_value,0) as unassigned_uom_value, greatest(coalesce(pds.unassigned_qty,0),0) as unassigned_qty, iv.created_at,
959
969
  0 as release_qty, 0 as release_uom_value
960
- FROM "inventories" "iv"
970
+ FROM "inventories" "iv"
961
971
  LEFT JOIN "product_detail_stocks" "pds" ON "pds"."product_detail_id" = "iv"."product_detail_id"
962
- INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
963
- INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
972
+ INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
973
+ INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
964
974
  INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
965
- WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
975
+ WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
966
976
  "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)
967
977
  AND ${orderInventory.batchId ? `"iv"."batch_id" = $11` : `1=1`}
968
978
  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
969
- ${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
979
+ ${queryStrings.query.length > 0 ? `AND ${queryStrings.query.join(' AND ')}` : ''}
970
980
  ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
971
981
  )
972
982
  ) dt1
973
- ) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
983
+ ) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
974
984
  ) dt3 where sort_seq > 0
975
- ) src where src.id = tgt.id
976
- returning
977
- tgt."id", tgt."qty", tgt."pallet_id", tgt."carton_id", tgt."batch_id", tgt."batch_id_ref", tgt."unit",
978
- tgt."uom", tgt."packing_type", tgt."packing_size", tgt."manufacture_year",
979
- tgt."locked_qty", tgt."uom_value", tgt."locked_uom_value", src."reserve_qty", src."reserve_uom_value";`, params);
980
- let totalAssigned = updatedInventories[0].reduce((acc, inv) => {
981
- return acc + inv.reserve_qty;
982
- }, 0);
983
- if (orderProduct.releaseQty != totalAssigned) {
984
- throw new index_1.ValidationError({
985
- errorCode: 'INSUFFICIENT_STOCK',
986
- message: 'insufficient stock',
987
- detail: JSON.stringify({
988
- code: 'insufficient stock',
989
- sku: orderProduct.product.sku,
990
- packingType: orderProduct.packingType,
991
- packingSize: orderProduct.packingSize,
992
- uom: orderProduct.uom,
993
- qty: orderProduct.releaseQty,
994
- uom_value: orderProduct.releaseUomValue
995
- })
996
- });
985
+ ) src where src.id = tgt.id AND tgt.qty >= coalesce(tgt.locked_qty, 0) + src.reserve_qty
986
+ returning
987
+ tgt."id", tgt."qty", tgt."pallet_id", tgt."carton_id", tgt."batch_id", tgt."batch_id_ref", tgt."unit",
988
+ tgt."uom", tgt."packing_type", tgt."packing_size", tgt."manufacture_year",
989
+ tgt."locked_qty", tgt."uom_value", tgt."locked_uom_value", src."reserve_qty", src."reserve_uom_value";`;
990
+ let updatedInventories;
991
+ for (let attempt = 1; attempt <= MAX_ASSIGN_RETRIES; attempt++) {
992
+ await tx.query('SAVEPOINT assign_retry');
993
+ updatedInventories = await tx.getRepository(warehouse_base_1.Inventory).query(assignQuery, params);
994
+ const assignedRows = (updatedInventories === null || updatedInventories === void 0 ? void 0 : updatedInventories[0]) || [];
995
+ let totalAssigned = assignedRows.reduce((acc, inv) => {
996
+ return acc + Number(inv.reserve_qty);
997
+ }, 0);
998
+ // Use epsilon comparison for floating-point tolerance
999
+ let opReleaseQty = orderProduct.releaseQty;
1000
+ if (!((_a = orderInventory.product) === null || _a === void 0 ? void 0 : _a.isInventoryDecimal)) {
1001
+ opReleaseQty = Math.round(opReleaseQty);
1002
+ totalAssigned = Math.round(totalAssigned);
1003
+ }
1004
+ else {
1005
+ opReleaseQty = Math.round(opReleaseQty * 1000) / 1000;
1006
+ totalAssigned = Math.round(totalAssigned * 1000) / 1000;
1007
+ }
1008
+ if (Math.abs(opReleaseQty - totalAssigned) > 0.001) {
1009
+ await tx.query('ROLLBACK TO SAVEPOINT assign_retry');
1010
+ if (attempt < MAX_ASSIGN_RETRIES) {
1011
+ await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
1012
+ continue;
1013
+ }
1014
+ throw new index_1.ValidationError({
1015
+ errorCode: 'INSUFFICIENT_STOCK',
1016
+ message: 'insufficient stock',
1017
+ detail: JSON.stringify({
1018
+ code: 'insufficient stock',
1019
+ sku: orderProduct.product.sku,
1020
+ packingType: orderProduct.packingType,
1021
+ packingSize: orderProduct.packingSize,
1022
+ uom: orderProduct.uom,
1023
+ qty: orderProduct.releaseQty,
1024
+ uom_value: orderProduct.releaseUomValue
1025
+ })
1026
+ });
1027
+ }
1028
+ await tx.query('RELEASE SAVEPOINT assign_retry');
1029
+ break;
997
1030
  }
998
- updatedInventories[0].forEach(inv => {
1031
+ ;
1032
+ ((updatedInventories === null || updatedInventories === void 0 ? void 0 : updatedInventories[0]) || []).forEach(inv => {
999
1033
  let oi = Object.assign(Object.assign({}, orderInventory), { inventory: { id: inv.id }, uom: inv.uom, packingType: inv.packing_type, batchId: inv.batch_id, releaseQty: inv.reserve_qty, releaseUomValue: inv.reserve_uom_value, pickedQty: 0, packedQty: 0, type: sales_base_1.ORDER_TYPES.RELEASE_OF_GOODS, status: sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET, domain: { id: domain.id }, bizplace: { id: customerBizplace.id } });
1000
1034
  assignedOrderInventories.push(oi);
1001
1035
  });
@@ -1007,9 +1041,11 @@ async function assignToInventory(releaseGood, orderProducts, customerBizplace, c
1007
1041
  .createQueryBuilder()
1008
1042
  .update(warehouse_base_1.ProductDetailStock)
1009
1043
  .set({
1010
- unassignedQty: () => `"unassigned_qty" + ${orderInventory.releaseQty}`,
1011
- unassignedUomValue: () => `"unassigned_uom_value" + ${orderInventory.releaseUomValue}`
1044
+ unassignedQty: () => `"unassigned_qty" + :releaseQty::numeric`,
1045
+ unassignedUomValue: () => `"unassigned_uom_value" + :releaseUomValue::numeric`
1012
1046
  })
1047
+ .setParameter('releaseQty', orderInventory.releaseQty)
1048
+ .setParameter('releaseUomValue', orderInventory.releaseUomValue)
1013
1049
  .where({ productDetail: orderInventory.productDetail.id })
1014
1050
  .execute();
1015
1051
  }