* 1721 - add refunds miscellaneous view (#860)

* Create new page for Miscellaneous Refunds

* Replace refund order dialog with dedicated page

* Add data test ids

* Update order details view for refunds (#874)

* 1719 - add refund entry to order history (#875)

* Add refund order history entry

* Update refund event with the right query

* 1722 - add refunds product view (#873)

* Create new page for Miscellaneous Refunds

* Replace refund order dialog with dedicated page

* Add data test ids

* Create refund products table

* Implement refund products view

* Update refund mutation with product lines input

* Fix products quantities on refund page

* Fix order refund submission

* Fix products refund submission input variables

* Filter out fulfillments on refund page

* Update refund page in storybook

* Fix test snapshots after wrong refunds rebase

* Set max refund as captured amount

* Refund queries adjustments

* Display refund values with nullish coalescing operator

* Update test snapshots with refunds

* Refactor order refund values calculation

* Create and use refund order line fragment

* Use old simple refund mutation for miscellaneous refund

* Submit for refund only lines with non-zero quantity set

* Fix showing refund error

* Fix refund details on order details page (#879)

* Update order details view for refunds (#874)

* 1719 - add refund entry to order history (#875)

* Add refund order history entry

* Update refund event with the right query

* 1722 - add refunds product view (#873)

* Create new page for Miscellaneous Refunds

* Replace refund order dialog with dedicated page

* Add data test ids

* Create refund products table

* Implement refund products view

* Update refund mutation with product lines input

* Fix products quantities on refund page

* Fix order refund submission

* Fix products refund submission input variables

* Filter out fulfillments on refund page

* Update refund page in storybook

* Fix test snapshots after wrong refunds rebase

* Set max refund as captured amount

* Refund queries adjustments

* Display refund values with nullish coalescing operator

* Update test snapshots with refunds

* Refactor order refund values calculation

* Create and use refund order line fragment

* Use old simple refund mutation for miscellaneous refund

* Submit for refund only lines with non-zero quantity set

* Fix showing refund error

* Add missing refund amount to order history

* Merge repeated order lines in fulfillment lines

* Update order history events types and test snapshots

* Update changelog with refunds changes
This commit is contained in:
Dawid Tarasiuk 2020-12-01 14:13:05 +01:00 committed by GitHub
parent b16206dfa8
commit eb351b396a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 8350 additions and 296 deletions

View file

@ -7,6 +7,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Add Order Confirmation settings - #840 by @orzechdev and @mmarkusik - Add Order Confirmation settings - #840 by @orzechdev and @mmarkusik
- Add Page Types - #807 by @orzechdev - Add Page Types - #807 by @orzechdev
- Add shipping methods to translation section - #864 by @marekchoinski - Add shipping methods to translation section - #864 by @marekchoinski
- New Miscellaneous and Product refunds - #870 by @orzechdev
# 2.11.1 # 2.11.1

View file

@ -32,6 +32,7 @@ module.exports = api => {
} }
], ],
"@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-proposal-nullish-coalescing-operator",
"react-intl-auto" "react-intl-auto"
]; ];

View file

@ -3284,6 +3284,10 @@
"context": "fulfillment group tracking number", "context": "fulfillment group tracking number",
"string": "Edit tracking" "string": "Edit tracking"
}, },
"src_dot_orders_dot_components_dot_OrderFulfillment_dot_2567258278": {
"context": "refunded fulfillment, section header",
"string": "Refunded ({quantity})"
},
"src_dot_orders_dot_components_dot_OrderFulfillment_dot_2796503714": { "src_dot_orders_dot_components_dot_OrderFulfillment_dot_2796503714": {
"context": "ordered product quantity", "context": "ordered product quantity",
"string": "Quantity" "string": "Quantity"
@ -3323,6 +3327,12 @@
"context": "order history message", "context": "order history message",
"string": "Order address was updated" "string": "Order address was updated"
}, },
"src_dot_orders_dot_components_dot_OrderHistory_dot_123236698": {
"string": "Shipment was refunded"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_1322321687": {
"string": "Refunded amount"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_1463685940": { "src_dot_orders_dot_components_dot_OrderHistory_dot_1463685940": {
"context": "order history message", "context": "order history message",
"string": "Order was marked as paid" "string": "Order was marked as paid"
@ -3407,6 +3417,10 @@
"context": "order history message", "context": "order history message",
"string": "Products were added to draft order" "string": "Products were added to draft order"
}, },
"src_dot_orders_dot_components_dot_OrderHistory_dot_3731274949": {
"context": "order history message",
"string": "Order was refunded by {refundedBy}"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_393045022": { "src_dot_orders_dot_components_dot_OrderHistory_dot_393045022": {
"context": "order history message", "context": "order history message",
"string": "Invoice no. {invoiceNumber} was updated" "string": "Invoice no. {invoiceNumber} was updated"
@ -3434,6 +3448,9 @@
"context": "order history message", "context": "order history message",
"string": "Payment failed" "string": "Payment failed"
}, },
"src_dot_orders_dot_components_dot_OrderHistory_dot_492197448": {
"string": "Products refunded"
},
"src_dot_orders_dot_components_dot_OrderHistory_dot_493321552": { "src_dot_orders_dot_components_dot_OrderHistory_dot_493321552": {
"context": "order history message", "context": "order history message",
"string": "Order cancel information was sent to customer" "string": "Order cancel information was sent to customer"
@ -3546,10 +3563,6 @@
"context": "dialog header", "context": "dialog header",
"string": "Capture Payment" "string": "Capture Payment"
}, },
"src_dot_orders_dot_components_dot_OrderPaymentDialog_dot_250371749": {
"context": "dialog header",
"string": "Refund Payment"
},
"src_dot_orders_dot_components_dot_OrderPaymentDialog_dot_75546233": { "src_dot_orders_dot_components_dot_OrderPaymentDialog_dot_75546233": {
"context": "amount of refunded money", "context": "amount of refunded money",
"string": "Amount" "string": "Amount"
@ -3561,6 +3574,10 @@
"context": "dialog header", "context": "dialog header",
"string": "Void Payment" "string": "Void Payment"
}, },
"src_dot_orders_dot_components_dot_OrderPayment_dot_1322321687": {
"context": "order payment",
"string": "Refunded amount"
},
"src_dot_orders_dot_components_dot_OrderPayment_dot_1325966144": { "src_dot_orders_dot_components_dot_OrderPayment_dot_1325966144": {
"context": "order shipping method name", "context": "order shipping method name",
"string": "Shipping" "string": "Shipping"
@ -3633,6 +3650,162 @@
"src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_353369701": { "src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_353369701": {
"string": "No products matching given query" "string": "No products matching given query"
}, },
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1580639738": {
"context": "order refund amount",
"string": "Proposed refund amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1705174606": {
"context": "order refund amount",
"string": "Max Refund"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_1734445951": {
"context": "order refund amount",
"string": "Refund total amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2045860028": {
"context": "order refund amount",
"string": "Authorized Amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2854815744": {
"context": "order refund amount",
"string": "Previously refunded"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_2907874606": {
"context": "order refund amount",
"string": "Selected products value"
},
"src_dot_orders_dot_components_dot_OrderRefundAmountValues_dot_79173946": {
"context": "order refund amount",
"string": "Shipment cost"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_120912052": {
"context": "order refund amount",
"string": "Refunded items cant be fulfilled"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_159210811": {
"string": "Amount must be bigger than 0"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_2256869831": {
"context": "section header",
"string": "Refunded Amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_2845258362": {
"context": "order refund amount, input button",
"string": "Refund"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_4033685232": {
"string": "Amount cannot be bigger than max refund"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_40513382": {
"context": "order refund amount, input button",
"string": "Refund {currency} {amount}"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_4224226791": {
"context": "label",
"string": "Automatic Amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_508357513": {
"context": "label",
"string": "Manual Amount"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_553737700": {
"context": "checkbox",
"string": "Refund shipment costs"
},
"src_dot_orders_dot_components_dot_OrderRefundAmount_dot_75546233": {
"context": "order refund amount, input label",
"string": "Amount"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1134347598": {
"context": "tabel column header",
"string": "Price"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1657559629": {
"string": "No products found"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_1895667608": {
"context": "tabel column header",
"string": "Product"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_3536696555": {
"context": "tabel column header",
"string": "Refunded Qty"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_3988345170": {
"context": "button",
"string": "Set maximal quantities"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_615325604": {
"context": "section header",
"string": "Fulfillment"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_878013594": {
"context": "tabel column header",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderRefundFulfilledProducts_dot_937914544": {
"context": "error message",
"string": "Improper value"
},
"src_dot_orders_dot_components_dot_OrderRefundPage_dot_194531545": {
"context": "page header",
"string": "Order no. {orderNumber} - Refund"
},
"src_dot_orders_dot_components_dot_OrderRefundPage_dot_3620521256": {
"context": "page header",
"string": "Order"
},
"src_dot_orders_dot_components_dot_OrderRefundPage_dot_580490159": {
"context": "page header with order number",
"string": "Order #{orderNumber}"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_1134347598": {
"context": "tabel column header",
"string": "Price"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_1404351240": {
"context": "section header",
"string": "Unfulfilled Products"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_1657559629": {
"string": "No products found"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_1895667608": {
"context": "tabel column header",
"string": "Product"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_2271032876": {
"context": "section notice",
"string": "Unfulfilled products will be restocked"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_3536696555": {
"context": "tabel column header",
"string": "Refunded Qty"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_3988345170": {
"context": "button",
"string": "Set maximal quantities"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_878013594": {
"context": "tabel column header",
"string": "Total"
},
"src_dot_orders_dot_components_dot_OrderRefundUnfulfilledProducts_dot_937914544": {
"context": "error message",
"string": "Improper value"
},
"src_dot_orders_dot_components_dot_OrderRefund_dot_1275363773": {
"context": "section header",
"string": "Refund Order"
},
"src_dot_orders_dot_components_dot_OrderRefund_dot_refundMiscellaneous": {
"context": "refund type",
"string": "Miscellaneous Refund"
},
"src_dot_orders_dot_components_dot_OrderRefund_dot_refundProducts": {
"context": "refund type",
"string": "Refund Products"
},
"src_dot_orders_dot_components_dot_OrderSettingsPage_dot_1149215359": { "src_dot_orders_dot_components_dot_OrderSettingsPage_dot_1149215359": {
"context": "header", "context": "header",
"string": "Order settings" "string": "Order settings"
@ -3733,9 +3906,6 @@
"src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": { "src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": {
"string": "Shipping method successfully updated" "string": "Shipping method successfully updated"
}, },
"src_dot_orders_dot_views_dot_OrderDetails_dot_667274681": {
"string": "Payment successfully refunded"
},
"src_dot_orders_dot_views_dot_OrderDetails_dot_694622335": { "src_dot_orders_dot_views_dot_OrderDetails_dot_694622335": {
"context": "window title", "context": "window title",
"string": "Draft Order #{orderNumber}" "string": "Draft Order #{orderNumber}"
@ -3772,6 +3942,10 @@
"src_dot_orders_dot_views_dot_OrderList_dot_1738939038": { "src_dot_orders_dot_views_dot_OrderList_dot_1738939038": {
"string": "Order draft successfully created" "string": "Order draft successfully created"
}, },
"src_dot_orders_dot_views_dot_OrderRefund_dot_1366101924": {
"context": "order refunded success message",
"string": "Refunded Items"
},
"src_dot_pageTypes": { "src_dot_pageTypes": {
"context": "page types section name", "context": "page types section name",
"string": "Page Types" "string": "Page Types"

35
package-lock.json generated
View file

@ -946,6 +946,24 @@
"@babel/plugin-syntax-json-strings": "^7.2.0" "@babel/plugin-syntax-json-strings": "^7.2.0"
} }
}, },
"@babel/plugin-proposal-nullish-coalescing-operator": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz",
"integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
"integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
"dev": true
}
}
},
"@babel/plugin-proposal-numeric-separator": { "@babel/plugin-proposal-numeric-separator": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.2.0.tgz",
@ -1058,6 +1076,23 @@
"@babel/helper-plugin-utils": "^7.0.0" "@babel/helper-plugin-utils": "^7.0.0"
} }
}, },
"@babel/plugin-syntax-nullish-coalescing-operator": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
"integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.0"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
"integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
"dev": true
}
}
},
"@babel/plugin-syntax-numeric-separator": { "@babel/plugin-syntax-numeric-separator": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.2.0.tgz",

View file

@ -81,6 +81,7 @@
"@babel/core": "^7.7.7", "@babel/core": "^7.7.7",
"@babel/plugin-proposal-class-properties": "^7.5.0", "@babel/plugin-proposal-class-properties": "^7.5.0",
"@babel/plugin-proposal-decorators": "^7.4.4", "@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
"@babel/plugin-proposal-numeric-separator": "^7.2.0", "@babel/plugin-proposal-numeric-separator": "^7.2.0",
"@babel/plugin-proposal-object-rest-spread": "^7.5.4", "@babel/plugin-proposal-object-rest-spread": "^7.5.4",
"@babel/plugin-proposal-optional-chaining": "^7.8.3", "@babel/plugin-proposal-optional-chaining": "^7.8.3",

View file

@ -47,6 +47,7 @@ enum AccountErrorCode {
DELETE_STAFF_ACCOUNT DELETE_STAFF_ACCOUNT
DELETE_SUPERUSER_ACCOUNT DELETE_SUPERUSER_ACCOUNT
GRAPHQL_ERROR GRAPHQL_ERROR
INACTIVE
INVALID INVALID
INVALID_PASSWORD INVALID_PASSWORD
LEFT_NOT_MANAGEABLE_PERMISSION LEFT_NOT_MANAGEABLE_PERMISSION
@ -1999,8 +2000,16 @@ type FulfillmentLine implements Node {
orderLine: OrderLine orderLine: OrderLine
} }
type FulfillmentRefundProducts {
errors: [Error!]! @deprecated(reason: "Use typed errors with error codes. This field will be removed after 2020-07-31.")
fulfillment: Fulfillment
order: Order
orderErrors: [OrderError!]!
}
enum FulfillmentStatus { enum FulfillmentStatus {
FULFILLED FULFILLED
REFUNDED
CANCELED CANCELED
} }
@ -2648,6 +2657,7 @@ type Mutation {
orderFulfill(input: OrderFulfillInput!, order: ID): OrderFulfill orderFulfill(input: OrderFulfillInput!, order: ID): OrderFulfill
orderFulfillmentCancel(id: ID!, input: FulfillmentCancelInput!): FulfillmentCancel orderFulfillmentCancel(id: ID!, input: FulfillmentCancelInput!): FulfillmentCancel
orderFulfillmentUpdateTracking(id: ID!, input: FulfillmentUpdateTrackingInput!): FulfillmentUpdateTracking orderFulfillmentUpdateTracking(id: ID!, input: FulfillmentUpdateTrackingInput!): FulfillmentUpdateTracking
orderFulfillmentRefundProducts(input: OrderRefundProductsInput!, order: ID!): FulfillmentRefundProducts
orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid
orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund
orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate
@ -2846,6 +2856,7 @@ type Order implements Node & ObjectWithMetadata {
totalBalance: Money! totalBalance: Money!
userEmail: String userEmail: String
isShippingRequired: Boolean! isShippingRequired: Boolean!
linesAvailableForRefund: [OrderLineAvailableForRefund]!
} }
enum OrderAction { enum OrderAction {
@ -2945,6 +2956,8 @@ enum OrderErrorCode {
UNIQUE UNIQUE
VOID_INACTIVE_PAYMENT VOID_INACTIVE_PAYMENT
ZERO_QUANTITY ZERO_QUANTITY
INVALID_REFUND_QUANTITY
CANNOT_REFUND_FULFILLMENT_LINE
INSUFFICIENT_STOCK INSUFFICIENT_STOCK
DUPLICATED_INPUT_ITEM DUPLICATED_INPUT_ITEM
NOT_AVAILABLE_IN_CHANNEL NOT_AVAILABLE_IN_CHANNEL
@ -2971,6 +2984,7 @@ type OrderEvent implements Node {
fulfilledItems: [FulfillmentLine] fulfilledItems: [FulfillmentLine]
warehouse: Warehouse warehouse: Warehouse
transactionReference: String transactionReference: String
shippingCostsIncluded: Boolean
} }
type OrderEventCountableConnection { type OrderEventCountableConnection {
@ -3028,6 +3042,7 @@ enum OrderEventsEnum {
FULFILLMENT_CANCELED FULFILLMENT_CANCELED
FULFILLMENT_RESTOCKED_ITEMS FULFILLMENT_RESTOCKED_ITEMS
FULFILLMENT_FULFILLED_ITEMS FULFILLMENT_FULFILLED_ITEMS
FULFILLMENT_REFUNDED
TRACKING_UPDATED TRACKING_UPDATED
NOTE_ADDED NOTE_ADDED
OTHER OTHER
@ -3082,6 +3097,11 @@ type OrderLine implements Node {
allocations: [Allocation!] allocations: [Allocation!]
} }
type OrderLineAvailableForRefund {
quantity: Int
orderLine: OrderLine
}
input OrderLineCreateInput { input OrderLineCreateInput {
quantity: Int! quantity: Int!
variantId: ID! variantId: ID!
@ -3103,6 +3123,24 @@ type OrderRefund {
orderErrors: [OrderError!]! orderErrors: [OrderError!]!
} }
input OrderRefundFulfillmentLineInput {
fulfillmentLineId: ID
quantity: Int!
}
input OrderRefundLineInput {
orderLineId: ID
quantity: Int!
}
input OrderRefundProductsInput {
orderLines: [OrderRefundLineInput!]
fulfillmentLines: [OrderRefundFulfillmentLineInput!]
notifyCustomer: Boolean
amountToRefund: PositiveDecimal
includeShippingCosts: Boolean = false
}
type OrderSettings { type OrderSettings {
automaticallyConfirmAllNewOrders: Boolean! automaticallyConfirmAllNewOrders: Boolean!
} }
@ -5089,6 +5127,7 @@ enum TransactionError {
} }
enum TransactionKind { enum TransactionKind {
EXTERNAL
AUTH AUTH
PENDING PENDING
ACTION_TO_CONFIRM ACTION_TO_CONFIRM

View file

@ -39,6 +39,7 @@ interface PriceFieldProps {
name?: string; name?: string;
value?: string | number; value?: string | number;
InputProps?: InputProps; InputProps?: InputProps;
inputProps?: InputProps["inputProps"];
required?: boolean; required?: boolean;
onChange(event: any); onChange(event: any);
} }
@ -55,7 +56,8 @@ export const PriceField: React.FC<PriceFieldProps> = props => {
onChange, onChange,
required, required,
value, value,
InputProps InputProps,
inputProps
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -93,7 +95,8 @@ export const PriceField: React.FC<PriceFieldProps> = props => {
}} }}
inputProps={{ inputProps={{
min: minValue, min: minValue,
type: "number" type: "number",
...inputProps
}} }}
name={name} name={name}
disabled={disabled} disabled={disabled}

View file

@ -15,9 +15,15 @@ const useStyles = makeStyles(
formLabel: { formLabel: {
marginBottom: theme.spacing(1) marginBottom: theme.spacing(1)
}, },
radioGroupInline: {
flexDirection: "row"
},
radioLabel: { radioLabel: {
marginBottom: -theme.spacing(0.5) marginBottom: -theme.spacing(0.5)
}, },
radioLabelInline: {
marginRight: theme.spacing(4)
},
root: { root: {
"& $radioLabel": { "& $radioLabel": {
"&:last-of-type": { "&:last-of-type": {
@ -53,6 +59,7 @@ interface RadioGroupFieldProps {
label?: React.ReactNode; label?: React.ReactNode;
name?: string; name?: string;
value: string | number; value: string | number;
variant?: "block" | "inline";
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
@ -66,7 +73,8 @@ export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
value, value,
onChange, onChange,
name, name,
hint hint,
variant = "block"
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -86,13 +94,19 @@ export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
name={name} name={name}
value={value} value={value}
onChange={onChange} onChange={onChange}
className={classNames({
[classes.radioGroupInline]: variant === "inline"
})}
> >
{choices.length > 0 ? ( {choices.length > 0 ? (
choices.map(choice => ( choices.map(choice => (
<FormControlLabel <FormControlLabel
disabled={choice.disabled} disabled={choice.disabled}
value={choice.value} value={choice.value}
className={classes.radioLabel} className={classNames({
[classes.radioLabel]: variant !== "inline",
[classes.radioLabelInline]: variant === "inline"
})}
control={<Radio color="primary" />} control={<Radio color="primary" />}
label={choice.label} label={choice.label}
key={choice.value} key={choice.value}

View file

@ -1,3 +1,4 @@
import grey from "@material-ui/core/colors/grey";
import yellow from "@material-ui/core/colors/yellow"; import yellow from "@material-ui/core/colors/yellow";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Typography, { TypographyProps } from "@material-ui/core/Typography"; import Typography, { TypographyProps } from "@material-ui/core/Typography";
@ -34,6 +35,9 @@ const useStyles = makeStyles(
}, },
successDot: { successDot: {
"&:before": { backgroundColor: theme.palette.primary.main, ...dot } "&:before": { backgroundColor: theme.palette.primary.main, ...dot }
},
unspecifiedDot: {
"&:before": { backgroundColor: grey[500], ...dot }
} }
}; };
}, },
@ -43,7 +47,7 @@ const useStyles = makeStyles(
interface StatusLabelProps { interface StatusLabelProps {
className?: string; className?: string;
label: string | React.ReactNode; label: string | React.ReactNode;
status: "success" | "neutral" | "error" | string; status: "success" | "neutral" | "unspecified" | "error" | string;
typographyProps?: TypographyProps; typographyProps?: TypographyProps;
} }
@ -59,6 +63,7 @@ const StatusLabel: React.FC<StatusLabelProps> = props => {
[className]: true, [className]: true,
[classes.successDot]: status === "success", [classes.successDot]: status === "success",
[classes.neutralDot]: status === "neutral", [classes.neutralDot]: status === "neutral",
[classes.unspecifiedDot]: status === "unspecified",
[classes.errorDot]: status === "error" [classes.errorDot]: status === "error"
})} })}
> >

View file

@ -4,6 +4,7 @@ import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import classNames from "classnames";
import React from "react"; import React from "react";
import { DateTime } from "../Date"; import { DateTime } from "../Date";
@ -25,7 +26,7 @@ const useStyles = makeStyles(
dateExpander: { dateExpander: {
color: theme.typography.caption.color, color: theme.typography.caption.color,
position: "absolute", position: "absolute",
right: theme.spacing(3) right: 0
}, },
dot: { dot: {
backgroundColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main,
@ -36,6 +37,7 @@ const useStyles = makeStyles(
top: 6, top: 6,
width: 8 width: 8
}, },
expanded: {},
noExpander: { noExpander: {
alignItems: "center", alignItems: "center",
display: "flex", display: "flex",
@ -43,12 +45,30 @@ const useStyles = makeStyles(
width: "100%" width: "100%"
}, },
panel: { panel: {
"&$expanded": {
margin: 0
},
"&:before": { "&:before": {
display: "none" display: "none"
}, },
background: "none", background: "none",
margin: 0,
width: "100%" width: "100%"
}, },
panelExpander: {
"&$expanded": {
margin: 0,
minHeight: 0
},
"&> .MuiExpansionPanelSummary-content": {
margin: 0
},
"&> .MuiExpansionPanelSummary-expandIcon": {
padding: 0,
right: theme.spacing(18)
},
margin: 0
},
root: { root: {
"&:last-child:after": { "&:last-child:after": {
background: theme.palette.background.default, background: theme.palette.background.default,
@ -90,10 +110,18 @@ export const TimelineEvent: React.FC<TimelineEventProps> = props => {
<div className={classes.root}> <div className={classes.root}>
<span className={classes.dot} /> <span className={classes.dot} />
{children ? ( {children ? (
<ExpansionPanel className={classes.panel} elevation={0}> <ExpansionPanel
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}> className={classNames(classes.panel, classes.expanded)}
elevation={0}
>
<ExpansionPanelSummary
className={classNames(classes.panelExpander, classes.expanded)}
expandIcon={<ExpandMoreIcon />}
>
<Typography>{title}</Typography> <Typography>{title}</Typography>
<Typography className={classes.dateExpander}>{title}</Typography> <Typography className={classes.dateExpander}>
<DateTime date={date} />
</Typography>
</ExpansionPanelSummary> </ExpansionPanelSummary>
<ExpansionPanelDetails> <ExpansionPanelDetails>
<Typography>{children}</Typography> <Typography>{children}</Typography>

View file

@ -7,6 +7,7 @@ export const fragmentOrderEvent = gql`
fragment OrderEventFragment on OrderEvent { fragment OrderEventFragment on OrderEvent {
id id
amount amount
shippingCostsIncluded
date date
email email
emailType emailType
@ -19,6 +20,14 @@ export const fragmentOrderEvent = gql`
id id
email email
} }
lines {
quantity
orderLine {
id
productName
variantName
}
}
} }
`; `;
@ -49,6 +58,23 @@ export const fragmentOrderLine = gql`
} }
} }
`; `;
export const fragmentRefundOrderLine = gql`
fragment RefundOrderLineFragment on OrderLine {
id
productName
quantity
unitPrice {
gross {
...Money
}
}
thumbnail(size: 64) {
url
}
}
`;
export const fulfillmentFragment = gql` export const fulfillmentFragment = gql`
${fragmentOrderLine} ${fragmentOrderLine}
fragment FulfillmentFragment on Fulfillment { fragment FulfillmentFragment on Fulfillment {

View file

@ -48,10 +48,24 @@ export interface OrderDetailsFragment_events_user {
email: string; email: string;
} }
export interface OrderDetailsFragment_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderDetailsFragment_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderDetailsFragment_events_lines_orderLine | null;
}
export interface OrderDetailsFragment_events { export interface OrderDetailsFragment_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -61,6 +75,7 @@ export interface OrderDetailsFragment_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderDetailsFragment_events_user | null; user: OrderDetailsFragment_events_user | null;
lines: (OrderDetailsFragment_events_lines | null)[] | null;
} }
export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant { export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant {

View file

@ -14,10 +14,24 @@ export interface OrderEventFragment_user {
email: string; email: string;
} }
export interface OrderEventFragment_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderEventFragment_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderEventFragment_lines_orderLine | null;
}
export interface OrderEventFragment { export interface OrderEventFragment {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -27,4 +41,5 @@ export interface OrderEventFragment {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderEventFragment_user | null; user: OrderEventFragment_user | null;
lines: (OrderEventFragment_lines | null)[] | null;
} }

View file

@ -0,0 +1,32 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL fragment: RefundOrderLineFragment
// ====================================================
export interface RefundOrderLineFragment_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface RefundOrderLineFragment_unitPrice {
__typename: "TaxedMoney";
gross: RefundOrderLineFragment_unitPrice_gross;
}
export interface RefundOrderLineFragment_thumbnail {
__typename: "Image";
url: string;
}
export interface RefundOrderLineFragment {
__typename: "OrderLine";
id: string;
productName: string;
quantity: number;
unitPrice: RefundOrderLineFragment_unitPrice | null;
thumbnail: RefundOrderLineFragment_thumbnail | null;
}

View file

@ -233,7 +233,11 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
/> />
<CardSpacer /> <CardSpacer />
<Metadata data={data} onChange={changeMetadata} /> <Metadata data={data} onChange={changeMetadata} />
<OrderHistory history={order?.events} onNoteAdd={onNoteAdd} /> <OrderHistory
history={order?.events}
orderCurrency={order?.total?.gross.currency}
onNoteAdd={onNoteAdd}
/>
</div> </div>
<div> <div>
<OrderCustomer <OrderCustomer

View file

@ -137,7 +137,11 @@ const OrderDraftPage: React.FC<OrderDraftPageProps> = props => {
onOrderLineRemove={onOrderLineRemove} onOrderLineRemove={onOrderLineRemove}
onShippingMethodEdit={onShippingMethodEdit} onShippingMethodEdit={onShippingMethodEdit}
/> />
<OrderHistory history={order?.events} onNoteAdd={onNoteAdd} /> <OrderHistory
history={order?.events}
orderCurrency={order?.total?.gross.currency}
onNoteAdd={onNoteAdd}
/>
</div> </div>
<div> <div>
<OrderCustomer <OrderCustomer

View file

@ -16,6 +16,7 @@ import StatusLabel from "@saleor/components/StatusLabel";
import TableCellAvatar, { import TableCellAvatar, {
AVATAR_MARGIN AVATAR_MARGIN
} from "@saleor/components/TableCellAvatar"; } from "@saleor/components/TableCellAvatar";
import { mergeRepeatedOrderLines } from "@saleor/orders/utils/data";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -95,7 +96,10 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
const intl = useIntl(); const intl = useIntl();
const lines = maybe(() => fulfillment.lines); const lines =
fulfillment?.status === FulfillmentStatus.REFUNDED
? mergeRepeatedOrderLines(fulfillment?.lines)
: fulfillment?.lines;
const status = maybe(() => fulfillment.status); const status = maybe(() => fulfillment.status);
const quantity = lines const quantity = lines
? lines.map(line => line.quantity).reduce((prev, curr) => prev + curr, 0) ? lines.map(line => line.quantity).reduce((prev, curr) => prev + curr, 0)
@ -119,6 +123,16 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
quantity quantity
} }
) )
: status === FulfillmentStatus.REFUNDED
? intl.formatMessage(
{
defaultMessage: "Refunded ({quantity})",
description: "refunded fulfillment, section header"
},
{
quantity
}
)
: intl.formatMessage( : intl.formatMessage(
{ {
defaultMessage: "Cancelled ({quantity})", defaultMessage: "Cancelled ({quantity})",
@ -136,7 +150,11 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
</> </>
} }
status={ status={
status === FulfillmentStatus.FULFILLED ? "success" : "error" status === FulfillmentStatus.FULFILLED
? "success"
: status === FulfillmentStatus.REFUNDED
? "unspecified"
: "error"
} }
/> />
) : ( ) : (
@ -239,47 +257,53 @@ const OrderFulfillment: React.FC<OrderFulfillmentProps> = props => {
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}
<TableRow> {(fulfillment?.warehouse || fulfillment?.trackingNumber) && (
<TableCell className={classes.infoRow} colSpan={numberOfColumns}> <TableRow>
<Typography color="textSecondary" variant="body2"> <TableCell className={classes.infoRow} colSpan={numberOfColumns}>
<FormattedMessage <Typography color="textSecondary" variant="body2">
defaultMessage="Fulfilled from: {warehouseName}" {fulfillment?.warehouse && (
description="fulfillment group" <FormattedMessage
values={{ defaultMessage="Fulfilled from: {warehouseName}"
warehouseName: ( description="fulfillment group"
<Typography values={{
className={classNames(classes.infoLabel, { warehouseName: (
[classes.infoLabelWithMargin]: !!fulfillment?.trackingNumber <Typography
})} className={classNames(classes.infoLabel, {
color="textPrimary" [classes.infoLabelWithMargin]: !!fulfillment?.trackingNumber
variant="body2" })}
> color="textPrimary"
{getStringOrPlaceholder(fulfillment?.warehouse?.name)} variant="body2"
</Typography> >
) {getStringOrPlaceholder(
}} fulfillment?.warehouse?.name
/> )}
</Typography> </Typography>
<Typography color="textSecondary" variant="body2"> )
{fulfillment?.trackingNumber && ( }}
<FormattedMessage />
defaultMessage="Tracking Number: {trackingNumber}" )}
values={{ </Typography>
trackingNumber: ( <Typography color="textSecondary" variant="body2">
<Typography {fulfillment?.trackingNumber && (
className={classes.infoLabel} <FormattedMessage
color="textPrimary" defaultMessage="Tracking Number: {trackingNumber}"
variant="body2" values={{
> trackingNumber: (
{fulfillment.trackingNumber} <Typography
</Typography> className={classes.infoLabel}
) color="textPrimary"
}} variant="body2"
/> >
)} {fulfillment.trackingNumber}
</Typography> </Typography>
</TableCell> )
</TableRow> }}
/>
)}
</Typography>
</TableCell>
</TableRow>
)}
</TableBody> </TableBody>
</ResponsiveTable> </ResponsiveTable>
{status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && ( {status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && (

View file

@ -2,6 +2,7 @@ import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton"; import Skeleton from "@saleor/components/Skeleton";
import { import {
Timeline, Timeline,
@ -144,6 +145,16 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => {
quantity: event.quantity quantity: event.quantity
} }
); );
case OrderEventsEnum.FULFILLMENT_REFUNDED:
return intl.formatMessage(
{
defaultMessage: "Order was refunded by {refundedBy}",
description: "order history message"
},
{
refundedBy: event.user ? event.user.email : null
}
);
case OrderEventsEnum.FULFILLMENT_RESTOCKED_ITEMS: case OrderEventsEnum.FULFILLMENT_RESTOCKED_ITEMS:
return intl.formatMessage( return intl.formatMessage(
{ {
@ -238,10 +249,16 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => {
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
eventSubtitle: {
marginTop: theme.spacing(1)
},
header: { header: {
fontWeight: 500, fontWeight: 500,
marginBottom: theme.spacing(1) marginBottom: theme.spacing(1)
}, },
linesTableCell: {
paddingRight: theme.spacing(3)
},
root: { marginTop: theme.spacing(4) }, root: { marginTop: theme.spacing(4) },
user: { user: {
marginBottom: theme.spacing(1) marginBottom: theme.spacing(1)
@ -252,11 +269,12 @@ const useStyles = makeStyles(
interface OrderHistoryProps { interface OrderHistoryProps {
history: OrderDetails_order_events[]; history: OrderDetails_order_events[];
orderCurrency: string;
onNoteAdd: (data: FormData) => void; onNoteAdd: (data: FormData) => void;
} }
const OrderHistory: React.FC<OrderHistoryProps> = props => { const OrderHistory: React.FC<OrderHistoryProps> = props => {
const { history, onNoteAdd } = props; const { history, orderCurrency, onNoteAdd } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
@ -310,6 +328,74 @@ const OrderHistory: React.FC<OrderHistoryProps> = props => {
/> />
); );
} }
if (event.type === OrderEventsEnum.FULFILLMENT_REFUNDED) {
return (
<TimelineEvent
date={event.date}
title={getEventMessage(event, intl)}
key={event.id}
>
{event.lines && (
<>
<Typography
variant="caption"
color="textSecondary"
className={classes.eventSubtitle}
>
<FormattedMessage defaultMessage="Products refunded" />
</Typography>
<table>
<tbody>
{event.lines.map(line => (
<tr key={line.orderLine.id}>
<td className={classes.linesTableCell}>
{line.orderLine.productName}
</td>
<td className={classes.linesTableCell}>
<Typography
variant="caption"
color="textSecondary"
>
{line.orderLine.variantName}
</Typography>
</td>
<td className={classes.linesTableCell}>
<Typography
variant="caption"
color="textSecondary"
>
{`qty: ${line.quantity}`}
</Typography>
</td>
</tr>
))}
</tbody>
</table>
<Typography
variant="caption"
color="textSecondary"
className={classes.eventSubtitle}
>
<FormattedMessage defaultMessage="Refunded amount" />
</Typography>
{(event.amount || event.amount === 0) && (
<Money
money={{
amount: event.amount,
currency: orderCurrency
}}
/>
)}
{event.shippingCostsIncluded && (
<Typography>
<FormattedMessage defaultMessage="Shipment was refunded" />
</Typography>
)}
</>
)}
</TimelineEvent>
);
}
return ( return (
<TimelineEvent <TimelineEvent
date={event.date} date={event.date}

View file

@ -58,6 +58,10 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
maybe(() => order.paymentStatus), maybe(() => order.paymentStatus),
intl intl
); );
const refundedAmount =
order?.totalCaptured &&
order?.total?.gross &&
subtractMoney(order.totalCaptured, order.total.gross);
return ( return (
<Card> <Card>
<CardTitle <CardTitle
@ -226,6 +230,21 @@ const OrderPayment: React.FC<OrderPaymentProps> = props => {
)} )}
</td> </td>
</tr> </tr>
<tr>
<td>
<FormattedMessage
defaultMessage="Refunded amount"
description="order payment"
/>
</td>
<td className={classes.textRight}>
{refundedAmount?.amount === undefined ? (
<Skeleton />
) : (
<Money money={refundedAmount} />
)}
</td>
</tr>
<tr className={classes.totalRow}> <tr className={classes.totalRow}>
<td> <td>
<FormattedMessage <FormattedMessage

View file

@ -26,7 +26,6 @@ export interface OrderPaymentDialogProps {
errors: OrderErrorFragment[]; errors: OrderErrorFragment[];
open: boolean; open: boolean;
initial: number; initial: number;
variant: "capture" | "refund";
onClose: () => void; onClose: () => void;
onSubmit: (data: FormData) => void; onSubmit: (data: FormData) => void;
} }
@ -36,7 +35,6 @@ const OrderPaymentDialog: React.FC<OrderPaymentDialogProps> = ({
errors, errors,
open, open,
initial, initial,
variant,
onClose, onClose,
onSubmit onSubmit
}) => { }) => {
@ -56,15 +54,10 @@ const OrderPaymentDialog: React.FC<OrderPaymentDialogProps> = ({
{({ data, change, submit }) => ( {({ data, change, submit }) => (
<> <>
<DialogTitle> <DialogTitle>
{variant === "capture" {intl.formatMessage({
? intl.formatMessage({ defaultMessage: "Capture Payment",
defaultMessage: "Capture Payment", description: "dialog header"
description: "dialog header" })}
})
: intl.formatMessage({
defaultMessage: "Refund Payment",
description: "dialog header"
})}
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
<TextField <TextField

View file

@ -0,0 +1,71 @@
import { makeStyles } from "@material-ui/core";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import CardTitle from "@saleor/components/CardTitle";
import RadioGroupField from "@saleor/components/RadioGroupField";
import React from "react";
import { defineMessages, useIntl } from "react-intl";
import { OrderRefundFormData, OrderRefundType } from "../OrderRefundPage/form";
const useStyles = makeStyles(
{
cartContent: { paddingBottom: 0 }
},
{ name: "OrderRefund" }
);
interface OrderRefundProps {
data: OrderRefundFormData;
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
}
const messages = defineMessages({
refundMiscellaneous: {
defaultMessage: "Miscellaneous Refund",
description: "refund type"
},
refundProducts: {
defaultMessage: "Refund Products",
description: "refund type"
}
});
const OrderRefund: React.FC<OrderRefundProps> = props => {
const { data, disabled, onChange } = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Refund Order",
description: "section header"
})}
/>
<CardContent className={classes.cartContent}>
<RadioGroupField
choices={[
{
label: intl.formatMessage(messages.refundProducts),
value: OrderRefundType.PRODUCTS
},
{
label: intl.formatMessage(messages.refundMiscellaneous),
value: OrderRefundType.MISCELLANEOUS
}
]}
disabled={disabled}
name={"type" as keyof FormData}
value={data.type}
onChange={onChange}
variant="inline"
/>
</CardContent>
</Card>
);
};
OrderRefund.displayName = "OrderRefund";
export default OrderRefund;

View file

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

View file

@ -0,0 +1,381 @@
import { makeStyles, RadioGroup } from "@material-ui/core";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Radio from "@material-ui/core/Radio";
import Typography from "@material-ui/core/Typography";
import CardSpacer from "@saleor/components/CardSpacer";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Hr from "@saleor/components/Hr";
import { IMoney } from "@saleor/components/Money";
import PriceField from "@saleor/components/PriceField";
import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment";
import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData";
import {
getAllFulfillmentLinesPriceSum,
getPreviouslyRefundedPrice,
getRefundedLinesPriceSum
} from "@saleor/orders/utils/data";
import { getFormErrors } from "@saleor/utils/errors";
import getOrderErrorMessage from "@saleor/utils/errors/order";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import OrderRefundAmountValues, {
OrderRefundAmountValuesProps
} from "../OrderRefundAmountValues";
import {
OrderRefundAmountCalculationMode,
OrderRefundFormData,
OrderRefundType
} from "../OrderRefundPage/form";
const getMiscellaneousAmountValues = (
order: OrderRefundData_order
): OrderRefundAmountValuesProps => {
const authorizedAmount = order?.total?.gross;
const previouslyRefunded = getPreviouslyRefundedPrice(order);
const maxRefund = order?.totalCaptured;
return {
authorizedAmount,
maxRefund,
previouslyRefunded
};
};
const getProductsAmountValues = (
order: OrderRefundData_order,
data: OrderRefundFormData
): OrderRefundAmountValuesProps => {
const authorizedAmount = order?.total?.gross;
const shipmentCost =
authorizedAmount?.currency &&
(order?.shippingPrice?.gross || {
amount: 0,
currency: authorizedAmount?.currency
});
const previouslyRefunded = getPreviouslyRefundedPrice(order);
const maxRefund = order?.totalCaptured;
const refundedLinesSum = getRefundedLinesPriceSum(
order?.lines,
data.refundedProductQuantities
);
const allFulfillmentLinesSum = getAllFulfillmentLinesPriceSum(
order?.fulfillments,
data.refundedFulfilledProductQuantities
);
const allLinesSum = refundedLinesSum + allFulfillmentLinesSum;
const calculatedTotalAmount = data.refundShipmentCosts
? allLinesSum + shipmentCost?.amount
: allLinesSum;
const selectedProductsValue = authorizedAmount?.currency && {
amount: allLinesSum,
currency: authorizedAmount.currency
};
const proposedRefundAmount = authorizedAmount?.currency && {
amount: calculatedTotalAmount,
currency: authorizedAmount.currency
};
const refundTotalAmount = authorizedAmount?.currency && {
amount: calculatedTotalAmount,
currency: authorizedAmount.currency
};
return {
authorizedAmount,
maxRefund,
previouslyRefunded,
proposedRefundAmount,
refundTotalAmount,
selectedProductsValue,
shipmentCost
};
};
const useStyles = makeStyles(
theme => ({
hr: {
margin: theme.spacing(1, 0)
},
maxRefundRow: {
fontWeight: 600
},
priceField: {
marginTop: theme.spacing(2)
},
refundButton: {
marginTop: theme.spacing(2)
},
refundCaution: {
marginTop: theme.spacing(1)
},
root: {
...theme.typography.body1,
lineHeight: 1.9,
width: "100%"
},
textRight: {
textAlign: "right"
}
}),
{ name: "OrderRefundAmount" }
);
interface RefundAmountInputProps {
data: OrderRefundFormData;
maxRefund: IMoney;
currencySymbol: string;
amountTooSmall: boolean;
amountTooBig: boolean;
disabled: boolean;
errors: OrderErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void;
}
const RefundAmountInput: React.FC<RefundAmountInputProps> = props => {
const {
data,
maxRefund,
amountTooSmall,
amountTooBig,
currencySymbol,
disabled,
errors,
onChange
} = props;
const intl = useIntl();
const classes = useStyles(props);
const formErrors = getFormErrors(["amount"], errors);
return (
<PriceField
disabled={disabled}
onChange={onChange}
currencySymbol={currencySymbol}
name={"amount" as keyof FormData}
value={data.amount}
label={intl.formatMessage({
defaultMessage: "Amount",
description: "order refund amount, input label"
})}
className={classes.priceField}
InputProps={{ inputProps: { max: maxRefund?.amount } }}
inputProps={{
"data-test": "amountInput",
max: maxRefund?.amount
}}
error={!!formErrors.amount || amountTooSmall || amountTooBig}
hint={
getOrderErrorMessage(formErrors.amount, intl) ||
(amountTooSmall &&
intl.formatMessage({
defaultMessage: "Amount must be bigger than 0"
})) ||
(amountTooBig &&
intl.formatMessage({
defaultMessage: "Amount cannot be bigger than max refund"
}))
}
/>
);
};
interface OrderRefundAmountProps {
data: OrderRefundFormData;
order: OrderRefundData_order;
disabled: boolean;
errors: OrderErrorFragment[];
onChange: (event: React.ChangeEvent<any>) => void;
onRefund: () => void;
}
const OrderRefundAmount: React.FC<OrderRefundAmountProps> = props => {
const { data, order, disabled, errors, onChange, onRefund } = props;
const classes = useStyles(props);
const intl = useIntl();
const amountCurrency = order?.total?.gross?.currency;
const {
authorizedAmount,
maxRefund,
previouslyRefunded,
proposedRefundAmount,
refundTotalAmount,
selectedProductsValue,
shipmentCost
} =
data.type === OrderRefundType.PRODUCTS
? getProductsAmountValues(order, data)
: getMiscellaneousAmountValues(order);
const selectedRefundAmount =
data.type === OrderRefundType.PRODUCTS &&
data.amountCalculationMode === OrderRefundAmountCalculationMode.AUTOMATIC
? refundTotalAmount?.amount
: data.amount;
const isAmountTooSmall = selectedRefundAmount && selectedRefundAmount <= 0;
const isAmountTooBig = selectedRefundAmount > maxRefund?.amount;
const disableRefundButton =
!selectedRefundAmount || isAmountTooSmall || isAmountTooBig;
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Refunded Amount",
description: "section header"
})}
/>
<CardContent>
{data.type === OrderRefundType.PRODUCTS && (
<RadioGroup
value={data.amountCalculationMode}
onChange={onChange}
name="amountCalculationMode"
>
<FormControlLabel
disabled={disabled}
value={OrderRefundAmountCalculationMode.AUTOMATIC}
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Automatic Amount",
description: "label"
})}
/>
{data.amountCalculationMode ===
OrderRefundAmountCalculationMode.AUTOMATIC && (
<>
<ControlledCheckbox
checked={data.refundShipmentCosts}
label={intl.formatMessage({
defaultMessage: "Refund shipment costs",
description: "checkbox"
})}
name="refundShipmentCosts"
onChange={onChange}
/>
<OrderRefundAmountValues
authorizedAmount={authorizedAmount}
previouslyRefunded={previouslyRefunded}
maxRefund={maxRefund}
selectedProductsValue={selectedProductsValue}
refundTotalAmount={refundTotalAmount}
shipmentCost={data.refundShipmentCosts && shipmentCost}
/>
<CardSpacer />
</>
)}
<Hr className={classes.hr} />
<FormControlLabel
disabled={disabled}
value={OrderRefundAmountCalculationMode.MANUAL}
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Manual Amount",
description: "label"
})}
/>
{data.amountCalculationMode ===
OrderRefundAmountCalculationMode.MANUAL && (
<>
<ControlledCheckbox
disabled={disabled}
checked={data.refundShipmentCosts}
label={intl.formatMessage({
defaultMessage: "Refund shipment costs",
description: "checkbox"
})}
name="refundShipmentCosts"
onChange={onChange}
/>
<OrderRefundAmountValues
authorizedAmount={authorizedAmount}
previouslyRefunded={previouslyRefunded}
maxRefund={maxRefund}
selectedProductsValue={selectedProductsValue}
proposedRefundAmount={proposedRefundAmount}
shipmentCost={data.refundShipmentCosts && shipmentCost}
/>
<RefundAmountInput
data={data}
maxRefund={maxRefund}
amountTooSmall={isAmountTooSmall}
amountTooBig={isAmountTooBig}
currencySymbol={amountCurrency}
disabled={disabled}
onChange={onChange}
errors={errors}
/>
</>
)}
</RadioGroup>
)}
{data.type === OrderRefundType.MISCELLANEOUS && (
<>
<OrderRefundAmountValues
authorizedAmount={authorizedAmount}
previouslyRefunded={previouslyRefunded}
maxRefund={maxRefund}
/>
<RefundAmountInput
data={data}
maxRefund={maxRefund}
amountTooSmall={isAmountTooSmall}
amountTooBig={isAmountTooBig}
currencySymbol={amountCurrency}
disabled={disabled}
onChange={onChange}
errors={errors}
/>
</>
)}
<Button
type="submit"
color="primary"
variant="contained"
fullWidth
size="large"
onClick={onRefund}
className={classes.refundButton}
disabled={disabled || disableRefundButton}
data-test="submit"
>
{!disableRefundButton ? (
<FormattedMessage
defaultMessage="Refund {currency} {amount}"
description="order refund amount, input button"
values={{
amount: Number(selectedRefundAmount).toFixed(2),
currency: amountCurrency
}}
/>
) : (
<FormattedMessage
defaultMessage="Refund"
description="order refund amount, input button"
/>
)}
</Button>
<Typography
variant="caption"
color="textSecondary"
className={classes.refundCaution}
>
<FormattedMessage
defaultMessage="Refunded items cant be fulfilled"
description="order refund amount"
/>
</Typography>
</CardContent>
</Card>
);
};
OrderRefundAmount.displayName = "OrderRefundAmount";
export default OrderRefundAmount;

View file

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

View file

@ -0,0 +1,153 @@
import { makeStyles } from "@material-ui/core";
import Money, { IMoney } from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton";
import React from "react";
import { FormattedMessage } from "react-intl";
const useStyles = makeStyles(
theme => ({
highlightedRow: {
fontWeight: 600
},
root: {
...theme.typography.body1,
lineHeight: 1.9,
width: "100%"
},
textRight: {
minWidth: 30,
textAlign: "right"
}
}),
{ name: "OrderRefundAmountValues" }
);
export interface OrderRefundAmountValuesProps {
authorizedAmount: IMoney;
shipmentCost?: IMoney;
selectedProductsValue?: IMoney;
previouslyRefunded: IMoney;
maxRefund: IMoney;
proposedRefundAmount?: IMoney;
refundTotalAmount?: IMoney;
}
const OrderRefundAmountValues: React.FC<OrderRefundAmountValuesProps> = ({
authorizedAmount,
shipmentCost,
selectedProductsValue,
previouslyRefunded,
maxRefund,
proposedRefundAmount,
refundTotalAmount
}) => {
const classes = useStyles({});
return (
<table className={classes.root}>
<tbody>
<tr>
<td>
<FormattedMessage
defaultMessage="Authorized Amount"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
{authorizedAmount?.amount !== undefined ? (
<Money money={authorizedAmount} />
) : (
<Skeleton />
)}
</td>
</tr>
{shipmentCost?.amount !== undefined && (
<tr>
<td>
<FormattedMessage
defaultMessage="Shipment cost"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
<Money money={shipmentCost} />
</td>
</tr>
)}
{selectedProductsValue?.amount !== undefined && (
<tr>
<td>
<FormattedMessage
defaultMessage="Selected products value"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
<Money money={selectedProductsValue} />
</td>
</tr>
)}
<tr>
<td>
<FormattedMessage
defaultMessage="Previously refunded"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
{previouslyRefunded?.amount !== undefined ? (
<>
<Money money={previouslyRefunded} />
</>
) : (
<Skeleton />
)}
</td>
</tr>
<tr className={classes.highlightedRow}>
<td>
<FormattedMessage
defaultMessage="Max Refund"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
{maxRefund?.amount !== undefined ? (
<Money money={maxRefund} />
) : (
<Skeleton />
)}
</td>
</tr>
{proposedRefundAmount?.amount !== undefined && (
<tr className={classes.highlightedRow}>
<td>
<FormattedMessage
defaultMessage="Proposed refund amount"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
<Money money={proposedRefundAmount} />
</td>
</tr>
)}
{refundTotalAmount?.amount !== undefined && (
<tr className={classes.highlightedRow}>
<td>
<FormattedMessage
defaultMessage="Refund total amount"
description="order refund amount"
/>
</td>
<td className={classes.textRight}>
<Money money={refundTotalAmount} />
</td>
</tr>
)}
</tbody>
</table>
);
};
OrderRefundAmountValues.displayName = "OrderRefundAmountValues";
export default OrderRefundAmountValues;

View file

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

View file

@ -0,0 +1,246 @@
import { makeStyles } from "@material-ui/core";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Table from "@material-ui/core/Table";
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 Typography from "@material-ui/core/Typography";
import { CSSProperties } from "@material-ui/styles";
import CardTitle from "@saleor/components/CardTitle";
import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton";
import TableCellAvatar from "@saleor/components/TableCellAvatar";
import { FormsetChange } from "@saleor/hooks/useFormset";
import { renderCollection } from "@saleor/misc";
import { OrderRefundData_order_fulfillments } from "@saleor/orders/types/OrderRefundData";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { OrderRefundFormData } from "../OrderRefundPage/form";
const useStyles = makeStyles(
theme => {
const inputPadding: CSSProperties = {
paddingBottom: theme.spacing(2),
paddingTop: theme.spacing(2)
};
return {
cartContent: {
paddingBottom: 0,
paddingTop: 0
},
colQuantity: {
textAlign: "right",
width: 210
},
notice: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(2)
},
orderNumber: {
display: "inline",
marginLeft: theme.spacing(1)
},
quantityInnerInput: {
...inputPadding
},
quantityInnerInputNoRemaining: {
paddingRight: 0
},
remainingQuantity: {
...inputPadding,
color: theme.palette.text.secondary,
whiteSpace: "nowrap"
},
setMaximalQuantityButton: {
marginTop: theme.spacing(1)
}
};
},
{ name: "OrderRefundFulfilledProducts" }
);
interface OrderRefundFulfilledProductsProps {
fulfillment: OrderRefundData_order_fulfillments;
data: OrderRefundFormData;
disabled: boolean;
orderNumber: string;
onRefundedProductQuantityChange: FormsetChange<string>;
onSetMaximalQuantities: () => void;
}
const OrderRefundFulfilledProducts: React.FC<OrderRefundFulfilledProductsProps> = props => {
const {
fulfillment,
data,
disabled,
orderNumber,
onRefundedProductQuantityChange,
onSetMaximalQuantities
} = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card>
<CardTitle
title={
<>
{intl.formatMessage({
defaultMessage: "Fulfillment",
description: "section header"
})}
{fulfillment && (
<Typography className={classes.orderNumber} variant="body1">
{`#${orderNumber}-${fulfillment?.fulfillmentOrder}`}
</Typography>
)}
</>
}
/>
<CardContent className={classes.cartContent}>
<Button
className={classes.setMaximalQuantityButton}
color="primary"
onClick={onSetMaximalQuantities}
data-test="setMaximalQuantityFulfilledButton"
data-test-id={fulfillment?.id}
>
<FormattedMessage
defaultMessage="Set maximal quantities"
description="button"
/>
</Button>
</CardContent>
<Table>
<TableHead>
<TableRow>
<TableCell>
<FormattedMessage
defaultMessage="Product"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Price"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Refunded Qty"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Total"
description="tabel column header"
/>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{renderCollection(
fulfillment?.lines,
line => {
const selectedLineQuantity = data.refundedFulfilledProductQuantities.find(
refundedLine => refundedLine.id === line.id
);
const isError =
Number(selectedLineQuantity?.value) > line?.quantity ||
Number(selectedLineQuantity?.value) < 0;
return (
<TableRow key={line?.id}>
<TableCellAvatar thumbnail={line?.orderLine?.thumbnail?.url}>
{line?.orderLine?.productName ? (
line?.orderLine?.productName
) : (
<Skeleton />
)}
</TableCellAvatar>
<TableCell>
{line?.orderLine?.unitPrice ? (
<Money money={line?.orderLine?.unitPrice.gross} />
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colQuantity}>
{line?.quantity ? (
<TextField
disabled={disabled}
type="number"
inputProps={{
className: classes.quantityInnerInput,
"data-test": "quantityInput",
"data-test-id": line?.id,
max: (line?.quantity).toString(),
min: 0,
style: { textAlign: "right" }
}}
fullWidth
value={selectedLineQuantity?.value}
onChange={event =>
onRefundedProductQuantityChange(
line.id,
event.target.value
)
}
InputProps={{
endAdornment: line?.quantity && (
<div className={classes.remainingQuantity}>
/ {line?.quantity}
</div>
)
}}
error={isError}
helperText={
isError &&
intl.formatMessage({
defaultMessage: "Improper value",
description: "error message"
})
}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell>
{(line?.quantity && line?.orderLine?.unitPrice.gross && (
<Money
money={{
...line?.orderLine.unitPrice.gross,
amount:
(line?.orderLine.unitPrice.gross.amount || 0) *
Number(selectedLineQuantity?.value)
}}
/>
)) || <Skeleton />}
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={4}>
<FormattedMessage defaultMessage="No products found" />
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
);
};
OrderRefundFulfilledProducts.displayName = "OrderRefundFulfilledProducts";
export default OrderRefundFulfilledProducts;

View file

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

View file

@ -0,0 +1,28 @@
import placeholderImage from "@assets/images/placeholder60x60.png";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import { orderToRefund } from "./fixtures";
import { OrderRefundType } from "./form";
import OrderRefundPage, { OrderRefundPageProps } from "./OrderRefundPage";
const props: OrderRefundPageProps = {
disabled: false,
errors: [],
onBack: () => undefined,
onSubmit: () => undefined,
order: orderToRefund(placeholderImage)
};
storiesOf("Views / Orders / Refund order", module)
.addDecorator(Decorator)
.add("products", () => (
<OrderRefundPage {...props} defaultType={OrderRefundType.PRODUCTS} />
))
.add("miscellaneous", () => (
<OrderRefundPage {...props} defaultType={OrderRefundType.MISCELLANEOUS} />
))
.add("loading", () => (
<OrderRefundPage {...props} disabled={true} order={undefined} />
));

View file

@ -0,0 +1,147 @@
import AppHeader from "@saleor/components/AppHeader";
import CardSpacer from "@saleor/components/CardSpacer";
import Container from "@saleor/components/Container";
import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment";
import { SubmitPromise } from "@saleor/hooks/useForm";
import { renderCollection } from "@saleor/misc";
import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData";
import { FulfillmentStatus } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import OrderRefund from "../OrderRefund";
import OrderRefundAmount from "../OrderRefundAmount";
import OrderRefundFulfilledProducts from "../OrderRefundFulfilledProducts";
import OrderRefundUnfulfilledProducts from "../OrderRefundUnfulfilledProducts";
import OrderRefundForm, {
OrderRefundSubmitData,
OrderRefundType
} from "./form";
export interface OrderRefundPageProps {
order: OrderRefundData_order;
defaultType?: OrderRefundType;
disabled: boolean;
errors: OrderErrorFragment[];
onBack: () => void;
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise;
}
const OrderRefundPage: React.FC<OrderRefundPageProps> = props => {
const {
order,
defaultType = OrderRefundType.PRODUCTS,
disabled,
errors = [],
onBack,
onSubmit
} = props;
const intl = useIntl();
const unfulfilledLines = order?.lines.filter(
line => line.quantity !== line.quantityFulfilled
);
const fulfilledFulfillemnts =
order?.fulfillments.filter(
fulfillment => fulfillment.status === FulfillmentStatus.FULFILLED
) || [];
return (
<OrderRefundForm
order={order}
defaultType={defaultType}
onSubmit={onSubmit}
>
{({ data, handlers, change, submit }) => (
<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} - Refund",
description: "page header"
},
{
orderNumber: order?.number
}
)}
/>
<Grid>
<div>
<OrderRefund data={data} disabled={disabled} onChange={change} />
{data.type === OrderRefundType.PRODUCTS && (
<>
{unfulfilledLines?.length > 0 && (
<>
<CardSpacer />
<OrderRefundUnfulfilledProducts
unfulfilledLines={unfulfilledLines}
data={data}
disabled={disabled}
onRefundedProductQuantityChange={
handlers.changeRefundedProductQuantity
}
onSetMaximalQuantities={
handlers.setMaximalRefundedProductQuantities
}
/>
</>
)}
{renderCollection(fulfilledFulfillemnts, fulfillment => (
<React.Fragment key={fulfillment?.id}>
<CardSpacer />
<OrderRefundFulfilledProducts
fulfillment={fulfillment}
data={data}
disabled={disabled}
orderNumber={order?.number}
onRefundedProductQuantityChange={
handlers.changeRefundedFulfilledProductQuantity
}
onSetMaximalQuantities={() =>
handlers.setMaximalRefundedFulfilledProductQuantities(
fulfillment?.id
)
}
/>
</React.Fragment>
))}
</>
)}
</div>
<div>
<OrderRefundAmount
data={data}
order={order}
disabled={disabled}
errors={errors}
onChange={change}
onRefund={submit}
/>
</div>
</Grid>
</Container>
)}
</OrderRefundForm>
);
};
OrderRefundPage.displayName = "OrderRefundPage";
export default OrderRefundPage;

View file

@ -0,0 +1,181 @@
/* eslint-disable sort-keys */
import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData";
import { FulfillmentStatus } from "@saleor/types/globalTypes";
export const orderToRefund = (placeholder: string): OrderRefundData_order => ({
__typename: "Order",
id: "ifgdfuhdfdf",
number: "22",
total: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 744.38,
currency: "USD"
}
},
totalCaptured: {
__typename: "Money",
amount: 644.38,
currency: "USD"
},
shippingPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 20,
currency: "USD"
}
},
lines: [
{
__typename: "OrderLine",
id: "diufhdsif",
productName: "Milk",
quantity: 19,
quantityFulfilled: 3,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 26.02,
currency: "USD"
}
}
},
{
__typename: "OrderLine",
id: "fdsfdfdsf",
productName: "Coffee",
quantity: 13,
quantityFulfilled: 5,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 10,
currency: "USD"
}
}
}
],
fulfillments: [
{
__typename: "Fulfillment",
id: "f12345qwertyu",
status: FulfillmentStatus.FULFILLED,
fulfillmentOrder: 1,
lines: [
{
__typename: "FulfillmentLine",
id: "fg3333fu66666",
quantity: 1,
orderLine: {
__typename: "OrderLine",
id: "diufhdsif",
productName: "Milk",
quantity: 1,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 26.02,
currency: "USD"
}
}
}
},
{
__typename: "FulfillmentLine",
id: "fgytrytytu88977",
quantity: 1,
orderLine: {
__typename: "OrderLine",
id: "fdsfdfdsf",
productName: "Coffee",
quantity: 1,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 10,
currency: "USD"
}
}
}
}
]
},
{
__typename: "Fulfillment",
id: "876543jhgfdfd",
status: FulfillmentStatus.FULFILLED,
fulfillmentOrder: 2,
lines: [
{
__typename: "FulfillmentLine",
id: "fgydgsyfu23456",
quantity: 2,
orderLine: {
__typename: "OrderLine",
id: "diufhdsif",
productName: "Milk",
quantity: 2,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 26.02,
currency: "USD"
}
}
}
},
{
__typename: "FulfillmentLine",
id: "fgydgsyfu555444",
quantity: 4,
orderLine: {
__typename: "OrderLine",
id: "fdsfdfdsf",
productName: "Coffee",
quantity: 4,
thumbnail: {
__typename: "Image",
url: placeholder
},
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 10,
currency: "USD"
}
}
}
}
]
}
]
});

