Move fulfillment warehouse selection to fulfilll page (#2199)
* Move order fulfillment warehouse selection to fulfill page * Use modal to select warehouse on fulfill order page * Remove tracking number input from fulfill order page * Update visual structure of fulfill order page * Fix fulfill order page styles * Update order fulfill utils
This commit is contained in:
parent
804e14768b
commit
2c97b2da41
28 changed files with 1432 additions and 2060 deletions
|
@ -2602,6 +2602,10 @@
|
||||||
"context": "header",
|
"context": "header",
|
||||||
"string": "Edit Media"
|
"string": "Edit Media"
|
||||||
},
|
},
|
||||||
|
"Ila7WO": {
|
||||||
|
"context": "change warehouse dialog description",
|
||||||
|
"string": "Choose warehouse from which you want to fulfill {productName}"
|
||||||
|
},
|
||||||
"ImTelT": {
|
"ImTelT": {
|
||||||
"context": "card subtitle",
|
"context": "card subtitle",
|
||||||
"string": "Select warehouses that will be used in this channel. You can assign warehouses to multiple channels."
|
"string": "Select warehouses that will be used in this channel. You can assign warehouses to multiple channels."
|
||||||
|
@ -2642,6 +2646,10 @@
|
||||||
"J0UdxG": {
|
"J0UdxG": {
|
||||||
"string": "Skip pricing for now"
|
"string": "Skip pricing for now"
|
||||||
},
|
},
|
||||||
|
"J0lNnk": {
|
||||||
|
"context": "column label",
|
||||||
|
"string": "Warehouse"
|
||||||
|
},
|
||||||
"J3uE0t": {
|
"J3uE0t": {
|
||||||
"context": "section header",
|
"context": "section header",
|
||||||
"string": "Attribute Values"
|
"string": "Attribute Values"
|
||||||
|
@ -3628,10 +3636,6 @@
|
||||||
"context": "product field",
|
"context": "product field",
|
||||||
"string": "Charge Taxes"
|
"string": "Charge Taxes"
|
||||||
},
|
},
|
||||||
"QWCh6/": {
|
|
||||||
"context": "change warehouse dialog description",
|
|
||||||
"string": "Choose warehouse you want to fulfill this order from"
|
|
||||||
},
|
|
||||||
"QY7FSs": {
|
"QY7FSs": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "create product type"
|
"string": "create product type"
|
||||||
|
@ -3660,10 +3664,6 @@
|
||||||
"context": "years after label",
|
"context": "years after label",
|
||||||
"string": "years after issue"
|
"string": "years after issue"
|
||||||
},
|
},
|
||||||
"QfKQx3": {
|
|
||||||
"context": "warehouse label when multiple products are unavailable",
|
|
||||||
"string": "{productCount} products are unavailable at this location"
|
|
||||||
},
|
|
||||||
"QiN4hv": {
|
"QiN4hv": {
|
||||||
"context": "active",
|
"context": "active",
|
||||||
"string": "Active"
|
"string": "Active"
|
||||||
|
@ -4369,6 +4369,10 @@
|
||||||
"W5SK5c": {
|
"W5SK5c": {
|
||||||
"string": "Select content type"
|
"string": "Select content type"
|
||||||
},
|
},
|
||||||
|
"W5hb5H": {
|
||||||
|
"context": "warehouse input",
|
||||||
|
"string": "Warehouse"
|
||||||
|
},
|
||||||
"W8i2Ez": {
|
"W8i2Ez": {
|
||||||
"context": "product field",
|
"context": "product field",
|
||||||
"string": "Name"
|
"string": "Name"
|
||||||
|
@ -5692,6 +5696,10 @@
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Set as default billing address"
|
"string": "Set as default billing address"
|
||||||
},
|
},
|
||||||
|
"hLSgWj": {
|
||||||
|
"context": "warehouse label number available of products",
|
||||||
|
"string": "{productCount} available at this location"
|
||||||
|
},
|
||||||
"hLUYBt": {
|
"hLUYBt": {
|
||||||
"context": "payment status",
|
"context": "payment status",
|
||||||
"string": "Pending"
|
"string": "Pending"
|
||||||
|
@ -6571,10 +6579,6 @@
|
||||||
"oYGfnY": {
|
"oYGfnY": {
|
||||||
"string": "ZIP / Postal code"
|
"string": "ZIP / Postal code"
|
||||||
},
|
},
|
||||||
"oiaUni": {
|
|
||||||
"context": "Support text under page header",
|
|
||||||
"string": "Fulfilling from {warehouseName}"
|
|
||||||
},
|
|
||||||
"oiuwOl": {
|
"oiuwOl": {
|
||||||
"context": "button",
|
"context": "button",
|
||||||
"string": "Assign"
|
"string": "Assign"
|
||||||
|
@ -7622,10 +7626,6 @@
|
||||||
"context": "ProductTypeDeleteWarningDialog title",
|
"context": "ProductTypeDeleteWarningDialog title",
|
||||||
"string": "Delete product {selectedTypesCount,plural,one{type} other{types}}"
|
"string": "Delete product {selectedTypesCount,plural,one{type} other{types}}"
|
||||||
},
|
},
|
||||||
"x4WAC7": {
|
|
||||||
"context": "warehouse label when one product is unavailable",
|
|
||||||
"string": "{productName} is unavailable at this location"
|
|
||||||
},
|
|
||||||
"x8V/xS": {
|
"x8V/xS": {
|
||||||
"context": "attribute visibility in storefront",
|
"context": "attribute visibility in storefront",
|
||||||
"string": "Public"
|
"string": "Public"
|
||||||
|
@ -7923,10 +7923,6 @@
|
||||||
"context": "attribute type",
|
"context": "attribute type",
|
||||||
"string": "Content Attribute"
|
"string": "Content Attribute"
|
||||||
},
|
},
|
||||||
"zbrHAw": {
|
|
||||||
"context": "Tracking number input label",
|
|
||||||
"string": "Tracking number"
|
|
||||||
},
|
|
||||||
"zjHH6b": {
|
"zjHH6b": {
|
||||||
"context": "sale start date",
|
"context": "sale start date",
|
||||||
"string": "Started"
|
"string": "Started"
|
||||||
|
|
|
@ -79,6 +79,7 @@ export const fragmentOrderLine = gql`
|
||||||
quantity
|
quantity
|
||||||
warehouse {
|
warehouse {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
variant {
|
variant {
|
||||||
|
@ -342,9 +343,11 @@ export const fragmentOrderFulfillLine = gql`
|
||||||
productName
|
productName
|
||||||
quantity
|
quantity
|
||||||
allocations {
|
allocations {
|
||||||
|
id
|
||||||
quantity
|
quantity
|
||||||
warehouse {
|
warehouse {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
quantityFulfilled
|
quantityFulfilled
|
||||||
|
|
|
@ -1227,6 +1227,7 @@ export const OrderLineFragmentDoc = gql`
|
||||||
quantity
|
quantity
|
||||||
warehouse {
|
warehouse {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
variant {
|
variant {
|
||||||
|
@ -1474,9 +1475,11 @@ export const OrderFulfillLineFragmentDoc = gql`
|
||||||
productName
|
productName
|
||||||
quantity
|
quantity
|
||||||
allocations {
|
allocations {
|
||||||
|
id
|
||||||
quantity
|
quantity
|
||||||
warehouse {
|
warehouse {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
quantityFulfilled
|
quantityFulfilled
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
|
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
|
||||||
import { allPermissions } from "@saleor/hooks/makeQuery";
|
import { allPermissions } from "@saleor/hooks/makeQuery";
|
||||||
import { order, warehouseSearch } from "@saleor/orders/fixtures";
|
import { fulfillOrderLine, warehouseSearch } from "@saleor/orders/fixtures";
|
||||||
import { searchWarehouses } from "@saleor/searches/useWarehouseSearch";
|
import { searchWarehouses } from "@saleor/searches/useWarehouseSearch";
|
||||||
import Decorator from "@saleor/storybook/Decorator";
|
import Decorator from "@saleor/storybook/Decorator";
|
||||||
import { storiesOf } from "@storybook/react";
|
import { storiesOf } from "@storybook/react";
|
||||||
|
@ -10,8 +10,8 @@ import OrderChangeWarehouseDialog, { OrderChangeWarehouseDialogProps } from ".";
|
||||||
|
|
||||||
const props: OrderChangeWarehouseDialogProps = {
|
const props: OrderChangeWarehouseDialogProps = {
|
||||||
open: true,
|
open: true,
|
||||||
lines: order("abc").lines,
|
line: fulfillOrderLine("abc"),
|
||||||
currentWarehouse: null,
|
currentWarehouseId: null,
|
||||||
onConfirm: () => null,
|
onConfirm: () => null,
|
||||||
onClose: () => null,
|
onClose: () => null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import Debounce from "@saleor/components/Debounce";
|
import Debounce from "@saleor/components/Debounce";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { OrderLineFragment, WarehouseFragment } from "@saleor/graphql";
|
import { OrderFulfillLineFragment, WarehouseFragment } from "@saleor/graphql";
|
||||||
import { buttonMessages } from "@saleor/intl";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -25,7 +25,7 @@ import {
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
useElementScroll,
|
useElementScroll,
|
||||||
} from "@saleor/macaw-ui";
|
} from "@saleor/macaw-ui";
|
||||||
import { isLineAvailableInWarehouse } from "@saleor/orders/utils/data";
|
import { getLineAvailableQuantityInWarehouse } from "@saleor/orders/utils/data";
|
||||||
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
|
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -37,16 +37,16 @@ import { useStyles } from "./styles";
|
||||||
|
|
||||||
export interface OrderChangeWarehouseDialogProps {
|
export interface OrderChangeWarehouseDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
lines: OrderLineFragment[];
|
line: OrderFulfillLineFragment;
|
||||||
currentWarehouse: WarehouseFragment;
|
currentWarehouseId: string;
|
||||||
onConfirm: (warehouse: WarehouseFragment) => void;
|
onConfirm: (warehouse: WarehouseFragment) => void;
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProps> = ({
|
export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProps> = ({
|
||||||
open,
|
open,
|
||||||
lines,
|
line,
|
||||||
currentWarehouse,
|
currentWarehouseId,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -63,10 +63,10 @@ export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProp
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (currentWarehouse?.id) {
|
if (currentWarehouseId) {
|
||||||
setSelectedWarehouseId(currentWarehouse.id);
|
setSelectedWarehouseId(currentWarehouseId);
|
||||||
}
|
}
|
||||||
}, [currentWarehouse]);
|
}, [currentWarehouseId]);
|
||||||
|
|
||||||
const { result: warehousesOpts, loadMore, search } = useWarehouseSearch({
|
const { result: warehousesOpts, loadMore, search } = useWarehouseSearch({
|
||||||
variables: {
|
variables: {
|
||||||
|
@ -103,7 +103,12 @@ export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProp
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<DialogContent className={classes.container}>
|
<DialogContent className={classes.container}>
|
||||||
<FormattedMessage {...messages.dialogDescription} />
|
<FormattedMessage
|
||||||
|
{...messages.dialogDescription}
|
||||||
|
values={{
|
||||||
|
productName: line?.productName,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Debounce debounceFn={search}>
|
<Debounce debounceFn={search}>
|
||||||
{debounceSearchChange => {
|
{debounceSearchChange => {
|
||||||
const handleSearchChange = (
|
const handleSearchChange = (
|
||||||
|
@ -144,15 +149,19 @@ export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProp
|
||||||
|
|
||||||
<DialogTable ref={setAnchor}>
|
<DialogTable ref={setAnchor}>
|
||||||
{filteredWarehouses ? (
|
{filteredWarehouses ? (
|
||||||
<RadioGroup value={selectedWarehouseId} onChange={handleChange}>
|
<RadioGroup
|
||||||
|
value={selectedWarehouseId}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={classes.tableBody}
|
||||||
|
>
|
||||||
{filteredWarehouses.map(warehouse => {
|
{filteredWarehouses.map(warehouse => {
|
||||||
const unavailableLines = lines?.filter(
|
const lineQuantityInWarehouse = getLineAvailableQuantityInWarehouse(
|
||||||
line => !isLineAvailableInWarehouse(line, warehouse),
|
line,
|
||||||
|
warehouse,
|
||||||
);
|
);
|
||||||
const someLinesUnavailable = unavailableLines?.length > 0;
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={warehouse.id}>
|
<TableRow key={warehouse.id}>
|
||||||
<TableCell>
|
<TableCell className={classes.tableCell}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={warehouse.id}
|
value={warehouse.id}
|
||||||
control={<Radio color="primary" />}
|
control={<Radio color="primary" />}
|
||||||
|
@ -161,28 +170,20 @@ export const OrderChangeWarehouseDialog: React.FC<OrderChangeWarehouseDialogProp
|
||||||
<span className={classes.warehouseName}>
|
<span className={classes.warehouseName}>
|
||||||
{warehouse.name}
|
{warehouse.name}
|
||||||
</span>
|
</span>
|
||||||
{someLinesUnavailable && (
|
|
||||||
<Typography className={classes.supportText}>
|
<Typography className={classes.supportText}>
|
||||||
{unavailableLines.length === 1
|
<FormattedMessage
|
||||||
? intl.formatMessage(
|
{...messages.productAvailability}
|
||||||
messages.productUnavailable,
|
values={{
|
||||||
{
|
productCount: lineQuantityInWarehouse,
|
||||||
productName:
|
}}
|
||||||
unavailableLines[0].productName,
|
/>
|
||||||
},
|
|
||||||
)
|
|
||||||
: intl.formatMessage(
|
|
||||||
messages.multipleProductsUnavailable,
|
|
||||||
{ productCount: unavailableLines.length },
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{currentWarehouse?.id === warehouse?.id && (
|
{currentWarehouseId === warehouse?.id && (
|
||||||
<Typography className={classes.helpText}>
|
<Typography className={classes.helpText}>
|
||||||
{intl.formatMessage(messages.currentSelection)}
|
<FormattedMessage {...messages.currentSelection} />
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
@ -7,8 +7,9 @@ export const changeWarehouseDialogMessages = defineMessages({
|
||||||
description: "change warehouse dialog title",
|
description: "change warehouse dialog title",
|
||||||
},
|
},
|
||||||
dialogDescription: {
|
dialogDescription: {
|
||||||
defaultMessage: "Choose warehouse you want to fulfill this order from",
|
defaultMessage:
|
||||||
id: "QWCh6/",
|
"Choose warehouse from which you want to fulfill {productName}",
|
||||||
|
id: "Ila7WO",
|
||||||
description: "change warehouse dialog description",
|
description: "change warehouse dialog description",
|
||||||
},
|
},
|
||||||
searchFieldPlaceholder: {
|
searchFieldPlaceholder: {
|
||||||
|
@ -21,15 +22,10 @@ export const changeWarehouseDialogMessages = defineMessages({
|
||||||
id: "Epm41J",
|
id: "Epm41J",
|
||||||
description: "change warehouse dialog warehouse list label",
|
description: "change warehouse dialog warehouse list label",
|
||||||
},
|
},
|
||||||
productUnavailable: {
|
productAvailability: {
|
||||||
defaultMessage: "{productName} is unavailable at this location",
|
defaultMessage: "{productCount} available at this location",
|
||||||
id: "x4WAC7",
|
id: "hLSgWj",
|
||||||
description: "warehouse label when one product is unavailable",
|
description: "warehouse label number available of products",
|
||||||
},
|
|
||||||
multipleProductsUnavailable: {
|
|
||||||
defaultMessage: "{productCount} products are unavailable at this location",
|
|
||||||
id: "QfKQx3",
|
|
||||||
description: "warehouse label when multiple products are unavailable",
|
|
||||||
},
|
},
|
||||||
currentSelection: {
|
currentSelection: {
|
||||||
defaultMessage: "currently selected",
|
defaultMessage: "currently selected",
|
||||||
|
|
|
@ -41,6 +41,15 @@ export const useStyles = makeStyles(
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
},
|
},
|
||||||
|
tableBody: {
|
||||||
|
display: "table",
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{ name: "OrderChangeWarehouseDialog" },
|
{ name: "OrderChangeWarehouseDialog" },
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
OrderDetailsFragment,
|
OrderDetailsFragment,
|
||||||
OrderDetailsQuery,
|
OrderDetailsQuery,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
WarehouseFragment,
|
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
|
@ -70,7 +69,6 @@ export interface OrderDetailsPageProps {
|
||||||
}>;
|
}>;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
selectedWarehouse?: WarehouseFragment;
|
|
||||||
onOrderLineAdd?: () => void;
|
onOrderLineAdd?: () => void;
|
||||||
onOrderLineChange?: (
|
onOrderLineChange?: (
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -96,7 +94,6 @@ export interface OrderDetailsPageProps {
|
||||||
onInvoiceClick(invoiceId: string);
|
onInvoiceClick(invoiceId: string);
|
||||||
onInvoiceGenerate();
|
onInvoiceGenerate();
|
||||||
onInvoiceSend(invoiceId: string);
|
onInvoiceSend(invoiceId: string);
|
||||||
onWarehouseChange?();
|
|
||||||
onSubmit(data: MetadataFormData): SubmitPromise;
|
onSubmit(data: MetadataFormData): SubmitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +121,6 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
order,
|
order,
|
||||||
shop,
|
shop,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
selectedWarehouse,
|
|
||||||
onBillingAddressEdit,
|
onBillingAddressEdit,
|
||||||
onFulfillmentApprove,
|
onFulfillmentApprove,
|
||||||
onFulfillmentCancel,
|
onFulfillmentCancel,
|
||||||
|
@ -146,7 +142,6 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
onOrderLineChange,
|
onOrderLineChange,
|
||||||
onOrderLineRemove,
|
onOrderLineRemove,
|
||||||
onShippingMethodEdit,
|
onShippingMethodEdit,
|
||||||
onWarehouseChange,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -268,8 +263,6 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
|
||||||
notAllowedToFulfillUnpaid={notAllowedToFulfillUnpaid}
|
notAllowedToFulfillUnpaid={notAllowedToFulfillUnpaid}
|
||||||
lines={unfulfilled}
|
lines={unfulfilled}
|
||||||
onFulfill={onOrderFulfill}
|
onFulfill={onOrderFulfill}
|
||||||
onWarehouseChange={onWarehouseChange}
|
|
||||||
selectedWarehouse={selectedWarehouse}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import { TableCell, TableRow, TextField, Typography } from "@material-ui/core";
|
import { TableCell, TableRow, TextField, Typography } from "@material-ui/core";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
||||||
import {
|
import { OrderFulfillLineFragment } from "@saleor/graphql";
|
||||||
OrderFulfillLineFragment,
|
|
||||||
OrderFulfillStockInput,
|
|
||||||
} from "@saleor/graphql";
|
|
||||||
import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset";
|
import { FormsetChange, FormsetData } from "@saleor/hooks/useFormset";
|
||||||
import { Tooltip, WarningIcon } from "@saleor/macaw-ui";
|
import {
|
||||||
|
ChevronIcon,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
WarningIcon,
|
||||||
|
} from "@saleor/macaw-ui";
|
||||||
import {
|
import {
|
||||||
getAttributesCaption,
|
getAttributesCaption,
|
||||||
getOrderLineAvailableQuantity,
|
getOrderLineAvailableQuantity,
|
||||||
getWarehouseStock,
|
getWarehouseStock,
|
||||||
|
OrderFulfillLineFormData,
|
||||||
} from "@saleor/orders/utils/data";
|
} from "@saleor/orders/utils/data";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -22,13 +25,19 @@ import { useStyles } from "./styles";
|
||||||
interface OrderFulfillLineProps {
|
interface OrderFulfillLineProps {
|
||||||
line: OrderFulfillLineFragment;
|
line: OrderFulfillLineFragment;
|
||||||
lineIndex: number;
|
lineIndex: number;
|
||||||
warehouseId: string;
|
formsetData: FormsetData<null, OrderFulfillLineFormData[]>;
|
||||||
formsetData: FormsetData<null, OrderFulfillStockInput[]>;
|
formsetChange: FormsetChange<OrderFulfillLineFormData[]>;
|
||||||
formsetChange: FormsetChange<OrderFulfillStockInput[]>;
|
onWarehouseChange: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
const { line, lineIndex, warehouseId, formsetData, formsetChange } = props;
|
const {
|
||||||
|
line,
|
||||||
|
lineIndex,
|
||||||
|
formsetData,
|
||||||
|
formsetChange,
|
||||||
|
onWarehouseChange,
|
||||||
|
} = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -37,14 +46,19 @@ export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
const lineFormQuantity = isPreorder
|
const lineFormQuantity = isPreorder
|
||||||
? 0
|
? 0
|
||||||
: formsetData[lineIndex]?.value?.[0]?.quantity;
|
: formsetData[lineIndex]?.value?.[0]?.quantity;
|
||||||
|
const lineFormWarehouse = formsetData[lineIndex]?.value?.[0]?.warehouse;
|
||||||
|
|
||||||
const overfulfill = lineFormQuantity > line.quantityToFulfill;
|
const overfulfill = lineFormQuantity > line.quantityToFulfill;
|
||||||
const warehouseStock = getWarehouseStock(line?.variant?.stocks, warehouseId);
|
|
||||||
|
const warehouseStock = getWarehouseStock(
|
||||||
|
line?.variant?.stocks,
|
||||||
|
lineFormWarehouse?.id,
|
||||||
|
);
|
||||||
const availableQuantity = getOrderLineAvailableQuantity(line, warehouseStock);
|
const availableQuantity = getOrderLineAvailableQuantity(line, warehouseStock);
|
||||||
|
|
||||||
const isStockExceeded = lineFormQuantity > availableQuantity;
|
const isStockExceeded = lineFormQuantity > availableQuantity;
|
||||||
|
|
||||||
if (!line) {
|
if (!line || !lineFormWarehouse) {
|
||||||
return (
|
return (
|
||||||
<TableRow key={lineIndex}>
|
<TableRow key={lineIndex}>
|
||||||
<TableCellAvatar className={classes.colName}>
|
<TableCellAvatar className={classes.colName}>
|
||||||
|
@ -59,6 +73,9 @@ export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
<TableCell className={classes.colStock}>
|
<TableCell className={classes.colStock}>
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className={classes.colWarehouse}>
|
||||||
|
<Skeleton />
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +133,7 @@ export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
formsetChange(line.id, [
|
formsetChange(line.id, [
|
||||||
{
|
{
|
||||||
quantity: parseInt(event.target.value, 10),
|
quantity: parseInt(event.target.value, 10),
|
||||||
warehouse: warehouseId,
|
warehouse: lineFormWarehouse,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -141,6 +158,20 @@ export const OrderFulfillLine: React.FC<OrderFulfillLineProps> = props => {
|
||||||
<TableCell className={classes.colStock} key="total">
|
<TableCell className={classes.colStock} key="total">
|
||||||
{isPreorder || isDeletedVariant ? undefined : availableQuantity}
|
{isPreorder || isDeletedVariant ? undefined : availableQuantity}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className={classes.colWarehouse}>
|
||||||
|
<IconButton
|
||||||
|
onClick={onWarehouseChange}
|
||||||
|
className={classes.warehouseButton}
|
||||||
|
data-test-id="select-warehouse-button"
|
||||||
|
>
|
||||||
|
<div className={classes.warehouseButtonContent}>
|
||||||
|
<div className={classes.warehouseButtonContentText}>
|
||||||
|
{lineFormWarehouse?.name ?? <Skeleton />}
|
||||||
|
</div>
|
||||||
|
<ChevronIcon />
|
||||||
|
</div>
|
||||||
|
</IconButton>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,4 +12,9 @@ export const messages = defineMessages({
|
||||||
id: "8vQGO0",
|
id: "8vQGO0",
|
||||||
description: "tooltip content when line's variant has been deleted",
|
description: "tooltip content when line's variant has been deleted",
|
||||||
},
|
},
|
||||||
|
warehouse: {
|
||||||
|
defaultMessage: "Warehouse",
|
||||||
|
id: "W5hb5H",
|
||||||
|
description: "warehouse input",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const useStyles = makeStyles(
|
||||||
width: 180,
|
width: 180,
|
||||||
},
|
},
|
||||||
colName: {
|
colName: {
|
||||||
width: 250,
|
width: 220,
|
||||||
},
|
},
|
||||||
colQuantity: {
|
colQuantity: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
|
@ -16,7 +16,11 @@ export const useStyles = makeStyles(
|
||||||
colSku: {
|
colSku: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
width: 150,
|
width: 100,
|
||||||
|
},
|
||||||
|
colWarehouse: {
|
||||||
|
width: 200,
|
||||||
|
textAlign: "right",
|
||||||
},
|
},
|
||||||
warningIcon: {
|
warningIcon: {
|
||||||
color: theme.palette.saleor.warning.mid,
|
color: theme.palette.saleor.warning.mid,
|
||||||
|
@ -42,6 +46,23 @@ export const useStyles = makeStyles(
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
},
|
},
|
||||||
|
warehouseButton: {
|
||||||
|
padding: theme.spacing(1.5),
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "right",
|
||||||
|
border: `1px solid ${theme.palette.divider}`,
|
||||||
|
},
|
||||||
|
warehouseButtonContent: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
cursor: "pointer",
|
||||||
|
},
|
||||||
|
warehouseButtonContentText: {
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{ name: "OrderFulfillLine" },
|
{ name: "OrderFulfillLine" },
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,24 +8,21 @@ import { orderToFulfill } from "./fixtures";
|
||||||
import OrderFulfillPage, { OrderFulfillPageProps } from "./OrderFulfillPage";
|
import OrderFulfillPage, { OrderFulfillPageProps } from "./OrderFulfillPage";
|
||||||
|
|
||||||
const props: OrderFulfillPageProps = {
|
const props: OrderFulfillPageProps = {
|
||||||
|
params: {},
|
||||||
errors: [],
|
errors: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
order: orderToFulfill,
|
order: orderToFulfill,
|
||||||
saveButtonBar: "default",
|
saveButtonBar: "default",
|
||||||
warehouse: warehouseList[0],
|
openModal: () => undefined,
|
||||||
|
closeModal: () => undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
storiesOf("Views / Orders / Fulfill order", module)
|
storiesOf("Views / Orders / Fulfill order", module)
|
||||||
.addDecorator(Decorator)
|
.addDecorator(Decorator)
|
||||||
.add("default", () => <OrderFulfillPage {...props} />)
|
.add("default", () => <OrderFulfillPage {...props} />)
|
||||||
.add("loading", () => (
|
.add("loading", () => (
|
||||||
<OrderFulfillPage
|
<OrderFulfillPage {...props} loading={true} order={undefined} />
|
||||||
{...props}
|
|
||||||
loading={true}
|
|
||||||
order={undefined}
|
|
||||||
warehouse={undefined}
|
|
||||||
/>
|
|
||||||
))
|
))
|
||||||
.add("error", () => (
|
.add("error", () => (
|
||||||
<OrderFulfillPage
|
<OrderFulfillPage
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
CardContent,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import { Backlink } from "@saleor/components/Backlink";
|
import { Backlink } from "@saleor/components/Backlink";
|
||||||
|
import CardSpacer from "@saleor/components/CardSpacer";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||||
import Form from "@saleor/components/Form";
|
import Form from "@saleor/components/Form";
|
||||||
import { Grid } from "@saleor/components/Grid";
|
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Savebar from "@saleor/components/Savebar";
|
import Savebar from "@saleor/components/Savebar";
|
||||||
|
@ -24,7 +23,6 @@ import {
|
||||||
OrderFulfillLineFragment,
|
OrderFulfillLineFragment,
|
||||||
OrderFulfillStockInput,
|
OrderFulfillStockInput,
|
||||||
ShopOrderSettingsFragment,
|
ShopOrderSettingsFragment,
|
||||||
WarehouseFragment,
|
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
|
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
|
||||||
|
@ -32,10 +30,17 @@ import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||||
import { renderCollection } from "@saleor/misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
import { orderUrl } from "@saleor/orders/urls";
|
import OrderChangeWarehouseDialog from "@saleor/orders/components/OrderChangeWarehouseDialog";
|
||||||
|
import {
|
||||||
|
OrderFulfillUrlDialog,
|
||||||
|
OrderFulfillUrlQueryParams,
|
||||||
|
orderUrl,
|
||||||
|
} from "@saleor/orders/urls";
|
||||||
import {
|
import {
|
||||||
getAttributesCaption,
|
getAttributesCaption,
|
||||||
|
getLineAllocationWithHighestQuantity,
|
||||||
getToFulfillOrderLines,
|
getToFulfillOrderLines,
|
||||||
|
OrderFulfillLineFormData,
|
||||||
} from "@saleor/orders/utils/data";
|
} from "@saleor/orders/utils/data";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -48,37 +53,42 @@ import { useStyles } from "./styles";
|
||||||
|
|
||||||
interface OrderFulfillFormData {
|
interface OrderFulfillFormData {
|
||||||
sendInfo: boolean;
|
sendInfo: boolean;
|
||||||
trackingNumber: string;
|
|
||||||
allowStockToBeExceeded: boolean;
|
allowStockToBeExceeded: boolean;
|
||||||
}
|
}
|
||||||
export interface OrderFulfillSubmitData extends OrderFulfillFormData {
|
export interface OrderFulfillSubmitData extends OrderFulfillFormData {
|
||||||
items: FormsetData<null, OrderFulfillStockInput[]>;
|
items: FormsetData<null, OrderFulfillStockInput[]>;
|
||||||
}
|
}
|
||||||
export interface OrderFulfillPageProps {
|
export interface OrderFulfillPageProps {
|
||||||
|
params: OrderFulfillUrlQueryParams;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
errors: FulfillOrderMutation["orderFulfill"]["errors"];
|
errors: FulfillOrderMutation["orderFulfill"]["errors"];
|
||||||
order: OrderFulfillDataQuery["order"];
|
order: OrderFulfillDataQuery["order"];
|
||||||
saveButtonBar: ConfirmButtonTransitionState;
|
saveButtonBar: ConfirmButtonTransitionState;
|
||||||
warehouse: WarehouseFragment;
|
|
||||||
shopSettings?: ShopOrderSettingsFragment;
|
shopSettings?: ShopOrderSettingsFragment;
|
||||||
onSubmit: (data: OrderFulfillSubmitData) => SubmitPromise;
|
onSubmit: (data: OrderFulfillSubmitData) => SubmitPromise;
|
||||||
|
openModal: (
|
||||||
|
action: OrderFulfillUrlDialog,
|
||||||
|
params?: OrderFulfillUrlQueryParams,
|
||||||
|
) => void;
|
||||||
|
closeModal: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialFormData: OrderFulfillFormData = {
|
const initialFormData: OrderFulfillFormData = {
|
||||||
sendInfo: true,
|
sendInfo: true,
|
||||||
trackingNumber: "",
|
|
||||||
allowStockToBeExceeded: false,
|
allowStockToBeExceeded: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
const {
|
const {
|
||||||
|
params,
|
||||||
loading,
|
loading,
|
||||||
errors,
|
errors,
|
||||||
order,
|
order,
|
||||||
saveButtonBar,
|
saveButtonBar,
|
||||||
warehouse,
|
|
||||||
shopSettings,
|
shopSettings,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
openModal,
|
||||||
|
closeModal,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -87,10 +97,15 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
|
|
||||||
const { change: formsetChange, data: formsetData } = useFormset<
|
const { change: formsetChange, data: formsetData } = useFormset<
|
||||||
null,
|
null,
|
||||||
OrderFulfillStockInput[]
|
OrderFulfillLineFormData[]
|
||||||
>(
|
>(
|
||||||
(getToFulfillOrderLines(order?.lines) as OrderFulfillLineFragment[]).map(
|
(getToFulfillOrderLines(order?.lines) as OrderFulfillLineFragment[]).map(
|
||||||
line => ({
|
line => {
|
||||||
|
const highestQuantityAllocation = getLineAllocationWithHighestQuantity(
|
||||||
|
line,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
data: null,
|
data: null,
|
||||||
id: line.id,
|
id: line.id,
|
||||||
label: getAttributesCaption(line?.variant?.attributes),
|
label: getAttributesCaption(line?.variant?.attributes),
|
||||||
|
@ -99,10 +114,11 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
quantity: line.quantityToFulfill,
|
quantity: line.quantityToFulfill,
|
||||||
warehouse: warehouse?.id,
|
warehouse: highestQuantityAllocation?.warehouse,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
};
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -122,7 +138,15 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
return onSubmit({
|
return onSubmit({
|
||||||
...formData,
|
...formData,
|
||||||
allowStockToBeExceeded,
|
allowStockToBeExceeded,
|
||||||
items: formsetData.filter(item => !!item.value),
|
items: formsetData
|
||||||
|
.filter(item => !!item.value)
|
||||||
|
.map(item => ({
|
||||||
|
...item,
|
||||||
|
value: item.value.map(value => ({
|
||||||
|
quantity: value.quantity,
|
||||||
|
warehouse: value.warehouse.id,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -166,6 +190,7 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Container>
|
<Container>
|
||||||
<Backlink href={orderUrl(order?.id)}>
|
<Backlink href={orderUrl(order?.id)}>
|
||||||
{order?.number
|
{order?.number
|
||||||
|
@ -179,12 +204,6 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
orderNumber: order?.number,
|
orderNumber: order?.number,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<Typography className={classes.warehouseLabel}>
|
|
||||||
<FormattedMessage
|
|
||||||
{...messages.fulfillingFrom}
|
|
||||||
values={{ warehouseName: warehouse?.name }}
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
<Form
|
<Form
|
||||||
confirmLeave
|
confirmLeave
|
||||||
initial={initialFormData}
|
initial={initialFormData}
|
||||||
|
@ -197,12 +216,11 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
>
|
>
|
||||||
{({ change, data, submit }) => (
|
{({ change, data, submit }) => (
|
||||||
<>
|
<>
|
||||||
<Grid>
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
title={intl.formatMessage(messages.itemsReadyToShip)}
|
title={intl.formatMessage(messages.itemsReadyToShip)}
|
||||||
/>
|
/>
|
||||||
{warehouse ? (
|
{order ? (
|
||||||
<ResponsiveTable className={classes.table}>
|
<ResponsiveTable className={classes.table}>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -223,50 +241,58 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
<TableCell className={classes.colStock}>
|
<TableCell className={classes.colStock}>
|
||||||
<FormattedMessage {...messages.stock} />
|
<FormattedMessage {...messages.stock} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className={classes.colWarehouse}>
|
||||||
|
<FormattedMessage {...messages.warehouse} />
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(
|
{renderCollection(
|
||||||
getToFulfillOrderLines(order?.lines),
|
getToFulfillOrderLines(order.lines),
|
||||||
(line: OrderFulfillLineFragment, lineIndex) => (
|
(line: OrderFulfillLineFragment, lineIndex) => (
|
||||||
<OrderFulfillLine
|
<OrderFulfillLine
|
||||||
key={line.id}
|
key={line.id}
|
||||||
line={line}
|
line={line}
|
||||||
lineIndex={lineIndex}
|
lineIndex={lineIndex}
|
||||||
warehouseId={warehouse?.id}
|
|
||||||
formsetData={formsetData}
|
formsetData={formsetData}
|
||||||
formsetChange={formsetChange}
|
formsetChange={formsetChange}
|
||||||
|
onWarehouseChange={() =>
|
||||||
|
openModal("change-warehouse", {
|
||||||
|
lineId: line.id,
|
||||||
|
warehouseId:
|
||||||
|
formsetData[lineIndex]?.value?.[0]?.warehouse
|
||||||
|
.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
) : (
|
) : (
|
||||||
|
<CardContent>
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
|
</CardContent>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className={classes.shipmentInformationCard}>
|
<CardSpacer />
|
||||||
<Typography className={classes.supportHeader}>
|
|
||||||
<FormattedMessage {...messages.shipmentInformation} />
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
value={data.trackingNumber}
|
|
||||||
name="trackingNumber"
|
|
||||||
label={intl.formatMessage(messages.trackingNumber)}
|
|
||||||
fullWidth
|
|
||||||
onChange={change}
|
|
||||||
/>
|
|
||||||
{shopSettings?.fulfillmentAutoApprove && (
|
{shopSettings?.fulfillmentAutoApprove && (
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage(messages.shipmentInformation)}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
checked={data.sendInfo}
|
checked={data.sendInfo}
|
||||||
label={intl.formatMessage(messages.sentShipmentDetails)}
|
label={intl.formatMessage(messages.sentShipmentDetails)}
|
||||||
name="sendInfo"
|
name="sendInfo"
|
||||||
onChange={change}
|
onChange={change}
|
||||||
/>
|
/>
|
||||||
)}
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
)}
|
||||||
|
|
||||||
<Savebar
|
<Savebar
|
||||||
disabled={!shouldEnableSave()}
|
disabled={!shouldEnableSave()}
|
||||||
|
@ -279,7 +305,9 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
tooltips={{
|
tooltips={{
|
||||||
confirm:
|
confirm:
|
||||||
notAllowedToFulfillUnpaid &&
|
notAllowedToFulfillUnpaid &&
|
||||||
intl.formatMessage(commonMessages.cannotFullfillUnpaidOrder),
|
intl.formatMessage(
|
||||||
|
commonMessages.cannotFullfillUnpaidOrder,
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
onCancel={() => navigate(orderUrl(order?.id))}
|
onCancel={() => navigate(orderUrl(order?.id))}
|
||||||
|
@ -288,7 +316,6 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
open={displayStockExceededDialog}
|
open={displayStockExceededDialog}
|
||||||
lines={order?.lines}
|
lines={order?.lines}
|
||||||
formsetData={formsetData}
|
formsetData={formsetData}
|
||||||
warehouseId={warehouse?.id}
|
|
||||||
confirmButtonState={saveButtonBar}
|
confirmButtonState={saveButtonBar}
|
||||||
onSubmit={submit}
|
onSubmit={submit}
|
||||||
onClose={() => setDisplayStockExceededDialog(false)}
|
onClose={() => setDisplayStockExceededDialog(false)}
|
||||||
|
@ -297,6 +324,25 @@ const OrderFulfillPage: React.FC<OrderFulfillPageProps> = props => {
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Container>
|
</Container>
|
||||||
|
<OrderChangeWarehouseDialog
|
||||||
|
open={params.action === "change-warehouse"}
|
||||||
|
line={order?.lines.find(line => line.id === params.lineId)}
|
||||||
|
currentWarehouseId={params.warehouseId}
|
||||||
|
onConfirm={warehouse => {
|
||||||
|
const lineFormQuantity = formsetData.find(
|
||||||
|
item => item.id === params.lineId,
|
||||||
|
)?.value?.[0]?.quantity;
|
||||||
|
|
||||||
|
formsetChange(params.lineId, [
|
||||||
|
{
|
||||||
|
quantity: lineFormQuantity,
|
||||||
|
warehouse,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
onClose={closeModal}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -71,14 +71,9 @@ export const messages = defineMessages({
|
||||||
id: "lF+VJQ",
|
id: "lF+VJQ",
|
||||||
description: "Shipment information card header",
|
description: "Shipment information card header",
|
||||||
},
|
},
|
||||||
trackingNumber: {
|
warehouse: {
|
||||||
defaultMessage: "Tracking number",
|
defaultMessage: "Warehouse",
|
||||||
id: "zbrHAw",
|
id: "J0lNnk",
|
||||||
description: "Tracking number input label",
|
description: "column label",
|
||||||
},
|
|
||||||
fulfillingFrom: {
|
|
||||||
defaultMessage: "Fulfilling from {warehouseName}",
|
|
||||||
id: "oiaUni",
|
|
||||||
description: "Support text under page header",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const useStyles = makeStyles(
|
||||||
width: 180,
|
width: 180,
|
||||||
},
|
},
|
||||||
colName: {
|
colName: {
|
||||||
width: 250,
|
width: 220,
|
||||||
},
|
},
|
||||||
colQuantity: {
|
colQuantity: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
|
@ -19,7 +19,11 @@ export const useStyles = makeStyles(
|
||||||
colSku: {
|
colSku: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
width: 150,
|
width: 100,
|
||||||
|
},
|
||||||
|
colWarehouse: {
|
||||||
|
textAlign: "right",
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
"&&": {
|
"&&": {
|
||||||
|
@ -41,9 +45,6 @@ export const useStyles = makeStyles(
|
||||||
lineHeight: "160%",
|
lineHeight: "160%",
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(2),
|
||||||
},
|
},
|
||||||
warehouseLabel: {
|
|
||||||
marginBottom: theme.spacing(4),
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
{ name: "OrderFulfillPage" },
|
{ name: "OrderFulfillPage" },
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { renderCollection } from "@saleor/misc";
|
||||||
import {
|
import {
|
||||||
getFulfillmentFormsetQuantity,
|
getFulfillmentFormsetQuantity,
|
||||||
getOrderLineAvailableQuantity,
|
getOrderLineAvailableQuantity,
|
||||||
OrderFulfillStockInputFormsetData,
|
OrderFulfillStockFormsetData,
|
||||||
} from "@saleor/orders/utils/data";
|
} from "@saleor/orders/utils/data";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
@ -26,8 +26,7 @@ import { useStyles } from "./styles";
|
||||||
export interface OrderFulfillStockExceededDialogProps {
|
export interface OrderFulfillStockExceededDialogProps {
|
||||||
lines: Array<FulfillmentFragment["lines"][0] | OrderFulfillLineFragment>;
|
lines: Array<FulfillmentFragment["lines"][0] | OrderFulfillLineFragment>;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
formsetData: OrderFulfillStockInputFormsetData;
|
formsetData: OrderFulfillStockFormsetData;
|
||||||
warehouseId: string;
|
|
||||||
confirmButtonState: ConfirmButtonTransitionState;
|
confirmButtonState: ConfirmButtonTransitionState;
|
||||||
onSubmit();
|
onSubmit();
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -38,7 +37,6 @@ const OrderFulfillStockExceededDialog: React.FC<OrderFulfillStockExceededDialogP
|
||||||
lines,
|
lines,
|
||||||
open,
|
open,
|
||||||
formsetData,
|
formsetData,
|
||||||
warehouseId,
|
|
||||||
confirmButtonState,
|
confirmButtonState,
|
||||||
onClose,
|
onClose,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
@ -49,8 +47,10 @@ const OrderFulfillStockExceededDialog: React.FC<OrderFulfillStockExceededDialogP
|
||||||
|
|
||||||
const exceededLines = lines?.filter(el => {
|
const exceededLines = lines?.filter(el => {
|
||||||
const line = "orderLine" in el ? el.orderLine : el;
|
const line = "orderLine" in el ? el.orderLine : el;
|
||||||
|
const lineFormWarehouse = formsetData?.find(item => item.id === el.id)
|
||||||
|
?.value?.[0]?.warehouse;
|
||||||
const stock = line.variant?.stocks.find(
|
const stock = line.variant?.stocks.find(
|
||||||
stock => stock.warehouse.id === warehouseId,
|
stock => stock.warehouse.id === lineFormWarehouse?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -91,14 +91,20 @@ const OrderFulfillStockExceededDialog: React.FC<OrderFulfillStockExceededDialogP
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(exceededLines, line => (
|
{renderCollection(exceededLines, line => {
|
||||||
|
const lineFormWarehouse = formsetData?.find(
|
||||||
|
item => item.id === line.id,
|
||||||
|
)?.value?.[0]?.warehouse;
|
||||||
|
|
||||||
|
return (
|
||||||
<OrderFulfillStockExceededDialogLine
|
<OrderFulfillStockExceededDialogLine
|
||||||
key={line?.id}
|
key={line?.id}
|
||||||
line={line}
|
line={line}
|
||||||
formsetData={formsetData}
|
formsetData={formsetData}
|
||||||
warehouseId={warehouseId}
|
warehouseId={lineFormWarehouse?.id}
|
||||||
/>
|
/>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</ResponsiveTable>
|
</ResponsiveTable>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
getAttributesCaption,
|
getAttributesCaption,
|
||||||
getFulfillmentFormsetQuantity,
|
getFulfillmentFormsetQuantity,
|
||||||
getOrderLineAvailableQuantity,
|
getOrderLineAvailableQuantity,
|
||||||
OrderFulfillStockInputFormsetData,
|
OrderFulfillStockFormsetData,
|
||||||
} from "@saleor/orders/utils/data";
|
} from "@saleor/orders/utils/data";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -14,23 +14,24 @@ import { useStyles } from "../OrderFulfillStockExceededDialog/styles";
|
||||||
export interface OrderFulfillStockExceededDialogLineProps {
|
export interface OrderFulfillStockExceededDialogLineProps {
|
||||||
line: OrderFulfillLineFragment | FulfillmentFragment["lines"][0];
|
line: OrderFulfillLineFragment | FulfillmentFragment["lines"][0];
|
||||||
warehouseId: string;
|
warehouseId: string;
|
||||||
formsetData: OrderFulfillStockInputFormsetData;
|
formsetData: OrderFulfillStockFormsetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderFulfillStockExceededDialogLine: React.FC<OrderFulfillStockExceededDialogLineProps> = props => {
|
const OrderFulfillStockExceededDialogLine: React.FC<OrderFulfillStockExceededDialogLineProps> = props => {
|
||||||
const { line: genericLine, warehouseId, formsetData } = props;
|
const { line: genericLine, warehouseId, formsetData } = props;
|
||||||
|
|
||||||
const line = "orderLine" in genericLine ? genericLine.orderLine : genericLine;
|
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
|
||||||
const stock = line?.variant?.stocks.find(
|
|
||||||
stock => stock.warehouse.id === warehouseId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!genericLine) {
|
if (!genericLine) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const line = "orderLine" in genericLine ? genericLine.orderLine : genericLine;
|
||||||
|
|
||||||
|
const stock = line?.variant?.stocks.find(
|
||||||
|
stock => stock.warehouse.id === warehouseId,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={line?.id}>
|
<TableRow key={line?.id}>
|
||||||
<TableCellAvatar
|
<TableCellAvatar
|
||||||
|
|
|
@ -2,10 +2,8 @@ import { Card, CardActions, TableBody, Typography } from "@material-ui/core";
|
||||||
import { Button } from "@saleor/components/Button";
|
import { Button } from "@saleor/components/Button";
|
||||||
import CardSpacer from "@saleor/components/CardSpacer";
|
import CardSpacer from "@saleor/components/CardSpacer";
|
||||||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import { OrderLineFragment } from "@saleor/graphql";
|
||||||
import { OrderLineFragment, WarehouseFragment } from "@saleor/graphql";
|
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { ChevronIcon, IconButton } from "@saleor/macaw-ui";
|
|
||||||
import { renderCollection } from "@saleor/misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
@ -20,8 +18,6 @@ interface OrderUnfulfilledProductsCardProps {
|
||||||
notAllowedToFulfillUnpaid: boolean;
|
notAllowedToFulfillUnpaid: boolean;
|
||||||
lines: OrderLineFragment[];
|
lines: OrderLineFragment[];
|
||||||
onFulfill: () => void;
|
onFulfill: () => void;
|
||||||
selectedWarehouse: WarehouseFragment;
|
|
||||||
onWarehouseChange: () => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps> = props => {
|
const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps> = props => {
|
||||||
|
@ -30,8 +26,6 @@ const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps>
|
||||||
notAllowedToFulfillUnpaid,
|
notAllowedToFulfillUnpaid,
|
||||||
lines,
|
lines,
|
||||||
onFulfill,
|
onFulfill,
|
||||||
selectedWarehouse,
|
|
||||||
onWarehouseChange,
|
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
@ -47,18 +41,6 @@ const OrderUnfulfilledProductsCard: React.FC<OrderUnfulfilledProductsCardProps>
|
||||||
withStatus
|
withStatus
|
||||||
status="unfulfilled"
|
status="unfulfilled"
|
||||||
className={classes.cardTitle}
|
className={classes.cardTitle}
|
||||||
toolbar={
|
|
||||||
<IconButton
|
|
||||||
onClick={onWarehouseChange}
|
|
||||||
className={classes.toolbarButton}
|
|
||||||
data-test-id="select-warehouse-button"
|
|
||||||
>
|
|
||||||
<div className={classes.toolbarButtonContent}>
|
|
||||||
<div>{selectedWarehouse?.name ?? <Skeleton />}</div>
|
|
||||||
<ChevronIcon />
|
|
||||||
</div>
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ResponsiveTable className={classes.table}>
|
<ResponsiveTable className={classes.table}>
|
||||||
<TableHeader />
|
<TableHeader />
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
OrderDetailsQuery,
|
OrderDetailsQuery,
|
||||||
OrderEventsEmailsEnum,
|
OrderEventsEmailsEnum,
|
||||||
OrderEventsEnum,
|
OrderEventsEnum,
|
||||||
|
OrderFulfillLineFragment,
|
||||||
OrderListQuery,
|
OrderListQuery,
|
||||||
OrderSettingsFragment,
|
OrderSettingsFragment,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
|
@ -1078,6 +1079,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1186,6 +1188,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1302,6 +1305,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1395,6 +1399,7 @@ export const order = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1628,6 +1633,7 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1721,6 +1727,7 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1869,6 +1876,7 @@ export const draftOrder = (placeholder: string): OrderDetailsFragment => ({
|
||||||
user: null,
|
user: null,
|
||||||
userEmail: null,
|
userEmail: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flatOrders = orders.map(order => ({
|
export const flatOrders = orders.map(order => ({
|
||||||
...order,
|
...order,
|
||||||
orderStatus: transformOrderStatus(order.status, {
|
orderStatus: transformOrderStatus(order.status, {
|
||||||
|
@ -1878,6 +1886,70 @@ export const flatOrders = orders.map(order => ({
|
||||||
formatMessage: (message: MessageDescriptor) => message.defaultMessage,
|
formatMessage: (message: MessageDescriptor) => message.defaultMessage,
|
||||||
} as any),
|
} as any),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const fulfillOrderLine = (
|
||||||
|
placeholderImage: string,
|
||||||
|
): OrderFulfillLineFragment => ({
|
||||||
|
__typename: "OrderLine",
|
||||||
|
id: "T3JkZXJMaW5lOjIz",
|
||||||
|
isShippingRequired: false,
|
||||||
|
productName: "Williams, Garcia and Walker (XS)",
|
||||||
|
quantity: 2,
|
||||||
|
quantityFulfilled: 2,
|
||||||
|
quantityToFulfill: 0,
|
||||||
|
allocations: [
|
||||||
|
{
|
||||||
|
id: "allocation_test_id",
|
||||||
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
|
id: "V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
|
__typename: "Warehouse",
|
||||||
|
},
|
||||||
|
quantity: 1,
|
||||||
|
__typename: "Allocation",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
thumbnail: {
|
||||||
|
__typename: "Image" as "Image",
|
||||||
|
url: placeholderImage,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
__typename: "ProductVariant",
|
||||||
|
id: "dsfsfuhb",
|
||||||
|
name: "Williams, Garcia and Walker (XS)",
|
||||||
|
sku: "5-1337",
|
||||||
|
attributes: [],
|
||||||
|
trackInventory: true,
|
||||||
|
preorder: null,
|
||||||
|
stocks: [
|
||||||
|
{
|
||||||
|
id: "stock_test_id1",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse1",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjc4OGUyMGRlLTlmYTAtNDI5My1iZDk2LWUwM2RjY2RhMzc0ZQ==",
|
||||||
|
__typename: "Warehouse",
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "stock_test_id2",
|
||||||
|
warehouse: {
|
||||||
|
name: "stock_warehouse2",
|
||||||
|
id:
|
||||||
|
"V2FyZWhvdXNlOjczYzI0OGNmLTliNzAtNDlmMi1hMDRlLTM4ZTYxMmQ5MDYwMQ==",
|
||||||
|
__typename: "Warehouse",
|
||||||
|
},
|
||||||
|
quantity: 166,
|
||||||
|
quantityAllocated: 0,
|
||||||
|
__typename: "Stock",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const variants = [
|
export const variants = [
|
||||||
{ id: "p1", name: "Product 1: variant 1", sku: "12345", stockQuantity: 3 },
|
{ id: "p1", name: "Product 1: variant 1", sku: "12345", stockQuantity: 3 },
|
||||||
{ id: "p2", name: "Product 1: variant 2", sku: "12346", stockQuantity: 1 },
|
{ id: "p2", name: "Product 1: variant 2", sku: "12346", stockQuantity: 1 },
|
||||||
|
|
|
@ -126,7 +126,11 @@ export type OrderUrlDialog =
|
||||||
|
|
||||||
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
|
||||||
|
|
||||||
export type OrderFulfillUrlQueryParams = Partial<{ warehouse: string }>;
|
export type OrderFulfillUrlFiltersType = "warehouseId" | "lineId";
|
||||||
|
export type OrderFulfillUrlFilters = Filters<OrderFulfillUrlFiltersType>;
|
||||||
|
export type OrderFulfillUrlDialog = "change-warehouse";
|
||||||
|
export type OrderFulfillUrlQueryParams = Dialog<OrderFulfillUrlDialog> &
|
||||||
|
OrderFulfillUrlFilters;
|
||||||
|
|
||||||
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
|
||||||
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
|
@ -525,6 +525,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -619,6 +620,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -713,6 +715,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -813,6 +816,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -912,6 +916,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1011,6 +1016,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1110,6 +1116,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1209,6 +1216,7 @@ describe("Get the total value of all replaced products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1442,6 +1450,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1536,6 +1545,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1630,6 +1640,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1730,6 +1741,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1829,6 +1841,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -1928,6 +1941,7 @@ describe("Get the total value of all selected products", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -2155,6 +2169,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -2254,6 +2269,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
@ -2353,6 +2369,7 @@ describe("Merge repeated order lines of fulfillment lines", () => {
|
||||||
{
|
{
|
||||||
id: "allocation_test_id",
|
id: "allocation_test_id",
|
||||||
warehouse: {
|
warehouse: {
|
||||||
|
name: "US Warehouse",
|
||||||
id:
|
id:
|
||||||
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
"V2FyZWhvdXNlOjk1NWY0ZDk2LWRmNTAtNGY0Zi1hOTM4LWM5MTYzYTA4YTViNg==",
|
||||||
__typename: "Warehouse",
|
__typename: "Warehouse",
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
FulfillmentStatus,
|
FulfillmentStatus,
|
||||||
OrderDetailsFragment,
|
OrderDetailsFragment,
|
||||||
OrderFulfillLineFragment,
|
OrderFulfillLineFragment,
|
||||||
OrderFulfillStockInput,
|
|
||||||
OrderLineFragment,
|
OrderLineFragment,
|
||||||
OrderLineStockDataFragment,
|
OrderLineStockDataFragment,
|
||||||
OrderRefundDataQuery,
|
OrderRefundDataQuery,
|
||||||
|
@ -328,12 +327,17 @@ export const getOrderLineAvailableQuantity = (
|
||||||
return availableQuantity;
|
return availableQuantity;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OrderFulfillStockInputFormsetData = Array<
|
export interface OrderFulfillLineFormData {
|
||||||
Pick<FormsetData<null, OrderFulfillStockInput[]>[0], "id" | "value">
|
quantity: number;
|
||||||
|
warehouse: WarehouseFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrderFulfillStockFormsetData = Array<
|
||||||
|
Pick<FormsetData<null, OrderFulfillLineFormData[]>[0], "id" | "value">
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const getFulfillmentFormsetQuantity = (
|
export const getFulfillmentFormsetQuantity = (
|
||||||
formsetData: OrderFulfillStockInputFormsetData,
|
formsetData: OrderFulfillStockFormsetData,
|
||||||
line: OrderLineStockDataFragment,
|
line: OrderLineStockDataFragment,
|
||||||
) => formsetData?.find(getById(line.id))?.value?.[0]?.quantity;
|
) => formsetData?.find(getById(line.id))?.value?.[0]?.quantity;
|
||||||
|
|
||||||
|
@ -343,9 +347,9 @@ export const getWarehouseStock = (
|
||||||
) => stocks?.find(stock => stock.warehouse.id === warehouseId);
|
) => stocks?.find(stock => stock.warehouse.id === warehouseId);
|
||||||
|
|
||||||
export const isLineAvailableInWarehouse = (
|
export const isLineAvailableInWarehouse = (
|
||||||
line: OrderLineStockDataFragment,
|
line: OrderFulfillLineFragment | OrderLineStockDataFragment,
|
||||||
warehouse: WarehouseFragment,
|
warehouse: WarehouseFragment,
|
||||||
) => {
|
): boolean => {
|
||||||
if (!line?.variant?.stocks) {
|
if (!line?.variant?.stocks) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -356,17 +360,59 @@ export const isLineAvailableInWarehouse = (
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transformFuflillmentLinesToStockInputFormsetData = (
|
export const getLineAvailableQuantityInWarehouse = (
|
||||||
|
line: OrderFulfillLineFragment,
|
||||||
|
warehouse: WarehouseFragment,
|
||||||
|
): number => {
|
||||||
|
if (!line?.variant?.stocks) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const stock = getWarehouseStock(line.variant.stocks, warehouse.id);
|
||||||
|
if (stock) {
|
||||||
|
return getOrderLineAvailableQuantity(line, stock);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLineAllocationWithHighestQuantity = (
|
||||||
|
line: OrderFulfillLineFragment,
|
||||||
|
): OrderFulfillLineFragment["allocations"][number] | undefined =>
|
||||||
|
line.allocations.reduce((prevAllocation, allocation) => {
|
||||||
|
if (!prevAllocation || prevAllocation.quantity < allocation.quantity) {
|
||||||
|
return allocation;
|
||||||
|
}
|
||||||
|
return prevAllocation;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
export const getWarehouseWithHighestAvailableQuantity = (
|
||||||
|
lines?: OrderLineFragment[],
|
||||||
|
): WarehouseFragment | undefined => {
|
||||||
|
let highestAvailableQuantity = 0;
|
||||||
|
|
||||||
|
return lines?.reduce(
|
||||||
|
(selectedWarehouse, line) =>
|
||||||
|
line.allocations.reduce((warehouse, allocation) => {
|
||||||
|
if (allocation.quantity > highestAvailableQuantity) {
|
||||||
|
highestAvailableQuantity = allocation.quantity;
|
||||||
|
return allocation.warehouse;
|
||||||
|
}
|
||||||
|
return warehouse;
|
||||||
|
}, selectedWarehouse),
|
||||||
|
null as WarehouseFragment,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const transformFuflillmentLinesToStockFormsetData = (
|
||||||
lines: FulfillmentFragment["lines"],
|
lines: FulfillmentFragment["lines"],
|
||||||
warehouseId: string,
|
warehouse: WarehouseFragment,
|
||||||
): OrderFulfillStockInputFormsetData =>
|
): OrderFulfillStockFormsetData =>
|
||||||
lines?.map(line => ({
|
lines?.map(line => ({
|
||||||
data: null,
|
data: null,
|
||||||
id: line.orderLine.id,
|
id: line.orderLine.id,
|
||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
quantity: line.quantity,
|
quantity: line.quantity,
|
||||||
warehouse: warehouseId,
|
warehouse,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -9,17 +9,15 @@ import {
|
||||||
OrderUpdateMutationVariables,
|
OrderUpdateMutationVariables,
|
||||||
useCustomerAddressesQuery,
|
useCustomerAddressesQuery,
|
||||||
useWarehouseListQuery,
|
useWarehouseListQuery,
|
||||||
WarehouseFragment,
|
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
|
||||||
import OrderChangeWarehouseDialog from "@saleor/orders/components/OrderChangeWarehouseDialog";
|
|
||||||
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
|
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
|
||||||
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
|
||||||
import OrderFulfillStockExceededDialog from "@saleor/orders/components/OrderFulfillStockExceededDialog";
|
import OrderFulfillStockExceededDialog from "@saleor/orders/components/OrderFulfillStockExceededDialog";
|
||||||
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
|
||||||
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
import { getById } from "@saleor/orders/components/OrderReturnPage/utils";
|
||||||
import { transformFuflillmentLinesToStockInputFormsetData } from "@saleor/orders/utils/data";
|
import { transformFuflillmentLinesToStockFormsetData } from "@saleor/orders/utils/data";
|
||||||
import { PartialMutationProviderOutput } from "@saleor/types";
|
import { PartialMutationProviderOutput } from "@saleor/types";
|
||||||
import { mapEdgesToItems } from "@saleor/utils/maps";
|
import { mapEdgesToItems } from "@saleor/utils/maps";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
@ -48,7 +46,6 @@ import {
|
||||||
OrderUrlQueryParams,
|
OrderUrlQueryParams,
|
||||||
} from "../../../urls";
|
} from "../../../urls";
|
||||||
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
|
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
|
||||||
import { useDefaultWarehouse } from "./useDefaultWarehouse";
|
|
||||||
|
|
||||||
interface OrderNormalDetailsProps {
|
interface OrderNormalDetailsProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -107,10 +104,7 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
const shop = data?.shop;
|
const shop = data?.shop;
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
|
|
||||||
const {
|
const { data: warehousesData } = useWarehouseListQuery({
|
||||||
data: warehousesData,
|
|
||||||
loading: warehousesLoading,
|
|
||||||
} = useWarehouseListQuery({
|
|
||||||
displayLoader: true,
|
displayLoader: true,
|
||||||
variables: {
|
variables: {
|
||||||
first: 30,
|
first: 30,
|
||||||
|
@ -119,15 +113,6 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
|
|
||||||
const warehouses = mapEdgesToItems(warehousesData?.warehouses);
|
const warehouses = mapEdgesToItems(warehousesData?.warehouses);
|
||||||
|
|
||||||
const [fulfillmentWarehouse, setFulfillmentWarehouse] = React.useState<
|
|
||||||
WarehouseFragment
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
useDefaultWarehouse({ warehouses, order, setter: setFulfillmentWarehouse }, [
|
|
||||||
warehousesData,
|
|
||||||
warehousesLoading,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: customerAddresses,
|
data: customerAddresses,
|
||||||
loading: customerAddressesLoading,
|
loading: customerAddressesLoading,
|
||||||
|
@ -206,11 +191,8 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
],
|
],
|
||||||
)}
|
)}
|
||||||
shippingMethods={data?.order?.shippingMethods || []}
|
shippingMethods={data?.order?.shippingMethods || []}
|
||||||
selectedWarehouse={fulfillmentWarehouse}
|
|
||||||
onOrderCancel={() => openModal("cancel")}
|
onOrderCancel={() => openModal("cancel")}
|
||||||
onOrderFulfill={() =>
|
onOrderFulfill={() => navigate(orderFulfillUrl(id))}
|
||||||
navigate(orderFulfillUrl(id, { warehouse: fulfillmentWarehouse?.id }))
|
|
||||||
}
|
|
||||||
onFulfillmentApprove={fulfillmentId =>
|
onFulfillmentApprove={fulfillmentId =>
|
||||||
navigate(
|
navigate(
|
||||||
orderUrl(id, {
|
orderUrl(id, {
|
||||||
|
@ -255,7 +237,6 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onInvoiceSend={id => openModal("invoice-send", { id })}
|
onInvoiceSend={id => openModal("invoice-send", { id })}
|
||||||
onWarehouseChange={() => openModal("change-warehouse")}
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<OrderCannotCancelOrderDialog
|
<OrderCannotCancelOrderDialog
|
||||||
|
@ -336,12 +317,11 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
/>
|
/>
|
||||||
<OrderFulfillStockExceededDialog
|
<OrderFulfillStockExceededDialog
|
||||||
lines={currentApproval?.fulfillment.lines}
|
lines={currentApproval?.fulfillment.lines}
|
||||||
formsetData={transformFuflillmentLinesToStockInputFormsetData(
|
formsetData={transformFuflillmentLinesToStockFormsetData(
|
||||||
currentApproval?.fulfillment.lines,
|
currentApproval?.fulfillment.lines,
|
||||||
currentApproval?.fulfillment.warehouse?.id,
|
currentApproval?.fulfillment.warehouse,
|
||||||
)}
|
)}
|
||||||
open={stockExceeded}
|
open={stockExceeded}
|
||||||
warehouseId={currentApproval?.fulfillment.warehouse?.id}
|
|
||||||
onClose={() => setStockExceeded(false)}
|
onClose={() => setStockExceeded(false)}
|
||||||
confirmButtonState="default"
|
confirmButtonState="default"
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
|
@ -391,13 +371,6 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
|
||||||
}
|
}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
/>
|
/>
|
||||||
<OrderChangeWarehouseDialog
|
|
||||||
open={params.action === "change-warehouse"}
|
|
||||||
lines={order?.lines}
|
|
||||||
currentWarehouse={fulfillmentWarehouse}
|
|
||||||
onConfirm={warehouse => setFulfillmentWarehouse(warehouse)}
|
|
||||||
onClose={closeModal}
|
|
||||||
/>
|
|
||||||
<OrderInvoiceEmailSendDialog
|
<OrderInvoiceEmailSendDialog
|
||||||
confirmButtonState={orderInvoiceSend.opts.status}
|
confirmButtonState={orderInvoiceSend.opts.status}
|
||||||
errors={orderInvoiceSend.opts.data?.invoiceSendEmail.errors || []}
|
errors={orderInvoiceSend.opts.data?.invoiceSendEmail.errors || []}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { OrderDetailsFragment, WarehouseFragment } from "@saleor/graphql";
|
|
||||||
import {
|
|
||||||
getToFulfillOrderLines,
|
|
||||||
isLineAvailableInWarehouse,
|
|
||||||
} from "@saleor/orders/utils/data";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export interface UseDefaultWarehouseOpts {
|
|
||||||
warehouses: WarehouseFragment[];
|
|
||||||
order: OrderDetailsFragment;
|
|
||||||
setter: React.Dispatch<React.SetStateAction<WarehouseFragment>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WarehousesAvailibility {
|
|
||||||
warehouse: WarehouseFragment;
|
|
||||||
linesAvailable: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDefaultWarehouse(
|
|
||||||
{ warehouses, order, setter }: UseDefaultWarehouseOpts,
|
|
||||||
deps: unknown[],
|
|
||||||
) {
|
|
||||||
React.useEffect(() => {
|
|
||||||
const warehousesAvailability: WarehousesAvailibility[] = warehouses?.map(
|
|
||||||
warehouse => {
|
|
||||||
if (!order?.lines) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const linesToFulfill = getToFulfillOrderLines(order.lines);
|
|
||||||
|
|
||||||
const linesAvailable = linesToFulfill.filter(line =>
|
|
||||||
isLineAvailableInWarehouse(line, warehouse),
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return {
|
|
||||||
warehouse,
|
|
||||||
linesAvailable,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const defaultWarehouse = order?.lines
|
|
||||||
? warehousesAvailability?.reduce((prev, curr) =>
|
|
||||||
curr.linesAvailable > prev.linesAvailable ? curr : prev,
|
|
||||||
).warehouse
|
|
||||||
: undefined;
|
|
||||||
setter(defaultWarehouse);
|
|
||||||
}, [order, ...deps]);
|
|
||||||
}
|
|
|
@ -2,9 +2,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import {
|
import {
|
||||||
useFulfillOrderMutation,
|
useFulfillOrderMutation,
|
||||||
useOrderFulfillDataQuery,
|
useOrderFulfillDataQuery,
|
||||||
useOrderFulfillmentUpdateTrackingMutation,
|
|
||||||
useOrderFulfillSettingsQuery,
|
useOrderFulfillSettingsQuery,
|
||||||
useWarehouseDetailsQuery,
|
|
||||||
} from "@saleor/graphql";
|
} from "@saleor/graphql";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
@ -12,7 +10,13 @@ import { getMutationErrors } from "@saleor/misc";
|
||||||
import OrderFulfillPage, {
|
import OrderFulfillPage, {
|
||||||
OrderFulfillSubmitData,
|
OrderFulfillSubmitData,
|
||||||
} from "@saleor/orders/components/OrderFulfillPage";
|
} from "@saleor/orders/components/OrderFulfillPage";
|
||||||
import { OrderFulfillUrlQueryParams, orderUrl } from "@saleor/orders/urls";
|
import {
|
||||||
|
orderFulfillUrl,
|
||||||
|
OrderFulfillUrlDialog,
|
||||||
|
OrderFulfillUrlQueryParams,
|
||||||
|
orderUrl,
|
||||||
|
} from "@saleor/orders/urls";
|
||||||
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -26,6 +30,11 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
|
OrderFulfillUrlDialog,
|
||||||
|
OrderFulfillUrlQueryParams
|
||||||
|
>(navigate, params => orderFulfillUrl(orderId, params), params);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: settings,
|
data: settings,
|
||||||
loading: settingsLoading,
|
loading: settingsLoading,
|
||||||
|
@ -38,8 +47,6 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [updateTracking] = useOrderFulfillmentUpdateTrackingMutation();
|
|
||||||
|
|
||||||
const [fulfillOrder, fulfillOrderOpts] = useFulfillOrderMutation({
|
const [fulfillOrder, fulfillOrderOpts] = useFulfillOrderMutation({
|
||||||
onCompleted: data => {
|
onCompleted: data => {
|
||||||
if (data.orderFulfill.errors.length === 0) {
|
if (data.orderFulfill.errors.length === 0) {
|
||||||
|
@ -56,12 +63,6 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: warehouseData } = useWarehouseDetailsQuery({
|
|
||||||
variables: {
|
|
||||||
id: params?.warehouse,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WindowTitle
|
<WindowTitle
|
||||||
|
@ -85,6 +86,7 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<OrderFulfillPage
|
<OrderFulfillPage
|
||||||
|
params={params}
|
||||||
loading={loading || settingsLoading || fulfillOrderOpts.loading}
|
loading={loading || settingsLoading || fulfillOrderOpts.loading}
|
||||||
errors={fulfillOrderOpts.data?.orderFulfill.errors}
|
errors={fulfillOrderOpts.data?.orderFulfill.errors}
|
||||||
onSubmit={async (formData: OrderFulfillSubmitData) => {
|
onSubmit={async (formData: OrderFulfillSubmitData) => {
|
||||||
|
@ -105,27 +107,13 @@ const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId, params }) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const fulfillments = res?.data?.orderFulfill?.order?.fulfillments;
|
|
||||||
if (fulfillments && formData.trackingNumber) {
|
|
||||||
updateTracking({
|
|
||||||
variables: {
|
|
||||||
id: fulfillments[fulfillments.length - 1].id,
|
|
||||||
input: {
|
|
||||||
...(formData?.trackingNumber && {
|
|
||||||
trackingNumber: formData.trackingNumber,
|
|
||||||
}),
|
|
||||||
notifyCustomer:
|
|
||||||
settings?.shop?.fulfillmentAutoApprove && formData.sendInfo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return getMutationErrors(res);
|
return getMutationErrors(res);
|
||||||
}}
|
}}
|
||||||
order={data?.order}
|
order={data?.order}
|
||||||
saveButtonBar={fulfillOrderOpts.status}
|
saveButtonBar={fulfillOrderOpts.status}
|
||||||
warehouse={warehouseData?.warehouse}
|
|
||||||
shopSettings={settings?.shop}
|
shopSettings={settings?.shop}
|
||||||
|
openModal={openModal}
|
||||||
|
closeModal={closeModal}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -20,7 +20,6 @@ const order = orderFixture(placeholderImage);
|
||||||
|
|
||||||
const props: Omit<OrderDetailsPageProps, "classes"> = {
|
const props: Omit<OrderDetailsPageProps, "classes"> = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
selectedWarehouse: undefined,
|
|
||||||
onBillingAddressEdit: undefined,
|
onBillingAddressEdit: undefined,
|
||||||
onFulfillmentApprove: () => undefined,
|
onFulfillmentApprove: () => undefined,
|
||||||
onFulfillmentCancel: () => undefined,
|
onFulfillmentCancel: () => undefined,
|
||||||
|
@ -39,7 +38,6 @@ const props: Omit<OrderDetailsPageProps, "classes"> = {
|
||||||
onProductClick: undefined,
|
onProductClick: undefined,
|
||||||
onProfileView: () => undefined,
|
onProfileView: () => undefined,
|
||||||
onShippingAddressEdit: undefined,
|
onShippingAddressEdit: undefined,
|
||||||
onWarehouseChange: undefined,
|
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
order,
|
order,
|
||||||
shop: shopFixture,
|
shop: shopFixture,
|
||||||
|
|
Loading…
Reference in a new issue