@unchainedshop/plugins 2.6.0 → 2.7.0
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/payment/payrexx/api/index.d.ts +35 -0
- package/lib/payment/payrexx/api/index.js +100 -0
- package/lib/payment/payrexx/api/index.js.map +1 -0
- package/lib/payment/payrexx/index.d.ts +1 -0
- package/lib/payment/payrexx/index.js +139 -0
- package/lib/payment/payrexx/index.js.map +1 -0
- package/lib/payment/payrexx/middleware.d.ts +1 -0
- package/lib/payment/payrexx/middleware.js +55 -0
- package/lib/payment/payrexx/middleware.js.map +1 -0
- package/lib/payment/payrexx/payrexx.d.ts +16 -0
- package/lib/payment/payrexx/payrexx.js +19 -0
- package/lib/payment/payrexx/payrexx.js.map +1 -0
- package/lib/payment/stripe/middleware.d.ts +4 -0
- package/lib/payment/stripe/middleware.js +54 -47
- package/lib/payment/stripe/middleware.js.map +1 -1
- package/lib/plugins-index.js +3 -1
- package/lib/plugins-index.js.map +1 -1
- package/lib/pricing/discount-100-off.d.ts +2 -1
- package/lib/pricing/discount-100-off.js.map +1 -1
- package/lib/pricing/discount-half-price-manual.d.ts +2 -1
- package/lib/pricing/discount-half-price-manual.js.map +1 -1
- package/lib/pricing/discount-half-price.d.ts +2 -1
- package/lib/pricing/discount-half-price.js.map +1 -1
- package/lib/pricing/order-discount.d.ts +2 -1
- package/lib/pricing/order-discount.js +2 -8
- package/lib/pricing/order-discount.js.map +1 -1
- package/lib/pricing/order-items-discount.js +4 -3
- package/lib/pricing/order-items-discount.js.map +1 -1
- package/lib/pricing/product-discount.js +24 -18
- package/lib/pricing/product-discount.js.map +1 -1
- package/package.json +24 -24
- package/src/payment/payrexx/api/index.ts +132 -0
- package/src/payment/payrexx/index.ts +184 -0
- package/src/payment/payrexx/middleware.ts +64 -0
- package/src/payment/payrexx/payrexx.ts +25 -0
- package/src/payment/payrexx/readme.md +4 -0
- package/src/payment/stripe/middleware.ts +58 -51
- package/src/plugins-index.ts +10 -0
- package/src/pricing/discount-100-off.ts +2 -1
- package/src/pricing/discount-half-price-manual.ts +2 -1
- package/src/pricing/discount-half-price.ts +2 -1
- package/src/pricing/order-discount.ts +4 -10
- package/src/pricing/order-items-discount.ts +9 -4
- package/src/pricing/product-discount.ts +37 -21
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const mapOrderDataToGatewayObject = ({ order, orderPayment, pricing }, options = {}) => {
|
|
2
|
+
const {
|
|
3
|
+
EMAIL_WEBSITE_NAME = 'Unchained',
|
|
4
|
+
EMAIL_WEBSITE_URL,
|
|
5
|
+
DATATRANS_SUCCESS_PATH = '/payrexx/success',
|
|
6
|
+
DATATRANS_ERROR_PATH = '/payrexx/error',
|
|
7
|
+
DATATRANS_CANCEL_PATH = '/payrexx/cancel',
|
|
8
|
+
} = process.env;
|
|
9
|
+
|
|
10
|
+
const { currency, amount } = pricing.total({ useNetPrice: false });
|
|
11
|
+
const gatewayObject = {
|
|
12
|
+
amount: Math.round(amount),
|
|
13
|
+
currency: currency.toUpperCase(),
|
|
14
|
+
purpose: encodeURIComponent(`${EMAIL_WEBSITE_NAME} #${order._id}`),
|
|
15
|
+
reservation: true,
|
|
16
|
+
skipResultPage: true,
|
|
17
|
+
successRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_SUCCESS_PATH}`,
|
|
18
|
+
failedRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_ERROR_PATH}`,
|
|
19
|
+
cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_CANCEL_PATH}`,
|
|
20
|
+
referenceId: orderPayment._id,
|
|
21
|
+
'fields[email]': order.contact?.emailAddress,
|
|
22
|
+
...options,
|
|
23
|
+
};
|
|
24
|
+
return gatewayObject;
|
|
25
|
+
};
|
|
@@ -4,18 +4,10 @@ import stripe from './stripe.js';
|
|
|
4
4
|
|
|
5
5
|
const logger = createLogger('unchained:core-payment:stripe:webhook');
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const environmentInEnv = STRIPE_WEBHOOK_ENVIRONMENT;
|
|
12
|
-
|
|
13
|
-
if (!environmentInMetadata && !environmentInEnv) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return environmentInEnv === environmentInMetadata;
|
|
18
|
-
}
|
|
7
|
+
export const WebhookEventTypes = {
|
|
8
|
+
PAYMENT_INTENT_SUCCEEDED: 'payment_intent.succeeded',
|
|
9
|
+
SETUP_INTENT_SUCCEEDED: 'setup_intent.succeeded',
|
|
10
|
+
};
|
|
19
11
|
|
|
20
12
|
export const stripeHandler = async (request, response) => {
|
|
21
13
|
const resolvedContext = request.unchainedContext as Context;
|
|
@@ -25,30 +17,50 @@ export const stripeHandler = async (request, response) => {
|
|
|
25
17
|
|
|
26
18
|
try {
|
|
27
19
|
const sig = request.headers['stripe-signature'];
|
|
28
|
-
event = stripe.webhooks.constructEvent(request.body, sig, STRIPE_ENDPOINT_SECRET);
|
|
29
|
-
logger.verbose(`received event`, {
|
|
30
|
-
type: event.type,
|
|
31
|
-
});
|
|
20
|
+
event = stripe.webhooks.constructEvent(request.body, sig, process.env.STRIPE_ENDPOINT_SECRET);
|
|
32
21
|
} catch (err) {
|
|
33
|
-
logger.error(`
|
|
22
|
+
logger.error(`Error constructing event: ${err.message}`);
|
|
34
23
|
response.writeHead(400);
|
|
35
24
|
response.end(err.message);
|
|
36
25
|
return;
|
|
37
26
|
}
|
|
38
27
|
|
|
28
|
+
if (!Object.values(WebhookEventTypes).find(event.type)) {
|
|
29
|
+
logger.verbose(`unhandled event type`, {
|
|
30
|
+
type: event.type,
|
|
31
|
+
});
|
|
32
|
+
response.writeHead(200);
|
|
33
|
+
response.end({
|
|
34
|
+
ignored: true,
|
|
35
|
+
message: `Unhandled event type: ${event.type}. Supported types: ${Object.values(WebhookEventTypes).join(', ')}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const environmentInMetadata = event.data?.object?.metadata?.environment || '';
|
|
40
|
+
const environmentInEnv = process.env.STRIPE_WEBHOOK_ENVIRONMENT || '';
|
|
41
|
+
if (environmentInMetadata !== environmentInEnv) {
|
|
42
|
+
logger.verbose(`unhandled event environment`, {
|
|
43
|
+
type: event.type,
|
|
44
|
+
environment: environmentInMetadata,
|
|
45
|
+
});
|
|
46
|
+
response.writeHead(200);
|
|
47
|
+
response.end(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
ignored: true,
|
|
50
|
+
message: `Unhandled event environment: ${environmentInMetadata}. Supported environment: ${environmentInEnv}`,
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
logger.verbose(`Processing event`, {
|
|
57
|
+
type: event.type,
|
|
58
|
+
});
|
|
39
59
|
try {
|
|
40
|
-
if (event.type ===
|
|
60
|
+
if (event.type === WebhookEventTypes.PAYMENT_INTENT_SUCCEEDED) {
|
|
41
61
|
const paymentIntent = event.data.object;
|
|
42
62
|
const { orderPaymentId } = paymentIntent.metadata || {};
|
|
43
63
|
|
|
44
|
-
if (!checkEnvironment(paymentIntent.metadata)) {
|
|
45
|
-
logger.verbose(`event ignored because of environment difference`, {
|
|
46
|
-
type: event.type,
|
|
47
|
-
});
|
|
48
|
-
response.end(JSON.stringify({ received: true, ignored: true }));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
64
|
logger.verbose(`checkout with orderPaymentId: ${orderPaymentId}`, {
|
|
53
65
|
type: event.type,
|
|
54
66
|
});
|
|
@@ -75,25 +87,26 @@ export const stripeHandler = async (request, response) => {
|
|
|
75
87
|
resolvedContext,
|
|
76
88
|
);
|
|
77
89
|
|
|
78
|
-
logger.info(`
|
|
90
|
+
logger.info(`checkout successful`, {
|
|
91
|
+
orderPaymentId,
|
|
79
92
|
orderId: order._id,
|
|
80
93
|
type: event.type,
|
|
81
94
|
});
|
|
82
|
-
|
|
95
|
+
response.writeHead(200);
|
|
96
|
+
response.end({
|
|
97
|
+
message: 'checkout successful',
|
|
98
|
+
orderId: order._id,
|
|
99
|
+
});
|
|
100
|
+
} else if (event.type === WebhookEventTypes.SETUP_INTENT_SUCCEEDED) {
|
|
83
101
|
const setupIntent = event.data.object;
|
|
84
102
|
const { paymentProviderId, userId } = setupIntent.metadata || {};
|
|
85
103
|
|
|
86
|
-
if (!checkEnvironment(setupIntent.metadata)) {
|
|
87
|
-
response.end(JSON.stringify({ received: true, ignored: true }));
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
104
|
logger.verbose(`registered payment credential with paymentProviderId: ${paymentProviderId}`, {
|
|
92
105
|
type: event.type,
|
|
93
106
|
userId,
|
|
94
107
|
});
|
|
95
108
|
|
|
96
|
-
await modules.payment.registerCredentials(
|
|
109
|
+
const paymentCredentials = await modules.payment.registerCredentials(
|
|
97
110
|
paymentProviderId,
|
|
98
111
|
{
|
|
99
112
|
transactionContext: {
|
|
@@ -104,29 +117,23 @@ export const stripeHandler = async (request, response) => {
|
|
|
104
117
|
resolvedContext,
|
|
105
118
|
);
|
|
106
119
|
|
|
107
|
-
logger.info(`
|
|
120
|
+
logger.info(`payment credentials registration successful`, {
|
|
108
121
|
userId,
|
|
122
|
+
paymentProviderId,
|
|
123
|
+
paymentCredentialsId: paymentCredentials?._id,
|
|
109
124
|
type: event.type,
|
|
110
125
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
response.writeHead(200);
|
|
127
|
+
response.end({
|
|
128
|
+
message: 'payment credentials registration successful',
|
|
129
|
+
paymentCredentialsId: paymentCredentials?._id,
|
|
114
130
|
});
|
|
115
|
-
response.writeHead(404);
|
|
116
|
-
response.end();
|
|
117
|
-
return;
|
|
118
131
|
}
|
|
119
|
-
} catch (
|
|
120
|
-
logger.error(
|
|
132
|
+
} catch (error) {
|
|
133
|
+
logger.error(error, {
|
|
121
134
|
type: event.type,
|
|
122
135
|
});
|
|
123
|
-
response.writeHead(
|
|
124
|
-
response.end(
|
|
125
|
-
return;
|
|
136
|
+
response.writeHead(500);
|
|
137
|
+
response.end(error.message);
|
|
126
138
|
}
|
|
127
|
-
// Return a 200 response to acknowledge receipt of the event
|
|
128
|
-
logger.verbose(`event processed`, {
|
|
129
|
-
type: event.type,
|
|
130
|
-
});
|
|
131
|
-
response.end(JSON.stringify({ received: true }));
|
|
132
139
|
};
|
package/src/plugins-index.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { appleIAPHandler, configureAppleTransactionsModule } from './payment/app
|
|
|
19
19
|
import { stripeHandler } from './payment/stripe/index.js';
|
|
20
20
|
import { postfinanceCheckoutHandler } from './payment/postfinance-checkout/index.js';
|
|
21
21
|
|
|
22
|
+
import { payrexxHandler } from './payment/payrexx/index.js';
|
|
23
|
+
|
|
22
24
|
// Warehousing
|
|
23
25
|
import './warehousing/store.js';
|
|
24
26
|
import './warehousing/eth-minter.js';
|
|
@@ -79,6 +81,7 @@ import './accounts/linkedin-oauth.js';
|
|
|
79
81
|
const {
|
|
80
82
|
CRYPTOPAY_WEBHOOK_PATH = '/payment/cryptopay',
|
|
81
83
|
STRIPE_WEBHOOK_PATH = '/payment/stripe',
|
|
84
|
+
PAYREXX_WEBHOOK_PATH = '/payment/payrexx',
|
|
82
85
|
PFCHECKOUT_WEBHOOK_PATH = '/payment/postfinance-checkout',
|
|
83
86
|
DATATRANS_WEBHOOK_PATH = '/payment/datatrans/webhook',
|
|
84
87
|
APPLE_IAP_WEBHOOK_PATH = '/payment/apple-iap',
|
|
@@ -146,6 +149,13 @@ export const connectDefaultPluginsToExpress4 = (
|
|
|
146
149
|
appleIAPHandler,
|
|
147
150
|
);
|
|
148
151
|
|
|
152
|
+
useMiddlewareWithCurrentContext(
|
|
153
|
+
app,
|
|
154
|
+
PAYREXX_WEBHOOK_PATH,
|
|
155
|
+
express.json({ type: 'application/json' }),
|
|
156
|
+
payrexxHandler,
|
|
157
|
+
);
|
|
158
|
+
|
|
149
159
|
// useMiddlewareWithCurrentContext(
|
|
150
160
|
// app,
|
|
151
161
|
// MINIO_PUT_SERVER_PATH,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IDiscountAdapter } from '@unchainedshop/types/discount.js';
|
|
2
2
|
import { OrderDiscountDirector, OrderDiscountAdapter } from '@unchainedshop/core-orders';
|
|
3
|
+
import { OrderDiscountConfiguration } from '@unchainedshop/core-orders/director/OrderDiscountConfiguration.js';
|
|
3
4
|
|
|
4
|
-
export const HundredOff: IDiscountAdapter = {
|
|
5
|
+
export const HundredOff: IDiscountAdapter<OrderDiscountConfiguration> = {
|
|
5
6
|
...OrderDiscountAdapter,
|
|
6
7
|
|
|
7
8
|
key: 'shop.unchained.discount.100-off',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IDiscountAdapter } from '@unchainedshop/types/discount.js';
|
|
2
2
|
import { OrderDiscountDirector, OrderDiscountAdapter } from '@unchainedshop/core-orders';
|
|
3
|
+
import { ProductDiscountConfiguration } from '@unchainedshop/core-products/director/ProductDiscountConfiguration.js';
|
|
3
4
|
|
|
4
|
-
export const HalfPriceManual: IDiscountAdapter = {
|
|
5
|
+
export const HalfPriceManual: IDiscountAdapter<ProductDiscountConfiguration> = {
|
|
5
6
|
...OrderDiscountAdapter,
|
|
6
7
|
|
|
7
8
|
key: 'shop.unchained.discount.half-price-manual',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IDiscountAdapter } from '@unchainedshop/types/discount.js';
|
|
2
2
|
import { OrderDiscountDirector, OrderDiscountAdapter } from '@unchainedshop/core-orders';
|
|
3
|
+
import { ProductDiscountConfiguration } from '@unchainedshop/core-products/director/ProductDiscountConfiguration.js';
|
|
3
4
|
|
|
4
|
-
export const HalfPrice: IDiscountAdapter = {
|
|
5
|
+
export const HalfPrice: IDiscountAdapter<ProductDiscountConfiguration> = {
|
|
5
6
|
...OrderDiscountAdapter,
|
|
6
7
|
|
|
7
8
|
key: 'shop.unchained.discount.half-price',
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { IOrderPricingAdapter, OrderPricingRowCategory } from '@unchainedshop/types/orders.pricing.js';
|
|
2
2
|
import { OrderPricingDirector, OrderPricingAdapter } from '@unchainedshop/core-orders';
|
|
3
3
|
import { calculation as calcUtils } from '@unchainedshop/utils';
|
|
4
|
+
import { OrderDiscountConfiguration } from '@unchainedshop/core-orders/director/OrderDiscountConfiguration.js';
|
|
4
5
|
|
|
5
|
-
export const OrderDiscount: IOrderPricingAdapter = {
|
|
6
|
+
export const OrderDiscount: IOrderPricingAdapter<OrderDiscountConfiguration> = {
|
|
6
7
|
...OrderPricingAdapter,
|
|
7
8
|
|
|
8
9
|
key: 'shop.unchained.pricing.order-discount',
|
|
@@ -22,9 +23,6 @@ export const OrderDiscount: IOrderPricingAdapter = {
|
|
|
22
23
|
...pricingAdapter,
|
|
23
24
|
|
|
24
25
|
calculate: async () => {
|
|
25
|
-
// discounts need to provide a *fixedRate*
|
|
26
|
-
// if you want to add percentual discounts,
|
|
27
|
-
// add it to the order item calculation
|
|
28
26
|
const totalAmountOfItems = params.calculationSheet.total({
|
|
29
27
|
category: OrderPricingRowCategory.Items,
|
|
30
28
|
useNetPrice: false,
|
|
@@ -61,10 +59,8 @@ export const OrderDiscount: IOrderPricingAdapter = {
|
|
|
61
59
|
|
|
62
60
|
params.discounts.forEach(({ configuration, discountId }) => {
|
|
63
61
|
// First, we deduce the discount from the items
|
|
64
|
-
let alreadyDeducted = 0;
|
|
65
|
-
|
|
66
62
|
const leftInDiscountToSplit = calcUtils.calculateAmountToSplit(
|
|
67
|
-
{ ...configuration
|
|
63
|
+
{ ...configuration },
|
|
68
64
|
totalAmountOfItems,
|
|
69
65
|
);
|
|
70
66
|
const [itemsDiscountAmount, itemsTaxAmount] = calcUtils.applyDiscountToMultipleShares(
|
|
@@ -72,11 +68,10 @@ export const OrderDiscount: IOrderPricingAdapter = {
|
|
|
72
68
|
Math.max(0, Math.min(amountLeft, leftInDiscountToSplit)),
|
|
73
69
|
);
|
|
74
70
|
amountLeft -= itemsDiscountAmount;
|
|
75
|
-
alreadyDeducted += itemsDiscountAmount;
|
|
76
71
|
|
|
77
72
|
// After the items, we deduct the remaining discount from payment & delivery fees
|
|
78
73
|
const leftInFeesToSplit = calcUtils.calculateAmountToSplit(
|
|
79
|
-
{ ...configuration
|
|
74
|
+
{ ...configuration },
|
|
80
75
|
totalAmountOfPaymentAndDelivery,
|
|
81
76
|
);
|
|
82
77
|
const [deliveryAndPaymentDiscountAmount, deliveryAndPaymentTaxAmount] =
|
|
@@ -85,7 +80,6 @@ export const OrderDiscount: IOrderPricingAdapter = {
|
|
|
85
80
|
Math.max(0, Math.min(amountLeft, leftInFeesToSplit)),
|
|
86
81
|
);
|
|
87
82
|
amountLeft -= deliveryAndPaymentDiscountAmount;
|
|
88
|
-
alreadyDeducted += itemsDiscountAmount;
|
|
89
83
|
|
|
90
84
|
const discountAmount = (itemsDiscountAmount + deliveryAndPaymentDiscountAmount) * -1;
|
|
91
85
|
const taxAmount = (itemsTaxAmount + deliveryAndPaymentTaxAmount) * -1;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { IOrderPricingAdapter, OrderPricingRowCategory } from '@unchainedshop/types/orders.pricing.js';
|
|
2
2
|
import { OrderPricingDirector, OrderPricingAdapter } from '@unchainedshop/core-orders';
|
|
3
3
|
import { calculation as calcUtils } from '@unchainedshop/utils';
|
|
4
|
+
import { OrderDiscountConfiguration } from '@unchainedshop/core-orders/director/OrderDiscountConfiguration.js';
|
|
4
5
|
|
|
5
|
-
const OrderItemsDiscount: IOrderPricingAdapter = {
|
|
6
|
+
const OrderItemsDiscount: IOrderPricingAdapter<OrderDiscountConfiguration> = {
|
|
6
7
|
...OrderPricingAdapter,
|
|
7
8
|
|
|
8
9
|
key: 'shop.unchained.pricing.order-items-discount',
|
|
@@ -38,15 +39,19 @@ const OrderItemsDiscount: IOrderPricingAdapter = {
|
|
|
38
39
|
),
|
|
39
40
|
);
|
|
40
41
|
|
|
41
|
-
let
|
|
42
|
+
let amountLeft = totalAmountOfItems;
|
|
42
43
|
|
|
43
44
|
params.discounts.forEach(({ configuration, discountId }) => {
|
|
44
45
|
// First, we deduce the discount from the items
|
|
46
|
+
const leftInItemsToSplit = calcUtils.calculateAmountToSplit(
|
|
47
|
+
{ ...configuration },
|
|
48
|
+
totalAmountOfItems,
|
|
49
|
+
);
|
|
45
50
|
const [itemsDiscountAmount, itemsTaxAmount] = calcUtils.applyDiscountToMultipleShares(
|
|
46
51
|
itemShares,
|
|
47
|
-
|
|
52
|
+
Math.max(0, Math.min(amountLeft, leftInItemsToSplit)),
|
|
48
53
|
);
|
|
49
|
-
|
|
54
|
+
amountLeft -= itemsDiscountAmount;
|
|
50
55
|
|
|
51
56
|
const discountAmount = itemsDiscountAmount * -1;
|
|
52
57
|
const taxAmount = itemsTaxAmount * -1;
|
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
} from '@unchainedshop/types/products.pricing.js';
|
|
6
6
|
import { ProductPricingDirector, ProductPricingAdapter } from '@unchainedshop/core-products';
|
|
7
7
|
import { calculation as calcUtils } from '@unchainedshop/utils';
|
|
8
|
+
import { ProductDiscountConfiguration } from '@unchainedshop/core-products/director/ProductDiscountConfiguration.js';
|
|
8
9
|
|
|
9
|
-
const ProductDiscount: IProductPricingAdapter = {
|
|
10
|
+
const ProductDiscount: IProductPricingAdapter<ProductDiscountConfiguration> = {
|
|
10
11
|
...ProductPricingAdapter,
|
|
11
12
|
|
|
12
13
|
key: 'shop.unchained.pricing.product-discount',
|
|
@@ -21,11 +22,15 @@ const ProductDiscount: IProductPricingAdapter = {
|
|
|
21
22
|
actions: (params) => {
|
|
22
23
|
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const addDiscounts = (
|
|
26
|
+
discount: Discount<ProductDiscountConfiguration>,
|
|
27
|
+
total: number,
|
|
28
|
+
resolvedConfiguration: ProductDiscountConfiguration,
|
|
29
|
+
) => {
|
|
30
|
+
const { discountId } = discount;
|
|
31
|
+
const { isNetPrice = false, taxRate, ...meta } = resolvedConfiguration;
|
|
32
|
+
const amount = calcUtils.applyRate(resolvedConfiguration, total);
|
|
33
|
+
const isTaxable = taxRate === undefined || taxRate === null;
|
|
29
34
|
pricingAdapter.resultSheet().addDiscount({
|
|
30
35
|
amount: amount * -1,
|
|
31
36
|
discountId,
|
|
@@ -33,28 +38,39 @@ const ProductDiscount: IProductPricingAdapter = {
|
|
|
33
38
|
isTaxable,
|
|
34
39
|
meta: { adapter: ProductDiscount.key, ...meta },
|
|
35
40
|
});
|
|
41
|
+
if (!isTaxable && taxRate) {
|
|
42
|
+
const taxAmount = calcUtils.getTaxAmount(amount, taxRate, isNetPrice);
|
|
43
|
+
pricingAdapter.resultSheet().addTax({
|
|
44
|
+
amount: taxAmount * -1,
|
|
45
|
+
rate: taxRate,
|
|
46
|
+
meta: { adapter: ProductDiscount.key, discountId, ...meta },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
36
49
|
};
|
|
37
50
|
|
|
38
51
|
return {
|
|
39
52
|
...pricingAdapter,
|
|
40
53
|
|
|
41
54
|
calculate: async () => {
|
|
42
|
-
const taxableTotal = params.calculationSheet.sum({
|
|
43
|
-
category: ProductPricingRowCategory.Item,
|
|
44
|
-
isTaxable: true,
|
|
45
|
-
});
|
|
46
|
-
const nonTaxableTotal = params.calculationSheet.sum({
|
|
47
|
-
category: ProductPricingRowCategory.Item,
|
|
48
|
-
isTaxable: false,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
55
|
params.discounts.forEach((discount) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const { configuration } = discount;
|
|
57
|
+
|
|
58
|
+
const resolvedConfiguration = configuration.customPriceConfigurationResolver
|
|
59
|
+
? configuration.customPriceConfigurationResolver(
|
|
60
|
+
params.context.product,
|
|
61
|
+
params.context.quantity,
|
|
62
|
+
params.context.configuration,
|
|
63
|
+
)
|
|
64
|
+
: configuration;
|
|
65
|
+
|
|
66
|
+
if (!resolvedConfiguration) return;
|
|
67
|
+
|
|
68
|
+
const total = params.calculationSheet.total({
|
|
69
|
+
category: ProductPricingRowCategory.Item,
|
|
70
|
+
useNetPrice: resolvedConfiguration.isNetPrice,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
addDiscounts(discount, total.amount, resolvedConfiguration);
|
|
58
74
|
});
|
|
59
75
|
|
|
60
76
|
return pricingAdapter.calculate();
|