@things-factory/operato-hub 4.3.130 → 4.3.132

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.
@@ -11,110 +11,1088 @@ const biz_base_1 = require("@things-factory/biz-base");
11
11
  const env_1 = require("@things-factory/env");
12
12
  const id_rule_base_1 = require("@things-factory/id-rule-base");
13
13
  const integration_accounting_1 = require("@things-factory/integration-accounting");
14
+ // import { createPayloadLog, PayloadType } from '@things-factory/integration-base'
14
15
  const product_base_1 = require("@things-factory/product-base");
15
16
  const sales_base_1 = require("@things-factory/sales-base");
16
17
  const setting_base_1 = require("@things-factory/setting-base");
17
18
  const shell_1 = require("@things-factory/shell");
19
+ const warehouse_base_1 = require("@things-factory/warehouse-base");
20
+ const index_1 = require("../errors/index");
18
21
  const debug = require('debug')('things-factory:integration-accounting:xilnex-router');
19
22
  exports.xilnexRouter = new koa_router_1.default();
20
23
  exports.xilnexRouter.post('/webhook-xilnex-po', async (context, next) => {
21
24
  try {
22
- const { id, warehouseId } = context.query;
23
- const { body, headers } = context.request;
24
- const data = (body === null || body === void 0 ? void 0 : body.Data) ? JSON.parse(body.Data) : {};
25
- const account = await integration_accounting_1.AccountingAPI.getAccounting(id);
26
- if (!account) {
27
- throw new Error(`${id} not found in Account`);
28
- }
29
- const { webhookKey } = account;
30
- const signature = headers['xilnex-webhook-signature'];
31
- const xilnex = new integration_accounting_1.Xilnex({});
32
- if (!xilnex.isValidSignature(body, webhookKey, signature)) {
33
- context.throw(400, 'Signature validation failed');
34
- }
35
- const customerDomain = account.domain;
36
- const customerBizplace = await (0, typeorm_1.getRepository)(biz_base_1.Bizplace).findOne({
37
- where: { domain: customerDomain }
38
- });
39
- const custCompanyBizplace = await (0, biz_base_1.getCompanyBizplace)(null, null, customerBizplace.id);
40
- const warehouseDomain = await (0, typeorm_1.getRepository)(shell_1.Domain).findOne({
41
- where: { id: warehouseId, extType: 'warehouse' }
42
- });
43
- if (!warehouseDomain) {
44
- context.throw(400, `${warehouseId} not found in warehouse domain`);
45
- }
46
- let accountWebhookOrder = {
47
- name: (0, uuid_1.v4)(),
48
- request: body,
49
- platform: integration_accounting_1.ACCOUNT_PLATFORM.XILNEX,
50
- domain: account.domain,
51
- status: integration_accounting_1.ACCOUNT_SYNC_STATUS.FAILED
52
- };
53
- if (data === null || data === void 0 ? void 0 : data.id) {
54
- const purchaseOrder = await integration_accounting_1.AccountingAPI.getPurchaseOrder(account, { orderId: data.id });
55
- const { orderInfo, orderItems } = purchaseOrder;
56
- const ganNoSetting = await (0, typeorm_1.getRepository)(setting_base_1.Setting).findOne({
57
- where: {
58
- domain: warehouseDomain,
59
- name: sales_base_1.ORDER_NUMBER_SETTING_KEY.GAN_NUMBER_RULE
60
- }
61
- });
62
- let orderNo = '';
63
- if (ganNoSetting) {
64
- orderNo = await (0, id_rule_base_1.generateId)({ domain: warehouseDomain, type: sales_base_1.ORDER_NUMBER_RULE_TYPE.GAN_NUMBER, seed: {} });
25
+ await (0, typeorm_1.getConnection)().transaction(async (tx) => {
26
+ const { id, warehouseId } = context.query;
27
+ const { body, headers } = context.request;
28
+ const data = (body === null || body === void 0 ? void 0 : body.Data) ? JSON.parse(body.Data) : {};
29
+ const account = await integration_accounting_1.AccountingAPI.getAccounting(id);
30
+ if (!account) {
31
+ context.throw(400, `${id} not found in Account`);
65
32
  }
66
- else {
67
- orderNo = sales_base_1.OrderNoGenerator.arrivalNotice();
68
- }
69
- let arrivalNotice = Object.assign(Object.assign({}, orderInfo), { name: orderNo, source: 'XILNEX', domain: warehouseDomain, bizplace: customerBizplace, status: 'INTRANSIT' });
70
- arrivalNotice = await (0, typeorm_1.getRepository)(sales_base_1.ArrivalNotice).save(arrivalNotice);
71
- let orderProducts = [];
72
- await Promise.all(orderItems.map(async (item) => {
73
- const productDetail = await (0, typeorm_1.getRepository)(product_base_1.ProductDetail).findOne({
74
- where: { domain: custCompanyBizplace.domain, refCode: item.product.sku },
75
- relations: ['product']
33
+ const { webhookKey } = account;
34
+ const signature = headers['xilnex-webhook-signature'];
35
+ const xilnex = new integration_accounting_1.Xilnex({});
36
+ if (!xilnex.isValidSignature(body, webhookKey, signature)) {
37
+ let errBody = JSON.stringify({
38
+ body,
39
+ webhookKey,
40
+ signature
76
41
  });
77
- if (!productDetail) {
78
- context.throw(404, `no matching product found`);
42
+ context.throw(400, `Signature validation failed: ${errBody}`);
43
+ }
44
+ const customerDomain = account.domain;
45
+ const customerBizplace = await tx.getRepository(biz_base_1.Bizplace).findOne({
46
+ where: { domain: customerDomain }
47
+ });
48
+ const custCompanyBizplace = await (0, biz_base_1.getCompanyBizplace)(null, null, customerBizplace.id, tx);
49
+ const warehouseDomain = await tx.getRepository(shell_1.Domain).findOne({
50
+ where: { id: warehouseId, extType: 'warehouse' }
51
+ });
52
+ if (!warehouseDomain) {
53
+ context.throw(400, `${warehouseId} not found in warehouse domain`);
54
+ }
55
+ let accountWebhookOrder = {
56
+ name: (0, uuid_1.v4)(),
57
+ request: body,
58
+ platform: integration_accounting_1.ACCOUNT_PLATFORM.XILNEX,
59
+ domain: account.domain,
60
+ status: integration_accounting_1.ACCOUNT_SYNC_STATUS.FAILED
61
+ };
62
+ if (data === null || data === void 0 ? void 0 : data.id) {
63
+ const purchaseOrder = await integration_accounting_1.AccountingAPI.getPurchaseOrder(account, { orderId: data.id });
64
+ const { orderInfo, orderItems } = purchaseOrder;
65
+ let accountInfo = (account === null || account === void 0 ? void 0 : account.accountInfo) ? JSON.parse(account.accountInfo) : {};
66
+ if (accountInfo.outlet == orderInfo.toOutlet) {
67
+ const poNoSetting = await tx.getRepository(setting_base_1.Setting).findOne({
68
+ where: {
69
+ domain: warehouseDomain,
70
+ name: sales_base_1.ORDER_NUMBER_SETTING_KEY.PO_NUMBER_RULE
71
+ }
72
+ });
73
+ let orderNo = poNoSetting
74
+ ? await (0, id_rule_base_1.generateId)({ domain: warehouseDomain, type: sales_base_1.ORDER_NUMBER_RULE_TYPE.PO_NUMBER, seed: {} })
75
+ : sales_base_1.OrderNoGenerator.purchaseOrder();
76
+ let purchaseOrder = {
77
+ name: orderNo,
78
+ refNo: orderInfo.orderId,
79
+ refNo2: null,
80
+ refNo3: null,
81
+ etaDate: orderInfo.etaDate,
82
+ ownTransport: orderInfo.ownTransport,
83
+ importCargo: orderInfo.importCargo,
84
+ container: orderInfo.container,
85
+ remark: orderInfo.remark,
86
+ currency: (orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.currency) || 'MYR',
87
+ source: 'XILNEX',
88
+ orderSourceType: 'XILNEX_PO',
89
+ domain: warehouseDomain,
90
+ bizplace: customerBizplace,
91
+ status: sales_base_1.PURCHASE_ORDER_STATUS.DRAFT
92
+ };
93
+ purchaseOrder = await tx.getRepository(sales_base_1.PurchaseOrder).save(purchaseOrder);
94
+ let orderProducts = [];
95
+ await Promise.all(orderItems.map(async (item) => {
96
+ const refCode = item.product.refCode;
97
+ let productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne({
98
+ where: { domain: custCompanyBizplace.domain, refCode: refCode },
99
+ relations: ['product']
100
+ });
101
+ if (!productDetail) {
102
+ const sku = item.product.sku;
103
+ const product = await tx.getRepository(product_base_1.Product).findOne({
104
+ where: { domain: custCompanyBizplace.domain, sku }
105
+ });
106
+ if (product) {
107
+ productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne({
108
+ where: { domain: custCompanyBizplace.domain, product },
109
+ relations: ['product']
110
+ });
111
+ }
112
+ else {
113
+ context.throw(404, `error.sku ${sku} and refCode ${refCode} not exist in product master`);
114
+ }
115
+ }
116
+ let orderProduct = {
117
+ name: sales_base_1.OrderNoGenerator.orderProduct(),
118
+ product: productDetail === null || productDetail === void 0 ? void 0 : productDetail.product,
119
+ productDetail,
120
+ batchId: (item === null || item === void 0 ? void 0 : item.batchId) || '-',
121
+ packingType: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingType,
122
+ packingSize: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingSize,
123
+ uom: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uom,
124
+ uomValue: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uomValue,
125
+ packQty: item.confirmedQty,
126
+ totalUomValue: `${(productDetail.uomValue * item.confirmedQty).toFixed(2)} ${productDetail.uom}`,
127
+ status: sales_base_1.PURCHASE_ORDER_STATUS.DRAFT,
128
+ unitPrice: item.unitCost || 1,
129
+ taxRate: item.taxAmount,
130
+ refItemId: item.refItemId,
131
+ purchaseOrder,
132
+ domain: warehouseDomain,
133
+ bizplace: customerBizplace
134
+ };
135
+ orderProducts.push(orderProduct);
136
+ }));
137
+ await tx.getRepository(sales_base_1.OrderProduct).save(orderProducts);
79
138
  }
80
- let orderProduct = {
81
- name: sales_base_1.OrderNoGenerator.orderProduct(),
82
- product: productDetail === null || productDetail === void 0 ? void 0 : productDetail.product,
83
- productDetail,
84
- batchId: (item === null || item === void 0 ? void 0 : item.batchId) || '-',
85
- packingType: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingType,
86
- packingSize: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingSize,
87
- uom: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uom,
88
- uomValue: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uomValue,
89
- packQty: item.packQty,
90
- totalUomValue: `${(productDetail.uomValue * item.packQty).toFixed(2)} ${productDetail.uom}`,
91
- status: 'PENDING',
92
- arrivalNotice,
93
- domain: warehouseDomain,
94
- bizplace: customerBizplace
95
- };
96
- orderProducts.push(orderProduct);
97
- }));
98
- await (0, typeorm_1.getRepository)(sales_base_1.OrderProduct).save(orderProducts);
99
- }
100
- accountWebhookOrder.status = integration_accounting_1.ACCOUNT_SYNC_STATUS.SUCCESS;
101
- await (0, typeorm_1.getRepository)(integration_accounting_1.AccountWebhookOrder).save(accountWebhookOrder);
139
+ accountWebhookOrder.status = integration_accounting_1.ACCOUNT_SYNC_STATUS.SUCCESS;
140
+ await tx.getRepository(integration_accounting_1.AccountWebhookOrder).save(accountWebhookOrder);
141
+ }
142
+ });
102
143
  context.status = 200;
103
144
  }
104
145
  catch (err) {
146
+ env_1.logger.error(`xilnex-router[webhook-xilnex-po-context]: ${JSON.stringify(context)}`);
105
147
  env_1.logger.error(`xilnex-router[webhook-xilnex-po]: ${err}`);
148
+ context.status = 400;
149
+ context.throw(400, err);
150
+ }
151
+ });
152
+ exports.xilnexRouter.post('/webhook-xilnex-transfer-note', async (context, next) => {
153
+ try {
154
+ await (0, typeorm_1.getConnection)().transaction(async (tx) => {
155
+ const { id, warehouseId } = context.query;
156
+ const { body, headers } = context.request;
157
+ const data = (body === null || body === void 0 ? void 0 : body.Data) ? JSON.parse(body.Data) : {};
158
+ const account = await integration_accounting_1.AccountingAPI.getAccounting(id);
159
+ if (!account) {
160
+ context.throw(400, `${id} not found in Account`);
161
+ }
162
+ const { webhookKey } = account;
163
+ const signature = headers['xilnex-webhook-signature'];
164
+ const xilnex = new integration_accounting_1.Xilnex({});
165
+ if (!xilnex.isValidSignature(body, webhookKey, signature)) {
166
+ let errBody = JSON.stringify({
167
+ body,
168
+ webhookKey,
169
+ signature
170
+ });
171
+ context.throw(400, `Signature validation failed: ${errBody}`);
172
+ }
173
+ const customerDomain = account.domain;
174
+ const customerBizplace = await tx.getRepository(biz_base_1.Bizplace).findOne({
175
+ where: { domain: customerDomain }
176
+ });
177
+ const custCompanyBizplace = await (0, biz_base_1.getCompanyBizplace)(null, null, customerBizplace.id, tx);
178
+ const warehouseDomain = await tx.getRepository(shell_1.Domain).findOne({
179
+ where: { id: warehouseId, extType: 'warehouse' }
180
+ });
181
+ if (!warehouseDomain) {
182
+ context.throw(400, `${warehouseId} not found in warehouse domain`);
183
+ }
184
+ let accountWebhookOrder = {
185
+ name: (0, uuid_1.v4)(),
186
+ request: body,
187
+ platform: integration_accounting_1.ACCOUNT_PLATFORM.XILNEX,
188
+ domain: account.domain,
189
+ status: integration_accounting_1.ACCOUNT_SYNC_STATUS.FAILED
190
+ };
191
+ let accountInfo = (account === null || account === void 0 ? void 0 : account.accountInfo) ? JSON.parse(account.accountInfo) : {};
192
+ if (data === null || data === void 0 ? void 0 : data.id) {
193
+ const transferNote = await integration_accounting_1.AccountingAPI.getTransferNote(account, { orderId: data.id });
194
+ const { orderInfo, orderItems } = transferNote;
195
+ if (orderInfo.fromLocation == accountInfo.outlet || orderInfo.toLocation == accountInfo.outlet) {
196
+ // VDC release
197
+ if (accountInfo.outlet == orderInfo.fromLocation) {
198
+ let releaseGood = {
199
+ domain: warehouseDomain,
200
+ bizplace: customerBizplace,
201
+ companyBizplace: custCompanyBizplace,
202
+ courierOption: false,
203
+ collectionOrderNo: orderInfo.orderId,
204
+ exportOption: false,
205
+ ownTransport: orderInfo.ownTransport,
206
+ packingOption: false,
207
+ refNo: orderInfo.orderId,
208
+ refNo2: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.fromLocation,
209
+ refNo3: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.toLocation,
210
+ releaseDate: orderInfo.issueDate,
211
+ type: 'b2b',
212
+ source: 'XILNEX',
213
+ orderSourceType: 'XILNEX_TRANSFER_NOTE',
214
+ marketplaceOrderStatus: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.status,
215
+ remark: (orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.remark) || null
216
+ };
217
+ // check existing order
218
+ await checkExistingOrder(releaseGood, customerBizplace, true, context, tx);
219
+ //massage data
220
+ let massagedData = await massageOrderItems(releaseGood, orderItems, context);
221
+ // validation
222
+ let errorList = [];
223
+ await Promise.all(massagedData.combinedItems.map(async (item) => {
224
+ let itemList = await tx.query(`
225
+ select wboi.* from warehouse_bizplace_onhand_inventories wboi
226
+ where wboi."domainId"= $1 and
227
+ wboi."bizplaceId" = $2 and
228
+ wboi."groupType" = 'SINGLE' and
229
+ wboi."productId" = $3 and
230
+ wboi."packingType" = $4 and
231
+ wboi."packingSize" = $5 and
232
+ wboi."uom" = $6 and
233
+ wboi."remainQty" >= $7 and
234
+ wboi."remainUomValue" >= $8
235
+ `, [
236
+ warehouseDomain.id,
237
+ customerBizplace.id,
238
+ item.product.id,
239
+ item.packingType,
240
+ item.packingSize,
241
+ item.uom,
242
+ item.releaseQty,
243
+ item.releaseUomValue
244
+ ]);
245
+ if (itemList.length <= 0) {
246
+ errorList.push(JSON.stringify({
247
+ code: 'insufficient stock',
248
+ sku: item.product.sku,
249
+ packingType: item.packingType,
250
+ packingSize: item.packingSize,
251
+ uom: item.uom,
252
+ qty: item.releaseQty,
253
+ uom_value: item.releaseUomValue
254
+ }));
255
+ }
256
+ console.log();
257
+ }));
258
+ if (errorList.length > 0) {
259
+ context.throw(404, errorList.join(', '));
260
+ }
261
+ let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, warehouseDomain, context, tx);
262
+ let result = await createReleaseGood(releaseGood, assignedOrderProducts, customerBizplace, warehouseDomain, context, tx);
263
+ let addReleaseGood = {
264
+ id: result.id,
265
+ name: result.name,
266
+ refNo: result.refNo,
267
+ refNo2: result.refNo2,
268
+ refNo3: result.refNo3,
269
+ status: result.status,
270
+ truckNo: result.truckNo,
271
+ ownTransport: result.ownTransport,
272
+ crossDocking: result.crossDocking,
273
+ type: result.type,
274
+ exportOption: result.exportOption,
275
+ releaseDate: result.releaseDate,
276
+ collectionOrderNo: result.collectionOrderNo,
277
+ paidAmount: result === null || result === void 0 ? void 0 : result.paidAmount,
278
+ discountAmount: result === null || result === void 0 ? void 0 : result.discountAmount,
279
+ taxAmount: result === null || result === void 0 ? void 0 : result.taxAmount,
280
+ shippingFee: result === null || result === void 0 ? void 0 : result.shippingFee,
281
+ bizplace: { name: result.bizplace.name },
282
+ domain: { name: result.domain.name },
283
+ orderInventories: result.orderInventories
284
+ };
285
+ context.body = { result: { addReleaseGood } };
286
+ // createPayloadLog(
287
+ // customerBizplace.id,
288
+ // `/webhook-xilnex-stock-request`,
289
+ // stockRequest,
290
+ // context.body,
291
+ // context,
292
+ // PayloadType.INGESTION
293
+ // )
294
+ }
295
+ // VDC inbound
296
+ if (accountInfo.outlet == orderInfo.toLocation) {
297
+ const ganNoSetting = await tx.getRepository(setting_base_1.Setting).findOne({
298
+ where: {
299
+ domain: warehouseDomain,
300
+ name: sales_base_1.ORDER_NUMBER_SETTING_KEY.GAN_NUMBER_RULE
301
+ }
302
+ });
303
+ let orderNo = '';
304
+ if (ganNoSetting) {
305
+ orderNo = await (0, id_rule_base_1.generateId)({ domain: warehouseDomain, type: sales_base_1.ORDER_NUMBER_RULE_TYPE.GAN_NUMBER, seed: {} });
306
+ }
307
+ else {
308
+ orderNo = sales_base_1.OrderNoGenerator.arrivalNotice();
309
+ }
310
+ let arrivalNotice = {
311
+ name: orderNo,
312
+ refNo: orderInfo.orderId,
313
+ refNo2: orderInfo.fromLocation,
314
+ refNo3: orderInfo.toLocation,
315
+ etaDate: orderInfo.etaDate,
316
+ ownTransport: orderInfo.ownTransport,
317
+ importCargo: orderInfo.importCargo,
318
+ container: orderInfo.container,
319
+ remark: orderInfo.remark,
320
+ source: 'XILNEX',
321
+ orderSourceType: 'XILNEX_TRANSFER_NOTE',
322
+ domain: warehouseDomain,
323
+ bizplace: customerBizplace,
324
+ status: 'INTRANSIT'
325
+ };
326
+ arrivalNotice = await tx.getRepository(sales_base_1.ArrivalNotice).save(arrivalNotice);
327
+ let orderProducts = [];
328
+ await Promise.all(orderItems.map(async (item) => {
329
+ const refCode = item.product.refCode;
330
+ let productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne({
331
+ where: { domain: custCompanyBizplace.domain, refCode: refCode },
332
+ relations: ['product']
333
+ });
334
+ if (!productDetail) {
335
+ const sku = item.product.sku;
336
+ const product = await tx.getRepository(product_base_1.Product).findOne({
337
+ where: { domain: custCompanyBizplace.domain, sku }
338
+ });
339
+ if (product) {
340
+ productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne({
341
+ where: { domain: custCompanyBizplace.domain, product },
342
+ relations: ['product']
343
+ });
344
+ }
345
+ else {
346
+ context.throw(404, `error.sku ${sku} and refCode ${refCode} not exist in product master`);
347
+ }
348
+ }
349
+ let orderProduct = {
350
+ name: sales_base_1.OrderNoGenerator.orderProduct(),
351
+ product: productDetail === null || productDetail === void 0 ? void 0 : productDetail.product,
352
+ productDetail,
353
+ batchId: (item === null || item === void 0 ? void 0 : item.batchId) || '-',
354
+ packingType: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingType,
355
+ packingSize: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingSize,
356
+ uom: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uom,
357
+ uomValue: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uomValue,
358
+ packQty: item.confirmedQty,
359
+ totalUomValue: `${(productDetail.uomValue * item.confirmedQty).toFixed(2)} ${productDetail.uom}`,
360
+ status: 'PENDING',
361
+ arrivalNotice,
362
+ domain: warehouseDomain,
363
+ bizplace: customerBizplace
364
+ };
365
+ orderProducts.push(orderProduct);
366
+ }));
367
+ await tx.getRepository(sales_base_1.OrderProduct).save(orderProducts);
368
+ }
369
+ }
370
+ }
371
+ accountWebhookOrder.status = integration_accounting_1.ACCOUNT_SYNC_STATUS.SUCCESS;
372
+ await tx.getRepository(integration_accounting_1.AccountWebhookOrder).save(accountWebhookOrder);
373
+ });
374
+ context.status = 200;
375
+ }
376
+ catch (err) {
377
+ env_1.logger.error(`xilnex-router[webhook-xilnex-transfer-note-context]: ${JSON.stringify(context)}`);
378
+ env_1.logger.error(`xilnex-router[webhook-xilnex-transfer-note]: ${err}`);
379
+ context.status = 400;
380
+ context.throw(400, err);
106
381
  }
107
382
  });
108
383
  exports.xilnexRouter.post('/webhook-xilnex-stock-request', async (context, next) => {
109
384
  try {
110
- const { id } = context.query;
111
- const { body } = context.request;
112
- const data = (body === null || body === void 0 ? void 0 : body.data) ? JSON.parse(body.data) : {};
113
- const account = await (0, typeorm_1.getRepository)(integration_accounting_1.Account).findOne(id);
385
+ await (0, typeorm_1.getConnection)().transaction(async (tx) => {
386
+ const { id, warehouseId } = context.query;
387
+ const { body, headers } = context.request;
388
+ const data = (body === null || body === void 0 ? void 0 : body.Data) ? JSON.parse(body.Data) : {};
389
+ const account = await integration_accounting_1.AccountingAPI.getAccounting(id);
390
+ if (!account) {
391
+ throw new Error(`${id} not found in Account`);
392
+ }
393
+ const { webhookKey } = account;
394
+ const signature = headers['xilnex-webhook-signature'];
395
+ const xilnex = new integration_accounting_1.Xilnex({});
396
+ if (!xilnex.isValidSignature(body, webhookKey, signature)) {
397
+ context.throw(400, 'Signature validation failed');
398
+ }
399
+ const customerDomain = account.domain;
400
+ const customerBizplace = await tx.getRepository(biz_base_1.Bizplace).findOne({
401
+ where: { domain: customerDomain }
402
+ });
403
+ const custCompanyBizplace = await (0, biz_base_1.getCompanyBizplace)(null, null, customerBizplace.id, tx);
404
+ const warehouseDomain = await tx.getRepository(shell_1.Domain).findOne({
405
+ where: { id: warehouseId, extType: 'warehouse' }
406
+ });
407
+ if (!warehouseDomain) {
408
+ context.throw(400, `${warehouseId} not found in warehouse domain`);
409
+ }
410
+ let accountWebhookOrder = {
411
+ name: (0, uuid_1.v4)(),
412
+ request: body,
413
+ platform: integration_accounting_1.ACCOUNT_PLATFORM.XILNEX,
414
+ domain: account.domain,
415
+ status: integration_accounting_1.ACCOUNT_SYNC_STATUS.FAILED
416
+ };
417
+ let accountInfo = (account === null || account === void 0 ? void 0 : account.accountInfo) ? JSON.parse(account.accountInfo) : {};
418
+ if (data === null || data === void 0 ? void 0 : data.id) {
419
+ const stockRequest = await integration_accounting_1.AccountingAPI.getStockRequest(account, { orderId: data.id });
420
+ const { orderInfo, orderItems } = stockRequest;
421
+ if (orderInfo.fromLocation == accountInfo.outlet || orderInfo.toLocation == accountInfo.outlet) {
422
+ // VDC release
423
+ if (accountInfo.outlet == orderInfo.toLocation) {
424
+ let releaseGood = {
425
+ domain: warehouseDomain,
426
+ bizplace: customerBizplace,
427
+ companyBizplace: custCompanyBizplace,
428
+ courierOption: false,
429
+ collectionOrderNo: orderInfo.orderId,
430
+ exportOption: false,
431
+ ownTransport: orderInfo.ownTransport,
432
+ packingOption: false,
433
+ refNo: orderInfo.orderId,
434
+ refNo2: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.fromLocation,
435
+ refNo3: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.toLocation,
436
+ releaseDate: orderInfo.issueDate,
437
+ type: 'b2b',
438
+ source: 'XILNEX',
439
+ orderSourceType: 'XILNEX_STOCK_REQUEST',
440
+ marketplaceOrderStatus: orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.status,
441
+ remark: (orderInfo === null || orderInfo === void 0 ? void 0 : orderInfo.remark) || null
442
+ };
443
+ // check existing order
444
+ await checkExistingOrder(releaseGood, customerBizplace, true, context, tx);
445
+ //massage data
446
+ let massagedData = await massageOrderItems(releaseGood, orderItems, context);
447
+ // validation
448
+ let errorList = [];
449
+ await Promise.all(massagedData.combinedItems.map(async (item) => {
450
+ let itemList = await tx.query(`
451
+ select wboi.* from warehouse_bizplace_onhand_inventories wboi
452
+ where wboi."domainId"= $1 and
453
+ wboi."bizplaceId" = $2 and
454
+ wboi."groupType" = 'SINGLE' and
455
+ wboi."productId" = $3 and
456
+ wboi."packingType" = $4 and
457
+ wboi."packingSize" = $5 and
458
+ wboi."uom" = $6 and
459
+ wboi."remainQty" >= $7 and
460
+ wboi."remainUomValue" >= $8
461
+ `, [
462
+ warehouseDomain.id,
463
+ customerBizplace.id,
464
+ item.product.id,
465
+ item.packingType,
466
+ item.packingSize,
467
+ item.uom,
468
+ item.releaseQty,
469
+ item.releaseUomValue
470
+ ]);
471
+ if (itemList.length <= 0) {
472
+ errorList.push(JSON.stringify({
473
+ code: 'insufficient stock',
474
+ sku: item.product.sku,
475
+ packingType: item.packingType,
476
+ packingSize: item.packingSize,
477
+ uom: item.uom,
478
+ qty: item.releaseQty,
479
+ uom_value: item.releaseUomValue
480
+ }));
481
+ }
482
+ console.log();
483
+ }));
484
+ if (errorList.length > 0) {
485
+ context.throw(404, errorList.join(', '));
486
+ }
487
+ let assignedOrderProducts = await assignToInventory(massagedData.orderProducts, customerBizplace, warehouseDomain, context, tx);
488
+ let result = await createReleaseGood(releaseGood, assignedOrderProducts, customerBizplace, warehouseDomain, context, tx);
489
+ let addReleaseGood = {
490
+ id: result.id,
491
+ name: result.name,
492
+ refNo: result.refNo,
493
+ refNo2: result.refNo2,
494
+ refNo3: result.refNo3,
495
+ status: result.status,
496
+ truckNo: result.truckNo,
497
+ ownTransport: result.ownTransport,
498
+ crossDocking: result.crossDocking,
499
+ type: result.type,
500
+ exportOption: result.exportOption,
501
+ releaseDate: result.releaseDate,
502
+ collectionOrderNo: result.collectionOrderNo,
503
+ paidAmount: result === null || result === void 0 ? void 0 : result.paidAmount,
504
+ discountAmount: result === null || result === void 0 ? void 0 : result.discountAmount,
505
+ taxAmount: result === null || result === void 0 ? void 0 : result.taxAmount,
506
+ shippingFee: result === null || result === void 0 ? void 0 : result.shippingFee,
507
+ bizplace: { name: result.bizplace.name },
508
+ domain: { name: result.domain.name },
509
+ orderInventories: result.orderInventories
510
+ };
511
+ context.body = { result: { addReleaseGood } };
512
+ // createPayloadLog(
513
+ // customerBizplace.id,
514
+ // `/webhook-xilnex-stock-request`,
515
+ // stockRequest,
516
+ // context.body,
517
+ // context,
518
+ // PayloadType.INGESTION
519
+ // )
520
+ }
521
+ // VDC inbound
522
+ if (accountInfo.outlet == orderInfo.fromLocation) {
523
+ const ganNoSetting = await tx.getRepository(setting_base_1.Setting).findOne({
524
+ where: {
525
+ domain: warehouseDomain,
526
+ name: sales_base_1.ORDER_NUMBER_SETTING_KEY.GAN_NUMBER_RULE
527
+ }
528
+ });
529
+ let orderNo = '';
530
+ if (ganNoSetting) {
531
+ orderNo = await (0, id_rule_base_1.generateId)({ domain: warehouseDomain, type: sales_base_1.ORDER_NUMBER_RULE_TYPE.GAN_NUMBER, seed: {} });
532
+ }
533
+ else {
534
+ orderNo = sales_base_1.OrderNoGenerator.arrivalNotice();
535
+ }
536
+ let arrivalNotice = {
537
+ name: orderNo,
538
+ refNo: orderInfo.orderId,
539
+ refNo2: orderInfo.fromLocation,
540
+ refNo3: orderInfo.toLocation,
541
+ etaDate: orderInfo.etaDate,
542
+ ownTransport: orderInfo.ownTransport,
543
+ importCargo: orderInfo.importCargo,
544
+ container: orderInfo.container,
545
+ remark: orderInfo.remark,
546
+ source: 'XILNEX',
547
+ orderSourceType: 'XILNEX_STOCK_REQUEST',
548
+ domain: warehouseDomain,
549
+ bizplace: customerBizplace,
550
+ status: 'INTRANSIT'
551
+ };
552
+ arrivalNotice = await tx.getRepository(sales_base_1.ArrivalNotice).save(arrivalNotice);
553
+ let orderProducts = [];
554
+ await Promise.all(orderItems.map(async (item) => {
555
+ const productDetail = await (0, typeorm_1.getRepository)(product_base_1.ProductDetail).findOne({
556
+ where: { domain: custCompanyBizplace.domain, refCode: item.product.refCode },
557
+ relations: ['product']
558
+ });
559
+ if (!productDetail) {
560
+ context.throw(404, `no matching product found`);
561
+ }
562
+ let orderProduct = {
563
+ name: sales_base_1.OrderNoGenerator.orderProduct(),
564
+ product: productDetail === null || productDetail === void 0 ? void 0 : productDetail.product,
565
+ productDetail,
566
+ batchId: (item === null || item === void 0 ? void 0 : item.batchId) || '-',
567
+ packingType: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingType,
568
+ packingSize: productDetail === null || productDetail === void 0 ? void 0 : productDetail.packingSize,
569
+ uom: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uom,
570
+ uomValue: productDetail === null || productDetail === void 0 ? void 0 : productDetail.uomValue,
571
+ packQty: item.confirmedQty,
572
+ totalUomValue: `${(productDetail.uomValue * item.confirmedQty).toFixed(2)} ${productDetail.uom}`,
573
+ status: 'PENDING',
574
+ arrivalNotice,
575
+ domain: warehouseDomain,
576
+ bizplace: customerBizplace
577
+ };
578
+ orderProducts.push(orderProduct);
579
+ }));
580
+ await tx.getRepository(sales_base_1.OrderProduct).save(orderProducts);
581
+ }
582
+ }
583
+ }
584
+ accountWebhookOrder.status = integration_accounting_1.ACCOUNT_SYNC_STATUS.SUCCESS;
585
+ await tx.getRepository(integration_accounting_1.AccountWebhookOrder).save(accountWebhookOrder);
586
+ });
114
587
  context.status = 200;
115
588
  }
116
589
  catch (err) {
117
590
  env_1.logger.error(`xilnex-router[webhook-xilnex-stock-request]: ${err}`);
118
591
  }
119
592
  });
593
+ async function checkExistingOrder(releaseGood, bizplace, checkDuplicationValidation = true, context, tx) {
594
+ const { domain } = context.state;
595
+ const refNo = releaseGood.refNo;
596
+ const refNo2 = releaseGood.refNo2;
597
+ const refNo3 = releaseGood.refNo3;
598
+ if (checkDuplicationValidation) {
599
+ const foundDraftReleaseGood = await tx.getRepository(sales_base_1.DraftReleaseGood).findOne({
600
+ where: {
601
+ domain,
602
+ refNo: refNo || null,
603
+ refNo2: refNo2 || null,
604
+ refNo3: refNo3 || null,
605
+ status: (0, typeorm_1.Not)((0, typeorm_1.In)([sales_base_1.DRAFT_RELEASE_ORDER_STATUS.CANCELLED]))
606
+ }
607
+ });
608
+ if (foundDraftReleaseGood) {
609
+ throw new index_1.ValidationError({
610
+ errorCode: 'EXISTING_ORDER',
611
+ message: 'existing draft release order found',
612
+ detail: foundDraftReleaseGood.name
613
+ });
614
+ }
615
+ const foundReleaseGood = await tx.getRepository(sales_base_1.ReleaseGood).findOne({
616
+ where: {
617
+ domain: domain,
618
+ refNo,
619
+ status: (0, typeorm_1.Not)((0, typeorm_1.In)([sales_base_1.ORDER_STATUS.CANCELLED, sales_base_1.ORDER_STATUS.PENDING_CANCEL]))
620
+ },
621
+ relations: ['bizplace', 'bizplace.domain', 'bizplace.company', 'bizplace.company.domain']
622
+ });
623
+ if (foundReleaseGood) {
624
+ throw new index_1.ValidationError({
625
+ errorCode: 'EXISTING_ORDER',
626
+ message: 'existing release order found',
627
+ detail: foundReleaseGood.name
628
+ });
629
+ }
630
+ }
631
+ }
632
+ async function massageOrderItems(releaseGood, inputOrderProducts, context) {
633
+ try {
634
+ let orderProducts = [];
635
+ let errorList = [];
636
+ let totalReleaseQty = 0;
637
+ //combine any repeated skus
638
+ inputOrderProducts = inputOrderProducts.reduce((acc, itm, idx) => {
639
+ let { sku, refCode } = itm.product;
640
+ let { packingType, packingSize, confirmedQty, orderItemId } = itm;
641
+ if (!sku && !refCode) {
642
+ errorList.push(JSON.stringify({
643
+ code: 'INVALID_DATA',
644
+ message: 'sku or refCode not found',
645
+ sku: sku,
646
+ refCode: refCode,
647
+ packingType: packingType === null || packingType === void 0 ? void 0 : packingType.trim(),
648
+ packingSize: packingSize,
649
+ qty: confirmedQty
650
+ }));
651
+ }
652
+ if (confirmedQty <= 0) {
653
+ errorList.push(JSON.stringify({
654
+ code: 'INVALID_DATA',
655
+ message: 'negative stock request',
656
+ sku: sku,
657
+ refCode: refCode,
658
+ packingType: packingType === null || packingType === void 0 ? void 0 : packingType.trim(),
659
+ packingSize: packingSize,
660
+ qty: confirmedQty
661
+ }));
662
+ }
663
+ let existingIndex = acc.findIndex(itm => (itm === null || itm === void 0 ? void 0 : itm.sku) == (sku === null || sku === void 0 ? void 0 : sku.trim()) &&
664
+ (itm === null || itm === void 0 ? void 0 : itm.refCode) == (refCode === null || refCode === void 0 ? void 0 : refCode.trim()) &&
665
+ (itm === null || itm === void 0 ? void 0 : itm.packingType) == (packingType === null || packingType === void 0 ? void 0 : packingType.trim()) &&
666
+ (itm === null || itm === void 0 ? void 0 : itm.packingSize) == packingSize &&
667
+ (itm === null || itm === void 0 ? void 0 : itm.refItemId) == orderItemId.trim());
668
+ if (existingIndex >= 0 && confirmedQty > 0) {
669
+ acc[existingIndex].confirmedQty = acc[existingIndex].confirmedQty + confirmedQty;
670
+ totalReleaseQty += confirmedQty;
671
+ }
672
+ else {
673
+ acc.push({
674
+ sku: sku === null || sku === void 0 ? void 0 : sku.trim(),
675
+ refCode: refCode === null || refCode === void 0 ? void 0 : refCode.trim(),
676
+ packingType: packingType === null || packingType === void 0 ? void 0 : packingType.trim(),
677
+ packingSize: packingSize,
678
+ releaseQty: confirmedQty,
679
+ refItemId: orderItemId
680
+ });
681
+ totalReleaseQty += confirmedQty;
682
+ }
683
+ return acc;
684
+ }, []);
685
+ if (errorList.length > 0) {
686
+ throw new index_1.ValidationError({
687
+ errorCode: 'INVALID_DATA',
688
+ message: 'invalid data',
689
+ detail: errorList.join(', ')
690
+ });
691
+ }
692
+ //find customer sku in system
693
+ await Promise.all(inputOrderProducts.map(async (inputOrderItem) => {
694
+ const sku = inputOrderItem.sku;
695
+ const refCode = inputOrderItem.refCode;
696
+ const packingType = inputOrderItem.packingType;
697
+ const packingSize = inputOrderItem.packingSize;
698
+ const qb = (0, typeorm_1.getRepository)(product_base_1.ProductDetail)
699
+ .createQueryBuilder('PD')
700
+ .innerJoinAndSelect('PD.product', 'PROD')
701
+ .where('PROD.bizplace_id = :bizplaceId', { bizplaceId: releaseGood.companyBizplace.id });
702
+ if (!sku && !refCode) {
703
+ context.throw(404, `error.sku ${sku} or refCode ${refCode} not found`);
704
+ }
705
+ if (refCode) {
706
+ qb.andWhere('PD.ref_code = :refCode', { refCode });
707
+ }
708
+ if (sku) {
709
+ qb.andWhere('PROD.sku = :sku', { sku });
710
+ }
711
+ if (packingType) {
712
+ qb.andWhere('PD.packing_type = :packingType', { packingType });
713
+ }
714
+ if ((!packingType && !refCode) || !packingSize) {
715
+ qb.andWhere('PD.is_default = :isDefault', { isDefault: true });
716
+ }
717
+ const productDetail = await qb.getOne();
718
+ // order Product based on product detail
719
+ if (productDetail) {
720
+ let newOrderProduct = Object.assign({}, inputOrderItem);
721
+ newOrderProduct = Object.assign(Object.assign({}, newOrderProduct), { product: productDetail.product, productDetail: productDetail, packingType: productDetail.packingType, packingSize: packingSize || productDetail.packingSize, uom: productDetail.uom, uomValue: productDetail.uomValue, refCode: inputOrderItem.refCode, releaseQty: inputOrderItem.releaseQty, releaseUomValue: inputOrderItem.releaseQty * (productDetail.uomValue || 1), packQty: 0, actualPackQty: 0, palletQty: 0, actualPalletQty: 0, type: 'RELEASE_OF_GOODS', status: sales_base_1.ORDER_PRODUCT_STATUS.ASSIGNED, batchId: '-', sellingPrice: inputOrderItem === null || inputOrderItem === void 0 ? void 0 : inputOrderItem.sellingPrice });
722
+ newOrderProduct.orderInventories = [
723
+ Object.assign(Object.assign({}, newOrderProduct), { packQty: 0, actualPackQty: 0, palletQty: 0, actualPalletQty: 0, type: 'RELEASE_OF_GOODS', status: sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET })
724
+ ];
725
+ orderProducts.push(newOrderProduct);
726
+ }
727
+ if (!productDetail) {
728
+ const qb = (0, typeorm_1.getRepository)(product_base_1.ProductBundle)
729
+ .createQueryBuilder('PB')
730
+ .innerJoinAndSelect('PB.productBundleSettings', 'PBS')
731
+ .innerJoinAndSelect('PBS.productDetail', 'PBD')
732
+ .innerJoinAndSelect('PBD.product', 'PROD')
733
+ .where('PB.bizplace_id = :bizplaceId', { bizplaceId: releaseGood.companyBizplace.id });
734
+ if (!sku && !refCode) {
735
+ context.throw(404, `error.sku ${sku} or refCode ${refCode} not found`);
736
+ }
737
+ if (refCode) {
738
+ qb.andWhere('PB.ref_code = :refCode', { refCode });
739
+ }
740
+ if (sku) {
741
+ qb.andWhere('PB.sku = :sku', { sku });
742
+ }
743
+ if (packingType) {
744
+ qb.andWhere('PB.packing_type = :packingType', { packingType });
745
+ }
746
+ const foundProductBundle = await qb.getOne();
747
+ if (foundProductBundle) {
748
+ let orderProduct = {
749
+ productBundle: foundProductBundle,
750
+ refCode,
751
+ packingType: foundProductBundle.packingType,
752
+ packingSize: packingSize || 1,
753
+ uom: inputOrderItem === null || inputOrderItem === void 0 ? void 0 : inputOrderItem.uom,
754
+ releaseQty: inputOrderItem.releaseQty,
755
+ releaseUomValue: inputOrderItem.releaseQty,
756
+ packQty: 0,
757
+ actualPackQty: 0,
758
+ palletQty: 0,
759
+ actualPalletQty: 0,
760
+ orderInventories: [],
761
+ type: 'RELEASE_OF_GOODS',
762
+ status: sales_base_1.ORDER_PRODUCT_STATUS.ASSIGNED,
763
+ batchId: '-',
764
+ sellingPrice: inputOrderItem === null || inputOrderItem === void 0 ? void 0 : inputOrderItem.sellingPrice
765
+ };
766
+ const bundleProductSettings = foundProductBundle.productBundleSettings;
767
+ bundleProductSettings.map(bundleProductSetting => {
768
+ const productDetailBundle = bundleProductSetting.productDetail;
769
+ let newOrderInventory = {
770
+ product: productDetailBundle.product,
771
+ productDetail: productDetailBundle,
772
+ sku: productDetailBundle.product.sku,
773
+ packingType: productDetailBundle.packingType,
774
+ packingSize: productDetailBundle.packingSize,
775
+ uom: productDetailBundle.uom,
776
+ packQty: 0,
777
+ actualPackQty: 0,
778
+ palletQty: 0,
779
+ actualPalletQty: 0,
780
+ releaseQty: inputOrderItem.confirmedQty * bundleProductSetting.bundleQty,
781
+ releaseUomValue: inputOrderItem.confirmedQty *
782
+ bundleProductSetting.bundleQty *
783
+ (productDetailBundle.uomValue <= 0 ? 1 : productDetailBundle.uomValue),
784
+ type: 'RELEASE_OF_GOODS',
785
+ status: sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET
786
+ };
787
+ orderProduct.orderInventories.push(newOrderInventory);
788
+ });
789
+ orderProducts.push(orderProduct);
790
+ }
791
+ else {
792
+ if (sku && refCode) {
793
+ context.throw(404, `error.sku ${sku} and refCode ${refCode} not exist in product / bundle master`);
794
+ }
795
+ if (sku) {
796
+ context.throw(404, `error.sku ${sku} not exist in product / bundle master`);
797
+ }
798
+ if (refCode) {
799
+ context.throw(404, `error.refCode ${refCode} not exist in product / bundle master`);
800
+ }
801
+ }
802
+ }
803
+ }));
804
+ //combine any repeated skus
805
+ let combinedItems = orderProducts.reduce((acc, op, idx) => {
806
+ let oi = (op === null || op === void 0 ? void 0 : op.orderInventories) || [];
807
+ oi.forEach(itm => {
808
+ let { sku, refCode } = itm.product;
809
+ let { packingType, packingSize, batchId, releaseQty, product, productDetail, uom } = itm;
810
+ if (!sku && !refCode) {
811
+ context.throw(404, `sku ${sku} or refCode ${refCode} not found`);
812
+ }
813
+ let existingIndex = acc.findIndex(itm => (itm === null || itm === void 0 ? void 0 : itm.sku) == (sku === null || sku === void 0 ? void 0 : sku.trim()) &&
814
+ (itm === null || itm === void 0 ? void 0 : itm.refCode) == (refCode === null || refCode === void 0 ? void 0 : refCode.trim()) &&
815
+ (itm === null || itm === void 0 ? void 0 : itm.packingType) == (packingType === null || packingType === void 0 ? void 0 : packingType.trim()) &&
816
+ (itm === null || itm === void 0 ? void 0 : itm.packingSize) == packingSize &&
817
+ (itm === null || itm === void 0 ? void 0 : itm.batchId) == (batchId === null || batchId === void 0 ? void 0 : batchId.trim()) &&
818
+ (itm === null || itm === void 0 ? void 0 : itm.uom) == (uom === null || uom === void 0 ? void 0 : uom.trim()));
819
+ if (existingIndex >= 0 && releaseQty > 0) {
820
+ acc[existingIndex].releaseQty = acc[existingIndex].releaseQty + releaseQty;
821
+ acc[existingIndex].releaseUomValue =
822
+ acc[existingIndex].releaseUomValue + releaseQty * (productDetail.uomValue <= 0 ? 1 : productDetail.uomValue);
823
+ }
824
+ else {
825
+ let updateObj = {
826
+ sku: sku === null || sku === void 0 ? void 0 : sku.trim(),
827
+ packingType: packingType === null || packingType === void 0 ? void 0 : packingType.trim(),
828
+ packingSize: packingSize,
829
+ uom: uom === null || uom === void 0 ? void 0 : uom.trim(),
830
+ releaseQty: releaseQty,
831
+ releaseUomValue: releaseQty * (productDetail.uomValue <= 0 ? 1 : productDetail.uomValue),
832
+ product,
833
+ productDetail
834
+ };
835
+ if (refCode)
836
+ updateObj['refCode'] = refCode.trim();
837
+ updateObj['batchId'] = batchId ? batchId === null || batchId === void 0 ? void 0 : batchId.trim() : '-';
838
+ acc.push(updateObj);
839
+ }
840
+ });
841
+ return acc;
842
+ }, []);
843
+ return { orderProducts, combinedItems };
844
+ }
845
+ catch (error) {
846
+ throw error;
847
+ }
848
+ }
849
+ async function assignToInventory(orderProducts, customerBizplace, warehouseDomain, context, tx) {
850
+ const { client, domain, user } = context.state;
851
+ for (let opIdx = 0; opIdx < orderProducts.length; opIdx++) {
852
+ let orderProduct = orderProducts[opIdx];
853
+ let assignedOrderInventories = [];
854
+ const pickingProductSetting = await tx.getRepository(setting_base_1.Setting).findOne({
855
+ where: { domain, name: 'rule-for-picking-product' }
856
+ });
857
+ for (let oiIdx = 0; oiIdx < orderProducts[opIdx].orderInventories.length; oiIdx++) {
858
+ const orderInventory = orderProduct.orderInventories[oiIdx];
859
+ let sortings = [];
860
+ switch (orderInventory.product.pickingStrategy) {
861
+ case 'LIFO':
862
+ sortings.push({ name: 'iv.created_at', desc: true });
863
+ if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
864
+ for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
865
+ sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
866
+ }
867
+ }
868
+ else {
869
+ sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
870
+ }
871
+ break;
872
+ case 'FEFO':
873
+ sortings.push({ name: 'iv.expiration_date', desc: false }, { name: 'iv.created_at', desc: false });
874
+ if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
875
+ for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
876
+ sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
877
+ }
878
+ }
879
+ else {
880
+ sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
881
+ }
882
+ break;
883
+ case 'FMFO':
884
+ sortings.push({ name: 'iv.manufacture_date', desc: false }, { name: 'iv.created_at', desc: false });
885
+ if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
886
+ for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
887
+ sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
888
+ }
889
+ }
890
+ else {
891
+ sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
892
+ }
893
+ break;
894
+ case 'LOCATION':
895
+ if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
896
+ for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
897
+ sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
898
+ }
899
+ }
900
+ else {
901
+ sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
902
+ }
903
+ break;
904
+ //Every other case includes 'FIFO' will be applicable for this case
905
+ default:
906
+ sortings.push({ name: 'iv.created_at', desc: false });
907
+ if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
908
+ for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
909
+ sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
910
+ }
911
+ }
912
+ else {
913
+ sortings.push({ name: 'loc.name', desc: false });
914
+ sortings.push({ name: 'iv.pallet_id', desc: false });
915
+ }
916
+ break;
917
+ }
918
+ let queryFilters = [];
919
+ let queryStrings = queryFilters.reduce((acc, itm, idx, arr) => {
920
+ acc.values.push(itm.filters);
921
+ switch (itm === null || itm === void 0 ? void 0 : itm.operator) {
922
+ case 'notin':
923
+ case 'in':
924
+ acc.query.push(`${itm.query} ${itm.operator == 'notin' ? 'NOT IN' : 'IN'} (${itm.filtes
925
+ .map((itm, idx) => {
926
+ return `$${acc.values.length + 1}`;
927
+ })
928
+ .join(',')})`);
929
+ break;
930
+ default:
931
+ acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
932
+ break;
933
+ }
934
+ acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
935
+ }, {
936
+ query: [],
937
+ values: []
938
+ });
939
+ let sortQuery = sortings
940
+ .map(itm => {
941
+ return `${itm.name} ${itm.desc ? 'DESC' : 'ASC'}`;
942
+ })
943
+ .join(`,`);
944
+ let updatedInventories = await tx.getRepository(warehouse_base_1.Inventory).query(`
945
+ update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
946
+ locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
947
+ updated_at = NOW(),
948
+ updater_id = $1
949
+ from (
950
+ select "id", "pallet_id","carton_id", "batch_id", "batch_id_ref", "unit", "uom", "packing_type", "packing_size", "manufacture_year",
951
+ "reserve_qty", (("uom_value"/"qty") * "reserve_qty") as "reserve_uom_value" from (
952
+ 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",
953
+ "release_qty", "release_uom_value", lock_amount,
954
+ case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end as "reserve_qty"
955
+ from (
956
+ select *,
957
+ qty - locked_qty as available_qty,
958
+ sum(qty - locked_qty - release_qty) over (order by sort_seq asc rows between unbounded preceding and current row) as lock_amount
959
+ from (
960
+ SELECT 0 as sort_seq, null as id, null as pallet_id, null as batch_id, null as batch_id_ref,
961
+ null as unit, '${orderInventory.uom}' as uom, '${orderInventory.packingType}' as packing_type, '${orderInventory.packingSize}' as packing_size,
962
+ null as manufacture_year, null as carton_id, 0 as uom_value, 0 as locked_uom_value, 0 as qty, 0 as locked_qty, null as created_at,
963
+ '${orderInventory.releaseQty}' as release_qty, '${orderInventory.releaseUomValue}' as release_uom_value
964
+ UNION
965
+ (
966
+ SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY ${sortQuery}) as sort_seq,
967
+ iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
968
+ iv.unit, iv.uom, iv.packing_type, iv.packing_size,
969
+ iv.manufacture_year, iv.carton_id, iv.uom_value,
970
+ coalesce(iv.locked_uom_value,0) as locked_uom_value, iv.qty, coalesce(iv.locked_qty,0) as locked_qty, iv.created_at,
971
+ 0 as release_qty, 0 as release_uom_value
972
+ FROM "inventories" "iv"
973
+ INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
974
+ WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
975
+ "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)
976
+ ${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
977
+ ORDER BY ${sortQuery}
978
+ )
979
+ ) dt1
980
+ ) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
981
+ ) dt3 where sort_seq > 0
982
+ ) src where src.id = tgt.id
983
+ returning
984
+ tgt."id", tgt."qty", tgt."pallet_id", tgt."carton_id", tgt."batch_id", tgt."batch_id_ref", tgt."unit",
985
+ tgt."uom", tgt."packing_type", tgt."packing_size", tgt."manufacture_year",
986
+ tgt."locked_qty", tgt."uom_value", tgt."locked_uom_value", src."reserve_qty", src."reserve_uom_value";`, [
987
+ (user === null || user === void 0 ? void 0 : user.id) || null,
988
+ warehouseDomain.id,
989
+ customerBizplace.id,
990
+ orderInventory.packingType,
991
+ orderInventory.packingSize,
992
+ orderInventory.product.id,
993
+ warehouse_base_1.INVENTORY_STATUS.STORED,
994
+ warehouse_base_1.LOCATION_TYPE.QUARANTINE,
995
+ warehouse_base_1.LOCATION_TYPE.RESERVE,
996
+ ...queryStrings.values
997
+ ]);
998
+ let totalAssigned = updatedInventories[0].reduce((acc, inv) => {
999
+ return acc + inv.reserve_qty;
1000
+ }, 0);
1001
+ if (orderInventory.releaseQty != totalAssigned) {
1002
+ throw new index_1.ValidationError({
1003
+ errorCode: 'INSUFFICIENT_STOCK',
1004
+ message: 'insufficient stock',
1005
+ detail: JSON.stringify({
1006
+ code: 'insufficient stock',
1007
+ sku: orderInventory.product.sku,
1008
+ packingType: orderInventory.packingType,
1009
+ packingSize: orderInventory.packingSize,
1010
+ uom: orderInventory.uom,
1011
+ qty: orderInventory.releaseQty,
1012
+ uom_value: orderInventory.releaseUomValue
1013
+ })
1014
+ });
1015
+ }
1016
+ updatedInventories[0].forEach(inv => {
1017
+ 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: warehouseDomain.id }, bizplace: { id: customerBizplace.id } });
1018
+ assignedOrderInventories.push(oi);
1019
+ });
1020
+ }
1021
+ orderProducts[opIdx].orderInventories = assignedOrderInventories;
1022
+ }
1023
+ return orderProducts;
1024
+ }
1025
+ async function createReleaseGood(releaseGood, orderProducts, bizplace, domain, context, tx) {
1026
+ try {
1027
+ if (orderProducts.length <= 0) {
1028
+ throw new index_1.ValidationError({
1029
+ errorCode: 'ITEM_NOT_FOUND',
1030
+ message: 'no order items found',
1031
+ detail: JSON.stringify(releaseGood)
1032
+ });
1033
+ }
1034
+ let newReleaseGood = new sales_base_1.ReleaseGood();
1035
+ // find RO number rule setting
1036
+ const roNoSetting = await tx.getRepository(setting_base_1.Setting).findOne({
1037
+ where: {
1038
+ domain,
1039
+ name: sales_base_1.ORDER_NUMBER_SETTING_KEY.RO_NUMBER_RULE
1040
+ }
1041
+ });
1042
+ newReleaseGood = {
1043
+ name: roNoSetting
1044
+ ? await (0, id_rule_base_1.generateId)({ domain, type: sales_base_1.ORDER_NUMBER_RULE_TYPE.RO_NUMBER, seed: {} })
1045
+ : sales_base_1.OrderNoGenerator.releaseGood(),
1046
+ domain: domain,
1047
+ bizplace: bizplace,
1048
+ collectionOrderNo: releaseGood.collectionOrderNo,
1049
+ courierOption: releaseGood.courierOption,
1050
+ exportOption: releaseGood.exportOption,
1051
+ ownTransport: releaseGood.ownTransport,
1052
+ packingOption: releaseGood.packingOption,
1053
+ marketplaceOrderStatus: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.marketplaceOrderStatus) || null,
1054
+ refNo: releaseGood.refNo,
1055
+ refNo2: releaseGood.refNo2 || null,
1056
+ refNo3: releaseGood.refNo3 || null,
1057
+ remark: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.remark) || null,
1058
+ releaseDate: releaseGood.releaseDate,
1059
+ truckNo: releaseGood.truckNo,
1060
+ type: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.type) ? releaseGood.type : 'b2b',
1061
+ status: sales_base_1.ORDER_STATUS.PENDING_WORKSHEET,
1062
+ noOfItems: orderProducts.length,
1063
+ source: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.source,
1064
+ paidAmount: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.paidAmount,
1065
+ discountAmount: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.discountAmount,
1066
+ taxAmount: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.taxAmount,
1067
+ shippingFee: releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.shippingFee,
1068
+ acceptedAt: new Date()
1069
+ };
1070
+ let resultReleaseGood = await tx.getRepository(sales_base_1.ReleaseGood).save(newReleaseGood);
1071
+ let combinedOrderInventories = [];
1072
+ let savedOrderProducts = await Promise.all(orderProducts.map(async (orderProduct) => {
1073
+ let orderInventories = orderProduct.orderInventories;
1074
+ orderProduct = Object.assign(new sales_base_1.OrderProduct(), Object.assign(Object.assign({}, orderProduct), { name: (0, uuid_1.v4)(), domain: domain, bizplace: bizplace, releaseGood: resultReleaseGood, type: sales_base_1.ORDER_TYPES.RELEASE_OF_GOODS, status: sales_base_1.ORDER_PRODUCT_STATUS.ASSIGNED }));
1075
+ let result = await tx.getRepository(sales_base_1.OrderProduct).save(orderProduct);
1076
+ for (let oiIdx = 0; oiIdx < orderInventories.length; oiIdx++) {
1077
+ let orderInventory = Object.assign(new sales_base_1.OrderInventory(), Object.assign(Object.assign({}, orderInventories[oiIdx]), { name: (0, uuid_1.v4)(), domain: domain, bizplace: bizplace, releaseGood: resultReleaseGood, type: sales_base_1.ORDER_TYPES.RELEASE_OF_GOODS, status: sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET }));
1078
+ await tx.getRepository(sales_base_1.OrderInventory).save(orderInventory);
1079
+ combinedOrderInventories.push({
1080
+ product: { name: orderInventory.product.name, sku: orderInventory.product.sku },
1081
+ batchId: orderInventory.batchId,
1082
+ packingType: orderInventory.packingType,
1083
+ packingSize: orderInventory.packingSize,
1084
+ releaseQty: orderInventory.releaseQty,
1085
+ releaseUomValue: orderInventory.releaseUomValue
1086
+ });
1087
+ }
1088
+ return result;
1089
+ }));
1090
+ newReleaseGood.orderProducts = savedOrderProducts;
1091
+ newReleaseGood.orderInventories = combinedOrderInventories;
1092
+ return newReleaseGood;
1093
+ }
1094
+ catch (error) {
1095
+ throw error;
1096
+ }
1097
+ }
120
1098
  //# sourceMappingURL=xilnex-router.js.map