View file

@ -0,0 +1,202 @@
import useForm, { FormChange, SubmitPromise } from "@saleor/hooks/useForm";
import useFormset, {
FormsetChange,
FormsetData
} from "@saleor/hooks/useFormset";
import { OrderRefundData_order } from "@saleor/orders/types/OrderRefundData";
import { FulfillmentStatus } from "@saleor/types/globalTypes";
import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
import React from "react";
export enum OrderRefundType {
MISCELLANEOUS = "miscellaneous",
PRODUCTS = "products"
}
export enum OrderRefundAmountCalculationMode {
AUTOMATIC = "automatic",
MANUAL = "manual"
}
export interface OrderRefundData {
amount: number | string;
type: OrderRefundType;
refundShipmentCosts: boolean;
amountCalculationMode: OrderRefundAmountCalculationMode;
}
export interface OrderRefundHandlers {
changeRefundedProductQuantity: FormsetChange<string>;
setMaximalRefundedProductQuantities: () => void;
changeRefundedFulfilledProductQuantity: FormsetChange<string>;
setMaximalRefundedFulfilledProductQuantities: (fulfillmentId: string) => void;
}
export interface OrderRefundFormData extends OrderRefundData {
refundedProductQuantities: FormsetData<null, string>;
refundedFulfilledProductQuantities: FormsetData<null, string>;
}
export type OrderRefundSubmitData = OrderRefundFormData;
export interface UseOrderRefundFormResult {
change: FormChange;
data: OrderRefundFormData;
disabled: boolean;
handlers: OrderRefundHandlers;
hasChanged: boolean;
submit: () => Promise<boolean>;
}
interface OrderRefundFormProps {
children: (props: UseOrderRefundFormResult) => React.ReactNode;
order: OrderRefundData_order;
defaultType: OrderRefundType;
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise;
}
function getOrderRefundPageFormData(
defaultType: OrderRefundType
): OrderRefundData {
return {
amount: undefined,
amountCalculationMode: OrderRefundAmountCalculationMode.AUTOMATIC,
refundShipmentCosts: false,
type: defaultType
};
}
function useOrderRefundForm(
order: OrderRefundData_order,
defaultType: OrderRefundType,
onSubmit: (data: OrderRefundSubmitData) => SubmitPromise
): UseOrderRefundFormResult {
const [changed, setChanged] = React.useState(false);
const triggerChange = () => setChanged(true);
const form = useForm(getOrderRefundPageFormData(defaultType));
const refundedProductQuantities = useFormset<null, string>(
order?.lines
.filter(line => line.quantityFulfilled !== line.quantity)
.map(line => ({
data: null,
id: line.id,
label: null,
value: "0"
}))
);
const refundedFulfilledProductQuantities = useFormset<null, string>(
order?.fulfillments
.filter(fulfillment => fulfillment.status === FulfillmentStatus.FULFILLED)
.reduce(
(linesQty, fulfillemnt) =>
linesQty.concat(
fulfillemnt.lines.map(fulfillmentLine => ({
data: null,
id: fulfillmentLine.id,
label: null,
value: "0"
}))
),
[]
)
);
const handleChange: FormChange = (event, cb) => {
form.change(event, cb);
triggerChange();
};
const handleRefundedProductQuantityChange: FormsetChange<string> = (
id,
value
) => {
triggerChange();
refundedProductQuantities.change(id, value);
};
const handleRefundedFulFilledProductQuantityChange = (
id: string,
value: string
) => {
triggerChange();
refundedFulfilledProductQuantities.change(id, value);
};
const handleMaximalRefundedProductQuantitiesSet = () => {
const newQuantities: FormsetData<
null,
string
> = refundedProductQuantities.data.map(selectedLine => {
const line = order.lines.find(line => line.id === selectedLine.id);
return {
data: null,
id: line.id,
label: null,
value: (line.quantity - line.quantityFulfilled).toString()
};
});
refundedProductQuantities.set(newQuantities);
triggerChange();
};
const handleMaximalRefundedFulfilledProductQuantitiesSet = (
fulfillmentId: string
) => {
const fulfillment = order.fulfillments.find(
fulfillment => fulfillment.id === fulfillmentId
);
const newQuantities: FormsetData<
null,
string
> = refundedFulfilledProductQuantities.data.map(selectedLine => {
const line = fulfillment.lines.find(line => line.id === selectedLine.id);
if (line) {
return {
data: null,
id: line.id,
label: null,
value: line.quantity.toString()
};
}
return selectedLine;
});
refundedFulfilledProductQuantities.set(newQuantities);
triggerChange();
};
const data: OrderRefundFormData = {
...form.data,
refundedFulfilledProductQuantities: refundedFulfilledProductQuantities.data,
refundedProductQuantities: refundedProductQuantities.data
};
const submit = () => handleFormSubmit(data, onSubmit, setChanged);
const disabled = !order;
return {
change: handleChange,
data,
disabled,
handlers: {
changeRefundedFulfilledProductQuantity: handleRefundedFulFilledProductQuantityChange,
changeRefundedProductQuantity: handleRefundedProductQuantityChange,
setMaximalRefundedFulfilledProductQuantities: handleMaximalRefundedFulfilledProductQuantitiesSet,
setMaximalRefundedProductQuantities: handleMaximalRefundedProductQuantitiesSet
},
hasChanged: changed,
submit
};
}
const OrderRefundForm: React.FC<OrderRefundFormProps> = ({
children,
order,
defaultType,
onSubmit
}) => {
const props = useOrderRefundForm(order, defaultType, onSubmit);
return <form onSubmit={props.submit}>{children(props)}</form>;
};
OrderRefundForm.displayName = "OrderRefundForm";
export default OrderRefundForm;

