@things-factory/operato-hub 4.3.185 → 4.3.187
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/actions/hub.js +0 -5
- package/config.development.js +23 -8
- package/dist-server/graphql/resolvers/synchronization/get-lmd-awb.js +137 -0
- package/dist-server/graphql/resolvers/synchronization/get-lmd-awb.js.map +1 -0
- package/dist-server/graphql/resolvers/synchronization/index.js +2 -1
- package/dist-server/graphql/resolvers/synchronization/index.js.map +1 -1
- package/dist-server/graphql/types/synchronization/index.js +1 -0
- package/dist-server/graphql/types/synchronization/index.js.map +1 -1
- package/dist-server/routers/api/restful-apis/unstable/add-release-order.js +48 -6
- package/dist-server/routers/api/restful-apis/unstable/add-release-order.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/utils/params.js +3 -1
- package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js +49 -7
- package/dist-server/routers/api/restful-apis/v1/warehouse/add-release-order.js.map +1 -1
- package/dist-server/routers/xilnex-router.js +1 -0
- package/dist-server/routers/xilnex-router.js.map +1 -1
- package/package.json +18 -18
- package/server/graphql/resolvers/synchronization/get-lmd-awb.ts +178 -0
- package/server/graphql/resolvers/synchronization/index.ts +3 -1
- package/server/graphql/types/synchronization/index.ts +1 -0
- package/server/routers/api/restful-apis/unstable/add-release-order.ts +69 -7
- package/server/routers/api/restful-apis/v1/utils/params.ts +3 -1
- package/server/routers/api/restful-apis/v1/warehouse/add-release-order.ts +70 -8
- package/server/routers/xilnex-router.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/operato-hub",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.187",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@things-factory/auth-ui": "^4.3.179",
|
|
63
63
|
"@things-factory/biz-base": "^4.3.179",
|
|
64
64
|
"@things-factory/biz-ui": "^4.3.179",
|
|
65
|
-
"@things-factory/board-service": "^4.3.
|
|
65
|
+
"@things-factory/board-service": "^4.3.186",
|
|
66
66
|
"@things-factory/board-ui": "^4.3.179",
|
|
67
67
|
"@things-factory/code-ui": "^4.3.179",
|
|
68
68
|
"@things-factory/component-ui": "^4.3.159",
|
|
@@ -71,26 +71,26 @@
|
|
|
71
71
|
"@things-factory/export-ui": "^4.3.179",
|
|
72
72
|
"@things-factory/export-ui-csv": "^4.3.179",
|
|
73
73
|
"@things-factory/export-ui-excel": "^4.3.179",
|
|
74
|
-
"@things-factory/geography": "^4.3.
|
|
74
|
+
"@things-factory/geography": "^4.3.186",
|
|
75
75
|
"@things-factory/grist-ui": "^4.3.179",
|
|
76
76
|
"@things-factory/help": "^4.3.179",
|
|
77
77
|
"@things-factory/i18n-base": "^4.3.179",
|
|
78
78
|
"@things-factory/import-ui": "^4.3.179",
|
|
79
79
|
"@things-factory/import-ui-excel": "^4.3.179",
|
|
80
|
-
"@things-factory/integration-accounting": "^4.3.
|
|
81
|
-
"@things-factory/integration-fulfillment": "^4.3.
|
|
82
|
-
"@things-factory/integration-lmd": "^4.3.
|
|
83
|
-
"@things-factory/integration-marketplace": "^4.3.
|
|
84
|
-
"@things-factory/integration-pos": "^4.3.
|
|
85
|
-
"@things-factory/integration-sellercraft": "^4.3.
|
|
86
|
-
"@things-factory/integration-sftp": "^4.3.
|
|
87
|
-
"@things-factory/lite-menu": "^4.3.
|
|
80
|
+
"@things-factory/integration-accounting": "^4.3.186",
|
|
81
|
+
"@things-factory/integration-fulfillment": "^4.3.186",
|
|
82
|
+
"@things-factory/integration-lmd": "^4.3.186",
|
|
83
|
+
"@things-factory/integration-marketplace": "^4.3.186",
|
|
84
|
+
"@things-factory/integration-pos": "^4.3.186",
|
|
85
|
+
"@things-factory/integration-sellercraft": "^4.3.186",
|
|
86
|
+
"@things-factory/integration-sftp": "^4.3.186",
|
|
87
|
+
"@things-factory/lite-menu": "^4.3.186",
|
|
88
88
|
"@things-factory/more-ui": "^4.3.179",
|
|
89
|
-
"@things-factory/notification": "^4.3.
|
|
89
|
+
"@things-factory/notification": "^4.3.186",
|
|
90
90
|
"@things-factory/pdf": "^4.3.159",
|
|
91
|
-
"@things-factory/product-ui": "^4.3.
|
|
91
|
+
"@things-factory/product-ui": "^4.3.187",
|
|
92
92
|
"@things-factory/resource-ui": "^4.3.179",
|
|
93
|
-
"@things-factory/sales-base": "^4.3.
|
|
93
|
+
"@things-factory/sales-base": "^4.3.187",
|
|
94
94
|
"@things-factory/scene-data-transform": "^4.3.159",
|
|
95
95
|
"@things-factory/scene-excel": "^4.3.159",
|
|
96
96
|
"@things-factory/scene-firebase": "^4.3.159",
|
|
@@ -102,11 +102,11 @@
|
|
|
102
102
|
"@things-factory/scene-mqtt": "^4.3.179",
|
|
103
103
|
"@things-factory/scene-restful": "^4.3.159",
|
|
104
104
|
"@things-factory/scene-visualizer": "^4.3.159",
|
|
105
|
-
"@things-factory/setting-ui": "^4.3.
|
|
105
|
+
"@things-factory/setting-ui": "^4.3.186",
|
|
106
106
|
"@things-factory/shell": "^4.3.179",
|
|
107
107
|
"@things-factory/system-ui": "^4.3.179",
|
|
108
|
-
"@things-factory/warehouse-base": "^4.3.
|
|
109
|
-
"@things-factory/worksheet-base": "^4.3.
|
|
108
|
+
"@things-factory/warehouse-base": "^4.3.187",
|
|
109
|
+
"@things-factory/worksheet-base": "^4.3.187",
|
|
110
110
|
"cron-parser": "^4.7.0",
|
|
111
111
|
"koa2-swagger-ui": "^5.0.2",
|
|
112
112
|
"swagger-jsdoc": "^5.0.0",
|
|
@@ -118,5 +118,5 @@
|
|
|
118
118
|
"resolutions": {
|
|
119
119
|
"core-js": "^3.16.0"
|
|
120
120
|
},
|
|
121
|
-
"gitHead": "
|
|
121
|
+
"gitHead": "a6192bef4662aafeb4e9d4e715dca07aa1459b2d"
|
|
122
122
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { getRepository, getConnection, Not, In, IsNull, SelectQueryBuilder, EntityManager } from 'typeorm'
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
3
|
+
|
|
4
|
+
import { ReleaseGood, OrderPackage, ORDER_STATUS } from '@things-factory/sales-base'
|
|
5
|
+
import { LastMileAPI } from '@things-factory/integration-lmd'
|
|
6
|
+
import { Board } from '@things-factory/board-service'
|
|
7
|
+
import { logger } from '@things-factory/env'
|
|
8
|
+
import { headlessModel, pdf } from '@things-factory/board-service'
|
|
9
|
+
import { uploadAwb } from '@things-factory/attachment-base'
|
|
10
|
+
import { Attachment } from '@things-factory/attachment-base'
|
|
11
|
+
import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
|
|
12
|
+
import { ApplicationType } from '@things-factory/auth-base'
|
|
13
|
+
import { SellercraftController } from '@things-factory/worksheet-base'
|
|
14
|
+
import { Setting } from '@things-factory/setting-base'
|
|
15
|
+
|
|
16
|
+
const LMD_BOARDS = {
|
|
17
|
+
dhl: 'operato-lmd-dhl-awb',
|
|
18
|
+
airspeed: 'operato-lmd-airspeed-awb'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const getLmdAwbResolver = {
|
|
22
|
+
async getLmdAwb(_: any, {}, context: any) {
|
|
23
|
+
const qb: SelectQueryBuilder<OrderPackage> = getRepository(OrderPackage)
|
|
24
|
+
.createQueryBuilder('op')
|
|
25
|
+
.innerJoinAndSelect('op.releaseGood', 'rg')
|
|
26
|
+
.innerJoinAndSelect('rg.domain', 'd')
|
|
27
|
+
.innerJoinAndSelect('rg.bizplace', 'b')
|
|
28
|
+
.innerJoinAndSelect('b.domain', 'd2')
|
|
29
|
+
.leftJoinAndSelect('rg.lastMileDelivery', 'lmd')
|
|
30
|
+
.where('rg.lmd_option = true')
|
|
31
|
+
.andWhere('rg.status NOT IN (:...statuses)', {
|
|
32
|
+
statuses: [ORDER_STATUS.CANCELLED, ORDER_STATUS.PENDING_CANCEL, ORDER_STATUS.DONE]
|
|
33
|
+
})
|
|
34
|
+
.andWhere('op.parcel_id IS NOT NULL')
|
|
35
|
+
.andWhere('op.airway_bill IS NULL')
|
|
36
|
+
|
|
37
|
+
const orderPackages: OrderPackage[] = await qb.getMany()
|
|
38
|
+
|
|
39
|
+
let releaseOrders: ReleaseGood = []
|
|
40
|
+
|
|
41
|
+
for (let orderPackage of orderPackages) {
|
|
42
|
+
await getConnection().transaction(async tx => {
|
|
43
|
+
try {
|
|
44
|
+
let awbRes = await LastMileAPI.getAwb(
|
|
45
|
+
{ ...orderPackage.releaseGood.lastMileDelivery, platform: 'lmdMiddleware' },
|
|
46
|
+
{ parcelId: orderPackage.parcelId }
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if (awbRes) {
|
|
50
|
+
await constructAndUploadAwb(orderPackage.releaseGood, orderPackage, awbRes)
|
|
51
|
+
releaseOrders.push(orderPackage.releaseGood)
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
logger.error(e)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// trigger RTS
|
|
60
|
+
try {
|
|
61
|
+
let uniqueReleaseOrders: ReleaseGood = [...new Map(releaseOrders.map(item => [item['id'], item])).values()]
|
|
62
|
+
|
|
63
|
+
for (let releaseOrder of uniqueReleaseOrders) {
|
|
64
|
+
const orderSource: string = releaseOrder.source
|
|
65
|
+
|
|
66
|
+
if (orderSource == ApplicationType.SELLERCRAFT) {
|
|
67
|
+
const sellercraft: Sellercraft = await getRepository(Sellercraft).findOne({
|
|
68
|
+
domain: releaseOrder.bizplace.domain,
|
|
69
|
+
status: SellercraftStatus.ACTIVE
|
|
70
|
+
})
|
|
71
|
+
if (sellercraft) {
|
|
72
|
+
await getConnection().transaction(async (tx: EntityManager) => {
|
|
73
|
+
const sellercraftCtrl: SellercraftController = new SellercraftController(tx, null, null)
|
|
74
|
+
const rtsTriggerLevel: Setting = await tx.getRepository(Setting).findOne({
|
|
75
|
+
where: { domain: releaseOrder.domain, category: 'id-rule', name: 'rts-trigger-level' }
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (rtsTriggerLevel && parseInt(rtsTriggerLevel?.value || 0) == 1) {
|
|
79
|
+
await sellercraftCtrl.initiateOrderShipment(sellercraft, releaseOrder)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
logger.error(`[get-lmd-awb-sof-trigger]: ${e}`)
|
|
87
|
+
}
|
|
88
|
+
//
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function constructAndUploadAwb(releaseOrder, orderPackage, awbRes) {
|
|
93
|
+
let {
|
|
94
|
+
shipmentID,
|
|
95
|
+
deliveryConfirmationNo,
|
|
96
|
+
deliveryDepotCode,
|
|
97
|
+
primarySortCode,
|
|
98
|
+
secondarySortCode,
|
|
99
|
+
recipient,
|
|
100
|
+
sender,
|
|
101
|
+
tracking_no,
|
|
102
|
+
courier
|
|
103
|
+
} = awbRes
|
|
104
|
+
|
|
105
|
+
let senderFullAddress = `${sender?.address}${sender?.address2 ? ', ' + sender?.address2 : ''}${
|
|
106
|
+
sender?.district ? ', ' + sender?.district : ''
|
|
107
|
+
}${sender?.city ? ', ' + sender?.city : ''}${sender?.postcode ? ', ' + sender?.postcode : ''}${
|
|
108
|
+
sender?.state ? ', ' + sender?.state : ''
|
|
109
|
+
}`
|
|
110
|
+
|
|
111
|
+
let recipientAddress1 = `${recipient?.address}${recipient?.address2 ? ', ' + recipient?.address2 : ''}${
|
|
112
|
+
recipient?.district ? ', ' + recipient?.district : ''
|
|
113
|
+
}`
|
|
114
|
+
|
|
115
|
+
let recipientAddress2 = `${recipient?.state}${recipient?.city ? ', ' + recipient?.city : ''}${
|
|
116
|
+
recipient?.postcode ? ', ' + recipient?.postcode : ''
|
|
117
|
+
}`
|
|
118
|
+
|
|
119
|
+
let data = {
|
|
120
|
+
senderFullAddress,
|
|
121
|
+
senderName: sender?.name,
|
|
122
|
+
recipientAddress1,
|
|
123
|
+
recipientAddress2,
|
|
124
|
+
recipientPhone: recipient?.phone,
|
|
125
|
+
recipientName: recipient?.name,
|
|
126
|
+
refId: shipmentID,
|
|
127
|
+
deliveryConfirmationNo: deliveryConfirmationNo || tracking_no,
|
|
128
|
+
deliveryDepotCode: deliveryDepotCode,
|
|
129
|
+
primarySortCode: primarySortCode,
|
|
130
|
+
secondarySortCode: secondarySortCode,
|
|
131
|
+
orderNo: releaseOrder?.refNo
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let lmdBoard: Board = await getRepository(Board).findOne({
|
|
135
|
+
where: { name: LMD_BOARDS[courier] },
|
|
136
|
+
relations: ['domain']
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
if (!lmdBoard) throw new Error(`[get-lmd-awb] board is not found (RO: ${releaseOrder.name})`)
|
|
140
|
+
|
|
141
|
+
const { model } = await headlessModel({ domain: lmdBoard.domain, id: lmdBoard.id })
|
|
142
|
+
|
|
143
|
+
const result = await pdf({
|
|
144
|
+
id: lmdBoard.id,
|
|
145
|
+
model,
|
|
146
|
+
data,
|
|
147
|
+
options: {
|
|
148
|
+
format: 'A4'
|
|
149
|
+
},
|
|
150
|
+
context: { state: { domain: lmdBoard.domain } }
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
let uuid = uuidv4()
|
|
154
|
+
let awb = await uploadAwb({
|
|
155
|
+
content: result,
|
|
156
|
+
title: uuid
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
if (!awb?.url) throw new Error(`[get-lmd-awb] awb was not uploaded for (RO: ${releaseOrder.name})`)
|
|
160
|
+
|
|
161
|
+
await getRepository(OrderPackage).update(
|
|
162
|
+
{ id: orderPackage.id },
|
|
163
|
+
{ airwayBill: awb.url, trackingNo: deliveryConfirmationNo || tracking_no }
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// create attachment
|
|
167
|
+
|
|
168
|
+
await getRepository(Attachment).insert({
|
|
169
|
+
name: uuid,
|
|
170
|
+
mimetype: 'application/pdf',
|
|
171
|
+
category: 'LMD_AWB',
|
|
172
|
+
refBy: orderPackage.id,
|
|
173
|
+
path: awb.url,
|
|
174
|
+
domain: releaseOrder.domain,
|
|
175
|
+
size: 0,
|
|
176
|
+
encoding: ''
|
|
177
|
+
})
|
|
178
|
+
}
|
|
@@ -8,6 +8,7 @@ import { syncProductsToAccountResolver } from './sync-products-to-account'
|
|
|
8
8
|
import { syncProductsToSellercraftResolver } from './sync-products-to-sellercraft'
|
|
9
9
|
import { createDatShipments } from './create-dat-shipments'
|
|
10
10
|
import { autoUpdateAllMarketplaceProductVariationQuantityResolver } from './auto-update-all-marketplace-product-variation-quantity'
|
|
11
|
+
import { getLmdAwbResolver } from './get-lmd-awb'
|
|
11
12
|
|
|
12
13
|
export const Query = {
|
|
13
14
|
...fetchSyncProductsResolver,
|
|
@@ -22,5 +23,6 @@ export const Mutation = {
|
|
|
22
23
|
...syncAllProductsToSellercraftResolver,
|
|
23
24
|
...sellercraftPackMarketplaceOrderResolver,
|
|
24
25
|
...createDatShipments,
|
|
25
|
-
...autoUpdateAllMarketplaceProductVariationQuantityResolver
|
|
26
|
+
...autoUpdateAllMarketplaceProductVariationQuantityResolver,
|
|
27
|
+
...getLmdAwbResolver
|
|
26
28
|
}
|
|
@@ -17,6 +17,7 @@ export const Mutation = /* GraphQL */ `
|
|
|
17
17
|
syncProductsToAccount(accountId: String!, bizplaceId: String!): Boolean @transaction
|
|
18
18
|
syncAllProductsToSellercraft: Boolean @transaction
|
|
19
19
|
sellercraftPackMarketplaceOrder: Boolean @transaction
|
|
20
|
+
getLmdAwb: Boolean @transaction
|
|
20
21
|
createDatShipments: Boolean @transaction
|
|
21
22
|
autoUpdateAllMarketplaceProductVariationQuantity(companyDomainId: String, warehouseId: String): Boolean @transaction
|
|
22
23
|
sendEmails: Boolean @transaction
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EntityManager,
|
|
3
|
+
getConnection,
|
|
4
|
+
getRepository,
|
|
5
|
+
In,
|
|
6
|
+
Not,
|
|
7
|
+
Repository,
|
|
8
|
+
SelectQueryBuilder,
|
|
9
|
+
ILike,
|
|
10
|
+
Brackets
|
|
11
|
+
} from 'typeorm'
|
|
2
12
|
import { v4 as uuidv4 } from 'uuid'
|
|
3
13
|
|
|
4
14
|
import { restfulApiRouter as router } from '@things-factory/api'
|
|
@@ -30,6 +40,7 @@ import {
|
|
|
30
40
|
import { Setting } from '@things-factory/setting-base'
|
|
31
41
|
import { Domain } from '@things-factory/shell'
|
|
32
42
|
import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
|
|
43
|
+
import { LastMileDelivery } from '@things-factory/integration-lmd'
|
|
33
44
|
|
|
34
45
|
import { ValidationError } from '../../../../errors/index'
|
|
35
46
|
import { businessMiddleware } from '../../../../middlewares/business-middleware'
|
|
@@ -116,7 +127,8 @@ router.post(
|
|
|
116
127
|
paidAmount: bodyReq?.paidAmount,
|
|
117
128
|
discountAmount: bodyReq?.discountAmount,
|
|
118
129
|
taxAmount: bodyReq?.taxAmount,
|
|
119
|
-
shippingFee: bodyReq?.shippingFee
|
|
130
|
+
shippingFee: bodyReq?.shippingFee,
|
|
131
|
+
lmdOption: bodyReq?.lmdOption
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// check existing order
|
|
@@ -132,6 +144,14 @@ router.post(
|
|
|
132
144
|
let itemList = await tx.query(
|
|
133
145
|
`
|
|
134
146
|
select wboi.* from warehouse_bizplace_onhand_inventories wboi
|
|
147
|
+
left join (
|
|
148
|
+
select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom, sum(i2.qty) as storage_qty, sum(i2.uom_value) as storage_uom_value from inventories i2
|
|
149
|
+
inner join locations l2 on l2.id = i2.location_id
|
|
150
|
+
where i2.domain_id = $1 and i2.bizplace_id = $2
|
|
151
|
+
and i2.product_id = $3 and i2.packing_type= $4 and i2.packing_size= $5 and i2.uom = $6 and i2.status='STORED'
|
|
152
|
+
and l2.type='STORAGE' group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
|
|
153
|
+
) storageInv on storageInv.product_id = wboi.product_id and storageInv.domain_id=wboi.domain_id and storageInv.bizplace_id=wboi.bizplace_id
|
|
154
|
+
and storageInv.packing_type=wboi.packing_type and storageInv.packing_size=wboi.packing_size and storageInv.uom=wboi.uom
|
|
135
155
|
where wboi."domain_id"= $1 and
|
|
136
156
|
wboi."bizplace_id" = $2 and
|
|
137
157
|
wboi."group_type" = 'SINGLE' and
|
|
@@ -139,8 +159,8 @@ router.post(
|
|
|
139
159
|
wboi."packing_type" = $4 and
|
|
140
160
|
wboi."packing_size" = $5 and
|
|
141
161
|
wboi."uom" = $6 and
|
|
142
|
-
wboi."remain_qty" >= $7 and
|
|
143
|
-
wboi."remain_uom_value" >= $8
|
|
162
|
+
(wboi."remain_qty" - wboi."transfer_qty" - coalesce(storageInv.storage_qty, 0)) >= $7 and
|
|
163
|
+
(wboi."remain_uom_value" - wboi."transfer_uom_value" - coalesce(storageInv.storage_uom_value, 0)) >= $8
|
|
144
164
|
`,
|
|
145
165
|
[
|
|
146
166
|
domain.id,
|
|
@@ -492,6 +512,12 @@ async function createReleaseGood(
|
|
|
492
512
|
})
|
|
493
513
|
: null
|
|
494
514
|
|
|
515
|
+
// assign lmd
|
|
516
|
+
let lmd: any = null
|
|
517
|
+
if (releaseGood?.lmdOption) {
|
|
518
|
+
lmd = await autoAssignLmd(domain, deliveryCountry?.id, releaseGood?.postalCode)
|
|
519
|
+
}
|
|
520
|
+
|
|
495
521
|
newReleaseGood = {
|
|
496
522
|
name: roNoSetting
|
|
497
523
|
? await generateId({ domain, type: ORDER_NUMBER_RULE_TYPE.RO_NUMBER, seed: {} })
|
|
@@ -545,6 +571,8 @@ async function createReleaseGood(
|
|
|
545
571
|
discountAmount: releaseGood?.discountAmount,
|
|
546
572
|
taxAmount: releaseGood?.taxAmount,
|
|
547
573
|
shippingFee: releaseGood?.shippingFee,
|
|
574
|
+
lmdOption: releaseGood?.lmdOption,
|
|
575
|
+
lastMileDelivery: lmd,
|
|
548
576
|
creator: user,
|
|
549
577
|
updater: user,
|
|
550
578
|
acceptedBy: user,
|
|
@@ -1040,7 +1068,9 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
1040
1068
|
'${orderInventory.releaseQty}' as release_qty, '${orderInventory.releaseUomValue}' as release_uom_value
|
|
1041
1069
|
UNION
|
|
1042
1070
|
(
|
|
1043
|
-
SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY ${
|
|
1071
|
+
SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY wiar.rank ${
|
|
1072
|
+
sortQuery ? ', ' + sortQuery : ''
|
|
1073
|
+
}) as sort_seq,
|
|
1044
1074
|
iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
|
|
1045
1075
|
iv.unit, iv.uom, iv.packing_type, iv.packing_size,
|
|
1046
1076
|
iv.manufacture_year, iv.carton_id, iv.uom_value,
|
|
@@ -1049,11 +1079,12 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
1049
1079
|
FROM "inventories" "iv"
|
|
1050
1080
|
INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
|
|
1051
1081
|
INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
|
|
1082
|
+
INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
|
|
1052
1083
|
WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
|
|
1053
|
-
"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)
|
|
1084
|
+
"iv"."domain_id" = $2 AND "iv"."bizplace_id" = $3 AND "iv"."packing_type" = $4 AND "iv"."packing_size" = $5 AND "iv"."product_id" = $6 AND "iv"."status" = $7 AND "loc"."type" NOT IN ($8, $9, $10)
|
|
1054
1085
|
AND "iv"."obsolete" = false AND case when "iv"."expiration_date" is not null and "p"."shelf_life" is not null then CURRENT_DATE < "iv"."expiration_date" - "p"."shelf_life" else true end
|
|
1055
1086
|
${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
|
|
1056
|
-
ORDER BY ${sortQuery}
|
|
1087
|
+
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
1057
1088
|
)
|
|
1058
1089
|
) dt1
|
|
1059
1090
|
) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
|
|
@@ -1073,6 +1104,7 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
1073
1104
|
INVENTORY_STATUS.STORED,
|
|
1074
1105
|
LOCATION_TYPE.QUARANTINE,
|
|
1075
1106
|
LOCATION_TYPE.RESERVE,
|
|
1107
|
+
LOCATION_TYPE.STORAGE,
|
|
1076
1108
|
...queryStrings.values
|
|
1077
1109
|
]
|
|
1078
1110
|
)
|
|
@@ -1123,3 +1155,33 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
1123
1155
|
|
|
1124
1156
|
return orderProducts
|
|
1125
1157
|
}
|
|
1158
|
+
|
|
1159
|
+
async function autoAssignLmd(domain, countryId, postalCode) {
|
|
1160
|
+
const qb: SelectQueryBuilder<LastMileDelivery> = getRepository(LastMileDelivery)
|
|
1161
|
+
.createQueryBuilder('lmd')
|
|
1162
|
+
.leftJoinAndSelect('lmd.lastMileDeliverySettings', 'lmds')
|
|
1163
|
+
.innerJoinAndSelect('lmds.geoCountry', 'gc')
|
|
1164
|
+
.where('lmd.domain_id = :domainId', { domainId: domain.id })
|
|
1165
|
+
.andWhere('lower(lmd.status) = :status', { status: 'active' })
|
|
1166
|
+
.andWhere('gc.id = :gcId', { gcId: countryId })
|
|
1167
|
+
.andWhere(
|
|
1168
|
+
new Brackets(qb => {
|
|
1169
|
+
qb.where(`lmds.include_postal_code = ''`)
|
|
1170
|
+
.orWhere('lmds.include_postal_code is null')
|
|
1171
|
+
.orWhere('lmds.include_postal_code like :includePostCode', { includePostCode: `%${postalCode}%` })
|
|
1172
|
+
})
|
|
1173
|
+
)
|
|
1174
|
+
.andWhere(
|
|
1175
|
+
new Brackets(qb => {
|
|
1176
|
+
qb.where(`lmds.exclude_postal_code = ''`)
|
|
1177
|
+
.orWhere('lmds.exclude_postal_code is null')
|
|
1178
|
+
.orWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
|
|
1179
|
+
})
|
|
1180
|
+
)
|
|
1181
|
+
// .andWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
|
|
1182
|
+
.orderBy('lmd.rank')
|
|
1183
|
+
|
|
1184
|
+
const lmdResult: LastMileDelivery = await qb.getOne()
|
|
1185
|
+
|
|
1186
|
+
return lmdResult
|
|
1187
|
+
}
|
|
@@ -188,6 +188,7 @@ export const params = {
|
|
|
188
188
|
'deliverTo.state',
|
|
189
189
|
'deliverTo.phone1',
|
|
190
190
|
'deliverTo.phone2',
|
|
191
|
+
'deliverTo.email',
|
|
191
192
|
'marketplaceOrderStatus',
|
|
192
193
|
'airwayBill',
|
|
193
194
|
'invoice',
|
|
@@ -217,7 +218,8 @@ export const params = {
|
|
|
217
218
|
'platformName',
|
|
218
219
|
'discountAmount',
|
|
219
220
|
'taxAmount',
|
|
220
|
-
'shippingFee'
|
|
221
|
+
'shippingFee',
|
|
222
|
+
'lmdOption'
|
|
221
223
|
],
|
|
222
224
|
'cancel-release-order': ['bizplaceId', 'roId', 'orderNo', 'orderNo2', 'orderNo3', 'remark'],
|
|
223
225
|
'update-release-order-details': [
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EntityManager,
|
|
3
|
+
getConnection,
|
|
4
|
+
getRepository,
|
|
5
|
+
In,
|
|
6
|
+
Not,
|
|
7
|
+
Repository,
|
|
8
|
+
SelectQueryBuilder,
|
|
9
|
+
ILike,
|
|
10
|
+
Brackets
|
|
11
|
+
} from 'typeorm'
|
|
2
12
|
import { v4 as uuidv4 } from 'uuid'
|
|
3
13
|
|
|
4
14
|
import { restfulApiRouter as router } from '@things-factory/api'
|
|
5
15
|
import { User } from '@things-factory/auth-base'
|
|
6
16
|
import { Bizplace, getCompanyBizplace } from '@things-factory/biz-base'
|
|
17
|
+
import { GeoCountry } from '@things-factory/geography'
|
|
7
18
|
import { generateId } from '@things-factory/id-rule-base'
|
|
8
19
|
import { Sellercraft } from '@things-factory/integration-sellercraft'
|
|
9
20
|
import { MarketplaceOrder } from '@things-factory/marketplace-base'
|
|
@@ -28,7 +39,7 @@ import {
|
|
|
28
39
|
import { Setting } from '@things-factory/setting-base'
|
|
29
40
|
import { Domain } from '@things-factory/shell'
|
|
30
41
|
import { Inventory, INVENTORY_STATUS, LOCATION_TYPE } from '@things-factory/warehouse-base'
|
|
31
|
-
import {
|
|
42
|
+
import { LastMileDelivery } from '@things-factory/integration-lmd'
|
|
32
43
|
|
|
33
44
|
import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
|
|
34
45
|
import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
|
|
@@ -120,6 +131,7 @@ router.post(
|
|
|
120
131
|
discountAmount: bodyReq?.discountAmount,
|
|
121
132
|
taxAmount: bodyReq?.taxAmount,
|
|
122
133
|
shippingFee: bodyReq?.shippingFee,
|
|
134
|
+
lmdOption: bodyReq?.lmdOption,
|
|
123
135
|
shippingOrder: {
|
|
124
136
|
shipName: bodyReq?.shippingOrder.shipName,
|
|
125
137
|
containerNo: bodyReq?.shippingOrder.containerNo,
|
|
@@ -143,6 +155,14 @@ router.post(
|
|
|
143
155
|
let itemList = await tx.query(
|
|
144
156
|
`
|
|
145
157
|
select wboi.* from warehouse_bizplace_onhand_inventories wboi
|
|
158
|
+
left join (
|
|
159
|
+
select i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom, sum(i2.qty) as storage_qty, sum(i2.uom_value) as storage_uom_value from inventories i2
|
|
160
|
+
inner join locations l2 on l2.id = i2.location_id
|
|
161
|
+
where i2.domain_id = $1 and i2.bizplace_id = $2
|
|
162
|
+
and i2.product_id = $3 and i2.packing_type= $4 and i2.packing_size= $5 and i2.uom = $6 and i2.status='STORED'
|
|
163
|
+
and l2.type='STORAGE' group by i2.product_id, i2.domain_id, i2.bizplace_id, i2.packing_type, i2.packing_size, i2.uom
|
|
164
|
+
) storageInv on storageInv.product_id = wboi.product_id and storageInv.domain_id=wboi.domain_id and storageInv.bizplace_id=wboi.bizplace_id
|
|
165
|
+
and storageInv.packing_type=wboi.packing_type and storageInv.packing_size=wboi.packing_size and storageInv.uom=wboi.uom
|
|
146
166
|
where wboi."domain_id"= $1 and
|
|
147
167
|
wboi."bizplace_id" = $2 and
|
|
148
168
|
wboi."group_type" = 'SINGLE' and
|
|
@@ -150,8 +170,8 @@ router.post(
|
|
|
150
170
|
wboi."packing_type" = $4 and
|
|
151
171
|
wboi."packing_size" = $5 and
|
|
152
172
|
wboi."uom" = $6 and
|
|
153
|
-
wboi."remain_qty" >= $7 and
|
|
154
|
-
wboi."remain_uom_value" >= $8
|
|
173
|
+
(wboi."remain_qty" - wboi."transfer_qty" - coalesce(storageInv.storage_qty, 0)) >= $7 and
|
|
174
|
+
(wboi."remain_uom_value" - wboi."transfer_uom_value" - coalesce(storageInv.storage_uom_value, 0)) >= $8
|
|
155
175
|
`,
|
|
156
176
|
[
|
|
157
177
|
domain.id,
|
|
@@ -414,6 +434,12 @@ async function createReleaseGood(
|
|
|
414
434
|
})
|
|
415
435
|
: null
|
|
416
436
|
|
|
437
|
+
// assign lmd
|
|
438
|
+
let lmd: any = null
|
|
439
|
+
if (releaseGood?.lmdOption) {
|
|
440
|
+
lmd = await autoAssignLmd(domain, deliveryCountry?.id, releaseGood?.postalCode)
|
|
441
|
+
}
|
|
442
|
+
|
|
417
443
|
newReleaseGood = {
|
|
418
444
|
name: roNoSetting
|
|
419
445
|
? await generateId({ domain, type: ORDER_NUMBER_RULE_TYPE.RO_NUMBER, seed: {} })
|
|
@@ -470,7 +496,9 @@ async function createReleaseGood(
|
|
|
470
496
|
updater: user,
|
|
471
497
|
acceptedBy: user,
|
|
472
498
|
acceptedAt: new Date(),
|
|
473
|
-
shippingOrder: shippingOrder ? shippingOrder : null
|
|
499
|
+
shippingOrder: shippingOrder ? shippingOrder : null,
|
|
500
|
+
lastMileDelivery: lmd,
|
|
501
|
+
lmdOption: releaseGood.lmdOption
|
|
474
502
|
}
|
|
475
503
|
|
|
476
504
|
let resultReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
|
|
@@ -910,7 +938,9 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
910
938
|
'${orderInventory.releaseQty}' as release_qty, '${orderInventory.releaseUomValue}' as release_uom_value
|
|
911
939
|
UNION
|
|
912
940
|
(
|
|
913
|
-
SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY ${
|
|
941
|
+
SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY wiar.rank ${
|
|
942
|
+
sortQuery ? ', ' + sortQuery : ''
|
|
943
|
+
}) as sort_seq,
|
|
914
944
|
iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
|
|
915
945
|
iv.unit, iv.uom, iv.packing_type, iv.packing_size,
|
|
916
946
|
iv.manufacture_year, iv.carton_id, iv.uom_value,
|
|
@@ -919,11 +949,12 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
919
949
|
FROM "inventories" "iv"
|
|
920
950
|
INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
|
|
921
951
|
INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
|
|
952
|
+
INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
|
|
922
953
|
WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
|
|
923
|
-
"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)
|
|
954
|
+
"iv"."domain_id" = $2 AND "iv"."bizplace_id" = $3 AND "iv"."packing_type" = $4 AND "iv"."packing_size" = $5 AND "iv"."product_id" = $6 AND "iv"."status" = $7 AND "loc"."type" NOT IN ($8, $9, $10)
|
|
924
955
|
AND "iv"."obsolete" = false AND case when "iv"."expiration_date" is not null and "p"."shelf_life" is not null then CURRENT_DATE < "iv"."expiration_date" - "p"."shelf_life" else true end
|
|
925
956
|
${queryStrings.query.length > 0 ? `AND ${queryStrings.join(' AND ')}` : ''}
|
|
926
|
-
ORDER BY ${sortQuery}
|
|
957
|
+
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
927
958
|
)
|
|
928
959
|
) dt1
|
|
929
960
|
) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
|
|
@@ -943,6 +974,7 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
943
974
|
INVENTORY_STATUS.STORED,
|
|
944
975
|
LOCATION_TYPE.QUARANTINE,
|
|
945
976
|
LOCATION_TYPE.RESERVE,
|
|
977
|
+
LOCATION_TYPE.STORAGE,
|
|
946
978
|
...queryStrings.values
|
|
947
979
|
]
|
|
948
980
|
)
|
|
@@ -981,3 +1013,33 @@ async function assignToInventory(orderProducts: OrderProduct[], customerBizplace
|
|
|
981
1013
|
|
|
982
1014
|
return orderProducts
|
|
983
1015
|
}
|
|
1016
|
+
|
|
1017
|
+
async function autoAssignLmd(domain, countryId, postalCode) {
|
|
1018
|
+
const qb: SelectQueryBuilder<LastMileDelivery> = getRepository(LastMileDelivery)
|
|
1019
|
+
.createQueryBuilder('lmd')
|
|
1020
|
+
.leftJoinAndSelect('lmd.lastMileDeliverySettings', 'lmds')
|
|
1021
|
+
.innerJoinAndSelect('lmds.geoCountry', 'gc')
|
|
1022
|
+
.where('lmd.domain_id = :domainId', { domainId: domain.id })
|
|
1023
|
+
.andWhere('lower(lmd.status) = :status', { status: 'active' })
|
|
1024
|
+
.andWhere('gc.id = :gcId', { gcId: countryId })
|
|
1025
|
+
.andWhere(
|
|
1026
|
+
new Brackets(qb => {
|
|
1027
|
+
qb.where(`lmds.include_postal_code = ''`)
|
|
1028
|
+
.orWhere('lmds.include_postal_code is null')
|
|
1029
|
+
.orWhere('lmds.include_postal_code like :includePostCode', { includePostCode: `%${postalCode}%` })
|
|
1030
|
+
})
|
|
1031
|
+
)
|
|
1032
|
+
.andWhere(
|
|
1033
|
+
new Brackets(qb => {
|
|
1034
|
+
qb.where(`lmds.exclude_postal_code = ''`)
|
|
1035
|
+
.orWhere('lmds.exclude_postal_code is null')
|
|
1036
|
+
.orWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
|
|
1037
|
+
})
|
|
1038
|
+
)
|
|
1039
|
+
// .andWhere('lmds.exclude_postal_code not like :excludePostCode', { excludePostCode: `%${postalCode}%` })
|
|
1040
|
+
.orderBy('lmd.rank')
|
|
1041
|
+
|
|
1042
|
+
const lmdResult: LastMileDelivery = await qb.getOne()
|
|
1043
|
+
|
|
1044
|
+
return lmdResult
|
|
1045
|
+
}
|
|
@@ -447,6 +447,7 @@ xilnexRouter.post('/webhook-xilnex-transfer-note', async (context, next) => {
|
|
|
447
447
|
palletQty: 1,
|
|
448
448
|
totalUomValue: `${(productDetail.uomValue * item.confirmedQty).toFixed(2)} ${productDetail.uom}`,
|
|
449
449
|
status: 'PENDING',
|
|
450
|
+
refItemId: item.orderItemId,
|
|
450
451
|
arrivalNotice,
|
|
451
452
|
domain: warehouseDomain,
|
|
452
453
|
bizplace: customerBizplace
|