diff --git a/src/containers/BackgroundTasks/BackgroundTasksProvider.tsx b/src/containers/BackgroundTasks/BackgroundTasksProvider.tsx index b6948caef..600addac1 100644 --- a/src/containers/BackgroundTasks/BackgroundTasksProvider.tsx +++ b/src/containers/BackgroundTasks/BackgroundTasksProvider.tsx @@ -1,21 +1,21 @@ import { IMessageContext } from "@saleor/components/messages"; import useNotifier from "@saleor/hooks/useNotifier"; +import { checkOrderInvoicesStatus } from "@saleor/orders/queries"; import ApolloClient from "apollo-client"; import React from "react"; import { useApolloClient } from "react-apollo"; import { IntlShape, useIntl } from "react-intl"; import BackgroundTasksContext from "./context"; -import { handleTask, queueCustom } from "./tasks"; +import { handleTask, queueCustom, queueInvoiceGenerate } from "./tasks"; import { QueuedTask, Task, TaskData, TaskStatus } from "./types"; export const backgroundTasksRefreshTime = 15 * 1000; -// TODO: Remove underscores when working on #575 or similar PR export function useBackgroundTasks( - _apolloClient: ApolloClient, - _notify: IMessageContext, - _intl: IntlShape + apolloClient: ApolloClient, + notify: IMessageContext, + intl: IntlShape ) { const idCounter = React.useRef(0); const tasks = React.useRef([]); @@ -64,6 +64,23 @@ export function useBackgroundTasks( case Task.CUSTOM: queueCustom(idCounter.current, tasks, data); break; + case Task.INVOICE_GENERATE: + queueInvoiceGenerate( + idCounter.current, + data.generateInvoice, + tasks, + () => + apolloClient.query({ + fetchPolicy: "network-only", + query: checkOrderInvoicesStatus, + variables: { + id: data.generateInvoice.orderId + } + }), + notify, + intl + ); + break; } return idCounter.current; diff --git a/src/containers/BackgroundTasks/tasks.ts b/src/containers/BackgroundTasks/tasks.ts index 8c60479ff..73efc35fa 100644 --- a/src/containers/BackgroundTasks/tasks.ts +++ b/src/containers/BackgroundTasks/tasks.ts @@ -1,4 +1,31 @@ -import { QueuedTask, TaskData, TaskStatus } from "./types"; +import { IMessageContext } from "@saleor/components/messages"; +import { commonMessages } from "@saleor/intl"; +import { CheckOrderInvoicesStatus } from "@saleor/orders/types/CheckOrderInvoicesStatus"; +import { JobStatusEnum } from "@saleor/types/globalTypes"; +import { ApolloQueryResult } from "apollo-client"; +import { defineMessages, IntlShape } from "react-intl"; + +import { + InvoiceGenerateParams, + QueuedTask, + TaskData, + TaskStatus +} from "./types"; + +export const messages = defineMessages({ + invoiceGenerateFinishedText: { + defaultMessage: + "Requested Invoice was generated. It was added to the top of the invoice list on this view. Enjoy!" + }, + invoiceGenerateFinishedTitle: { + defaultMessage: "Invoice Generated", + description: "invoice generating has finished, header" + }, + invoiceGenerationFailedTitle: { + defaultMessage: "Invoice Generation", + description: "dialog header, title" + } +}); export async function handleTask(task: QueuedTask): Promise { let status = TaskStatus.PENDING; @@ -41,3 +68,46 @@ export function queueCustom( } ]; } + +export function queueInvoiceGenerate( + id: number, + generateInvoice: InvoiceGenerateParams, + tasks: React.MutableRefObject, + fetch: () => Promise>, + notify: IMessageContext, + intl: IntlShape +) { + if (!generateInvoice) { + throw new Error("generateInvoice is required when creating custom task"); + } + tasks.current = [ + ...tasks.current, + { + handle: async () => { + const result = await fetch(); + const status = result.data.order.invoices.find( + invoice => invoice.id === generateInvoice.invoiceId + ).status; + + return status === JobStatusEnum.SUCCESS + ? TaskStatus.SUCCESS + : status === JobStatusEnum.PENDING + ? TaskStatus.PENDING + : TaskStatus.FAILURE; + }, + id, + onCompleted: data => + data.status === TaskStatus.SUCCESS + ? notify({ + text: intl.formatMessage(messages.invoiceGenerateFinishedText), + title: intl.formatMessage(messages.invoiceGenerateFinishedTitle) + }) + : notify({ + text: intl.formatMessage(commonMessages.somethingWentWrong), + title: intl.formatMessage(messages.invoiceGenerationFailedTitle) + }), + onError: handleError, + status: TaskStatus.PENDING + } + ]; +} diff --git a/src/containers/BackgroundTasks/types.ts b/src/containers/BackgroundTasks/types.ts index 0eabb6f7c..b927a0554 100644 --- a/src/containers/BackgroundTasks/types.ts +++ b/src/containers/BackgroundTasks/types.ts @@ -1,11 +1,16 @@ export enum Task { - CUSTOM + CUSTOM, + INVOICE_GENERATE } export enum TaskStatus { FAILURE, PENDING, SUCCESS } +export interface InvoiceGenerateParams { + orderId: string; + invoiceId: string; +} export interface OnCompletedTaskData { status: TaskStatus; @@ -21,6 +26,7 @@ export interface QueuedTask { } export interface TaskData { + generateInvoice?: InvoiceGenerateParams; id?: string; handle?: () => Promise; onCompleted?: OnCompletedTaskFn; diff --git a/src/orders/queries.ts b/src/orders/queries.ts index 334c15538..521ce36da 100644 --- a/src/orders/queries.ts +++ b/src/orders/queries.ts @@ -228,3 +228,15 @@ export const useOrderFulfillData = makeQuery< OrderFulfillData, OrderFulfillDataVariables >(orderFulfillData); + +export const checkOrderInvoicesStatus = gql` + ${fragmentInvoice} + query CheckOrderInvoicesStatus($id: ID!) { + order(id: $id) { + id + invoices { + ...InvoiceFragment + } + } + } +`; diff --git a/src/orders/types/CheckOrderInvoicesStatus.ts b/src/orders/types/CheckOrderInvoicesStatus.ts new file mode 100644 index 000000000..85dc7962e --- /dev/null +++ b/src/orders/types/CheckOrderInvoicesStatus.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { JobStatusEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL query operation: CheckOrderInvoicesStatus +// ==================================================== + +export interface CheckOrderInvoicesStatus_order_invoices { + __typename: "Invoice"; + id: string; + number: string | null; + createdAt: any; + url: string | null; + status: JobStatusEnum; +} + +export interface CheckOrderInvoicesStatus_order { + __typename: "Order"; + id: string; + invoices: (CheckOrderInvoicesStatus_order_invoices | null)[] | null; +} + +export interface CheckOrderInvoicesStatus { + order: CheckOrderInvoicesStatus_order | null; +} + +export interface CheckOrderInvoicesStatusVariables { + id: string; +} diff --git a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx index 55617e370..4d59363db 100644 --- a/src/orders/views/OrderDetails/OrderDetailsMessages.tsx +++ b/src/orders/views/OrderDetails/OrderDetailsMessages.tsx @@ -1,3 +1,4 @@ +import { messages } from "@saleor/containers/BackgroundTasks/tasks"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; @@ -45,6 +46,7 @@ interface OrderDetailsMessages { handleShippingMethodUpdate: (data: OrderShippingMethodUpdate) => void; handleUpdate: (data: OrderUpdate) => void; handleInvoiceGeneratePending: (data: InvoiceRequest) => void; + handleInvoiceGenerateFinished: (data: InvoiceRequest) => void; handleInvoiceSend: (data: InvoiceEmailSend) => void; }) => React.ReactElement; id: string; @@ -270,6 +272,16 @@ export const OrderDetailsMessages: React.FC = ({ closeModal(); } }; + const handleInvoiceGenerateFinished = (data: InvoiceRequest) => { + const errs = data.invoiceRequest?.errors; + if (errs.length === 0) { + pushMessage({ + text: intl.formatMessage(messages.invoiceGenerateFinishedText), + title: intl.formatMessage(messages.invoiceGenerateFinishedTitle) + }); + closeModal(); + } + }; const handleInvoiceSend = (data: InvoiceEmailSend) => { const errs = data.invoiceSendEmail?.errors; if (errs.length === 0) { @@ -286,6 +298,7 @@ export const OrderDetailsMessages: React.FC = ({ handleDraftCancel, handleDraftFinalize, handleDraftUpdate, + handleInvoiceGenerateFinished, handleInvoiceGeneratePending, handleInvoiceSend, handleNoteAdd, diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index c6c8efe00..4b446a1d4 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -1,10 +1,13 @@ import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; +import { Task } from "@saleor/containers/BackgroundTasks/types"; +import useBackgroundTask from "@saleor/hooks/useBackgroundTask"; import useNavigator from "@saleor/hooks/useNavigator"; import useUser from "@saleor/hooks/useUser"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; +import { InvoiceRequest } from "@saleor/orders/types/InvoiceRequest"; import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import { useWarehouseList } from "@saleor/warehouses/queries"; @@ -18,7 +21,11 @@ import { transformAddressToForm, } from "../../../misc"; import { productUrl } from "../../../products/urls"; -import { FulfillmentStatus, OrderStatus } from "../../../types/globalTypes"; +import { + FulfillmentStatus, + JobStatusEnum, + OrderStatus, +} from "../../../types/globalTypes"; import OrderAddressEditDialog from "../../components/OrderAddressEditDialog"; import OrderCancelDialog from "../../components/OrderCancelDialog"; import OrderDetailsPage from "../../components/OrderDetailsPage"; @@ -104,6 +111,7 @@ export const OrderDetails: React.FC = ({ id, params }) => { first: 30, }, }); + const { queue } = useBackgroundTask(); const intl = useIntl(); const [openModal, closeModal] = createDialogActionHandlers< @@ -149,7 +157,21 @@ export const OrderDetails: React.FC = ({ id, params }) => { onDraftFinalize={orderMessages.handleDraftFinalize} onDraftCancel={orderMessages.handleDraftCancel} onOrderMarkAsPaid={orderMessages.handleOrderMarkAsPaid} - onInvoiceRequest={orderMessages.handleInvoiceGeneratePending} + onInvoiceRequest={(data: InvoiceRequest) => { + if ( + data.invoiceRequest.invoice.status === JobStatusEnum.SUCCESS + ) { + orderMessages.handleInvoiceGenerateFinished(data); + } else { + orderMessages.handleInvoiceGeneratePending(data); + queue(Task.INVOICE_GENERATE, { + params: { + invoiceId: data.invoiceRequest.invoice.id, + orderId: id, + }, + }); + } + }} onInvoiceSend={orderMessages.handleInvoiceSend} > {({