@unchainedshop/core-orders 1.1.3
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/.npm/package/README +7 -0
- package/.npm/package/npm-shrinkwrap.json +190 -0
- package/.versions +54 -0
- package/README.md +5 -0
- package/lib/db/OrderDeliveriesCollection.d.ts +3 -0
- package/lib/db/OrderDeliveriesCollection.js +8 -0
- package/lib/db/OrderDeliveriesCollection.js.map +1 -0
- package/lib/db/OrderDeliveriesSchema.d.ts +1 -0
- package/lib/db/OrderDeliveriesSchema.js +20 -0
- package/lib/db/OrderDeliveriesSchema.js.map +1 -0
- package/lib/db/OrderDiscountTrigger.d.ts +4 -0
- package/lib/db/OrderDiscountTrigger.js +6 -0
- package/lib/db/OrderDiscountTrigger.js.map +1 -0
- package/lib/db/OrderDiscountsCollection.d.ts +3 -0
- package/lib/db/OrderDiscountsCollection.js +11 -0
- package/lib/db/OrderDiscountsCollection.js.map +1 -0
- package/lib/db/OrderDiscountsSchema.d.ts +1 -0
- package/lib/db/OrderDiscountsSchema.js +12 -0
- package/lib/db/OrderDiscountsSchema.js.map +1 -0
- package/lib/db/OrderPaymentsCollection.d.ts +3 -0
- package/lib/db/OrderPaymentsCollection.js +8 -0
- package/lib/db/OrderPaymentsCollection.js.map +1 -0
- package/lib/db/OrderPaymentsSchema.d.ts +1 -0
- package/lib/db/OrderPaymentsSchema.js +20 -0
- package/lib/db/OrderPaymentsSchema.js.map +1 -0
- package/lib/db/OrderPositionsCollection.d.ts +3 -0
- package/lib/db/OrderPositionsCollection.js +11 -0
- package/lib/db/OrderPositionsCollection.js.map +1 -0
- package/lib/db/OrderPositionsSchema.d.ts +1 -0
- package/lib/db/OrderPositionsSchema.js +37 -0
- package/lib/db/OrderPositionsSchema.js.map +1 -0
- package/lib/db/OrdersCollection.d.ts +3 -0
- package/lib/db/OrdersCollection.js +33 -0
- package/lib/db/OrdersCollection.js.map +1 -0
- package/lib/db/OrdersSchema.d.ts +1 -0
- package/lib/db/OrdersSchema.js +30 -0
- package/lib/db/OrdersSchema.js.map +1 -0
- package/lib/director/OrderDiscountAdapter.d.ts +2 -0
- package/lib/director/OrderDiscountAdapter.js +3 -0
- package/lib/director/OrderDiscountAdapter.js.map +1 -0
- package/lib/director/OrderDiscountDirector.d.ts +2 -0
- package/lib/director/OrderDiscountDirector.js +3 -0
- package/lib/director/OrderDiscountDirector.js.map +1 -0
- package/lib/director/OrderPricingAdapter.d.ts +3 -0
- package/lib/director/OrderPricingAdapter.js +25 -0
- package/lib/director/OrderPricingAdapter.js.map +1 -0
- package/lib/director/OrderPricingDirector.d.ts +2 -0
- package/lib/director/OrderPricingDirector.js +48 -0
- package/lib/director/OrderPricingDirector.js.map +1 -0
- package/lib/director/OrderPricingSheet.d.ts +3 -0
- package/lib/director/OrderPricingSheet.js +113 -0
- package/lib/director/OrderPricingSheet.js.map +1 -0
- package/lib/module/configureOrderDeliveriesModule.d.ts +7 -0
- package/lib/module/configureOrderDeliveriesModule.js +164 -0
- package/lib/module/configureOrderDeliveriesModule.js.map +1 -0
- package/lib/module/configureOrderDiscountsModule.d.ts +7 -0
- package/lib/module/configureOrderDiscountsModule.js +183 -0
- package/lib/module/configureOrderDiscountsModule.js.map +1 -0
- package/lib/module/configureOrderPaymentsModule.d.ts +7 -0
- package/lib/module/configureOrderPaymentsModule.js +229 -0
- package/lib/module/configureOrderPaymentsModule.js.map +1 -0
- package/lib/module/configureOrderPositionsModule.d.ts +7 -0
- package/lib/module/configureOrderPositionsModule.js +213 -0
- package/lib/module/configureOrderPositionsModule.js.map +1 -0
- package/lib/module/configureOrdersModule-mutations.d.ts +12 -0
- package/lib/module/configureOrdersModule-mutations.js +162 -0
- package/lib/module/configureOrdersModule-mutations.js.map +1 -0
- package/lib/module/configureOrdersModule-processing.d.ts +13 -0
- package/lib/module/configureOrdersModule-processing.js +343 -0
- package/lib/module/configureOrdersModule-processing.js.map +1 -0
- package/lib/module/configureOrdersModule-queries.d.ts +5 -0
- package/lib/module/configureOrdersModule-queries.js +55 -0
- package/lib/module/configureOrdersModule-queries.js.map +1 -0
- package/lib/module/configureOrdersModule-transformations.d.ts +5 -0
- package/lib/module/configureOrdersModule-transformations.js +108 -0
- package/lib/module/configureOrdersModule-transformations.js.map +1 -0
- package/lib/module/configureOrdersModule.d.ts +3 -0
- package/lib/module/configureOrdersModule.js +239 -0
- package/lib/module/configureOrdersModule.js.map +1 -0
- package/lib/orders-index.d.ts +11 -0
- package/lib/orders-index.js +12 -0
- package/lib/orders-index.js.map +1 -0
- package/lib/orders-settings.d.ts +20 -0
- package/lib/orders-settings.js +17 -0
- package/lib/orders-settings.js.map +1 -0
- package/lib/service/createUserCartService.d.ts +2 -0
- package/lib/service/createUserCartService.js +23 -0
- package/lib/service/createUserCartService.js.map +1 -0
- package/lib/service/migrateOrderCartService.d.ts +2 -0
- package/lib/service/migrateOrderCartService.js +10 -0
- package/lib/service/migrateOrderCartService.js.map +1 -0
- package/lib/service/orderServices.d.ts +2 -0
- package/lib/service/orderServices.js +7 -0
- package/lib/service/orderServices.js.map +1 -0
- package/package.js +33 -0
- package/package.json +43 -0
- package/plugins/discount-100-off.ts +44 -0
- package/plugins/discount-half-price-manual.ts +43 -0
- package/plugins/discount-half-price.ts +46 -0
- package/plugins/order-delivery.ts +45 -0
- package/plugins/order-discount.ts +158 -0
- package/plugins/order-items-discount.ts +116 -0
- package/plugins/order-items.ts +61 -0
- package/plugins/order-payment.ts +46 -0
- package/src/db/OrderDeliveriesCollection.ts +12 -0
- package/src/db/OrderDeliveriesSchema.js +24 -0
- package/src/db/OrderDiscountTrigger.ts +4 -0
- package/src/db/OrderDiscountsCollection.ts +15 -0
- package/src/db/OrderDiscountsSchema.js +15 -0
- package/src/db/OrderPaymentsCollection.ts +12 -0
- package/src/db/OrderPaymentsSchema.js +24 -0
- package/src/db/OrderPositionsCollection.ts +15 -0
- package/src/db/OrderPositionsSchema.js +41 -0
- package/src/db/OrdersCollection.ts +37 -0
- package/src/db/OrdersSchema.js +34 -0
- package/src/director/OrderDiscountAdapter.ts +5 -0
- package/src/director/OrderDiscountDirector.ts +4 -0
- package/src/director/OrderPricingAdapter.ts +41 -0
- package/src/director/OrderPricingDirector.ts +68 -0
- package/src/director/OrderPricingSheet.ts +138 -0
- package/src/module/configureOrderDeliveriesModule.ts +240 -0
- package/src/module/configureOrderDiscountsModule.ts +245 -0
- package/src/module/configureOrderPaymentsModule.ts +331 -0
- package/src/module/configureOrderPositionsModule.ts +329 -0
- package/src/module/configureOrdersModule-mutations.ts +230 -0
- package/src/module/configureOrdersModule-processing.ts +506 -0
- package/src/module/configureOrdersModule-queries.ts +71 -0
- package/src/module/configureOrdersModule-transformations.ts +145 -0
- package/src/module/configureOrdersModule.ts +359 -0
- package/src/orders-index.ts +15 -0
- package/src/orders-settings.ts +23 -0
- package/src/service/createUserCartService.ts +38 -0
- package/src/service/migrateOrderCartService.ts +22 -0
- package/src/service/orderServices.ts +8 -0
- package/tests/orders-index.test.ts +50 -0
- package/tsconfig.build.json +26 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Collection, FindOptions, Query } from '@unchainedshop/types/common';
|
|
2
|
+
import { Order, OrderTransformations } from '@unchainedshop/types/orders';
|
|
3
|
+
import { OrderPricingSheet } from '../director/OrderPricingSheet';
|
|
4
|
+
|
|
5
|
+
export const configureOrderModuleTransformations = ({
|
|
6
|
+
Orders,
|
|
7
|
+
}: {
|
|
8
|
+
Orders: Collection<Order>;
|
|
9
|
+
}): OrderTransformations => {
|
|
10
|
+
return {
|
|
11
|
+
discounted: async (order, orderDiscount, requestContext) => {
|
|
12
|
+
const { modules } = requestContext;
|
|
13
|
+
|
|
14
|
+
// Delivery discounts
|
|
15
|
+
const orderDelivery = await modules.orders.deliveries.findDelivery({
|
|
16
|
+
orderDeliveryId: order.deliveryId,
|
|
17
|
+
});
|
|
18
|
+
const orderDeliveryDiscounts = modules.orders.deliveries.discounts(
|
|
19
|
+
orderDelivery,
|
|
20
|
+
{ order, orderDiscount },
|
|
21
|
+
requestContext,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Payment discounts
|
|
25
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
26
|
+
orderPaymentId: order.paymentId,
|
|
27
|
+
});
|
|
28
|
+
const orderPaymentDiscounts = modules.orders.payments.discounts(
|
|
29
|
+
orderPayment,
|
|
30
|
+
{ order, orderDiscount },
|
|
31
|
+
requestContext,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Position discounts
|
|
35
|
+
const orderPositions = await modules.orders.positions.findOrderPositions({
|
|
36
|
+
orderId: order._id,
|
|
37
|
+
});
|
|
38
|
+
const orderPositionDiscounts = orderPositions.flatMap((orderPosition) =>
|
|
39
|
+
modules.orders.positions.discounts(orderPosition, { order, orderDiscount }, requestContext),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// order discounts
|
|
43
|
+
const pricingSheet = OrderPricingSheet({
|
|
44
|
+
calculation: order.calculation,
|
|
45
|
+
currency: order.currency,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const orderDiscounts = pricingSheet
|
|
49
|
+
.discountPrices(orderDiscount._id)
|
|
50
|
+
.map((discount) => ({ order, ...discount }));
|
|
51
|
+
|
|
52
|
+
// All discounts
|
|
53
|
+
const discounted = [
|
|
54
|
+
...orderPaymentDiscounts,
|
|
55
|
+
...orderDeliveryDiscounts,
|
|
56
|
+
...orderPositionDiscounts,
|
|
57
|
+
...orderDiscounts,
|
|
58
|
+
].filter(Boolean);
|
|
59
|
+
|
|
60
|
+
return discounted;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
discountTotal: async (order, orderDiscount, requestContext) => {
|
|
64
|
+
const { modules } = requestContext;
|
|
65
|
+
const orderDiscountId = orderDiscount._id;
|
|
66
|
+
|
|
67
|
+
// Delivery discounts
|
|
68
|
+
const orderDelivery = await modules.orders.deliveries.findDelivery({
|
|
69
|
+
orderDeliveryId: order.deliveryId,
|
|
70
|
+
});
|
|
71
|
+
const orderDeliveryDiscountSum =
|
|
72
|
+
orderDelivery &&
|
|
73
|
+
modules.orders.deliveries
|
|
74
|
+
.pricingSheet(orderDelivery, order.currency, requestContext)
|
|
75
|
+
.discountSum(orderDiscountId);
|
|
76
|
+
|
|
77
|
+
// Payment discounts
|
|
78
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
79
|
+
orderPaymentId: order.paymentId,
|
|
80
|
+
});
|
|
81
|
+
const orderPaymentDiscountSum =
|
|
82
|
+
orderPayment &&
|
|
83
|
+
modules.orders.payments
|
|
84
|
+
.pricingSheet(orderPayment, order.currency, requestContext)
|
|
85
|
+
.discountSum(orderDiscountId);
|
|
86
|
+
|
|
87
|
+
// Position discounts
|
|
88
|
+
const orderPositions = await modules.orders.positions.findOrderPositions({
|
|
89
|
+
orderId: order._id,
|
|
90
|
+
});
|
|
91
|
+
const orderPositionDiscounts = orderPositions.map((orderPosition) =>
|
|
92
|
+
modules.orders.positions
|
|
93
|
+
.pricingSheet(orderPosition, order.currency, requestContext)
|
|
94
|
+
.discountSum(orderDiscountId),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// order discounts
|
|
98
|
+
const orderDiscountSum = OrderPricingSheet({
|
|
99
|
+
calculation: order.calculation,
|
|
100
|
+
currency: order.currency,
|
|
101
|
+
}).discountSum(orderDiscountId);
|
|
102
|
+
|
|
103
|
+
const prices = [
|
|
104
|
+
orderDeliveryDiscountSum,
|
|
105
|
+
orderPaymentDiscountSum,
|
|
106
|
+
...orderPositionDiscounts,
|
|
107
|
+
orderDiscountSum,
|
|
108
|
+
];
|
|
109
|
+
const amount = prices.reduce((oldValue, price) => oldValue + (price || 0), 0);
|
|
110
|
+
return {
|
|
111
|
+
amount,
|
|
112
|
+
currency: order.currency,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
isCart: (order) => {
|
|
117
|
+
return order.status === null;
|
|
118
|
+
},
|
|
119
|
+
cart: async ({ orderNumber, countryContext }, user) => {
|
|
120
|
+
const selector: Query = {
|
|
121
|
+
countryCode: countryContext || user.lastLogin.countryContext,
|
|
122
|
+
status: { $eq: null },
|
|
123
|
+
userId: user._id,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (orderNumber) {
|
|
127
|
+
selector.orderNumber = orderNumber;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const options: FindOptions = {
|
|
131
|
+
sort: {
|
|
132
|
+
updated: -1,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
return Orders.findOne(selector, options);
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
pricingSheet: (order) => {
|
|
139
|
+
return OrderPricingSheet({
|
|
140
|
+
calculation: order.calculation,
|
|
141
|
+
currency: order.currency,
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { Context } from '@unchainedshop/types/api';
|
|
2
|
+
import { ModuleInput, Update } from '@unchainedshop/types/common';
|
|
3
|
+
import { Order, OrderStatus, OrdersModule, OrdersSettingsOptions } from '@unchainedshop/types/orders';
|
|
4
|
+
import { OrderDelivery } from '@unchainedshop/types/orders.deliveries';
|
|
5
|
+
import { OrderPayment } from '@unchainedshop/types/orders.payments';
|
|
6
|
+
import { OrderPosition } from '@unchainedshop/types/orders.positions';
|
|
7
|
+
import { log } from '@unchainedshop/logger';
|
|
8
|
+
import { generateDbFilterById } from '@unchainedshop/utils';
|
|
9
|
+
import { OrderDeliveriesCollection } from '../db/OrderDeliveriesCollection';
|
|
10
|
+
import { OrderDiscountsCollection } from '../db/OrderDiscountsCollection';
|
|
11
|
+
import { OrderDiscountTrigger } from '../db/OrderDiscountTrigger';
|
|
12
|
+
import { OrderPaymentsCollection } from '../db/OrderPaymentsCollection';
|
|
13
|
+
import { OrderPositionsCollection } from '../db/OrderPositionsCollection';
|
|
14
|
+
import { OrdersCollection } from '../db/OrdersCollection';
|
|
15
|
+
import { OrderDiscountDirector } from '../director/OrderDiscountDirector';
|
|
16
|
+
import { OrderPricingDirector } from '../director/OrderPricingDirector';
|
|
17
|
+
import { ordersSettings } from '../orders-settings';
|
|
18
|
+
import { configureOrderDeliveriesModule } from './configureOrderDeliveriesModule';
|
|
19
|
+
import { configureOrderDiscountsModule } from './configureOrderDiscountsModule';
|
|
20
|
+
import { configureOrderPaymentsModule } from './configureOrderPaymentsModule';
|
|
21
|
+
import { configureOrderPositionsModule } from './configureOrderPositionsModule';
|
|
22
|
+
import { configureOrderModuleMutations } from './configureOrdersModule-mutations';
|
|
23
|
+
import { configureOrderModuleProcessing } from './configureOrdersModule-processing';
|
|
24
|
+
import { configureOrdersModuleQueries } from './configureOrdersModule-queries';
|
|
25
|
+
import { configureOrderModuleTransformations } from './configureOrdersModule-transformations';
|
|
26
|
+
|
|
27
|
+
export const configureOrdersModule = async ({
|
|
28
|
+
db,
|
|
29
|
+
options: orderOptions = {},
|
|
30
|
+
}: ModuleInput<OrdersSettingsOptions>): Promise<OrdersModule> => {
|
|
31
|
+
ordersSettings.configureSettings(orderOptions);
|
|
32
|
+
|
|
33
|
+
const Orders = await OrdersCollection(db);
|
|
34
|
+
const OrderDeliveries = await OrderDeliveriesCollection(db);
|
|
35
|
+
const OrderDiscounts = await OrderDiscountsCollection(db);
|
|
36
|
+
const OrderPayments = await OrderPaymentsCollection(db);
|
|
37
|
+
const OrderPositions = await OrderPositionsCollection(db);
|
|
38
|
+
|
|
39
|
+
const findOrderPositions = async (order: Order) =>
|
|
40
|
+
OrderPositions.find(
|
|
41
|
+
{
|
|
42
|
+
orderId: order._id,
|
|
43
|
+
quantity: { $gt: 0 },
|
|
44
|
+
},
|
|
45
|
+
{},
|
|
46
|
+
).toArray();
|
|
47
|
+
|
|
48
|
+
const findOrderDelivery = async (order: Order) =>
|
|
49
|
+
OrderDeliveries.findOne(generateDbFilterById(order.deliveryId), {});
|
|
50
|
+
|
|
51
|
+
const findOrderPayment = async (order: Order) =>
|
|
52
|
+
OrderPayments.findOne(generateDbFilterById(order.paymentId), {});
|
|
53
|
+
|
|
54
|
+
const findNewOrderNumber = async (order: Order, index = 0) => {
|
|
55
|
+
const newHashID = ordersSettings.orderNumberHashFn(order, index);
|
|
56
|
+
if ((await Orders.countDocuments({ orderNumber: newHashID }, { limit: 1 })) === 0) {
|
|
57
|
+
return newHashID;
|
|
58
|
+
}
|
|
59
|
+
return findNewOrderNumber(order, index + 1);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const updateStatus: OrdersModule['updateStatus'] = async (
|
|
63
|
+
orderId,
|
|
64
|
+
{ status, info },
|
|
65
|
+
requestContext,
|
|
66
|
+
) => {
|
|
67
|
+
const selector = generateDbFilterById(orderId);
|
|
68
|
+
const order = await Orders.findOne(selector, {});
|
|
69
|
+
|
|
70
|
+
if (order.status === status) return order;
|
|
71
|
+
|
|
72
|
+
const date = new Date();
|
|
73
|
+
const $set: Partial<Order> = {
|
|
74
|
+
status,
|
|
75
|
+
updated: new Date(),
|
|
76
|
+
updatedBy: requestContext.userId,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
switch (status) {
|
|
80
|
+
// explicitly use fallthrough here!
|
|
81
|
+
case OrderStatus.FULLFILLED:
|
|
82
|
+
if (!order.fullfilled) {
|
|
83
|
+
$set.fullfilled = date;
|
|
84
|
+
}
|
|
85
|
+
case OrderStatus.REJECTED: // eslint-disable-line no-fallthrough
|
|
86
|
+
if (!order.rejected) {
|
|
87
|
+
$set.rejected = date;
|
|
88
|
+
}
|
|
89
|
+
case OrderStatus.CONFIRMED: // eslint-disable-line no-fallthrough
|
|
90
|
+
if (!order.confirmed) {
|
|
91
|
+
$set.confirmed = date;
|
|
92
|
+
}
|
|
93
|
+
case OrderStatus.PENDING: // eslint-disable-line no-fallthrough
|
|
94
|
+
if (!order.ordered) {
|
|
95
|
+
$set.ordered = date;
|
|
96
|
+
}
|
|
97
|
+
if (!order.orderNumber) {
|
|
98
|
+
// Order Numbers can be set by the user
|
|
99
|
+
$set.orderNumber = await findNewOrderNumber(order);
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const modifier: Update<Order> = {
|
|
107
|
+
$set,
|
|
108
|
+
$push: {
|
|
109
|
+
log: {
|
|
110
|
+
date,
|
|
111
|
+
status,
|
|
112
|
+
info,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
log(`New Status: ${status}`, { orderId });
|
|
118
|
+
|
|
119
|
+
await Orders.updateOne(selector, modifier);
|
|
120
|
+
|
|
121
|
+
return Orders.findOne(selector, {});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const updateDiscounts = async (order: Order, requestContext: Context) => {
|
|
125
|
+
const { modules } = requestContext;
|
|
126
|
+
|
|
127
|
+
// 1. go through existing order-discounts and check if discount still valid,
|
|
128
|
+
// those who are not valid anymore should get removed
|
|
129
|
+
const discounts = await modules.orders.discounts.findOrderDiscounts({
|
|
130
|
+
orderId: order._id,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await Promise.all(
|
|
134
|
+
discounts.map(async (discount) => {
|
|
135
|
+
const isValid = await modules.orders.discounts.isValid(discount, requestContext);
|
|
136
|
+
|
|
137
|
+
if (!isValid) {
|
|
138
|
+
await modules.orders.discounts.delete(discount._id, requestContext);
|
|
139
|
+
}
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// 2. run auto-system discount
|
|
144
|
+
const cleanedDiscounts = await modules.orders.discounts.findOrderDiscounts({
|
|
145
|
+
orderId: order._id,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const currentDiscountKeys = cleanedDiscounts.map(({ discountKey }) => discountKey);
|
|
149
|
+
|
|
150
|
+
const director = await OrderDiscountDirector.actions({ order }, requestContext);
|
|
151
|
+
const systemDiscounts = await director.findSystemDiscounts();
|
|
152
|
+
|
|
153
|
+
await Promise.all(
|
|
154
|
+
systemDiscounts
|
|
155
|
+
.filter((key) => currentDiscountKeys.indexOf(key) === -1)
|
|
156
|
+
.map((discountKey) =>
|
|
157
|
+
modules.orders.discounts.create(
|
|
158
|
+
{
|
|
159
|
+
orderId: order._id,
|
|
160
|
+
discountKey,
|
|
161
|
+
trigger: OrderDiscountTrigger.SYSTEM,
|
|
162
|
+
},
|
|
163
|
+
requestContext.userId,
|
|
164
|
+
),
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const initProviders = async (order: Order, requestContext: Context) => {
|
|
170
|
+
const { modules } = requestContext;
|
|
171
|
+
|
|
172
|
+
let updatedOrder = order;
|
|
173
|
+
|
|
174
|
+
// Init delivery provider
|
|
175
|
+
const supportedDeliveryProviders = await modules.delivery.findSupported(
|
|
176
|
+
{ order: updatedOrder },
|
|
177
|
+
requestContext,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const orderDelivery = await modules.orders.deliveries.findDelivery({
|
|
181
|
+
orderDeliveryId: updatedOrder.deliveryId,
|
|
182
|
+
});
|
|
183
|
+
const deliveryProviderId = orderDelivery?.deliveryProviderId;
|
|
184
|
+
|
|
185
|
+
const isAlreadyInitializedWithSupportedDeliveryProvider = supportedDeliveryProviders?.some(
|
|
186
|
+
(provider) => {
|
|
187
|
+
return provider._id === deliveryProviderId;
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (supportedDeliveryProviders?.length > 0 && !isAlreadyInitializedWithSupportedDeliveryProvider) {
|
|
192
|
+
const defaultOrderDeliveryProvider = await modules.delivery.determineDefault(
|
|
193
|
+
supportedDeliveryProviders,
|
|
194
|
+
{ order: updatedOrder },
|
|
195
|
+
requestContext,
|
|
196
|
+
);
|
|
197
|
+
if (defaultOrderDeliveryProvider) {
|
|
198
|
+
updatedOrder = await modules.orders.setDeliveryProvider(
|
|
199
|
+
updatedOrder._id,
|
|
200
|
+
defaultOrderDeliveryProvider._id,
|
|
201
|
+
requestContext,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Init payment provider
|
|
207
|
+
const supportedPaymentProviders = await modules.payment.paymentProviders.findSupported(
|
|
208
|
+
{ order: updatedOrder },
|
|
209
|
+
requestContext,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
213
|
+
orderPaymentId: updatedOrder.paymentId,
|
|
214
|
+
});
|
|
215
|
+
const paymentProviderId = orderPayment?.paymentProviderId;
|
|
216
|
+
|
|
217
|
+
const isAlreadyInitializedWithSupportedPaymentProvider = supportedPaymentProviders?.some(
|
|
218
|
+
(provider) => {
|
|
219
|
+
return provider._id === paymentProviderId;
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (supportedPaymentProviders?.length > 0 && !isAlreadyInitializedWithSupportedPaymentProvider) {
|
|
224
|
+
const paymentCredentials = await modules.payment.paymentCredentials.findPaymentCredentials(
|
|
225
|
+
{ userId: updatedOrder.userId, isPreferred: true },
|
|
226
|
+
{
|
|
227
|
+
sort: {
|
|
228
|
+
created: -1,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const defaultOrderPaymentProvider = await modules.payment.paymentProviders.determineDefault(
|
|
234
|
+
supportedPaymentProviders,
|
|
235
|
+
{ order: updatedOrder, paymentCredentials },
|
|
236
|
+
requestContext,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (defaultOrderPaymentProvider) {
|
|
240
|
+
updatedOrder = await modules.orders.setPaymentProvider(
|
|
241
|
+
updatedOrder._id,
|
|
242
|
+
defaultOrderPaymentProvider._id,
|
|
243
|
+
requestContext,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return updatedOrder;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const updateCalculation: OrdersModule['updateCalculation'] = async (orderId, requestContext) => {
|
|
251
|
+
const { modules } = requestContext;
|
|
252
|
+
|
|
253
|
+
const selector = generateDbFilterById(orderId);
|
|
254
|
+
let order = (await Orders.findOne(selector, {})) as Order;
|
|
255
|
+
|
|
256
|
+
// Don't recalculate orders, only carts
|
|
257
|
+
if (order.status !== null) return order;
|
|
258
|
+
|
|
259
|
+
await updateDiscounts(order, requestContext);
|
|
260
|
+
|
|
261
|
+
order = await initProviders(order, requestContext);
|
|
262
|
+
|
|
263
|
+
let orderPositions = (await findOrderPositions(order)) as OrderPosition[];
|
|
264
|
+
orderPositions = await Promise.all(
|
|
265
|
+
orderPositions.map((orderPosition) =>
|
|
266
|
+
modules.orders.positions.updateCalculation(orderPosition, requestContext),
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
let orderDelivery = (await findOrderDelivery(order)) as OrderDelivery;
|
|
271
|
+
if (orderDelivery) {
|
|
272
|
+
orderDelivery = await modules.orders.deliveries.updateCalculation(orderDelivery, requestContext);
|
|
273
|
+
}
|
|
274
|
+
let orderPayment = (await findOrderPayment(order)) as OrderPayment;
|
|
275
|
+
if (orderPayment) {
|
|
276
|
+
orderPayment = await modules.orders.payments.updateCalculation(orderPayment, requestContext);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
orderPositions = await Promise.all(
|
|
280
|
+
orderPositions.map((orderPosition) =>
|
|
281
|
+
modules.orders.positions.updateScheduling(
|
|
282
|
+
{ order, orderDelivery, orderPosition },
|
|
283
|
+
requestContext,
|
|
284
|
+
),
|
|
285
|
+
),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const pricing = await OrderPricingDirector.actions(
|
|
289
|
+
{ order, orderPositions, orderDelivery, orderPayment },
|
|
290
|
+
requestContext,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const calculation = await pricing.calculate();
|
|
294
|
+
|
|
295
|
+
await Orders.updateOne(selector, {
|
|
296
|
+
$set: {
|
|
297
|
+
calculation,
|
|
298
|
+
updated: new Date(),
|
|
299
|
+
updatedBy: requestContext.userId,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return Orders.findOne(selector, {});
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const orderQueries = configureOrdersModuleQueries({ Orders });
|
|
307
|
+
const orderTransformations = configureOrderModuleTransformations({
|
|
308
|
+
Orders,
|
|
309
|
+
});
|
|
310
|
+
const orderProcessing = configureOrderModuleProcessing({
|
|
311
|
+
Orders,
|
|
312
|
+
OrderDeliveries,
|
|
313
|
+
OrderPayments,
|
|
314
|
+
OrderPositions,
|
|
315
|
+
updateCalculation,
|
|
316
|
+
updateStatus,
|
|
317
|
+
});
|
|
318
|
+
const orderMutations = configureOrderModuleMutations({
|
|
319
|
+
Orders,
|
|
320
|
+
OrderDeliveries,
|
|
321
|
+
OrderPayments,
|
|
322
|
+
initProviders,
|
|
323
|
+
updateCalculation,
|
|
324
|
+
updateStatus,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const orderDiscountsModule = configureOrderDiscountsModule({
|
|
328
|
+
OrderDiscounts,
|
|
329
|
+
updateCalculation,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const orderPositionsModule = configureOrderPositionsModule({
|
|
333
|
+
OrderPositions,
|
|
334
|
+
updateCalculation,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const orderPaymentsModule = configureOrderPaymentsModule({
|
|
338
|
+
OrderPayments,
|
|
339
|
+
updateCalculation,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const orderDeliveriesModule = configureOrderDeliveriesModule({
|
|
343
|
+
OrderDeliveries,
|
|
344
|
+
updateCalculation,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
...orderQueries,
|
|
349
|
+
...orderTransformations,
|
|
350
|
+
...orderProcessing,
|
|
351
|
+
...orderMutations,
|
|
352
|
+
|
|
353
|
+
// Subentities
|
|
354
|
+
deliveries: orderDeliveriesModule,
|
|
355
|
+
discounts: orderDiscountsModule,
|
|
356
|
+
positions: orderPositionsModule,
|
|
357
|
+
payments: orderPaymentsModule,
|
|
358
|
+
};
|
|
359
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { configureOrdersModule } from './module/configureOrdersModule';
|
|
2
|
+
export { orderServices } from './service/orderServices';
|
|
3
|
+
|
|
4
|
+
export { OrderStatus } from '@unchainedshop/types/orders';
|
|
5
|
+
export { OrderDeliveryStatus } from '@unchainedshop/types/orders.deliveries';
|
|
6
|
+
export { OrderPaymentStatus } from '@unchainedshop/types/orders.payments';
|
|
7
|
+
|
|
8
|
+
export { OrderDiscountAdapter } from './director/OrderDiscountAdapter';
|
|
9
|
+
export { OrderDiscountDirector } from './director/OrderDiscountDirector';
|
|
10
|
+
|
|
11
|
+
export { OrderPricingAdapter } from './director/OrderPricingAdapter';
|
|
12
|
+
export { OrderPricingDirector } from './director/OrderPricingDirector';
|
|
13
|
+
export { OrderPricingSheet } from './director/OrderPricingSheet';
|
|
14
|
+
|
|
15
|
+
export { ordersSettings } from './orders-settings';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { generateRandomHash } from '@unchainedshop/utils';
|
|
2
|
+
|
|
3
|
+
export const defaultValidateOrderPosition = ({ product }, { modules }) => {
|
|
4
|
+
if (!modules.products.isActive(product)) {
|
|
5
|
+
throw new Error('This product is inactive');
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const ordersSettings = {
|
|
10
|
+
ensureUserHasCart: null,
|
|
11
|
+
orderNumberHashFn: null,
|
|
12
|
+
validateOrderPosition: null,
|
|
13
|
+
|
|
14
|
+
configureSettings({
|
|
15
|
+
ensureUserHasCart = false,
|
|
16
|
+
orderNumberHashFn = generateRandomHash,
|
|
17
|
+
validateOrderPosition = defaultValidateOrderPosition,
|
|
18
|
+
} = {}) {
|
|
19
|
+
this.ensureUserHasCart = ensureUserHasCart;
|
|
20
|
+
this.orderNumberHashFn = orderNumberHashFn;
|
|
21
|
+
this.validateOrderPosition = validateOrderPosition;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { CreateUserCartService } from '@unchainedshop/types/orders';
|
|
2
|
+
|
|
3
|
+
export const createUserCartService: CreateUserCartService = async (
|
|
4
|
+
{ user, orderNumber, countryCode },
|
|
5
|
+
requestContext,
|
|
6
|
+
) => {
|
|
7
|
+
const { countryContext, modules, services } = requestContext;
|
|
8
|
+
|
|
9
|
+
const normalizedCountryCode = countryCode || countryContext;
|
|
10
|
+
|
|
11
|
+
const currency = await services.countries.resolveDefaultCurrencyCode(
|
|
12
|
+
{
|
|
13
|
+
isoCode: normalizedCountryCode,
|
|
14
|
+
},
|
|
15
|
+
requestContext,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const order = await modules.orders.create(
|
|
19
|
+
{
|
|
20
|
+
orderNumber,
|
|
21
|
+
currency,
|
|
22
|
+
countryCode: normalizedCountryCode,
|
|
23
|
+
billingAddress: user.lastBillingAddress || user.profile?.address,
|
|
24
|
+
contact:
|
|
25
|
+
user.lastContact ||
|
|
26
|
+
(!user.guest
|
|
27
|
+
? {
|
|
28
|
+
telNumber: user.profile?.phoneMobile,
|
|
29
|
+
emailAddress: modules.users.primaryEmail(user)?.address,
|
|
30
|
+
}
|
|
31
|
+
: {}),
|
|
32
|
+
},
|
|
33
|
+
user._id,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
await modules.orders.initProviders(order, requestContext);
|
|
37
|
+
return order;
|
|
38
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MigrateOrderCartsService } from '@unchainedshop/types/orders';
|
|
2
|
+
|
|
3
|
+
export const migrateOrderCartsService: MigrateOrderCartsService = async (
|
|
4
|
+
{ fromUser, toUser, shouldMerge },
|
|
5
|
+
requestContext,
|
|
6
|
+
) => {
|
|
7
|
+
const fromCart = await requestContext.modules.orders.cart(
|
|
8
|
+
{ countryContext: requestContext.countryContext },
|
|
9
|
+
fromUser,
|
|
10
|
+
);
|
|
11
|
+
const toCart = await requestContext.modules.orders.cart(
|
|
12
|
+
{ countryContext: requestContext.countryContext },
|
|
13
|
+
toUser,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!fromCart) {
|
|
17
|
+
// No cart, don't copy
|
|
18
|
+
return toCart;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return requestContext.modules.orders.migrateCart({ fromCart, shouldMerge, toCart }, requestContext);
|
|
22
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { OrderServices } from '@unchainedshop/types/orders';
|
|
2
|
+
import { migrateOrderCartsService } from './migrateOrderCartService';
|
|
3
|
+
import { createUserCartService } from './createUserCartService';
|
|
4
|
+
|
|
5
|
+
export const orderServices: OrderServices = {
|
|
6
|
+
migrateOrderCarts: migrateOrderCartsService,
|
|
7
|
+
createUserCart: createUserCartService,
|
|
8
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
import { initDb } from 'meteor/unchained:mongodb';
|
|
3
|
+
import { configureOrdersModule } from 'meteor/unchained:core-orders';
|
|
4
|
+
import { OrdersModule } from '@unchainedshop/types/orders';
|
|
5
|
+
|
|
6
|
+
describe('Test exports', () => {
|
|
7
|
+
let module: OrdersModule;
|
|
8
|
+
|
|
9
|
+
before(async () => {
|
|
10
|
+
const db = await initDb();
|
|
11
|
+
module = await configureOrdersModule({ db }).catch((error) => {
|
|
12
|
+
console.error(error);
|
|
13
|
+
|
|
14
|
+
throw error;
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('Insert order', async () => {
|
|
19
|
+
let order = await module.create(
|
|
20
|
+
{
|
|
21
|
+
billingAddress: {
|
|
22
|
+
lastName: 'Mustermann',
|
|
23
|
+
firstName: 'Max',
|
|
24
|
+
addressLine: 'Teststreet 11',
|
|
25
|
+
city: 'Zürich',
|
|
26
|
+
postalCode: '8009',
|
|
27
|
+
company: 'Unchained Commerce',
|
|
28
|
+
countryCode: 'CH',
|
|
29
|
+
},
|
|
30
|
+
orderNumber: 'Nr-1234123',
|
|
31
|
+
currency: 'CHF',
|
|
32
|
+
countryCode: 'CH',
|
|
33
|
+
},
|
|
34
|
+
'Test-User-1'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
assert.ok(order);
|
|
38
|
+
const orderId = order._id;
|
|
39
|
+
order = await module.findOrder({ orderId });
|
|
40
|
+
|
|
41
|
+
assert.ok(order);
|
|
42
|
+
|
|
43
|
+
const deletedCount = await module
|
|
44
|
+
.delete(orderId, 'Test-User-1')
|
|
45
|
+
.catch((error) => {
|
|
46
|
+
return 0;
|
|
47
|
+
});
|
|
48
|
+
assert.equal(deletedCount, 1);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
|
9
|
+
"lib": ["esnext"],
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"noImplicitReturns": true,
|
|
13
|
+
"noUnusedLocals": false,
|
|
14
|
+
"outDir": "lib",
|
|
15
|
+
"preserveWatchOutput": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"sourceMap": true,
|
|
18
|
+
"target": "esnext",
|
|
19
|
+
"types": ["node"],
|
|
20
|
+
"baseUrl": ".", // This must be specified if "paths" is.
|
|
21
|
+
"paths": {
|
|
22
|
+
"meteor/unchained:*": ["node_modules/@unchainedshop/types/index.d.ts"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"include": ["src"]
|
|
26
|
+
}
|