Merge pull request #487 from mirumee/warehouses/fulfillment-view

Add fulfillment view
This commit is contained in:
Marcin Gębala 2020-04-28 17:21:37 +02:00 committed by GitHub
commit 37b43a064c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 4133 additions and 1511 deletions

View file

@ -18,8 +18,8 @@ export interface UseFormsetOutput<TData = object, TValue = any> {
set: (data: FormsetData<TData, TValue>) => void;
}
function useFormset<TData = object, TValue = any>(
initial: FormsetData<TData>
): UseFormsetOutput<TData> {
initial: FormsetData<TData, TValue>
): UseFormsetOutput<TData, TValue> {
const [data, setData] = useStateFromProps<FormsetData<TData, TValue>>(
initial || []
);

View file

@ -81,6 +81,10 @@ export const commonMessages = defineMessages({
});
export const buttonMessages = defineMessages({
accept: {
defaultMessage: "Accept",
description: "button"
},
back: {
defaultMessage: "Back",
description: "button"
@ -117,6 +121,10 @@ export const buttonMessages = defineMessages({
defaultMessage: "Manage",
description: "button"
},
ok: {
defaultMessage: "OK",
description: "button"
},
remove: {
defaultMessage: "Remove",
description: "button"

View file

@ -4,14 +4,13 @@ import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
export interface OrderBulkCancelDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
numberOfOrders: string;
open: boolean;
onClose: () => void;
onConfirm: (restock: boolean) => void;
onConfirm: () => void;
}
const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
@ -22,7 +21,6 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
onConfirm
}) => {
const intl = useIntl();
const [restock, setRestock] = React.useState(true);
return (
<ActionDialog
@ -34,7 +32,7 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
description: "dialog header"
})}
onClose={onClose}
onConfirm={() => onConfirm(restock)}
onConfirm={onConfirm}
>
<DialogContentText>
<FormattedMessage
@ -45,15 +43,6 @@ const OrderBulkCancelDialog: React.FC<OrderBulkCancelDialogProps> = ({
}}
/>
</DialogContentText>
<ControlledCheckbox
checked={restock}
label={intl.formatMessage({
defaultMessage: "Release all stock allocated to these orders",
description: "switch button"
})}
name="restock"
onChange={event => setRestock(event.target.value)}
/>
</ActionDialog>
);
};

View file

@ -4,45 +4,25 @@ import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import { makeStyles } from "@material-ui/core/styles";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, {
ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form";
import { buttonMessages } from "@saleor/intl";
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
import FormSpacer from "@saleor/components/FormSpacer";
import getOrderErrorMessage from "@saleor/utils/errors/order";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
export interface FormData {
restock: boolean;
}
const useStyles = makeStyles(
theme => ({
deleteButton: {
"&:hover": {
backgroundColor: theme.palette.error.main
},
backgroundColor: theme.palette.error.main,
color: theme.palette.error.contrastText
}
}),
{ name: "OrderCancelDialog" }
);
export interface OrderCancelDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
errors: OrderErrorFragment[];
number: string;
open: boolean;
onClose?();
onSubmit(data: FormData);
onClose: () => void;
onSubmit: () => void;
}
const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
@ -55,76 +35,52 @@ const OrderCancelDialog: React.FC<OrderCancelDialogProps> = props => {
onClose
} = props;
const classes = useStyles(props);
const intl = useIntl();
const errors = useModalDialogErrors(apiErrors, open);
return (
<Dialog onClose={onClose} open={open}>
<Form
initial={{
restock: true
}}
onSubmit={onSubmit}
>
{({ data, change }) => (
<Dialog onClose={onClose} open={open} maxWidth="sm">
<DialogTitle>
<FormattedMessage
defaultMessage="Cancel Order"
description="dialog header"
id="OrderCancelDialogHeader"
/>
</DialogTitle>
<DialogContent>
<DialogContentText>
<FormattedMessage
defaultMessage="Cancelling this order will release unfulfilled stocks, so they can be bought by other customers. <b>Order will not be refunded when cancelling order - You need to do it manually.</b> Are you sure you want to cancel this order?"
values={{
b: (...chunks) => <b>{chunks}</b>,
orderNumber
}}
/>
</DialogContentText>
{errors.length > 0 && (
<>
<DialogTitle>
<FormattedMessage
defaultMessage="Cancel Order"
description="dialog header"
id="OrderCancelDialogHeader"
/>
</DialogTitle>
<DialogContent>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to cancel order #{orderNumber}?"
values={{
orderNumber
}}
/>
<ControlledCheckbox
checked={data.restock}
label={intl.formatMessage({
defaultMessage: "Release all stock allocated to this order",
description: "switch button"
})}
name="restock"
onChange={change}
/>
<FormSpacer />
{errors.map(err => (
<DialogContentText color="error">
{getOrderErrorMessage(err, intl)}
</DialogContentText>
{errors.length > 0 && (
<>
<FormSpacer />
{errors.map(err => (
<DialogContentText color="error">
{getOrderErrorMessage(err, intl)}
</DialogContentText>
))}
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
transitionState={confirmButtonState}
className={classes.deleteButton}
variant="contained"
type="submit"
>
<FormattedMessage
defaultMessage="Cancel Order"
description="button"
id="OrderCancelDialogButton"
/>
</ConfirmButton>
</DialogActions>
))}
</>
)}
</Form>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
onClick={onSubmit}
transitionState={confirmButtonState}
variant="contained"
type="submit"
>
<FormattedMessage {...buttonMessages.accept} />
</ConfirmButton>
</DialogActions>
</Dialog>
);
};

View file

@ -0,0 +1,57 @@
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import makeStyles from "@material-ui/core/styles/makeStyles";
import React from "react";
import { FormattedMessage } from "react-intl";
import { buttonMessages } from "@saleor/intl";
import { DialogProps } from "@saleor/types";
const useStyles = makeStyles(
theme => ({
button: {
backgroundColor: theme.palette.error.main
}
}),
{
name: "OrderCannotCancelOrderDialog"
}
);
const OrderCannotCancelOrderDialog: React.FC<DialogProps> = ({
open,
onClose
}) => {
const classes = useStyles({});
return (
<Dialog onClose={onClose} open={open} maxWidth="sm">
<DialogTitle>
<FormattedMessage
defaultMessage="Saleor couldnt cancel order"
description="dialog header"
/>
</DialogTitle>
<DialogContent>
<DialogContentText>
<FormattedMessage defaultMessage="There are still fulfillments created for this order. Cancel the fulfillments first before you cancel the order." />
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
variant="contained"
className={classes.button}
onClick={onClose}
>
<FormattedMessage {...buttonMessages.ok} />
</Button>
</DialogActions>
</Dialog>
);
};
OrderCannotCancelOrderDialog.displayName = "OrderCannotCancelOrderDialog";
export default OrderCannotCancelOrderDialog;

View file

@ -0,0 +1,2 @@
export { default } from "./OrderCannotCancelOrderDialog";
export * from "./OrderCannotCancelOrderDialog";

View file

@ -0,0 +1,44 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import Decorator from "@saleor/storybook/Decorator";
import { warehouseList } from "@saleor/warehouses/fixtures";
import { OrderErrorCode } from "@saleor/types/globalTypes";
import OrderFulfillPage, { OrderFulfillPageProps } from "./OrderFulfillPage";
import { orderToFulfill } from "./fixtures";
const props: OrderFulfillPageProps = {
disabled: false,
errors: [],
onBack: () => undefined,
onSubmit: () => undefined,
order: orderToFulfill,
saveButtonBar: "default",
warehouses: warehouseList
};
storiesOf("Views / Orders / Fulfill order", module)
.addDecorator(Decorator)
.add("default", () => <OrderFulfillPage {...props} />)
.add("loading", () => (
<OrderFulfillPage
{...props}
disabled={true}
order={undefined}
warehouses={undefined}
/>
))
.add("error", () => (
<OrderFulfillPage
{...props}
errors={[
{
__typename: "OrderError",
code: OrderErrorCode.INSUFFICIENT_STOCK,
field: null,
orderLine: orderToFulfill.lines[0].id,
warehouse: warehouseList[0].id
}
]}
/>
));

View file

@ -0,0 +1,413 @@
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import TextField from "@material-ui/core/TextField";
import classNames from "classnames";
import Typography from "@material-ui/core/Typography";
import useFormset, { FormsetData } from "@saleor/hooks/useFormset";
import {
OrderFulfillStockInput,
OrderErrorCode
} from "@saleor/types/globalTypes";
import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import TableCellAvatar from "@saleor/components/TableCellAvatar";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form";
import {
OrderFulfillData_order,
OrderFulfillData_order_lines
} from "@saleor/orders/types/OrderFulfillData";
import CardTitle from "@saleor/components/CardTitle";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import makeStyles from "@material-ui/core/styles/makeStyles";
import { update } from "@saleor/utils/lists";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { renderCollection } from "@saleor/misc";
import Skeleton from "@saleor/components/Skeleton";
import AppHeader from "@saleor/components/AppHeader";
import { FulfillOrder_orderFulfill_errors } from "@saleor/orders/types/FulfillOrder";
const useStyles = makeStyles(
theme => ({
actionBar: {
flexDirection: "row",
paddingLeft: theme.spacing(2) + 2
},
colName: {
width: 250
},
colQuantity: {
width: 210
},
colQuantityContent: {
alignItems: "center",
display: "inline-flex"
},
colQuantityHeader: {
textAlign: "right"
},
colQuantityTotal: {
textAlign: "right",
width: 180
},
colSku: {
textAlign: "right",
width: 150
},
error: {
color: theme.palette.error.main
},
full: {
fontWeight: 600
},
quantityInnerInput: {
padding: "16px 0 14px 12px"
},
quantityInput: {
width: 100
},
remainingQuantity: {
marginLeft: theme.spacing()
},
table: {
"&&": {
tableLayout: "fixed"
}
}
}),
{ name: "OrderFulfillPage" }
);
interface OrderFulfillFormData {
sendInfo: boolean;
}
interface OrderFulfillSubmitData extends OrderFulfillFormData {
items: FormsetData<null, OrderFulfillStockInput[]>;
}
export interface OrderFulfillPageProps {
disabled: boolean;
errors: FulfillOrder_orderFulfill_errors[];
order: OrderFulfillData_order;
saveButtonBar: ConfirmButtonTransitionState;
warehouses: WarehouseFragment[];
onBack: () => void;
onSubmit: (data: OrderFulfillSubmitData) => void;
}
const initialFormData: OrderFulfillFormData = {
sendInfo: true
};
function getRemainingQuantity(line: OrderFulfillData_order_lines): number {
return line.quantity - line.quantityFulfilled;
}
const OrderFulfillPage: React.FC<OrderFulfillPageProps> = ({
disabled,
errors,
order,
saveButtonBar,
warehouses,
onBack,
onSubmit
}) => {
const intl = useIntl();
const classes = useStyles({});
const { change: formsetChange, data: formsetData } = useFormset<
null,
OrderFulfillStockInput[]
>(
order?.lines
.filter(line => getRemainingQuantity(line) > 0)
.map(line => ({
data: null,
id: line.id,
label: line.variant.attributes
.map(attribute =>
attribute.values
.map(attributeValue => attributeValue.name)
.join(" , ")
)
.join(" / "),
value: line.variant.stocks.map(stock => ({
quantity: 0,
warehouse: stock.warehouse.id
}))
}))
);
const handleSubmit = (formData: OrderFulfillFormData) =>
onSubmit({
...formData,
items: formsetData
});
return (
<Container>
<AppHeader onBack={onBack}>
{order?.number
? intl.formatMessage(
{
defaultMessage: "Order #{orderNumber}",
description: "page header with order number"
},
{
orderNumber: order.number
}
)
: intl.formatMessage({
defaultMessage: "Order",
description: "page header"
})}
</AppHeader>
<PageHeader
title={intl.formatMessage(
{
defaultMessage: "Order no. {orderNumber} - Add Fulfillment",
description: "page header"
},
{
orderNumber: order?.number
}
)}
/>
<Form initial={initialFormData} onSubmit={handleSubmit}>
{({ change, data, submit }) => (
<>
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Items ready to ship",
description: "header"
})}
/>
<ResponsiveTable className={classes.table}>
<TableHead>
<TableRow>
<TableCell className={classes.colName}>
<FormattedMessage defaultMessage="Product name" />
</TableCell>
<TableCell className={classes.colSku}>
<FormattedMessage
defaultMessage="SKU"
description="product's sku"
/>
</TableCell>
{warehouses?.map(warehouse => (
<TableCell
key={warehouse.id}
className={classNames(
classes.colQuantity,
classes.colQuantityHeader
)}
>
{warehouse.name}
</TableCell>
))}
<TableCell className={classes.colQuantityTotal}>
<FormattedMessage
defaultMessage="Quantity to fulfill"
description="quantity of fulfilled products"
/>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{renderCollection(
order?.lines.filter(line => getRemainingQuantity(line) > 0),
(line, lineIndex) => {
if (!line) {
return (
<TableRow>
<TableCellAvatar className={classes.colName}>
<Skeleton />
</TableCellAvatar>
<TableCell className={classes.colSku}>
<Skeleton />
</TableCell>
{warehouses?.map(warehouse => (
<TableCell
className={classes.colQuantity}
key={warehouse.id}
>
<Skeleton />
</TableCell>
))}
<TableCell className={classes.colQuantityTotal}>
{" "}
<Skeleton />
</TableCell>
</TableRow>
);
}
const remainingQuantity = getRemainingQuantity(line);
const quantityToFulfill = formsetData[
lineIndex
].value.reduce(
(quantityToFulfill, lineInput) =>
quantityToFulfill + (lineInput.quantity || 0),
0
);
const overfulfill = remainingQuantity < quantityToFulfill;
return (
<TableRow key={line.id}>
<TableCellAvatar
className={classes.colName}
thumbnail={line?.thumbnail?.url}
>
{line.productName}
<Typography color="textSecondary" variant="caption">
{line.variant.attributes
.map(attribute =>
attribute.values
.map(attributeValue => attributeValue.name)
.join(", ")
)
.join(" / ")}
</Typography>
</TableCellAvatar>
<TableCell className={classes.colSku}>
{line.variant.sku}
</TableCell>
{warehouses?.map(warehouse => {
const warehouseStock = line.variant.stocks.find(
stock => stock.warehouse.id === warehouse.id
);
const formsetStock = formsetData[
lineIndex
].value.find(
line => line.warehouse === warehouse.id
);
if (!warehouseStock) {
return (
<TableCell
className={classNames(
classes.colQuantity,
classes.error
)}
>
<FormattedMessage
defaultMessage="No Stock"
description="no variant stock in warehouse"
/>
</TableCell>
);
}
const availableQuantity =
warehouseStock.quantity -
warehouseStock.quantityAllocated;
return (
<TableCell className={classes.colQuantity}>
<div className={classes.colQuantityContent}>
<TextField
type="number"
inputProps={{
className: classes.quantityInnerInput,
max: warehouseStock.quantity,
min: 0,
style: { textAlign: "right" }
}}
className={classes.quantityInput}
value={formsetStock.quantity}
onChange={event =>
formsetChange(
line.id,
update(
{
quantity: parseInt(
event.target.value,
10
),
warehouse: warehouse.id
},
formsetData[lineIndex].value,
(a, b) => a.warehouse === b.warehouse
)
)
}
error={
overfulfill ||
formsetStock.quantity >
availableQuantity ||
!!errors?.find(
err =>
err.warehouse === warehouse.id &&
err.orderLine === line.id &&
err.code ===
OrderErrorCode.INSUFFICIENT_STOCK
)
}
/>
<div className={classes.remainingQuantity}>
/ {availableQuantity}
</div>
</div>
</TableCell>
);
})}
<TableCell className={classes.colQuantityTotal}>
<span
className={classNames({
[classes.error]: overfulfill,
[classes.full]:
remainingQuantity <= quantityToFulfill
})}
>
{quantityToFulfill}
</span>{" "}
/ {remainingQuantity}
</TableCell>
</TableRow>
);
}
)}
</TableBody>
</ResponsiveTable>
<CardActions className={classes.actionBar}>
<ControlledCheckbox
checked={data.sendInfo}
label={intl.formatMessage({
defaultMessage: "Send shipment details to customer",
description: "checkbox"
})}
name="sendInfo"
onChange={change}
/>
</CardActions>
</Card>
<SaveButtonBar
disabled={disabled}
labels={{
save: intl.formatMessage({
defaultMessage: "Fulfill",
description: "fulfill order, button"
})
}}
state={saveButtonBar}
onSave={submit}
onCancel={onBack}
/>
</>
)}
</Form>
</Container>
);
};
OrderFulfillPage.displayName = "OrderFulfillPage";
export default OrderFulfillPage;

