From eb351b396a624af9dfe2cfdb0706b86ff7a98ac8 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 1 Dec 2020 14:13:05 +0100 Subject: [PATCH] Refunds (#870) * 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 --- CHANGELOG.md | 1 + babel.config.js | 1 + locale/defaultMessages.json | 188 +- package-lock.json | 35 + package.json | 1 + schema.graphql | 39 + src/components/PriceField/PriceField.tsx | 7 +- .../RadioGroupField/RadioGroupField.tsx | 18 +- src/components/StatusLabel/StatusLabel.tsx | 7 +- src/components/Timeline/TimelineEvent.tsx | 36 +- src/fragments/orders.ts | 26 + src/fragments/types/OrderDetailsFragment.ts | 15 + src/fragments/types/OrderEventFragment.ts | 15 + .../types/RefundOrderLineFragment.ts | 32 + .../OrderDetailsPage/OrderDetailsPage.tsx | 6 +- .../OrderDraftPage/OrderDraftPage.tsx | 6 +- .../OrderFulfillment/OrderFulfillment.tsx | 110 +- .../components/OrderHistory/OrderHistory.tsx | 88 +- .../components/OrderPayment/OrderPayment.tsx | 19 + .../OrderPaymentDialog/OrderPaymentDialog.tsx | 15 +- .../components/OrderRefund/OrderRefund.tsx | 71 + src/orders/components/OrderRefund/index.ts | 2 + .../OrderRefundAmount/OrderRefundAmount.tsx | 381 ++ .../components/OrderRefundAmount/index.ts | 2 + .../OrderRefundAmountValues.tsx | 153 + .../OrderRefundAmountValues/index.ts | 2 + .../OrderRefundFulfilledProducts.tsx | 246 + .../OrderRefundFulfilledProducts/index.ts | 2 + .../OrderRefundPage.stories.tsx | 28 + .../OrderRefundPage/OrderRefundPage.tsx | 147 + .../components/OrderRefundPage/fixtures.ts | 181 + .../components/OrderRefundPage/form.tsx | 202 + .../components/OrderRefundPage/index.ts | 2 + .../OrderRefundUnfulfilledProducts.tsx | 237 + .../OrderRefundUnfulfilledProducts/index.ts | 2 + src/orders/containers/OrderOperations.tsx | 243 +- src/orders/fixtures.ts | 55 + src/orders/index.tsx | 7 + src/orders/mutations.ts | 33 +- src/orders/queries.ts | 52 +- src/orders/types/FulfillOrder.ts | 15 + src/orders/types/OrderAddNote.ts | 15 + src/orders/types/OrderCancel.ts | 15 + src/orders/types/OrderCapture.ts | 15 + src/orders/types/OrderConfirm.ts | 17 + src/orders/types/OrderDetails.ts | 15 + src/orders/types/OrderDraftCancel.ts | 15 + src/orders/types/OrderDraftFinalize.ts | 15 + src/orders/types/OrderDraftUpdate.ts | 15 + src/orders/types/OrderFulfillmentCancel.ts | 15 + .../types/OrderFulfillmentRefundProducts.ts | 427 ++ .../types/OrderFulfillmentUpdateTracking.ts | 15 + src/orders/types/OrderLineDelete.ts | 15 + src/orders/types/OrderLineUpdate.ts | 15 + src/orders/types/OrderLinesAdd.ts | 15 + src/orders/types/OrderMarkAsPaid.ts | 15 + src/orders/types/OrderRefund.ts | 15 + src/orders/types/OrderRefundData.ts | 122 + src/orders/types/OrderUpdate.ts | 15 + src/orders/types/OrderVoid.ts | 15 + src/orders/urls.ts | 5 +- src/orders/utils/data.test.ts | 525 ++ src/orders/utils/data.ts | 77 + .../OrderDetails/OrderDetailsMessages.tsx | 15 - src/orders/views/OrderDetails/index.tsx | 23 +- src/orders/views/OrderRefund/OrderRefund.tsx | 157 + src/orders/views/OrderRefund/index.ts | 2 + src/pages/types/PageCreate.ts | 1 - .../__snapshots__/Stories.test.ts.snap | 4269 ++++++++++++++++- src/storybook/stories/orders/OrderHistory.tsx | 6 +- .../stories/orders/OrderPaymentDialog.tsx | 9 +- src/types/globalTypes.ts | 23 + 72 files changed, 8350 insertions(+), 296 deletions(-) create mode 100644 src/fragments/types/RefundOrderLineFragment.ts create mode 100644 src/orders/components/OrderRefund/OrderRefund.tsx create mode 100644 src/orders/components/OrderRefund/index.ts create mode 100644 src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx create mode 100644 src/orders/components/OrderRefundAmount/index.ts create mode 100644 src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx create mode 100644 src/orders/components/OrderRefundAmountValues/index.ts create mode 100644 src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx create mode 100644 src/orders/components/OrderRefundFulfilledProducts/index.ts create mode 100644 src/orders/components/OrderRefundPage/OrderRefundPage.stories.tsx create mode 100644 src/orders/components/OrderRefundPage/OrderRefundPage.tsx create mode 100644 src/orders/components/OrderRefundPage/fixtures.ts create mode 100644 src/orders/components/OrderRefundPage/form.tsx create mode 100644 src/orders/components/OrderRefundPage/index.ts create mode 100644 src/orders/components/OrderRefundUnfulfilledProducts/OrderRefundUnfulfilledProducts.tsx create mode 100644 src/orders/components/OrderRefundUnfulfilledProducts/index.ts create mode 100644 src/orders/types/OrderFulfillmentRefundProducts.ts create mode 100644 src/orders/types/OrderRefundData.ts create mode 100644 src/orders/utils/data.test.ts create mode 100644 src/orders/utils/data.ts create mode 100644 src/orders/views/OrderRefund/OrderRefund.tsx create mode 100644 src/orders/views/OrderRefund/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 887a18556..a95ce0ccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 Page Types - #807 by @orzechdev - Add shipping methods to translation section - #864 by @marekchoinski +- New Miscellaneous and Product refunds - #870 by @orzechdev # 2.11.1 diff --git a/babel.config.js b/babel.config.js index c149a757e..ba7eccdfc 100644 --- a/babel.config.js +++ b/babel.config.js @@ -32,6 +32,7 @@ module.exports = api => { } ], "@babel/plugin-proposal-object-rest-spread", + "@babel/plugin-proposal-nullish-coalescing-operator", "react-intl-auto" ]; diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 79ef21b79..23da84d8f 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3284,6 +3284,10 @@ "context": "fulfillment group tracking number", "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": { "context": "ordered product quantity", "string": "Quantity" @@ -3323,6 +3327,12 @@ "context": "order history message", "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": { "context": "order history message", "string": "Order was marked as paid" @@ -3407,6 +3417,10 @@ "context": "order history message", "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": { "context": "order history message", "string": "Invoice no. {invoiceNumber} was updated" @@ -3434,6 +3448,9 @@ "context": "order history message", "string": "Payment failed" }, + "src_dot_orders_dot_components_dot_OrderHistory_dot_492197448": { + "string": "Products refunded" + }, "src_dot_orders_dot_components_dot_OrderHistory_dot_493321552": { "context": "order history message", "string": "Order cancel information was sent to customer" @@ -3546,10 +3563,6 @@ "context": "dialog header", "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": { "context": "amount of refunded money", "string": "Amount" @@ -3561,6 +3574,10 @@ "context": "dialog header", "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": { "context": "order shipping method name", "string": "Shipping" @@ -3633,6 +3650,162 @@ "src_dot_orders_dot_components_dot_OrderProductAddDialog_dot_353369701": { "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 can’t 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": { "context": "header", "string": "Order settings" @@ -3733,9 +3906,6 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": { "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": { "context": "window title", "string": "Draft Order #{orderNumber}" @@ -3772,6 +3942,10 @@ "src_dot_orders_dot_views_dot_OrderList_dot_1738939038": { "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": { "context": "page types section name", "string": "Page Types" diff --git a/package-lock.json b/package-lock.json index 695d245ec..aed8e5dc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -946,6 +946,24 @@ "@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": { "version": "7.2.0", "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/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": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.2.0.tgz", diff --git a/package.json b/package.json index 9d62b3dcb..f29f25df4 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "@babel/core": "^7.7.7", "@babel/plugin-proposal-class-properties": "^7.5.0", "@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-object-rest-spread": "^7.5.4", "@babel/plugin-proposal-optional-chaining": "^7.8.3", diff --git a/schema.graphql b/schema.graphql index 85e21ee84..6d393f495 100644 --- a/schema.graphql +++ b/schema.graphql @@ -47,6 +47,7 @@ enum AccountErrorCode { DELETE_STAFF_ACCOUNT DELETE_SUPERUSER_ACCOUNT GRAPHQL_ERROR + INACTIVE INVALID INVALID_PASSWORD LEFT_NOT_MANAGEABLE_PERMISSION @@ -1999,8 +2000,16 @@ type FulfillmentLine implements Node { 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 { FULFILLED + REFUNDED CANCELED } @@ -2648,6 +2657,7 @@ type Mutation { orderFulfill(input: OrderFulfillInput!, order: ID): OrderFulfill orderFulfillmentCancel(id: ID!, input: FulfillmentCancelInput!): FulfillmentCancel orderFulfillmentUpdateTracking(id: ID!, input: FulfillmentUpdateTrackingInput!): FulfillmentUpdateTracking + orderFulfillmentRefundProducts(input: OrderRefundProductsInput!, order: ID!): FulfillmentRefundProducts orderMarkAsPaid(id: ID!, transactionReference: String): OrderMarkAsPaid orderRefund(amount: PositiveDecimal!, id: ID!): OrderRefund orderUpdate(id: ID!, input: OrderUpdateInput!): OrderUpdate @@ -2846,6 +2856,7 @@ type Order implements Node & ObjectWithMetadata { totalBalance: Money! userEmail: String isShippingRequired: Boolean! + linesAvailableForRefund: [OrderLineAvailableForRefund]! } enum OrderAction { @@ -2945,6 +2956,8 @@ enum OrderErrorCode { UNIQUE VOID_INACTIVE_PAYMENT ZERO_QUANTITY + INVALID_REFUND_QUANTITY + CANNOT_REFUND_FULFILLMENT_LINE INSUFFICIENT_STOCK DUPLICATED_INPUT_ITEM NOT_AVAILABLE_IN_CHANNEL @@ -2971,6 +2984,7 @@ type OrderEvent implements Node { fulfilledItems: [FulfillmentLine] warehouse: Warehouse transactionReference: String + shippingCostsIncluded: Boolean } type OrderEventCountableConnection { @@ -3028,6 +3042,7 @@ enum OrderEventsEnum { FULFILLMENT_CANCELED FULFILLMENT_RESTOCKED_ITEMS FULFILLMENT_FULFILLED_ITEMS + FULFILLMENT_REFUNDED TRACKING_UPDATED NOTE_ADDED OTHER @@ -3082,6 +3097,11 @@ type OrderLine implements Node { allocations: [Allocation!] } +type OrderLineAvailableForRefund { + quantity: Int + orderLine: OrderLine +} + input OrderLineCreateInput { quantity: Int! variantId: ID! @@ -3103,6 +3123,24 @@ type OrderRefund { 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 { automaticallyConfirmAllNewOrders: Boolean! } @@ -5089,6 +5127,7 @@ enum TransactionError { } enum TransactionKind { + EXTERNAL AUTH PENDING ACTION_TO_CONFIRM diff --git a/src/components/PriceField/PriceField.tsx b/src/components/PriceField/PriceField.tsx index a5b0c97f4..ed70c88fa 100644 --- a/src/components/PriceField/PriceField.tsx +++ b/src/components/PriceField/PriceField.tsx @@ -39,6 +39,7 @@ interface PriceFieldProps { name?: string; value?: string | number; InputProps?: InputProps; + inputProps?: InputProps["inputProps"]; required?: boolean; onChange(event: any); } @@ -55,7 +56,8 @@ export const PriceField: React.FC = props => { onChange, required, value, - InputProps + InputProps, + inputProps } = props; const classes = useStyles(props); @@ -93,7 +95,8 @@ export const PriceField: React.FC = props => { }} inputProps={{ min: minValue, - type: "number" + type: "number", + ...inputProps }} name={name} disabled={disabled} diff --git a/src/components/RadioGroupField/RadioGroupField.tsx b/src/components/RadioGroupField/RadioGroupField.tsx index fc46cca80..bc30a5a8f 100644 --- a/src/components/RadioGroupField/RadioGroupField.tsx +++ b/src/components/RadioGroupField/RadioGroupField.tsx @@ -15,9 +15,15 @@ const useStyles = makeStyles( formLabel: { marginBottom: theme.spacing(1) }, + radioGroupInline: { + flexDirection: "row" + }, radioLabel: { marginBottom: -theme.spacing(0.5) }, + radioLabelInline: { + marginRight: theme.spacing(4) + }, root: { "& $radioLabel": { "&:last-of-type": { @@ -53,6 +59,7 @@ interface RadioGroupFieldProps { label?: React.ReactNode; name?: string; value: string | number; + variant?: "block" | "inline"; onChange: (event: React.ChangeEvent) => void; } @@ -66,7 +73,8 @@ export const RadioGroupField: React.FC = props => { value, onChange, name, - hint + hint, + variant = "block" } = props; const classes = useStyles(props); @@ -86,13 +94,19 @@ export const RadioGroupField: React.FC = props => { name={name} value={value} onChange={onChange} + className={classNames({ + [classes.radioGroupInline]: variant === "inline" + })} > {choices.length > 0 ? ( choices.map(choice => ( } label={choice.label} key={choice.value} diff --git a/src/components/StatusLabel/StatusLabel.tsx b/src/components/StatusLabel/StatusLabel.tsx index 9024ed4d3..1838ac014 100644 --- a/src/components/StatusLabel/StatusLabel.tsx +++ b/src/components/StatusLabel/StatusLabel.tsx @@ -1,3 +1,4 @@ +import grey from "@material-ui/core/colors/grey"; import yellow from "@material-ui/core/colors/yellow"; import { makeStyles } from "@material-ui/core/styles"; import Typography, { TypographyProps } from "@material-ui/core/Typography"; @@ -34,6 +35,9 @@ const useStyles = makeStyles( }, successDot: { "&:before": { backgroundColor: theme.palette.primary.main, ...dot } + }, + unspecifiedDot: { + "&:before": { backgroundColor: grey[500], ...dot } } }; }, @@ -43,7 +47,7 @@ const useStyles = makeStyles( interface StatusLabelProps { className?: string; label: string | React.ReactNode; - status: "success" | "neutral" | "error" | string; + status: "success" | "neutral" | "unspecified" | "error" | string; typographyProps?: TypographyProps; } @@ -59,6 +63,7 @@ const StatusLabel: React.FC = props => { [className]: true, [classes.successDot]: status === "success", [classes.neutralDot]: status === "neutral", + [classes.unspecifiedDot]: status === "unspecified", [classes.errorDot]: status === "error" })} > diff --git a/src/components/Timeline/TimelineEvent.tsx b/src/components/Timeline/TimelineEvent.tsx index ae9088315..49c4972cc 100644 --- a/src/components/Timeline/TimelineEvent.tsx +++ b/src/components/Timeline/TimelineEvent.tsx @@ -4,6 +4,7 @@ import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary"; import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import classNames from "classnames"; import React from "react"; import { DateTime } from "../Date"; @@ -25,7 +26,7 @@ const useStyles = makeStyles( dateExpander: { color: theme.typography.caption.color, position: "absolute", - right: theme.spacing(3) + right: 0 }, dot: { backgroundColor: theme.palette.primary.main, @@ -36,6 +37,7 @@ const useStyles = makeStyles( top: 6, width: 8 }, + expanded: {}, noExpander: { alignItems: "center", display: "flex", @@ -43,12 +45,30 @@ const useStyles = makeStyles( width: "100%" }, panel: { + "&$expanded": { + margin: 0 + }, "&:before": { display: "none" }, background: "none", + margin: 0, width: "100%" }, + panelExpander: { + "&$expanded": { + margin: 0, + minHeight: 0 + }, + "&> .MuiExpansionPanelSummary-content": { + margin: 0 + }, + "&> .MuiExpansionPanelSummary-expandIcon": { + padding: 0, + right: theme.spacing(18) + }, + margin: 0 + }, root: { "&:last-child:after": { background: theme.palette.background.default, @@ -90,10 +110,18 @@ export const TimelineEvent: React.FC = props => {
{children ? ( - - }> + + } + > {title} - {title} + + + {children} diff --git a/src/fragments/orders.ts b/src/fragments/orders.ts index 2701ac70c..ab11173a5 100644 --- a/src/fragments/orders.ts +++ b/src/fragments/orders.ts @@ -7,6 +7,7 @@ export const fragmentOrderEvent = gql` fragment OrderEventFragment on OrderEvent { id amount + shippingCostsIncluded date email emailType @@ -19,6 +20,14 @@ export const fragmentOrderEvent = gql` id 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` ${fragmentOrderLine} fragment FulfillmentFragment on Fulfillment { diff --git a/src/fragments/types/OrderDetailsFragment.ts b/src/fragments/types/OrderDetailsFragment.ts index 136dfbf27..cf1a2b0d5 100644 --- a/src/fragments/types/OrderDetailsFragment.ts +++ b/src/fragments/types/OrderDetailsFragment.ts @@ -48,10 +48,24 @@ export interface OrderDetailsFragment_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -61,6 +75,7 @@ export interface OrderDetailsFragment_events { transactionReference: string | null; type: OrderEventsEnum | null; user: OrderDetailsFragment_events_user | null; + lines: (OrderDetailsFragment_events_lines | null)[] | null; } export interface OrderDetailsFragment_fulfillments_lines_orderLine_variant { diff --git a/src/fragments/types/OrderEventFragment.ts b/src/fragments/types/OrderEventFragment.ts index 39d5ace4a..613a9cce0 100644 --- a/src/fragments/types/OrderEventFragment.ts +++ b/src/fragments/types/OrderEventFragment.ts @@ -14,10 +14,24 @@ export interface OrderEventFragment_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -27,4 +41,5 @@ export interface OrderEventFragment { transactionReference: string | null; type: OrderEventsEnum | null; user: OrderEventFragment_user | null; + lines: (OrderEventFragment_lines | null)[] | null; } diff --git a/src/fragments/types/RefundOrderLineFragment.ts b/src/fragments/types/RefundOrderLineFragment.ts new file mode 100644 index 000000000..aeb89fa1b --- /dev/null +++ b/src/fragments/types/RefundOrderLineFragment.ts @@ -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; +} diff --git a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx index 9d1016e85..f2b777951 100644 --- a/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx +++ b/src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx @@ -233,7 +233,11 @@ const OrderDetailsPage: React.FC = props => { /> - +
= props => { onOrderLineRemove={onOrderLineRemove} onShippingMethodEdit={onShippingMethodEdit} /> - +
= props => { 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 quantity = lines ? lines.map(line => line.quantity).reduce((prev, curr) => prev + curr, 0) @@ -119,6 +123,16 @@ const OrderFulfillment: React.FC = props => { quantity } ) + : status === FulfillmentStatus.REFUNDED + ? intl.formatMessage( + { + defaultMessage: "Refunded ({quantity})", + description: "refunded fulfillment, section header" + }, + { + quantity + } + ) : intl.formatMessage( { defaultMessage: "Cancelled ({quantity})", @@ -136,7 +150,11 @@ const OrderFulfillment: React.FC = props => { } status={ - status === FulfillmentStatus.FULFILLED ? "success" : "error" + status === FulfillmentStatus.FULFILLED + ? "success" + : status === FulfillmentStatus.REFUNDED + ? "unspecified" + : "error" } /> ) : ( @@ -239,47 +257,53 @@ const OrderFulfillment: React.FC = props => { ))} - - - - - {getStringOrPlaceholder(fulfillment?.warehouse?.name)} - - ) - }} - /> - - - {fulfillment?.trackingNumber && ( - - {fulfillment.trackingNumber} - - ) - }} - /> - )} - - - + {(fulfillment?.warehouse || fulfillment?.trackingNumber) && ( + + + + {fulfillment?.warehouse && ( + + {getStringOrPlaceholder( + fulfillment?.warehouse?.name + )} + + ) + }} + /> + )} + + + {fulfillment?.trackingNumber && ( + + {fulfillment.trackingNumber} + + ) + }} + /> + )} + + + + )} {status === FulfillmentStatus.FULFILLED && !fulfillment.trackingNumber && ( diff --git a/src/orders/components/OrderHistory/OrderHistory.tsx b/src/orders/components/OrderHistory/OrderHistory.tsx index 4bd735351..5d115517c 100644 --- a/src/orders/components/OrderHistory/OrderHistory.tsx +++ b/src/orders/components/OrderHistory/OrderHistory.tsx @@ -2,6 +2,7 @@ import { makeStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import Form from "@saleor/components/Form"; import Hr from "@saleor/components/Hr"; +import Money from "@saleor/components/Money"; import Skeleton from "@saleor/components/Skeleton"; import { Timeline, @@ -144,6 +145,16 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => { 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: return intl.formatMessage( { @@ -238,10 +249,16 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => { const useStyles = makeStyles( theme => ({ + eventSubtitle: { + marginTop: theme.spacing(1) + }, header: { fontWeight: 500, marginBottom: theme.spacing(1) }, + linesTableCell: { + paddingRight: theme.spacing(3) + }, root: { marginTop: theme.spacing(4) }, user: { marginBottom: theme.spacing(1) @@ -252,11 +269,12 @@ const useStyles = makeStyles( interface OrderHistoryProps { history: OrderDetails_order_events[]; + orderCurrency: string; onNoteAdd: (data: FormData) => void; } const OrderHistory: React.FC = props => { - const { history, onNoteAdd } = props; + const { history, orderCurrency, onNoteAdd } = props; const classes = useStyles(props); const intl = useIntl(); @@ -310,6 +328,74 @@ const OrderHistory: React.FC = props => { /> ); } + if (event.type === OrderEventsEnum.FULFILLMENT_REFUNDED) { + return ( + + {event.lines && ( + <> + + + + + + {event.lines.map(line => ( + + + + + + ))} + +
+ {line.orderLine.productName} + + + {line.orderLine.variantName} + + + + {`qty: ${line.quantity}`} + +
+ + + + {(event.amount || event.amount === 0) && ( + + )} + {event.shippingCostsIncluded && ( + + + + )} + + )} +
+ ); + } return ( = props => { maybe(() => order.paymentStatus), intl ); + const refundedAmount = + order?.totalCaptured && + order?.total?.gross && + subtractMoney(order.totalCaptured, order.total.gross); return ( = props => { )} + + + + + + {refundedAmount?.amount === undefined ? ( + + ) : ( + + )} + + void; onSubmit: (data: FormData) => void; } @@ -36,7 +35,6 @@ const OrderPaymentDialog: React.FC = ({ errors, open, initial, - variant, onClose, onSubmit }) => { @@ -56,15 +54,10 @@ const OrderPaymentDialog: React.FC = ({ {({ data, change, submit }) => ( <> - {variant === "capture" - ? intl.formatMessage({ - defaultMessage: "Capture Payment", - description: "dialog header" - }) - : intl.formatMessage({ - defaultMessage: "Refund Payment", - description: "dialog header" - })} + {intl.formatMessage({ + defaultMessage: "Capture Payment", + description: "dialog header" + })} ) => void; +} + +const messages = defineMessages({ + refundMiscellaneous: { + defaultMessage: "Miscellaneous Refund", + description: "refund type" + }, + refundProducts: { + defaultMessage: "Refund Products", + description: "refund type" + } +}); + +const OrderRefund: React.FC = props => { + const { data, disabled, onChange } = props; + const classes = useStyles(props); + const intl = useIntl(); + + return ( + + + + + + + ); +}; +OrderRefund.displayName = "OrderRefund"; +export default OrderRefund; diff --git a/src/orders/components/OrderRefund/index.ts b/src/orders/components/OrderRefund/index.ts new file mode 100644 index 000000000..23ffe32b5 --- /dev/null +++ b/src/orders/components/OrderRefund/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefund"; +export { default } from "./OrderRefund"; diff --git a/src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx b/src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx new file mode 100644 index 000000000..778e651ec --- /dev/null +++ b/src/orders/components/OrderRefundAmount/OrderRefundAmount.tsx @@ -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) => void; +} + +const RefundAmountInput: React.FC = props => { + const { + data, + maxRefund, + amountTooSmall, + amountTooBig, + currencySymbol, + disabled, + errors, + onChange + } = props; + const intl = useIntl(); + const classes = useStyles(props); + + const formErrors = getFormErrors(["amount"], errors); + + return ( + + ); +}; + +interface OrderRefundAmountProps { + data: OrderRefundFormData; + order: OrderRefundData_order; + disabled: boolean; + errors: OrderErrorFragment[]; + onChange: (event: React.ChangeEvent) => void; + onRefund: () => void; +} + +const OrderRefundAmount: React.FC = 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 ( + + + + {data.type === OrderRefundType.PRODUCTS && ( + + } + label={intl.formatMessage({ + defaultMessage: "Automatic Amount", + description: "label" + })} + /> + {data.amountCalculationMode === + OrderRefundAmountCalculationMode.AUTOMATIC && ( + <> + + + + + )} +
+ } + label={intl.formatMessage({ + defaultMessage: "Manual Amount", + description: "label" + })} + /> + {data.amountCalculationMode === + OrderRefundAmountCalculationMode.MANUAL && ( + <> + + + + + )} +
+ )} + {data.type === OrderRefundType.MISCELLANEOUS && ( + <> + + + + )} + + + + +
+
+ ); +}; +OrderRefundAmount.displayName = "OrderRefundAmount"; +export default OrderRefundAmount; diff --git a/src/orders/components/OrderRefundAmount/index.ts b/src/orders/components/OrderRefundAmount/index.ts new file mode 100644 index 000000000..f6f346ecf --- /dev/null +++ b/src/orders/components/OrderRefundAmount/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundAmount"; +export { default } from "./OrderRefundAmount"; diff --git a/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx b/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx new file mode 100644 index 000000000..4fa9dc175 --- /dev/null +++ b/src/orders/components/OrderRefundAmountValues/OrderRefundAmountValues.tsx @@ -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 = ({ + authorizedAmount, + shipmentCost, + selectedProductsValue, + previouslyRefunded, + maxRefund, + proposedRefundAmount, + refundTotalAmount +}) => { + const classes = useStyles({}); + + return ( + + + + + + + {shipmentCost?.amount !== undefined && ( + + + + + )} + {selectedProductsValue?.amount !== undefined && ( + + + + + )} + + + + + + + + + {proposedRefundAmount?.amount !== undefined && ( + + + + + )} + {refundTotalAmount?.amount !== undefined && ( + + + + + )} + +
+ + + {authorizedAmount?.amount !== undefined ? ( + + ) : ( + + )} +
+ + + +
+ + + +
+ + + {previouslyRefunded?.amount !== undefined ? ( + <> + + + ) : ( + + )} +
+ + + {maxRefund?.amount !== undefined ? ( + + ) : ( + + )} +
+ + + +
+ + + +
+ ); +}; +OrderRefundAmountValues.displayName = "OrderRefundAmountValues"; +export default OrderRefundAmountValues; diff --git a/src/orders/components/OrderRefundAmountValues/index.ts b/src/orders/components/OrderRefundAmountValues/index.ts new file mode 100644 index 000000000..b0f5c8559 --- /dev/null +++ b/src/orders/components/OrderRefundAmountValues/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundAmountValues"; +export { default } from "./OrderRefundAmountValues"; diff --git a/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx b/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx new file mode 100644 index 000000000..bc464f64b --- /dev/null +++ b/src/orders/components/OrderRefundFulfilledProducts/OrderRefundFulfilledProducts.tsx @@ -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; + onSetMaximalQuantities: () => void; +} + +const OrderRefundFulfilledProducts: React.FC = props => { + const { + fulfillment, + data, + disabled, + orderNumber, + onRefundedProductQuantityChange, + onSetMaximalQuantities + } = props; + const classes = useStyles(props); + const intl = useIntl(); + + return ( + + + {intl.formatMessage({ + defaultMessage: "Fulfillment", + description: "section header" + })} + {fulfillment && ( + + {`#${orderNumber}-${fulfillment?.fulfillmentOrder}`} + + )} + + } + /> + + + + + + + + + + + + + + + + + + + + + + {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 ( + + + {line?.orderLine?.productName ? ( + line?.orderLine?.productName + ) : ( + + )} + + + {line?.orderLine?.unitPrice ? ( + + ) : ( + + )} + + + {line?.quantity ? ( + + onRefundedProductQuantityChange( + line.id, + event.target.value + ) + } + InputProps={{ + endAdornment: line?.quantity && ( +
+ / {line?.quantity} +
+ ) + }} + error={isError} + helperText={ + isError && + intl.formatMessage({ + defaultMessage: "Improper value", + description: "error message" + }) + } + /> + ) : ( + + )} +
+ + {(line?.quantity && line?.orderLine?.unitPrice.gross && ( + + )) || } + +
+ ); + }, + () => ( + + + + + + ) + )} +
+
+
+ ); +}; +OrderRefundFulfilledProducts.displayName = "OrderRefundFulfilledProducts"; +export default OrderRefundFulfilledProducts; diff --git a/src/orders/components/OrderRefundFulfilledProducts/index.ts b/src/orders/components/OrderRefundFulfilledProducts/index.ts new file mode 100644 index 000000000..0081ba0d1 --- /dev/null +++ b/src/orders/components/OrderRefundFulfilledProducts/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundFulfilledProducts"; +export { default } from "./OrderRefundFulfilledProducts"; diff --git a/src/orders/components/OrderRefundPage/OrderRefundPage.stories.tsx b/src/orders/components/OrderRefundPage/OrderRefundPage.stories.tsx new file mode 100644 index 000000000..010249339 --- /dev/null +++ b/src/orders/components/OrderRefundPage/OrderRefundPage.stories.tsx @@ -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", () => ( + + )) + .add("miscellaneous", () => ( + + )) + .add("loading", () => ( + + )); diff --git a/src/orders/components/OrderRefundPage/OrderRefundPage.tsx b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx new file mode 100644 index 000000000..d7580f363 --- /dev/null +++ b/src/orders/components/OrderRefundPage/OrderRefundPage.tsx @@ -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 = 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 ( + + {({ data, handlers, change, submit }) => ( + + + {order?.number + ? intl.formatMessage( + { + defaultMessage: "Order #{orderNumber}", + description: "page header with order number" + }, + { + orderNumber: order.number + } + ) + : intl.formatMessage({ + defaultMessage: "Order", + description: "page header" + })} + + + +
+ + {data.type === OrderRefundType.PRODUCTS && ( + <> + {unfulfilledLines?.length > 0 && ( + <> + + + + )} + {renderCollection(fulfilledFulfillemnts, fulfillment => ( + + + + handlers.setMaximalRefundedFulfilledProductQuantities( + fulfillment?.id + ) + } + /> + + ))} + + )} +
+
+ +
+
+
+ )} +
+ ); +}; +OrderRefundPage.displayName = "OrderRefundPage"; +export default OrderRefundPage; diff --git a/src/orders/components/OrderRefundPage/fixtures.ts b/src/orders/components/OrderRefundPage/fixtures.ts new file mode 100644 index 000000000..fdbf9baf5 --- /dev/null +++ b/src/orders/components/OrderRefundPage/fixtures.ts @@ -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" + } + } + } + } + ] + } + ] +}); diff --git a/src/orders/components/OrderRefundPage/form.tsx b/src/orders/components/OrderRefundPage/form.tsx new file mode 100644 index 000000000..3a1917527 --- /dev/null +++ b/src/orders/components/OrderRefundPage/form.tsx @@ -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; + setMaximalRefundedProductQuantities: () => void; + changeRefundedFulfilledProductQuantity: FormsetChange; + setMaximalRefundedFulfilledProductQuantities: (fulfillmentId: string) => void; +} + +export interface OrderRefundFormData extends OrderRefundData { + refundedProductQuantities: FormsetData; + refundedFulfilledProductQuantities: FormsetData; +} + +export type OrderRefundSubmitData = OrderRefundFormData; + +export interface UseOrderRefundFormResult { + change: FormChange; + data: OrderRefundFormData; + disabled: boolean; + handlers: OrderRefundHandlers; + hasChanged: boolean; + submit: () => Promise; +} + +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( + order?.lines + .filter(line => line.quantityFulfilled !== line.quantity) + .map(line => ({ + data: null, + id: line.id, + label: null, + value: "0" + })) + ); + const refundedFulfilledProductQuantities = useFormset( + 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 = ( + 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 = ({ + children, + order, + defaultType, + onSubmit +}) => { + const props = useOrderRefundForm(order, defaultType, onSubmit); + + return
{children(props)}
; +}; + +OrderRefundForm.displayName = "OrderRefundForm"; +export default OrderRefundForm; diff --git a/src/orders/components/OrderRefundPage/index.ts b/src/orders/components/OrderRefundPage/index.ts new file mode 100644 index 000000000..f606b5996 --- /dev/null +++ b/src/orders/components/OrderRefundPage/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundPage"; +export { default } from "./OrderRefundPage"; diff --git a/src/orders/components/OrderRefundUnfulfilledProducts/OrderRefundUnfulfilledProducts.tsx b/src/orders/components/OrderRefundUnfulfilledProducts/OrderRefundUnfulfilledProducts.tsx new file mode 100644 index 000000000..c934a0bfd --- /dev/null +++ b/src/orders/components/OrderRefundUnfulfilledProducts/OrderRefundUnfulfilledProducts.tsx @@ -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; + onSetMaximalQuantities: () => void; +} + +const OrderRefundUnfulfilledProducts: React.FC = props => { + const { + unfulfilledLines, + data, + disabled, + onRefundedProductQuantityChange, + onSetMaximalQuantities + } = props; + const classes = useStyles(props); + const intl = useIntl(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + {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 ( + + + {line?.productName ? line?.productName : } + + + {line?.unitPrice ? ( + + ) : ( + + )} + + + {lineQuantity || lineQuantity === 0 ? ( + + onRefundedProductQuantityChange( + line.id, + event.target.value + ) + } + InputProps={{ + endAdornment: lineQuantity && ( +
+ / {lineQuantity} +
+ ) + }} + error={isError} + helperText={ + isError && + intl.formatMessage({ + defaultMessage: "Improper value", + description: "error message" + }) + } + /> + ) : ( + + )} +
+ + {(line?.unitPrice.gross && ( + + )) || } + +
+ ); + }, + () => ( + + + + + + ) + )} +
+
+
+ ); +}; +OrderRefundUnfulfilledProducts.displayName = "OrderRefundUnfulfilledProducts"; +export default OrderRefundUnfulfilledProducts; diff --git a/src/orders/components/OrderRefundUnfulfilledProducts/index.ts b/src/orders/components/OrderRefundUnfulfilledProducts/index.ts new file mode 100644 index 000000000..c8239f81b --- /dev/null +++ b/src/orders/components/OrderRefundUnfulfilledProducts/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefundUnfulfilledProducts"; +export { default } from "./OrderRefundUnfulfilledProducts"; diff --git a/src/orders/containers/OrderOperations.tsx b/src/orders/containers/OrderOperations.tsx index 68832d315..3dc85c25e 100644 --- a/src/orders/containers/OrderOperations.tsx +++ b/src/orders/containers/OrderOperations.tsx @@ -17,7 +17,6 @@ import { TypedOrderLinesAddMutation, TypedOrderLineUpdateMutation, TypedOrderMarkAsPaidMutation, - TypedOrderRefundMutation, TypedOrderShippingMethodUpdateMutation, TypedOrderUpdateMutation, TypedOrderVoidMutation @@ -66,7 +65,6 @@ import { OrderMarkAsPaid, OrderMarkAsPaidVariables } from "../types/OrderMarkAsPaid"; -import { OrderRefund, OrderRefundVariables } from "../types/OrderRefund"; import { OrderShippingMethodUpdate, OrderShippingMethodUpdateVariables @@ -97,10 +95,6 @@ interface OrderOperationsProps { OrderCapture, OrderCaptureVariables >; - orderPaymentRefund: PartialMutationProviderOutput< - OrderRefund, - OrderRefundVariables - >; orderPaymentMarkAsPaid: PartialMutationProviderOutput< OrderMarkAsPaid, OrderMarkAsPaidVariables @@ -154,7 +148,6 @@ interface OrderOperationsProps { onOrderMarkAsPaid: (data: OrderMarkAsPaid) => void; onNoteAdd: (data: OrderAddNote) => void; onPaymentCapture: (data: OrderCapture) => void; - onPaymentRefund: (data: OrderRefund) => void; onUpdate: (data: OrderUpdate) => void; onDraftCancel: (data: OrderDraftCancel) => void; onDraftFinalize: (data: OrderDraftFinalize) => void; @@ -177,7 +170,6 @@ const OrderOperations: React.FC = ({ onOrderLineUpdate, onOrderVoid, onPaymentCapture, - onPaymentRefund, onShippingMethodUpdate, onUpdate, onDraftCancel, @@ -194,172 +186,159 @@ const OrderOperations: React.FC = ({ {(...orderCancel) => ( {(...paymentCapture) => ( - - {(...paymentRefund) => ( - - {(...addNote) => ( - - {(...update) => ( - + {(...addNote) => ( + + {(...update) => ( + + {(...updateDraft) => ( + - {(...updateDraft) => ( - ( + - {(...updateShippingMethod) => ( - ( + - {(...deleteOrderLine) => ( - ( + - {(...addOrderLine) => ( - ( + - {(...updateOrderLine) => ( - ( + - {(...cancelFulfillment) => ( - ( + - {( - ...updateTrackingNumber - ) => ( - ( + - {(...finalizeDraft) => ( - ( + {( - ...cancelDraft + ...markAsPaid ) => ( - {( - ...markAsPaid + ...invoiceRequest ) => ( - {( - ...invoiceRequest - ) => ( - - {( + ...invoiceEmailSend + ) => + children({ + orderAddNote: getMutationProviderData( + ...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 - ) => - children( - { - orderAddNote: getMutationProviderData( - ...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 - ), - 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 - ) - } - ) - } - - )} - + ), + orderLineDelete: getMutationProviderData( + ...deleteOrderLine + ), + orderLineUpdate: getMutationProviderData( + ...updateOrderLine + ), + orderLinesAdd: getMutationProviderData( + ...addOrderLine + ), + orderPaymentCapture: getMutationProviderData( + ...paymentCapture + ), + orderPaymentMarkAsPaid: getMutationProviderData( + ...markAsPaid + ), + orderShippingMethodUpdate: getMutationProviderData( + ...updateShippingMethod + ), + orderUpdate: getMutationProviderData( + ...update + ), + orderVoid: getMutationProviderData( + ...orderVoid + ) + }) + } + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} - + )} )} diff --git a/src/orders/fixtures.ts b/src/orders/fixtures.ts index 9b407165f..9665bb10e 100644 --- a/src/orders/fixtures.ts +++ b/src/orders/fixtures.ts @@ -831,8 +831,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: null, id: "T3JkZXJFdmVudDoyMQ==", invoiceNumber: null, + lines: [], message: null, quantity: 1, + shippingCostsIncluded: false, transactionReference: "123", type: OrderEventsEnum.FULFILLMENT_FULFILLED_ITEMS, user: { @@ -841,6 +843,47 @@ export const order = (placeholder: string): OrderDetails_order => ({ 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", amount: null, @@ -849,8 +892,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: null, id: "T3JkZXJFdmVudDo0", invoiceNumber: null, + lines: [], message: "This is note", quantity: null, + shippingCostsIncluded: false, transactionReference: "124", type: OrderEventsEnum.NOTE_ADDED, user: null @@ -863,8 +908,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: null, id: "T3JkZXJFdmVudDo1", invoiceNumber: null, + lines: [], message: "This is note", quantity: null, + shippingCostsIncluded: false, transactionReference: "125", type: OrderEventsEnum.NOTE_ADDED, user: null @@ -877,8 +924,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: null, id: "T3JkZXJFdmVudDo2", invoiceNumber: null, + lines: [], message: "Note from external service", quantity: null, + shippingCostsIncluded: false, transactionReference: "126", type: OrderEventsEnum.EXTERNAL_SERVICE_NOTIFICATION, user: null @@ -891,8 +940,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: OrderEventsEmailsEnum.ORDER_CANCEL, id: "T3JkZXJFdmVudDo3", invoiceNumber: null, + lines: [], message: null, quantity: null, + shippingCostsIncluded: false, transactionReference: "127", type: OrderEventsEnum.EMAIL_SENT, user: null @@ -905,8 +956,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: OrderEventsEmailsEnum.ORDER_REFUND, id: "T3JkZXJFdmVudDo4", invoiceNumber: null, + lines: [], message: null, quantity: null, + shippingCostsIncluded: false, transactionReference: "128", type: OrderEventsEnum.EMAIL_SENT, user: null @@ -919,8 +972,10 @@ export const order = (placeholder: string): OrderDetails_order => ({ emailType: null, id: "T3JkZXJFdmVudDo5", invoiceNumber: null, + lines: [], message: null, quantity: null, + shippingCostsIncluded: false, transactionReference: "129", type: OrderEventsEnum.PAYMENT_AUTHORIZED, user: null diff --git a/src/orders/index.tsx b/src/orders/index.tsx index 3983b91ba..46d353c0c 100644 --- a/src/orders/index.tsx +++ b/src/orders/index.tsx @@ -15,6 +15,7 @@ import { OrderListUrlQueryParams, OrderListUrlSortField, orderPath, + orderRefundPath, orderSettingsPath, OrderUrlQueryParams } from "./urls"; @@ -22,6 +23,7 @@ import OrderDetailsComponent from "./views/OrderDetails"; import OrderDraftListComponent from "./views/OrderDraftList"; import OrderFulfillComponent from "./views/OrderFulfill"; import OrderListComponent from "./views/OrderList"; +import OrderRefundComponent from "./views/OrderRefund"; import OrderSettings from "./views/OrderSettings"; const OrderList: React.FC> = ({ location }) => { @@ -65,6 +67,10 @@ const OrderFulfill: React.FC> = ({ match }) => ( ); +const OrderRefund: React.FC> = ({ match }) => ( + +); + const Component = () => { const intl = useIntl(); @@ -76,6 +82,7 @@ const Component = () => { + diff --git a/src/orders/mutations.ts b/src/orders/mutations.ts index acc65aff4..c201a9389 100644 --- a/src/orders/mutations.ts +++ b/src/orders/mutations.ts @@ -7,6 +7,7 @@ import { fragmentOrderDetails, fragmentOrderEvent, fragmentOrderSettings, + fulfillmentFragment, invoiceFragment } from "@saleor/fragments/orders"; import makeMutation from "@saleor/hooks/makeMutation"; @@ -50,6 +51,10 @@ import { OrderFulfillmentCancel, OrderFulfillmentCancelVariables } from "./types/OrderFulfillmentCancel"; +import { + OrderFulfillmentRefundProducts, + OrderFulfillmentRefundProductsVariables +} from "./types/OrderFulfillmentRefundProducts"; import { OrderFulfillmentUpdateTracking, OrderFulfillmentUpdateTrackingVariables @@ -186,11 +191,37 @@ const orderRefundMutation = gql` } } `; -export const TypedOrderRefundMutation = TypedMutation< +export const useOrderRefundMutation = makeMutation< OrderRefund, OrderRefundVariables >(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` ${fragmentOrderDetails} ${orderErrorFragment} diff --git a/src/orders/queries.ts b/src/orders/queries.ts index fe7106d6b..09e2bd84e 100644 --- a/src/orders/queries.ts +++ b/src/orders/queries.ts @@ -1,8 +1,10 @@ import { fragmentAddress } from "@saleor/fragments/address"; import { fragmentOrderDetails, - fragmentOrderSettings + fragmentOrderSettings, + fragmentRefundOrderLine } from "@saleor/fragments/orders"; +import { fragmentMoney } from "@saleor/fragments/products"; import makeQuery from "@saleor/hooks/makeQuery"; import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch"; import gql from "graphql-tag"; @@ -18,6 +20,10 @@ import { OrderFulfillDataVariables } from "./types/OrderFulfillData"; import { OrderList, OrderListVariables } from "./types/OrderList"; +import { + OrderRefundData, + OrderRefundDataVariables +} from "./types/OrderRefundData"; import { OrderSettings } from "./types/OrderSettings"; import { SearchOrderVariant as SearchOrderVariantType, @@ -254,3 +260,47 @@ export const orderSettingsQuery = gql` export const useOrderSettingsQuery = makeQuery( 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); diff --git a/src/orders/types/FulfillOrder.ts b/src/orders/types/FulfillOrder.ts index 79aaaa24a..6c72a4282 100644 --- a/src/orders/types/FulfillOrder.ts +++ b/src/orders/types/FulfillOrder.ts @@ -56,10 +56,24 @@ export interface FulfillOrder_orderFulfill_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -69,6 +83,7 @@ export interface FulfillOrder_orderFulfill_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderAddNote.ts b/src/orders/types/OrderAddNote.ts index 71cc69285..b8998271a 100644 --- a/src/orders/types/OrderAddNote.ts +++ b/src/orders/types/OrderAddNote.ts @@ -20,10 +20,24 @@ export interface OrderAddNote_orderAddNote_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -33,6 +47,7 @@ export interface OrderAddNote_orderAddNote_order_events { transactionReference: string | null; type: OrderEventsEnum | null; user: OrderAddNote_orderAddNote_order_events_user | null; + lines: (OrderAddNote_orderAddNote_order_events_lines | null)[] | null; } export interface OrderAddNote_orderAddNote_order { diff --git a/src/orders/types/OrderCancel.ts b/src/orders/types/OrderCancel.ts index 7f1c18ca6..48f3ab2ba 100644 --- a/src/orders/types/OrderCancel.ts +++ b/src/orders/types/OrderCancel.ts @@ -54,10 +54,24 @@ export interface OrderCancel_orderCancel_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderCancel_orderCancel_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderCapture.ts b/src/orders/types/OrderCapture.ts index 48ac81ef4..caacb902c 100644 --- a/src/orders/types/OrderCapture.ts +++ b/src/orders/types/OrderCapture.ts @@ -54,10 +54,24 @@ export interface OrderCapture_orderCapture_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderCapture_orderCapture_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderConfirm.ts b/src/orders/types/OrderConfirm.ts index ae7a8e772..cf6a78b3f 100644 --- a/src/orders/types/OrderConfirm.ts +++ b/src/orders/types/OrderConfirm.ts @@ -54,18 +54,34 @@ export interface OrderConfirm_orderConfirm_order_events_user { 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 { __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: OrderConfirm_orderConfirm_order_events_user | null; + lines: (OrderConfirm_orderConfirm_order_events_lines | null)[] | null; } export interface OrderConfirm_orderConfirm_order_fulfillments_lines_orderLine_variant { @@ -326,6 +342,7 @@ export interface OrderConfirm_orderConfirm_order { discount: OrderConfirm_orderConfirm_order_discount | null; invoices: (OrderConfirm_orderConfirm_order_invoices | null)[] | null; channel: OrderConfirm_orderConfirm_order_channel; + isPaid: boolean | null; } export interface OrderConfirm_orderConfirm { diff --git a/src/orders/types/OrderDetails.ts b/src/orders/types/OrderDetails.ts index d9517b580..35d0b6576 100644 --- a/src/orders/types/OrderDetails.ts +++ b/src/orders/types/OrderDetails.ts @@ -48,10 +48,24 @@ export interface OrderDetails_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -61,6 +75,7 @@ export interface OrderDetails_order_events { transactionReference: string | null; type: OrderEventsEnum | null; user: OrderDetails_order_events_user | null; + lines: (OrderDetails_order_events_lines | null)[] | null; } export interface OrderDetails_order_fulfillments_lines_orderLine_variant { diff --git a/src/orders/types/OrderDraftCancel.ts b/src/orders/types/OrderDraftCancel.ts index 1e5345fe5..ca189622d 100644 --- a/src/orders/types/OrderDraftCancel.ts +++ b/src/orders/types/OrderDraftCancel.ts @@ -54,10 +54,24 @@ export interface OrderDraftCancel_draftOrderDelete_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderDraftCancel_draftOrderDelete_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderDraftFinalize.ts b/src/orders/types/OrderDraftFinalize.ts index f6b13f347..f355c8e6c 100644 --- a/src/orders/types/OrderDraftFinalize.ts +++ b/src/orders/types/OrderDraftFinalize.ts @@ -54,10 +54,24 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderDraftFinalize_draftOrderComplete_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderDraftUpdate.ts b/src/orders/types/OrderDraftUpdate.ts index c91035dc1..5ab2b9f4c 100644 --- a/src/orders/types/OrderDraftUpdate.ts +++ b/src/orders/types/OrderDraftUpdate.ts @@ -54,10 +54,24 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderDraftUpdate_draftOrderUpdate_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderFulfillmentCancel.ts b/src/orders/types/OrderFulfillmentCancel.ts index d08750b40..0899a3bd8 100644 --- a/src/orders/types/OrderFulfillmentCancel.ts +++ b/src/orders/types/OrderFulfillmentCancel.ts @@ -54,10 +54,24 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events_user 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderFulfillmentCancel_orderFulfillmentCancel_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderFulfillmentRefundProducts.ts b/src/orders/types/OrderFulfillmentRefundProducts.ts new file mode 100644 index 000000000..58683080f --- /dev/null +++ b/src/orders/types/OrderFulfillmentRefundProducts.ts @@ -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; +} diff --git a/src/orders/types/OrderFulfillmentUpdateTracking.ts b/src/orders/types/OrderFulfillmentUpdateTracking.ts index 970ee7510..513f1fdb4 100644 --- a/src/orders/types/OrderFulfillmentUpdateTracking.ts +++ b/src/orders/types/OrderFulfillmentUpdateTracking.ts @@ -54,10 +54,24 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderFulfillmentUpdateTracking_orderFulfillmentUpdateTracking_o transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderLineDelete.ts b/src/orders/types/OrderLineDelete.ts index ecc6186de..5196b89b8 100644 --- a/src/orders/types/OrderLineDelete.ts +++ b/src/orders/types/OrderLineDelete.ts @@ -54,10 +54,24 @@ export interface OrderLineDelete_draftOrderLineDelete_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderLineDelete_draftOrderLineDelete_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderLineUpdate.ts b/src/orders/types/OrderLineUpdate.ts index 455354484..9e408659b 100644 --- a/src/orders/types/OrderLineUpdate.ts +++ b/src/orders/types/OrderLineUpdate.ts @@ -54,10 +54,24 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderLineUpdate_draftOrderLineUpdate_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderLinesAdd.ts b/src/orders/types/OrderLinesAdd.ts index 2a4821e4f..a28a6b2be 100644 --- a/src/orders/types/OrderLinesAdd.ts +++ b/src/orders/types/OrderLinesAdd.ts @@ -54,10 +54,24 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderLinesAdd_draftOrderLinesCreate_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderMarkAsPaid.ts b/src/orders/types/OrderMarkAsPaid.ts index c79e80526..fc3719d4b 100644 --- a/src/orders/types/OrderMarkAsPaid.ts +++ b/src/orders/types/OrderMarkAsPaid.ts @@ -54,10 +54,24 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderMarkAsPaid_orderMarkAsPaid_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderRefund.ts b/src/orders/types/OrderRefund.ts index 3e509cd48..c47f96a0d 100644 --- a/src/orders/types/OrderRefund.ts +++ b/src/orders/types/OrderRefund.ts @@ -54,10 +54,24 @@ export interface OrderRefund_orderRefund_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderRefund_orderRefund_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderRefundData.ts b/src/orders/types/OrderRefundData.ts new file mode 100644 index 000000000..31c134628 --- /dev/null +++ b/src/orders/types/OrderRefundData.ts @@ -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; +} diff --git a/src/orders/types/OrderUpdate.ts b/src/orders/types/OrderUpdate.ts index 4ad6d1598..b9fb56b5c 100644 --- a/src/orders/types/OrderUpdate.ts +++ b/src/orders/types/OrderUpdate.ts @@ -54,10 +54,24 @@ export interface OrderUpdate_orderUpdate_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderUpdate_orderUpdate_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/types/OrderVoid.ts b/src/orders/types/OrderVoid.ts index a85fd06dd..4c052297d 100644 --- a/src/orders/types/OrderVoid.ts +++ b/src/orders/types/OrderVoid.ts @@ -54,10 +54,24 @@ export interface OrderVoid_orderVoid_order_events_user { 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 { __typename: "OrderEvent"; id: string; amount: number | null; + shippingCostsIncluded: boolean | null; date: any | null; email: string | null; emailType: OrderEventsEmailsEnum | null; @@ -67,6 +81,7 @@ export interface OrderVoid_orderVoid_order_events { transactionReference: string | null; type: OrderEventsEnum | 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 { diff --git a/src/orders/urls.ts b/src/orders/urls.ts index 1d7473e85..1280dcba5 100644 --- a/src/orders/urls.ts +++ b/src/orders/urls.ts @@ -103,7 +103,6 @@ export type OrderUrlDialog = | "edit-shipping-address" | "finalize" | "mark-paid" - | "refund" | "void" | "invoice-send"; export type OrderUrlQueryParams = Dialog & SingleAction; @@ -116,3 +115,7 @@ export const orderFulfillUrl = (id: string) => orderFulfillPath(encodeURIComponent(id)); export const orderSettingsPath = urlJoin(orderSectionUrl, "settings"); + +export const orderRefundPath = (id: string) => urlJoin(orderPath(id), "refund"); +export const orderRefundUrl = (id: string) => + orderRefundPath(encodeURIComponent(id)); diff --git a/src/orders/utils/data.test.ts b/src/orders/utils/data.test.ts new file mode 100644 index 000000000..3330444bb --- /dev/null +++ b/src/orders/utils/data.test.ts @@ -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 = [ + { + 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 = [ + { + 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 = [ + { + 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 = [ + { + 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); + }); +}); diff --git a/src/orders/utils/data.ts b/src/orders/utils/data.ts new file mode 100644 index 000000000..2816e91bc --- /dev/null +++ b/src/orders/utils/data.ts @@ -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 +): 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 +): 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()); +} diff --git a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx index c32a5d450..25067ffea 100644 --- a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx +++ b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx @@ -19,7 +19,6 @@ import { OrderLineDelete } from "../../types/OrderLineDelete"; import { OrderLinesAdd } from "../../types/OrderLinesAdd"; import { OrderLineUpdate } from "../../types/OrderLineUpdate"; import { OrderMarkAsPaid } from "../../types/OrderMarkAsPaid"; -import { OrderRefund } from "../../types/OrderRefund"; import { OrderShippingMethodUpdate } from "../../types/OrderShippingMethodUpdate"; import { OrderUpdate } from "../../types/OrderUpdate"; import { OrderVoid } from "../../types/OrderVoid"; @@ -42,7 +41,6 @@ interface OrderDetailsMessages { handleOrderMarkAsPaid: (data: OrderMarkAsPaid) => void; handleOrderVoid: (data: OrderVoid) => void; handlePaymentCapture: (data: OrderCapture) => void; - handlePaymentRefund: (data: OrderRefund) => void; handleShippingMethodUpdate: (data: OrderShippingMethodUpdate) => void; handleUpdate: (data: OrderUpdate) => void; handleInvoiceGeneratePending: (data: InvoiceRequest) => void; @@ -80,18 +78,6 @@ export const OrderDetailsMessages: React.FC = ({ 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 errs = data.orderMarkAsPaid?.errors; if (errs.length === 0) { @@ -313,7 +299,6 @@ export const OrderDetailsMessages: React.FC = ({ handleOrderMarkAsPaid, handleOrderVoid, handlePaymentCapture, - handlePaymentRefund, handleShippingMethodUpdate, handleUpdate }); diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index 5bb46ff4d..845735818 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -49,6 +49,7 @@ import { orderDraftListUrl, orderFulfillUrl, orderListUrl, + orderRefundUrl, orderUrl, OrderUrlDialog, OrderUrlQueryParams @@ -156,7 +157,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { onOrderCancel={orderMessages.handleOrderCancel} onOrderVoid={orderMessages.handleOrderVoid} onPaymentCapture={orderMessages.handlePaymentCapture} - onPaymentRefund={orderMessages.handlePaymentRefund} onUpdate={orderMessages.handleUpdate} onDraftUpdate={orderMessages.handleDraftUpdate} onShippingMethodUpdate={ @@ -199,7 +199,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { orderLineDelete, orderLineUpdate, orderPaymentCapture, - orderPaymentRefund, orderVoid, orderShippingMethodUpdate, orderUpdate, @@ -281,7 +280,7 @@ export const OrderDetails: React.FC = ({ id, params }) => { } onPaymentCapture={() => openModal("capture")} onPaymentVoid={() => openModal("void")} - onPaymentRefund={() => openModal("refund")} + onPaymentRefund={() => navigate(orderRefundUrl(id))} onProductClick={id => () => navigate(productUrl(id))} onBillingAddressEdit={() => openModal("edit-billing-address") @@ -371,7 +370,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { } initial={order?.total.gross.amount} open={params.action === "capture"} - variant="capture" onClose={closeModal} onSubmit={variables => orderPaymentCapture.mutate({ @@ -380,23 +378,6 @@ export const OrderDetails: React.FC = ({ id, params }) => { }) } /> - - orderPaymentRefund.mutate({ - ...variables, - id - }) - } - /> ({ + 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 = ({ 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 ( + navigate(orderUrl(orderId))} + /> + ); +}; +OrderRefund.displayName = "OrderRefund"; +export default OrderRefund; diff --git a/src/orders/views/OrderRefund/index.ts b/src/orders/views/OrderRefund/index.ts new file mode 100644 index 000000000..23ffe32b5 --- /dev/null +++ b/src/orders/views/OrderRefund/index.ts @@ -0,0 +1,2 @@ +export * from "./OrderRefund"; +export { default } from "./OrderRefund"; diff --git a/src/pages/types/PageCreate.ts b/src/pages/types/PageCreate.ts index abc82deaf..320ba975b 100644 --- a/src/pages/types/PageCreate.ts +++ b/src/pages/types/PageCreate.ts @@ -67,7 +67,6 @@ export interface PageCreate_pageCreate_page_pageType { id: string; name: string; attributes: (PageCreate_pageCreate_page_pageType_attributes | null)[] | null; - message: string | null; } export interface PageCreate_pageCreate_page_metadata { diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index ceebc185b..b2277ea3b 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -10156,12 +10156,12 @@ exports[`Storyshots Generics / Timeline with expansion panels 1`] = ` class="TimelineEvent-dot-id" />
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -12218,12 +12402,6 @@ exports[`Storyshots Orders / OrderPaymentDialog errors 1`] = ` /> `; -exports[`Storyshots Orders / OrderPaymentDialog refund payment 1`] = ` -
-`; - exports[`Storyshots Orders / OrderPaymentVoidDialog default 1`] = `
+ + + Refunded amount + + + -$234.93 + + @@ -97446,6 +97634,160 @@ exports[`Storyshots Views / Orders / Order details cancelled 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -98475,6 +98817,16 @@ exports[`Storyshots Views / Orders / Order details default 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -99101,6 +99453,160 @@ exports[`Storyshots Views / Orders / Order details default 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -100160,6 +100666,16 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -100786,6 +101302,160 @@ exports[`Storyshots Views / Orders / Order details fulfilled 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -101313,28 +101983,6 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = ` - - -
- Fulfilled from: -
- ... -
-
-
- -
@@ -101495,6 +102143,20 @@ exports[`Storyshots Views / Orders / Order details loading 1`] = ` + + + Refunded amount + + + + ‌ + + + @@ -102579,6 +103241,16 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -103205,6 +103877,160 @@ exports[`Storyshots Views / Orders / Order details no customer note 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -104264,6 +105090,16 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -104890,6 +105726,160 @@ exports[`Storyshots Views / Orders / Order details no payment 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -105949,6 +106939,16 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -106575,6 +107575,160 @@ exports[`Storyshots Views / Orders / Order details no shipping address 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -107634,6 +108788,16 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -108260,6 +109424,160 @@ exports[`Storyshots Views / Orders / Order details partially fulfilled 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -109319,6 +110637,16 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -109945,6 +111273,160 @@ exports[`Storyshots Views / Orders / Order details payment confirmed 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -111004,6 +112486,16 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -111630,6 +113122,160 @@ exports[`Storyshots Views / Orders / Order details payment error 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -112689,6 +114335,16 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -113315,6 +114971,160 @@ exports[`Storyshots Views / Orders / Order details pending payment 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -114374,6 +116184,16 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -115000,6 +116820,160 @@ exports[`Storyshots Views / Orders / Order details refunded payment 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -116059,6 +118033,16 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -116685,6 +118669,160 @@ exports[`Storyshots Views / Orders / Order details rejected payment 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -117744,6 +119882,16 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = ` $0.00 + + + Refunded amount + + + -$234.93 + + @@ -118370,6 +120518,160 @@ exports[`Storyshots Views / Orders / Order details unfulfilled 1`] = `
+
+ +
+ +
+
+
+
+
+
+
+ Products refunded +
+ + + + + + + + + + + + + +
+ Milk + +
+ Cow's milk +
+
+
+ qty: 4 +
+
+ Milk + +
+ Goat's milk +
+
+
+ qty: 4 +
+
+
+ Refunded amount +
+
+ Shipment was refunded +
+
+
+
+
+
+
+
+
@@ -123210,6 +125512,1887 @@ exports[`Storyshots Views / Orders / Order settings loading 1`] = `
`; +exports[`Storyshots Views / Orders / Refund order loading 1`] = ` +
+
+
+
+
+ Order no. - Refund +
+
+
+
+
+
+
+
+
+ + Refund Order + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Refunded Amount + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + +
+ Authorized Amount + + + ‌ + +
+ Previously refunded + + + ‌ + +
+ Max Refund + + + ‌ + +
+
+
+ +
+ +
+ Refunded items can’t be fulfilled +
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Orders / Refund order miscellaneous 1`] = ` +
+
+
+
+
+ Order no. 22 - Refund +
+
+
+
+
+
+
+
+
+ + Refund Order + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + Refunded Amount + +
+
+
+
+
+ + + + + + + + + + + + + + + +
+ Authorized Amount + + $744.38 +
+ Previously refunded + + -$100.00 +
+ Max Refund + + $644.38 +
+
+ +
+ +
+
+ USD +
+
+ +
+
+ +
+ Refunded items can’t be fulfilled +
+
+
+
+
+
+ +
+`; + +exports[`Storyshots Views / Orders / Refund order products 1`] = ` +
+
+
+
+
+ Order no. 22 - Refund +
+
+
+
+
+
+
+
+
+ + Refund Order + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+ + Unfulfilled Products + +
+
+
+
+
+
+ Unfulfilled products will be restocked +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Product + + Price + + Refunded Qty + + Total +
+
+
+ +
+
+ Milk +
+
+
+ $26.02 + +
+
+ +
+ / 16 +
+ +
+
+
+ $0.00 +
+
+
+ +
+
+ Coffee +
+
+
+ $10.00 + +
+
+ +
+ / 8 +
+ +
+
+
+ $0.00 +
+
+
+
+
+ + Fulfillment +
+ #22-1 +
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Product + + Price + + Refunded Qty + + Total +
+
+
+ +
+
+ Milk +
+
+
+ $26.02 + +
+
+ +
+ / 1 +
+ +
+
+
+ $0.00 +
+
+
+ +
+
+ Coffee +
+
+
+ $10.00 + +
+
+ +
+ / 1 +
+ +
+
+
+ $0.00 +
+
+
+
+
+ + Fulfillment +
+ #22-2 +
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Product + + Price + + Refunded Qty + + Total +
+
+
+ +
+
+ Milk +
+
+
+ $26.02 + +
+
+ +
+ / 2 +
+ +
+
+
+ $0.00 +
+
+
+ +
+
+ Coffee +
+
+
+ $10.00 + +
+
+ +
+ / 4 +
+ +
+
+
+ $0.00 +
+
+
+
+
+
+ + Refunded Amount + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Authorized Amount + + $744.38 +
+ Selected products value + + $0.00 +
+ Previously refunded + + -$100.00 +
+ Max Refund + + $644.38 +
+ Refund total amount + + $0.00 +
+
+
+ +
+ +
+ Refunded items can’t be fulfilled +
+
+
+
+
+
+ +
+`; + exports[`Storyshots Views / Page types / Create page type default 1`] = `
( - + )); diff --git a/src/storybook/stories/orders/OrderPaymentDialog.tsx b/src/storybook/stories/orders/OrderPaymentDialog.tsx index 2462aa193..5c5cbdfd6 100644 --- a/src/storybook/stories/orders/OrderPaymentDialog.tsx +++ b/src/storybook/stories/orders/OrderPaymentDialog.tsx @@ -13,23 +13,19 @@ const props: OrderPaymentDialogProps = { initial: 0, onClose: () => undefined, onSubmit: () => undefined, - open: true, - variant: "capture" + open: true }; storiesOf("Orders / OrderPaymentDialog", module) .addDecorator(Decorator) .add("capture payment", () => ) - .add("refund payment", () => ( - - )) .add("errors", () => ( )); diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 6e80d089a..0b4f847a3 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -17,6 +17,7 @@ export enum AccountErrorCode { DELETE_SUPERUSER_ACCOUNT = "DELETE_SUPERUSER_ACCOUNT", DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", GRAPHQL_ERROR = "GRAPHQL_ERROR", + INACTIVE = "INACTIVE", INVALID = "INVALID", INVALID_CREDENTIALS = "INVALID_CREDENTIALS", INVALID_PASSWORD = "INVALID_PASSWORD", @@ -454,6 +455,7 @@ export enum FileTypesEnum { export enum FulfillmentStatus { CANCELED = "CANCELED", FULFILLED = "FULFILLED", + REFUNDED = "REFUNDED", } export enum InvoiceErrorCode { @@ -567,6 +569,7 @@ export enum OrderErrorCode { CANNOT_CANCEL_ORDER = "CANNOT_CANCEL_ORDER", CANNOT_DELETE = "CANNOT_DELETE", CANNOT_REFUND = "CANNOT_REFUND", + CANNOT_REFUND_FULFILLMENT_LINE = "CANNOT_REFUND_FULFILLMENT_LINE", CAPTURE_INACTIVE_PAYMENT = "CAPTURE_INACTIVE_PAYMENT", CHANNEL_INACTIVE = "CHANNEL_INACTIVE", DUPLICATED_INPUT_ITEM = "DUPLICATED_INPUT_ITEM", @@ -574,6 +577,7 @@ export enum OrderErrorCode { GRAPHQL_ERROR = "GRAPHQL_ERROR", INSUFFICIENT_STOCK = "INSUFFICIENT_STOCK", INVALID = "INVALID", + INVALID_REFUND_QUANTITY = "INVALID_REFUND_QUANTITY", NOT_AVAILABLE_IN_CHANNEL = "NOT_AVAILABLE_IN_CHANNEL", NOT_EDITABLE = "NOT_EDITABLE", NOT_FOUND = "NOT_FOUND", @@ -613,6 +617,7 @@ export enum OrderEventsEnum { EXTERNAL_SERVICE_NOTIFICATION = "EXTERNAL_SERVICE_NOTIFICATION", FULFILLMENT_CANCELED = "FULFILLMENT_CANCELED", FULFILLMENT_FULFILLED_ITEMS = "FULFILLMENT_FULFILLED_ITEMS", + FULFILLMENT_REFUNDED = "FULFILLMENT_REFUNDED", FULFILLMENT_RESTOCKED_ITEMS = "FULFILLMENT_RESTOCKED_ITEMS", INVOICE_GENERATED = "INVOICE_GENERATED", INVOICE_REQUESTED = "INVOICE_REQUESTED", @@ -1351,6 +1356,24 @@ export interface OrderLineInput { 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 { automaticallyConfirmAllNewOrders: boolean; }