Replace order line total calculation with API value (#2969)

* Query total price in order line data

* Add order line total to discount context consumer

* Replace frontend calculations with api value for order line totals

* Split logic for order discount and order line discount contexts

* Pass extra props to money component

* Add unit test

* Do not pass all props to Money's span
This commit is contained in:
Michał Droń 2023-01-12 13:57:39 +01:00 committed by GitHub
parent f02a5a32e1
commit 88d0cecfed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 430 additions and 58 deletions

View file

@ -24,7 +24,8 @@ export interface MoneyProps {
money: IMoney | null;
}
export const Money: React.FC<MoneyProps> = ({ money }) => {
export const Money: React.FC<MoneyProps> = props => {
const { money, ...rest } = props;
const { locale } = useLocale();
const classes = useStyles();
@ -43,7 +44,7 @@ export const Money: React.FC<MoneyProps> = ({ money }) => {
});
return (
<span className={classes.root}>
<span className={classes.root} {...rest}>
<span className={classes.currency}>{money.currency}</span>
{amount}
</span>

View file

@ -101,6 +101,9 @@ export const fragmentOrderLine = gql`
quantity
quantityFulfilled
quantityToFulfill
totalPrice {
...TaxedMoney
}
unitDiscount {
amount
currency

View file

@ -1302,6 +1302,16 @@ export const StockFragmentDoc = gql`
}
}
${WarehouseFragmentDoc}`;
export const TaxedMoneyFragmentDoc = gql`
fragment TaxedMoney on TaxedMoney {
net {
...Money
}
gross {
...Money
}
}
${MoneyFragmentDoc}`;
export const OrderLineFragmentDoc = gql`
fragment OrderLine on OrderLine {
id
@ -1333,6 +1343,9 @@ export const OrderLineFragmentDoc = gql`
quantity
quantityFulfilled
quantityToFulfill
totalPrice {
...TaxedMoney
}
unitDiscount {
amount
currency
@ -1365,7 +1378,8 @@ export const OrderLineFragmentDoc = gql`
url
}
}
${StockFragmentDoc}`;
${StockFragmentDoc}
${TaxedMoneyFragmentDoc}`;
export const FulfillmentFragmentDoc = gql`
fragment Fulfillment on Fulfillment {
id
@ -2404,16 +2418,6 @@ export const StaffMemberDetailsFragmentDoc = gql`
}
}
${StaffMemberFragmentDoc}`;
export const TaxedMoneyFragmentDoc = gql`
fragment TaxedMoney on TaxedMoney {
net {
...Money
}
gross {
...Money
}
}
${MoneyFragmentDoc}`;
export const CountryFragmentDoc = gql`
fragment Country on CountryDisplay {
country

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,54 @@
import { order } from "@saleor/orders/fixtures";
import { render, screen } from "@testing-library/react";
import React from "react";
import TableLine from "./TableLine";
jest.mock("react-intl", () => ({
useIntl: jest.fn(() => ({
formatMessage: jest.fn(x => x.defaultMessage),
})),
defineMessages: jest.fn(x => x),
}));
jest.mock("@saleor/macaw-ui", () => ({
useStyles: jest.fn(() => () => ({})),
makeStyles: jest.fn(() => () => ({})),
Avatar: jest.fn(() => () => <></>),
IconButton: jest.fn(() => () => <></>),
DeleteIcon: jest.fn(() => () => <></>),
ImageIcon: jest.fn(() => () => <></>),
}));
const mockedOrder = order("");
describe("TableLine rendering", () => {
it("renders with values from API", async () => {
// Arrange
const mockedLine = mockedOrder.lines[0];
const props = {
line: mockedLine,
onOrderLineChange: jest.fn(),
onOrderLineRemove: jest.fn(),
addOrderLineDiscount: jest.fn(),
removeOrderLineDiscount: jest.fn(),
orderLineDiscountUpdateStatus: "default" as const,
orderLineDiscountRemoveStatus: "default" as const,
openDialog: jest.fn(),
closeDialog: jest.fn(),
isDialogOpen: false,
totalDiscountedPrice: mockedLine.totalPrice.gross,
unitUndiscountedPrice: mockedLine.undiscountedUnitPrice.gross,
unitDiscountedPrice: mockedLine.unitPrice.gross,
};
// Act
render(<TableLine {...props} />);
// Assert
const tableLine = screen.getByTestId(`table-line-total-${mockedLine.id}`);
expect(tableLine).toHaveTextContent(
mockedLine.totalPrice.gross.currency.toString(),
);
});
});

View file

@ -73,13 +73,14 @@ const TableLine: React.FC<TableLineProps> = ({
closeDialog,
orderLineDiscountRemoveStatus,
isDialogOpen,
undiscountedPrice,
discountedPrice,
totalDiscountedPrice,
unitUndiscountedPrice,
unitDiscountedPrice,
orderLineDiscountUpdateStatus,
}) => {
const classes = useStyles();
const popperAnchorRef = useRef<HTMLTableRowElement | null>(null);
const { id, thumbnail, productName, productSku, quantity } = line;
const { id, thumbnail, productName, productSku } = line;
const alerts = useLineAlerts({
line,
@ -87,14 +88,14 @@ const TableLine: React.FC<TableLineProps> = ({
});
const getUnitPriceLabel = () => {
const money = <Money money={undiscountedPrice} />;
const money = <Money money={unitUndiscountedPrice} />;
if (!!orderLineDiscount) {
return (
<>
<Typography className={classes.strike}>{money}</Typography>
<Link onClick={openDialog}>
<Money money={discountedPrice} />
<Money money={unitDiscountedPrice} />
</Link>
</>
);
@ -134,7 +135,7 @@ const TableLine: React.FC<TableLineProps> = ({
anchorRef={popperAnchorRef}
onClose={closeDialog}
modalType={ORDER_LINE_DISCOUNT}
maxPrice={undiscountedPrice}
maxPrice={unitUndiscountedPrice}
onConfirm={addOrderLineDiscount}
onRemove={removeOrderLineDiscount}
existingDiscount={orderLineDiscount}
@ -146,9 +147,10 @@ const TableLine: React.FC<TableLineProps> = ({
<TableCell className={classes.colTotal}>
<Money
money={{
amount: discountedPrice.amount * quantity,
currency: discountedPrice.currency,
amount: totalDiscountedPrice.amount,
currency: totalDiscountedPrice.currency,
}}
data-test-id={`table-line-total-${line.id}`}
/>
</TableCell>
<TableCell className={classes.colAction}>

View file

@ -17,8 +17,6 @@ export const getDiscountsProvidersWrapper = (
openDialog: () => {},
closeDialog: () => {},
isDialogOpen: false,
undiscountedPrice: order.total.gross,
discountedPrice: order.total.gross,
};
const MockOrderDiscountProvider = ({ children }) => {
@ -29,6 +27,8 @@ export const getDiscountsProvidersWrapper = (
orderDiscount: null,
addOrderDiscount: () => {},
removeOrderDiscount: () => {},
discountedPrice: order.total.gross,
undiscountedPrice: order.undiscountedTotal.gross,
};
return (
@ -46,6 +46,9 @@ export const getDiscountsProvidersWrapper = (
orderLineDiscount: null,
orderLineDiscountUpdateStatus: "default",
orderLineDiscountRemoveStatus: "default",
totalDiscountedPrice: order.lines[0].totalPrice.gross,
unitUndiscountedPrice: order.lines[0].undiscountedUnitPrice.gross,
unitDiscountedPrice: order.lines[0].unitPrice.gross,
});
return (

View file

@ -97,8 +97,8 @@ const TableLine: React.FC<TableLineProps> = ({
<TableCell className={classes.colTotal} align="right">
<Money
money={{
amount: line.quantity * line.orderLine.unitPrice.gross.amount,
currency: line.orderLine.unitPrice.gross.currency,
amount: line.orderLine.totalPrice.gross.amount,
currency: line.orderLine.totalPrice.gross.currency,
}}
/>
</TableCell>

View file

@ -1093,6 +1093,19 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1207,6 +1220,19 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1329,6 +1355,19 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 55.53,
currency: "USD",
},
net: {
__typename: "Money",
amount: 55.53,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1428,7 +1467,19 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1672,6 +1723,19 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1771,6 +1835,19 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
__typename: "Image" as "Image",
url: placeholder,
},
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",

View file

@ -576,6 +576,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 2,
quantityFulfilled: 2,
quantityToFulfill: 0,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -676,6 +689,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 10,
quantityFulfilled: 2,
quantityToFulfill: 8,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
net: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -776,6 +802,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 6,
quantityFulfilled: 1,
quantityToFulfill: 5,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 478.26,
currency: "USD",
},
net: {
__typename: "Money",
amount: 478.26,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -882,6 +921,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 20,
quantityFulfilled: 6,
quantityToFulfill: 14,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -987,6 +1039,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 25,
quantityFulfilled: 8,
quantityToFulfill: 17,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1092,6 +1157,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 10,
quantityFulfilled: 3,
quantityToFulfill: 7,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
net: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1197,6 +1275,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 20,
quantityFulfilled: 6,
quantityToFulfill: 14,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1302,6 +1393,19 @@ describe("Get the total value of all replaced products", () => {
quantity: 25,
quantityFulfilled: 8,
quantityToFulfill: 17,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1541,6 +1645,19 @@ describe("Get the total value of all selected products", () => {
quantity: 2,
quantityFulfilled: 2,
quantityToFulfill: 0,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1641,6 +1758,19 @@ describe("Get the total value of all selected products", () => {
quantity: 10,
quantityFulfilled: 2,
quantityToFulfill: 8,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
net: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1741,6 +1871,19 @@ describe("Get the total value of all selected products", () => {
quantity: 6,
quantityFulfilled: 1,
quantityToFulfill: 5,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 478.26,
currency: "USD",
},
net: {
__typename: "Money",
amount: 478.26,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1847,6 +1990,19 @@ describe("Get the total value of all selected products", () => {
quantity: 20,
quantityFulfilled: 6,
quantityToFulfill: 14,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1594.2,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -1952,6 +2108,19 @@ describe("Get the total value of all selected products", () => {
quantity: 25,
quantityFulfilled: 8,
quantityToFulfill: 17,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
net: {
__typename: "Money",
amount: 1992.75,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -2057,6 +2226,19 @@ describe("Get the total value of all selected products", () => {
quantity: 10,
quantityFulfilled: 3,
quantityToFulfill: 7,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
net: {
__typename: "Money",
amount: 797.1,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -2290,6 +2472,19 @@ describe("Merge repeated order lines of fulfillment lines", () => {
quantity: 2,
quantityFulfilled: 2,
quantityToFulfill: 0,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -2395,6 +2590,19 @@ describe("Merge repeated order lines of fulfillment lines", () => {
quantity: 2,
quantityFulfilled: 2,
quantityToFulfill: 0,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
net: {
__typename: "Money",
amount: 159.42,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",
@ -2500,6 +2708,19 @@ describe("Merge repeated order lines of fulfillment lines", () => {
quantity: 3,
quantityFulfilled: 1,
quantityToFulfill: 2,
totalPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 239.13,
currency: "USD",
},
net: {
__typename: "Money",
amount: 239.13,
currency: "USD",
},
},
undiscountedUnitPrice: {
__typename: "TaxedMoney",
currency: "USD",

View file

@ -1,4 +1,5 @@
import {
MoneyFragment,
OrderDetailsFragment,
useOrderDiscountAddMutation,
useOrderDiscountDeleteMutation,
@ -25,6 +26,8 @@ export interface OrderDiscountContextConsumerProps
orderDiscount?: OrderDiscountData;
addOrderDiscount: (data: OrderDiscountCommonInput) => void;
removeOrderDiscount: () => void;
discountedPrice: MoneyFragment;
undiscountedPrice: MoneyFragment;
}
interface OrderDiscountProviderProps {

View file

@ -1,4 +1,5 @@
import {
MoneyFragment,
OrderDetailsFragment,
useOrderLineDiscountRemoveMutation,
useOrderLineDiscountUpdateMutation,
@ -30,6 +31,9 @@ export interface OrderLineDiscountContextConsumerProps
orderLineDiscount?: OrderLineDiscountData;
orderLineDiscountUpdateStatus: ConfirmButtonTransitionState;
orderLineDiscountRemoveStatus: ConfirmButtonTransitionState;
totalDiscountedPrice: MoneyFragment;
unitUndiscountedPrice: MoneyFragment;
unitDiscountedPrice: MoneyFragment;
}
interface DiscountProviderProps {
@ -108,8 +112,10 @@ export const OrderLineDiscountProvider: React.FC<DiscountProviderProps> = ({
orderLineDiscountRemoveStatus: orderLineDiscountRemoveOpts.status,
closeDialog: handleCloseDialog,
openDialog: handleOpenDialog(orderLineId),
discountedPrice: getOrderLine(orderLineId).unitPrice.gross,
undiscountedPrice: getOrderLine(orderLineId).undiscountedUnitPrice.gross,
totalDiscountedPrice: getOrderLine(orderLineId).totalPrice.gross,
unitDiscountedPrice: getOrderLine(orderLineId).unitPrice.gross,
unitUndiscountedPrice: getOrderLine(orderLineId).undiscountedUnitPrice
.gross,
});
return (

View file

@ -20,8 +20,6 @@ export interface OrderDiscountConsumerCommonProps {
openDialog: () => void;
closeDialog: () => void;
isDialogOpen: boolean;
undiscountedPrice: MoneyFragment;
discountedPrice: MoneyFragment;
}
export interface OrderLineDiscountConsumerProps {