View file

@ -0,0 +1,172 @@
/* eslint-disable sort-keys */
import { OrderFulfillData_order } from "@saleor/orders/types/OrderFulfillData";
import { warehouseList } from "@saleor/warehouses/fixtures";
import * as placeholderImage from "@assets/images/sample-product.jpg";
export const orderToFulfill: OrderFulfillData_order = {
__typename: "Order",
id: "T3JkZXI6Mg==",
lines: [
{
__typename: "OrderLine",
id: "T3JkZXJMaW5lOjQ=",
isShippingRequired: true,
productName: "T-Shirt",
quantity: 3,
quantityFulfilled: 1,
variant: {
__typename: "ProductVariant",
id: "UHJvZHVjdFZhcmlhbnQ6Mjk2",
name: "S",
sku: "62783187",
attributes: [
{
__typename: "SelectedAttribute",
values: [
{
__typename: "AttributeValue",
id: "QXR0cmlidXRlVmFsdWU6MzY=",
name: "S"
}
]
}
],
stocks: [
{
__typename: "Stock",
id: "U3RvY2s6NTIy",
warehouse: warehouseList[0],
quantity: 1217,
quantityAllocated: 10
},
{
__typename: "Stock",
id: "U3RvY2s6NTIx",
warehouse: warehouseList[1],
quantity: 1217,
quantityAllocated: 20
},
{
__typename: "Stock",
id: "U3RvY2s6NTIz",
warehouse: warehouseList[2],
quantity: 1217,
quantityAllocated: 4
},
{
__typename: "Stock",
id: "U3RvY2s6NTI0",
warehouse: warehouseList[3],
quantity: 1220,
quantityAllocated: 7
}
]
},
thumbnail: {
__typename: "Image",
url: placeholderImage
}
},
{
__typename: "OrderLine",
id: "T3JkZXJMaW5lOjU=",
isShippingRequired: true,
productName: "Lemon Juice",
quantity: 4,
quantityFulfilled: 0,
variant: {
__typename: "ProductVariant",
id: "UHJvZHVjdFZhcmlhbnQ6MTgx",
name: "2.5l",
sku: "998323583",
attributes: [
{
__typename: "SelectedAttribute",
values: [
{
__typename: "AttributeValue",
id: "QXR0cmlidXRlVmFsdWU6NjE=",
name: "2.5l"
}
]
}
],
stocks: [
{
__typename: "Stock",
id: "U3RvY2s6NTI=",
warehouse: warehouseList[1],
quantity: 760,
quantityAllocated: 2
},
{
__typename: "Stock",
id: "U3RvY2s6NTE=",
warehouse: warehouseList[2],
quantity: 760,
quantityAllocated: 33
},
{
__typename: "Stock",
id: "U3RvY2s6NTM=",
warehouse: warehouseList[3],
quantity: 760,
quantityAllocated: 4
}
]
},
thumbnail: {
__typename: "Image",
url: placeholderImage
}
},
{
__typename: "OrderLine",
id: "T3JkZXJMaW5lOjY=",
isShippingRequired: true,
productName: "Orange Juice",
quantity: 3,
quantityFulfilled: 2,
variant: {
__typename: "ProductVariant",
id: "UHJvZHVjdFZhcmlhbnQ6MTgy",
name: "5l",
sku: "998323584",
attributes: [
{
__typename: "SelectedAttribute",
values: [
{
__typename: "AttributeValue",
id: "QXR0cmlidXRlVmFsdWU6NjI=",
name: "5l"
}
]
}
],
stocks: [
{
__typename: "Stock",
id: "U3RvY2s6NTc=",
warehouse: warehouseList[0],
quantity: 587,
quantityAllocated: 0
},
{
__typename: "Stock",
id: "U3RvY2s6NTY=",
warehouse: warehouseList[2],
quantity: 587,
quantityAllocated: 1
}
]
},
thumbnail: {
__typename: "Image",
url: placeholderImage
}
}
],
number: "9123"
};

View file

@ -0,0 +1,2 @@
export * from "./OrderFulfillPage";
export { default } from "./OrderFulfillPage";

View file

