From ff14720e23711bb4c087715800c6ba746127973b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=BBegle=C5=84?= Date: Thu, 23 Dec 2021 13:42:10 +0100 Subject: [PATCH] Display error if no invoicing plugin is active (#1709) * Add error message if no invoice plugin installed * Fix types * Update messages --- locale/defaultMessages.json | 8 +++++ schema.graphql | 34 ++++++++----------- src/orders/views/OrderDetails/index.tsx | 23 ++++++++++++- src/types/globalTypes.ts | 14 ++++---- .../WebhookDetailsPage/WebhookDetailsPage.tsx | 12 +++---- .../WebhookEvents/WebhookEvents.tsx | 8 ++--- src/webhooks/handlers.ts | 8 ++--- src/webhooks/types/WebhookDetails.ts | 6 ++-- src/webhooks/utils.ts | 18 +++++----- src/webhooks/views/WebhooksCreate.tsx | 6 ++-- src/webhooks/views/WebhooksDetails.tsx | 6 ++-- 11 files changed, 84 insertions(+), 59 deletions(-) diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index 8e50d2619..d41afdcb8 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -5180,12 +5180,20 @@ "src_dot_orders_dot_views_dot_OrderDetails_dot_4085755992": { "string": "Invoice email sent" }, + "src_dot_orders_dot_views_dot_OrderDetails_dot_4207717971": { + "context": "snackbar title", + "string": "Could not generate invoice" + }, "src_dot_orders_dot_views_dot_OrderDetails_dot_55607988": { "string": "Invoice is Generating" }, "src_dot_orders_dot_views_dot_OrderDetails_dot_617145655": { "string": "Shipping method successfully updated" }, + "src_dot_orders_dot_views_dot_OrderDetails_dot_811176136": { + "context": "error message", + "string": "No invoice plugin installed" + }, "src_dot_orders_dot_views_dot_OrderDetails_dot_927945225": { "string": "Fulfillment successfully cancelled" }, diff --git a/schema.graphql b/schema.graphql index 3d3a008bc..b3024ae56 100644 --- a/schema.graphql +++ b/schema.graphql @@ -464,8 +464,6 @@ type AssignedVariantAttribute { type Attribute implements Node & ObjectWithMetadata { id: ID! - productTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! - productVariantTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! privateMetadata: [MetadataItem]! metadata: [MetadataItem]! inputType: AttributeInputTypeEnum @@ -483,6 +481,8 @@ type Attribute implements Node & ObjectWithMetadata { translation(languageCode: LanguageCodeEnum!): AttributeTranslation storefrontSearchPosition: Int! withChoices: Boolean! + productTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! + productVariantTypes(before: String, after: String, first: Int, last: Int): ProductTypeCountableConnection! } type AttributeBulkDelete { @@ -2611,6 +2611,7 @@ enum InvoiceErrorCode { NUMBER_NOT_SET NOT_FOUND INVALID_STATUS + NO_INVOICE_PLUGIN } type InvoiceRequest { @@ -4378,6 +4379,7 @@ input OrderFilterInput { channels: [ID] isClickAndCollect: Boolean isPreorder: Boolean + ids: [ID] } type OrderFulfill { @@ -5899,7 +5901,7 @@ input PublishableChannelListingInput { type Query { webhook(id: ID!): Webhook - webhookEvents: [WebhookEvent] + webhookEvents: [WebhookEvent] @deprecated(reason: "This field will be removed in Saleor 4.0. Use `WebhookEventTypeAsyncEnum` and `WebhookEventTypeSyncEnum` to get available event types.") webhookSamplePayload(eventType: WebhookSampleEventTypeEnum!): JSONString warehouse(id: ID!): Warehouse warehouses(filter: WarehouseFilterInput, sortBy: WarehouseSortingInput, before: String, after: String, first: Int, last: Int): WarehouseCountableConnection @@ -7148,7 +7150,6 @@ type Warehouse implements Node & ObjectWithMetadata { id: ID! name: String! slug: String! - shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection! address: Address! email: String! isPrivate: Boolean! @@ -7156,6 +7157,7 @@ type Warehouse implements Node & ObjectWithMetadata { metadata: [MetadataItem]! companyName: String! @deprecated(reason: "This field will be removed in Saleor 4.0. Use `Address.companyName` instead.") clickAndCollectOption: WarehouseClickAndCollectOptionEnum! + shippingZones(before: String, after: String, first: Int, last: Int): ShippingZoneCountableConnection! } enum WarehouseClickAndCollectOptionEnum { @@ -7275,8 +7277,8 @@ input WebhookCreateInput { name: String targetUrl: String events: [WebhookEventTypeEnum] - asyncEvents: [WebhookEventTypeAsync!] - syncEvents: [WebhookEventTypeSync!] + asyncEvents: [WebhookEventTypeAsyncEnum!] + syncEvents: [WebhookEventTypeSyncEnum!] app: ID isActive: Boolean secretKey: String @@ -7308,16 +7310,16 @@ type WebhookEvent { } type WebhookEventAsync { - eventType: WebhookEventTypeAsync! + eventType: WebhookEventTypeAsyncEnum! name: String! } type WebhookEventSync { - eventType: WebhookEventTypeSync! + eventType: WebhookEventTypeSyncEnum! name: String! } -enum WebhookEventTypeAsync { +enum WebhookEventTypeAsyncEnum { ANY_EVENTS ORDER_CREATED ORDER_CONFIRMED @@ -7403,7 +7405,7 @@ enum WebhookEventTypeEnum { TRANSLATION_UPDATED } -enum WebhookEventTypeSync { +enum WebhookEventTypeSyncEnum { PAYMENT_AUTHORIZE PAYMENT_CAPTURE PAYMENT_CONFIRM @@ -7448,14 +7450,6 @@ enum WebhookSampleEventTypeEnum { PAGE_CREATED PAGE_UPDATED PAGE_DELETED - PAYMENT_AUTHORIZE - PAYMENT_CAPTURE - PAYMENT_CONFIRM - PAYMENT_LIST_GATEWAYS - PAYMENT_PROCESS - PAYMENT_REFUND - PAYMENT_VOID - SHIPPING_LIST_METHODS_FOR_CHECKOUT TRANSLATION_CREATED TRANSLATION_UPDATED } @@ -7470,8 +7464,8 @@ input WebhookUpdateInput { name: String targetUrl: String events: [WebhookEventTypeEnum] - asyncEvents: [WebhookEventTypeAsync!] - syncEvents: [WebhookEventTypeSync!] + asyncEvents: [WebhookEventTypeAsyncEnum!] + syncEvents: [WebhookEventTypeSyncEnum!] app: ID isActive: Boolean secretKey: String diff --git a/src/orders/views/OrderDetails/index.tsx b/src/orders/views/OrderDetails/index.tsx index 808b3d0b1..d25de4bbe 100644 --- a/src/orders/views/OrderDetails/index.tsx +++ b/src/orders/views/OrderDetails/index.tsx @@ -17,7 +17,11 @@ import { import React from "react"; import { useIntl } from "react-intl"; -import { JobStatusEnum, OrderStatus } from "../../../types/globalTypes"; +import { + InvoiceErrorCode, + JobStatusEnum, + OrderStatus +} from "../../../types/globalTypes"; import OrderOperations from "../../containers/OrderOperations"; import { TypedOrderDetailsQuery } from "../../queries"; import { @@ -138,6 +142,23 @@ export const OrderDetails: React.FC = ({ id, params }) => { onDraftCancel={orderMessages.handleDraftCancel} onOrderMarkAsPaid={orderMessages.handleOrderMarkAsPaid} onInvoiceRequest={(data: InvoiceRequest) => { + if ( + data.invoiceRequest.errors.some( + err => err.code === InvoiceErrorCode.NO_INVOICE_PLUGIN + ) + ) { + notify({ + title: intl.formatMessage({ + defaultMessage: "Could not generate invoice", + description: "snackbar title" + }), + text: intl.formatMessage({ + defaultMessage: "No invoice plugin installed", + description: "error message" + }), + status: "error" + }); + } if ( data.invoiceRequest.invoice.status === JobStatusEnum.SUCCESS ) { diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index d652288fe..7ca53f72c 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -525,6 +525,7 @@ export enum InvoiceErrorCode { INVALID_STATUS = "INVALID_STATUS", NOT_FOUND = "NOT_FOUND", NOT_READY = "NOT_READY", + NO_INVOICE_PLUGIN = "NO_INVOICE_PLUGIN", NUMBER_NOT_SET = "NUMBER_NOT_SET", REQUIRED = "REQUIRED", URL_NOT_SET = "URL_NOT_SET", @@ -1817,7 +1818,7 @@ export enum WebhookErrorCode { UNIQUE = "UNIQUE", } -export enum WebhookEventTypeAsync { +export enum WebhookEventTypeAsyncEnum { ANY_EVENTS = "ANY_EVENTS", CHECKOUT_CREATED = "CHECKOUT_CREATED", CHECKOUT_UPDATED = "CHECKOUT_UPDATED", @@ -1903,7 +1904,7 @@ export enum WebhookEventTypeEnum { TRANSLATION_UPDATED = "TRANSLATION_UPDATED", } -export enum WebhookEventTypeSync { +export enum WebhookEventTypeSyncEnum { PAYMENT_AUTHORIZE = "PAYMENT_AUTHORIZE", PAYMENT_CAPTURE = "PAYMENT_CAPTURE", PAYMENT_CONFIRM = "PAYMENT_CONFIRM", @@ -2375,6 +2376,7 @@ export interface OrderFilterInput { channels?: (string | null)[] | null; isClickAndCollect?: boolean | null; isPreorder?: boolean | null; + ids?: (string | null)[] | null; } export interface OrderFulfillInput { @@ -3013,8 +3015,8 @@ export interface WebhookCreateInput { name?: string | null; targetUrl?: string | null; events?: (WebhookEventTypeEnum | null)[] | null; - asyncEvents?: WebhookEventTypeAsync[] | null; - syncEvents?: WebhookEventTypeSync[] | null; + asyncEvents?: WebhookEventTypeAsyncEnum[] | null; + syncEvents?: WebhookEventTypeSyncEnum[] | null; app?: string | null; isActive?: boolean | null; secretKey?: string | null; @@ -3024,8 +3026,8 @@ export interface WebhookUpdateInput { name?: string | null; targetUrl?: string | null; events?: (WebhookEventTypeEnum | null)[] | null; - asyncEvents?: WebhookEventTypeAsync[] | null; - syncEvents?: WebhookEventTypeSync[] | null; + asyncEvents?: WebhookEventTypeAsyncEnum[] | null; + syncEvents?: WebhookEventTypeSyncEnum[] | null; app?: string | null; isActive?: boolean | null; secretKey?: string | null; diff --git a/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx b/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx index 2ecfea84a..c131f0488 100644 --- a/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx +++ b/src/webhooks/components/WebhookDetailsPage/WebhookDetailsPage.tsx @@ -8,8 +8,8 @@ import Savebar from "@saleor/components/Savebar"; import { WebhookErrorFragment } from "@saleor/fragments/types/WebhookErrorFragment"; import { Backlink } from "@saleor/macaw-ui"; import { - WebhookEventTypeAsync, - WebhookEventTypeSync + WebhookEventTypeAsyncEnum, + WebhookEventTypeSyncEnum } from "@saleor/types/globalTypes"; import WebhookEvents from "@saleor/webhooks/components/WebhookEvents"; import WebhookInfo from "@saleor/webhooks/components/WebhookInfo"; @@ -29,8 +29,8 @@ import { useIntl } from "react-intl"; import { getHeaderTitle } from "./messages"; export interface FormData { - syncEvents: WebhookEventTypeSync[]; - asyncEvents: WebhookEventTypeAsync[]; + syncEvents: WebhookEventTypeSyncEnum[]; + asyncEvents: WebhookEventTypeAsyncEnum[]; isActive: boolean; name: string; secretKey: string | null; @@ -72,11 +72,11 @@ const WebhookDetailsPage: React.FC = ({ {({ data, hasChanged, submit, change }) => { const syncEventsChoices = disabled ? [] - : mapSyncEventsToChoices(Object.values(WebhookEventTypeSync)); + : mapSyncEventsToChoices(Object.values(WebhookEventTypeSyncEnum)); const asyncEventsChoices = disabled ? [] : mapAsyncEventsToChoices( - Object.values(WebhookEventTypeAsync), + Object.values(WebhookEventTypeAsyncEnum), data.asyncEvents ); diff --git a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx b/src/webhooks/components/WebhookEvents/WebhookEvents.tsx index cfc5c50f7..17551cb0c 100644 --- a/src/webhooks/components/WebhookEvents/WebhookEvents.tsx +++ b/src/webhooks/components/WebhookEvents/WebhookEvents.tsx @@ -7,8 +7,8 @@ import MultiAutocompleteSelectField, { } from "@saleor/components/MultiAutocompleteSelectField"; import { ChangeEvent } from "@saleor/hooks/useForm"; import { - WebhookEventTypeAsync, - WebhookEventTypeSync + WebhookEventTypeAsyncEnum, + WebhookEventTypeSyncEnum } from "@saleor/types/globalTypes"; import { mapAsyncEventsToChoices, @@ -22,8 +22,8 @@ import { messages } from "./messages"; interface WebhookEventsProps { data: { - syncEvents: WebhookEventTypeSync[]; - asyncEvents: WebhookEventTypeAsync[]; + syncEvents: WebhookEventTypeSyncEnum[]; + asyncEvents: WebhookEventTypeAsyncEnum[]; }; syncEventsChoices: MultiAutocompleteChoiceType[]; asyncEventsChoices: MultiAutocompleteChoiceType[]; diff --git a/src/webhooks/handlers.ts b/src/webhooks/handlers.ts index b9c451b9a..96fca3d70 100644 --- a/src/webhooks/handlers.ts +++ b/src/webhooks/handlers.ts @@ -1,7 +1,7 @@ import { ChangeEvent } from "@saleor/hooks/useForm"; import { - WebhookEventTypeAsync, - WebhookEventTypeSync + WebhookEventTypeAsyncEnum, + WebhookEventTypeSyncEnum } from "@saleor/types/globalTypes"; import { toggle } from "@saleor/utils/lists"; @@ -9,7 +9,7 @@ import { filterSelectedAsyncEvents } from "./utils"; export const createSyncEventsSelectHandler = ( change: (event: ChangeEvent, cb?: () => void) => void, - syncEvents: WebhookEventTypeSync[] + syncEvents: WebhookEventTypeSyncEnum[] ) => (event: ChangeEvent) => { const events = toggle(event.target.value, syncEvents, (a, b) => a === b); @@ -23,7 +23,7 @@ export const createSyncEventsSelectHandler = ( export const createAsyncEventsSelectHandler = ( change: (event: ChangeEvent, cb?: () => void) => void, - asyncEvents: WebhookEventTypeAsync[] + asyncEvents: WebhookEventTypeAsyncEnum[] ) => (event: ChangeEvent) => { const events = toggle(event.target.value, asyncEvents, (a, b) => a === b); const filteredEvents = filterSelectedAsyncEvents(events); diff --git a/src/webhooks/types/WebhookDetails.ts b/src/webhooks/types/WebhookDetails.ts index 0dfa3d5ee..2ba9374e7 100644 --- a/src/webhooks/types/WebhookDetails.ts +++ b/src/webhooks/types/WebhookDetails.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { WebhookEventTypeSync, WebhookEventTypeAsync } from "./../../types/globalTypes"; +import { WebhookEventTypeSyncEnum, WebhookEventTypeAsyncEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: WebhookDetails @@ -17,12 +17,12 @@ export interface WebhookDetails_webhook_app { export interface WebhookDetails_webhook_syncEvents { __typename: "WebhookEventSync"; - eventType: WebhookEventTypeSync; + eventType: WebhookEventTypeSyncEnum; } export interface WebhookDetails_webhook_asyncEvents { __typename: "WebhookEventAsync"; - eventType: WebhookEventTypeAsync; + eventType: WebhookEventTypeAsyncEnum; } export interface WebhookDetails_webhook { diff --git a/src/webhooks/utils.ts b/src/webhooks/utils.ts index 13f800659..b59c38ab5 100644 --- a/src/webhooks/utils.ts +++ b/src/webhooks/utils.ts @@ -1,8 +1,8 @@ import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { WebhookFragment } from "@saleor/fragments/types/WebhookFragment"; import { - WebhookEventTypeAsync, - WebhookEventTypeSync + WebhookEventTypeAsyncEnum, + WebhookEventTypeSyncEnum } from "@saleor/types/globalTypes"; export function isUnnamed(webhook: WebhookFragment): boolean { @@ -10,7 +10,7 @@ export function isUnnamed(webhook: WebhookFragment): boolean { } export function mapSyncEventsToChoices( - events: WebhookEventTypeSync[] + events: WebhookEventTypeSyncEnum[] ): MultiAutocompleteChoiceType[] { return events.map(event => ({ label: event, @@ -19,26 +19,26 @@ export function mapSyncEventsToChoices( } export function mapAsyncEventsToChoices( - events: WebhookEventTypeAsync[], - selectedEvents: WebhookEventTypeAsync[] + events: WebhookEventTypeAsyncEnum[], + selectedEvents: WebhookEventTypeAsyncEnum[] ): MultiAutocompleteChoiceType[] { const isAnyAsyncEventSelected = selectedEvents.includes( - WebhookEventTypeAsync.ANY_EVENTS + WebhookEventTypeAsyncEnum.ANY_EVENTS ); return events.map(event => ({ label: event, value: event, disabled: - event !== WebhookEventTypeAsync.ANY_EVENTS && isAnyAsyncEventSelected + event !== WebhookEventTypeAsyncEnum.ANY_EVENTS && isAnyAsyncEventSelected })); } export const filterSelectedAsyncEvents = ( - asyncEvents: WebhookEventTypeAsync[] + asyncEvents: WebhookEventTypeAsyncEnum[] ) => { const anyEvent = asyncEvents.find( - event => event === WebhookEventTypeAsync.ANY_EVENTS + event => event === WebhookEventTypeAsyncEnum.ANY_EVENTS ); if (anyEvent) { return [anyEvent]; diff --git a/src/webhooks/views/WebhooksCreate.tsx b/src/webhooks/views/WebhooksCreate.tsx index 75c71ed99..887858a2f 100644 --- a/src/webhooks/views/WebhooksCreate.tsx +++ b/src/webhooks/views/WebhooksCreate.tsx @@ -4,7 +4,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; -import { WebhookEventTypeAsync } from "@saleor/types/globalTypes"; +import { WebhookEventTypeAsyncEnum } from "@saleor/types/globalTypes"; import React from "react"; import { useIntl } from "react-intl"; @@ -46,9 +46,9 @@ export const WebhooksCreate: React.FC = ({ id }) => { app: id, syncEvents: data.syncEvents, asyncEvents: data.asyncEvents.includes( - WebhookEventTypeAsync.ANY_EVENTS + WebhookEventTypeAsyncEnum.ANY_EVENTS ) - ? [WebhookEventTypeAsync.ANY_EVENTS] + ? [WebhookEventTypeAsyncEnum.ANY_EVENTS] : data.asyncEvents, isActive: data.isActive, name: data.name, diff --git a/src/webhooks/views/WebhooksDetails.tsx b/src/webhooks/views/WebhooksDetails.tsx index d4e2268e4..56fe01977 100644 --- a/src/webhooks/views/WebhooksDetails.tsx +++ b/src/webhooks/views/WebhooksDetails.tsx @@ -4,7 +4,7 @@ import { WindowTitle } from "@saleor/components/WindowTitle"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; -import { WebhookEventTypeAsync } from "@saleor/types/globalTypes"; +import { WebhookEventTypeAsyncEnum } from "@saleor/types/globalTypes"; import { WebhookUpdate } from "@saleor/webhooks/types/WebhookUpdate"; import React from "react"; import { useIntl } from "react-intl"; @@ -71,9 +71,9 @@ export const WebhooksDetails: React.FC = ({ id }) => { input: { syncEvents: data.syncEvents, asyncEvents: data.asyncEvents.includes( - WebhookEventTypeAsync.ANY_EVENTS + WebhookEventTypeAsyncEnum.ANY_EVENTS ) - ? [WebhookEventTypeAsync.ANY_EVENTS] + ? [WebhookEventTypeAsyncEnum.ANY_EVENTS] : data.asyncEvents, isActive: data.isActive, name: data.name,