@unchainedshop/core 5.0.0-alpha.10 → 5.0.0-alpha.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/lib/bulk-exporter/handlers/exportUsersHandler.js +1 -0
- package/lib/directors/BasePricingSheet.d.ts +2 -0
- package/lib/directors/DeliveryPricingSheet.js +9 -4
- package/lib/directors/EnrollmentAdapter.d.ts +11 -1
- package/lib/directors/EnrollmentAdapter.js +7 -1
- package/lib/directors/EnrollmentDirector.d.ts +2 -1
- package/lib/directors/EnrollmentDirector.js +6 -4
- package/lib/directors/OrderPricingSheet.js +6 -7
- package/lib/directors/PaymentPricingSheet.js +9 -4
- package/lib/directors/ProductPricingAdapter.d.ts +1 -0
- package/lib/directors/ProductPricingDirector.js +1 -0
- package/lib/directors/ProductPricingSheet.js +7 -4
- package/lib/directors/QuotationAdapter.js +5 -2
- package/lib/factory/registerEnrollment.d.ts +8 -1
- package/lib/factory/registerEnrollment.js +12 -14
- package/lib/factory/registerQuotation.d.ts +2 -1
- package/lib/factory/registerQuotation.js +3 -2
- package/lib/plugins/PluginRegistry.d.ts +1 -1
- package/lib/plugins/PluginRegistry.js +8 -2
- package/lib/services/addCartQuotation.d.ts +9 -13
- package/lib/services/addCartQuotation.js +43 -38
- package/lib/services/calculateDiscountTotal.d.ts +5 -1
- package/lib/services/calculateDiscountTotal.js +36 -28
- package/lib/services/checkoutOrder.js +4 -0
- package/lib/services/index.d.ts +8 -0
- package/lib/services/index.js +8 -0
- package/lib/services/initializeEnrollment.d.ts +1 -1
- package/lib/services/initializeEnrollment.js +11 -0
- package/lib/services/migrateOrderCart.js +2 -2
- package/lib/services/migrateUserData.js +3 -0
- package/lib/services/processEnrollment.js +6 -3
- package/lib/services/processOrder.js +4 -4
- package/lib/services/reserveOrderQuotationsForCheckout.d.ts +8 -0
- package/lib/services/reserveOrderQuotationsForCheckout.js +49 -0
- package/lib/services/simulateConfigurablePriceRange.js +1 -1
- package/lib/services/simulateDeliveryPricing.js +1 -1
- package/lib/services/simulatePaymentPricing.js +1 -1
- package/lib/services/suspendEnrollment.d.ts +5 -0
- package/lib/services/suspendEnrollment.js +21 -0
- package/lib/services/terminateEnrollment.d.ts +6 -2
- package/lib/services/terminateEnrollment.js +27 -7
- package/lib/services/updateCartItem.d.ts +7 -7
- package/lib/services/updateCartItem.js +28 -25
- package/lib/services/updateEnrollmentPlan.d.ts +5 -0
- package/lib/services/updateEnrollmentPlan.js +26 -0
- package/lib/services/validateOrder.js +7 -2
- package/package.json +22 -22
- package/lib/errors/common.d.ts +0 -568
- package/lib/errors/common.js +0 -789
- package/lib/errors/index.d.ts +0 -1
- package/lib/errors/index.js +0 -1
- package/lib/services/addCartDiscount.d.ts +0 -10
- package/lib/services/addCartDiscount.js +0 -27
- package/lib/services/addCartItem.d.ts +0 -15
- package/lib/services/addCartItem.js +0 -41
- package/lib/services/addEmail.d.ts +0 -7
- package/lib/services/addEmail.js +0 -17
- package/lib/services/addProductAssignment.d.ts +0 -3
- package/lib/services/addProductAssignment.js +0 -63
- package/lib/services/authenticateWithPassword.d.ts +0 -15
- package/lib/services/authenticateWithPassword.js +0 -34
- package/lib/services/changePassword.d.ts +0 -10
- package/lib/services/changePassword.js +0 -19
- package/lib/services/countOrdersWithFilters.d.ts +0 -3
- package/lib/services/countOrdersWithFilters.js +0 -25
- package/lib/services/createAssortment.d.ts +0 -7
- package/lib/services/createAssortment.js +0 -10
- package/lib/services/createBookmark.d.ts +0 -8
- package/lib/services/createBookmark.js +0 -21
- package/lib/services/createDeliveryProvider.d.ts +0 -6
- package/lib/services/createDeliveryProvider.js +0 -19
- package/lib/services/createEnrollment.d.ts +0 -17
- package/lib/services/createEnrollment.js +0 -33
- package/lib/services/createProduct.d.ts +0 -7
- package/lib/services/createProduct.js +0 -8
- package/lib/services/createProductBundleItem.d.ts +0 -5
- package/lib/services/createProductBundleItem.js +0 -25
- package/lib/services/createProductReview.d.ts +0 -8
- package/lib/services/createProductReview.js +0 -12
- package/lib/services/deleteDeadCarts.d.ts +0 -2
- package/lib/services/deleteDeadCarts.js +0 -17
- package/lib/services/deliverOrder.d.ts +0 -5
- package/lib/services/deliverOrder.js +0 -26
- package/lib/services/findActiveWorkTypes.d.ts +0 -2
- package/lib/services/findActiveWorkTypes.js +0 -6
- package/lib/services/findDeliveryInterfaces.d.ts +0 -7
- package/lib/services/findDeliveryInterfaces.js +0 -13
- package/lib/services/findOrdersWithFilters.d.ts +0 -9
- package/lib/services/findOrdersWithFilters.js +0 -25
- package/lib/services/findPaymentInterfaces.d.ts +0 -7
- package/lib/services/findPaymentInterfaces.js +0 -13
- package/lib/services/findWarehousingInterfaces.d.ts +0 -7
- package/lib/services/findWarehousingInterfaces.js +0 -13
- package/lib/services/getDeliveryDiscounts.d.ts +0 -8
- package/lib/services/getDeliveryDiscounts.js +0 -14
- package/lib/services/getPaymentDiscounts.d.ts +0 -8
- package/lib/services/getPaymentDiscounts.js +0 -14
- package/lib/services/getShopInfo.d.ts +0 -25
- package/lib/services/getShopInfo.js +0 -47
- package/lib/services/impersonateUser.d.ts +0 -8
- package/lib/services/impersonateUser.js +0 -20
- package/lib/services/loginAsGuest.d.ts +0 -10
- package/lib/services/loginAsGuest.js +0 -19
- package/lib/services/obfuscateWorkerData.d.ts +0 -4
- package/lib/services/obfuscateWorkerData.js +0 -4
- package/lib/services/orderPricingAccessors.d.ts +0 -18
- package/lib/services/orderPricingAccessors.js +0 -29
- package/lib/services/payOrder.d.ts +0 -5
- package/lib/services/payOrder.js +0 -25
- package/lib/services/registerUser.d.ts +0 -14
- package/lib/services/registerUser.js +0 -44
- package/lib/services/removeBookmark.d.ts +0 -6
- package/lib/services/removeBookmark.js +0 -10
- package/lib/services/removeCartItem.d.ts +0 -6
- package/lib/services/removeCartItem.js +0 -19
- package/lib/services/resetPasswordWithMigration.d.ts +0 -14
- package/lib/services/resetPasswordWithMigration.js +0 -31
- package/lib/services/resolveWorkType.d.ts +0 -1
- package/lib/services/resolveWorkType.js +0 -7
- package/lib/services/searchProductsWithAssortment.d.ts +0 -19
- package/lib/services/searchProductsWithAssortment.js +0 -17
- package/lib/services/toggleBookmark.d.ts +0 -8
- package/lib/services/toggleBookmark.js +0 -33
- package/lib/services/updateCart.d.ts +0 -15
- package/lib/services/updateCart.js +0 -27
- package/lib/services/updateEnrollment.d.ts +0 -13
- package/lib/services/updateEnrollment.js +0 -36
- package/lib/services/verifyEmailWithMigration.d.ts +0 -15
- package/lib/services/verifyEmailWithMigration.js +0 -23
|
@@ -1,44 +1,52 @@
|
|
|
1
1
|
import { OrderPricingRowCategory, OrderPricingSheet, DeliveryPricingRowCategory, DeliveryPricingSheet, ProductPricingRowCategory, ProductPricingSheet, PaymentPricingRowCategory, PaymentPricingSheet, } from "../directors/index.js";
|
|
2
|
-
export async function calculateDiscountTotalService(order, orderDiscount) {
|
|
3
|
-
const
|
|
2
|
+
export async function calculateDiscountTotalService(order, orderDiscount, { useNetPrice = false } = {}) {
|
|
3
|
+
const discountId = orderDiscount._id;
|
|
4
4
|
const orderDelivery = await this.orders.deliveries.findDelivery({
|
|
5
5
|
orderDeliveryId: order.deliveryId,
|
|
6
6
|
});
|
|
7
|
-
const orderDeliveryDiscountSum = DeliveryPricingSheet({
|
|
8
|
-
calculation: orderDelivery?.calculation || [],
|
|
9
|
-
currencyCode: order.currencyCode,
|
|
10
|
-
}).total({ category: DeliveryPricingRowCategory.Discount, discountId: orderDiscountId });
|
|
11
7
|
const orderPayment = await this.orders.payments.findOrderPayment({
|
|
12
8
|
orderPaymentId: order.paymentId,
|
|
13
9
|
});
|
|
14
|
-
const orderPaymentDiscountSum = PaymentPricingSheet({
|
|
15
|
-
calculation: orderPayment?.calculation || [],
|
|
16
|
-
currencyCode: order.currencyCode,
|
|
17
|
-
}).total({ category: PaymentPricingRowCategory.Discount, discountId: orderDiscountId });
|
|
18
10
|
const orderPositions = await this.orders.positions.findOrderPositions({
|
|
19
11
|
orderId: order._id,
|
|
20
12
|
});
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
...
|
|
37
|
-
|
|
13
|
+
const discountedSheets = [
|
|
14
|
+
{
|
|
15
|
+
category: DeliveryPricingRowCategory.Discount,
|
|
16
|
+
sheet: DeliveryPricingSheet({
|
|
17
|
+
calculation: orderDelivery?.calculation || [],
|
|
18
|
+
currencyCode: order.currencyCode,
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
category: PaymentPricingRowCategory.Discount,
|
|
23
|
+
sheet: PaymentPricingSheet({
|
|
24
|
+
calculation: orderPayment?.calculation || [],
|
|
25
|
+
currencyCode: order.currencyCode,
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
...orderPositions.map((orderPosition) => ({
|
|
29
|
+
category: ProductPricingRowCategory.Discount,
|
|
30
|
+
sheet: ProductPricingSheet({
|
|
31
|
+
calculation: orderPosition.calculation || [],
|
|
32
|
+
currencyCode: order.currencyCode,
|
|
33
|
+
quantity: orderPosition.quantity,
|
|
34
|
+
}),
|
|
35
|
+
})),
|
|
36
|
+
{
|
|
37
|
+
category: OrderPricingRowCategory.Discounts,
|
|
38
|
+
sheet: OrderPricingSheet({
|
|
39
|
+
calculation: order.calculation,
|
|
40
|
+
currencyCode: order.currencyCode,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
38
43
|
];
|
|
39
|
-
const amount =
|
|
44
|
+
const amount = discountedSheets.reduce((sum, { category, sheet }) => sum + (sheet.total({ category, discountId, useNetPrice }).amount || 0), 0);
|
|
45
|
+
const taxAmount = discountedSheets.reduce((sum, { category, sheet }) => sum + sheet.taxSum({ baseCategory: category, discountId }), 0);
|
|
40
46
|
return {
|
|
41
47
|
amount,
|
|
42
48
|
currencyCode: order.currencyCode,
|
|
49
|
+
isTaxable: taxAmount !== 0,
|
|
50
|
+
isNetPrice: useNetPrice,
|
|
43
51
|
};
|
|
44
52
|
}
|
|
@@ -3,6 +3,7 @@ import { nextUserCartService } from "./nextUserCart.js";
|
|
|
3
3
|
import { validateOrderService } from "./validateOrder.js";
|
|
4
4
|
import { processOrderService } from "./processOrder.js";
|
|
5
5
|
import { reserveOrderDiscountsForCheckout } from "./reserveOrderDiscountsForCheckout.js";
|
|
6
|
+
import { reserveOrderQuotationsForCheckout } from "./reserveOrderQuotationsForCheckout.js";
|
|
6
7
|
export async function checkoutOrderService(orderId, transactionContext) {
|
|
7
8
|
const order = await this.orders.findOrder({ orderId });
|
|
8
9
|
if (!order)
|
|
@@ -12,7 +13,9 @@ export async function checkoutOrderService(orderId, transactionContext) {
|
|
|
12
13
|
await validateOrderService.bind(this)(order);
|
|
13
14
|
const lock = await this.orders.acquireLock(order._id, 'checkout');
|
|
14
15
|
let discounts;
|
|
16
|
+
let quotations;
|
|
15
17
|
try {
|
|
18
|
+
quotations = await reserveOrderQuotationsForCheckout.call(this, order);
|
|
16
19
|
discounts = await reserveOrderDiscountsForCheckout.call(this, order);
|
|
17
20
|
const processedOrder = await processOrderService.bind(this)(order, transactionContext);
|
|
18
21
|
if (processedOrder.billingAddress) {
|
|
@@ -31,6 +34,7 @@ export async function checkoutOrderService(orderId, transactionContext) {
|
|
|
31
34
|
}
|
|
32
35
|
finally {
|
|
33
36
|
await discounts?.release();
|
|
37
|
+
await quotations?.release();
|
|
34
38
|
await lock.release();
|
|
35
39
|
}
|
|
36
40
|
}
|
package/lib/services/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ import { rejectOrderService } from './rejectOrder.ts';
|
|
|
31
31
|
import { discountedEntitiesService } from './discountedEntities.ts';
|
|
32
32
|
import { createManualOrderDiscountService } from './createManualOrderDiscount.ts';
|
|
33
33
|
import { initializeEnrollmentService } from './initializeEnrollment.ts';
|
|
34
|
+
import { suspendEnrollmentService } from './suspendEnrollment.ts';
|
|
35
|
+
import { updateEnrollmentPlanService } from './updateEnrollmentPlan.ts';
|
|
34
36
|
import { activateEnrollmentService } from './activateEnrollment.ts';
|
|
35
37
|
import { terminateEnrollmentService } from './terminateEnrollment.ts';
|
|
36
38
|
import { invalidateFilterCacheService } from './invalidateFilterCache.ts';
|
|
@@ -48,6 +50,8 @@ import { createFilterOptionService } from './createFilterOption.ts';
|
|
|
48
50
|
import { removeFilterOptionService } from './removeFilterOption.ts';
|
|
49
51
|
import { removeCartDiscountService } from './removeCartDiscount.ts';
|
|
50
52
|
import { addMultipleCartProductsService } from './addMultipleCartProducts.ts';
|
|
53
|
+
import { addCartQuotationService } from './addCartQuotation.ts';
|
|
54
|
+
import { updateCartItemService } from './updateCartItem.ts';
|
|
51
55
|
import { ercMetadataService } from './ercMetadata.ts';
|
|
52
56
|
import { simulateProductPricingService } from './simulateProductPricing.ts';
|
|
53
57
|
import { simulateProductDispatchingService } from './simulateProductDispatching.ts';
|
|
@@ -105,6 +109,8 @@ export default function initServices(modules: Modules, customServices?: CustomSe
|
|
|
105
109
|
resolveOrderItemDispatches: Bound<typeof resolveOrderItemDispatchesService>;
|
|
106
110
|
removeCartDiscount: Bound<typeof removeCartDiscountService>;
|
|
107
111
|
addMultipleCartProducts: Bound<typeof addMultipleCartProductsService>;
|
|
112
|
+
addCartQuotation: Bound<typeof addCartQuotationService>;
|
|
113
|
+
updateCartItem: Bound<typeof updateCartItemService>;
|
|
108
114
|
};
|
|
109
115
|
products: {
|
|
110
116
|
simulateProductPricing: Bound<typeof simulateProductPricingService>;
|
|
@@ -131,6 +137,8 @@ export default function initServices(modules: Modules, customServices?: CustomSe
|
|
|
131
137
|
initializeEnrollment: Bound<typeof initializeEnrollmentService>;
|
|
132
138
|
activateEnrollment: Bound<typeof activateEnrollmentService>;
|
|
133
139
|
terminateEnrollment: Bound<typeof terminateEnrollmentService>;
|
|
140
|
+
suspendEnrollment: Bound<typeof suspendEnrollmentService>;
|
|
141
|
+
updateEnrollmentPlan: Bound<typeof updateEnrollmentPlanService>;
|
|
134
142
|
};
|
|
135
143
|
quotations: {
|
|
136
144
|
fulfillQuotation: Bound<typeof fulfillQuotationService>;
|
package/lib/services/index.js
CHANGED
|
@@ -30,6 +30,8 @@ import { rejectOrderService } from "./rejectOrder.js";
|
|
|
30
30
|
import { discountedEntitiesService } from "./discountedEntities.js";
|
|
31
31
|
import { createManualOrderDiscountService } from "./createManualOrderDiscount.js";
|
|
32
32
|
import { initializeEnrollmentService } from "./initializeEnrollment.js";
|
|
33
|
+
import { suspendEnrollmentService } from "./suspendEnrollment.js";
|
|
34
|
+
import { updateEnrollmentPlanService } from "./updateEnrollmentPlan.js";
|
|
33
35
|
import { activateEnrollmentService } from "./activateEnrollment.js";
|
|
34
36
|
import { terminateEnrollmentService } from "./terminateEnrollment.js";
|
|
35
37
|
import { invalidateFilterCacheService } from "./invalidateFilterCache.js";
|
|
@@ -47,6 +49,8 @@ import { createFilterOptionService } from "./createFilterOption.js";
|
|
|
47
49
|
import { removeFilterOptionService } from "./removeFilterOption.js";
|
|
48
50
|
import { removeCartDiscountService } from "./removeCartDiscount.js";
|
|
49
51
|
import { addMultipleCartProductsService } from "./addMultipleCartProducts.js";
|
|
52
|
+
import { addCartQuotationService } from "./addCartQuotation.js";
|
|
53
|
+
import { updateCartItemService } from "./updateCartItem.js";
|
|
50
54
|
import { ercMetadataService } from "./ercMetadata.js";
|
|
51
55
|
import { simulateProductPricingService } from "./simulateProductPricing.js";
|
|
52
56
|
import { simulateProductDispatchingService } from "./simulateProductDispatching.js";
|
|
@@ -116,6 +120,8 @@ export default function initServices(modules, customServices = {}) {
|
|
|
116
120
|
resolveOrderItemDispatches: resolveOrderItemDispatchesService,
|
|
117
121
|
removeCartDiscount: removeCartDiscountService,
|
|
118
122
|
addMultipleCartProducts: addMultipleCartProductsService,
|
|
123
|
+
addCartQuotation: addCartQuotationService,
|
|
124
|
+
updateCartItem: updateCartItemService,
|
|
119
125
|
},
|
|
120
126
|
products: {
|
|
121
127
|
simulateProductPricing: simulateProductPricingService,
|
|
@@ -142,6 +148,8 @@ export default function initServices(modules, customServices = {}) {
|
|
|
142
148
|
initializeEnrollment: initializeEnrollmentService,
|
|
143
149
|
activateEnrollment: activateEnrollmentService,
|
|
144
150
|
terminateEnrollment: terminateEnrollmentService,
|
|
151
|
+
suspendEnrollment: suspendEnrollmentService,
|
|
152
|
+
updateEnrollmentPlan: updateEnrollmentPlanService,
|
|
145
153
|
},
|
|
146
154
|
quotations: {
|
|
147
155
|
fulfillQuotation: fulfillQuotationService,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Enrollment } from '@unchainedshop/core-enrollments';
|
|
2
2
|
import type { Modules } from '../modules.ts';
|
|
3
3
|
export declare function initializeEnrollmentService(this: Modules, enrollment: Enrollment, params: {
|
|
4
4
|
orderIdForFirstPeriod?: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addToDate } from '@unchainedshop/core-enrollments';
|
|
1
2
|
import { EnrollmentDirector } from "../core-index.js";
|
|
2
3
|
import { processEnrollmentService } from "./processEnrollment.js";
|
|
3
4
|
import { addMessageService } from "./addMessage.js";
|
|
@@ -14,6 +15,16 @@ export async function initializeEnrollmentService(enrollment, params) {
|
|
|
14
15
|
orderId: params.orderIdForFirstPeriod,
|
|
15
16
|
}));
|
|
16
17
|
}
|
|
18
|
+
const { minimumCommitmentPeriods, billingInterval, billingIntervalCount } = product?.plan || {};
|
|
19
|
+
if (minimumCommitmentPeriods && billingInterval) {
|
|
20
|
+
const contractStartDate = updatedEnrollment.periods.find(({ isTrial }) => !isTrial)?.start || new Date();
|
|
21
|
+
updatedEnrollment = (await this.enrollments.updateCommitment(enrollment._id, {
|
|
22
|
+
contractStartDate,
|
|
23
|
+
minimumCommitmentEnd: addToDate(contractStartDate, {
|
|
24
|
+
[billingInterval.toLowerCase()]: (billingIntervalCount || 1) * minimumCommitmentPeriods,
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
17
28
|
const processedEnrollment = await processEnrollmentService.bind(this)(updatedEnrollment);
|
|
18
29
|
const user = await this.users.findUserById(enrollment.userId);
|
|
19
30
|
const locale = this.users.userLocale(user);
|
|
@@ -12,10 +12,10 @@ export async function migrateOrderCartsService({ fromUserId, toUserId, shouldMer
|
|
|
12
12
|
return toCart;
|
|
13
13
|
}
|
|
14
14
|
if (!toCart || !shouldMerge) {
|
|
15
|
-
this.orders.setCartOwner({ orderId: fromCart._id, userId: toUserId });
|
|
15
|
+
await this.orders.setCartOwner({ orderId: fromCart._id, userId: toUserId });
|
|
16
16
|
return updateCalculationService.bind(this)(fromCart._id);
|
|
17
17
|
}
|
|
18
|
-
this.orders.moveCartPositions({ fromOrderId: fromCart._id, toOrderId: toCart._id });
|
|
18
|
+
await this.orders.moveCartPositions({ fromOrderId: fromCart._id, toOrderId: toCart._id });
|
|
19
19
|
if (fromCart.billingAddress && !toCart.billingAddress) {
|
|
20
20
|
await this.orders.updateBillingAddress(toCart._id, fromCart.billingAddress);
|
|
21
21
|
}
|
|
@@ -4,6 +4,9 @@ import { migrateOrderCartsService } from "./migrateOrderCart.js";
|
|
|
4
4
|
export async function migrateUserDataService(userIdBeforeLogin, userId) {
|
|
5
5
|
const user = await this.users.findUserById(userId);
|
|
6
6
|
const userBeforeLogin = await this.users.findUserById(userIdBeforeLogin);
|
|
7
|
+
if (userBeforeLogin?.guest) {
|
|
8
|
+
await this.quotations.replaceUserIdOfOpenQuotations(userIdBeforeLogin, userId);
|
|
9
|
+
}
|
|
7
10
|
await migrateOrderCartsService.bind(this)({
|
|
8
11
|
fromUserId: userIdBeforeLogin,
|
|
9
12
|
toUserId: userId,
|
|
@@ -3,6 +3,12 @@ import { EnrollmentDirector } from "../core-index.js";
|
|
|
3
3
|
import { createServiceError } from "../errors.js";
|
|
4
4
|
const findNextStatus = async (enrollment, modules) => {
|
|
5
5
|
let status = enrollment.status;
|
|
6
|
+
if (modules.enrollments.isExpired(enrollment, {}))
|
|
7
|
+
return EnrollmentStatus.TERMINATED;
|
|
8
|
+
if (status === EnrollmentStatus.SUSPENDED) {
|
|
9
|
+
const resumeDue = enrollment.resumeAt && new Date(enrollment.resumeAt).getTime() <= Date.now();
|
|
10
|
+
return resumeDue ? EnrollmentStatus.ACTIVE : status;
|
|
11
|
+
}
|
|
6
12
|
const product = await modules.products.findProduct({
|
|
7
13
|
productId: enrollment.productId,
|
|
8
14
|
});
|
|
@@ -19,9 +25,6 @@ const findNextStatus = async (enrollment, modules) => {
|
|
|
19
25
|
status = EnrollmentStatus.PAUSED;
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
|
-
else if (modules.enrollments.isExpired(enrollment, {})) {
|
|
23
|
-
status = EnrollmentStatus.TERMINATED;
|
|
24
|
-
}
|
|
25
28
|
return status;
|
|
26
29
|
};
|
|
27
30
|
export async function processEnrollmentService(enrollment) {
|
|
@@ -7,14 +7,14 @@ import { fulfillQuotationService } from "./fulfillQuotation.js";
|
|
|
7
7
|
import { createLogger } from '@unchainedshop/logger';
|
|
8
8
|
import { createServiceError } from "../errors.js";
|
|
9
9
|
const logger = createLogger('unchained:core:processOrder');
|
|
10
|
-
const isAutoConfirmationEnabled = async ({ orderPayment, orderDelivery, }, modules) => {
|
|
10
|
+
const isAutoConfirmationEnabled = async ({ order, orderPayment, orderDelivery, }, modules) => {
|
|
11
11
|
if (orderPayment.status !== OrderPaymentStatus.PAID) {
|
|
12
12
|
const paymentProvider = await modules.payment.paymentProviders.findProvider({
|
|
13
13
|
paymentProviderId: orderPayment.paymentProviderId,
|
|
14
14
|
});
|
|
15
15
|
if (!paymentProvider)
|
|
16
16
|
throw createServiceError('PaymentProviderNotFoundError', `Payment provider not found: ${orderPayment.paymentProviderId}`);
|
|
17
|
-
const actions = await PaymentDirector.actions(paymentProvider, {}, { modules });
|
|
17
|
+
const actions = await PaymentDirector.actions(paymentProvider, { order, orderPayment, userId: order.userId }, { modules });
|
|
18
18
|
if (!actions.isPayLaterAllowed())
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
@@ -24,7 +24,7 @@ const isAutoConfirmationEnabled = async ({ orderPayment, orderDelivery, }, modul
|
|
|
24
24
|
});
|
|
25
25
|
if (!deliveryProvider)
|
|
26
26
|
throw createServiceError('DeliveryProviderNotFoundError', `Delivery provider not found: ${orderDelivery.deliveryProviderId}`);
|
|
27
|
-
const director = await DeliveryDirector.actions(deliveryProvider, {}, { modules });
|
|
27
|
+
const director = await DeliveryDirector.actions(deliveryProvider, { order, orderDelivery }, { modules });
|
|
28
28
|
if (!director.isAutoReleaseAllowed())
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
@@ -50,7 +50,7 @@ const findNextStatus = async (status, order, modules) => {
|
|
|
50
50
|
if (!orderDelivery)
|
|
51
51
|
return status;
|
|
52
52
|
if (status === OrderStatus.PENDING) {
|
|
53
|
-
if (await isAutoConfirmationEnabled({ orderPayment, orderDelivery }, modules)) {
|
|
53
|
+
if (await isAutoConfirmationEnabled({ order, orderPayment, orderDelivery }, modules)) {
|
|
54
54
|
return OrderStatus.CONFIRMED;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Order } from '@unchainedshop/core-orders';
|
|
2
|
+
import type { Modules } from '../modules.ts';
|
|
3
|
+
export declare function isQuotationReserved(this: Modules, quotationId: string, { exceptOrderId }?: {
|
|
4
|
+
exceptOrderId?: string;
|
|
5
|
+
}): Promise<boolean>;
|
|
6
|
+
export declare function reserveOrderQuotationsForCheckout(this: Modules, order: Order): Promise<{
|
|
7
|
+
release: () => Promise<void>;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { OrderStatus } from '@unchainedshop/core-orders';
|
|
2
|
+
import { createServiceError } from "../errors.js";
|
|
3
|
+
const RESERVATION_LOCK_TTL = 60000;
|
|
4
|
+
export async function isQuotationReserved(quotationId, { exceptOrderId } = {}) {
|
|
5
|
+
const orderIds = (await this.orders.positions.findOrderPositions({ quotationId }))
|
|
6
|
+
.map(({ orderId }) => orderId)
|
|
7
|
+
.filter((orderId) => orderId !== exceptOrderId);
|
|
8
|
+
if (!orderIds.length)
|
|
9
|
+
return false;
|
|
10
|
+
const count = await this.orders.count({
|
|
11
|
+
orderIds,
|
|
12
|
+
status: [OrderStatus.PENDING, OrderStatus.CONFIRMED, OrderStatus.FULFILLED],
|
|
13
|
+
});
|
|
14
|
+
return count > 0;
|
|
15
|
+
}
|
|
16
|
+
export async function reserveOrderQuotationsForCheckout(order) {
|
|
17
|
+
const locks = [];
|
|
18
|
+
const release = async () => {
|
|
19
|
+
await Promise.all(locks.map((lock) => lock.release()));
|
|
20
|
+
};
|
|
21
|
+
try {
|
|
22
|
+
const positions = await this.orders.positions.findOrderPositions({ orderId: order._id });
|
|
23
|
+
const quotationIds = [
|
|
24
|
+
...new Set(positions.map(({ quotationId }) => quotationId).filter(Boolean)),
|
|
25
|
+
].sort();
|
|
26
|
+
for (const quotationId of quotationIds) {
|
|
27
|
+
try {
|
|
28
|
+
locks.push(await this.orders.acquireLock(quotationId, 'quotation', RESERVATION_LOCK_TTL));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (!error?.message?.startsWith('Could not acquire lock'))
|
|
32
|
+
throw error;
|
|
33
|
+
throw createServiceError('QuotationInvalidError', 'Quotation is being ordered by another order');
|
|
34
|
+
}
|
|
35
|
+
const quotation = await this.quotations.findQuotation({ quotationId });
|
|
36
|
+
if (!quotation || !this.quotations.isProposalValid(quotation)) {
|
|
37
|
+
throw createServiceError('QuotationInvalidError', 'Quotation expired, fulfilled or not valid for this order, please request a new offer');
|
|
38
|
+
}
|
|
39
|
+
if (await isQuotationReserved.call(this, quotationId, { exceptOrderId: order._id })) {
|
|
40
|
+
throw createServiceError('QuotationInvalidError', 'Quotation is reserved by another order');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { release };
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
await release();
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -22,7 +22,7 @@ export async function simulateConfigurablePriceRangeService({ product, currencyC
|
|
|
22
22
|
return {
|
|
23
23
|
...unitPrice,
|
|
24
24
|
isNetPrice: useNetPrice,
|
|
25
|
-
isTaxable: pricing.taxSum()
|
|
25
|
+
isTaxable: pricing.taxSum() !== 0,
|
|
26
26
|
currencyCode: pricing.currencyCode,
|
|
27
27
|
};
|
|
28
28
|
}))).filter(Boolean);
|
|
@@ -17,7 +17,7 @@ export async function simulateDeliveryPricingService(pricingContext, options = {
|
|
|
17
17
|
amount: orderPrice.amount,
|
|
18
18
|
currencyCode: orderPrice.currencyCode,
|
|
19
19
|
countryCode: pricingContext.countryCode,
|
|
20
|
-
isTaxable: pricing.taxSum()
|
|
20
|
+
isTaxable: pricing.taxSum() !== 0,
|
|
21
21
|
isNetPrice: options.useNetPrice || false,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -17,7 +17,7 @@ export async function simulatePaymentPricingService(pricingContext, options = {}
|
|
|
17
17
|
amount: orderPrice.amount,
|
|
18
18
|
currencyCode: orderPrice.currencyCode,
|
|
19
19
|
countryCode: pricingContext.countryCode,
|
|
20
|
-
isTaxable: pricing.taxSum()
|
|
20
|
+
isTaxable: pricing.taxSum() !== 0,
|
|
21
21
|
isNetPrice: options.useNetPrice || false,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EnrollmentStatus } from '@unchainedshop/core-enrollments';
|
|
2
|
+
import { addMessageService } from "./addMessage.js";
|
|
3
|
+
export async function suspendEnrollmentService(enrollment, { resumeAt } = {}) {
|
|
4
|
+
if (enrollment.status === EnrollmentStatus.TERMINATED)
|
|
5
|
+
return enrollment;
|
|
6
|
+
let updatedEnrollment = (await this.enrollments.updateStatus(enrollment._id, {
|
|
7
|
+
status: EnrollmentStatus.SUSPENDED,
|
|
8
|
+
info: 'suspended manually',
|
|
9
|
+
}));
|
|
10
|
+
if (resumeAt) {
|
|
11
|
+
updatedEnrollment = (await this.enrollments.updateResumeAt(enrollment._id, resumeAt));
|
|
12
|
+
}
|
|
13
|
+
const user = await this.users.findUserById(enrollment.userId);
|
|
14
|
+
const locale = this.users.userLocale(user);
|
|
15
|
+
await addMessageService.bind(this)('ENROLLMENT_STATUS', {
|
|
16
|
+
reason: 'status_change',
|
|
17
|
+
locale: locale.baseName,
|
|
18
|
+
enrollmentId: updatedEnrollment._id,
|
|
19
|
+
});
|
|
20
|
+
return updatedEnrollment;
|
|
21
|
+
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import { type Enrollment } from '@unchainedshop/core-enrollments';
|
|
2
|
-
|
|
1
|
+
import { type Enrollment, type EnrollmentTerminationReason } from '@unchainedshop/core-enrollments';
|
|
2
|
+
import type { Modules } from '../modules.ts';
|
|
3
|
+
export declare function terminateEnrollmentService(this: Modules, enrollment: Enrollment, { reason, comment }?: {
|
|
4
|
+
reason?: EnrollmentTerminationReason;
|
|
5
|
+
comment?: string;
|
|
6
|
+
}): Promise<Enrollment>;
|
|
@@ -1,14 +1,34 @@
|
|
|
1
|
-
import { EnrollmentStatus } from '@unchainedshop/core-enrollments';
|
|
1
|
+
import { EnrollmentStatus, } from '@unchainedshop/core-enrollments';
|
|
2
2
|
import { processEnrollmentService } from "./processEnrollment.js";
|
|
3
3
|
import { addMessageService } from "./addMessage.js";
|
|
4
|
-
|
|
4
|
+
import { EnrollmentDirector } from "../core-index.js";
|
|
5
|
+
import { createServiceError } from "../errors.js";
|
|
6
|
+
export async function terminateEnrollmentService(enrollment, { reason, comment } = {}) {
|
|
5
7
|
if (enrollment.status === EnrollmentStatus.TERMINATED)
|
|
6
8
|
return enrollment;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
11
|
-
|
|
9
|
+
const product = await this.products.findProduct({ productId: enrollment.productId });
|
|
10
|
+
if (!product)
|
|
11
|
+
throw createServiceError('ProductNotFoundError', 'Product not found for enrollment');
|
|
12
|
+
const director = await EnrollmentDirector.actions({ enrollment, product }, { modules: this });
|
|
13
|
+
const terminationDate = await director.terminationDate({ referenceDate: new Date() });
|
|
14
|
+
if (!terminationDate) {
|
|
15
|
+
throw createServiceError('EnrollmentTerminationNotAllowedError', 'Enrollment termination is not allowed at this time');
|
|
16
|
+
}
|
|
17
|
+
const expires = new Date(Math.min(Math.max(terminationDate.getTime(), enrollment.minimumCommitmentEnd?.getTime() ?? 0), enrollment.expires?.getTime() ?? Infinity));
|
|
18
|
+
if (reason || comment) {
|
|
19
|
+
await this.enrollments.updateCancellation(enrollment._id, { reason, comment });
|
|
20
|
+
}
|
|
21
|
+
let updatedEnrollment;
|
|
22
|
+
if (expires.getTime() > Date.now()) {
|
|
23
|
+
updatedEnrollment = (await this.enrollments.updateExpiry(enrollment._id, expires));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
updatedEnrollment = (await this.enrollments.updateStatus(enrollment._id, {
|
|
27
|
+
status: EnrollmentStatus.TERMINATED,
|
|
28
|
+
info: 'terminated manually',
|
|
29
|
+
}));
|
|
30
|
+
updatedEnrollment = await processEnrollmentService.bind(this)(updatedEnrollment);
|
|
31
|
+
}
|
|
12
32
|
const user = await this.users.findUserById(enrollment.userId);
|
|
13
33
|
const locale = this.users.userLocale(user);
|
|
14
34
|
await addMessageService.bind(this)('ENROLLMENT_STATUS', {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type OrderPosition } from '@unchainedshop/core-orders';
|
|
1
|
+
import { type Order, type OrderPosition } from '@unchainedshop/core-orders';
|
|
2
2
|
import type { Modules } from '../modules.ts';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export declare function updateCartItemService(this: Modules, { order, item, quantity, configuration, }: {
|
|
4
|
+
order: Order;
|
|
5
|
+
item: OrderPosition;
|
|
6
|
+
quantity?: number | null;
|
|
6
7
|
configuration?: {
|
|
7
8
|
key: string;
|
|
8
9
|
value: string;
|
|
9
|
-
}[];
|
|
10
|
-
}
|
|
11
|
-
export declare function updateCartItemService(this: Modules, params: UpdateCartItemParams, context: any): Promise<OrderPosition>;
|
|
10
|
+
}[] | null;
|
|
11
|
+
}, unchainedAPI?: Record<string, any>): Promise<OrderPosition | null>;
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
import { ordersSettings } from '@unchainedshop/core-orders';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
export async function updateCartItemService(
|
|
5
|
-
const {
|
|
6
|
-
const item = await this.orders.positions.findOrderPosition({ itemId });
|
|
7
|
-
if (!item) {
|
|
8
|
-
throw new OrderItemNotFoundError({ orderItemId: itemId });
|
|
9
|
-
}
|
|
10
|
-
const order = await this.orders.findOrder({ orderId: item.orderId });
|
|
11
|
-
if (!order) {
|
|
12
|
-
throw new OrderNotFoundError({ orderId: item.orderId });
|
|
13
|
-
}
|
|
14
|
-
if (!this.orders.isCart(order)) {
|
|
15
|
-
throw new OrderWrongStatusError({ status: order.status });
|
|
16
|
-
}
|
|
2
|
+
import { QuotationDirector } from "../directors/QuotationDirector.js";
|
|
3
|
+
import { createServiceError } from "../errors.js";
|
|
4
|
+
export async function updateCartItemService({ order, item, quantity, configuration, }, unchainedAPI = {}) {
|
|
5
|
+
const context = { ...unchainedAPI, modules: this };
|
|
17
6
|
const product = await this.products.findProduct({ productId: item.productId });
|
|
18
7
|
if (!product) {
|
|
19
|
-
throw
|
|
8
|
+
throw createServiceError('ProductNotFoundError', `Product not found: ${item.productId}`);
|
|
20
9
|
}
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
let itemQuantity = quantity === undefined ? 1 : quantity;
|
|
11
|
+
let itemConfiguration = configuration;
|
|
12
|
+
if (item.quotationId) {
|
|
13
|
+
const quotation = await this.quotations.findQuotation({ quotationId: item.quotationId });
|
|
14
|
+
if (!quotation) {
|
|
15
|
+
throw createServiceError('QuotationNotFoundError', `Quotation not found: ${item.quotationId}`);
|
|
16
|
+
}
|
|
17
|
+
const director = await QuotationDirector.actions({ quotation }, context);
|
|
18
|
+
const requestedQuantity = quantity || item.quantity;
|
|
19
|
+
const quotationConfiguration = await director.transformItemConfiguration({
|
|
20
|
+
quantity: requestedQuantity,
|
|
21
|
+
configuration: configuration || item.configuration || [],
|
|
22
|
+
});
|
|
23
|
+
if (!quotationConfiguration) {
|
|
24
|
+
throw createServiceError('QuotationItemConfigurationError', `Quotation ${quotation._id} does not take quantity ${requestedQuantity}`);
|
|
25
|
+
}
|
|
26
|
+
itemQuantity = quotationConfiguration.quantity || requestedQuantity;
|
|
27
|
+
itemConfiguration = quotationConfiguration.configuration;
|
|
23
28
|
}
|
|
24
29
|
await ordersSettings.validateOrderPosition({
|
|
25
30
|
order,
|
|
26
31
|
product,
|
|
27
|
-
configuration,
|
|
28
|
-
quantityDiff:
|
|
32
|
+
configuration: itemConfiguration,
|
|
33
|
+
quantityDiff: itemQuantity - item.quantity,
|
|
29
34
|
}, context);
|
|
30
|
-
|
|
35
|
+
return this.orders.positions.updateProductItem({
|
|
31
36
|
orderPositionId: item._id,
|
|
32
|
-
quantity:
|
|
33
|
-
configuration:
|
|
37
|
+
quantity: itemQuantity || null,
|
|
38
|
+
configuration: itemConfiguration || null,
|
|
34
39
|
});
|
|
35
|
-
await updateCalculationService.bind(this)(order._id);
|
|
36
|
-
return (await this.orders.positions.findOrderPosition({ itemId: item._id }));
|
|
37
40
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Enrollment, EnrollmentPlan } from '@unchainedshop/core-enrollments';
|
|
2
|
+
import type { Modules } from '../modules.ts';
|
|
3
|
+
export declare function updateEnrollmentPlanService(this: Modules, enrollment: Enrollment, { plan }: {
|
|
4
|
+
plan: EnrollmentPlan;
|
|
5
|
+
}): Promise<Enrollment>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { emit } from '@unchainedshop/events';
|
|
2
|
+
import { EnrollmentDirector } from "../core-index.js";
|
|
3
|
+
import { processEnrollmentService } from "./processEnrollment.js";
|
|
4
|
+
import { addMessageService } from "./addMessage.js";
|
|
5
|
+
import { createServiceError } from "../errors.js";
|
|
6
|
+
export async function updateEnrollmentPlanService(enrollment, { plan }) {
|
|
7
|
+
const product = await this.products.findProduct({ productId: enrollment.productId });
|
|
8
|
+
if (!product)
|
|
9
|
+
throw createServiceError('ProductNotFoundError', 'Product not found for enrollment');
|
|
10
|
+
const director = await EnrollmentDirector.actions({ enrollment, product }, { modules: this });
|
|
11
|
+
const newPlan = await director.transformPlanToNewPlan({ plan });
|
|
12
|
+
if (!newPlan) {
|
|
13
|
+
throw createServiceError('EnrollmentPlanChangeNotSupportedError', 'Plan change is not supported for this enrollment');
|
|
14
|
+
}
|
|
15
|
+
let updatedEnrollment = (await this.enrollments.updatePlan(enrollment._id, newPlan));
|
|
16
|
+
updatedEnrollment = await processEnrollmentService.bind(this)(updatedEnrollment);
|
|
17
|
+
await emit('ENROLLMENT_PLAN_CHANGE', { enrollment: updatedEnrollment });
|
|
18
|
+
const user = await this.users.findUserById(enrollment.userId);
|
|
19
|
+
const locale = this.users.userLocale(user);
|
|
20
|
+
await addMessageService.bind(this)('ENROLLMENT_STATUS', {
|
|
21
|
+
reason: 'plan_change',
|
|
22
|
+
locale: locale.baseName,
|
|
23
|
+
enrollmentId: updatedEnrollment._id,
|
|
24
|
+
});
|
|
25
|
+
return updatedEnrollment;
|
|
26
|
+
}
|
|
@@ -29,8 +29,13 @@ export async function validateOrderService(order) {
|
|
|
29
29
|
(await this.quotations.findQuotation({
|
|
30
30
|
quotationId: orderPosition.quotationId,
|
|
31
31
|
}));
|
|
32
|
-
if (quotation &&
|
|
33
|
-
|
|
32
|
+
if (quotation &&
|
|
33
|
+
!this.quotations.isProposalValidFor(quotation, {
|
|
34
|
+
userId: order.userId,
|
|
35
|
+
productId: orderPosition.productId,
|
|
36
|
+
currencyCode: order.currencyCode,
|
|
37
|
+
})) {
|
|
38
|
+
throw createServiceError('QuotationInvalidError', 'Quotation expired, fulfilled or not valid for this order, please request a new offer');
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
}
|