Skip to content

Commit 7f8229f

Browse files
fix(SFI-1599): use basePrice for item amount in L2/L3 data and fix discount calculation (#1490)
* fix(SFI-1599): use basePrice for item amount in L2/L3 data and fix discount calculation * fix(SFI-1599): correct tax calculation in L2/L3 data by removing per-unit division
1 parent 3377c80 commit 7f8229f

2 files changed

Lines changed: 59 additions & 41 deletions

File tree

src/cartridges/int_adyen_SFRA/cartridge/adyen/scripts/payments/__tests__/adyenLevelTwoThreeData.test.js

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ jest.mock('*/cartridge/adyen/utils/lineItemHelper', () => ({
1717
isProductLineItem: jest.fn(() => false),
1818
}));
1919

20-
const { getLineItems } = require('*/cartridge/adyen/scripts/payments/adyenLevelTwoThreeData');
20+
const {
21+
getLineItems,
22+
} = require('*/cartridge/adyen/scripts/payments/adyenLevelTwoThreeData');
2123
const AdyenHelper = require('*/cartridge/adyen/utils/adyenHelper');
2224

2325
describe('getLineItems (Enhanced Scheme Data)', () => {
@@ -26,7 +28,7 @@ describe('getLineItems (Enhanced Scheme Data)', () => {
2628
productID: 'SW1234567890X',
2729
quantityValue: 2,
2830
adjustedNetPrice: 100,
29-
getAdjustedTax: 20,
31+
getAdjustedTax: 20,
3032
};
3133

3234
const createMockOrderOrBasket = (customerData = {}) => ({
@@ -49,17 +51,21 @@ describe('getLineItems (Enhanced Scheme Data)', () => {
4951

5052
it('should return enhanced line item fields with tax, description, and commodity code', () => {
5153
const result = getLineItems({
52-
Order: createMockOrderOrBasket({ registered: true, customerNo: 'cust-9999' }),
54+
Order: createMockOrderOrBasket({
55+
registered: true,
56+
customerNo: 'cust-9999',
57+
}),
5358
});
5459

5560
expect(result).toEqual({
56-
'enhancedSchemeData.totalTaxAmount': 10,
61+
'enhancedSchemeData.totalTaxAmount': 20,
5762
'enhancedSchemeData.customerReference': 'cust-9999',
58-
'enhancedSchemeData.itemDetailLine1.unitPrice': '50',
59-
'enhancedSchemeData.itemDetailLine1.totalAmount': 100,
63+
'enhancedSchemeData.itemDetailLine1.unitPrice': '50',
64+
'enhancedSchemeData.itemDetailLine1.totalAmount': 100,
6065
'enhancedSchemeData.itemDetailLine1.quantity': 2,
6166
'enhancedSchemeData.itemDetailLine1.unitOfMeasure': 'EAC',
62-
'enhancedSchemeData.itemDetailLine1.commodityCode': 'mocked_comodity_code',
67+
'enhancedSchemeData.itemDetailLine1.commodityCode':
68+
'mocked_comodity_code',
6369
'enhancedSchemeData.itemDetailLine1.description': 'Super Widget',
6470
'enhancedSchemeData.itemDetailLine1.productCode': 'SW1234567890',
6571
});
@@ -68,10 +74,15 @@ describe('getLineItems (Enhanced Scheme Data)', () => {
6874
it('should truncate customerReference to 25 characters', () => {
6975
const longCustomerNo = 'very-long-customer-number-1234567890';
7076
const result = getLineItems({
71-
Order: createMockOrderOrBasket({ registered: true, customerNo: longCustomerNo }),
77+
Order: createMockOrderOrBasket({
78+
registered: true,
79+
customerNo: longCustomerNo,
80+
}),
7281
});
7382

74-
expect(result['enhancedSchemeData.customerReference'].length).toBeLessThanOrEqual(25);
83+
expect(
84+
result['enhancedSchemeData.customerReference'].length,
85+
).toBeLessThanOrEqual(25);
7586
});
7687

7788
it('should return null when no Order or Basket is passed', () => {
@@ -96,31 +107,36 @@ describe('getLineItems (Enhanced Scheme Data)', () => {
96107
toArray: () => [],
97108
}),
98109
getCustomer: () => ({
99-
getID: () => null
110+
getID: () => null,
100111
}),
101112
getCustomerNo: () => null,
102113
},
103114
});
104115

105-
expect(result['enhancedSchemeData.customerReference']).toBe('no-unique-ref');
116+
expect(result['enhancedSchemeData.customerReference']).toBe(
117+
'no-unique-ref',
118+
);
106119
});
107120

108121
it('should include discount amount when product has basePrice > adjustedPrice', () => {
109122
const lineItemHelper = require('*/cartridge/adyen/utils/lineItemHelper');
110123
lineItemHelper.isProductLineItem.mockReturnValueOnce(true);
111124

112125
AdyenHelper.getCurrencyValueForApi = jest.fn(() => ({
113-
divide: jest.fn(() => ({
114-
value: { toFixed: () => '10' },
115-
})),
126+
value: { toFixed: () => '20' },
116127
}));
117128

129+
// qty=2, basePrice=60/unit (line=120), adjustedPrice=100 (line total).
130+
// Expected total line discount = 60*2 - 100 = 20.
118131
const discountedLineItem = {
119132
...mockLineItem,
120133
basePrice: {
121-
value: 120,
122-
subtract: jest.fn((adjustedPrice) => ({
123-
value: 20,
134+
value: 60,
135+
multiply: jest.fn(() => ({
136+
value: 120,
137+
subtract: jest.fn(() => ({
138+
value: 20,
139+
})),
124140
})),
125141
},
126142
adjustedPrice: {
@@ -145,8 +161,12 @@ describe('getLineItems (Enhanced Scheme Data)', () => {
145161
},
146162
});
147163

148-
expect(result['enhancedSchemeData.itemDetailLine1.discountAmount']).toBe('10');
164+
// Total line discount (not per-unit). Adyen formula:
165+
// totalAmount = quantity * unitPrice - discountAmount => 100 = 2 * 60 - 20
166+
expect(result['enhancedSchemeData.itemDetailLine1.discountAmount']).toBe(
167+
'20',
168+
);
169+
expect(result['enhancedSchemeData.itemDetailLine1.unitPrice']).toBe('60');
170+
expect(result['enhancedSchemeData.itemDetailLine1.totalAmount']).toBe(100);
149171
});
150-
151-
152172
});

src/cartridges/int_adyen_SFRA/cartridge/adyen/scripts/payments/adyenLevelTwoThreeData.js

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,15 @@ function getShopperReference(orderOrBasket) {
3434
}
3535

3636
function getDiscountAmount(lineItem, quantity) {
37-
if (LineItemHelper.isProductLineItem(lineItem)) {
38-
const { basePrice } = lineItem;
39-
const { adjustedPrice } = lineItem;
40-
if (basePrice && adjustedPrice && basePrice.value > adjustedPrice.value) {
41-
const discountPerUnit = AdyenHelper.getCurrencyValueForApi(
42-
basePrice.subtract(adjustedPrice),
43-
).divide(quantity);
44-
return discountPerUnit.value.toFixed();
45-
}
46-
}
47-
return null;
37+
if (!LineItemHelper.isProductLineItem(lineItem)) return null;
38+
const { basePrice, adjustedPrice } = lineItem;
39+
if (!basePrice || !adjustedPrice) return null;
40+
// Total line discount = (per-unit basePrice * quantity) - adjustedPrice line total.
41+
const baseTotal = basePrice.multiply(quantity);
42+
if (baseTotal.value <= adjustedPrice.value) return null;
43+
return AdyenHelper.getCurrencyValueForApi(
44+
baseTotal.subtract(adjustedPrice),
45+
).value.toFixed();
4846
}
4947

5048
function collectShippingLineItems(shipments) {
@@ -60,7 +58,7 @@ function collectShippingLineItems(shipments) {
6058

6159
function buildEnhancedSchemeDataFields(
6260
index,
63-
itemAmount,
61+
unitPrice,
6462
quantity,
6563
totalAmount,
6664
commodityCode,
@@ -69,8 +67,7 @@ function buildEnhancedSchemeDataFields(
6967
discountAmount,
7068
) {
7169
return {
72-
[`enhancedSchemeData.itemDetailLine${index + 1}.unitPrice`]:
73-
itemAmount.value.toFixed(),
70+
[`enhancedSchemeData.itemDetailLine${index + 1}.unitPrice`]: unitPrice,
7471
[`enhancedSchemeData.itemDetailLine${index + 1}.totalAmount`]: totalAmount,
7572
[`enhancedSchemeData.itemDetailLine${index + 1}.quantity`]: quantity,
7673
[`enhancedSchemeData.itemDetailLine${index + 1}.unitOfMeasure`]: 'EAC',
@@ -98,17 +95,18 @@ function processLineItem(acc, lineItem, index) {
9895
const description = LineItemHelper.getDescription(lineItem);
9996
const id = LineItemHelper.getId(lineItem);
10097
const quantity = LineItemHelper.getQuantity(lineItem);
101-
const itemAmount = LineItemHelper.getItemAmount(lineItem).divide(quantity);
102-
const vatAmount = LineItemHelper.getVatAmount(lineItem).divide(quantity);
103-
const discountAmount = getDiscountAmount(lineItem, quantity);
98+
const quantityNum = parseFloat(quantity) || 1;
99+
const lineAmount = LineItemHelper.getItemAmount(lineItem);
100+
const vatAmount = LineItemHelper.getVatAmount(lineItem);
101+
const discountAmount = getDiscountAmount(lineItem, quantityNum);
104102
const commodityCode = AdyenConfigs.getAdyenLevel23CommodityCode();
105-
const unitPrice = parseFloat(itemAmount.value.toFixed());
106-
const quantityNum = parseFloat(quantity);
103+
// Derive unitPrice from totalAmount = quantity * unitPrice - discountAmount.
104+
const totalAmount = parseFloat(lineAmount.value.toFixed());
107105
const discount = discountAmount ? parseFloat(discountAmount) : 0;
108-
const totalAmount = quantityNum * unitPrice - discount;
106+
const unitPrice = ((totalAmount + discount) / quantityNum).toFixed();
109107
const currentLineItem = buildEnhancedSchemeDataFields(
110108
index,
111-
itemAmount,
109+
unitPrice,
112110
quantity,
113111
totalAmount,
114112
commodityCode,

0 commit comments

Comments
 (0)