View file

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

View file

@ -0,0 +1,237 @@
import { makeStyles } from "@material-ui/core";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Table from "@material-ui/core/Table";
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 Typography from "@material-ui/core/Typography";
import { CSSProperties } from "@material-ui/styles";
import CardTitle from "@saleor/components/CardTitle";
import Money from "@saleor/components/Money";
import Skeleton from "@saleor/components/Skeleton";
import TableCellAvatar from "@saleor/components/TableCellAvatar";
import { FormsetChange } from "@saleor/hooks/useFormset";
import { renderCollection } from "@saleor/misc";
import { OrderRefundData_order_lines } from "@saleor/orders/types/OrderRefundData";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { OrderRefundFormData } from "../OrderRefundPage/form";
const useStyles = makeStyles(
theme => {
const inputPadding: CSSProperties = {
paddingBottom: theme.spacing(2),
paddingTop: theme.spacing(2)
};
return {
cartContent: {
paddingBottom: 0,
paddingTop: 0
},
colQuantity: {
textAlign: "right",
width: 210
},
notice: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(2)
},
quantityInnerInput: {
...inputPadding
},
quantityInnerInputNoRemaining: {
paddingRight: 0
},
remainingQuantity: {
...inputPadding,
color: theme.palette.text.secondary,
whiteSpace: "nowrap"
},
setMaximalQuantityButton: {
marginTop: theme.spacing(1)
}
};
},
{ name: "OrderRefundUnfulfilledProducts" }
);
interface OrderRefundUnfulfilledProductsProps {
unfulfilledLines: OrderRefundData_order_lines[];
data: OrderRefundFormData;
disabled: boolean;
onRefundedProductQuantityChange: FormsetChange<string>;
onSetMaximalQuantities: () => void;
}
const OrderRefundUnfulfilledProducts: React.FC<OrderRefundUnfulfilledProductsProps> = props => {
const {
unfulfilledLines,
data,
disabled,
onRefundedProductQuantityChange,
onSetMaximalQuantities
} = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Unfulfilled Products",
description: "section header"
})}
/>
<CardContent className={classes.cartContent}>
<Typography
variant="caption"
color="textSecondary"
className={classes.notice}
>
<FormattedMessage
defaultMessage="Unfulfilled products will be restocked"
description="section notice"
/>
</Typography>
<Button
className={classes.setMaximalQuantityButton}
color="primary"
onClick={onSetMaximalQuantities}
data-test="setMaximalQuantityUnfulfilledButton"
>
<FormattedMessage
defaultMessage="Set maximal quantities"
description="button"
/>
</Button>
</CardContent>
<Table>
<TableHead>
<TableRow>
<TableCell>
<FormattedMessage
defaultMessage="Product"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Price"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Refunded Qty"
description="tabel column header"
/>
</TableCell>
<TableCell>
<FormattedMessage
defaultMessage="Total"
description="tabel column header"
/>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{renderCollection(
unfulfilledLines,
line => {
const selectedLineQuantity = data.refundedProductQuantities.find(
refundedLine => refundedLine.id === line.id
);
const lineQuantity = line?.quantity - line?.quantityFulfilled;
const isError =
Number(selectedLineQuantity?.value) > lineQuantity ||
Number(selectedLineQuantity?.value) < 0;
return (
<TableRow key={line?.id}>
<TableCellAvatar thumbnail={line?.thumbnail?.url}>
{line?.productName ? line?.productName : <Skeleton />}
</TableCellAvatar>
<TableCell>
{line?.unitPrice ? (
<Money money={line?.unitPrice.gross} />
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colQuantity}>
{lineQuantity || lineQuantity === 0 ? (
<TextField
disabled={disabled}
type="number"
inputProps={{
className: classes.quantityInnerInput,
"data-test": "quantityInput",
"data-test-id": line?.id,
max: lineQuantity.toString(),
min: 0,
style: { textAlign: "right" }
}}
fullWidth
value={selectedLineQuantity?.value}
onChange={event =>
onRefundedProductQuantityChange(
line.id,
event.target.value
)
}
InputProps={{
endAdornment: lineQuantity && (
<div className={classes.remainingQuantity}>
/ {lineQuantity}
</div>
)
}}
error={isError}
helperText={
isError &&
intl.formatMessage({
defaultMessage: "Improper value",
description: "error message"
})
}
/>
) : (
<Skeleton />
)}
</TableCell>
<TableCell>
{(line?.unitPrice.gross && (
<Money
money={{
...line.unitPrice.gross,
amount:
(line.unitPrice.gross.amount ?? 0) *
Number(selectedLineQuantity?.value)
}}
/>
)) || <Skeleton />}
</TableCell>
</TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={4}>
<FormattedMessage defaultMessage="No products found" />
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
);
};
OrderRefundUnfulfilledProducts.displayName = "OrderRefundUnfulfilledProducts";
export default OrderRefundUnfulfilledProducts;

View file

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

View file

@ -17,7 +17,6 @@ import {
TypedOrderLinesAddMutation, TypedOrderLinesAddMutation,
TypedOrderLineUpdateMutation, TypedOrderLineUpdateMutation,
TypedOrderMarkAsPaidMutation, TypedOrderMarkAsPaidMutation,
TypedOrderRefundMutation,
TypedOrderShippingMethodUpdateMutation, TypedOrderShippingMethodUpdateMutation,
TypedOrderUpdateMutation, TypedOrderUpdateMutation,
TypedOrderVoidMutation TypedOrderVoidMutation
@ -66,7 +65,6 @@ import {
OrderMarkAsPaid, OrderMarkAsPaid,
OrderMarkAsPaidVariables OrderMarkAsPaidVariables
} from "../types/OrderMarkAsPaid"; } from "../types/OrderMarkAsPaid";
import { OrderRefund, OrderRefundVariables } from "../types/OrderRefund";
import { import {
OrderShippingMethodUpdate, OrderShippingMethodUpdate,
OrderShippingMethodUpdateVariables OrderShippingMethodUpdateVariables
@ -97,10 +95,6 @@ interface OrderOperationsProps {
OrderCapture, OrderCapture,
OrderCaptureVariables OrderCaptureVariables
>; >;
orderPaymentRefund: PartialMutationProviderOutput<
OrderRefund,
OrderRefundVariables
>;
orderPaymentMarkAsPaid: PartialMutationProviderOutput< orderPaymentMarkAsPaid: PartialMutationProviderOutput<
OrderMarkAsPaid, OrderMarkAsPaid,
OrderMarkAsPaidVariables OrderMarkAsPaidVariables
@ -154,7 +148,6 @@ interface OrderOperationsProps {
onOrderMarkAsPaid: (data: OrderMarkAsPaid) => void; onOrderMarkAsPaid: (data: OrderMarkAsPaid) => void;
onNoteAdd: (data: OrderAddNote) => void; onNoteAdd: (data: OrderAddNote) => void;
onPaymentCapture: (data: OrderCapture) => void; onPaymentCapture: (data: OrderCapture) => void;
onPaymentRefund: (data: OrderRefund) => void;
onUpdate: (data: OrderUpdate) => void; onUpdate: (data: OrderUpdate) => void;
onDraftCancel: (data: OrderDraftCancel) => void; onDraftCancel: (data: OrderDraftCancel) => void;
onDraftFinalize: (data: OrderDraftFinalize) => void; onDraftFinalize: (data: OrderDraftFinalize) => void;
@ -177,7 +170,6 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
onOrderLineUpdate, onOrderLineUpdate,
onOrderVoid, onOrderVoid,
onPaymentCapture, onPaymentCapture,
onPaymentRefund,
onShippingMethodUpdate, onShippingMethodUpdate,
onUpdate, onUpdate,
onDraftCancel, onDraftCancel,
@ -194,172 +186,159 @@ const OrderOperations: React.FC<OrderOperationsProps> = ({
{(...orderCancel) => ( {(...orderCancel) => (
<TypedOrderCaptureMutation onCompleted={onPaymentCapture}> <TypedOrderCaptureMutation onCompleted={onPaymentCapture}>
{(...paymentCapture) => ( {(...paymentCapture) => (
<TypedOrderRefundMutation onCompleted={onPaymentRefund}> <TypedOrderAddNoteMutation onCompleted={onNoteAdd}>
{(...paymentRefund) => ( {(...addNote) => (
<TypedOrderAddNoteMutation onCompleted={onNoteAdd}> <TypedOrderUpdateMutation onCompleted={onUpdate}>
{(...addNote) => ( {(...update) => (
<TypedOrderUpdateMutation onCompleted={onUpdate}> <TypedOrderDraftUpdateMutation
{(...update) => ( onCompleted={onDraftUpdate}
<TypedOrderDraftUpdateMutation >
onCompleted={onDraftUpdate} {(...updateDraft) => (
<TypedOrderShippingMethodUpdateMutation
onCompleted={onShippingMethodUpdate}
> >
{(...updateDraft) => ( {(...updateShippingMethod) => (
<TypedOrderShippingMethodUpdateMutation <TypedOrderLineDeleteMutation
onCompleted={onShippingMethodUpdate} onCompleted={onOrderLineDelete}
> >
{(...updateShippingMethod) => ( {(...deleteOrderLine) => (
<TypedOrderLineDeleteMutation <TypedOrderLinesAddMutation
onCompleted={onOrderLineDelete} onCompleted={onOrderLinesAdd}
> >
{(...deleteOrderLine) => ( {(...addOrderLine) => (
<TypedOrderLinesAddMutation <TypedOrderLineUpdateMutation
onCompleted={onOrderLinesAdd} onCompleted={onOrderLineUpdate}
> >
{(...addOrderLine) => ( {(...updateOrderLine) => (
<TypedOrderLineUpdateMutation <TypedOrderFulfillmentCancelMutation
onCompleted={onOrderLineUpdate} onCompleted={
onOrderFulfillmentCancel
}
> >
{(...updateOrderLine) => ( {(...cancelFulfillment) => (
<TypedOrderFulfillmentCancelMutation <TypedOrderFulfillmentUpdateTrackingMutation
onCompleted={ onCompleted={
onOrderFulfillmentCancel onOrderFulfillmentUpdate
} }
> >
{(...cancelFulfillment) => ( {(...updateTrackingNumber) => (
<TypedOrderFulfillmentUpdateTrackingMutation <TypedOrderDraftFinalizeMutation
onCompleted={ onCompleted={
onOrderFulfillmentUpdate onDraftFinalize
} }
> >
{( {(...finalizeDraft) => (
...updateTrackingNumber <TypedOrderDraftCancelMutation
) => (
<TypedOrderDraftFinalizeMutation
onCompleted={ onCompleted={
onDraftFinalize onDraftCancel
} }
> >
{(...finalizeDraft) => ( {(...cancelDraft) => (
<TypedOrderDraftCancelMutation <TypedOrderMarkAsPaidMutation
onCompleted={ onCompleted={
onDraftCancel onOrderMarkAsPaid
} }
> >
{( {(
...cancelDraft ...markAsPaid
) => ( ) => (
<TypedOrderMarkAsPaidMutation <TypedInvoiceRequestMutation
onCompleted={ onCompleted={
onOrderMarkAsPaid onInvoiceRequest
} }
> >
{( {(
...markAsPaid ...invoiceRequest
) => ( ) => (
<TypedInvoiceRequestMutation <TypedInvoiceEmailSendMutation
onCompleted={ onCompleted={
onInvoiceRequest onInvoiceSend
} }
> >
{( {(
...invoiceRequest ...invoiceEmailSend
) => ( ) =>
<TypedInvoiceEmailSendMutation children({
onCompleted={ orderAddNote: getMutationProviderData(
onInvoiceSend ...addNote
} ),
> orderCancel: getMutationProviderData(
{( ...orderCancel
),
orderDraftCancel: getMutationProviderData(
...cancelDraft
),
orderDraftFinalize: getMutationProviderData(
...finalizeDraft
),
orderDraftUpdate: getMutationProviderData(
...updateDraft
),
orderFulfillmentCancel: getMutationProviderData(
...cancelFulfillment
),
orderFulfillmentUpdateTracking: getMutationProviderData(
...updateTrackingNumber
),
orderInvoiceRequest: getMutationProviderData(
...invoiceRequest
),
orderInvoiceSend: getMutationProviderData(
...invoiceEmailSend ...invoiceEmailSend
) => ),
children( orderLineDelete: getMutationProviderData(
{ ...deleteOrderLine
orderAddNote: getMutationProviderData( ),
...addNote orderLineUpdate: getMutationProviderData(
), ...updateOrderLine
orderCancel: getMutationProviderData( ),
...orderCancel orderLinesAdd: getMutationProviderData(
), ...addOrderLine
orderDraftCancel: getMutationProviderData( ),
...cancelDraft orderPaymentCapture: getMutationProviderData(
), ...paymentCapture
orderDraftFinalize: getMutationProviderData( ),
...finalizeDraft orderPaymentMarkAsPaid: getMutationProviderData(
), ...markAsPaid
orderDraftUpdate: getMutationProviderData( ),
...updateDraft orderShippingMethodUpdate: getMutationProviderData(
), ...updateShippingMethod
orderFulfillmentCancel: getMutationProviderData( ),
...cancelFulfillment orderUpdate: getMutationProviderData(
), ...update
orderFulfillmentUpdateTracking: getMutationProviderData( ),
...updateTrackingNumber orderVoid: getMutationProviderData(
), ...orderVoid
orderInvoiceRequest: getMutationProviderData( )
...invoiceRequest })
), }
orderInvoiceSend: getMutationProviderData( </TypedInvoiceEmailSendMutation>
...invoiceEmailSend
),
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
)
}
)
}
</TypedInvoiceEmailSendMutation>
)}
</TypedInvoiceRequestMutation>
)} )}
</TypedOrderMarkAsPaidMutation> </TypedInvoiceRequestMutation>
)} )}
</TypedOrderDraftCancelMutation> </TypedOrderMarkAsPaidMutation>
)} )}
</TypedOrderDraftFinalizeMutation> </TypedOrderDraftCancelMutation>
)} )}
</TypedOrderFulfillmentUpdateTrackingMutation> </TypedOrderDraftFinalizeMutation>
)} )}
</TypedOrderFulfillmentCancelMutation> </TypedOrderFulfillmentUpdateTrackingMutation>
)} )}
</TypedOrderLineUpdateMutation> </TypedOrderFulfillmentCancelMutation>
)} )}
</TypedOrderLinesAddMutation> </TypedOrderLineUpdateMutation>
)} )}
</TypedOrderLineDeleteMutation> </TypedOrderLinesAddMutation>
)} )}
</TypedOrderShippingMethodUpdateMutation> </TypedOrderLineDeleteMutation>
)} )}
</TypedOrderDraftUpdateMutation> </TypedOrderShippingMethodUpdateMutation>
)} )}
</TypedOrderUpdateMutation> </TypedOrderDraftUpdateMutation>
)} )}
</TypedOrderAddNoteMutation> </TypedOrderUpdateMutation>
)} )}
</TypedOrderRefundMutation> </TypedOrderAddNoteMutation>
)} )}
</TypedOrderCaptureMutation> </TypedOrderCaptureMutation>
)} )}

View file

@ -831,8 +831,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: null, emailType: null,
id: "T3JkZXJFdmVudDoyMQ==", id: "T3JkZXJFdmVudDoyMQ==",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: null, message: null,
quantity: 1, quantity: 1,
shippingCostsIncluded: false,
transactionReference: "123", transactionReference: "123",
type: OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS, type: OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS,
user: { user: {
@ -841,6 +843,47 @@ export const order = (placeholder: string): OrderDetails_order => ({
id: "QWRkcmVzczoxNQ==" id: "QWRkcmVzczoxNQ=="
} }
}, },
{
__typename: "OrderEvent",
amount: null,
date: "2018-09-17T13:22:24.376193+00:00",
email: null,
emailType: null,
id: "UYgDNUnnfyiuyimuhd==",
invoiceNumber: null,
lines: [
{
__typename: "OrderEventOrderLineObject",
orderLine: {
__typename: "OrderLine",
id: "h47gfncfgwegfehfhj",
productName: "Milk",
variantName: "Cow's milk"
},
quantity: 4
},
{
__typename: "OrderEventOrderLineObject",
orderLine: {
__typename: "OrderLine",
id: "7846f857t4t84y8fgh",
productName: "Milk",
variantName: "Goat's milk"
},
quantity: 4
}
],
message: null,
quantity: 1,
shippingCostsIncluded: true,
transactionReference: "123",
type: OrderEventsEnum.FULFILLMENT_REFUNDED,
user: {
__typename: "User",
email: "admin@example.com",
id: "QWRkcmVzczoxNQ=="
}
},
{ {
__typename: "OrderEvent", __typename: "OrderEvent",
amount: null, amount: null,
@ -849,8 +892,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: null, emailType: null,
id: "T3JkZXJFdmVudDo0", id: "T3JkZXJFdmVudDo0",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: "This is note", message: "This is note",
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "124", transactionReference: "124",
type: OrderEventsEnum.NOTE_ADDED, type: OrderEventsEnum.NOTE_ADDED,
user: null user: null
@ -863,8 +908,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: null, emailType: null,
id: "T3JkZXJFdmVudDo1", id: "T3JkZXJFdmVudDo1",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: "This is note", message: "This is note",
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "125", transactionReference: "125",
type: OrderEventsEnum.NOTE_ADDED, type: OrderEventsEnum.NOTE_ADDED,
user: null user: null
@ -877,8 +924,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: null, emailType: null,
id: "T3JkZXJFdmVudDo2", id: "T3JkZXJFdmVudDo2",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: "Note from external service", message: "Note from external service",
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "126", transactionReference: "126",
type: OrderEventsEnum.EXTERNAL_SERVICE_NOTIFICATION, type: OrderEventsEnum.EXTERNAL_SERVICE_NOTIFICATION,
user: null user: null
@ -891,8 +940,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: OrderEventsEmailsEnum.ORDER_CANCEL, emailType: OrderEventsEmailsEnum.ORDER_CANCEL,
id: "T3JkZXJFdmVudDo3", id: "T3JkZXJFdmVudDo3",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: null, message: null,
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "127", transactionReference: "127",
type: OrderEventsEnum.EMAIL_SENT, type: OrderEventsEnum.EMAIL_SENT,
user: null user: null
@ -905,8 +956,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: OrderEventsEmailsEnum.ORDER_REFUND, emailType: OrderEventsEmailsEnum.ORDER_REFUND,
id: "T3JkZXJFdmVudDo4", id: "T3JkZXJFdmVudDo4",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: null, message: null,
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "128", transactionReference: "128",
type: OrderEventsEnum.EMAIL_SENT, type: OrderEventsEnum.EMAIL_SENT,
user: null user: null
@ -919,8 +972,10 @@ export const order = (placeholder: string): OrderDetails_order => ({
emailType: null, emailType: null,
id: "T3JkZXJFdmVudDo5", id: "T3JkZXJFdmVudDo5",
invoiceNumber: null, invoiceNumber: null,
lines: [],
message: null, message: null,
quantity: null, quantity: null,
shippingCostsIncluded: false,
transactionReference: "129", transactionReference: "129",
type: OrderEventsEnum.PAYMENT_AUTHORIZED, type: OrderEventsEnum.PAYMENT_AUTHORIZED,
user: null user: null

View file

@ -15,6 +15,7 @@ import {
OrderListUrlQueryParams, OrderListUrlQueryParams,
OrderListUrlSortField, OrderListUrlSortField,
orderPath, orderPath,
orderRefundPath,
orderSettingsPath, orderSettingsPath,
OrderUrlQueryParams OrderUrlQueryParams
} from "./urls"; } from "./urls";
@ -22,6 +23,7 @@ import OrderDetailsComponent from "./views/OrderDetails";
import OrderDraftListComponent from "./views/OrderDraftList"; import OrderDraftListComponent from "./views/OrderDraftList";
import OrderFulfillComponent from "./views/OrderFulfill"; import OrderFulfillComponent from "./views/OrderFulfill";
import OrderListComponent from "./views/OrderList"; import OrderListComponent from "./views/OrderList";
import OrderRefundComponent from "./views/OrderRefund";
import OrderSettings from "./views/OrderSettings"; import OrderSettings from "./views/OrderSettings";
const OrderList: React.FC<RouteComponentProps<any>> = ({ location }) => { const OrderList: React.FC<RouteComponentProps<any>> = ({ location }) => {
@ -65,6 +67,10 @@ const OrderFulfill: React.FC<RouteComponentProps<any>> = ({ match }) => (
<OrderFulfillComponent orderId={decodeURIComponent(match.params.id)} /> <OrderFulfillComponent orderId={decodeURIComponent(match.params.id)} />
); );
const OrderRefund: React.FC<RouteComponentProps<any>> = ({ match }) => (
<OrderRefundComponent orderId={decodeURIComponent(match.params.id)} />
);
const Component = () => { const Component = () => {
const intl = useIntl(); const intl = useIntl();
@ -76,6 +82,7 @@ const Component = () => {
<Route exact path={orderDraftListPath} component={OrderDraftList} /> <Route exact path={orderDraftListPath} component={OrderDraftList} />
<Route exact path={orderListPath} component={OrderList} /> <Route exact path={orderListPath} component={OrderList} />
<Route path={orderFulfillPath(":id")} component={OrderFulfill} /> <Route path={orderFulfillPath(":id")} component={OrderFulfill} />
<Route path={orderRefundPath(":id")} component={OrderRefund} />
<Route path={orderPath(":id")} component={OrderDetails} /> <Route path={orderPath(":id")} component={OrderDetails} />
</Switch> </Switch>
</> </>

View file

@ -7,6 +7,7 @@ import {
fragmentOrderDetails, fragmentOrderDetails,
fragmentOrderEvent, fragmentOrderEvent,
fragmentOrderSettings, fragmentOrderSettings,
fulfillmentFragment,
invoiceFragment invoiceFragment
} from "@saleor/fragments/orders"; } from "@saleor/fragments/orders";
import makeMutation from "@saleor/hooks/makeMutation"; import makeMutation from "@saleor/hooks/makeMutation";
@ -50,6 +51,10 @@ import {
OrderFulfillmentCancel, OrderFulfillmentCancel,
OrderFulfillmentCancelVariables OrderFulfillmentCancelVariables
} from "./types/OrderFulfillmentCancel"; } from "./types/OrderFulfillmentCancel";
import {
OrderFulfillmentRefundProducts,
OrderFulfillmentRefundProductsVariables
} from "./types/OrderFulfillmentRefundProducts";
import { import {
OrderFulfillmentUpdateTracking, OrderFulfillmentUpdateTracking,
OrderFulfillmentUpdateTrackingVariables OrderFulfillmentUpdateTrackingVariables
@ -186,11 +191,37 @@ const orderRefundMutation = gql`
} }
} }
`; `;
export const TypedOrderRefundMutation = TypedMutation< export const useOrderRefundMutation = makeMutation<
OrderRefund, OrderRefund,
OrderRefundVariables OrderRefundVariables
>(orderRefundMutation); >(orderRefundMutation);
const orderFulfillmentRefundProductsMutation = gql`
${fragmentOrderDetails}
${fulfillmentFragment}
${orderErrorFragment}
mutation OrderFulfillmentRefundProducts(
$input: OrderRefundProductsInput!
$order: ID!
) {
orderFulfillmentRefundProducts(input: $input, order: $order) {
errors: orderErrors {
...OrderErrorFragment
}
fulfillment {
...FulfillmentFragment
}
order {
...OrderDetailsFragment
}
}
}
`;
export const useOrderFulfillmentRefundProductsMutation = makeMutation<
OrderFulfillmentRefundProducts,
OrderFulfillmentRefundProductsVariables
>(orderFulfillmentRefundProductsMutation);
const orderVoidMutation = gql` const orderVoidMutation = gql`
${fragmentOrderDetails} ${fragmentOrderDetails}
${orderErrorFragment} ${orderErrorFragment}

View file

@ -1,8 +1,10 @@
import { fragmentAddress } from "@saleor/fragments/address"; import { fragmentAddress } from "@saleor/fragments/address";
import { import {
fragmentOrderDetails, fragmentOrderDetails,
fragmentOrderSettings fragmentOrderSettings,
fragmentRefundOrderLine
} from "@saleor/fragments/orders"; } from "@saleor/fragments/orders";
import { fragmentMoney } from "@saleor/fragments/products";
import makeQuery from "@saleor/hooks/makeQuery"; import makeQuery from "@saleor/hooks/makeQuery";
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch"; import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
import gql from "graphql-tag"; import gql from "graphql-tag";
@ -18,6 +20,10 @@ import {
OrderFulfillDataVariables OrderFulfillDataVariables
} from "./types/OrderFulfillData"; } from "./types/OrderFulfillData";
import { OrderList, OrderListVariables } from "./types/OrderList"; import { OrderList, OrderListVariables } from "./types/OrderList";
import {
OrderRefundData,
OrderRefundDataVariables
} from "./types/OrderRefundData";
import { OrderSettings } from "./types/OrderSettings"; import { OrderSettings } from "./types/OrderSettings";
import { import {
SearchOrderVariant as SearchOrderVariantType, SearchOrderVariant as SearchOrderVariantType,
@ -254,3 +260,47 @@ export const orderSettingsQuery = gql`
export const useOrderSettingsQuery = makeQuery<OrderSettings, never>( export const useOrderSettingsQuery = makeQuery<OrderSettings, never>(
orderSettingsQuery orderSettingsQuery
); );
const orderRefundData = gql`
${fragmentMoney}
${fragmentRefundOrderLine}
query OrderRefundData($orderId: ID!) {
order(id: $orderId) {
id
number
total {
gross {
...Money
}
}
totalCaptured {
...Money
}
shippingPrice {
gross {
...Money
}
}
lines {
...RefundOrderLineFragment
quantityFulfilled
}
fulfillments {
id
status
fulfillmentOrder
lines {
id
quantity
orderLine {
...RefundOrderLineFragment
}
}
}
}
}
`;
export const useOrderRefundData = makeQuery<
OrderRefundData,
OrderRefundDataVariables
>(orderRefundData);

View file

@ -56,10 +56,24 @@ export interface FulfillOrder_orderFulfill_order_events_user {
email: string; email: string;
} }
export interface FulfillOrder_orderFulfill_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface FulfillOrder_orderFulfill_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: FulfillOrder_orderFulfill_order_events_lines_orderLine | null;
}
export interface FulfillOrder_orderFulfill_order_events { export interface FulfillOrder_orderFulfill_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -69,6 +83,7 @@ export interface FulfillOrder_orderFulfill_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: FulfillOrder_orderFulfill_order_events_user | null; user: FulfillOrder_orderFulfill_order_events_user | null;
lines: (FulfillOrder_orderFulfill_order_events_lines | null)[] | null;
} }
export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_variant { export interface FulfillOrder_orderFulfill_order_fulfillments_lines_orderLine_variant {

View file

@ -20,10 +20,24 @@ export interface OrderAddNote_orderAddNote_order_events_user {
email: string; email: string;
} }
export interface OrderAddNote_orderAddNote_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderAddNote_orderAddNote_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderAddNote_orderAddNote_order_events_lines_orderLine | null;
}
export interface OrderAddNote_orderAddNote_order_events { export interface OrderAddNote_orderAddNote_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -33,6 +47,7 @@ export interface OrderAddNote_orderAddNote_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderAddNote_orderAddNote_order_events_user | null; user: OrderAddNote_orderAddNote_order_events_user | null;
lines: (OrderAddNote_orderAddNote_order_events_lines | null)[] | null;
} }
export interface OrderAddNote_orderAddNote_order { export interface OrderAddNote_orderAddNote_order {

View file

@ -54,10 +54,24 @@ export interface OrderCancel_orderCancel_order_events_user {
email: string; email: string;
} }
export interface OrderCancel_orderCancel_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderCancel_orderCancel_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderCancel_orderCancel_order_events_lines_orderLine | null;
}
export interface OrderCancel_orderCancel_order_events { export interface OrderCancel_orderCancel_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderCancel_orderCancel_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderCancel_orderCancel_order_events_user | null; user: OrderCancel_orderCancel_order_events_user | null;
lines: (OrderCancel_orderCancel_order_events_lines | null)[] | null;
} }
export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine_variant { export interface OrderCancel_orderCancel_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderCapture_orderCapture_order_events_user {
email: string; email: string;
} }
export interface OrderCapture_orderCapture_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderCapture_orderCapture_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderCapture_orderCapture_order_events_lines_orderLine | null;
}
export interface OrderCapture_orderCapture_order_events { export interface OrderCapture_orderCapture_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderCapture_orderCapture_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderCapture_orderCapture_order_events_user | null; user: OrderCapture_orderCapture_order_events_user | null;
lines: (OrderCapture_orderCapture_order_events_lines | null)[] | null;
} }
export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine_variant { export interface OrderCapture_orderCapture_order_fulfillments_lines_orderLine_variant {

View file

@ -54,18 +54,34 @@ export interface OrderConfirm_orderConfirm_order_events_user {
email: string; email: string;
} }
export interface OrderConfirm_orderConfirm_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderConfirm_orderConfirm_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderConfirm_orderConfirm_order_events_lines_orderLine | null;
}
export interface OrderConfirm_orderConfirm_order_events { export interface OrderConfirm_orderConfirm_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
invoiceNumber: string | null; invoiceNumber: string | null;
message: string | null; message: string | null;
quantity: number | null; quantity: number | null;
transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderConfirm_orderConfirm_order_events_user | null; user: OrderConfirm_orderConfirm_order_events_user | null;
lines: (OrderConfirm_orderConfirm_order_events_lines | null)[] | null;
} }
export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant { export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant {
@ -326,6 +342,7 @@ export interface OrderConfirm_orderConfirm_order {
discount: OrderConfirm_orderConfirm_order_discount | null; discount: OrderConfirm_orderConfirm_order_discount | null;
invoices: (OrderConfirm_orderConfirm_order_invoices | null)[] | null; invoices: (OrderConfirm_orderConfirm_order_invoices | null)[] | null;
channel: OrderConfirm_orderConfirm_order_channel; channel: OrderConfirm_orderConfirm_order_channel;
isPaid: boolean | null;
} }
export interface OrderConfirm_orderConfirm { export interface OrderConfirm_orderConfirm {

View file

@ -48,10 +48,24 @@ export interface OrderDetails_order_events_user {
email: string; email: string;
} }
export interface OrderDetails_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderDetails_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderDetails_order_events_lines_orderLine | null;
}
export interface OrderDetails_order_events { export interface OrderDetails_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -61,6 +75,7 @@ export interface OrderDetails_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderDetails_order_events_user | null; user: OrderDetails_order_events_user | null;
lines: (OrderDetails_order_events_lines | null)[] | null;
} }
export interface OrderDetails_order_fulfillments_lines_orderLine_variant { export interface OrderDetails_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderDraftCancel_draftOrderDelete_order_events_user {
email: string; email: string;
} }
export interface OrderDraftCancel_draftOrderDelete_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderDraftCancel_draftOrderDelete_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderDraftCancel_draftOrderDelete_order_events_lines_orderLine | null;
}
export interface OrderDraftCancel_draftOrderDelete_order_events { export interface OrderDraftCancel_draftOrderDelete_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderDraftCancel_draftOrderDelete_order_events_user | null; user: OrderDraftCancel_draftOrderDelete_order_events_user | null;
lines: (OrderDraftCancel_draftOrderDelete_order_events_lines | null)[] | null;
} }
export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_variant { export interface OrderDraftCancel_draftOrderDelete_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events_user {
email: string; email: string;
} }
export interface OrderDraftFinalize_draftOrderComplete_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderDraftFinalize_draftOrderComplete_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderDraftFinalize_draftOrderComplete_order_events_lines_orderLine | null;
}
export interface OrderDraftFinalize_draftOrderComplete_order_events { export interface OrderDraftFinalize_draftOrderComplete_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderDraftFinalize_draftOrderComplete_order_events_user | null; user: OrderDraftFinalize_draftOrderComplete_order_events_user | null;
lines: (OrderDraftFinalize_draftOrderComplete_order_events_lines | null)[] | null;
} }
export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_variant { export interface OrderDraftFinalize_draftOrderComplete_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events_user {
email: string; email: string;
} }
export interface OrderDraftUpdate_draftOrderUpdate_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderDraftUpdate_draftOrderUpdate_order_events_lines_orderLine | null;
}
export interface OrderDraftUpdate_draftOrderUpdate_order_events { export interface OrderDraftUpdate_draftOrderUpdate_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderDraftUpdate_draftOrderUpdate_order_events_user | null; user: OrderDraftUpdate_draftOrderUpdate_order_events_user | null;
lines: (OrderDraftUpdate_draftOrderUpdate_order_events_lines | null)[] | null;
} }
export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_variant { export interface OrderDraftUpdate_draftOrderUpdate_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_user
email: string; email: string;
} }
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines_orderLine | null;
}
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events { export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderFulfillmentCancel_orderFulfillmentCancel_order_events_user | null; user: OrderFulfillmentCancel_orderFulfillmentCancel_order_events_user | null;
lines: (OrderFulfillmentCancel_orderFulfillmentCancel_order_events_lines | null)[] | null;
} }
export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_variant { export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_fulfillments_lines_orderLine_variant {

View file

@ -0,0 +1,427 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { OrderRefundProductsInput, OrderErrorCode, FulfillmentStatus, OrderEventsEmailsEnum, OrderEventsEnum, PaymentChargeStatusEnum, OrderStatus, OrderAction, JobStatusEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: OrderFulfillmentRefundProducts
// ====================================================
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_errors {
__typename: "OrderError";
code: OrderErrorCode;
field: string | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice_gross;
net: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice_net;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
variant: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_variant | null;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_unitPrice | null;
thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine_thumbnail | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines_orderLine | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment {
__typename: "Fulfillment";
id: string;
lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_lines | null)[] | null;
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment_warehouse | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_metadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_privateMetadata {
__typename: "MetadataItem";
key: string;
value: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_billingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_billingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_billingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_user {
__typename: "User";
id: string;
email: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines_orderLine | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events {
__typename: "OrderEvent";
id: string;
amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null;
email: string | null;
emailType: OrderEventsEmailsEnum | null;
invoiceNumber: string | null;
message: string | null;
quantity: number | null;
transactionReference: string | null;
type: OrderEventsEnum | null;
user: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_user | null;
lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events_lines | null)[] | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice_gross;
net: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice_net;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
variant: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_variant | null;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_unitPrice | null;
thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine_thumbnail | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines_orderLine | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments {
__typename: "Fulfillment";
id: string;
lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_lines | null)[] | null;
fulfillmentOrder: number;
status: FulfillmentStatus;
trackingNumber: string;
warehouse: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments_warehouse | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_variant {
__typename: "ProductVariant";
id: string;
quantityAvailable: number;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice_net {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice_gross;
net: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice_net;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines {
__typename: "OrderLine";
id: string;
isShippingRequired: boolean;
variant: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_variant | null;
productName: string;
productSku: string;
quantity: number;
quantityFulfilled: number;
unitPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_unitPrice | null;
thumbnail: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines_thumbnail | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingAddress_country {
__typename: "CountryDisplay";
code: string;
country: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingAddress {
__typename: "Address";
city: string;
cityArea: string;
companyName: string;
country: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingAddress_country;
countryArea: string;
firstName: string;
id: string;
lastName: string;
phone: string | null;
postalCode: string;
streetAddress1: string;
streetAddress2: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingMethod {
__typename: "ShippingMethod";
id: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice_gross;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal_gross;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total_tax {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total {
__typename: "TaxedMoney";
gross: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total_gross;
tax: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total_tax;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalAuthorized {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalCaptured {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_user {
__typename: "User";
id: string;
email: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_availableShippingMethods_price {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_availableShippingMethods {
__typename: "ShippingMethod";
id: string;
name: string;
price: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_availableShippingMethods_price | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_discount {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_invoices {
__typename: "Invoice";
id: string;
number: string | null;
createdAt: any;
url: string | null;
status: JobStatusEnum;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_channel {
__typename: "Channel";
isActive: boolean;
id: string;
name: string;
currencyCode: string;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order {
__typename: "Order";
id: string;
metadata: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_metadata | null)[];
privateMetadata: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_privateMetadata | null)[];
billingAddress: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_billingAddress | null;
canFinalize: boolean;
created: any;
customerNote: string;
events: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_events | null)[] | null;
fulfillments: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_fulfillments | null)[];
lines: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_lines | null)[];
number: string | null;
paymentStatus: PaymentChargeStatusEnum | null;
shippingAddress: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingAddress | null;
shippingMethod: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingMethod | null;
shippingMethodName: string | null;
shippingPrice: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_shippingPrice | null;
status: OrderStatus;
subtotal: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_subtotal | null;
total: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_total | null;
actions: (OrderAction | null)[];
totalAuthorized: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalAuthorized | null;
totalCaptured: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_totalCaptured | null;
user: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_user | null;
userEmail: string | null;
availableShippingMethods: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_availableShippingMethods | null)[] | null;
discount: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_discount | null;
invoices: (OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_invoices | null)[] | null;
channel: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order_channel;
isPaid: boolean | null;
}
export interface OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts {
__typename: "FulfillmentRefundProducts";
errors: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_errors[];
fulfillment: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_fulfillment | null;
order: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts_order | null;
}
export interface OrderFulfillmentRefundProducts {
orderFulfillmentRefundProducts: OrderFulfillmentRefundProducts_orderFulfillmentRefundProducts | null;
}
export interface OrderFulfillmentRefundProductsVariables {
input: OrderRefundProductsInput;
order: string;
}

View file

@ -54,10 +54,24 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
email: string; email: string;
} }
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines_orderLine | null;
}
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events { export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_user | null; user: OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_user | null;
lines: (OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_events_lines | null)[] | null;
} }
export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_variant { export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderLineDelete_draftOrderLineDelete_order_events_user {
email: string; email: string;
} }
export interface OrderLineDelete_draftOrderLineDelete_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderLineDelete_draftOrderLineDelete_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderLineDelete_draftOrderLineDelete_order_events_lines_orderLine | null;
}
export interface OrderLineDelete_draftOrderLineDelete_order_events { export interface OrderLineDelete_draftOrderLineDelete_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderLineDelete_draftOrderLineDelete_order_events_user | null; user: OrderLineDelete_draftOrderLineDelete_order_events_user | null;
lines: (OrderLineDelete_draftOrderLineDelete_order_events_lines | null)[] | null;
} }
export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine_variant { export interface OrderLineDelete_draftOrderLineDelete_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_events_user {
email: string; email: string;
} }
export interface OrderLineUpdate_draftOrderLineUpdate_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderLineUpdate_draftOrderLineUpdate_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderLineUpdate_draftOrderLineUpdate_order_events_lines_orderLine | null;
}
export interface OrderLineUpdate_draftOrderLineUpdate_order_events { export interface OrderLineUpdate_draftOrderLineUpdate_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderLineUpdate_draftOrderLineUpdate_order_events_user | null; user: OrderLineUpdate_draftOrderLineUpdate_order_events_user | null;
lines: (OrderLineUpdate_draftOrderLineUpdate_order_events_lines | null)[] | null;
} }
export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine_variant { export interface OrderLineUpdate_draftOrderLineUpdate_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_events_user {
email: string; email: string;
} }
export interface OrderLinesAdd_draftOrderLinesCreate_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderLinesAdd_draftOrderLinesCreate_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderLinesAdd_draftOrderLinesCreate_order_events_lines_orderLine | null;
}
export interface OrderLinesAdd_draftOrderLinesCreate_order_events { export interface OrderLinesAdd_draftOrderLinesCreate_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderLinesAdd_draftOrderLinesCreate_order_events_user | null; user: OrderLinesAdd_draftOrderLinesCreate_order_events_user | null;
lines: (OrderLinesAdd_draftOrderLinesCreate_order_events_lines | null)[] | null;
} }
export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine_variant { export interface OrderLinesAdd_draftOrderLinesCreate_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_user {
email: string; email: string;
} }
export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderMarkAsPaid_orderMarkAsPaid_order_events_lines_orderLine | null;
}
export interface OrderMarkAsPaid_orderMarkAsPaid_order_events { export interface OrderMarkAsPaid_orderMarkAsPaid_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderMarkAsPaid_orderMarkAsPaid_order_events_user | null; user: OrderMarkAsPaid_orderMarkAsPaid_order_events_user | null;
lines: (OrderMarkAsPaid_orderMarkAsPaid_order_events_lines | null)[] | null;
} }
export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_variant { export interface OrderMarkAsPaid_orderMarkAsPaid_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderRefund_orderRefund_order_events_user {
email: string; email: string;
} }
export interface OrderRefund_orderRefund_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderRefund_orderRefund_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderRefund_orderRefund_order_events_lines_orderLine | null;
}
export interface OrderRefund_orderRefund_order_events { export interface OrderRefund_orderRefund_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderRefund_orderRefund_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderRefund_orderRefund_order_events_user | null; user: OrderRefund_orderRefund_order_events_user | null;
lines: (OrderRefund_orderRefund_order_events_lines | null)[] | null;
} }
export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine_variant { export interface OrderRefund_orderRefund_order_fulfillments_lines_orderLine_variant {

View file

@ -0,0 +1,122 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { FulfillmentStatus } from "./../../types/globalTypes";
// ====================================================
// GraphQL query operation: OrderRefundData
// ====================================================
export interface OrderRefundData_order_total_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderRefundData_order_total {
__typename: "TaxedMoney";
gross: OrderRefundData_order_total_gross;
}
export interface OrderRefundData_order_totalCaptured {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderRefundData_order_shippingPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderRefundData_order_shippingPrice {
__typename: "TaxedMoney";
gross: OrderRefundData_order_shippingPrice_gross;
}
export interface OrderRefundData_order_lines_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderRefundData_order_lines_unitPrice {
__typename: "TaxedMoney";
gross: OrderRefundData_order_lines_unitPrice_gross;
}
export interface OrderRefundData_order_lines_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderRefundData_order_lines {
__typename: "OrderLine";
id: string;
productName: string;
quantity: number;
unitPrice: OrderRefundData_order_lines_unitPrice | null;
thumbnail: OrderRefundData_order_lines_thumbnail | null;
quantityFulfilled: number;
}
export interface OrderRefundData_order_fulfillments_lines_orderLine_unitPrice_gross {
__typename: "Money";
amount: number;
currency: string;
}
export interface OrderRefundData_order_fulfillments_lines_orderLine_unitPrice {
__typename: "TaxedMoney";
gross: OrderRefundData_order_fulfillments_lines_orderLine_unitPrice_gross;
}
export interface OrderRefundData_order_fulfillments_lines_orderLine_thumbnail {
__typename: "Image";
url: string;
}
export interface OrderRefundData_order_fulfillments_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
quantity: number;
unitPrice: OrderRefundData_order_fulfillments_lines_orderLine_unitPrice | null;
thumbnail: OrderRefundData_order_fulfillments_lines_orderLine_thumbnail | null;
}
export interface OrderRefundData_order_fulfillments_lines {
__typename: "FulfillmentLine";
id: string;
quantity: number;
orderLine: OrderRefundData_order_fulfillments_lines_orderLine | null;
}
export interface OrderRefundData_order_fulfillments {
__typename: "Fulfillment";
id: string;
status: FulfillmentStatus;
fulfillmentOrder: number;
lines: (OrderRefundData_order_fulfillments_lines | null)[] | null;
}
export interface OrderRefundData_order {
__typename: "Order";
id: string;
number: string | null;
total: OrderRefundData_order_total | null;
totalCaptured: OrderRefundData_order_totalCaptured | null;
shippingPrice: OrderRefundData_order_shippingPrice | null;
lines: (OrderRefundData_order_lines | null)[];
fulfillments: (OrderRefundData_order_fulfillments | null)[];
}
export interface OrderRefundData {
order: OrderRefundData_order | null;
}
export interface OrderRefundDataVariables {
orderId: string;
}

View file

@ -54,10 +54,24 @@ export interface OrderUpdate_orderUpdate_order_events_user {
email: string; email: string;
} }
export interface OrderUpdate_orderUpdate_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderUpdate_orderUpdate_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderUpdate_orderUpdate_order_events_lines_orderLine | null;
}
export interface OrderUpdate_orderUpdate_order_events { export interface OrderUpdate_orderUpdate_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderUpdate_orderUpdate_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderUpdate_orderUpdate_order_events_user | null; user: OrderUpdate_orderUpdate_order_events_user | null;
lines: (OrderUpdate_orderUpdate_order_events_lines | null)[] | null;
} }
export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_variant { export interface OrderUpdate_orderUpdate_order_fulfillments_lines_orderLine_variant {

View file

@ -54,10 +54,24 @@ export interface OrderVoid_orderVoid_order_events_user {
email: string; email: string;
} }
export interface OrderVoid_orderVoid_order_events_lines_orderLine {
__typename: "OrderLine";
id: string;
productName: string;
variantName: string;
}
export interface OrderVoid_orderVoid_order_events_lines {
__typename: "OrderEventOrderLineObject";
quantity: number | null;
orderLine: OrderVoid_orderVoid_order_events_lines_orderLine | null;
}
export interface OrderVoid_orderVoid_order_events { export interface OrderVoid_orderVoid_order_events {
__typename: "OrderEvent"; __typename: "OrderEvent";
id: string; id: string;
amount: number | null; amount: number | null;
shippingCostsIncluded: boolean | null;
date: any | null; date: any | null;
email: string | null; email: string | null;
emailType: OrderEventsEmailsEnum | null; emailType: OrderEventsEmailsEnum | null;
@ -67,6 +81,7 @@ export interface OrderVoid_orderVoid_order_events {
transactionReference: string | null; transactionReference: string | null;
type: OrderEventsEnum | null; type: OrderEventsEnum | null;
user: OrderVoid_orderVoid_order_events_user | null; user: OrderVoid_orderVoid_order_events_user | null;
lines: (OrderVoid_orderVoid_order_events_lines | null)[] | null;
} }
export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine_variant { export interface OrderVoid_orderVoid_order_fulfillments_lines_orderLine_variant {

View file

@ -103,7 +103,6 @@ export type OrderUrlDialog =
| "edit-shipping-address" | "edit-shipping-address"
| "finalize" | "finalize"
| "mark-paid" | "mark-paid"
| "refund"
| "void" | "void"
| "invoice-send"; | "invoice-send";
export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction; export type OrderUrlQueryParams = Dialog<OrderUrlDialog> & SingleAction;
@ -116,3 +115,7 @@ export const orderFulfillUrl = (id: string) =>
orderFulfillPath(encodeURIComponent(id)); orderFulfillPath(encodeURIComponent(id));
export const orderSettingsPath = urlJoin(orderSectionUrl, "settings"); export const orderSettingsPath = urlJoin(orderSectionUrl, "settings");
export const orderRefundPath = (id: string) => urlJoin(orderPath(id), "refund");
export const orderRefundUrl = (id: string) =>
orderRefundPath(encodeURIComponent(id));

View file

@ -0,0 +1,525 @@
/* eslint-disable sort-keys */
import { FormsetData } from "@saleor/hooks/useFormset";
import { FulfillmentStatus } from "@saleor/types/globalTypes";
import { OrderDetails_order_fulfillments_lines } from "../types/OrderDetails";
import {
OrderRefundData_order_fulfillments,
OrderRefundData_order_lines
} from "../types/OrderRefundData";
import {
getAllFulfillmentLinesPriceSum,
getPreviouslyRefundedPrice,
getRefundedLinesPriceSum,
mergeRepeatedOrderLines,
OrderWithTotalAndTotalCaptured
} from "./data";
describe("Get previously refunded price", () => {
it("is able to calculate refunded price from order", () => {
const order: OrderWithTotalAndTotalCaptured = {
total: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 160,
currency: "USD"
}
},
totalCaptured: {
__typename: "Money",
amount: 100,
currency: "USD"
}
};
const refundedPrice = getPreviouslyRefundedPrice(order);
expect(refundedPrice.amount).toBe(-60);
});
});
describe("Get refunded lines price sum", () => {
const lines: OrderRefundData_order_lines[] = [
{
__typename: "OrderLine",
id: "1",
productName: "Milk 1",
quantity: 1,
quantityFulfilled: 1,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 10,
currency: "USD"
}
}
},
{
__typename: "OrderLine",
id: "2",
productName: "Milk 2",
quantity: 2,
quantityFulfilled: 2,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 6,
currency: "USD"
}
}
},
{
__typename: "OrderLine",
id: "3",
productName: "Milk 3",
quantity: 4,
quantityFulfilled: 4,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 4,
currency: "USD"
}
}
}
];
it("is able to sum lines prices", () => {
const refundedProductQuantities: FormsetData<null, string> = [
{
data: null,
id: "1",
label: null,
value: "1"
},
{
data: null,
id: "2",
label: null,
value: "1"
},
{
data: null,
id: "3",
label: null,
value: "1"
}
];
const refundedLinesPriceSum = getRefundedLinesPriceSum(
lines,
refundedProductQuantities
);
expect(refundedLinesPriceSum).toBe(20);
});
it("is able to sum lines prices multiplied by different quantities", () => {
const refundedProductQuantities: FormsetData<null, string> = [
{
data: null,
id: "1",
label: null,
value: "0"
},
{
data: null,
id: "2",
label: null,
value: "2"
},
{
data: null,
id: "3",
label: null,
value: "4"
}
];
const refundedLinesPriceSum = getRefundedLinesPriceSum(
lines,
refundedProductQuantities
);
expect(refundedLinesPriceSum).toBe(28);
});
});
describe("Get get all fulfillment lines price sum", () => {
const fulfillments: OrderRefundData_order_fulfillments[] = [
{
__typename: "Fulfillment",
fulfillmentOrder: 1,
id: "1",
lines: [
{
__typename: "FulfillmentLine",
id: "1-fulfillment-1",
orderLine: {
__typename: "OrderLine",
id: "1-line-1",
productName: "Milk 1",
quantity: 1,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 10,
currency: "USD"
}
}
},
quantity: 1
}
],
status: FulfillmentStatus.FULFILLED
},
{
__typename: "Fulfillment",
fulfillmentOrder: 2,
id: "2",
lines: [
{
__typename: "FulfillmentLine",
id: "2-fulfillment-1",
orderLine: {
__typename: "OrderLine",
id: "2-line-1",
productName: "Milk 1",
quantity: 2,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 6,
currency: "USD"
}
}
},
quantity: 1
},
{
__typename: "FulfillmentLine",
id: "2-fulfillment-2",
orderLine: {
__typename: "OrderLine",
id: "2-line-2",
productName: "Milk 2",
quantity: 2,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 6,
currency: "USD"
}
}
},
quantity: 1
}
],
status: FulfillmentStatus.FULFILLED
},
{
__typename: "Fulfillment",
fulfillmentOrder: 1,
id: "3",
lines: [
{
__typename: "FulfillmentLine",
id: "3-fulfillment-1",
orderLine: {
__typename: "OrderLine",
id: "3-line-1",
productName: "Milk 1",
quantity: 4,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 4,
currency: "USD"
}
}
},
quantity: 1
},
{
__typename: "FulfillmentLine",
id: "3-fulfillment-2",
orderLine: {
__typename: "OrderLine",
id: "3-line-2",
productName: "Milk 2",
quantity: 4,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 4,
currency: "USD"
}
}
},
quantity: 1
},
{
__typename: "FulfillmentLine",
id: "3-fulfillment-3",
orderLine: {
__typename: "OrderLine",
id: "3-line-3",
productName: "Milk 3",
quantity: 4,
thumbnail: undefined,
unitPrice: {
__typename: "TaxedMoney",
gross: {
__typename: "Money",
amount: 4,
currency: "USD"
}
}
},
quantity: 1
}
],
status: FulfillmentStatus.FULFILLED
}
];
it("is able to sum fulfillment lines prices", () => {
const refundedFulfilledProductQuantities: FormsetData<null, string> = [
{
data: null,
id: "1-fulfillment-1",
label: null,
value: "1"
},
{
data: null,
id: "2-fulfillment-1",
label: null,
value: "1"
},
{
data: null,
id: "2-fulfillment-2",
label: null,
value: "1"
},
{
data: null,
id: "3-fulfillment-1",
label: null,
value: "1"
},
{
data: null,
id: "3-fulfillment-2",
label: null,
value: "1"
},
{
data: null,
id: "3-fulfillment-3",
label: null,
value: "1"
}
];
const allFulfillmentLinesPriceSum = getAllFulfillmentLinesPriceSum(
fulfillments,
refundedFulfilledProductQuantities
);
expect(allFulfillmentLinesPriceSum).toBe(34);
});
it("is able to sum fulfillment lines prices multiplied by different quantities", () => {
const refundedFulfilledProductQuantities: FormsetData<null, string> = [
{
data: null,
id: "1-fulfillment-1",
label: null,
value: "0"
},
{
data: null,
id: "2-fulfillment-1",
label: null,
value: "2"
},
{
data: null,
id: "2-fulfillment-2",
label: null,
value: "2"
},
{
data: null,
id: "3-fulfillment-1",
label: null,
value: "4"
},
{
data: null,
id: "3-fulfillment-2",
label: null,
value: "4"
},
{
data: null,
id: "3-fulfillment-3",
label: null,
value: "4"
}
];
const allFulfillmentLinesPriceSum = getAllFulfillmentLinesPriceSum(
fulfillments,
refundedFulfilledProductQuantities
);
expect(allFulfillmentLinesPriceSum).toBe(72);
});
});
describe("Merge repeated order lines of fulfillment lines", () => {
it("is able to merge repeated order lines and sum their quantities", () => {
const lines: OrderDetails_order_fulfillments_lines[] = [
{
id: "RnVsZmlsbG1lbnRMaW5lOjMx",
quantity: 1,
orderLine: {
id: "T3JkZXJMaW5lOjQ1",
isShippingRequired: false,
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
productSku: "lake-tunes-mp3",
quantity: 2,
quantityFulfilled: 2,
unitPrice: {
gross: {
amount: 9.99,
currency: "USD",
__typename: "Money"
},
net: {
amount: 9.99,
currency: "USD",
__typename: "Money"
},
__typename: "TaxedMoney"
},
thumbnail: {
url:
"http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png",
__typename: "Image"
},
__typename: "OrderLine"
},
__typename: "FulfillmentLine"
},
{
id: "RnVsZmlsbG1lbnRMaW5lOjMy",
quantity: 1,
orderLine: {
id: "T3JkZXJMaW5lOjQ1",
isShippingRequired: false,
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6MzE3",
quantityAvailable: 50,
__typename: "ProductVariant"
},
productName: "Lake Tunes",
productSku: "lake-tunes-mp3",
quantity: 2,
quantityFulfilled: 2,
unitPrice: {
gross: {
amount: 9.99,
currency: "USD",
__typename: "Money"
},
net: {
amount: 9.99,
currency: "USD",
__typename: "Money"
},
__typename: "TaxedMoney"
},
thumbnail: {
url:
"http://localhost:8000/media/__sized__/products/saleor-digital-03_2-thumbnail-255x255.png",
__typename: "Image"
},
__typename: "OrderLine"
},
__typename: "FulfillmentLine"
},
{
id: "RnVsZmlsbG1lbnRMaW5lOjMz",
quantity: 1,
orderLine: {
id: "T3JkZXJMaW5lOjQ3",
isShippingRequired: true,
variant: {
id: "UHJvZHVjdFZhcmlhbnQ6Mjg2",
quantityAvailable: 50,
__typename: "ProductVariant"
},
productName: "T-shirt",
productSku: "29810068",
quantity: 3,
quantityFulfilled: 1,
unitPrice: {
gross: {
amount: 2.5,
currency: "USD",
__typename: "Money"
},
net: {
amount: 2.5,
currency: "USD",
__typename: "Money"
},
__typename: "TaxedMoney"
},
thumbnail: {
url:
"http://localhost:8000/media/__sized__/products/saleordemoproduct_cl_boot06_1-thumbnail-255x255.png",
__typename: "Image"
},
__typename: "OrderLine"
},
__typename: "FulfillmentLine"
}
];
const mergedLines = mergeRepeatedOrderLines(lines);
expect(mergedLines).toHaveLength(2);
expect(
mergedLines.find(
fulfillmentLine => fulfillmentLine.orderLine.id === "T3JkZXJMaW5lOjQ1"
).quantity
).toBe(2);
});
});