@ -9,6 +9,7 @@ import TableRow from "@material-ui/core/TableRow";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import classNames from "classnames";
import CardMenu from "@saleor/components/CardMenu";
import CardTitle from "@saleor/components/CardTitle";
@ -19,7 +20,7 @@ import StatusLabel from "@saleor/components/StatusLabel";
import TableCellAvatar, {
AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar";
import { maybe, renderCollection } from "../../../misc";
import { maybe, renderCollection, getStringOrPlaceholder } from "../../../misc";
import { FulfillmentStatus } from "../../../types/globalTypes";
import { OrderDetails_order_fulfillments } from "../../types/OrderDetails";
@ -46,7 +47,15 @@ const useStyles = makeStyles(
textAlign: "right",
width: 120
},
infoLabel: {
display: "inline-block"
},
infoLabelWithMargin: {
marginBottom: theme.spacing()
},
infoRow: {
padding: theme.spacing(2, 3)
},
orderNumber: {
display: "inline",
marginLeft: theme.spacing(1)
@ -68,7 +77,7 @@ interface OrderFulfillmentProps {
onTrackingCodeAdd: () => void;
}
const numberOfColumns = 3;
const numberOfColumns = 4;
const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
const {
@ -107,8 +116,8 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
)
: intl.formatMessage(
{
defaultMessage: "Fulfilled ({quantity})",
description: "section header"
defaultMessage: "Cancelled ({quantity})",
description: "cancelled fulfillment, section header"
},
{
quantity
@ -135,7 +144,7 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
menuItems={[
{
label: intl.formatMessage({
defaultMessage: "Cancel shipment",
defaultMessage: "Cancel Fulfillment",
description: "button"
}),
onSelect: onOrderFulfillmentCancel
@ -216,18 +225,47 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
</TableCell>
</TableRow>
))}
{maybe(() => fulfillment.trackingNumber) && (
<TableRow>
<TableCell colSpan={numberOfColumns}>
<TableRow>
<TableCell className={classes.infoRow} colSpan={numberOfColumns}>
<Typography color="textSecondary" variant="body2">
<FormattedMessage
defaultMessage="Tracking Number: {trackingNumber}"
defaultMessage="Fulfilled from: {warehouseName}"
description="fulfillment group"
values={{
trackingNumber: fulfillment.trackingNumber
warehouseName: (
<Typography
className={classNames(classes.infoLabel, {
[classes.infoLabelWithMargin]: !!fulfillment?.trackingNumber
})}
color="textPrimary"
variant="body2"
>
{getStringOrPlaceholder(fulfillment?.warehouse?.name)}
</Typography>
)
}}
/>
</TableCell>
</TableRow>
)}
</Typography>
<Typography color="textSecondary" variant="body2">
{fulfillment?.trackingNumber && (
<FormattedMessage
defaultMessage="Tracking Number: {trackingNumber}"
values={{
trackingNumber: (
<Typography
className={classes.infoLabel}
color="textPrimary"
variant="body2"
>
{fulfillment.trackingNumber}
</Typography>
)
}}
/>
)}
</Typography>
</TableCell>
</TableRow>
</TableBody>
</ResponsiveTable>
{status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && (

View file

@ -11,25 +11,29 @@ import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, {
ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form";
import { buttonMessages } from "@saleor/intl";
import { OrderErrorFragment } from "@saleor/orders/types/OrderErrorFragment";
import FormSpacer from "@saleor/components/FormSpacer";
import getOrderErrorMessage from "@saleor/utils/errors/order";
import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
export interface FormData {
restock: boolean;
export interface OrderFulfillmentCancelDialogFormData {
warehouseId: string;
}
const useStyles = makeStyles(
theme => ({
deleteButton: {
"&:hover": {
backgroundColor: theme.palette.error.main
},
backgroundColor: theme.palette.error.main,
color: theme.palette.error.contrastText
enableOverflow: {
overflow: "visible"
},
paragraph: {
marginBottom: theme.spacing(2)
},
selectCcontainer: {
width: "60%"
}
}),
{ name: "OrderFulfillmentCancelDialog" }
@ -39,69 +43,99 @@ export interface OrderFulfillmentCancelDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
errors: OrderErrorFragment[];
open: boolean;
warehouses: WarehouseFragment[];
onClose();
onConfirm(data: FormData);
onConfirm(data: OrderFulfillmentCancelDialogFormData);
}
const OrderFulfillmentCancelDialog: React.FC<OrderFulfillmentCancelDialogProps> = props => {
const { confirmButtonState, errors, open, onConfirm, onClose } = props;
const {
confirmButtonState,
errors,
open,
warehouses,
onConfirm,
onClose
} = props;
const classes = useStyles(props);
const intl = useIntl();
const [displayValue, setDisplayValue] = React.useState("");
const choices = warehouses?.map(warehouse => ({
label: warehouse.name,
value: warehouse.id
}));
return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="xs">
<Form initial={{ restock: true }} onSubmit={onConfirm}>
{({ change, data, submit }) => (
<>
<DialogTitle>
<FormattedMessage
defaultMessage="Cancel Fulfillment"
description="dialog header"
/>
</DialogTitle>
<DialogContent>
<DialogContentText>
<FormattedMessage defaultMessage="Are you sure you want to cancel this fulfillment?" />
</DialogContentText>
<ControlledCheckbox
checked={data.restock}
label={intl.formatMessage({
defaultMessage: "Restock items?",
description: "switch button"
})}
name="restock"
onChange={change}
/>
{errors.length > 0 && (
<>
<FormSpacer />
{errors.map(err => (
<DialogContentText color="error">
{getOrderErrorMessage(err, intl)}
</DialogContentText>
))}
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
transitionState={confirmButtonState}
className={classes.deleteButton}
variant="contained"
onClick={submit}
>
<Dialog
classes={{
paper: classes.enableOverflow
}}
onClose={onClose}
open={open}
fullWidth
maxWidth="sm"
>
<Form initial={{ warehouseId: null }} onSubmit={onConfirm}>
{({ change, data: formData, submit }) => {
const handleChange = createSingleAutocompleteSelectHandler(
change,
setDisplayValue,
choices
);
return (
<>
<DialogTitle>
<FormattedMessage
defaultMessage="Cancel fulfillment"
description="button"
defaultMessage="Cancel Fulfillment"
description="dialog header"
/>
</ConfirmButton>
</DialogActions>
</>
)}
</DialogTitle>
<DialogContent className={classes.enableOverflow}>
<DialogContentText className={classes.paragraph}>
<FormattedMessage defaultMessage="Are you sure you want to cancel fulfillment? Canceling a fulfillment will restock products at a selected warehouse." />
</DialogContentText>
<div className={classes.selectCcontainer}>
<SingleAutocompleteSelectField
choices={choices}
displayValue={displayValue}
label={intl.formatMessage({
defaultMessage: "Select Warehouse",
description: "select warehouse to restock items"
})}
name="warehouseId"
value={formData.warehouseId}
onChange={handleChange}
/>
</div>
{errors.length > 0 && (
<>
<FormSpacer />
{errors.map(err => (
<DialogContentText color="error">
{getOrderErrorMessage(err, intl)}
</DialogContentText>
))}
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
disabled={formData.warehouseId === null}
transitionState={confirmButtonState}
variant="contained"
onClick={submit}
>
<FormattedMessage {...buttonMessages.accept} />
</ConfirmButton>
</DialogActions>
</>
);
}}
</Form>
</Dialog>
);

View file

@ -54,7 +54,7 @@ const useStyles = makeStyles(
width: 120
},
quantityInput: {
width: "4rem"
width: 100
},
remainingQuantity: {
marginLeft: theme.spacing(),

View file

@ -3,16 +3,15 @@ import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableFooter from "@material-ui/core/TableFooter";
import TableRow from "@material-ui/core/TableRow";
import TableHead from "@material-ui/core/TableHead";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Checkbox from "@saleor/components/Checkbox";
import { DateTime } from "@saleor/components/Date";
import Money from "@saleor/components/Money";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import TableHead from "@saleor/components/TableHead";
import TablePagination from "@saleor/components/TablePagination";
import {
maybe,
@ -20,7 +19,7 @@ import {
transformOrderStatus,
transformPaymentStatus
} from "@saleor/misc";
import { ListActions, ListProps, SortPage } from "@saleor/types";
import { ListProps, SortPage } from "@saleor/types";
import { OrderListUrlSortField } from "@saleor/orders/urls";
import TableCellHeader from "@saleor/components/TableCellHeader";
import { getArrowDirection } from "@saleor/utils/sort";
@ -59,14 +58,11 @@ const useStyles = makeStyles(
{ name: "OrderList" }
);
interface OrderListProps
extends ListProps,
ListActions,
SortPage<OrderListUrlSortField> {
interface OrderListProps extends ListProps, SortPage<OrderListUrlSortField> {
orders: OrderList_orders_edges_node[];
}
const numberOfColumns = 7;
const numberOfColumns = 6;
export const OrderList: React.FC<OrderListProps> = props => {
const {
@ -79,12 +75,7 @@ export const OrderList: React.FC<OrderListProps> = props => {
onUpdateListSettings,
onRowClick,
onSort,
isChecked,
selected,
sort,
toggle,
toggleAll,
toolbar
sort
} = props;
const classes = useStyles(props);
@ -99,14 +90,7 @@ export const OrderList: React.FC<OrderListProps> = props => {
: undefined;
return (
<ResponsiveTable>
<TableHead
colSpan={numberOfColumns}
selected={selected}
disabled={disabled}
items={orders}
toggleAll={toggleAll}
toolbar={toolbar}
>
<TableHead>
<TableCellHeader
direction={
sort.sort === OrderListUrlSortField.number
@ -206,84 +190,67 @@ export const OrderList: React.FC<OrderListProps> = props => {
<TableBody>
{renderCollection(
orderList,
order => {
const isSelected = order ? isChecked(order.id) : false;
return (
<TableRow
hover={!!order}
className={!!order ? classes.link : undefined}
onClick={order ? onRowClick(order.id) : undefined}
key={order ? order.id : "skeleton"}
selected={isSelected}
>
<TableCell padding="checkbox">
<Checkbox
checked={isSelected}
disabled={disabled}
disableClickPropagation
onChange={() => toggle(order.id)}
/>
</TableCell>
<TableCell className={classes.colNumber}>
{maybe(() => order.number) ? (
"#" + order.number
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colDate}>
{maybe(() => order.created) ? (
<DateTime date={order.created} />
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colCustomer}>
{maybe(() => order.billingAddress) ? (
<>
{order.billingAddress.firstName}
&nbsp;
{order.billingAddress.lastName}
</>
) : maybe(() => order.userEmail) !== undefined ? (
order.userEmail
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colPayment}>
{maybe(() => order.paymentStatus.status) !== undefined ? (
order.paymentStatus.status === null ? null : (
<StatusLabel
status={order.paymentStatus.status}
label={order.paymentStatus.localized}
/>
)
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colFulfillment}>
{maybe(() => order.status) ? (
order => (
<TableRow
hover={!!order}
className={!!order ? classes.link : undefined}
onClick={order ? onRowClick(order.id) : undefined}
key={order ? order.id : "skeleton"}
>
<TableCell className={classes.colNumber}>
{maybe(() => order.number) ? "#" + order.number : <Skeleton />}
</TableCell>
<TableCell className={classes.colDate}>
{maybe(() => order.created) ? (
<DateTime date={order.created} />
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colCustomer}>
{maybe(() => order.billingAddress) ? (
<>
{order.billingAddress.firstName}
&nbsp;
{order.billingAddress.lastName}
</>
) : maybe(() => order.userEmail) !== undefined ? (
order.userEmail
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colPayment}>
{maybe(() => order.paymentStatus.status) !== undefined ? (
order.paymentStatus.status === null ? null : (
<StatusLabel
status={order.status.status}
label={order.status.localized}
status={order.paymentStatus.status}
label={order.paymentStatus.localized}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colTotal}>
{maybe(() => order.total.gross) ? (
<Money money={order.total.gross} />
) : (
<Skeleton />
)}
</TableCell>
</TableRow>
);
},
)
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colFulfillment}>
{maybe(() => order.status) ? (
<StatusLabel
status={order.status.status}
label={order.status.localized}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colTotal}>
{maybe(() => order.total.gross) ? (
<Money money={order.total.gross} />
) : (
<Skeleton />
)}
</TableCell>
</TableRow>
),
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>

View file

@ -7,12 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import { sectionNames } from "@saleor/intl";
import {
FilterPageProps,
ListActions,
PageListProps,
SortPage
} from "@saleor/types";
import { FilterPageProps, PageListProps, SortPage } from "@saleor/types";
import { OrderListUrlSortField } from "@saleor/orders/urls";
import FilterBar from "@saleor/components/FilterBar";
import { OrderList_orders_edges_node } from "../../types/OrderList";
@ -25,7 +20,6 @@ import {
export interface OrderListPageProps
extends PageListProps,
ListActions,
FilterPageProps<OrderFilterKeys, OrderListFilterOpts>,
SortPage<OrderListUrlSortField> {
orders: OrderList_orders_edges_node[];

View file

@ -6,7 +6,6 @@ import {
TypedOrderAddNoteMutation,
TypedOrderCancelMutation,
TypedOrderCaptureMutation,
TypedOrderCreateFulfillmentMutation,
TypedOrderDraftCancelMutation,
TypedOrderDraftFinalizeMutation,
TypedOrderDraftUpdateMutation,
@ -24,10 +23,6 @@ import {
import { OrderAddNote, OrderAddNoteVariables } from "../types/OrderAddNote";
import { OrderCancel, OrderCancelVariables } from "../types/OrderCancel";
import { OrderCapture, OrderCaptureVariables } from "../types/OrderCapture";
import {
OrderCreateFulfillment,
OrderCreateFulfillmentVariables
} from "../types/OrderCreateFulfillment";
import {
OrderDraftCancel,
OrderDraftCancelVariables
@ -80,10 +75,6 @@ interface OrderOperationsProps {
OrderCancel,
OrderCancelVariables
>;
orderCreateFulfillment: PartialMutationProviderOutput<
OrderCreateFulfillment,
OrderCreateFulfillmentVariables
>;
orderFulfillmentCancel: PartialMutationProviderOutput<
OrderFulfillmentCancel,
OrderFulfillmentCancelVariables
@ -139,7 +130,6 @@ interface OrderOperationsProps {
>;
}) => React.ReactNode;
onOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
onOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void;
onOrderFulfillmentUpdate: (data: OrderFulfillmentUpdateTracking) => void;
onOrderCancel: (data: OrderCancel) => void;
onOrderVoid: (data: OrderVoid) => void;
@ -160,7 +150,6 @@ interface OrderOperationsProps {
const OrderOperations: React.FC<OrderOperationsProps> = ({
children,
onDraftUpdate,
onOrderFulfillmentCreate,
onNoteAdd,
onOrderCancel,
onOrderLinesAdd,
@ -185,151 +174,140 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
{(...paymentCapture) => (
<TypedOrderRefundMutation onCompleted={onPaymentRefund}>
{(...paymentRefund) => (
<TypedOrderCreateFulfillmentMutation
onCompleted={onOrderFulfillmentCreate}
>
{(...createFulfillment) => (
<TypedOrderAddNoteMutation onCompleted={onNoteAdd}>
{(...addNote) => (
<TypedOrderUpdateMutation onCompleted={onUpdate}>
{(...update) => (
<TypedOrderDraftUpdateMutation
onCompleted={onDraftUpdate}
<TypedOrderAddNoteMutation onCompleted={onNoteAdd}>
{(...addNote) => (
<TypedOrderUpdateMutation onCompleted={onUpdate}>
{(...update) => (
<TypedOrderDraftUpdateMutation
onCompleted={onDraftUpdate}
>
{(...updateDraft) => (
<TypedOrderShippingMethodUpdateMutation
onCompleted={onShippingMethodUpdate}
>
{(...updateDraft) => (
<TypedOrderShippingMethodUpdateMutation
onCompleted={onShippingMethodUpdate}
{(...updateShippingMethod) => (
<TypedOrderLineDeleteMutation
onCompleted={onOrderLineDelete}
>
{(...updateShippingMethod) => (
<TypedOrderLineDeleteMutation
onCompleted={onOrderLineDelete}
{(...deleteOrderLine) => (
<TypedOrderLinesAddMutation
onCompleted={onOrderLinesAdd}
>
{(...deleteOrderLine) => (
<TypedOrderLinesAddMutation
onCompleted={onOrderLinesAdd}
{(...addOrderLine) => (
<TypedOrderLineUpdateMutation
onCompleted={onOrderLineUpdate}
>
{(...addOrderLine) => (
<TypedOrderLineUpdateMutation
onCompleted={onOrderLineUpdate}
{(...updateOrderLine) => (
<TypedOrderFulfillmentCancelMutation
onCompleted={
onOrderFulfillmentCancel
}
>
{(...updateOrderLine) => (
<TypedOrderFulfillmentCancelMutation
{(...cancelFulfillment) => (
<TypedOrderFulfillmentUpdateTrackingMutation
onCompleted={
onOrderFulfillmentCancel
onOrderFulfillmentUpdate
}
>
{(...cancelFulfillment) => (
<TypedOrderFulfillmentUpdateTrackingMutation
{(
...updateTrackingNumber
) => (
<TypedOrderDraftFinalizeMutation
onCompleted={
onOrderFulfillmentUpdate
onDraftFinalize
}
>
{(
...updateTrackingNumber
) => (
<TypedOrderDraftFinalizeMutation
{(...finalizeDraft) => (
<TypedOrderDraftCancelMutation
onCompleted={
onDraftFinalize
onDraftCancel
}
>
{(
...finalizeDraft
...cancelDraft
) => (
<TypedOrderDraftCancelMutation
<TypedOrderMarkAsPaidMutation
onCompleted={
onDraftCancel
onOrderMarkAsPaid
}
>
{(
...cancelDraft
) => (
<TypedOrderMarkAsPaidMutation
onCompleted={
onOrderMarkAsPaid
}
>
{(
...markAsPaid
) =>
children({
orderAddNote: getMutationProviderData(
...addNote
),
orderCancel: getMutationProviderData(
...orderCancel
),
orderDraftCancel: getMutationProviderData(
...cancelDraft
),
orderDraftFinalize: getMutationProviderData(
...finalizeDraft
),
orderDraftUpdate: getMutationProviderData(
...updateDraft
),
orderFulfillmentCancel: getMutationProviderData(
...cancelFulfillment
),
orderFulfillmentUpdateTracking: getMutationProviderData(
...updateTrackingNumber
),
orderLineDelete: getMutationProviderData(
...deleteOrderLine
),
orderLineUpdate: getMutationProviderData(
...updateOrderLine
),
orderLinesAdd: getMutationProviderData(
...addOrderLine
),
orderPaymentCapture: getMutationProviderData(
...paymentCapture
),
orderPaymentMarkAsPaid: getMutationProviderData(
...markAsPaid
) =>
children({
orderAddNote: getMutationProviderData(
...addNote
),
orderCancel: getMutationProviderData(
...orderCancel
),
orderCreateFulfillment: getMutationProviderData(
...createFulfillment
),
orderDraftCancel: getMutationProviderData(
...cancelDraft
),
orderDraftFinalize: getMutationProviderData(
...finalizeDraft
),
orderDraftUpdate: getMutationProviderData(
...updateDraft
),
orderFulfillmentCancel: getMutationProviderData(
...cancelFulfillment
),
orderFulfillmentUpdateTracking: getMutationProviderData(
...updateTrackingNumber
),
orderLineDelete: getMutationProviderData(
...deleteOrderLine
),
orderLineUpdate: getMutationProviderData(
...updateOrderLine
),
orderLinesAdd: getMutationProviderData(
...addOrderLine
),
orderPaymentCapture: getMutationProviderData(
...paymentCapture
),
orderPaymentMarkAsPaid: getMutationProviderData(
...markAsPaid
),
orderPaymentRefund: getMutationProviderData(
...paymentRefund
),
orderShippingMethodUpdate: getMutationProviderData(
...updateShippingMethod
),
orderUpdate: getMutationProviderData(
...update
),
orderVoid: getMutationProviderData(
...orderVoid
)
})
}
</TypedOrderMarkAsPaidMutation>
)}
</TypedOrderDraftCancelMutation>
),
orderPaymentRefund: getMutationProviderData(
...paymentRefund
),
orderShippingMethodUpdate: getMutationProviderData(
...updateShippingMethod
),
orderUpdate: getMutationProviderData(
...update
),
orderVoid: getMutationProviderData(
...orderVoid
)
})
}
</TypedOrderMarkAsPaidMutation>
)}
</TypedOrderDraftFinalizeMutation>
</TypedOrderDraftCancelMutation>
)}
</TypedOrderFulfillmentUpdateTrackingMutation>
</TypedOrderDraftFinalizeMutation>
)}
</TypedOrderFulfillmentCancelMutation>
</TypedOrderFulfillmentUpdateTrackingMutation>
)}
</TypedOrderLineUpdateMutation>
</TypedOrderFulfillmentCancelMutation>
)}
</TypedOrderLinesAddMutation>
</TypedOrderLineUpdateMutation>
)}
</TypedOrderLineDeleteMutation>
</TypedOrderLinesAddMutation>
)}
</TypedOrderShippingMethodUpdateMutation>
</TypedOrderLineDeleteMutation>
)}
</TypedOrderDraftUpdateMutation>
</TypedOrderShippingMethodUpdateMutation>
)}
</TypedOrderUpdateMutation>
</TypedOrderDraftUpdateMutation>
)}
</TypedOrderAddNoteMutation>
</TypedOrderUpdateMutation>
)}
</TypedOrderCreateFulfillmentMutation>
</TypedOrderAddNoteMutation>
)}
</TypedOrderRefundMutation>
)}

View file

@ -1,5 +1,6 @@
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
import { MessageDescriptor } from "react-intl";
import { warehouseList } from "@saleor/warehouses/fixtures";
import { transformOrderStatus, transformPaymentStatus } from "../misc";
import {
FulfillmentStatus,
@ -865,7 +866,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
}
],
status: FulfillmentStatus.FULFILLED,
trackingNumber: ""
trackingNumber: "",
warehouse: warehouseList[1]
},
{
__typename: "Fulfillment",
@ -905,7 +907,8 @@ export const order = (placeholder: string): OrderDetails_order => ({
}
],
status: FulfillmentStatus.FULFILLED,
trackingNumber: ""
trackingNumber: "01nn12399su12nndfsy",
warehouse: warehouseList[0]
}
],
id: "T3JkZXI6OQ==",

View file

@ -14,9 +14,11 @@ import {
orderPath,
OrderUrlQueryParams,
OrderDraftListUrlSortField,
OrderListUrlSortField
OrderListUrlSortField,
orderFulfillPath
} from "./urls";
import OrderDetailsComponent from "./views/OrderDetails";
import OrderFulfillComponent from "./views/OrderFulfill";
import OrderDraftListComponent from "./views/OrderDraftList";
import OrderListComponent from "./views/OrderList";
@ -57,6 +59,10 @@ const OrderDetails: React.FC<RouteComponentProps<any>> = ({
);
};
const OrderFulfill: React.FC<RouteComponentProps<any>> = ({ match }) => (
<OrderFulfillComponent orderId={decodeURIComponent(match.params.id)} />
);
const Component = () => {
const intl = useIntl();
@ -66,6 +72,7 @@ const Component = () => {
<Switch>
<Route exact path={orderDraftListPath} component={OrderDraftList} />
<Route exact path={orderListPath} component={OrderList} />
<Route path={orderFulfillPath(":id")} component={OrderFulfill} />
<Route path={orderPath(":id")} component={OrderDetails} />
</Switch>
</>

View file

@ -8,20 +8,13 @@ import {
fragmentOrderEvent
} from "./queries";
import { OrderAddNote, OrderAddNoteVariables } from "./types/OrderAddNote";
import {
OrderBulkCancel,
OrderBulkCancelVariables
} from "./types/OrderBulkCancel";
import { OrderCancel, OrderCancelVariables } from "./types/OrderCancel";
import { OrderCapture, OrderCaptureVariables } from "./types/OrderCapture";
import {
OrderCreateFulfillment,
OrderCreateFulfillmentVariables
} from "./types/OrderCreateFulfillment";
import {
OrderDraftBulkCancel,
OrderDraftBulkCancelVariables
} from "./types/OrderDraftBulkCancel";
import { FulfillOrder, FulfillOrderVariables } from "./types/FulfillOrder";
import {
OrderDraftCancel,
OrderDraftCancelVariables
@ -74,8 +67,8 @@ export const orderErrorFragment = gql`
const orderCancelMutation = gql`
${fragmentOrderDetails}
${orderErrorFragment}
mutation OrderCancel($id: ID!, $restock: Boolean!) {
orderCancel(id: $id, restock: $restock) {
mutation OrderCancel($id: ID!) {
orderCancel(id: $id) {
errors: orderErrors {
...OrderErrorFragment
}
@ -90,21 +83,6 @@ export const TypedOrderCancelMutation = TypedMutation<
OrderCancelVariables
>(orderCancelMutation);
const orderBulkCancelMutation = gql`
${orderErrorFragment}
mutation OrderBulkCancel($ids: [ID]!, $restock: Boolean!) {
orderBulkCancel(ids: $ids, restock: $restock) {
errors: orderErrors {
...OrderErrorFragment
}
}
}
`;
export const TypedOrderBulkCancelMutation = TypedMutation<
OrderBulkCancel,
OrderBulkCancelVariables
>(orderBulkCancelMutation);
const orderDraftCancelMutation = gql`
${fragmentOrderDetails}
${orderErrorFragment}
@ -234,28 +212,6 @@ export const TypedOrderCaptureMutation = TypedMutation<
OrderCaptureVariables
>(orderCaptureMutation);
const orderCreateFulfillmentMutation = gql`
${fragmentOrderDetails}
${orderErrorFragment}
mutation OrderCreateFulfillment(
$order: ID!
$input: FulfillmentCreateInput!
) {
orderFulfillmentCreate(order: $order, input: $input) {
errors: orderErrors {
...OrderErrorFragment
}
order {
...OrderDetailsFragment
}
}
}
`;
export const TypedOrderCreateFulfillmentMutation = TypedMutation<
OrderCreateFulfillment,
OrderCreateFulfillmentVariables
>(orderCreateFulfillmentMutation);
const orderFulfillmentUpdateTrackingMutation = gql`
${fragmentOrderDetails}
${orderErrorFragment}
@ -477,3 +433,24 @@ export const TypedOrderLineUpdateMutation = TypedMutation<
OrderLineUpdate,
OrderLineUpdateVariables
>(orderLineUpdateMutation);
const fulfillOrder = gql`
${fragmentOrderDetails}
${orderErrorFragment}
mutation FulfillOrder($orderId: ID!, $input: OrderFulfillInput!) {
orderFulfill(order: $orderId, input: $input) {
errors: orderErrors {
...OrderErrorFragment
warehouse
orderLine
}
order {
...OrderDetailsFragment
}
}
}
`;
export const useOrderFulfill = makeMutation<
FulfillOrder,
FulfillOrderVariables
>(fulfillOrder);

View file

@ -13,6 +13,10 @@ import {
SearchOrderVariant as SearchOrderVariantType,
SearchOrderVariantVariables
} from "./types/SearchOrderVariant";
import {
OrderFulfillData,
OrderFulfillDataVariables
} from "./types/OrderFulfillData";
export const fragmentOrderEvent = gql`
fragment OrderEventFragment on OrderEvent {
@ -73,11 +77,32 @@ export const fragmentOrderLine = gql`
}
}
`;
export const fulfillmentFragment = gql`
${fragmentOrderLine}
fragment FulfillmentFragment on Fulfillment {
id
lines {
id
quantity
orderLine {
...OrderLineFragment
}
}
fulfillmentOrder
status
trackingNumber
warehouse {
id
name
}
}
`;
export const fragmentOrderDetails = gql`
${fragmentAddress}
${fragmentOrderEvent}
${fragmentOrderLine}
${fulfillmentFragment}
fragment OrderDetailsFragment on Order {
id
billingAddress {
@ -90,17 +115,7 @@ export const fragmentOrderDetails = gql`
...OrderEventFragment
}
fulfillments {
id
lines {
id
quantity
orderLine {
...OrderLineFragment
}
}
fulfillmentOrder
status
trackingNumber
...FulfillmentFragment
}
lines {
...OrderLineFragment
@ -327,3 +342,45 @@ export const useOrderVariantSearch = makeTopLevelSearch<
SearchOrderVariantType,
SearchOrderVariantVariables
>(searchOrderVariant);
const orderFulfillData = gql`
query OrderFulfillData($orderId: ID!) {
order(id: $orderId) {
id
lines {
id
isShippingRequired
productName
quantity
quantityFulfilled
variant {
id
name
sku
attributes {
values {
id
name
}
}
stocks {
id
warehouse {
id
}
quantity
quantityAllocated
}
}
thumbnail(size: 64) {
url
}
}
number
}
}
`;
export const useOrderFulfillData = makeQuery<
OrderFulfillData,
OrderFulfillDataVariables
>(orderFulfillData);

View file

@ -0,0 +1,291 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { OrderFulfillInput, OrderErrorCode, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: FulfillOrder
// ====================================================
export interface FulfillOrder_orderFulfill_errors {
__typename: "OrderError";
code: OrderErrorCode;
field: string | null;
warehouse: string | null;
orderLine: string | null;
}
export interface FulfillOrder_orderFulfill_order_billingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface FulfillOrder_orderFulfill_order_billingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: FulfillOrder_orderFulfill_order_billingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface FulfillOrder_orderFulfill_order_events_user {
__typename: "User";
id: string;
email: string;
}
export interface FulfillOrder_orderFulfill_order_events {
__typename: "OrderEvent";
id: string;
amount: number | null;
date: any | null;
email: string | null;
emailType: OrderEventsEmailsEnum | null;
message: string | null;
quantity: number | null;
type: OrderEventsEnum | null;
user: FulfillOrder_orderFulfill_order_events_user | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_gross;
net: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice_net;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_unitPrice | null;
thumbnail: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_thumbnail | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine | null;
}
export interface FulfillOrder_orderFulfill_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface FulfillOrder_orderFulfill_order_fulfillments {
__typename: "Fulfillment";
id: string;
lines: (FulfillOrder_orderFulfill_order_fulfillments_lines | null)[] | null;
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: FulfillOrder_orderFulfill_order_fulfillments_warehouse | null;
}
export interface FulfillOrder_orderFulfill_order_lines_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_lines_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_lines_unitPrice {
__typename: "TaxedMoney";
gross: FulfillOrder_orderFulfill_order_lines_unitPrice_gross;
net: FulfillOrder_orderFulfill_order_lines_unitPrice_net;
}
export interface FulfillOrder_orderFulfill_order_lines_thumbnail {
__typename: "Image";
url: string;
}
export interface FulfillOrder_orderFulfill_order_lines {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: FulfillOrder_orderFulfill_order_lines_unitPrice | null;
thumbnail: FulfillOrder_orderFulfill_order_lines_thumbnail | null;
}
export interface FulfillOrder_orderFulfill_order_shippingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface FulfillOrder_orderFulfill_order_shippingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: FulfillOrder_orderFulfill_order_shippingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface FulfillOrder_orderFulfill_order_shippingMethod {
__typename: "ShippingMethod";
id: string;
}
export interface FulfillOrder_orderFulfill_order_shippingPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_shippingPrice {
__typename: "TaxedMoney";
gross: FulfillOrder_orderFulfill_order_shippingPrice_gross;
}
export interface FulfillOrder_orderFulfill_order_subtotal_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_subtotal {
__typename: "TaxedMoney";
gross: FulfillOrder_orderFulfill_order_subtotal_gross;
}
export interface FulfillOrder_orderFulfill_order_total_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_total_tax {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_total {
__typename: "TaxedMoney";
gross: FulfillOrder_orderFulfill_order_total_gross;
tax: FulfillOrder_orderFulfill_order_total_tax;
}
export interface FulfillOrder_orderFulfill_order_totalAuthorized {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_totalCaptured {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_user {
__typename: "User";
id: string;
email: string;
}
export interface FulfillOrder_orderFulfill_order_availableShippingMethods_price {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillOrder_orderFulfill_order_availableShippingMethods {
__typename: "ShippingMethod";
id: string;
name: string;
price: FulfillOrder_orderFulfill_order_availableShippingMethods_price | null;
}
export interface FulfillOrder_orderFulfill_order {
__typename: "Order";
id: string;
billingAddress: FulfillOrder_orderFulfill_order_billingAddress | null;
canFinalize: boolean;
created: any;
customerNote: string;
events: (FulfillOrder_orderFulfill_order_events | null)[] | null;
fulfillments: (FulfillOrder_orderFulfill_order_fulfillments | null)[];
lines: (FulfillOrder_orderFulfill_order_lines | null)[];
number: string | null;
paymentStatus: PaymentChargeStatusEnum | null;
shippingAddress: FulfillOrder_orderFulfill_order_shippingAddress | null;
shippingMethod: FulfillOrder_orderFulfill_order_shippingMethod | null;
shippingMethodName: string | null;
shippingPrice: FulfillOrder_orderFulfill_order_shippingPrice | null;
status: OrderStatus;
subtotal: FulfillOrder_orderFulfill_order_subtotal | null;
total: FulfillOrder_orderFulfill_order_total | null;
actions: (OrderAction | null)[];
totalAuthorized: FulfillOrder_orderFulfill_order_totalAuthorized | null;
totalCaptured: FulfillOrder_orderFulfill_order_totalCaptured | null;
user: FulfillOrder_orderFulfill_order_user | null;
userEmail: string | null;
availableShippingMethods: (FulfillOrder_orderFulfill_order_availableShippingMethods | null)[] | null;
}
export interface FulfillOrder_orderFulfill {
__typename: "OrderFulfill";
errors: FulfillOrder_orderFulfill_errors[];
order: FulfillOrder_orderFulfill_order | null;
}
export interface FulfillOrder {
orderFulfill: FulfillOrder_orderFulfill | null;
}
export interface FulfillOrderVariables {
orderId: string;
input: OrderFulfillInput;
}

View file

@ -0,0 +1,67 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { FulfillmentStatus } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: FulfillmentFragment
// ====================================================
export interface FulfillmentFragment_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillmentFragment_lines_orderLine_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface FulfillmentFragment_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: FulfillmentFragment_lines_orderLine_unitPrice_gross;
net: FulfillmentFragment_lines_orderLine_unitPrice_net;
}
export interface FulfillmentFragment_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface FulfillmentFragment_lines_orderLine {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: FulfillmentFragment_lines_orderLine_unitPrice | null;
thumbnail: FulfillmentFragment_lines_orderLine_thumbnail | null;
}
export interface FulfillmentFragment_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: FulfillmentFragment_lines_orderLine | null;
}
export interface FulfillmentFragment_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface FulfillmentFragment {
__typename: "Fulfillment";
id: string;
lines: (FulfillmentFragment_lines | null)[] | null;
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: FulfillmentFragment_warehouse | null;
}

View file

@ -1,29 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { OrderErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: OrderBulkCancel
// ====================================================
export interface OrderBulkCancel_orderBulkCancel_errors {
__typename: "OrderError";
code: OrderErrorCode;
field: string | null;
}
export interface OrderBulkCancel_orderBulkCancel {
__typename: "OrderBulkCancel";
errors: OrderBulkCancel_orderBulkCancel_errors[];
}
export interface OrderBulkCancel {
orderBulkCancel: OrderBulkCancel_orderBulkCancel | null;
}
export interface OrderBulkCancelVariables {
ids: (string | null)[];
restock: boolean;
}

View file

@ -97,6 +97,12 @@ export interface OrderCancel_orderCancel_order_fulfillments_lines {
orderLine: OrderCancel_orderCancel_order_fulfillments_lines_orderLine | null;
}
export interface OrderCancel_orderCancel_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderCancel_orderCancel_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderCancel_orderCancel_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderCancel_orderCancel_order_fulfillments_warehouse | null;
}
export interface OrderCancel_orderCancel_order_lines_unitPrice_gross {
@ -278,5 +285,4 @@ export interface OrderCancel {
export interface OrderCancelVariables {
id: string;
restock: boolean;
}

View file

@ -97,6 +97,12 @@ export interface OrderCapture_orderCapture_order_fulfillments_lines {
orderLine: OrderCapture_orderCapture_order_fulfillments_lines_orderLine | null;
}
export interface OrderCapture_orderCapture_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderCapture_orderCapture_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderCapture_orderCapture_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderCapture_orderCapture_order_fulfillments_warehouse | null;
}
export interface OrderCapture_orderCapture_order_lines_unitPrice_gross {

View file

@ -1,282 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { FulfillmentCreateInput, OrderErrorCode, OrderEventsEmailsEnum, OrderEventsEnum, FulfillmentStatus, PaymentChargeStatusEnum, OrderStatus, OrderAction } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: OrderCreateFulfillment
// ====================================================
export interface OrderCreateFulfillment_orderFulfillmentCreate_errors {
__typename: "OrderError";
code: OrderErrorCode;
field: string | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_events_user {
__typename: "User";
id: string;
email: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_events {
__typename: "OrderEvent";
id: string;
amount: number | null;
date: any | null;
email: string | null;
emailType: OrderEventsEmailsEnum | null;
message: string | null;
quantity: number | null;
type: OrderEventsEnum | null;
user: OrderCreateFulfillment_orderFulfillmentCreate_order_events_user | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_gross;
net: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice_net;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_unitPrice | null;
thumbnail: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine_thumbnail | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines_orderLine | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments {
__typename: "Fulfillment";
id: string;
lines: (OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments_lines | null)[] | null;
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice {
__typename: "TaxedMoney";
gross: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_gross;
net: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice_net;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_lines {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_unitPrice | null;
thumbnail: OrderCreateFulfillment_orderFulfillmentCreate_order_lines_thumbnail | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingMethod {
__typename: "ShippingMethod";
id: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice {
__typename: "TaxedMoney";
gross: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice_gross;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal {
__typename: "TaxedMoney";
gross: OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal_gross;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total_tax {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_total {
__typename: "TaxedMoney";
gross: OrderCreateFulfillment_orderFulfillmentCreate_order_total_gross;
tax: OrderCreateFulfillment_orderFulfillmentCreate_order_total_tax;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_totalAuthorized {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_totalCaptured {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_user {
__typename: "User";
id: string;
email: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods_price {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods {
__typename: "ShippingMethod";
id: string;
name: string;
price: OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods_price | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate_order {
__typename: "Order";
id: string;
billingAddress: OrderCreateFulfillment_orderFulfillmentCreate_order_billingAddress | null;
canFinalize: boolean;
created: any;
customerNote: string;
events: (OrderCreateFulfillment_orderFulfillmentCreate_order_events | null)[] | null;
fulfillments: (OrderCreateFulfillment_orderFulfillmentCreate_order_fulfillments | null)[];
lines: (OrderCreateFulfillment_orderFulfillmentCreate_order_lines | null)[];
number: string | null;
paymentStatus: PaymentChargeStatusEnum | null;
shippingAddress: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingAddress | null;
shippingMethod: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingMethod | null;
shippingMethodName: string | null;
shippingPrice: OrderCreateFulfillment_orderFulfillmentCreate_order_shippingPrice | null;
status: OrderStatus;
subtotal: OrderCreateFulfillment_orderFulfillmentCreate_order_subtotal | null;
total: OrderCreateFulfillment_orderFulfillmentCreate_order_total | null;
actions: (OrderAction | null)[];
totalAuthorized: OrderCreateFulfillment_orderFulfillmentCreate_order_totalAuthorized | null;
totalCaptured: OrderCreateFulfillment_orderFulfillmentCreate_order_totalCaptured | null;
user: OrderCreateFulfillment_orderFulfillmentCreate_order_user | null;
userEmail: string | null;
availableShippingMethods: (OrderCreateFulfillment_orderFulfillmentCreate_order_availableShippingMethods | null)[] | null;
}
export interface OrderCreateFulfillment_orderFulfillmentCreate {
__typename: "FulfillmentCreate";
errors: OrderCreateFulfillment_orderFulfillmentCreate_errors[];
order: OrderCreateFulfillment_orderFulfillmentCreate_order | null;
}
export interface OrderCreateFulfillment {
orderFulfillmentCreate: OrderCreateFulfillment_orderFulfillmentCreate | null;
}
export interface OrderCreateFulfillmentVariables {
order: string;
input: FulfillmentCreateInput;
}

View file

@ -91,6 +91,12 @@ export interface OrderDetails_order_fulfillments_lines {
orderLine: OrderDetails_order_fulfillments_lines_orderLine | null;
}
export interface OrderDetails_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderDetails_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -98,6 +104,7 @@ export interface OrderDetails_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderDetails_order_fulfillments_warehouse | null;
}
export interface OrderDetails_order_lines_unitPrice_gross {

View file

@ -91,6 +91,12 @@ export interface OrderDetailsFragment_fulfillments_lines {
orderLine: OrderDetailsFragment_fulfillments_lines_orderLine | null;
}
export interface OrderDetailsFragment_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderDetailsFragment_fulfillments {
__typename: "Fulfillment";
id: string;
@ -98,6 +104,7 @@ export interface OrderDetailsFragment_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderDetailsFragment_fulfillments_warehouse | null;
}
export interface OrderDetailsFragment_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines {
orderLine: OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderDraftCancel_draftOrderDelete_order_fulfillments_warehouse | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines
orderLine: OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderDraftFinalize_draftOrderComplete_order_fulfillments_warehouse | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines {
orderLine: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderDraftUpdate_draftOrderUpdate_order_fulfillments_warehouse | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_lines_unitPrice_gross {

View file

@ -0,0 +1,71 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: OrderFulfillData
// ====================================================
export interface OrderFulfillData_order_lines_variant_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
}
export interface OrderFulfillData_order_lines_variant_attributes {
__typename: "SelectedAttribute";
values: (OrderFulfillData_order_lines_variant_attributes_values | null)[];
}
export interface OrderFulfillData_order_lines_variant_stocks_warehouse {
__typename: "Warehouse";
id: string;
}
export interface OrderFulfillData_order_lines_variant_stocks {
__typename: "Stock";
id: string;
warehouse: OrderFulfillData_order_lines_variant_stocks_warehouse;
quantity: number;
quantityAllocated: number;
}
export interface OrderFulfillData_order_lines_variant {
__typename: "ProductVariant";
id: string;
name: string;
sku: string;
attributes: OrderFulfillData_order_lines_variant_attributes[];
stocks: (OrderFulfillData_order_lines_variant_stocks | null)[] | null;
}
export interface OrderFulfillData_order_lines_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderFulfillData_order_lines {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
productName: string;
quantity: number;
quantityFulfilled: number;
variant: OrderFulfillData_order_lines_variant | null;
thumbnail: OrderFulfillData_order_lines_thumbnail | null;
}
export interface OrderFulfillData_order {
__typename: "Order";
id: string;
lines: (OrderFulfillData_order_lines | null)[];
number: string | null;
}
export interface OrderFulfillData {
order: OrderFulfillData_order | null;
}
export interface OrderFulfillDataVariables {
orderId: string;
}

View file

@ -97,6 +97,12 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment
orderLine: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillment
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
orderLine: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines {
orderLine: OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine | null;
}
export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderLineDelete_draftOrderLineDelete_order_fulfillments_warehouse | null;
}
export interface OrderLineDelete_draftOrderLineDelete_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines {
orderLine: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine | null;
}
export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_warehouse | null;
}
export interface OrderLineUpdate_draftOrderLineUpdate_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines {
orderLine: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine | null;
}
export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_warehouse | null;
}
export interface OrderLinesAdd_draftOrderLinesCreate_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines {
orderLine: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_warehouse | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderRefund_orderRefund_order_fulfillments_lines {
orderLine: OrderRefund_orderRefund_order_fulfillments_lines_orderLine | null;
}
export interface OrderRefund_orderRefund_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderRefund_orderRefund_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderRefund_orderRefund_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderRefund_orderRefund_order_fulfillments_warehouse | null;
}
export interface OrderRefund_orderRefund_order_lines_unitPrice_gross {

View file

@ -97,6 +97,12 @@ export interface OrderVoid_orderVoid_order_fulfillments_lines {
orderLine: OrderVoid_orderVoid_order_fulfillments_lines_orderLine | null;
}
export interface OrderVoid_orderVoid_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderVoid_orderVoid_order_fulfillments {
__typename: "Fulfillment";
id: string;
@ -104,6 +110,7 @@ export interface OrderVoid_orderVoid_order_fulfillments {
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderVoid_orderVoid_order_fulfillments_warehouse | null;
}
export interface OrderVoid_orderVoid_order_lines_unitPrice_gross {

View file

@ -97,10 +97,14 @@ export type OrderUrlDialog =
| "edit-shipping"
| "edit-shipping-address"
| "finalize"
| "fulfill"
| "mark-paid"
| "refund"
| "void";
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
export const orderUrl = (id: string, params?: OrderUrlQueryParams) =>
orderPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
export const orderFulfillPath = (id: string) =>
urlJoin(orderPath(id), "fulfill");
export const orderFulfillUrl = (id: string) =>
orderFulfillPath(encodeURIComponent(id));

View file

@ -7,7 +7,6 @@ import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandl
import { OrderAddNote } from "../../types/OrderAddNote";
import { OrderCancel } from "../../types/OrderCancel";
import { OrderCapture } from "../../types/OrderCapture";
import { OrderCreateFulfillment } from "../../types/OrderCreateFulfillment";
import { OrderDraftCancel } from "../../types/OrderDraftCancel";
import { OrderDraftFinalize } from "../../types/OrderDraftFinalize";
import { OrderDraftUpdate } from "../../types/OrderDraftUpdate";
@ -31,7 +30,6 @@ interface OrderDetailsMessages {
handleNoteAdd: (data: OrderAddNote) => void;
handleOrderCancel: (data: OrderCancel) => void;
handleOrderFulfillmentCancel: (data: OrderFulfillmentCancel) => void;
handleOrderFulfillmentCreate: (data: OrderCreateFulfillment) => void;
handleOrderFulfillmentUpdate: (
data: OrderFulfillmentUpdateTracking
) => void;
@ -86,17 +84,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
closeModal();
}
};
const handleOrderFulfillmentCreate = (data: OrderCreateFulfillment) => {
const errs = data.orderFulfillmentCreate?.errors;
if (errs.length === 0) {
pushMessage({
text: intl.formatMessage({
defaultMessage: "Items successfully fulfilled"
})
});
closeModal();
}
};
const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => {
const errs = data.orderMarkAsPaid?.errors;
if (errs.length === 0) {
@ -256,7 +243,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
handleNoteAdd,
handleOrderCancel,
handleOrderFulfillmentCancel,
handleOrderFulfillmentCreate,
handleOrderFulfillmentUpdate,
handleOrderLineDelete,
handleOrderLineUpdate,

View file

@ -8,6 +8,8 @@ import useUser from "@saleor/hooks/useUser";
import useCustomerSearch from "@saleor/searches/useCustomerSearch";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { useWarehouseList } from "@saleor/warehouses/queries";
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
import { customerUrl } from "../../../customers/urls";
import {
maybe,
@ -15,7 +17,7 @@ import {
getStringOrPlaceholder
} from "../../../misc";
import { productUrl } from "../../../products/urls";
import { OrderStatus } from "../../../types/globalTypes";
import { OrderStatus, FulfillmentStatus } from "../../../types/globalTypes";
import OrderAddressEditDialog from "../../components/OrderAddressEditDialog";
import OrderCancelDialog from "../../components/OrderCancelDialog";
import OrderDetailsPage from "../../components/OrderDetailsPage";
@ -25,7 +27,6 @@ import OrderDraftFinalizeDialog, {
} from "../../components/OrderDraftFinalizeDialog";
import OrderDraftPage from "../../components/OrderDraftPage";
import OrderFulfillmentCancelDialog from "../../components/OrderFulfillmentCancelDialog";
import OrderFulfillmentDialog from "../../components/OrderFulfillmentDialog";
import OrderFulfillmentTrackingDialog from "../../components/OrderFulfillmentTrackingDialog";
import OrderMarkAsPaidDialog from "../../components/OrderMarkAsPaidDialog/OrderMarkAsPaidDialog";
import OrderPaymentDialog from "../../components/OrderPaymentDialog";
@ -39,7 +40,8 @@ import {
orderListUrl,
orderUrl,
OrderUrlQueryParams,
OrderUrlDialog
OrderUrlDialog,
orderFulfillUrl
} from "../../urls";
import { OrderDetailsMessages } from "./OrderDetailsMessages";
@ -95,6 +97,12 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
} = useOrderVariantSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const warehouses = useWarehouseList({
displayLoader: true,
variables: {
first: 30
}
});
const intl = useIntl();
const [openModal, closeModal] = createDialogActionHandlers<
@ -118,9 +126,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
{orderMessages => (
<OrderOperations
order={id}
onOrderFulfillmentCreate={
orderMessages.handleOrderFulfillmentCreate
}
onNoteAdd={orderMessages.handleNoteAdd}
onOrderCancel={orderMessages.handleOrderCancel}
onOrderVoid={orderMessages.handleOrderVoid}
@ -147,7 +152,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
{({
orderAddNote,
orderCancel,
orderCreateFulfillment,
orderDraftUpdate,
orderLinesAdd,
orderLineDelete,
@ -194,7 +198,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
)}
userPermissions={user?.userPermissions || []}
onOrderCancel={() => openModal("cancel")}
onOrderFulfill={() => openModal("fulfill")}
onOrderFulfill={() => navigate(orderFulfillUrl(id))}
onFulfillmentCancel={fulfillmentId =>
navigate(
orderUrl(id, {
@ -226,6 +230,17 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
navigate(customerUrl(order.user.id))
}
/>
<OrderCannotCancelOrderDialog
onClose={closeModal}
open={
params.action === "cancel" &&
order?.fulfillments.some(
fulfillment =>
fulfillment.status ===
FulfillmentStatus.FULFILLED
)
}
/>
<OrderCancelDialog
confirmButtonState={orderCancel.opts.status}
errors={
@ -234,10 +249,9 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
number={order?.number}
open={params.action === "cancel"}
onClose={closeModal}
onSubmit={variables =>
onSubmit={() =>
orderCancel.mutate({
id,
...variables
id
})
}
/>
@ -298,38 +312,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
})
}
/>
<OrderFulfillmentDialog
confirmButtonState={
orderCreateFulfillment.opts.status
}
errors={
orderCreateFulfillment.opts.data
?.orderFulfillmentCreate.errors || []
}
open={params.action === "fulfill"}
lines={maybe(() => order.lines, []).filter(
line => line.quantityFulfilled < line.quantity
)}
onClose={closeModal}
onSubmit={variables =>
orderCreateFulfillment.mutate({
input: {
...variables,
lines: maybe(() => order.lines, [])
.filter(
line =>
line.quantityFulfilled < line.quantity
)
.map((line, lineIndex) => ({
orderLineId: line.id,
quantity: variables.lines[lineIndex]
}))
.filter(line => line.quantity > 0)
},
order: order.id
})
}
/>
<OrderFulfillmentCancelDialog
confirmButtonState={
orderFulfillmentCancel.opts.status
@ -339,6 +321,11 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
?.orderFulfillmentCancel.errors || []
}
open={params.action === "cancel-fulfillment"}
warehouses={
warehouses.data?.warehouses.edges.map(
edge => edge.node
) || []
}
onConfirm={variables =>
orderFulfillmentCancel.mutate({
id: params.id,

View file

@ -0,0 +1,94 @@
import { useIntl } from "react-intl";
import React from "react";
import { useOrderFulfillData } from "@saleor/orders/queries";
import OrderFulfillPage from "@saleor/orders/components/OrderFulfillPage";
import useNavigator from "@saleor/hooks/useNavigator";
import { orderUrl } from "@saleor/orders/urls";
import { useWarehouseList } from "@saleor/warehouses/queries";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { useOrderFulfill } from "@saleor/orders/mutations";
import useNotifier from "@saleor/hooks/useNotifier";
export interface OrderFulfillProps {
orderId: string;
}
const OrderFulfill: React.FC<OrderFulfillProps> = ({ orderId }) => {
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
const { data, loading } = useOrderFulfillData({
displayLoader: true,
variables: {
orderId
}
});
const { data: warehouseData, loading: warehousesLoading } = useWarehouseList({
displayLoader: true,
variables: {
first: 20
}
});
const [fulfillOrder, fulfillOrderOpts] = useOrderFulfill({
onCompleted: data => {
if (data.orderFulfill.errors.length === 0) {
navigate(orderUrl(orderId), true);
notify({
text: intl.formatMessage({
defaultMessage: "Fulfilled Items",
description: "order fulfilled success message"
})
});
}
}
});
return (
<>
<WindowTitle
title={
data?.order?.number
? intl.formatMessage(
{
defaultMessage: "Fulfill Order #{orderNumber}",
description: "window title"
},
{
orderNumber: data.order.number
}
)
: intl.formatMessage({
defaultMessage: "Fulfill Order",
description: "window title"
})
}
/>
<OrderFulfillPage
disabled={loading || warehousesLoading || fulfillOrderOpts.loading}
errors={fulfillOrderOpts.data?.orderFulfill.errors}
onBack={() => navigate(orderUrl(orderId))}
onSubmit={formData =>
fulfillOrder({
variables: {
input: {
lines: formData.items.map(line => ({
orderLineId: line.id,
stocks: line.value
})),
notifyCustomer: formData.sendInfo
},
orderId
}
})
}
order={data?.order}
saveButtonBar="default"
warehouses={warehouseData?.warehouses.edges.map(edge => edge.node)}
/>
</>
);
};
OrderFulfill.displayName = "OrderFulfill";
export default OrderFulfill;

View file

@ -0,0 +1,2 @@
export * from "./OrderFulfill";
export { default } from "./OrderFulfill";

View file

@ -1,12 +1,10 @@
import Button from "@material-ui/core/Button";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog";
import SaveFilterTabDialog, {
SaveFilterTabDialogFormData
} from "@saleor/components/SaveFilterTabDialog";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
@ -14,20 +12,15 @@ import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop";
import { maybe } from "@saleor/misc";
import { maybe, getStringOrPlaceholder } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import createSortHandler from "@saleor/utils/handlers/sortHandler";
import { getSortParams } from "@saleor/utils/sort";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import OrderBulkCancelDialog from "../../components/OrderBulkCancelDialog";
import OrderListPage from "../../components/OrderListPage/OrderListPage";
import {
TypedOrderBulkCancelMutation,
useOrderDraftCreateMutation
} from "../../mutations";
import { useOrderDraftCreateMutation } from "../../mutations";
import { useOrderListQuery } from "../../queries";
import { OrderBulkCancel } from "../../types/OrderBulkCancel";
import { OrderDraftCreate } from "../../types/OrderDraftCreate";
import {
orderListUrl,
@ -56,9 +49,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
const notify = useNotifier();
const paginate = usePaginator();
const shop = useShop();
const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions(
params.ids
);
const { updateListSettings, settings } = useListSettings(
ListViews.ORDER_LIST
);
@ -91,7 +81,6 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
resetFilters,
handleSearchChange
] = createFilterHandlers({
cleanupFn: reset,
createUrl: orderListUrl,
getFilterQueryParam,
navigate,
@ -103,19 +92,16 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
OrderListUrlQueryParams
>(navigate, orderListUrl, params);
const handleTabChange = (tab: number) => {
reset();
const handleTabChange = (tab: number) =>
navigate(
orderListUrl({
activeTab: tab.toString(),
...getFilterTabs()[tab - 1].data
})
);
};
const handleFilterTabDelete = () => {
deleteFilterTab(currentTab);
reset();
navigate(orderListUrl());
};
@ -135,7 +121,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
}),
[params, settings.rowNumber]
);
const { data, loading, refetch } = useOrderListQuery({
const { data, loading } = useOrderListQuery({
displayLoader: true,
variables: queryVariables
});
@ -146,101 +132,48 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
params
);
const handleOrderBulkCancel = (data: OrderBulkCancel) => {
if (data.orderBulkCancel.errors.length === 0) {
notify({
text: intl.formatMessage({
defaultMessage: "Orders cancelled"
})
});
reset();
refetch();
closeModal();
}
};
const handleSort = createSortHandler(navigate, orderListUrl, params);
return (
<TypedOrderBulkCancelMutation onCompleted={handleOrderBulkCancel}>
{(orderBulkCancel, orderBulkCancelOpts) => {
const onOrderBulkCancel = (restock: boolean) =>
orderBulkCancel({
variables: {
ids: params.ids,
restock
}
});
return (
<>
<OrderListPage
currencySymbol={currencySymbol}
settings={settings}
currentTab={currentTab}
disabled={loading}
filterOpts={getFilterOpts(params)}
orders={maybe(() => data.orders.edges.map(edge => edge.node))}
pageInfo={pageInfo}
sort={getSortParams(params)}
onAdd={createOrder}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(orderUrl(id))}
onSort={handleSort}
isChecked={isSelected}
selected={listElements.length}
toggle={toggle}
toggleAll={toggleAll}
toolbar={
<Button
color="primary"
onClick={() =>
openModal("cancel", {
ids: listElements
})
}
>
<FormattedMessage
defaultMessage="Cancel"
description="cancel orders, button"
/>
</Button>
}
onSearchChange={handleSearchChange}
onFilterChange={changeFilters}
onTabSave={() => openModal("save-search")}
onTabDelete={() => openModal("delete-search")}
onTabChange={handleTabChange}
initialSearch={params.query || ""}
tabs={getFilterTabs().map(tab => tab.name)}
onAll={resetFilters}
/>
<OrderBulkCancelDialog
confirmButtonState={orderBulkCancelOpts.status}
numberOfOrders={maybe(() => params.ids.length.toString(), "...")}
onClose={closeModal}
onConfirm={onOrderBulkCancel}
open={params.action === "cancel"}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
/>
</>
);
}}
</TypedOrderBulkCancelMutation>
<>
<OrderListPage
currencySymbol={currencySymbol}
settings={settings}
currentTab={currentTab}
disabled={loading}
filterOpts={getFilterOpts(params)}
orders={maybe(() => data.orders.edges.map(edge => edge.node))}
pageInfo={pageInfo}
sort={getSortParams(params)}
onAdd={createOrder}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(orderUrl(id))}
onSort={handleSort}
onSearchChange={handleSearchChange}
onFilterChange={changeFilters}
onTabSave={() => openModal("save-search")}
onTabDelete={() => openModal("delete-search")}
onTabChange={handleTabChange}
initialSearch={params.query || ""}
tabs={getFilterTabs().map(tab => tab.name)}
onAll={resetFilters}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleFilterTabDelete}
tabName={getStringOrPlaceholder(tabs[currentTab - 1]?.name)}
/>
</>
);
};

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ import { storiesOf } from "@storybook/react";
import React from "react";
import { OrderErrorCode } from "@saleor/types/globalTypes";
import { warehouseList } from "@saleor/warehouses/fixtures";
import OrderFulfillmentCancelDialog, {
OrderFulfillmentCancelDialogProps
} from "../../../orders/components/OrderFulfillmentCancelDialog";
@ -12,7 +13,8 @@ const props: OrderFulfillmentCancelDialogProps = {
errors: [],
onClose: () => undefined,
onConfirm: () => undefined,
open: true
open: true,
warehouses: warehouseList
};
storiesOf("Orders / OrderFulfillmentCancelDialog", module)

View file

@ -24,6 +24,7 @@ export enum AccountErrorCode {
NOT_FOUND = "NOT_FOUND",
OUT_OF_SCOPE_GROUP = "OUT_OF_SCOPE_GROUP",
OUT_OF_SCOPE_PERMISSION = "OUT_OF_SCOPE_PERMISSION",
OUT_OF_SCOPE_SERVICE_ACCOUNT = "OUT_OF_SCOPE_SERVICE_ACCOUNT",
OUT_OF_SCOPE_USER = "OUT_OF_SCOPE_USER",
PASSWORD_ENTIRELY_NUMERIC = "PASSWORD_ENTIRELY_NUMERIC",
PASSWORD_TOO_COMMON = "PASSWORD_TOO_COMMON",
@ -456,6 +457,7 @@ export enum OrderErrorCode {
CANNOT_DELETE = "CANNOT_DELETE",
CANNOT_REFUND = "CANNOT_REFUND",
CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT",
DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM",
FULFILL_ORDER_LINE = "FULFILL_ORDER_LINE",
GRAPHQL_ERROR = "GRAPHQL_ERROR",
INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK",
@ -790,6 +792,7 @@ export enum WebhookEventTypeEnum {
}
export enum WebhookSortField {
APP = "APP",
NAME = "NAME",
SERVICE_ACCOUNT = "SERVICE_ACCOUNT",
TARGET_URL = "TARGET_URL",
@ -994,18 +997,7 @@ export interface DraftOrderInput {
}
export interface FulfillmentCancelInput {
restock?: boolean | null;
}
export interface FulfillmentCreateInput {
trackingNumber?: string | null;
notifyCustomer?: boolean | null;
lines: (FulfillmentLineInput | null)[];
}
export interface FulfillmentLineInput {
orderLineId?: string | null;
quantity?: number | null;
warehouseId: string;
}
export interface FulfillmentUpdateTrackingInput {
@ -1074,6 +1066,21 @@ export interface OrderFilterInput {
search?: string | null;
}
export interface OrderFulfillInput {
lines: OrderFulfillLineInput[];
notifyCustomer?: boolean | null;
}
export interface OrderFulfillLineInput {
orderLineId?: string | null;
stocks: OrderFulfillStockInput[];
}
export interface OrderFulfillStockInput {
quantity?: number | null;
warehouse?: string | null;
}
export interface OrderLineCreateInput {
quantity: number;
variantId: string;
@ -1478,6 +1485,7 @@ export interface WebhookCreateInput {
targetUrl?: string | null;
events?: (WebhookEventTypeEnum | null)[] | null;
serviceAccount?: string | null;
app?: string | null;
isActive?: boolean | null;
secretKey?: string | null;
}
@ -1497,6 +1505,7 @@ export interface WebhookUpdateInput {
targetUrl?: string | null;
events?: (WebhookEventTypeEnum | null)[] | null;
serviceAccount?: string | null;
app?: string | null;
isActive?: boolean | null;
secretKey?: string | null;
}