@things-factory/operato-hub 4.3.744 → 4.3.745
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/routers/api/restful-apis/v1/company/add-contact-points.js +71 -2
- package/dist-server/routers/api/restful-apis/v1/company/add-contact-points.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/company/index.js +1 -0
- package/dist-server/routers/api/restful-apis/v1/company/index.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/company/update-contact-points.js +243 -0
- package/dist-server/routers/api/restful-apis/v1/company/update-contact-points.js.map +1 -0
- package/dist-server/routers/api/restful-apis/v1/utils/params.js +109 -28
- package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/index.js +1 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/index.js.map +1 -1
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.js +365 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.js.map +1 -0
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js +946 -19
- package/dist-server/routers/api/restful-apis/v1/warehouse/update-release-order-details.js.map +1 -1
- package/openapi/v1/contact-point.yaml +266 -0
- package/openapi/v1/inbound.yaml +349 -0
- package/openapi/v1/outbound.yaml +256 -150
- package/package.json +18 -18
- package/server/routers/api/restful-apis/v1/company/add-contact-points.ts +91 -2
- package/server/routers/api/restful-apis/v1/company/index.ts +1 -0
- package/server/routers/api/restful-apis/v1/company/update-contact-points.ts +267 -0
- package/server/routers/api/restful-apis/v1/utils/params.ts +109 -28
- package/server/routers/api/restful-apis/v1/warehouse/index.ts +1 -0
- package/server/routers/api/restful-apis/v1/warehouse/update-arrival-notice.ts +429 -0
- package/server/routers/api/restful-apis/v1/warehouse/update-release-order-details.ts +1122 -29
|
@@ -1,12 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
const typeorm_1 = require("typeorm");
|
|
15
|
+
const uuid_1 = require("uuid");
|
|
4
16
|
const api_1 = require("@things-factory/api");
|
|
5
17
|
const biz_base_1 = require("@things-factory/biz-base");
|
|
18
|
+
const product_base_1 = require("@things-factory/product-base");
|
|
6
19
|
const sales_base_1 = require("@things-factory/sales-base");
|
|
20
|
+
const setting_base_1 = require("@things-factory/setting-base");
|
|
21
|
+
const warehouse_base_1 = require("@things-factory/warehouse-base");
|
|
22
|
+
const worksheet_base_1 = require("@things-factory/worksheet-base");
|
|
7
23
|
const middlewares_1 = require("../middlewares");
|
|
8
24
|
const error_util_1 = require("../utils/error-util");
|
|
9
25
|
const debug = require('debug')('things-factory:operato-hub:restful-api:v1:update-release-order-details');
|
|
26
|
+
// Allowed statuses for updates (order information only)
|
|
27
|
+
const ALLOWED_STATUSES_FOR_INFO_UPDATE = [
|
|
28
|
+
sales_base_1.ORDER_STATUS.PENDING,
|
|
29
|
+
sales_base_1.ORDER_STATUS.PENDING_RECEIVE,
|
|
30
|
+
sales_base_1.ORDER_STATUS.PENDING_WORKSHEET,
|
|
31
|
+
sales_base_1.ORDER_STATUS.READY_TO_PICK,
|
|
32
|
+
sales_base_1.ORDER_STATUS.PICKING,
|
|
33
|
+
sales_base_1.ORDER_STATUS.READY_TO_LOAD,
|
|
34
|
+
sales_base_1.ORDER_STATUS.LOADING,
|
|
35
|
+
sales_base_1.ORDER_STATUS.READY_TO_PACK,
|
|
36
|
+
sales_base_1.ORDER_STATUS.PACKING
|
|
37
|
+
];
|
|
38
|
+
// Allowed statuses for order product updates
|
|
39
|
+
const ALLOWED_STATUSES_FOR_PRODUCT_UPDATE = [
|
|
40
|
+
sales_base_1.ORDER_STATUS.PENDING,
|
|
41
|
+
sales_base_1.ORDER_STATUS.PENDING_RECEIVE,
|
|
42
|
+
sales_base_1.ORDER_STATUS.PENDING_WORKSHEET,
|
|
43
|
+
sales_base_1.ORDER_STATUS.READY_TO_PICK
|
|
44
|
+
];
|
|
10
45
|
api_1.restfulApiRouter.post('/v1/warehouse/update-release-order-details', middlewares_1.businessMiddleware, middlewares_1.validationMiddleware, middlewares_1.loggingMiddleware, async (context, next) => {
|
|
11
46
|
try {
|
|
12
47
|
const { domain } = context.state;
|
|
@@ -18,11 +53,12 @@ api_1.restfulApiRouter.post('/v1/warehouse/update-release-order-details', middle
|
|
|
18
53
|
throw new error_util_1.ApiError('E04', 'warehouse id not found');
|
|
19
54
|
let patch = {};
|
|
20
55
|
patch.releaseGood = bodyReq.releaseGood;
|
|
56
|
+
patch.orderProducts = bodyReq.orderProducts || [];
|
|
21
57
|
if (bodyReq === null || bodyReq === void 0 ? void 0 : bodyReq.shippingOrder)
|
|
22
58
|
patch.shippingOrder = bodyReq.shippingOrder;
|
|
23
59
|
else
|
|
24
60
|
patch.shippingOrder = null;
|
|
25
|
-
context.body = await updateReleaseGoodDetails(tx, domain, patch, user);
|
|
61
|
+
context.body = await updateReleaseGoodDetails(tx, domain, patch, user, context);
|
|
26
62
|
});
|
|
27
63
|
}
|
|
28
64
|
catch (e) {
|
|
@@ -32,36 +68,42 @@ api_1.restfulApiRouter.post('/v1/warehouse/update-release-order-details', middle
|
|
|
32
68
|
(0, error_util_1.throwInternalServerError)(context, e);
|
|
33
69
|
}
|
|
34
70
|
});
|
|
35
|
-
async function updateReleaseGoodDetails(tx, domain, patch, user) {
|
|
36
|
-
let { releaseGood, shippingOrder } = patch;
|
|
37
|
-
let status = [sales_base_1.ORDER_STATUS.DONE, sales_base_1.ORDER_STATUS.CANCELLED, sales_base_1.ORDER_STATUS.REJECTED];
|
|
71
|
+
async function updateReleaseGoodDetails(tx, domain, patch, user, context) {
|
|
72
|
+
let { releaseGood, shippingOrder, orderProducts: inputOrderProducts } = patch;
|
|
38
73
|
// Build where clause: prioritize refOrderId over roId
|
|
39
74
|
let whereClause = { domain };
|
|
40
75
|
const refOrderId = releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.refOrderId;
|
|
41
76
|
const hasRefOrderId = refOrderId != null && refOrderId !== '';
|
|
77
|
+
const roId = releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.roId;
|
|
42
78
|
if (hasRefOrderId) {
|
|
43
79
|
whereClause.refOrderId = refOrderId;
|
|
44
80
|
}
|
|
45
|
-
else if (
|
|
46
|
-
whereClause.id =
|
|
81
|
+
else if (roId) {
|
|
82
|
+
whereClause.id = roId;
|
|
47
83
|
}
|
|
48
84
|
else {
|
|
49
85
|
throw new error_util_1.ApiError('E04', 'release order: roId or refOrderId is required');
|
|
50
86
|
}
|
|
51
87
|
let foundReleaseGood = await tx.getRepository(sales_base_1.ReleaseGood).findOne({
|
|
52
88
|
where: whereClause,
|
|
53
|
-
relations: ['shippingOrder']
|
|
89
|
+
relations: ['bizplace', 'shippingOrder', 'orderProducts', 'orderProducts.product', 'orderProducts.productDetail']
|
|
54
90
|
});
|
|
55
91
|
if (!foundReleaseGood) {
|
|
56
|
-
const searchId = hasRefOrderId
|
|
57
|
-
? `refOrderId: ${refOrderId}`
|
|
58
|
-
: `roId: ${releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.roId}`;
|
|
92
|
+
const searchId = hasRefOrderId ? `refOrderId: ${refOrderId}` : `roId: ${roId}`;
|
|
59
93
|
throw new error_util_1.ApiError('E04', `release order: ${searchId}`);
|
|
60
94
|
}
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
// Validate status for order information updates
|
|
96
|
+
const releaseGoodStatus = foundReleaseGood.status;
|
|
97
|
+
if (!ALLOWED_STATUSES_FOR_INFO_UPDATE.includes(releaseGoodStatus)) {
|
|
98
|
+
throw new error_util_1.ApiError('E04', `Release order status "${releaseGoodStatus}" does not allow updates. Allowed statuses: ${ALLOWED_STATUSES_FOR_INFO_UPDATE.join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
// Validate status for order product updates
|
|
101
|
+
if (inputOrderProducts && inputOrderProducts.length > 0) {
|
|
102
|
+
if (!ALLOWED_STATUSES_FOR_PRODUCT_UPDATE.includes(releaseGoodStatus)) {
|
|
103
|
+
throw new error_util_1.ApiError('E04', `Order product updates are not allowed when release order status is "${releaseGoodStatus}". Order products can only be updated when status is: ${ALLOWED_STATUSES_FOR_PRODUCT_UPDATE.join(', ')}`);
|
|
104
|
+
}
|
|
63
105
|
}
|
|
64
|
-
//
|
|
106
|
+
// Update shipping order if provided
|
|
65
107
|
if (shippingOrder !== null) {
|
|
66
108
|
shippingOrder.remark = shippingOrder.exportRemark;
|
|
67
109
|
shippingOrder.containerClosureDateTime = shippingOrder.containerClosureDate
|
|
@@ -69,15 +111,900 @@ async function updateReleaseGoodDetails(tx, domain, patch, user) {
|
|
|
69
111
|
: null;
|
|
70
112
|
delete shippingOrder.exportRemark;
|
|
71
113
|
}
|
|
72
|
-
|
|
73
|
-
|
|
114
|
+
const releaseGoodEntityForShipping = foundReleaseGood;
|
|
115
|
+
const foundShippingOrder = releaseGoodEntityForShipping.shippingOrder;
|
|
116
|
+
if (foundShippingOrder && shippingOrder) {
|
|
117
|
+
await tx.getRepository(sales_base_1.ShippingOrder).update(foundShippingOrder.id, shippingOrder);
|
|
74
118
|
}
|
|
75
|
-
|
|
76
|
-
else if (!foundReleaseGood.shippingOrder && shippingOrder) {
|
|
119
|
+
else if (!foundShippingOrder && shippingOrder) {
|
|
77
120
|
let newShippingOrder = await tx.getRepository(sales_base_1.ShippingOrder).save(Object.assign(Object.assign({}, shippingOrder), { name: sales_base_1.OrderNoGenerator.shippingOrder(), domain, bizplace: await (0, biz_base_1.getMyBizplace)(domain, user), status: sales_base_1.ORDER_STATUS.PENDING, creator: user, updater: user }));
|
|
78
121
|
releaseGood.shippingOrder = newShippingOrder;
|
|
79
122
|
}
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
// Update order products if provided
|
|
124
|
+
if (inputOrderProducts && inputOrderProducts.length > 0) {
|
|
125
|
+
await updateOrderProducts(tx, foundReleaseGood, inputOrderProducts, domain, user, context);
|
|
126
|
+
}
|
|
127
|
+
// Update release good fields
|
|
128
|
+
const updateData = {
|
|
129
|
+
remark: (releaseGood === null || releaseGood === void 0 ? void 0 : releaseGood.remark) ? releaseGood.remark : null,
|
|
130
|
+
updater: user
|
|
131
|
+
};
|
|
132
|
+
const releaseGoodPatch = releaseGood;
|
|
133
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.refNo) !== undefined)
|
|
134
|
+
updateData.refNo = releaseGoodPatch.refNo;
|
|
135
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.refNo2) !== undefined)
|
|
136
|
+
updateData.refNo2 = releaseGoodPatch.refNo2;
|
|
137
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.refNo3) !== undefined)
|
|
138
|
+
updateData.refNo3 = releaseGoodPatch.refNo3;
|
|
139
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.description) !== undefined)
|
|
140
|
+
updateData.description = releaseGoodPatch.description;
|
|
141
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.truckNo) !== undefined)
|
|
142
|
+
updateData.truckNo = releaseGoodPatch.truckNo;
|
|
143
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.collectionOrderNo) !== undefined)
|
|
144
|
+
updateData.collectionOrderNo = releaseGoodPatch.collectionOrderNo;
|
|
145
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.releaseDate) !== undefined)
|
|
146
|
+
updateData.releaseDate = releaseGoodPatch.releaseDate;
|
|
147
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.ownTransport) !== undefined)
|
|
148
|
+
updateData.ownTransport = releaseGoodPatch.ownTransport;
|
|
149
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.exportOption) !== undefined)
|
|
150
|
+
updateData.exportOption = releaseGoodPatch.exportOption;
|
|
151
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.packingOption) !== undefined)
|
|
152
|
+
updateData.packingOption = releaseGoodPatch.packingOption;
|
|
153
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.courierOption) !== undefined)
|
|
154
|
+
updateData.courierOption = releaseGoodPatch.courierOption;
|
|
155
|
+
if ((releaseGoodPatch === null || releaseGoodPatch === void 0 ? void 0 : releaseGoodPatch.shippingOrder) !== undefined)
|
|
156
|
+
updateData.shippingOrder = releaseGoodPatch.shippingOrder;
|
|
157
|
+
const releaseGoodEntityForUpdate = foundReleaseGood;
|
|
158
|
+
// Exclude orderProducts relation to prevent TypeORM from syncing and clearing foreign keys
|
|
159
|
+
const { orderProducts, orderInventories, deliveryOrders } = releaseGoodEntityForUpdate, releaseGoodWithoutRelations = __rest(releaseGoodEntityForUpdate, ["orderProducts", "orderInventories", "deliveryOrders"]);
|
|
160
|
+
await tx.getRepository(sales_base_1.ReleaseGood).save(Object.assign(Object.assign({}, releaseGoodWithoutRelations), updateData));
|
|
161
|
+
// Fetch updated release good with relations for response
|
|
162
|
+
// Query order products separately to ensure we get all of them including newly added ones
|
|
163
|
+
const releaseGoodId = foundReleaseGood.id;
|
|
164
|
+
// Fetch release good basic info
|
|
165
|
+
const updatedReleaseGood = await tx.getRepository(sales_base_1.ReleaseGood).findOne({
|
|
166
|
+
where: { id: releaseGoodId },
|
|
167
|
+
relations: ['shippingOrder']
|
|
168
|
+
});
|
|
169
|
+
if (!updatedReleaseGood) {
|
|
170
|
+
throw new error_util_1.ApiError('E04', 'Failed to retrieve updated release order');
|
|
171
|
+
}
|
|
172
|
+
// Fetch all order products separately to ensure we get newly added ones
|
|
173
|
+
const allOrderProducts = await tx.getRepository(sales_base_1.OrderProduct).find({
|
|
174
|
+
where: { releaseGood: { id: releaseGoodId } },
|
|
175
|
+
relations: ['product', 'productDetail']
|
|
176
|
+
});
|
|
177
|
+
// Attach order products to release good
|
|
178
|
+
updatedReleaseGood.orderProducts = allOrderProducts;
|
|
179
|
+
const releaseGoodForResponse = updatedReleaseGood;
|
|
180
|
+
// Format response - only include necessary fields
|
|
181
|
+
const resultOrderProducts = (releaseGoodForResponse.orderProducts || []).map((op) => {
|
|
182
|
+
var _a, _b, _c;
|
|
183
|
+
return ({
|
|
184
|
+
id: op.id,
|
|
185
|
+
product: {
|
|
186
|
+
sku: ((_a = op.product) === null || _a === void 0 ? void 0 : _a.sku) || null,
|
|
187
|
+
name: ((_b = op.product) === null || _b === void 0 ? void 0 : _b.name) || null
|
|
188
|
+
},
|
|
189
|
+
productDetail: {
|
|
190
|
+
refCode: ((_c = op.productDetail) === null || _c === void 0 ? void 0 : _c.refCode) || null
|
|
191
|
+
},
|
|
192
|
+
batchId: op.batchId,
|
|
193
|
+
packingType: op.packingType,
|
|
194
|
+
packingSize: op.packingSize,
|
|
195
|
+
releaseQty: op.releaseQty,
|
|
196
|
+
releaseUomValue: op.releaseUomValue,
|
|
197
|
+
uom: op.uom,
|
|
198
|
+
refItemId: op.refItemId || null,
|
|
199
|
+
unitPrice: op.unitPrice || null,
|
|
200
|
+
remark: op.remark || null,
|
|
201
|
+
status: op.status
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
const shippingOrderData = releaseGoodForResponse.shippingOrder
|
|
205
|
+
? {
|
|
206
|
+
id: releaseGoodForResponse.shippingOrder.id,
|
|
207
|
+
name: releaseGoodForResponse.shippingOrder.name,
|
|
208
|
+
status: releaseGoodForResponse.shippingOrder.status
|
|
209
|
+
}
|
|
210
|
+
: null;
|
|
211
|
+
const data = {
|
|
212
|
+
id: releaseGoodForResponse.id,
|
|
213
|
+
roNo: releaseGoodForResponse.name,
|
|
214
|
+
refOrderId: releaseGoodForResponse.refOrderId,
|
|
215
|
+
refNo: releaseGoodForResponse.refNo,
|
|
216
|
+
refNo2: releaseGoodForResponse.refNo2,
|
|
217
|
+
refNo3: releaseGoodForResponse.refNo3,
|
|
218
|
+
description: releaseGoodForResponse.description,
|
|
219
|
+
truckNo: releaseGoodForResponse.truckNo,
|
|
220
|
+
collectionOrderNo: releaseGoodForResponse.collectionOrderNo,
|
|
221
|
+
releaseDate: releaseGoodForResponse.releaseDate,
|
|
222
|
+
ownTransport: releaseGoodForResponse.ownTransport,
|
|
223
|
+
exportOption: releaseGoodForResponse.exportOption,
|
|
224
|
+
packingOption: releaseGoodForResponse.packingOption,
|
|
225
|
+
courierOption: releaseGoodForResponse.courierOption,
|
|
226
|
+
status: releaseGoodForResponse.status,
|
|
227
|
+
remark: releaseGoodForResponse.remark,
|
|
228
|
+
orderProducts: resultOrderProducts,
|
|
229
|
+
shippingOrder: shippingOrderData
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
responseCode: '200',
|
|
233
|
+
message: 'success',
|
|
234
|
+
data
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
async function updateOrderProducts(tx, releaseGood, inputOrderProducts, domain, user, context) {
|
|
238
|
+
// Get existing order products with their order inventories
|
|
239
|
+
const existingOrderProducts = await tx.getRepository(sales_base_1.OrderProduct).find({
|
|
240
|
+
where: { releaseGood },
|
|
241
|
+
relations: ['product', 'productDetail', 'orderInventories', 'orderInventories.inventory']
|
|
242
|
+
});
|
|
243
|
+
// Determine hasOrderInventories by checking if releaseGood has any orderInventories
|
|
244
|
+
// Check directly on releaseGood by counting orderInventories
|
|
245
|
+
const orderInventoriesCount = await tx.getRepository(sales_base_1.OrderInventory).count({
|
|
246
|
+
where: { releaseGood }
|
|
247
|
+
});
|
|
248
|
+
const hasOrderInventories = orderInventoriesCount > 0;
|
|
249
|
+
// Process removals first
|
|
250
|
+
const orderProductIdsToRemove = [];
|
|
251
|
+
const orderProductsToUpdate = [];
|
|
252
|
+
const orderProductsToAdd = [];
|
|
253
|
+
for (const orderProductReq of inputOrderProducts) {
|
|
254
|
+
if (orderProductReq._action === 'remove') {
|
|
255
|
+
const matchingOP = findMatchingOrderProduct(existingOrderProducts, orderProductReq);
|
|
256
|
+
if (!matchingOP) {
|
|
257
|
+
throw new error_util_1.ApiError('E04', `Order product not found for removal. Provide id, sku, refCode, batchId, packingType, or refItemId to identify the item.`);
|
|
258
|
+
}
|
|
259
|
+
orderProductIdsToRemove.push(matchingOP.id);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
// Try to find existing order product for update
|
|
263
|
+
const matchingOP = findMatchingOrderProduct(existingOrderProducts, orderProductReq);
|
|
264
|
+
if (matchingOP) {
|
|
265
|
+
orderProductsToUpdate.push({ existing: matchingOP, request: orderProductReq });
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
orderProductsToAdd.push(orderProductReq);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Validate: Prevent removing all products from the order
|
|
272
|
+
// If removing products would leave the order with zero products, throw an error
|
|
273
|
+
const existingProductCount = existingOrderProducts.length;
|
|
274
|
+
const productsToRemoveCount = orderProductIdsToRemove.length;
|
|
275
|
+
const productsToAddCount = orderProductsToAdd.length;
|
|
276
|
+
const remainingProductCount = existingProductCount - productsToRemoveCount + productsToAddCount;
|
|
277
|
+
if (remainingProductCount <= 0) {
|
|
278
|
+
throw new error_util_1.ApiError('E04', 'Cannot remove all products from the release order. Please use the cancellation API to cancel the entire order instead of removing the final item(s).');
|
|
279
|
+
}
|
|
280
|
+
// Remove order products
|
|
281
|
+
if (orderProductIdsToRemove.length > 0) {
|
|
282
|
+
await removeOrderProducts(tx, orderProductIdsToRemove, hasOrderInventories);
|
|
283
|
+
}
|
|
284
|
+
// Update existing order products
|
|
285
|
+
for (const { existing, request } of orderProductsToUpdate) {
|
|
286
|
+
await updateExistingOrderProduct(tx, existing, request, releaseGood, domain, user, context, hasOrderInventories);
|
|
287
|
+
}
|
|
288
|
+
// Add new order products
|
|
289
|
+
for (const orderProductReq of orderProductsToAdd) {
|
|
290
|
+
await addNewOrderProduct(tx, orderProductReq, releaseGood, domain, user, context, hasOrderInventories);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function findMatchingOrderProduct(existingOrderProducts, orderProductReq) {
|
|
294
|
+
var _a, _b, _c;
|
|
295
|
+
// First try by ID if provided
|
|
296
|
+
if (orderProductReq.id) {
|
|
297
|
+
const found = existingOrderProducts.find(op => op.id === orderProductReq.id);
|
|
298
|
+
if (found)
|
|
299
|
+
return found;
|
|
300
|
+
}
|
|
301
|
+
// Extract matching fields
|
|
302
|
+
const sku = ((_a = orderProductReq.product) === null || _a === void 0 ? void 0 : _a.sku) || orderProductReq.sku;
|
|
303
|
+
const refCode = ((_b = orderProductReq.product) === null || _b === void 0 ? void 0 : _b.refCode) || ((_c = orderProductReq.productDetail) === null || _c === void 0 ? void 0 : _c.refCode) || orderProductReq.refCode;
|
|
304
|
+
const batchId = orderProductReq.batchId;
|
|
305
|
+
const packingType = orderProductReq.packingType;
|
|
306
|
+
const refItemId = orderProductReq.refItemId;
|
|
307
|
+
// Need at least one matching field (besides id)
|
|
308
|
+
if (!sku && !refCode && !batchId && !packingType && !refItemId) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
// Match by provided fields (all provided fields must match)
|
|
312
|
+
return (existingOrderProducts.find(op => {
|
|
313
|
+
var _a, _b;
|
|
314
|
+
const matchesSku = !sku || ((_a = op.product) === null || _a === void 0 ? void 0 : _a.sku) === sku;
|
|
315
|
+
const matchesRefCode = !refCode || ((_b = op.productDetail) === null || _b === void 0 ? void 0 : _b.refCode) === refCode;
|
|
316
|
+
const matchesBatchId = !batchId || op.batchId === batchId;
|
|
317
|
+
const matchesPackingType = !packingType || op.packingType === packingType;
|
|
318
|
+
const matchesRefItemId = !refItemId || op.refItemId === refItemId;
|
|
319
|
+
return matchesSku && matchesRefCode && matchesBatchId && matchesPackingType && matchesRefItemId;
|
|
320
|
+
}) || null);
|
|
321
|
+
}
|
|
322
|
+
async function removeOrderProducts(tx, orderProductIds, hasOrderInventories) {
|
|
323
|
+
var _a;
|
|
324
|
+
for (const orderProductId of orderProductIds) {
|
|
325
|
+
const orderProduct = await tx.getRepository(sales_base_1.OrderProduct).findOne({
|
|
326
|
+
where: { id: orderProductId },
|
|
327
|
+
relations: ['orderInventories', 'orderInventories.inventory']
|
|
328
|
+
});
|
|
329
|
+
if (!orderProduct)
|
|
330
|
+
continue;
|
|
331
|
+
const orderProductEntity = orderProduct;
|
|
332
|
+
const orderInventories = (orderProductEntity.orderInventories || []);
|
|
333
|
+
const releaseQty = orderProductEntity.releaseQty || 0;
|
|
334
|
+
const releaseUomValue = orderProductEntity.releaseUomValue || 0;
|
|
335
|
+
if (hasOrderInventories && orderInventories.length > 0) {
|
|
336
|
+
// Release locked quantities from inventories
|
|
337
|
+
for (const orderInventory of orderInventories) {
|
|
338
|
+
const inventory = orderInventory.inventory;
|
|
339
|
+
if (inventory) {
|
|
340
|
+
const oiReleaseQty = orderInventory.releaseQty || 0;
|
|
341
|
+
const oiReleaseUomValue = orderInventory.releaseUomValue || 0;
|
|
342
|
+
const inventoryEntity = inventory;
|
|
343
|
+
await tx
|
|
344
|
+
.getRepository(warehouse_base_1.Inventory)
|
|
345
|
+
.createQueryBuilder()
|
|
346
|
+
.update(warehouse_base_1.Inventory)
|
|
347
|
+
.set({
|
|
348
|
+
lockedQty: () => `COALESCE(locked_qty, 0) - ${oiReleaseQty}`,
|
|
349
|
+
lockedUomValue: () => `COALESCE(locked_uom_value, 0) - ${oiReleaseUomValue}`,
|
|
350
|
+
updater: orderProductEntity.updater
|
|
351
|
+
})
|
|
352
|
+
.where('id = :id', { id: inventoryEntity.id })
|
|
353
|
+
.execute();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Delete order inventories
|
|
357
|
+
// First, delete any worksheet details that reference these order inventories
|
|
358
|
+
const oiIds = orderInventories.map(oi => oi.id).filter((id) => !!id);
|
|
359
|
+
if (oiIds.length > 0) {
|
|
360
|
+
// Find and delete worksheet details that reference the order inventories to be deleted
|
|
361
|
+
// Join with OrderInventory to find worksheet details by order inventory IDs
|
|
362
|
+
const worksheetDetailsToDelete = await tx
|
|
363
|
+
.getRepository(worksheet_base_1.WorksheetDetail)
|
|
364
|
+
.createQueryBuilder('wsd')
|
|
365
|
+
.innerJoin('wsd.targetInventory', 'oi')
|
|
366
|
+
.where('oi.id IN (:...oiIds)', { oiIds })
|
|
367
|
+
.andWhere('wsd.domain = :domainId', {
|
|
368
|
+
domainId: ((_a = orderProductEntity.domain) === null || _a === void 0 ? void 0 : _a.id) || orderProductEntity.domainId
|
|
369
|
+
})
|
|
370
|
+
.getMany();
|
|
371
|
+
if (worksheetDetailsToDelete.length > 0) {
|
|
372
|
+
const worksheetDetailIds = worksheetDetailsToDelete.map(wsd => wsd.id);
|
|
373
|
+
await tx.getRepository(worksheet_base_1.WorksheetDetail).delete(worksheetDetailIds);
|
|
374
|
+
}
|
|
375
|
+
// Now delete the order inventories
|
|
376
|
+
await tx.getRepository(sales_base_1.OrderInventory).delete(oiIds);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
// Update ProductDetailStock unassigned quantities
|
|
381
|
+
const productDetail = orderProductEntity.productDetail;
|
|
382
|
+
if (productDetail && productDetail.id) {
|
|
383
|
+
await tx
|
|
384
|
+
.getRepository(warehouse_base_1.ProductDetailStock)
|
|
385
|
+
.createQueryBuilder()
|
|
386
|
+
.update(warehouse_base_1.ProductDetailStock)
|
|
387
|
+
.set({
|
|
388
|
+
unassignedQty: () => `"unassigned_qty" - ${releaseQty}`,
|
|
389
|
+
unassignedUomValue: () => `"unassigned_uom_value" - ${releaseUomValue}`
|
|
390
|
+
})
|
|
391
|
+
.where({ productDetail: productDetail.id })
|
|
392
|
+
.execute();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Delete order product
|
|
396
|
+
await tx.getRepository(sales_base_1.OrderProduct).delete(orderProductId);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async function updateExistingOrderProduct(tx, existing, request, releaseGood, domain, user, context, hasOrderInventories) {
|
|
400
|
+
var _a, _b, _c, _d, _e;
|
|
401
|
+
const newQty = request.releaseQty !== undefined ? request.releaseQty : request.qty;
|
|
402
|
+
if (newQty === undefined) {
|
|
403
|
+
// No quantity change, just update other fields if provided
|
|
404
|
+
const updateData = { updater: user };
|
|
405
|
+
if (request.remark !== undefined)
|
|
406
|
+
updateData.remark = request.remark;
|
|
407
|
+
if (request.unitPrice !== undefined)
|
|
408
|
+
updateData.unitPrice = request.unitPrice;
|
|
409
|
+
await tx.getRepository(sales_base_1.OrderProduct).update(existing.id, updateData);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (newQty <= 0) {
|
|
413
|
+
throw new error_util_1.ApiError('E01', 'releaseQty must be greater than 0');
|
|
414
|
+
}
|
|
415
|
+
const qtyDiff = newQty - existing.releaseQty;
|
|
416
|
+
const uomValueDiff = (newQty - existing.releaseQty) * (((_a = existing.productDetail) === null || _a === void 0 ? void 0 : _a.uomValue) || 1);
|
|
417
|
+
// Validate decimal places for non-decimal products
|
|
418
|
+
if (!((_b = existing.product) === null || _b === void 0 ? void 0 : _b.isInventoryDecimal) && newQty % 1 !== 0) {
|
|
419
|
+
throw new error_util_1.ApiError('E01', `releaseQty must be an integer for product ${(_c = existing.product) === null || _c === void 0 ? void 0 : _c.sku}`);
|
|
420
|
+
}
|
|
421
|
+
// Round to 3 decimal places
|
|
422
|
+
const roundedNewQty = ((_d = existing.product) === null || _d === void 0 ? void 0 : _d.isInventoryDecimal) ? Math.round(newQty * 1000) / 1000 : Math.round(newQty);
|
|
423
|
+
const roundedNewUomValue = roundedNewQty * (((_e = existing.productDetail) === null || _e === void 0 ? void 0 : _e.uomValue) || 1);
|
|
424
|
+
if (hasOrderInventories) {
|
|
425
|
+
// Update order inventories
|
|
426
|
+
await updateOrderInventoriesForQtyChange(tx, existing, qtyDiff, uomValueDiff, roundedNewQty, roundedNewUomValue, user, releaseGood, domain);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
// Update ProductDetailStock
|
|
430
|
+
await tx
|
|
431
|
+
.getRepository(warehouse_base_1.ProductDetailStock)
|
|
432
|
+
.createQueryBuilder()
|
|
433
|
+
.update(warehouse_base_1.ProductDetailStock)
|
|
434
|
+
.set({
|
|
435
|
+
unassignedQty: () => `"unassigned_qty" + ${qtyDiff}`,
|
|
436
|
+
unassignedUomValue: () => `"unassigned_uom_value" + ${uomValueDiff}`
|
|
437
|
+
})
|
|
438
|
+
.where({ productDetail: existing.productDetail.id })
|
|
439
|
+
.execute();
|
|
440
|
+
}
|
|
441
|
+
// Update order product
|
|
442
|
+
const updateData = {
|
|
443
|
+
releaseQty: roundedNewQty,
|
|
444
|
+
releaseUomValue: roundedNewUomValue,
|
|
445
|
+
updater: user
|
|
446
|
+
};
|
|
447
|
+
if (request.remark !== undefined)
|
|
448
|
+
updateData.remark = request.remark;
|
|
449
|
+
if (request.unitPrice !== undefined)
|
|
450
|
+
updateData.unitPrice = request.unitPrice;
|
|
451
|
+
await tx.getRepository(sales_base_1.OrderProduct).update(existing.id, updateData);
|
|
452
|
+
}
|
|
453
|
+
async function updateOrderInventoriesForQtyChange(tx, orderProduct, qtyDiff, uomValueDiff, newQty, newUomValue, user, releaseGood, domain) {
|
|
454
|
+
const existingOrderInventories = (await tx.getRepository(sales_base_1.OrderInventory).find({
|
|
455
|
+
where: { orderProduct },
|
|
456
|
+
relations: ['inventory']
|
|
457
|
+
}));
|
|
458
|
+
if (qtyDiff > 0) {
|
|
459
|
+
// Increase: assign more inventory
|
|
460
|
+
await assignAdditionalInventory(tx, orderProduct, qtyDiff, uomValueDiff, user, releaseGood, domain);
|
|
461
|
+
}
|
|
462
|
+
else if (qtyDiff < 0) {
|
|
463
|
+
// Decrease: select latest order inventories and deduct from them
|
|
464
|
+
const reductionQty = Math.abs(qtyDiff);
|
|
465
|
+
const reductionUomValue = Math.abs(uomValueDiff);
|
|
466
|
+
// Sort order inventories by creation date (latest first)
|
|
467
|
+
const sortedOrderInventories = [...existingOrderInventories].sort((a, b) => {
|
|
468
|
+
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
469
|
+
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
470
|
+
return dateB - dateA; // Latest first
|
|
471
|
+
});
|
|
472
|
+
let remainingReductionQty = reductionQty;
|
|
473
|
+
let remainingReductionUomValue = reductionUomValue;
|
|
474
|
+
const orderInventoriesToDelete = [];
|
|
475
|
+
for (const oi of sortedOrderInventories) {
|
|
476
|
+
if (remainingReductionQty <= 0 && remainingReductionUomValue <= 0) {
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
const oiReleaseQty = oi.releaseQty || 0;
|
|
480
|
+
const oiReleaseUomValue = oi.releaseUomValue || 0;
|
|
481
|
+
// Calculate how much to deduct from this order inventory
|
|
482
|
+
const deductQty = Math.min(remainingReductionQty, oiReleaseQty);
|
|
483
|
+
const deductUomValue = Math.min(remainingReductionUomValue, oiReleaseUomValue);
|
|
484
|
+
const inventory = oi.inventory;
|
|
485
|
+
if (inventory) {
|
|
486
|
+
// Release locked quantities from inventory
|
|
487
|
+
await tx
|
|
488
|
+
.getRepository(warehouse_base_1.Inventory)
|
|
489
|
+
.createQueryBuilder()
|
|
490
|
+
.update(warehouse_base_1.Inventory)
|
|
491
|
+
.set({
|
|
492
|
+
lockedQty: () => `COALESCE(locked_qty, 0) - ${deductQty}`,
|
|
493
|
+
lockedUomValue: () => `COALESCE(locked_uom_value, 0) - ${deductUomValue}`,
|
|
494
|
+
updater: user
|
|
495
|
+
})
|
|
496
|
+
.where('id = :id', { id: inventory.id })
|
|
497
|
+
.execute();
|
|
498
|
+
}
|
|
499
|
+
const newOiQty = oiReleaseQty - deductQty;
|
|
500
|
+
const newOiUomValue = oiReleaseUomValue - deductUomValue;
|
|
501
|
+
if (newOiQty <= 0 || newOiUomValue <= 0) {
|
|
502
|
+
// Mark this order inventory for deletion
|
|
503
|
+
orderInventoriesToDelete.push(oi.id);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
// Update order inventory with remaining quantity
|
|
507
|
+
await tx.getRepository(sales_base_1.OrderInventory).update(oi.id, {
|
|
508
|
+
releaseQty: newOiQty,
|
|
509
|
+
releaseUomValue: newOiUomValue,
|
|
510
|
+
updater: user
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
remainingReductionQty -= deductQty;
|
|
514
|
+
remainingReductionUomValue -= deductUomValue;
|
|
515
|
+
}
|
|
516
|
+
// Delete order inventories that are fully consumed
|
|
517
|
+
if (orderInventoriesToDelete.length > 0) {
|
|
518
|
+
// Find and delete worksheet details that reference these order inventories
|
|
519
|
+
const worksheetDetailsToDelete = await tx
|
|
520
|
+
.getRepository(worksheet_base_1.WorksheetDetail)
|
|
521
|
+
.createQueryBuilder('wsd')
|
|
522
|
+
.innerJoin('wsd.targetInventory', 'oi')
|
|
523
|
+
.where('oi.id IN (:...oiIds)', { oiIds: orderInventoriesToDelete })
|
|
524
|
+
.andWhere('wsd.domain = :domainId', { domainId: domain.id })
|
|
525
|
+
.getMany();
|
|
526
|
+
if (worksheetDetailsToDelete.length > 0) {
|
|
527
|
+
const worksheetDetailIds = worksheetDetailsToDelete.map(wsd => wsd.id);
|
|
528
|
+
await tx.getRepository(worksheet_base_1.WorksheetDetail).delete(worksheetDetailIds);
|
|
529
|
+
}
|
|
530
|
+
// Now delete the order inventories
|
|
531
|
+
await tx.getRepository(sales_base_1.OrderInventory).delete(orderInventoriesToDelete);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// Update order product totals
|
|
535
|
+
const remainingOrderInventories = (await tx.getRepository(sales_base_1.OrderInventory).find({
|
|
536
|
+
where: { orderProduct }
|
|
537
|
+
}));
|
|
538
|
+
const totalReleaseQty = remainingOrderInventories.reduce((sum, oi) => sum + (oi.releaseQty || 0), 0);
|
|
539
|
+
const totalReleaseUomValue = remainingOrderInventories.reduce((sum, oi) => sum + (oi.releaseUomValue || 0), 0);
|
|
540
|
+
await tx.getRepository(sales_base_1.OrderProduct).update(orderProduct.id, {
|
|
541
|
+
releaseQty: totalReleaseQty,
|
|
542
|
+
releaseUomValue: totalReleaseUomValue,
|
|
543
|
+
updater: user
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
async function assignAdditionalInventory(tx, orderProduct, qtyDiff, uomValueDiff, user, releaseGood, domain) {
|
|
547
|
+
var _a;
|
|
548
|
+
// Get customer bizplace from releaseGood (it's already loaded)
|
|
549
|
+
// If not loaded, fetch it using bizplaceId
|
|
550
|
+
let customerBizplace = releaseGood.bizplace;
|
|
551
|
+
if (!customerBizplace && releaseGood.bizplaceId) {
|
|
552
|
+
customerBizplace = await tx.getRepository(biz_base_1.Bizplace).findOne({
|
|
553
|
+
where: { id: releaseGood.bizplaceId }
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (!customerBizplace) {
|
|
557
|
+
throw new error_util_1.ApiError('E04', 'Customer bizplace not found for release order');
|
|
558
|
+
}
|
|
559
|
+
// Get release shelf life override from contact point (deliverTo)
|
|
560
|
+
let releaseShelfLifeOverride = null;
|
|
561
|
+
if (releaseGood.deliverToId) {
|
|
562
|
+
const deliverToContactPoint = await tx
|
|
563
|
+
.getRepository(biz_base_1.ContactPoint)
|
|
564
|
+
.findOne({ where: { id: releaseGood.deliverToId } });
|
|
565
|
+
if ((deliverToContactPoint === null || deliverToContactPoint === void 0 ? void 0 : deliverToContactPoint.releaseShelfLife) != null && deliverToContactPoint.releaseShelfLife !== 0) {
|
|
566
|
+
releaseShelfLifeOverride = deliverToContactPoint.releaseShelfLife;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
// Get picking product setting
|
|
570
|
+
const pickingProductSetting = await tx.getRepository(setting_base_1.Setting).findOne({
|
|
571
|
+
where: { domain, name: 'rule-for-picking-product' }
|
|
572
|
+
});
|
|
573
|
+
// Determine orderInventory status based on releaseGood status
|
|
574
|
+
let orderInventoryStatus;
|
|
575
|
+
switch (releaseGood.status) {
|
|
576
|
+
case sales_base_1.ORDER_STATUS.PENDING:
|
|
577
|
+
orderInventoryStatus = sales_base_1.ORDER_INVENTORY_STATUS.PENDING;
|
|
578
|
+
break;
|
|
579
|
+
case sales_base_1.ORDER_STATUS.PENDING_RECEIVE:
|
|
580
|
+
orderInventoryStatus = sales_base_1.ORDER_INVENTORY_STATUS.PENDING_RECEIVE;
|
|
581
|
+
break;
|
|
582
|
+
case sales_base_1.ORDER_STATUS.PENDING_WORKSHEET:
|
|
583
|
+
orderInventoryStatus = sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET;
|
|
584
|
+
break;
|
|
585
|
+
case sales_base_1.ORDER_STATUS.READY_TO_PICK:
|
|
586
|
+
orderInventoryStatus = sales_base_1.ORDER_INVENTORY_STATUS.READY_TO_PICK;
|
|
587
|
+
break;
|
|
588
|
+
default:
|
|
589
|
+
// Default to PENDING_WORKSHEET for other statuses
|
|
590
|
+
orderInventoryStatus = sales_base_1.ORDER_INVENTORY_STATUS.PENDING_WORKSHEET;
|
|
591
|
+
}
|
|
592
|
+
// Ensure product is loaded
|
|
593
|
+
if (!orderProduct.product) {
|
|
594
|
+
orderProduct.product = await tx.getRepository(product_base_1.Product).findOne({
|
|
595
|
+
where: { id: orderProduct.productId }
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
const product = orderProduct.product;
|
|
599
|
+
if (!product) {
|
|
600
|
+
throw new error_util_1.ApiError('E04', 'Product not found for order product');
|
|
601
|
+
}
|
|
602
|
+
// Determine picking strategy sortings
|
|
603
|
+
let sortings = [];
|
|
604
|
+
switch (product.pickingStrategy) {
|
|
605
|
+
case 'LIFO':
|
|
606
|
+
sortings.push({ name: 'iv.created_at', desc: true });
|
|
607
|
+
if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
|
|
608
|
+
for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
|
|
609
|
+
sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
|
|
614
|
+
}
|
|
615
|
+
break;
|
|
616
|
+
case 'FEFO':
|
|
617
|
+
sortings.push({ name: 'iv.expiration_date', desc: false }, { name: 'iv.created_at', desc: false });
|
|
618
|
+
if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
|
|
619
|
+
for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
|
|
620
|
+
sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
case 'FMFO':
|
|
628
|
+
sortings.push({ name: 'iv.manufacture_date', desc: false }, { name: 'iv.created_at', desc: false });
|
|
629
|
+
if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
|
|
630
|
+
for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
|
|
631
|
+
sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
|
|
636
|
+
}
|
|
637
|
+
break;
|
|
638
|
+
case 'LOCATION':
|
|
639
|
+
if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
|
|
640
|
+
for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
|
|
641
|
+
sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
sortings.push({ name: 'loc.name', desc: false }, { name: 'iv.created_at', desc: false });
|
|
646
|
+
}
|
|
647
|
+
break;
|
|
648
|
+
// Every other case includes 'FIFO' will be applicable for this case
|
|
649
|
+
default:
|
|
650
|
+
sortings.push({ name: 'iv.created_at', desc: false });
|
|
651
|
+
if (pickingProductSetting === null || pickingProductSetting === void 0 ? void 0 : pickingProductSetting.value) {
|
|
652
|
+
for (const [key, value] of Object.entries(JSON.parse(pickingProductSetting.value))) {
|
|
653
|
+
sortings.push({ name: `loc.${key}`, desc: value == 'DESC' ? true : false });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
sortings.push({ name: 'loc.name', desc: false });
|
|
658
|
+
sortings.push({ name: 'iv.pallet_id', desc: false });
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
let queryFilters = [];
|
|
663
|
+
let queryStrings = queryFilters.reduce((acc, itm, idx, arr) => {
|
|
664
|
+
acc.values.push(itm.filters);
|
|
665
|
+
switch (itm === null || itm === void 0 ? void 0 : itm.operator) {
|
|
666
|
+
case 'notin':
|
|
667
|
+
case 'in':
|
|
668
|
+
acc.query.push(`${itm.query} ${itm.operator == 'notin' ? 'NOT IN' : 'IN'} (${itm.filters
|
|
669
|
+
.map((itm, idx) => {
|
|
670
|
+
return `$${acc.values.length + 1}`;
|
|
671
|
+
})
|
|
672
|
+
.join(',')})`);
|
|
673
|
+
break;
|
|
674
|
+
default:
|
|
675
|
+
acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
acc.query.push(`${itm.query} ${(itm === null || itm === void 0 ? void 0 : itm.operator) ? itm.operator : '='} $${acc.values.length + 1}`);
|
|
679
|
+
return acc;
|
|
680
|
+
}, {
|
|
681
|
+
query: [],
|
|
682
|
+
values: []
|
|
683
|
+
});
|
|
684
|
+
let sortQuery = sortings
|
|
685
|
+
.map(itm => {
|
|
686
|
+
return `${itm.name} ${itm.desc ? 'DESC' : 'ASC'}`;
|
|
687
|
+
})
|
|
688
|
+
.join(`,`);
|
|
689
|
+
// Ensure productDetail is loaded for uom
|
|
690
|
+
if (!orderProduct.productDetail) {
|
|
691
|
+
orderProduct.productDetail = await tx.getRepository(product_base_1.ProductDetail).findOne({
|
|
692
|
+
where: { id: orderProduct.productDetailId }
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
const productDetail = orderProduct.productDetail;
|
|
696
|
+
const uom = (productDetail && productDetail.uom) || product.uom || 'UN';
|
|
697
|
+
let params = [
|
|
698
|
+
user.id,
|
|
699
|
+
domain.id,
|
|
700
|
+
customerBizplace.id,
|
|
701
|
+
orderProduct.packingType,
|
|
702
|
+
orderProduct.packingSize,
|
|
703
|
+
product.id,
|
|
704
|
+
warehouse_base_1.INVENTORY_STATUS.STORED,
|
|
705
|
+
warehouse_base_1.LOCATION_TYPE.QUARANTINE,
|
|
706
|
+
warehouse_base_1.LOCATION_TYPE.RESERVE,
|
|
707
|
+
warehouse_base_1.LOCATION_TYPE.STORAGE
|
|
708
|
+
];
|
|
709
|
+
let batchId = orderProduct.batchId && orderProduct.batchId !== '-' ? String(orderProduct.batchId).trim() : null;
|
|
710
|
+
if (batchId)
|
|
711
|
+
params.push(batchId);
|
|
712
|
+
params = [...params, ...queryStrings.values];
|
|
713
|
+
// Handle warehouse code filtering
|
|
714
|
+
const warehouseCode = orderProduct.warehouseCode;
|
|
715
|
+
const warehouseNameFilter = warehouseCode ? `AND w.name = $${params.length + 1}` : '';
|
|
716
|
+
if (warehouseCode) {
|
|
717
|
+
params.push(String(warehouseCode).trim());
|
|
718
|
+
}
|
|
719
|
+
// Add release shelf life override parameter
|
|
720
|
+
const releaseShelfLifeParamIndex = params.length + 1;
|
|
721
|
+
params.push(releaseShelfLifeOverride);
|
|
722
|
+
// Build the sophisticated SQL query with window functions
|
|
723
|
+
let query = `
|
|
724
|
+
update inventories tgt set locked_qty = coalesce(locked_qty,0) + src.reserve_qty,
|
|
725
|
+
locked_uom_value = coalesce(locked_uom_value,0) + src.reserve_uom_value,
|
|
726
|
+
updated_at = NOW(),
|
|
727
|
+
updater_id = $1
|
|
728
|
+
from (
|
|
729
|
+
select "id", "pallet_id","carton_id", "batch_id", "batch_id_ref", "unit", "uom", "packing_type", "packing_size", "manufacture_year",
|
|
730
|
+
"reserve_qty", (("uom_value"/"qty") * "reserve_qty") as "reserve_uom_value" from (
|
|
731
|
+
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",
|
|
732
|
+
"release_qty", "release_uom_value", lock_amount,
|
|
733
|
+
case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end as "reserve_qty"
|
|
734
|
+
from (
|
|
735
|
+
select *,
|
|
736
|
+
(
|
|
737
|
+
case when (qty - greatest(locked_qty, 0) - greatest(unassigned_qty, 0)) < 0 then 0
|
|
738
|
+
else qty - greatest(locked_qty, 0) - greatest(unassigned_qty, 0) end
|
|
739
|
+
) as available_qty,
|
|
740
|
+
sum(qty - locked_qty - release_qty - unassigned_qty) over (order by sort_seq asc rows between unbounded preceding and current row) as lock_amount
|
|
741
|
+
from (
|
|
742
|
+
SELECT 0 as sort_seq, null as id, null as pallet_id, null as batch_id, null as batch_id_ref,
|
|
743
|
+
null as unit, '${uom}' as uom, '${orderProduct.packingType}' as packing_type, '${orderProduct.packingSize}' as packing_size,
|
|
744
|
+
null as manufacture_year, null as carton_id, 0 as uom_value, 0 as locked_uom_value, 0 as qty, 0 as locked_qty, 0 as unassigned_uom_value, 0 as unassigned_qty, null as created_at,
|
|
745
|
+
${qtyDiff}::numeric as release_qty, ${uomValueDiff}::numeric as release_uom_value
|
|
746
|
+
UNION
|
|
747
|
+
(
|
|
748
|
+
SELECT ROW_NUMBER() OVER(PARTITION BY iv.domain_id ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}) as sort_seq,
|
|
749
|
+
iv.id, iv.pallet_id, iv.batch_id, iv.batch_id_ref,
|
|
750
|
+
iv.unit, iv.uom, iv.packing_type, iv.packing_size,
|
|
751
|
+
iv.manufacture_year, iv.carton_id, iv.uom_value,
|
|
752
|
+
coalesce(iv.locked_uom_value,0) as locked_uom_value, iv.qty, greatest(coalesce(iv.locked_qty,0),0) as locked_qty, coalesce(pds.unassigned_uom_value,0) as unassigned_uom_value, greatest(coalesce(pds.unassigned_qty,0),0) as unassigned_qty,
|
|
753
|
+
iv.created_at, 0 as release_qty, 0 as release_uom_value
|
|
754
|
+
FROM "inventories" "iv"
|
|
755
|
+
LEFT JOIN "product_detail_stocks" "pds" ON "pds"."product_detail_id" = "iv"."product_detail_id"
|
|
756
|
+
INNER JOIN "locations" "loc" ON "loc"."id"="iv"."location_id"
|
|
757
|
+
INNER JOIN "warehouses" "w" ON "w"."id" = "loc"."warehouse_id"
|
|
758
|
+
INNER JOIN "products" "p" ON "p"."id"="iv"."product_id"
|
|
759
|
+
INNER JOIN "warehouse_inventory_assignment_rankings" "wiar" ON "wiar"."location_type"="loc"."type"
|
|
760
|
+
WHERE case when coalesce("iv"."locked_qty",0) < 0 then 0 else ("iv"."qty" - coalesce("iv"."locked_qty",0)) end > 0 AND
|
|
761
|
+
"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)
|
|
762
|
+
AND ${batchId ? `"iv"."batch_id" = $11` : `1=1`}
|
|
763
|
+
${warehouseNameFilter}
|
|
764
|
+
AND "iv"."obsolete" = false AND (
|
|
765
|
+
"iv"."expiration_date" IS NULL
|
|
766
|
+
OR
|
|
767
|
+
CASE
|
|
768
|
+
WHEN $${releaseShelfLifeParamIndex}::integer IS NOT NULL AND $${releaseShelfLifeParamIndex}::integer > 0 THEN
|
|
769
|
+
CURRENT_DATE < ("iv"."expiration_date" - $${releaseShelfLifeParamIndex}::integer)
|
|
770
|
+
WHEN "p"."min_outbound_shelf_life" IS NOT NULL AND "p"."min_outbound_shelf_life" > 0 THEN
|
|
771
|
+
CURRENT_DATE < ("iv"."expiration_date" - "p"."min_outbound_shelf_life")
|
|
772
|
+
ELSE
|
|
773
|
+
TRUE
|
|
774
|
+
END
|
|
775
|
+
)
|
|
776
|
+
${queryStrings.query.length > 0 ? `AND ${queryStrings.query.join(' AND ')}` : ''}
|
|
777
|
+
ORDER BY wiar.rank ${sortQuery ? ', ' + sortQuery : ''}
|
|
778
|
+
)
|
|
779
|
+
) dt1
|
|
780
|
+
) dt2 where case when "lock_amount" < 0 then "available_qty" else "available_qty" - "lock_amount" end > 0
|
|
781
|
+
) dt3 where sort_seq > 0
|
|
782
|
+
) src where src.id = tgt.id
|
|
783
|
+
returning
|
|
784
|
+
tgt."id", tgt."qty", tgt."pallet_id", tgt."carton_id", tgt."batch_id", tgt."batch_id_ref", tgt."unit",
|
|
785
|
+
tgt."uom", tgt."packing_type", tgt."packing_size", tgt."manufacture_year",
|
|
786
|
+
tgt."locked_qty", tgt."uom_value", tgt."locked_uom_value", src."reserve_qty", src."reserve_uom_value";`;
|
|
787
|
+
let updatedInventories = await tx.getRepository(warehouse_base_1.Inventory).query(query, params);
|
|
788
|
+
let totalAssigned = ((_a = updatedInventories[0]) === null || _a === void 0 ? void 0 : _a.reduce((acc, inv) => {
|
|
789
|
+
return acc + inv.reserve_qty;
|
|
790
|
+
}, 0)) || 0;
|
|
791
|
+
// For non-decimal products, round the values before comparison
|
|
792
|
+
let roundedQtyDiff = qtyDiff;
|
|
793
|
+
if (!product.isInventoryDecimal) {
|
|
794
|
+
roundedQtyDiff = Math.round(qtyDiff);
|
|
795
|
+
totalAssigned = Math.round(totalAssigned);
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
// For decimal products, round to 3 decimal places
|
|
799
|
+
roundedQtyDiff = Math.round(qtyDiff * 1000) / 1000;
|
|
800
|
+
totalAssigned = Math.round(totalAssigned * 1000) / 1000;
|
|
801
|
+
}
|
|
802
|
+
if (Math.abs(roundedQtyDiff - totalAssigned) > 0.001) {
|
|
803
|
+
// Using small epsilon for float comparison
|
|
804
|
+
throw new error_util_1.ApiError('E01', 'INSUFFICIENT_STOCK');
|
|
805
|
+
}
|
|
806
|
+
// Create order inventory records for each assigned inventory
|
|
807
|
+
if (updatedInventories[0] && updatedInventories[0].length > 0) {
|
|
808
|
+
// Ensure releaseGood.id is available (required for TypeORM relation)
|
|
809
|
+
if (!releaseGood.id) {
|
|
810
|
+
throw new error_util_1.ApiError('E04', 'Release good ID is required for order inventory');
|
|
811
|
+
}
|
|
812
|
+
// Ensure orderProduct.id is available (required for TypeORM relation)
|
|
813
|
+
if (!orderProduct.id) {
|
|
814
|
+
throw new error_util_1.ApiError('E04', 'Order product ID is required for order inventory');
|
|
815
|
+
}
|
|
816
|
+
// Get existing order inventories for this order product to check for duplicates
|
|
817
|
+
const existingOrderInventories = await tx.getRepository(sales_base_1.OrderInventory).find({
|
|
818
|
+
where: { orderProduct },
|
|
819
|
+
relations: ['inventory']
|
|
820
|
+
});
|
|
821
|
+
// Check if worksheets exist for this release good
|
|
822
|
+
const existingWorksheets = await tx.getRepository(worksheet_base_1.Worksheet).find({
|
|
823
|
+
where: {
|
|
824
|
+
releaseGood: { id: releaseGood.id },
|
|
825
|
+
type: worksheet_base_1.WORKSHEET_TYPE.PICKING,
|
|
826
|
+
status: (0, typeorm_1.In)([worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED])
|
|
827
|
+
},
|
|
828
|
+
relations: ['bizplace']
|
|
829
|
+
});
|
|
830
|
+
const newOrderInventories = [];
|
|
831
|
+
for (const inv of updatedInventories[0]) {
|
|
832
|
+
// Check if an order inventory already exists for this inventory ID
|
|
833
|
+
const existingOI = existingOrderInventories.find((oi) => oi.inventory && oi.inventory.id === inv.id);
|
|
834
|
+
let savedOrderInventory;
|
|
835
|
+
if (existingOI) {
|
|
836
|
+
// Update existing order inventory
|
|
837
|
+
const updatedReleaseQty = (existingOI.releaseQty || 0) + inv.reserve_qty;
|
|
838
|
+
const updatedReleaseUomValue = (existingOI.releaseUomValue || 0) + inv.reserve_uom_value;
|
|
839
|
+
await tx.getRepository(sales_base_1.OrderInventory).update(existingOI.id, {
|
|
840
|
+
releaseQty: updatedReleaseQty,
|
|
841
|
+
releaseUomValue: updatedReleaseUomValue,
|
|
842
|
+
updater: user
|
|
843
|
+
});
|
|
844
|
+
// Reload to get updated entity
|
|
845
|
+
savedOrderInventory = (await tx.getRepository(sales_base_1.OrderInventory).findOne({
|
|
846
|
+
where: { id: existingOI.id },
|
|
847
|
+
relations: ['inventory']
|
|
848
|
+
}));
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
// Create new order inventory
|
|
852
|
+
savedOrderInventory = await tx.getRepository(sales_base_1.OrderInventory).save(Object.assign(new sales_base_1.OrderInventory(), {
|
|
853
|
+
name: (0, uuid_1.v4)(),
|
|
854
|
+
domain: domain,
|
|
855
|
+
bizplace: customerBizplace,
|
|
856
|
+
releaseGood: releaseGood,
|
|
857
|
+
releaseGoodId: releaseGood.id,
|
|
858
|
+
orderProduct: orderProduct,
|
|
859
|
+
orderProductId: orderProduct.id,
|
|
860
|
+
product: product,
|
|
861
|
+
productDetail: productDetail,
|
|
862
|
+
inventory: { id: inv.id },
|
|
863
|
+
packingType: inv.packing_type,
|
|
864
|
+
packingSize: inv.packing_size,
|
|
865
|
+
batchId: inv.batch_id,
|
|
866
|
+
releaseQty: inv.reserve_qty,
|
|
867
|
+
releaseUomValue: inv.reserve_uom_value,
|
|
868
|
+
uom: inv.uom,
|
|
869
|
+
type: sales_base_1.ORDER_TYPES.RELEASE_OF_GOODS,
|
|
870
|
+
status: orderInventoryStatus,
|
|
871
|
+
creator: user,
|
|
872
|
+
updater: user
|
|
873
|
+
}));
|
|
874
|
+
newOrderInventories.push(savedOrderInventory);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
// Create worksheet details for newly created order inventories if worksheets exist
|
|
878
|
+
if (existingWorksheets.length > 0 && newOrderInventories.length > 0) {
|
|
879
|
+
for (const worksheet of existingWorksheets) {
|
|
880
|
+
const worksheetDetails = newOrderInventories.map((oi) => ({
|
|
881
|
+
domain: domain,
|
|
882
|
+
bizplace: worksheet.bizplace || customerBizplace,
|
|
883
|
+
worksheet: worksheet,
|
|
884
|
+
name: worksheet_base_1.WorksheetNoGenerator.pickingDetail(),
|
|
885
|
+
type: worksheet_base_1.WORKSHEET_TYPE.PICKING,
|
|
886
|
+
status: worksheet_base_1.WORKSHEET_STATUS.DEACTIVATED,
|
|
887
|
+
targetInventory: oi,
|
|
888
|
+
creator: user,
|
|
889
|
+
updater: user
|
|
890
|
+
}));
|
|
891
|
+
await tx.getRepository(worksheet_base_1.WorksheetDetail).save(worksheetDetails);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
async function addNewOrderProduct(tx, orderProductReq, releaseGood, domain, user, context, hasOrderInventories) {
|
|
897
|
+
var _a, _b, _c;
|
|
898
|
+
const sku = ((_a = orderProductReq.product) === null || _a === void 0 ? void 0 : _a.sku) || orderProductReq.sku;
|
|
899
|
+
const refCode = ((_b = orderProductReq.product) === null || _b === void 0 ? void 0 : _b.refCode) || orderProductReq.refCode;
|
|
900
|
+
const packingType = orderProductReq.packingType;
|
|
901
|
+
const packingSize = orderProductReq.packingSize;
|
|
902
|
+
const releaseQty = orderProductReq.releaseQty !== undefined ? orderProductReq.releaseQty : orderProductReq.qty;
|
|
903
|
+
const refItemId = ((_c = orderProductReq.product) === null || _c === void 0 ? void 0 : _c.refItemId) || orderProductReq.refItemId || null;
|
|
904
|
+
if (!sku && !refCode) {
|
|
905
|
+
throw new error_util_1.ApiError('E01', 'sku or refCode is required for new items');
|
|
906
|
+
}
|
|
907
|
+
if (!releaseQty || releaseQty <= 0) {
|
|
908
|
+
throw new error_util_1.ApiError('E01', 'releaseQty must be greater than 0');
|
|
909
|
+
}
|
|
910
|
+
// Ensure releaseGood has an ID (required for TypeORM relation)
|
|
911
|
+
if (!releaseGood.id) {
|
|
912
|
+
throw new error_util_1.ApiError('E04', 'Release good ID is required');
|
|
913
|
+
}
|
|
914
|
+
// Get company bizplace for product lookup
|
|
915
|
+
const customerCompanyBizplace = await (0, biz_base_1.getCompanyBizplace)(null, null, releaseGood.bizplaceId, tx);
|
|
916
|
+
// Find product detail
|
|
917
|
+
const qb = tx
|
|
918
|
+
.getRepository(product_base_1.ProductDetail)
|
|
919
|
+
.createQueryBuilder('pd')
|
|
920
|
+
.innerJoinAndSelect('pd.product', 'prod')
|
|
921
|
+
.where('prod.bizplace_id = :bizplaceId', { bizplaceId: customerCompanyBizplace.id })
|
|
922
|
+
.andWhere('pd.deleted_at IS NULL');
|
|
923
|
+
if (refCode) {
|
|
924
|
+
qb.andWhere('pd.ref_code = :refCode', { refCode });
|
|
925
|
+
}
|
|
926
|
+
if (sku) {
|
|
927
|
+
qb.andWhere('prod.sku = :sku', { sku });
|
|
928
|
+
}
|
|
929
|
+
if (packingType) {
|
|
930
|
+
qb.andWhere('pd.packing_type = :packingType', { packingType });
|
|
931
|
+
}
|
|
932
|
+
if (packingSize) {
|
|
933
|
+
qb.andWhere('pd.packing_size = :packingSize', { packingSize });
|
|
934
|
+
}
|
|
935
|
+
if (!refCode && !packingType && !packingSize) {
|
|
936
|
+
qb.andWhere('pd.is_default = :isDefault', { isDefault: true });
|
|
937
|
+
}
|
|
938
|
+
const productDetail = await qb.getOne();
|
|
939
|
+
if (!productDetail) {
|
|
940
|
+
throw new error_util_1.ApiError('E04', `Product not found: ${sku || refCode}`);
|
|
941
|
+
}
|
|
942
|
+
// Validate decimal places
|
|
943
|
+
if (!productDetail.product.isInventoryDecimal && releaseQty % 1 !== 0) {
|
|
944
|
+
throw new error_util_1.ApiError('E01', `releaseQty must be an integer for product ${sku || refCode}`);
|
|
945
|
+
}
|
|
946
|
+
// Round to 3 decimal places
|
|
947
|
+
const roundedReleaseQty = productDetail.product.isInventoryDecimal
|
|
948
|
+
? Math.round(releaseQty * 1000) / 1000
|
|
949
|
+
: Math.round(releaseQty);
|
|
950
|
+
const releaseUomValue = roundedReleaseQty * (productDetail.uomValue || 1);
|
|
951
|
+
// Ensure bizplace is loaded - fetch it if not already loaded
|
|
952
|
+
let bizplace = releaseGood.bizplace;
|
|
953
|
+
if (!bizplace && releaseGood.bizplaceId) {
|
|
954
|
+
bizplace = await tx.getRepository(biz_base_1.Bizplace).findOne({ where: { id: releaseGood.bizplaceId } });
|
|
955
|
+
if (!bizplace) {
|
|
956
|
+
throw new error_util_1.ApiError('E04', 'Bizplace not found for release order');
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else if (!bizplace) {
|
|
960
|
+
throw new error_util_1.ApiError('E04', 'Bizplace is required for order product');
|
|
961
|
+
}
|
|
962
|
+
const repo = tx.getRepository(sales_base_1.OrderProduct);
|
|
963
|
+
const created = repo.create({
|
|
964
|
+
name: `OP-` + (0, uuid_1.v4)(),
|
|
965
|
+
// Use relation stubs (IDs only) to force FK persistence
|
|
966
|
+
domain: { id: domain.id },
|
|
967
|
+
bizplace: { id: bizplace.id },
|
|
968
|
+
releaseGood: { id: releaseGood.id },
|
|
969
|
+
releaseGoodId: releaseGood.id,
|
|
970
|
+
product: { id: productDetail.product.id },
|
|
971
|
+
productDetail: { id: productDetail.id },
|
|
972
|
+
packingType: productDetail.packingType,
|
|
973
|
+
packingSize: packingSize || productDetail.packingSize,
|
|
974
|
+
batchId: orderProductReq.batchId || '',
|
|
975
|
+
refItemId: refItemId,
|
|
976
|
+
releaseQty: roundedReleaseQty,
|
|
977
|
+
releaseUomValue: releaseUomValue,
|
|
978
|
+
uom: productDetail.uom,
|
|
979
|
+
uomValue: productDetail.uomValue,
|
|
980
|
+
packQty: 0,
|
|
981
|
+
actualPackQty: 0,
|
|
982
|
+
palletQty: 0,
|
|
983
|
+
actualPalletQty: 0,
|
|
984
|
+
type: sales_base_1.ORDER_TYPES.RELEASE_OF_GOODS,
|
|
985
|
+
status: sales_base_1.ORDER_PRODUCT_STATUS.ASSIGNED,
|
|
986
|
+
unitPrice: orderProductReq.unitPrice || null,
|
|
987
|
+
remark: orderProductReq.remark || null,
|
|
988
|
+
creator: user,
|
|
989
|
+
updater: user
|
|
990
|
+
});
|
|
991
|
+
const newOrderProduct = await repo.save(created);
|
|
992
|
+
if (hasOrderInventories) {
|
|
993
|
+
// Assign inventory - pass releaseGood so status can be determined
|
|
994
|
+
await assignAdditionalInventory(tx, newOrderProduct, roundedReleaseQty, releaseUomValue, user, releaseGood, domain);
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
// Update ProductDetailStock
|
|
998
|
+
await tx
|
|
999
|
+
.getRepository(warehouse_base_1.ProductDetailStock)
|
|
1000
|
+
.createQueryBuilder()
|
|
1001
|
+
.update(warehouse_base_1.ProductDetailStock)
|
|
1002
|
+
.set({
|
|
1003
|
+
unassignedQty: () => `"unassigned_qty" + ${roundedReleaseQty}`,
|
|
1004
|
+
unassignedUomValue: () => `"unassigned_uom_value" + ${releaseUomValue}`
|
|
1005
|
+
})
|
|
1006
|
+
.where({ productDetail: productDetail.id })
|
|
1007
|
+
.execute();
|
|
1008
|
+
}
|
|
82
1009
|
}
|
|
83
1010
|
//# sourceMappingURL=update-release-order-details.js.map
|