77
src/orders/utils/data.ts Normal file
View file

@ -0,0 +1,77 @@
import { IMoney, subtractMoney } from "@saleor/components/Money";
import { FormsetData } from "@saleor/hooks/useFormset";
import { OrderDetails_order_fulfillments_lines } from "../types/OrderDetails";
import {
OrderRefundData_order,
OrderRefundData_order_fulfillments,
OrderRefundData_order_lines
} from "../types/OrderRefundData";
export type OrderWithTotalAndTotalCaptured = Pick<
OrderRefundData_order,
"total" | "totalCaptured"
>;
export function getPreviouslyRefundedPrice(
order: OrderWithTotalAndTotalCaptured
): IMoney {
return (
order?.totalCaptured &&
order?.total?.gross &&
subtractMoney(order?.totalCaptured, order?.total?.gross)
);
}
export function getRefundedLinesPriceSum(
lines: OrderRefundData_order_lines[],
refundedProductQuantities: FormsetData<null, string>
): number {
return lines?.reduce((sum, line) => {
const refundedLine = refundedProductQuantities.find(
refundedLine => refundedLine.id === line.id
);
return sum + line.unitPrice.gross.amount * Number(refundedLine?.value || 0);
}, 0);
}
export function getAllFulfillmentLinesPriceSum(
fulfillments: OrderRefundData_order_fulfillments[],
refundedFulfilledProductQuantities: FormsetData<null, string>
): number {
return fulfillments?.reduce((sum, fulfillment) => {
const fulfilmentLinesSum = fulfillment?.lines.reduce((sum, line) => {
const refundedLine = refundedFulfilledProductQuantities.find(
refundedLine => refundedLine.id === line.id
);
return (
sum +
line.orderLine.unitPrice.gross.amount * Number(refundedLine?.value || 0)
);
}, 0);
return sum + fulfilmentLinesSum;
}, 0);
}
export function mergeRepeatedOrderLines(
fulfillmentLines: OrderDetails_order_fulfillments_lines[]
) {
return fulfillmentLines.reduce((prev, curr) => {
const existingOrderLineIndex = prev.findIndex(
prevLine => prevLine.orderLine.id === curr.orderLine.id
);
if (existingOrderLineIndex === -1) {
prev.push(curr);
} else {
const existingOrderLine = prev[existingOrderLineIndex];
prev[existingOrderLineIndex] = {
...existingOrderLine,
quantity: existingOrderLine.quantity + curr.quantity
};
}
return prev;
}, Array<OrderDetails_order_fulfillments_lines>());
}

