@things-factory/operato-hub 4.3.130 → 4.3.131

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