@things-factory/sales-base 3.8.7 → 3.8.11
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/dist-server/controllers/ecommerce/sellercraft-controller.js +59 -0
- package/dist-server/controllers/ecommerce/sellercraft-controller.js.map +1 -1
- package/dist-server/controllers/order-controller.js +41 -2
- package/dist-server/controllers/order-controller.js.map +1 -1
- package/dist-server/entities/release-good.js +4 -0
- package/dist-server/entities/release-good.js.map +1 -1
- package/dist-server/graphql/resolvers/release-good/bulk-add-release-goods.js +79 -17
- package/dist-server/graphql/resolvers/release-good/bulk-add-release-goods.js.map +1 -1
- package/dist-server/graphql/resolvers/release-good/bulk-release-goods-available-items.js +27 -22
- package/dist-server/graphql/resolvers/release-good/bulk-release-goods-available-items.js.map +1 -1
- package/dist-server/graphql/resolvers/release-good/confirm-release-good.js +54 -5
- package/dist-server/graphql/resolvers/release-good/confirm-release-good.js.map +1 -1
- package/dist-server/graphql/resolvers/release-good/generate-release-good.js +1 -1
- package/dist-server/graphql/resolvers/release-good/generate-release-good.js.map +1 -1
- package/dist-server/graphql/resolvers/release-good/reject-release-good.js +28 -1
- package/dist-server/graphql/resolvers/release-good/reject-release-good.js.map +1 -1
- package/dist-server/graphql/types/release-good/new-release-good.js +1 -0
- package/dist-server/graphql/types/release-good/new-release-good.js.map +1 -1
- package/dist-server/graphql/types/release-good/raw-release-good.js +1 -0
- package/dist-server/graphql/types/release-good/raw-release-good.js.map +1 -1
- package/dist-server/utils/inventory-util.js +89 -1
- package/dist-server/utils/inventory-util.js.map +1 -1
- package/package.json +4 -4
- package/server/controllers/ecommerce/sellercraft-controller.ts +77 -1
- package/server/controllers/order-controller.ts +57 -4
- package/server/entities/release-good.ts +3 -0
- package/server/graphql/resolvers/release-good/bulk-add-release-goods.ts +114 -46
- package/server/graphql/resolvers/release-good/bulk-release-goods-available-items.ts +32 -24
- package/server/graphql/resolvers/release-good/confirm-release-good.ts +67 -7
- package/server/graphql/resolvers/release-good/generate-release-good.ts +1 -0
- package/server/graphql/resolvers/release-good/reject-release-good.ts +40 -4
- package/server/graphql/types/release-good/new-release-good.ts +1 -0
- package/server/graphql/types/release-good/raw-release-good.ts +1 -0
- package/server/utils/index.ts +1 -1
- package/server/utils/inventory-util.ts +129 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
|
|
2
|
+
|
|
1
3
|
import { Role, User } from '@things-factory/auth-base'
|
|
2
4
|
import { Bizplace, getDomainUsers } from '@things-factory/biz-base'
|
|
3
5
|
import { sendNotification } from '@things-factory/notification'
|
|
6
|
+
import { ProductDetail } from '@things-factory/product-base'
|
|
7
|
+
import { Domain } from '@things-factory/shell'
|
|
8
|
+
import { Inventory } from '@things-factory/warehouse-base'
|
|
9
|
+
|
|
4
10
|
import {
|
|
5
11
|
ArrivalNotice,
|
|
6
12
|
DeliveryOrder,
|
|
@@ -12,9 +18,6 @@ import {
|
|
|
12
18
|
ReturnOrder,
|
|
13
19
|
VasOrder
|
|
14
20
|
} from '../entities'
|
|
15
|
-
import { Domain } from '@things-factory/shell'
|
|
16
|
-
import { Inventory } from '@things-factory/warehouse-base'
|
|
17
|
-
import { EntityManager, EntitySchema, FindOneOptions } from 'typeorm'
|
|
18
21
|
|
|
19
22
|
export type ReferenceOrderType = ArrivalNotice | ReleaseGood | VasOrder | InventoryCheck | DeliveryOrder | ReturnOrder
|
|
20
23
|
export type OrderTargetTypes = OrderProduct | OrderInventory | OrderVas
|
|
@@ -48,7 +51,9 @@ export type NotificationMsgInterface = {
|
|
|
48
51
|
export class OrderController {
|
|
49
52
|
public readonly ERROR_MSG = {
|
|
50
53
|
FIND: {
|
|
51
|
-
NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}
|
|
54
|
+
NO_RESULT: (condition: any) => `There's no results matched with condition ${condition}`,
|
|
55
|
+
NO_CHILD_RESULT: (condition: any) => `There's no child result matched with condition ${condition}`,
|
|
56
|
+
NOT_MATCH: (source: any, target: any) => `Unable to find matching ${target} using ${source}`
|
|
52
57
|
},
|
|
53
58
|
CREATE: {
|
|
54
59
|
ID_EXISTS: 'Target has ID already',
|
|
@@ -217,6 +222,54 @@ export class OrderController {
|
|
|
217
222
|
return await this.trxMgr.getRepository(Inventory).save(inventory)
|
|
218
223
|
}
|
|
219
224
|
|
|
225
|
+
async getChildPackingSize(
|
|
226
|
+
productDetails: ProductDetail[],
|
|
227
|
+
defaultProductDetail: ProductDetail,
|
|
228
|
+
unmatchingProductDetail: ProductDetail
|
|
229
|
+
): Promise<number> {
|
|
230
|
+
let hasChildRelation: boolean = Boolean(unmatchingProductDetail?.childProductDetail)
|
|
231
|
+
let hasMatchingChild: boolean
|
|
232
|
+
let packingSize: number = 1
|
|
233
|
+
let currentChildProductDetail: ProductDetail
|
|
234
|
+
|
|
235
|
+
if (hasChildRelation) {
|
|
236
|
+
currentChildProductDetail = productDetails.find(
|
|
237
|
+
(productDetail: ProductDetail) => productDetail.id === unmatchingProductDetail.childProductDetail.id
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if (!currentChildProductDetail) {
|
|
241
|
+
throw new Error(this.ERROR_MSG.FIND.NOT_MATCH('packing type', `GTIN (${unmatchingProductDetail.gtin})`))
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
|
|
245
|
+
if (hasMatchingChild) {
|
|
246
|
+
packingSize = unmatchingProductDetail.packingSize
|
|
247
|
+
} else {
|
|
248
|
+
packingSize = unmatchingProductDetail.packingSize * packingSize
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
throw new Error(this.ERROR_MSG.FIND.NO_CHILD_RESULT(`${unmatchingProductDetail.gtin}`))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
while (hasChildRelation && !hasMatchingChild) {
|
|
255
|
+
if (hasMatchingChild) {
|
|
256
|
+
packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
|
|
257
|
+
} else if (!hasMatchingChild && hasChildRelation) {
|
|
258
|
+
packingSize = currentChildProductDetail.packingSize * packingSize // 12 x 10
|
|
259
|
+
currentChildProductDetail = productDetails.find(
|
|
260
|
+
(productDetail: ProductDetail) => productDetail.id === currentChildProductDetail.childProductDetail.id
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
hasChildRelation = Boolean(currentChildProductDetail?.childProductDetail)
|
|
264
|
+
hasMatchingChild = Boolean(defaultProductDetail.id === currentChildProductDetail.id)
|
|
265
|
+
} else if (!hasChildRelation && !hasMatchingChild) {
|
|
266
|
+
throw new Error(this.ERROR_MSG.FIND.NO_RESULT(unmatchingProductDetail.gtin))
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return packingSize
|
|
271
|
+
}
|
|
272
|
+
|
|
220
273
|
/**
|
|
221
274
|
* @summary set common stamp like domain, creator, updater
|
|
222
275
|
* @description Set common stamp to passed record
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityManager, getRepository, In, Repository } from 'typeorm'
|
|
1
|
+
import { EntityManager, getConnection, getRepository, In, Repository } from 'typeorm'
|
|
2
2
|
|
|
3
3
|
import { Role, User } from '@things-factory/auth-base'
|
|
4
4
|
import { Bizplace } from '@things-factory/biz-base'
|
|
@@ -7,6 +7,7 @@ import { sendNotification } from '@things-factory/notification'
|
|
|
7
7
|
import { Product } from '@things-factory/product-base'
|
|
8
8
|
import { Setting } from '@things-factory/setting-base'
|
|
9
9
|
import { Domain } from '@things-factory/shell'
|
|
10
|
+
import { Inventory } from '@things-factory/warehouse-base'
|
|
10
11
|
|
|
11
12
|
import { ORDER_STATUS } from '../../../constants'
|
|
12
13
|
import {
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
} from '../../../constants/order'
|
|
18
19
|
import { OrderInventory, OrderProduct, ReleaseGood } from '../../../entities'
|
|
19
20
|
import { ValidationError } from '../../../errors'
|
|
21
|
+
import { InventoryUtil } from '../../../utils'
|
|
20
22
|
import { OrderNoGenerator } from '../../../utils/order-no-generator'
|
|
21
23
|
import { bulkReleaseGoodsAvailableItemsFunction } from './bulk-release-goods-available-items'
|
|
22
24
|
|
|
@@ -38,46 +40,70 @@ export const bulkAddReleaseGoods = {
|
|
|
38
40
|
|
|
39
41
|
let releaseGoods: Partial<ReleaseGood[]> = extractRawReleaseGoods(rawReleaseGoods)
|
|
40
42
|
|
|
43
|
+
let errorsFound: any[] = []
|
|
41
44
|
for (let i = 0, l = releaseGoods.length; i < l; i++) {
|
|
42
45
|
// generate release good by group to avoid duplication
|
|
43
46
|
// if this function is called simultaneously by different users
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
try {
|
|
48
|
+
await getConnection().transaction(async (childTx: EntityManager) => {
|
|
49
|
+
const existingReleaseGood: ReleaseGood = await childTx.getRepository(ReleaseGood).findOne({
|
|
50
|
+
where: {
|
|
51
|
+
...Object.fromEntries(
|
|
52
|
+
Object.entries(releaseGoods[i]).filter(([_, val]) => !/^\s*$/.test(val) && _ != 'orderInventories')
|
|
53
|
+
),
|
|
54
|
+
domain
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
if (existingReleaseGood) throw new Error('release good order is already exist in the system')
|
|
58
|
+
|
|
59
|
+
let availableItems: any[] = await bulkReleaseGoodsAvailableItemsFunction(
|
|
60
|
+
_,
|
|
61
|
+
[...releaseGoods[i].orderInventories],
|
|
62
|
+
bizplaceId,
|
|
63
|
+
context,
|
|
64
|
+
childTx
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (availableItems.some(item => !item.releaseQty || item.releaseQty > item.assignedQty))
|
|
68
|
+
throw new ValidationError({
|
|
69
|
+
...ValidationError.ERROR_CODES.INSUFFICIENT_STOCK,
|
|
70
|
+
detail: { data: JSON.stringify(availableItems) }
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// update orderInventories if availableItems are valid
|
|
74
|
+
releaseGoods[i].orderInventories = availableItems
|
|
75
|
+
|
|
76
|
+
const createdReleaseGood: ReleaseGood = await bulkGenerateReleaseGoods(
|
|
77
|
+
_,
|
|
78
|
+
releaseGoods[i],
|
|
79
|
+
bizplaceId,
|
|
80
|
+
roNoSetting,
|
|
81
|
+
domain,
|
|
82
|
+
user,
|
|
83
|
+
childTx
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
createdReleaseGoods.push(createdReleaseGood)
|
|
56
87
|
})
|
|
88
|
+
} catch (e) {
|
|
89
|
+
errorsFound.push(e)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
_,
|
|
63
|
-
releaseGoods[i],
|
|
64
|
-
bizplaceId,
|
|
65
|
-
roNoSetting,
|
|
93
|
+
let confirmedReleaseGoods: ReleaseGood[] = []
|
|
94
|
+
try {
|
|
95
|
+
confirmedReleaseGoods = await bulkConfirmReleaseGoods(
|
|
96
|
+
createdReleaseGoods.map(rg => rg.name),
|
|
66
97
|
domain,
|
|
67
98
|
user,
|
|
99
|
+
context,
|
|
68
100
|
tx
|
|
69
101
|
)
|
|
70
|
-
|
|
71
|
-
|
|
102
|
+
} catch (e) {
|
|
103
|
+
errorsFound.push(e)
|
|
72
104
|
}
|
|
73
105
|
|
|
74
|
-
|
|
75
|
-
createdReleaseGoods.map(rg => rg.name),
|
|
76
|
-
domain,
|
|
77
|
-
user,
|
|
78
|
-
context,
|
|
79
|
-
tx
|
|
80
|
-
)
|
|
106
|
+
// if (errorsFound.length) { then ?? }
|
|
81
107
|
|
|
82
108
|
return confirmedReleaseGoods
|
|
83
109
|
}
|
|
@@ -109,7 +135,6 @@ async function bulkGenerateReleaseGoods(
|
|
|
109
135
|
deliveryAddress2: releaseGood?.deliveryAddress2 || null,
|
|
110
136
|
deliveryAddress3: releaseGood?.deliveryAddress3 || null,
|
|
111
137
|
deliveryAddress4: releaseGood?.deliveryAddress4 || null,
|
|
112
|
-
deliveryAddress5: releaseGood?.deliveryAddress5 || null,
|
|
113
138
|
attentionTo: releaseGood?.attentionTo || null,
|
|
114
139
|
attentionCompany: releaseGood?.attentionCompany || null,
|
|
115
140
|
city: releaseGood?.city || null,
|
|
@@ -131,40 +156,82 @@ async function bulkGenerateReleaseGoods(
|
|
|
131
156
|
}
|
|
132
157
|
|
|
133
158
|
newReleaseGood = await tx.getRepository(ReleaseGood).save(newReleaseGood)
|
|
159
|
+
let finalOrderInventories: OrderInventory[] = []
|
|
134
160
|
|
|
161
|
+
// pre assign inventory to orderInventories
|
|
135
162
|
for (let oi of orderInventories) {
|
|
136
|
-
|
|
163
|
+
const pickingProductSetting: Setting = await tx.getRepository(Setting).findOne({
|
|
164
|
+
where: { domain, name: 'rule-for-picking-product' }
|
|
165
|
+
})
|
|
137
166
|
|
|
138
|
-
let
|
|
139
|
-
|
|
140
|
-
|
|
167
|
+
let locationSortingRules = []
|
|
168
|
+
if (pickingProductSetting) {
|
|
169
|
+
let locationSetting = JSON.parse(pickingProductSetting.value)
|
|
170
|
+
for (const key in locationSetting) {
|
|
171
|
+
locationSortingRules.push({ name: key, desc: locationSetting[key] == 'ASC' ? false : true })
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const product: Product = await tx.getRepository(Product).findOne(oi.productId)
|
|
176
|
+
|
|
177
|
+
finalOrderInventories.push(
|
|
178
|
+
...(await InventoryUtil.autoAssignInventoryForRelease(
|
|
179
|
+
product,
|
|
180
|
+
oi,
|
|
181
|
+
oi.packingType,
|
|
182
|
+
locationSortingRules,
|
|
183
|
+
bizplace,
|
|
184
|
+
warehouseDomain,
|
|
185
|
+
tx
|
|
186
|
+
))
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (let oi of finalOrderInventories) {
|
|
191
|
+
let generatedOI: OrderInventory = Object.assign(new OrderInventory(), oi)
|
|
192
|
+
generatedOI = {
|
|
193
|
+
...generatedOI,
|
|
141
194
|
domain: warehouseDomain,
|
|
142
|
-
bizplace
|
|
195
|
+
bizplace,
|
|
143
196
|
status: ORDER_INVENTORY_STATUS.PENDING,
|
|
144
197
|
name: OrderNoGenerator.orderInventory(),
|
|
145
|
-
batchId: oi?.batchId || '',
|
|
146
198
|
releaseGood: newReleaseGood,
|
|
147
|
-
product: await tx.getRepository(Product).findOne(oi.productId),
|
|
148
199
|
creator: user,
|
|
149
200
|
updater: user
|
|
150
201
|
}
|
|
151
202
|
|
|
152
|
-
let newOrderProduct: OrderProduct =
|
|
153
|
-
|
|
154
|
-
|
|
203
|
+
let newOrderProduct: Partial<OrderProduct> = {
|
|
204
|
+
name: generatedOI.name,
|
|
205
|
+
product: generatedOI.product,
|
|
206
|
+
batchId: generatedOI.batchId,
|
|
207
|
+
packingType: generatedOI.packingType,
|
|
208
|
+
packingSize: generatedOI.packingSize,
|
|
209
|
+
uom: generatedOI.uom,
|
|
210
|
+
domain: warehouseDomain,
|
|
211
|
+
bizplace,
|
|
212
|
+
releaseQty: generatedOI.releaseQty,
|
|
213
|
+
releaseUomValue: generatedOI.releaseUomValue,
|
|
155
214
|
packQty: 0,
|
|
156
215
|
actualPackQty: 0,
|
|
157
216
|
palletQty: 0,
|
|
158
217
|
actualPalletQty: 0,
|
|
159
|
-
status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN
|
|
218
|
+
status: ORDER_PRODUCT_STATUS.PENDING_ASSIGN,
|
|
219
|
+
releaseGood: newReleaseGood,
|
|
220
|
+
creator: user,
|
|
221
|
+
updater: user
|
|
160
222
|
}
|
|
161
223
|
|
|
162
224
|
newOrderProduct = await tx.getRepository(OrderProduct).save(newOrderProduct)
|
|
163
225
|
|
|
164
|
-
await tx.getRepository(OrderInventory).save({ ...
|
|
165
|
-
}
|
|
226
|
+
await tx.getRepository(OrderInventory).save({ ...generatedOI, orderProduct: newOrderProduct })
|
|
166
227
|
|
|
167
|
-
|
|
228
|
+
// update inventory locked qty and uom value
|
|
229
|
+
await tx.getRepository(Inventory).update(oi.inventory.id, {
|
|
230
|
+
lockedQty: (oi.inventory?.lockedQty || 0) + generatedOI.releaseQty,
|
|
231
|
+
lockedUomValue: (oi.inventory?.lockedUomValue || 0) + generatedOI.releaseUomValue,
|
|
232
|
+
updater: user
|
|
233
|
+
})
|
|
234
|
+
}
|
|
168
235
|
|
|
169
236
|
return newReleaseGood
|
|
170
237
|
} catch (error) {
|
|
@@ -195,7 +262,7 @@ async function bulkConfirmReleaseGoods(
|
|
|
195
262
|
]
|
|
196
263
|
})
|
|
197
264
|
|
|
198
|
-
if (!foundReleaseGoods.length) throw new Error(`
|
|
265
|
+
if (!foundReleaseGoods.length) throw new Error(`release good order doesn't exists.`)
|
|
199
266
|
let customerBizplace: Bizplace = foundReleaseGoods[0].bizplace
|
|
200
267
|
|
|
201
268
|
let foundOrderInventories: OrderInventory[] = foundReleaseGoods
|
|
@@ -295,6 +362,7 @@ function extractRawReleaseGoods(rawReleaseGoods): Partial<ReleaseGood[]> {
|
|
|
295
362
|
oi => oi.sku === item.sku && oi.packingType === item.packingType && oi.packingSize === item.packingSize
|
|
296
363
|
)
|
|
297
364
|
|
|
365
|
+
// if there is duplicated SKU, merge them and sum up the releaseQty
|
|
298
366
|
if (duplicateSkuIdx >= 0) {
|
|
299
367
|
releaseGoods[idx].orderInventories[duplicateSkuIdx].releaseQty += item.releaseQty
|
|
300
368
|
} else {
|
|
@@ -2,6 +2,7 @@ import { EntityManager } from 'typeorm'
|
|
|
2
2
|
|
|
3
3
|
import { Bizplace, getCompanyBizplace } from '@things-factory/biz-base'
|
|
4
4
|
import { Domain } from '@things-factory/shell'
|
|
5
|
+
import { LOCATION_TYPE } from '@things-factory/warehouse-base'
|
|
5
6
|
|
|
6
7
|
export const bulkReleaseGoodsAvailableItems = {
|
|
7
8
|
async bulkReleaseGoodsAvailableItems(_: any, { rawReleaseGoods, bizplaceId }, context: any) {
|
|
@@ -47,6 +48,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
47
48
|
CREATE TEMP TABLE temp_order_products(
|
|
48
49
|
product_id VARCHAR(50),
|
|
49
50
|
sku VARCHAR(150),
|
|
51
|
+
product_info VARCHAR(250),
|
|
50
52
|
packing_type VARCHAR(50),
|
|
51
53
|
packing_size INT,
|
|
52
54
|
uom VARCHAR(10),
|
|
@@ -59,6 +61,8 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
59
61
|
`
|
|
60
62
|
INSERT INTO temp_order_products
|
|
61
63
|
SELECT p.id AS product_id, js.sku,
|
|
64
|
+
CASE WHEN p.description NOT IN (NULL, '', '-') THEN CONCAT(p.name, '(', p.description, ')')
|
|
65
|
+
ELSE p.name END AS product_info,
|
|
62
66
|
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_type ELSE pd.packing_type END,
|
|
63
67
|
CASE WHEN js.packing_type NOTNULL AND js.packing_size NOTNULL THEN js.packing_size ELSE pd.packing_size END,
|
|
64
68
|
CASE WHEN js.uom NOTNULL THEN js.uom ELSE pd.uom END,
|
|
@@ -72,16 +76,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
72
76
|
|
|
73
77
|
let availableItems = await tx.query(
|
|
74
78
|
`
|
|
75
|
-
|
|
79
|
+
WITH inv AS (
|
|
80
|
+
SELECT i.product_id, foo.sku, foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom,
|
|
76
81
|
SUM(i.qty - COALESCE(i.locked_qty, 0)) - COALESCE(
|
|
77
82
|
(
|
|
78
83
|
SELECT SUM(oi.release_qty) FROM order_inventories oi
|
|
79
|
-
WHERE (
|
|
80
|
-
oi.status = 'PENDING'
|
|
81
|
-
OR oi.status = 'PENDING_RECEIVE'
|
|
82
|
-
OR oi.status = 'PENDING_WORKSHEET'
|
|
83
|
-
OR oi.status = 'PENDING_SPLIT'
|
|
84
|
-
)
|
|
84
|
+
WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
|
|
85
85
|
AND oi.inventory_id IS null
|
|
86
86
|
AND oi.product_id = i.product_id::uuid
|
|
87
87
|
AND oi.packing_type = i.packing_type
|
|
@@ -92,12 +92,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
92
92
|
SUM(i.uom_value - COALESCE(i.locked_uom_value, 0)) - COALESCE(
|
|
93
93
|
(
|
|
94
94
|
SELECT SUM(oi.release_uom_value) FROM order_inventories oi
|
|
95
|
-
WHERE (
|
|
96
|
-
oi.status = 'PENDING'
|
|
97
|
-
OR oi.status = 'PENDING_RECEIVE'
|
|
98
|
-
OR oi.status = 'PENDING_WORKSHEET'
|
|
99
|
-
OR oi.status = 'PENDING_SPLIT'
|
|
100
|
-
)
|
|
95
|
+
WHERE oi.status IN ('PENDING', 'PENDING_RECEIVE', 'PENDING_WORKSHEET', 'PENDING_SPLIT')
|
|
101
96
|
AND oi.inventory_id IS null
|
|
102
97
|
AND oi.product_id = i.product_id::uuid
|
|
103
98
|
AND oi.packing_type = i.packing_type
|
|
@@ -106,10 +101,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
106
101
|
AND oi.bizplace_id = $2
|
|
107
102
|
), 0) as "remain_uom_value"
|
|
108
103
|
FROM inventories i
|
|
104
|
+
LEFT JOIN locations l
|
|
105
|
+
ON i.location_id = l.id
|
|
109
106
|
INNER JOIN (
|
|
110
|
-
SELECT top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
|
|
107
|
+
SELECT top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
|
|
111
108
|
FROM temp_order_products top
|
|
112
|
-
GROUP BY top.product_id, top.sku, top.packing_type, top.packing_size, top.uom
|
|
109
|
+
GROUP BY top.product_id, top.sku, top.product_info, top.packing_type, top.packing_size, top.uom
|
|
113
110
|
) AS foo
|
|
114
111
|
ON i.product_id = foo.product_id::uuid
|
|
115
112
|
AND i.packing_type = foo.packing_type
|
|
@@ -117,10 +114,12 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
117
114
|
AND i.uom = foo.uom
|
|
118
115
|
AND i.domain_id = $1
|
|
119
116
|
AND i.bizplace_id = $2
|
|
120
|
-
|
|
117
|
+
WHERE l.type NOT IN ($3, $4)
|
|
118
|
+
GROUP BY i.product_id, foo.sku,foo.product_info, i.batch_id, i.packing_type, i.packing_size, i.uom
|
|
121
119
|
ORDER BY foo.sku, remain_qty
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
) SELECT * FROM inv WHERE remain_qty > 0
|
|
121
|
+
`,
|
|
122
|
+
[domain.id, bizplaceId, LOCATION_TYPE.QUARANTINE, LOCATION_TYPE.RESERVE]
|
|
124
123
|
)
|
|
125
124
|
|
|
126
125
|
await tx.query('DROP TABLE temp_order_products')
|
|
@@ -128,6 +127,7 @@ export async function bulkReleaseGoodsAvailableItemsFunction(
|
|
|
128
127
|
availableItems = availableItems.map(item => {
|
|
129
128
|
return {
|
|
130
129
|
productId: item.product_id,
|
|
130
|
+
productInfo: item.product_info,
|
|
131
131
|
sku: item.sku,
|
|
132
132
|
batchId: item.batch_id,
|
|
133
133
|
packingType: item.packing_type,
|
|
@@ -162,33 +162,41 @@ function _extractData(rawData, validatedData) {
|
|
|
162
162
|
return val.sku == raw.sku && a === b
|
|
163
163
|
})
|
|
164
164
|
|
|
165
|
-
let releaseUomValue = 0
|
|
165
|
+
let releaseUomValue: number = 0
|
|
166
166
|
|
|
167
167
|
// if sku is matched, assign qty and product id
|
|
168
168
|
if (idx >= 0) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
(Math.round((validatedData[idx]?.remainUomValue / validatedData[idx]?.remainQty) * 100) / 100) * raw.releaseQty
|
|
169
|
+
const uomValuePerQty: number =
|
|
170
|
+
Math.round((validatedData[idx].remainUomValue / validatedData[idx].remainQty) * 100) / 100
|
|
172
171
|
|
|
172
|
+
// use Math.round again because 4.2 * 6 = 25.200000000000003
|
|
173
|
+
releaseUomValue = Math.round(uomValuePerQty * raw.releaseQty * 100) / 100
|
|
174
|
+
|
|
175
|
+
// assign qty to rawData as much as possible
|
|
173
176
|
raw.assignedQty =
|
|
174
177
|
validatedData[idx].remainQty >= raw.releaseQty
|
|
175
178
|
? raw.releaseQty > 0
|
|
176
179
|
? raw.releaseQty
|
|
177
180
|
: 0
|
|
178
|
-
: validatedData[idx].remainQty
|
|
181
|
+
: validatedData[idx].remainQty > 0
|
|
182
|
+
? validatedData[idx].remainQty
|
|
183
|
+
: 0
|
|
179
184
|
|
|
180
185
|
raw.assignedUomValue =
|
|
181
186
|
validatedData[idx].remainUomValue >= releaseUomValue
|
|
182
187
|
? releaseUomValue > 0
|
|
183
188
|
? releaseUomValue
|
|
184
189
|
: 0
|
|
185
|
-
: validatedData[idx].remainUomValue
|
|
190
|
+
: validatedData[idx].remainUomValue > 0
|
|
191
|
+
? validatedData[idx].remainUomValue
|
|
192
|
+
: 0
|
|
186
193
|
|
|
187
194
|
// deduct qty & uomValue from validateData
|
|
188
195
|
validatedData[idx].remainQty -= raw.assignedQty
|
|
189
196
|
validatedData[idx].remainUomValue -= raw.assignedUomValue
|
|
190
197
|
|
|
191
198
|
raw.productId = validatedData[idx].productId
|
|
199
|
+
raw.productInfo = validatedData[idx].productInfo
|
|
192
200
|
raw.packingType = validatedData[idx].packingType
|
|
193
201
|
raw.packingSize = validatedData[idx].packingSize
|
|
194
202
|
raw.uom = validatedData[idx].uom
|
|
@@ -4,12 +4,13 @@ import { Role, User } from '@things-factory/auth-base'
|
|
|
4
4
|
import { Bizplace } from '@things-factory/biz-base'
|
|
5
5
|
import { MarketplaceStore } from '@things-factory/integration-marketplace'
|
|
6
6
|
import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
|
|
7
|
+
import { MarketplaceOrder, MarketplaceProductVariation } from '@things-factory/marketplace-base'
|
|
7
8
|
import { sendNotification } from '@things-factory/notification'
|
|
8
9
|
import { Domain } from '@things-factory/shell'
|
|
9
10
|
|
|
10
11
|
import { ORDER_INVENTORY_STATUS, ORDER_STATUS, ORDER_VAS_STATUS } from '../../../constants'
|
|
11
12
|
import { EcommerceController, SellercraftController } from '../../../controllers'
|
|
12
|
-
import { OrderInventory, OrderVas, ReleaseGood } from '../../../entities'
|
|
13
|
+
import { OrderInventory, OrderProduct, OrderVas, ReleaseGood } from '../../../entities'
|
|
13
14
|
import { confirmArrivalNoticeFunction } from '../arrival-notice/confirm-arrival-notice'
|
|
14
15
|
|
|
15
16
|
export const confirmReleaseGoodResolver = {
|
|
@@ -46,6 +47,7 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
|
|
|
46
47
|
'bizplace.company.domain',
|
|
47
48
|
'orderProducts',
|
|
48
49
|
'orderProducts.product',
|
|
50
|
+
'orderProducts.product.productDetails',
|
|
49
51
|
'orderInventories',
|
|
50
52
|
'orderInventories.product',
|
|
51
53
|
'orderVass'
|
|
@@ -54,26 +56,77 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
|
|
|
54
56
|
|
|
55
57
|
if (!foundReleaseGood) throw new Error(`Release good order doesn't exists.`)
|
|
56
58
|
let foundOIs: OrderInventory[] = foundReleaseGood.orderInventories
|
|
59
|
+
let foundBundleInfo: any[] = JSON.parse(foundReleaseGood.bundleInfo)
|
|
57
60
|
let foundOVs: OrderVas[] = foundReleaseGood.orderVass
|
|
61
|
+
let foundOPs: OrderProduct[] = foundReleaseGood.orderProducts
|
|
58
62
|
let customerBizplace: Bizplace = foundReleaseGood.bizplace
|
|
59
63
|
const companyDomain: Domain = customerBizplace?.company.domain
|
|
64
|
+
const sellercraft: Sellercraft = await tx
|
|
65
|
+
.getRepository(Sellercraft)
|
|
66
|
+
.findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
|
|
60
67
|
|
|
61
68
|
if (foundReleaseGood.type == 'b2c') {
|
|
62
|
-
const sellercraft: Sellercraft = await tx
|
|
63
|
-
.getRepository(Sellercraft)
|
|
64
|
-
.findOne({ domain: foundReleaseGood.bizplace.domain, status: SellercraftStatus.ACTIVE })
|
|
65
|
-
|
|
66
69
|
if (sellercraft) {
|
|
67
70
|
const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
|
|
68
71
|
foundReleaseGood = await sellercraftCtrl.packOrder(sellercraft, foundReleaseGood)
|
|
69
72
|
} else {
|
|
70
73
|
// find for any existing marketplace store connections
|
|
71
|
-
|
|
72
|
-
where: { domain: companyDomain, status: 'ACTIVE'
|
|
74
|
+
let marketplaceStores: MarketplaceStore[] = await tx.getRepository(MarketplaceStore).find({
|
|
75
|
+
where: { domain: companyDomain, status: 'ACTIVE' },
|
|
73
76
|
relations: ['marketplaceDistributors']
|
|
74
77
|
})
|
|
75
78
|
|
|
79
|
+
const foundMarketplaceOrder: MarketplaceOrder = await tx.getRepository(MarketplaceOrder).findOne({
|
|
80
|
+
where: { domain: companyDomain, orderNo: foundReleaseGood.refNo },
|
|
81
|
+
relations: ['marketplaceStore']
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
let matchedMarketplaceStore: MarketplaceStore
|
|
85
|
+
if (foundMarketplaceOrder) {
|
|
86
|
+
matchedMarketplaceStore = marketplaceStores.filter(
|
|
87
|
+
marketplaceStore => marketplaceStore.id != foundMarketplaceOrder.marketplaceStore.id
|
|
88
|
+
)[0]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let combinedOrderInventories: any[] = foundOIs.map(oi => {
|
|
92
|
+
return {
|
|
93
|
+
sku: oi.product.sku,
|
|
94
|
+
releaseQty: oi.releaseQty
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
if (foundBundleInfo?.length) {
|
|
99
|
+
foundBundleInfo.map(bundle => {
|
|
100
|
+
combinedOrderInventories.push({
|
|
101
|
+
sku: bundle.sku,
|
|
102
|
+
releaseQty: bundle.releaseQty
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (let oi of combinedOrderInventories) {
|
|
108
|
+
let foundMarketplaceProductVariations: MarketplaceProductVariation[] = await tx
|
|
109
|
+
.getRepository(MarketplaceProductVariation)
|
|
110
|
+
.find({
|
|
111
|
+
where: { domain: companyDomain, sku: oi.sku },
|
|
112
|
+
relations: ['marketplaceProduct', 'marketplaceProduct.marketplaceStore']
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
if (foundMarketplaceProductVariations) {
|
|
116
|
+
await Promise.all(
|
|
117
|
+
foundMarketplaceProductVariations.map(async variation => {
|
|
118
|
+
if (variation.marketplaceProduct.marketplaceStore.id != matchedMarketplaceStore.id) {
|
|
119
|
+
variation.reserveQty -= oi.releaseQty
|
|
120
|
+
|
|
121
|
+
await tx.getRepository(MarketplaceProductVariation).save(variation)
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
76
128
|
if (marketplaceStores?.length && marketplaceStores.some(store => store.isAutoUpdateStockQty)) {
|
|
129
|
+
marketplaceStores = marketplaceStores.filter(marketplaceStore => marketplaceStore.isAutoUpdateStockQty)
|
|
77
130
|
let productIds: string[] = foundOIs.map(oi => oi.product.id)
|
|
78
131
|
productIds = Array.from(new Set([...productIds]))
|
|
79
132
|
const ecommerceCtrl: EcommerceController = new EcommerceController(tx, domain, user)
|
|
@@ -82,6 +135,13 @@ export async function confirmReleaseGood(name: string, context: any, tx?: Entity
|
|
|
82
135
|
}
|
|
83
136
|
}
|
|
84
137
|
|
|
138
|
+
if (foundReleaseGood.type == 'b2b') {
|
|
139
|
+
if (sellercraft) {
|
|
140
|
+
const sellercraftCtrl: SellercraftController = new SellercraftController(tx, domain, user)
|
|
141
|
+
await sellercraftCtrl.updateSellercraftStock(sellercraft, foundOPs, 'CONFIRM_ORDER')
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
85
145
|
// 1. RO Status change (PENDING => PENDING_RECEIVE)
|
|
86
146
|
const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).save({
|
|
87
147
|
...foundReleaseGood,
|
|
@@ -222,6 +222,7 @@ export async function generateReleaseGoodFunction(
|
|
|
222
222
|
refNo3: releaseGood.refNo3,
|
|
223
223
|
releaseDate: releaseGood.releaseDate,
|
|
224
224
|
truckNo: releaseGood.truckNo,
|
|
225
|
+
bundleInfo: releaseGood?.bundleInfo || [],
|
|
225
226
|
orderInventories: releaseGood.orderInventories,
|
|
226
227
|
type: releaseGood?.type ? releaseGood.type : 'b2b',
|
|
227
228
|
status: ORDER_STATUS.PENDING,
|