Modify order return page to handle waiting fulfillments (#1349)

* Modify return page to show quantityToFulfill

* Include calculation of waiting items on refunds

* Cleanups

* Fix return option

* Fix tests, cleanup

* Refactor getProductsAmountValues

* Fix unfulfilled max quantity selection

* Count waiting fulfillments when choosing if replace is possible

* Trigger deploy

* Fix empty unfulfilled line display when no lines presnt on return
This commit is contained in:
Tomasz Szymański 2021-09-09 12:59:37 +02:00 committed by GitHub
parent 0dc82f5825
commit 457d50c251
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 187 additions and 57 deletions

View file

@ -2,7 +2,8 @@ import { OrderDetails_order } from "@saleor/orders/types/OrderDetails";
import {
getFulfilledFulfillemnts,
getUnfulfilledLines
getUnfulfilledLines,
getWaitingFulfillments
} from "../OrderReturnPage/utils";
export const hasAnyItemsReplaceable = (order?: OrderDetails_order) => {
@ -12,9 +13,13 @@ export const hasAnyItemsReplaceable = (order?: OrderDetails_order) => {
const hasAnyUnfulfilledItems = getUnfulfilledLines(order).length > 0;
const hasAnyWaitingLines = getWaitingFulfillments(order).length > 0;
const hasAnyFulfilmentsToReturn = getFulfilledFulfillemnts(order).length > 0;
return hasAnyUnfulfilledItems || hasAnyFulfilmentsToReturn;
return (
hasAnyUnfulfilledItems || hasAnyFulfilmentsToReturn || hasAnyWaitingLines
);
};
export interface ConditionalItem {

View file

@ -40,12 +40,19 @@ const getShipmentCost = (order: OrderRefundData_order) =>
const getMaxRefund = (order: OrderRefundData_order) => order?.totalCaptured;
export const getProductsAmountValues = (
order: OrderRefundData_order,
fulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>,
unfulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>,
shipmentCosts
): OrderRefundAmountValuesProps => {
export const getProductsAmountValues = ({
order,
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities,
refundShipmentCosts
}: {
order: OrderRefundData_order;
fulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>;
waitingItemsQuantities: FormsetData<null | LineItemData, string | number>;
unfulfilledItemsQuantities: FormsetData<null | LineItemData, string | number>;
refundShipmentCosts: any;
}): OrderRefundAmountValuesProps => {
const authorizedAmount = getAuthorizedAmount(order);
const shipmentCost = getShipmentCost(order);
@ -55,17 +62,22 @@ export const getProductsAmountValues = (
order?.lines,
unfulfilledItemsQuantities as FormsetData<null, string>
);
const waitingLinesSum = getAllFulfillmentLinesPriceSum(
order?.fulfillments,
waitingItemsQuantities as FormsetData<null, string>
);
const allFulfillmentLinesSum = getAllFulfillmentLinesPriceSum(
order?.fulfillments,
fulfilledItemsQuantities as FormsetData<null, string>
);
const allLinesSum = refundedLinesSum + allFulfillmentLinesSum;
const allLinesSum =
refundedLinesSum + allFulfillmentLinesSum + waitingLinesSum;
const calculatedTotalAmount = getCalculatedTotalAmount({
allLinesSum,
maxRefund,
previouslyRefunded,
shipmentCost,
shipmentCosts
shipmentCosts: refundShipmentCosts
});
const selectedProductsValue = authorizedAmount?.currency && {
@ -150,6 +162,7 @@ export const getReturnProductsAmountValues = (
const {
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities,
refundShipmentCosts
} = formData;
@ -175,12 +188,13 @@ export const getReturnProductsAmountValues = (
};
return {
...getProductsAmountValues(
...getProductsAmountValues({
order,
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities,
refundShipmentCosts
),
}),
refundTotalAmount,
replacedProductsValue,
selectedProductsValue
@ -195,9 +209,10 @@ export const getRefundProductsAmountValues = (
refundedProductQuantities
}: OrderRefundFormData
) =>
getProductsAmountValues(
getProductsAmountValues({
order,
refundedFulfilledProductQuantities,
refundedProductQuantities,
fulfilledItemsQuantities: refundedFulfilledProductQuantities,
waitingItemsQuantities: [],
unfulfilledItemsQuantities: refundedProductQuantities,
refundShipmentCosts
);
});

View file

@ -16,8 +16,9 @@ import OrderRefundForm, { OrderRefundSubmitData } from "./form";
import ItemsCard from "./OrderReturnRefundItemsCard/ReturnItemsCard";
import {
getFulfilledFulfillemnts,
getParsedFulfiledLines,
getUnfulfilledLines
getParsedLines,
getUnfulfilledLines,
getWaitingFulfillments
} from "./utils";
const messages = defineMessages({
@ -46,10 +47,15 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => {
return (
<OrderRefundForm order={order} onSubmit={onSubmit}>
{({ data, handlers, change, submit }) => {
const { fulfilledItemsQuantities, unfulfilledItemsQuantities } = data;
const {
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities
} = data;
const hasAnyItemsSelected =
fulfilledItemsQuantities.some(({ value }) => !!value) ||
waitingItemsQuantities.some(({ value }) => !!value) ||
unfulfilledItemsQuantities.some(({ value }) => !!value);
return (
@ -83,6 +89,27 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => {
<CardSpacer />
</>
)}
{renderCollection(
getWaitingFulfillments(order),
({ id, lines }) => (
<React.Fragment key={id}>
<ItemsCard
errors={errors}
order={order}
fulfilmentId={id}
lines={getParsedLines(lines)}
itemsQuantities={data.waitingItemsQuantities}
itemsSelections={data.itemsToBeReplaced}
onChangeQuantity={handlers.changeWaitingItemsQuantity}
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
id
)}
onChangeSelected={handlers.changeItemsToBeReplaced}
/>
<CardSpacer />
</React.Fragment>
)
)}
{renderCollection(
getFulfilledFulfillemnts(order),
({ id, lines }) => (
@ -91,11 +118,11 @@ const OrderRefundPage: React.FC<OrderReturnPageProps> = props => {
errors={errors}
order={order}
fulfilmentId={id}
lines={getParsedFulfiledLines(lines)}
lines={getParsedLines(lines)}
itemsQuantities={data.fulfilledItemsQuantities}
itemsSelections={data.itemsToBeReplaced}
onChangeQuantity={handlers.changeFulfiledItemsQuantity}
onSetMaxQuantity={handlers.handleSetMaximalFulfiledItemsQuantities(
onSetMaxQuantity={handlers.handleSetMaximalItemsQuantities(
id
)}
onChangeSelected={handlers.changeItemsToBeReplaced}

View file

@ -163,7 +163,7 @@ const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({
line => {
const {
quantity,
quantityFulfilled,
quantityToFulfill,
id,
thumbnail,
unitPrice,
@ -175,9 +175,7 @@ const ItemsCard: React.FC<OrderReturnRefundLinesCardProps> = ({
.isRefunded;
const isReplacable = !!variant && !isRefunded;
const isReturnable = !!variant;
const lineQuantity = fulfilmentId
? quantity
: quantity - quantityFulfilled;
const lineQuantity = fulfilmentId ? quantity : quantityToFulfill;
const isSelected = itemsSelections.find(getById(id))?.value;
const currentQuantity = itemsQuantities.find(getById(id))?.value;
const anyLineWithoutVariant = lines.some(

View file

@ -39,15 +39,17 @@ export interface OrderReturnData {
export interface OrderReturnHandlers {
changeFulfiledItemsQuantity: FormsetChange<number>;
changeWaitingItemsQuantity: FormsetChange<number>;
changeUnfulfiledItemsQuantity: FormsetChange<number>;
changeItemsToBeReplaced: FormsetChange<boolean>;
handleSetMaximalFulfiledItemsQuantities;
handleSetMaximalItemsQuantities;
handleSetMaximalUnfulfiledItemsQuantities;
}
export interface OrderReturnFormData extends OrderReturnData {
itemsToBeReplaced: FormsetReplacementData;
fulfilledItemsQuantities: FormsetQuantityData;
waitingItemsQuantities: FormsetQuantityData;
unfulfilledItemsQuantities: FormsetQuantityData;
}
@ -109,10 +111,27 @@ function useOrderReturnForm(
return refundedFulfilmentsItems.concat(fulfilledFulfillmentsItems);
};
const getItemsWaiting = () => {
const commonOptions = {
initialValue: 0,
isFulfillment: true
};
return getParsedLineDataForFulfillmentStatus(
order,
FulfillmentStatus.WAITING_FOR_APPROVAL,
commonOptions
);
};
const fulfiledItemsQuatities = useFormset<LineItemData, number>(
getItemsFulfilled()
);
const waitingItemsQuantities = useFormset<LineItemData, number>(
getItemsWaiting()
);
const getItemsToBeReplaced = () => {
if (!order) {
return [];
@ -134,10 +153,17 @@ function useOrderReturnForm(
{ initialValue: false, isFulfillment: true }
);
const waitingFulfillmentsItems = getParsedLineDataForFulfillmentStatus(
order,
FulfillmentStatus.WAITING_FOR_APPROVAL,
{ initialValue: false, isFulfillment: true }
);
return [
...orderLinesItems,
...refundedFulfilmentsItems,
...fulfilledFulfillmentsItems
...fulfilledFulfillmentsItems,
...waitingFulfillmentsItems
];
};
@ -149,7 +175,7 @@ function useOrderReturnForm(
const newQuantities: FormsetQuantityData = unfulfiledItemsQuantites.data.map(
({ id }) => {
const line = order.lines.find(getById(id));
const initialValue = line.quantity - line.quantityFulfilled;
const initialValue = line.quantityToFulfill;
return getLineItem(line, { initialValue });
}
@ -159,14 +185,16 @@ function useOrderReturnForm(
unfulfiledItemsQuantites.set(newQuantities);
};
const handleSetMaximalFulfiledItemsQuantities = (
fulfillmentId: string
) => () => {
const { lines } = order.fulfillments.find(getById(fulfillmentId));
const handleSetMaximalItemsQuantities = (fulfillmentId: string) => () => {
const fulfillment = order.fulfillments.find(getById(fulfillmentId));
const newQuantities: FormsetQuantityData = fulfiledItemsQuatities.data.map(
item => {
const line = lines.find(getById(item.id));
const quantities =
fulfillment.status === FulfillmentStatus.WAITING_FOR_APPROVAL
? waitingItemsQuantities
: fulfiledItemsQuatities;
const newQuantities: FormsetQuantityData = quantities.data.map(item => {
const line = fulfillment.lines.find(getById(item.id));
if (!line) {
return item;
@ -176,15 +204,15 @@ function useOrderReturnForm(
initialValue: line.quantity,
isRefunded: item.data.isRefunded
});
}
);
});
triggerChange();
fulfiledItemsQuatities.set(newQuantities);
quantities.set(newQuantities);
};
const data: OrderReturnFormData = {
fulfilledItemsQuantities: fulfiledItemsQuatities.data,
waitingItemsQuantities: waitingItemsQuantities.data,
itemsToBeReplaced: itemsToBeReplaced.data,
unfulfilledItemsQuantities: unfulfiledItemsQuantites.data,
...form.data
@ -208,11 +236,14 @@ function useOrderReturnForm(
changeFulfiledItemsQuantity: handleHandlerChange(
fulfiledItemsQuatities.change
),
changeWaitingItemsQuantity: handleHandlerChange(
waitingItemsQuantities.change
),
changeItemsToBeReplaced: handleHandlerChange(itemsToBeReplaced.change),
changeUnfulfiledItemsQuantity: handleHandlerChange(
unfulfiledItemsQuantites.change
),
handleSetMaximalFulfiledItemsQuantities,
handleSetMaximalItemsQuantities,
handleSetMaximalUnfulfiledItemsQuantities
},
hasChanged,

View file

@ -14,7 +14,7 @@ const fulfiledStatuses = [
];
export const getOrderUnfulfilledLines = (order: OrderDetails_order) =>
order?.lines.filter(line => line.quantityFulfilled !== line.quantity) || [];
order?.lines.filter(line => line.quantityToFulfill > 0) || [];
export const getFulfilledFulfillment = fulfillment =>
fulfiledStatuses.includes(fulfillment.status);
@ -22,12 +22,23 @@ export const getFulfilledFulfillment = fulfillment =>
export const getFulfilledFulfillemnts = (order?: OrderDetails_order) =>
order?.fulfillments.filter(getFulfilledFulfillment) || [];
export const getWaitingFulfillments = (order: OrderDetails_order) =>
order?.fulfillments.filter(
f => f.status === FulfillmentStatus.WAITING_FOR_APPROVAL
) || [];
export const getUnfulfilledLines = (order?: OrderDetails_order) =>
order?.lines.filter(line => line.quantity !== line.quantityFulfilled) || [];
order?.lines.filter(line => line.quantityToFulfill > 0) || [];
export const getAllOrderFulfilledLines = (order?: OrderDetails_order) =>
getFulfilledFulfillemnts(order).reduce(
(result, { lines }) => [...result, ...getParsedFulfiledLines(lines)],
(result, { lines }) => [...result, ...getParsedLines(lines)],
[]
);
export const getAllOrderWaitingLines = (order?: OrderDetails_order) =>
getWaitingFulfillments(order).reduce(
(result, { lines }) => [...result, ...getParsedLines(lines)],
[]
);
@ -77,11 +88,11 @@ export const getParsedLinesOfFulfillments = (
fullfillments: OrderDetails_order_fulfillments[]
) =>
fullfillments.reduce(
(result, { lines }) => [...result, ...getParsedFulfiledLines(lines)],
(result, { lines }) => [...result, ...getParsedLines(lines)],
[]
);
export const getParsedFulfiledLines = (
export const getParsedLines = (
lines: OrderDetailsFragment_fulfillments_lines[]
) =>
lines.map(({ id, quantity, orderLine }) => ({

View file

@ -1522,6 +1522,8 @@ describe("Get the total value of all selected products", () => {
}
];
const waitingItemsQuantities: FormsetData<LineItemData, number> = [];
const itemsToBeReplaced: FormsetData<LineItemData, boolean> = [
{
data: { isFulfillment: false, isRefunded: false },
@ -1591,6 +1593,7 @@ describe("Get the total value of all selected products", () => {
},
{
itemsToBeReplaced,
waitingItemsQuantities,
unfulfilledItemsQuantities,
fulfilledItemsQuantities
}

View file

@ -1,7 +1,7 @@
import { IMoney, subtractMoney } from "@saleor/components/Money";
import { WarehouseFragment } from "@saleor/fragments/types/WarehouseFragment";
import { FormsetData } from "@saleor/hooks/useFormset";
import { OrderErrorCode } from "@saleor/types/globalTypes";
import { FulfillmentStatus, OrderErrorCode } from "@saleor/types/globalTypes";
import {
LineItemData,
@ -9,6 +9,7 @@ import {
} from "../components/OrderReturnPage/form";
import {
getAllOrderFulfilledLines,
getAllOrderWaitingLines,
getById
} from "../components/OrderReturnPage/utils";
import { FulfillOrder_orderFulfill_errors } from "../types/FulfillOrder";
@ -80,16 +81,33 @@ const getItemPriceAndQuantity = ({
return { selectedQuantity, unitPrice };
};
const getFulfillmentByFulfillmentLineId = (order, fulfillmentLineId) => {
for (const fulfillment of order.fulfillments) {
if (fulfillment.lines.find(getById(fulfillmentLineId))) {
return fulfillment;
}
}
};
const selectItemPriceAndQuantity = (
order: OrderDetails_order,
{
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities
}: Partial<OrderReturnFormData>,
id: string,
isFulfillment: boolean
) =>
isFulfillment
) => {
const fulfillment = getFulfillmentByFulfillmentLineId(order, id);
if (fulfillment?.status === FulfillmentStatus.WAITING_FOR_APPROVAL) {
return getItemPriceAndQuantity({
id,
itemsQuantities: waitingItemsQuantities,
orderLines: getAllOrderWaitingLines(order)
});
}
return isFulfillment
? getItemPriceAndQuantity({
id,
itemsQuantities: fulfilledItemsQuantities,
@ -100,12 +118,14 @@ const selectItemPriceAndQuantity = (
itemsQuantities: unfulfilledItemsQuantities,
orderLines: order.lines
});
};
export const getReplacedProductsAmount = (
order: OrderDetails_order,
{
itemsToBeReplaced,
unfulfilledItemsQuantities,
waitingItemsQuantities,
fulfilledItemsQuantities
}: Partial<OrderReturnFormData>
) => {
@ -124,7 +144,11 @@ export const getReplacedProductsAmount = (
const { unitPrice, selectedQuantity } = selectItemPriceAndQuantity(
order,
{ fulfilledItemsQuantities, unfulfilledItemsQuantities },
{
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities
},
id,
isFulfillment
);
@ -137,7 +161,12 @@ export const getReplacedProductsAmount = (
export const getReturnSelectedProductsAmount = (
order: OrderDetails_order,
{ itemsToBeReplaced, unfulfilledItemsQuantities, fulfilledItemsQuantities }
{
itemsToBeReplaced,
waitingItemsQuantities,
unfulfilledItemsQuantities,
fulfilledItemsQuantities
}
) => {
if (!order) {
return 0;
@ -155,7 +184,13 @@ export const getReturnSelectedProductsAmount = (
orderLines: getAllOrderFulfilledLines(order)
});
return unfulfilledItemsValue + fulfiledItemsValue;
const waitingItemsValue = getPartialProductsValue({
itemsQuantities: waitingItemsQuantities,
itemsToBeReplaced,
orderLines: getAllOrderWaitingLines(order)
});
return unfulfilledItemsValue + fulfiledItemsValue + waitingItemsValue;
};
const getPartialProductsValue = ({

View file

@ -24,6 +24,7 @@ class ReturnFormDataParser {
public getParsedData = (): OrderReturnProductsInput => {
const {
fulfilledItemsQuantities,
waitingItemsQuantities,
unfulfilledItemsQuantities,
refundShipmentCosts
} = this.formData;
@ -32,6 +33,10 @@ class ReturnFormDataParser {
OrderReturnFulfillmentLineInput
>(fulfilledItemsQuantities, "fulfillmentLineId");
const waitingLines = this.getParsedLineData<
OrderReturnFulfillmentLineInput
>(waitingItemsQuantities, "fulfillmentLineId");
const orderLines = this.getParsedLineData<OrderReturnLineInput>(
unfulfilledItemsQuantities,
"orderLineId"
@ -39,7 +44,7 @@ class ReturnFormDataParser {
return {
amountToRefund: this.getAmountToRefund(),
fulfillmentLines,
fulfillmentLines: fulfillmentLines.concat(waitingLines),
includeShippingCosts: refundShipmentCosts,
orderLines,
refund: this.getShouldRefund(orderLines, fulfillmentLines)