View file

@ -19,7 +19,6 @@ import { OrderLineDelete } from "../../types/OrderLineDelete";
import { OrderLinesAdd } from "../../types/OrderLinesAdd"; import { OrderLinesAdd } from "../../types/OrderLinesAdd";
import { OrderLineUpdate } from "../../types/OrderLineUpdate"; import { OrderLineUpdate } from "../../types/OrderLineUpdate";
import { OrderMarkAsPaid } from "../../types/OrderMarkAsPaid"; import { OrderMarkAsPaid } from "../../types/OrderMarkAsPaid";
import { OrderRefund } from "../../types/OrderRefund";
import { OrderShippingMethodUpdate } from "../../types/OrderShippingMethodUpdate"; import { OrderShippingMethodUpdate } from "../../types/OrderShippingMethodUpdate";
import { OrderUpdate } from "../../types/OrderUpdate"; import { OrderUpdate } from "../../types/OrderUpdate";
import { OrderVoid } from "../../types/OrderVoid"; import { OrderVoid } from "../../types/OrderVoid";
@ -42,7 +41,6 @@ interface OrderDetailsMessages {
handleOrderMarkAsPaid: (data: OrderMarkAsPaid) => void; handleOrderMarkAsPaid: (data: OrderMarkAsPaid) => void;
handleOrderVoid: (data: OrderVoid) => void; handleOrderVoid: (data: OrderVoid) => void;
handlePaymentCapture: (data: OrderCapture) => void; handlePaymentCapture: (data: OrderCapture) => void;
handlePaymentRefund: (data: OrderRefund) => void;
handleShippingMethodUpdate: (data: OrderShippingMethodUpdate) => void; handleShippingMethodUpdate: (data: OrderShippingMethodUpdate) => void;
handleUpdate: (data: OrderUpdate) => void; handleUpdate: (data: OrderUpdate) => void;
handleInvoiceGeneratePending: (data: InvoiceRequest) => void; handleInvoiceGeneratePending: (data: InvoiceRequest) => void;
@ -80,18 +78,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
closeModal(); closeModal();
} }
}; };
const handlePaymentRefund = (data: OrderRefund) => {
const errs = data.orderRefund?.errors;
if (errs.length === 0) {
pushMessage({
status: "success",
text: intl.formatMessage({
defaultMessage: "Payment successfully refunded"
})
});
closeModal();
}
};
const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => { const handleOrderMarkAsPaid = (data: OrderMarkAsPaid) => {
const errs = data.orderMarkAsPaid?.errors; const errs = data.orderMarkAsPaid?.errors;
if (errs.length === 0) { if (errs.length === 0) {
@ -313,7 +299,6 @@ export const OrderDetailsMessages: React.FC<OrderDetailsMessages> = ({
handleOrderMarkAsPaid, handleOrderMarkAsPaid,
handleOrderVoid, handleOrderVoid,
handlePaymentCapture, handlePaymentCapture,
handlePaymentRefund,
handleShippingMethodUpdate, handleShippingMethodUpdate,
handleUpdate handleUpdate
}); });

View file

@ -49,6 +49,7 @@ import {
orderDraftListUrl, orderDraftListUrl,
orderFulfillUrl, orderFulfillUrl,
orderListUrl, orderListUrl,
orderRefundUrl,
orderUrl, orderUrl,
OrderUrlDialog, OrderUrlDialog,
OrderUrlQueryParams OrderUrlQueryParams
@ -156,7 +157,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
onOrderCancel={orderMessages.handleOrderCancel} onOrderCancel={orderMessages.handleOrderCancel}
onOrderVoid={orderMessages.handleOrderVoid} onOrderVoid={orderMessages.handleOrderVoid}
onPaymentCapture={orderMessages.handlePaymentCapture} onPaymentCapture={orderMessages.handlePaymentCapture}
onPaymentRefund={orderMessages.handlePaymentRefund}
onUpdate={orderMessages.handleUpdate} onUpdate={orderMessages.handleUpdate}
onDraftUpdate={orderMessages.handleDraftUpdate} onDraftUpdate={orderMessages.handleDraftUpdate}
onShippingMethodUpdate={ onShippingMethodUpdate={
@ -199,7 +199,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
orderLineDelete, orderLineDelete,
orderLineUpdate, orderLineUpdate,
orderPaymentCapture, orderPaymentCapture,
orderPaymentRefund,
orderVoid, orderVoid,
orderShippingMethodUpdate, orderShippingMethodUpdate,
orderUpdate, orderUpdate,
@ -281,7 +280,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
} }
onPaymentCapture={() => openModal("capture")} onPaymentCapture={() => openModal("capture")}
onPaymentVoid={() => openModal("void")} onPaymentVoid={() => openModal("void")}
onPaymentRefund={() => openModal("refund")} onPaymentRefund={() => navigate(orderRefundUrl(id))}
onProductClick={id => () => navigate(productUrl(id))} onProductClick={id => () => navigate(productUrl(id))}
onBillingAddressEdit={() => onBillingAddressEdit={() =>
openModal("edit-billing-address") openModal("edit-billing-address")
@ -371,7 +370,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
} }
initial={order?.total.gross.amount} initial={order?.total.gross.amount}
open={params.action === "capture"} open={params.action === "capture"}
variant="capture"
onClose={closeModal} onClose={closeModal}
onSubmit={variables => onSubmit={variables =>
orderPaymentCapture.mutate({ orderPaymentCapture.mutate({
@ -380,23 +378,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
}) })
} }
/> />
<OrderPaymentDialog
confirmButtonState={orderPaymentRefund.opts.status}
errors={
orderPaymentRefund.opts.data?.orderRefund.errors ||
[]
}
initial={order?.total.gross.amount}
open={params.action === "refund"}
variant="refund"
onClose={closeModal}
onSubmit={variables =>
orderPaymentRefund.mutate({
...variables,
id
})
}
/>
<OrderFulfillmentCancelDialog <OrderFulfillmentCancelDialog
confirmButtonState={ confirmButtonState={
orderFulfillmentCancel.opts.status orderFulfillmentCancel.opts.status

View file

@ -0,0 +1,157 @@
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import OrderRefundPage from "@saleor/orders/components/OrderRefundPage";
import {
OrderRefundAmountCalculationMode,
OrderRefundSubmitData,
OrderRefundType
} from "@saleor/orders/components/OrderRefundPage/form";
import {
useOrderFulfillmentRefundProductsMutation,
useOrderRefundMutation
} from "@saleor/orders/mutations";
import { useOrderRefundData } from "@saleor/orders/queries";
import { orderUrl } from "@saleor/orders/urls";
import React from "react";
import { useIntl } from "react-intl";
const getAutomaticallyCalculatedProductsRefundInput = (
formData: OrderRefundSubmitData
) => ({
fulfillmentLines: formData.refundedFulfilledProductQuantities
.filter(line => line.value !== "0")
.map(line => ({
fulfillmentLineId: line.id,
quantity: Number(line.value)
})),
includeShippingCosts: formData.refundShipmentCosts,
orderLines: formData.refundedProductQuantities
.filter(line => line.value !== "0")
.map(line => ({
orderLineId: line.id,
quantity: Number(line.value)
}))
});
const getManuallySetProductsRefundInput = (
formData: OrderRefundSubmitData
) => ({
amountToRefund: formData.amount,
fulfillmentLines: formData.refundedFulfilledProductQuantities
.filter(line => line.value !== "0")
.map(line => ({
fulfillmentLineId: line.id,
quantity: Number(line.value)
})),
includeShippingCosts: formData.refundShipmentCosts,
orderLines: formData.refundedProductQuantities
.filter(line => line.value !== "0")
.map(line => ({
orderLineId: line.id,
quantity: Number(line.value)
}))
});
interface OrderRefundProps {
orderId: string;
}
const OrderRefund: React.FC<OrderRefundProps> = ({ orderId }) => {
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
const { data, loading } = useOrderRefundData({
displayLoader: true,
variables: {
orderId
}
});
const [refundOrder, refundOrderOpts] = useOrderRefundMutation({
onCompleted: data => {
if (data.orderRefund.errors.length === 0) {
navigate(orderUrl(orderId), true);
notify({
status: "success",
text: intl.formatMessage({
defaultMessage: "Refunded Items",
description: "order refunded success message"
})
});
}
}
});
const [
refundOrderFulfillmentProducts,
refundOrderFulfillmentProductsOpts
] = useOrderFulfillmentRefundProductsMutation({
onCompleted: data => {
if (data.orderFulfillmentRefundProducts.errors.length === 0) {
navigate(orderUrl(orderId), true);
notify({
status: "success",
text: intl.formatMessage({
defaultMessage: "Refunded Items",
description: "order refunded success message"
})
});
}
}
});
const handleSubmitMiscellaneousRefund = async (
formData: OrderRefundSubmitData
) => {
const response = await refundOrder({
variables: {
amount: formData.amount,
id: orderId
}
});
return response.errors || [];
};
const handleSubmitProductsRefund = async (
formData: OrderRefundSubmitData
) => {
const input =
formData.amountCalculationMode ===
OrderRefundAmountCalculationMode.AUTOMATIC
? getAutomaticallyCalculatedProductsRefundInput(formData)
: getManuallySetProductsRefundInput(formData);
const response = await refundOrderFulfillmentProducts({
variables: {
input,
order: orderId
}
});
return response.errors || [];
};
const handleSubmit = async (formData: OrderRefundSubmitData) =>
formData.type === OrderRefundType.MISCELLANEOUS
? handleSubmitMiscellaneousRefund(formData)
: handleSubmitProductsRefund(formData);
return (
<OrderRefundPage
order={data?.order}
disabled={
loading ||
refundOrderOpts.loading ||
refundOrderFulfillmentProductsOpts.loading
}
errors={[
...(refundOrderOpts.data?.orderRefund.errors || []),
...(refundOrderFulfillmentProductsOpts.data
?.orderFulfillmentRefundProducts.errors || [])
]}
onSubmit={handleSubmit}
onBack={() => navigate(orderUrl(orderId))}
/>
);
};
OrderRefund.displayName = "OrderRefund";
export default OrderRefund;

View file

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

View file

@ -67,7 +67,6 @@ export interface PageCreate_pageCreate_page_pageType {
id: string; id: string;
name: string; name: string;
attributes: (PageCreate_pageCreate_page_pageType_attributes | null)[] | null; attributes: (PageCreate_pageCreate_page_pageType_attributes | null)[] | null;
message: string | null;
} }
export interface PageCreate_pageCreate_page_metadata { export interface PageCreate_pageCreate_page_metadata {

File diff suppressed because it is too large Load diff

View file

@ -10,5 +10,9 @@ const order = orderFixture("");
storiesOf("Orders / OrderHistory", module) storiesOf("Orders / OrderHistory", module)
.addDecorator(Decorator) .addDecorator(Decorator)
.add("default", () => ( .add("default", () => (
<OrderHistory onNoteAdd={undefined} history={order.events} /> <OrderHistory
onNoteAdd={undefined}
orderCurrency={order.total.gross.currency}
history={order.events}
/>
)); ));

View file

@ -13,23 +13,19 @@ const props: OrderPaymentDialogProps = {
initial: 0, initial: 0,
onClose: () => undefined, onClose: () => undefined,
onSubmit: () => undefined, onSubmit: () => undefined,
open: true, open: true
variant: "capture"
}; };
storiesOf("Orders / OrderPaymentDialog", module) storiesOf("Orders / OrderPaymentDialog", module)
.addDecorator(Decorator) .addDecorator(Decorator)
.add("capture payment", () => <OrderPaymentDialog {...props} />) .add("capture payment", () => <OrderPaymentDialog {...props} />)
.add("refund payment", () => (
<OrderPaymentDialog {...props} variant="refund" />
))
.add("errors", () => ( .add("errors", () => (
<OrderPaymentDialog <OrderPaymentDialog
{...props} {...props}
errors={[ errors={[
{ {
__typename: "OrderError", __typename: "OrderError",
code: OrderErrorCode.CANNOT_REFUND, code: OrderErrorCode.CAPTURE_INACTIVE_PAYMENT,
field: null field: null
}, },
{ {
@ -38,6 +34,5 @@ storiesOf("Orders / OrderPaymentDialog", module)
field: "payment" field: "payment"
} }
]} ]}
variant="refund"
/> />
)); ));

View file

@ -17,6 +17,7 @@ export enum AccountErrorCode {
DELETE_SUPERUSER_ACCOUNT = "DELETE_SUPERUSER_ACCOUNT", DELETE_SUPERUSER_ACCOUNT = "DELETE_SUPERUSER_ACCOUNT",
DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM",
GRAPHQL_ERROR = "GRAPHQL_ERROR", GRAPHQL_ERROR = "GRAPHQL_ERROR",
INACTIVE = "INACTIVE",
INVALID = "INVALID", INVALID = "INVALID",
INVALID_CREDENTIALS = "INVALID_CREDENTIALS", INVALID_CREDENTIALS = "INVALID_CREDENTIALS",
INVALID_PASSWORD = "INVALID_PASSWORD", INVALID_PASSWORD = "INVALID_PASSWORD",
@ -454,6 +455,7 @@ export enum FileTypesEnum {
export enum FulfillmentStatus { export enum FulfillmentStatus {
CANCELED = "CANCELED", CANCELED = "CANCELED",
FULFILLED = "FULFILLED", FULFILLED = "FULFILLED",
REFUNDED = "REFUNDED",
} }
export enum InvoiceErrorCode { export enum InvoiceErrorCode {
@ -567,6 +569,7 @@ export enum OrderErrorCode {
CANNOT_CANCEL_ORDER = "CANNOT_CANCEL_ORDER", CANNOT_CANCEL_ORDER = "CANNOT_CANCEL_ORDER",
CANNOT_DELETE = "CANNOT_DELETE", CANNOT_DELETE = "CANNOT_DELETE",
CANNOT_REFUND = "CANNOT_REFUND", CANNOT_REFUND = "CANNOT_REFUND",
CANNOT_REFUND_FULFILLMENT_LINE = "CANNOT_REFUND_FULFILLMENT_LINE",
CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT", CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT",
CHANNEL_INACTIVE = "CHANNEL_INACTIVE", CHANNEL_INACTIVE = "CHANNEL_INACTIVE",
DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM",
@ -574,6 +577,7 @@ export enum OrderErrorCode {
GRAPHQL_ERROR = "GRAPHQL_ERROR", GRAPHQL_ERROR = "GRAPHQL_ERROR",
INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK", INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK",
INVALID = "INVALID", INVALID = "INVALID",
INVALID_REFUND_QUANTITY = "INVALID_REFUND_QUANTITY",
NOT_AVAILABLE_IN_CHANNEL = "NOT_AVAILABLE_IN_CHANNEL", NOT_AVAILABLE_IN_CHANNEL = "NOT_AVAILABLE_IN_CHANNEL",
NOT_EDITABLE = "NOT_EDITABLE", NOT_EDITABLE = "NOT_EDITABLE",
NOT_FOUND = "NOT_FOUND", NOT_FOUND = "NOT_FOUND",
@ -613,6 +617,7 @@ export enum OrderEventsEnum {
EXTERNAL_SERVICE_NOTIFICATION = "EXTERNAL_SERVICE_NOTIFICATION", EXTERNAL_SERVICE_NOTIFICATION = "EXTERNAL_SERVICE_NOTIFICATION",
FULFILLMENT_CANCELED = "FULFILLMENT_CANCELED", FULFILLMENT_CANCELED = "FULFILLMENT_CANCELED",
FULFILLMENT_FULFILLED_ITEMS = "FULFILLMENT_FULFILLED_ITEMS", FULFILLMENT_FULFILLED_ITEMS = "FULFILLMENT_FULFILLED_ITEMS",
FULFILLMENT_REFUNDED = "FULFILLMENT_REFUNDED",
FULFILLMENT_RESTOCKED_ITEMS = "FULFILLMENT_RESTOCKED_ITEMS", FULFILLMENT_RESTOCKED_ITEMS = "FULFILLMENT_RESTOCKED_ITEMS",
INVOICE_GENERATED = "INVOICE_GENERATED", INVOICE_GENERATED = "INVOICE_GENERATED",
INVOICE_REQUESTED = "INVOICE_REQUESTED", INVOICE_REQUESTED = "INVOICE_REQUESTED",
@ -1351,6 +1356,24 @@ export interface OrderLineInput {
quantity: number; quantity: number;
} }
export interface OrderRefundFulfillmentLineInput {
fulfillmentLineId?: string | null;
quantity: number;
}
export interface OrderRefundLineInput {
orderLineId?: string | null;
quantity: number;
}
export interface OrderRefundProductsInput {
orderLines?: OrderRefundLineInput[] | null;
fulfillmentLines?: OrderRefundFulfillmentLineInput[] | null;
notifyCustomer?: boolean | null;
amountToRefund?: any | null;
includeShippingCosts?: boolean | null;
}
export interface OrderSettingsUpdateInput { export interface OrderSettingsUpdateInput {
automaticallyConfirmAllNewOrders: boolean; automaticallyConfirmAllNewOrders: boolean;
} }