diff --git a/ditto/base.json b/ditto/base.json index 0e294089e..850711cce 100644 --- a/ditto/base.json +++ b/ditto/base.json @@ -434,6 +434,7 @@ "text_6650b36fc702a4014c87899a": "Netsuite invoice url", "text_6655a88569eed300ee8c4d44": "NetSuite synchronisation successfully triggered.", "text_6650b36fc702a4014c8788fd": "Sync invoice to NetSuite", + "text_665d742ee9853200e3a6be7f": "Sync credit note to NetSuite", "text_62f50d3cc15266f3bd1d83ce": "Infos copied to clipboard", "text_62f50d26c989ab0319688498": "Environment information", "text_62f50d26c989ab031968849a": "Use and share this information if you’re reporting a bug to Lago", diff --git a/src/components/customers/CustomerMainInfos.tsx b/src/components/customers/CustomerMainInfos.tsx index c006f16f9..2e6b6516f 100644 --- a/src/components/customers/CustomerMainInfos.tsx +++ b/src/components/customers/CustomerMainInfos.tsx @@ -103,11 +103,6 @@ gql` name accountId } - - # Only added to satisfy type check in the code bellow - ... on OktaIntegration { - id - } } } } @@ -135,7 +130,11 @@ export const CustomerMainInfos = ({ loading, customer, onEdit }: CustomerMainInf (provider) => provider?.code === customer?.paymentProviderCode, ) - const connectedNetsuiteIntegration = integrationsData?.integrations?.collection?.find( + const allNetsuiteIntegrations = integrationsData?.integrations?.collection.filter( + (i) => i.__typename === 'NetsuiteIntegration', + ) as NetsuiteIntegration[] | undefined + + const connectedNetsuiteIntegration = allNetsuiteIntegrations?.find( (integration) => integration?.id === customer?.netsuiteCustomer?.integrationId, ) as NetsuiteIntegration diff --git a/src/components/customers/addDrawer/ExternalAppsAccordion.tsx b/src/components/customers/addDrawer/ExternalAppsAccordion.tsx index 961f3dfd0..789f67544 100644 --- a/src/components/customers/addDrawer/ExternalAppsAccordion.tsx +++ b/src/components/customers/addDrawer/ExternalAppsAccordion.tsx @@ -32,6 +32,7 @@ import { CreateCustomerInput, CurrencyEnum, IntegrationTypeEnum, + NetsuiteIntegration, ProviderPaymentMethodsEnum, ProviderTypeEnum, UpdateCustomerInput, @@ -95,12 +96,7 @@ gql` integrations(limit: $limit, page: $page, type: $type) { collection { ... on NetsuiteIntegration { - id - code - name - } - # OktaIntegration have to be defined to fix TS linting - ... on OktaIntegration { + __typename id code name @@ -149,9 +145,13 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA (p) => p.code === formikProps.values.paymentProviderCode, ) - const selectedNetsuiteIntegration = integrationsData?.integrations?.collection.find( + const allNetsuiteIntegrations = integrationsData?.integrations?.collection.filter( + (i) => i.__typename === 'NetsuiteIntegration', + ) as NetsuiteIntegration[] | undefined + + const selectedNetsuiteIntegration = allNetsuiteIntegrations?.find( (i) => i.code === formikProps.values.integrationCustomer?.integrationCode, - ) + ) as NetsuiteIntegration const { data: subsidiariesData } = useSubsidiariesListForCustomerCreateEditExternalAppsAccordionQuery({ @@ -183,9 +183,9 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA }, [providersData?.paymentProviders?.collection]) const connectedIntegrationsData: BasicComboBoxData[] | [] = useMemo(() => { - if (!integrationsData?.integrations?.collection.length) return [] + if (!allNetsuiteIntegrations?.length) return [] - return integrationsData?.integrations?.collection.map((integration) => ({ + return allNetsuiteIntegrations?.map((integration) => ({ value: integration.code, label: integration.name, labelNode: ( @@ -200,7 +200,7 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA ), })) - }, [integrationsData?.integrations?.collection]) + }, [allNetsuiteIntegrations]) const connectedIntegrationSubscidiaries: BasicComboBoxData[] | [] = useMemo(() => { if (!subsidiariesData?.integrationSubsidiaries?.collection.length) return [] @@ -257,7 +257,6 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA })} /> - {showPaymentProviderSection && ( )} - )} - {isSyncWithProviderDisabled && (formikProps.values.paymentProvider === ProviderTypeEnum.Gocardless || formikProps.values.paymentProvider === ProviderTypeEnum.Adyen) && ( diff --git a/src/components/settings/integrations/AddNetsuiteDialog.tsx b/src/components/settings/integrations/AddNetsuiteDialog.tsx index 5eded2b27..b6916febf 100644 --- a/src/components/settings/integrations/AddNetsuiteDialog.tsx +++ b/src/components/settings/integrations/AddNetsuiteDialog.tsx @@ -163,7 +163,7 @@ export const AddNetsuiteDialog = forwardRef((_, ref) => { return handleError(res.errors) } } else { - const connectionId = `${componentId.replace(':', '')}-${Date.now()}` + const connectionId = `${componentId.replaceAll(':', '')}-${Date.now()}` const nango = new Nango({ publicKey: nangoPublicKey }) try { diff --git a/src/core/constants/externalUrls.ts b/src/core/constants/externalUrls.ts index d2fe2d1b3..c735c8454 100644 --- a/src/core/constants/externalUrls.ts +++ b/src/core/constants/externalUrls.ts @@ -23,3 +23,9 @@ export const buildNetsuiteInvoiceUrl = ( ) => { return `https://${connectionAccountId}.app.netsuite.com/app/accounting/transactions/custinvc.nl?id=${netsuiteInvoiceId}` } +export const buildNetsuiteCreditNoteUrl = ( + connectionAccountId?: string | null, + netsuiteCreditNoteId?: string | null, +) => { + return `https://${connectionAccountId}.app.netsuite.com/app/accounting/transactions/custcred.nl?id=${netsuiteCreditNoteId}` +} diff --git a/src/generated/graphql.tsx b/src/generated/graphql.tsx index 66efb5046..f7b881d33 100644 --- a/src/generated/graphql.tsx +++ b/src/generated/graphql.tsx @@ -127,6 +127,15 @@ export enum AggregationTypeEnum { WeightedSumAgg = 'weighted_sum_agg' } +export type AnrokIntegration = { + __typename?: 'AnrokIntegration'; + apiKey: Scalars['String']['output']; + code: Scalars['String']['output']; + hasMappingsConfigured?: Maybe; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; +}; + export type AppliedAddOn = { __typename?: 'AppliedAddOn'; addOn: AddOn; @@ -1165,6 +1174,7 @@ export type CreatePlanInput = { export type CreateRecurringTransactionRuleInput = { interval?: InputMaybe; method?: InputMaybe; + startedAt?: InputMaybe; targetOngoingBalance?: InputMaybe; thresholdCredits?: InputMaybe; trigger: RecurringTransactionTriggerEnum; @@ -1199,8 +1209,10 @@ export type CreditNote = { currency: CurrencyEnum; customer: Customer; description?: Maybe; + externalIntegrationId?: Maybe; fileUrl?: Maybe; id: Scalars['ID']['output']; + integrationSyncable: Scalars['Boolean']['output']; invoice?: Maybe; issuingDate: Scalars['ISO8601Date']['output']; items: Array; @@ -2232,7 +2244,7 @@ export type GroupedChargeUsage = { units: Scalars['Float']['output']; }; -export type Integration = NetsuiteIntegration | OktaIntegration; +export type Integration = AnrokIntegration | NetsuiteIntegration | OktaIntegration; export type IntegrationCollection = { __typename?: 'IntegrationCollection'; @@ -2703,6 +2715,8 @@ export type Mutation = { revokeInvite?: Maybe; /** Revoke a membership */ revokeMembership?: Maybe; + /** Sync integration credit note */ + syncIntegrationCreditNote?: Maybe; /** Sync integration invoice */ syncIntegrationInvoice?: Maybe; /** Unassign a coupon from a customer */ @@ -3059,6 +3073,11 @@ export type MutationRevokeMembershipArgs = { }; +export type MutationSyncIntegrationCreditNoteArgs = { + input: SyncIntegrationCreditNoteInput; +}; + + export type MutationSyncIntegrationInvoiceArgs = { input: SyncIntegrationInvoiceInput; }; @@ -3941,6 +3960,7 @@ export type RecurringTransactionRule = { lagoId: Scalars['ID']['output']; method: RecurringTransactionMethodEnum; paidCredits: Scalars['String']['output']; + startedAt?: Maybe; targetOngoingBalance?: Maybe; thresholdCredits?: Maybe; trigger: RecurringTransactionTriggerEnum; @@ -4084,6 +4104,21 @@ export type SubsidiaryCollection = { metadata: CollectionMetadata; }; +/** Autogenerated input type of SyncIntegrationCreditNote */ +export type SyncIntegrationCreditNoteInput = { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + creditNoteId: Scalars['ID']['input']; +}; + +/** Autogenerated return type of SyncIntegrationCreditNote */ +export type SyncIntegrationCreditNotePayload = { + __typename?: 'SyncIntegrationCreditNotePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + creditNoteId?: Maybe; +}; + /** Autogenerated input type of SyncIntegrationInvoice */ export type SyncIntegrationInvoiceInput = { /** A unique identifier for the client performing the mutation. */ @@ -4711,6 +4746,7 @@ export type UpdateRecurringTransactionRuleInput = { lagoId?: InputMaybe; method?: InputMaybe; paidCredits?: InputMaybe; + startedAt?: InputMaybe; targetOngoingBalance?: InputMaybe; thresholdCredits?: InputMaybe; trigger?: InputMaybe; @@ -5112,7 +5148,7 @@ export type IntegrationsListForCustomerMainInfosQueryVariables = Exact<{ }>; -export type IntegrationsListForCustomerMainInfosQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename: 'NetsuiteIntegration', id: string, name: string, accountId?: string | null } | { __typename?: 'OktaIntegration', id: string }> } | null }; +export type IntegrationsListForCustomerMainInfosQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename: 'NetsuiteIntegration', id: string, name: string, accountId?: string | null } | { __typename?: 'OktaIntegration' }> } | null }; export type CustomerAppliedTaxRatesForSettingsFragment = { __typename?: 'Customer', id: string, taxes?: Array<{ __typename?: 'Tax', id: string, name: string, code: string, rate: number, autoGenerated: boolean }> | null }; @@ -5222,7 +5258,7 @@ export type IntegrationsListForCustomerEditExternalAppsAccordionQueryVariables = }>; -export type IntegrationsListForCustomerEditExternalAppsAccordionQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration', id: string, code: string, name: string } | { __typename?: 'OktaIntegration', id: string, code: string, name: string }> } | null }; +export type IntegrationsListForCustomerEditExternalAppsAccordionQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename: 'NetsuiteIntegration', id: string, code: string, name: string } | { __typename?: 'OktaIntegration' }> } | null }; export type SubsidiariesListForCustomerCreateEditExternalAppsAccordionQueryVariables = Exact<{ integrationId?: InputMaybe; @@ -5897,7 +5933,7 @@ export type GetNetsuiteIntegrationsSettingsQueryVariables = Exact<{ }>; -export type GetNetsuiteIntegrationsSettingsQuery = { __typename?: 'Query', integration?: { __typename?: 'NetsuiteIntegration', id: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, code: string, hasMappingsConfigured?: boolean | null, name: string, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; +export type GetNetsuiteIntegrationsSettingsQuery = { __typename?: 'Query', integration?: { __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, code: string, hasMappingsConfigured?: boolean | null, name: string, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; export type NetsuiteMapItemDialogItemFragment = { __typename?: 'IntegrationItem', id: string, externalId: string, externalName?: string | null, externalAccountCode?: string | null, itemType: IntegrationItemTypeEnum }; @@ -6270,7 +6306,7 @@ export type IntegrationsListForCustomerInvoiceDetailsQueryVariables = Exact<{ }>; -export type IntegrationsListForCustomerInvoiceDetailsQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename: 'NetsuiteIntegration', id: string, accountId?: string | null, name: string } | { __typename?: 'OktaIntegration', id: string }> } | null }; +export type IntegrationsListForCustomerInvoiceDetailsQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename: 'NetsuiteIntegration', id: string, accountId?: string | null, name: string } | { __typename?: 'OktaIntegration' }> } | null }; export type DownloadInvoiceMutationVariables = Exact<{ input: DownloadInvoiceInput; @@ -6412,7 +6448,21 @@ export type GetCreditNoteQueryVariables = Exact<{ }>; -export type GetCreditNoteQuery = { __typename?: 'Query', creditNote?: { __typename?: 'CreditNote', id: string, balanceAmountCents: any, canBeVoided: boolean, couponsAdjustmentAmountCents: any, createdAt: any, creditAmountCents: any, creditStatus?: CreditNoteCreditStatusEnum | null, currency: CurrencyEnum, number: string, refundAmountCents: any, refundedAt?: any | null, refundStatus?: CreditNoteRefundStatusEnum | null, subTotalExcludingTaxesAmountCents: any, totalAmountCents: any, customer: { __typename?: 'Customer', id: string, name?: string | null, deletedAt?: any | null, applicableTimezone: TimezoneEnum }, invoice?: { __typename?: 'Invoice', id: string, number: string } | null, appliedTaxes?: Array<{ __typename?: 'CreditNoteAppliedTax', id: string, amountCents: any, baseAmountCents: any, taxRate: number, taxName: string }> | null, items: Array<{ __typename?: 'CreditNoteItem', amountCents: any, amountCurrency: CurrencyEnum, fee: { __typename?: 'Fee', id: string, amountCents: any, eventsCount?: any | null, units: number, feeType: FeeTypesEnum, itemName: string, groupedBy: any, invoiceName?: string | null, appliedTaxes?: Array<{ __typename?: 'FeeAppliedTax', id: string, tax: { __typename?: 'Tax', id: string, rate: number } }> | null, trueUpParentFee?: { __typename?: 'Fee', id: string } | null, charge?: { __typename?: 'Charge', id: string, billableMetric: { __typename?: 'BillableMetric', id: string, name: string, aggregationType: AggregationTypeEnum } } | null, subscription?: { __typename?: 'Subscription', id: string, name?: string | null, plan: { __typename?: 'Plan', id: string, name: string, invoiceDisplayName?: string | null } } | null, chargeFilter?: { __typename?: 'ChargeFilter', invoiceDisplayName?: string | null, values: any } | null } }> } | null }; +export type GetCreditNoteQuery = { __typename?: 'Query', creditNote?: { __typename?: 'CreditNote', id: string, balanceAmountCents: any, canBeVoided: boolean, couponsAdjustmentAmountCents: any, createdAt: any, creditAmountCents: any, creditStatus?: CreditNoteCreditStatusEnum | null, currency: CurrencyEnum, number: string, refundAmountCents: any, refundedAt?: any | null, refundStatus?: CreditNoteRefundStatusEnum | null, subTotalExcludingTaxesAmountCents: any, totalAmountCents: any, integrationSyncable: boolean, externalIntegrationId?: string | null, customer: { __typename?: 'Customer', id: string, name?: string | null, deletedAt?: any | null, applicableTimezone: TimezoneEnum, netsuiteCustomer?: { __typename?: 'NetsuiteCustomer', integrationId?: string | null } | null }, invoice?: { __typename?: 'Invoice', id: string, number: string } | null, appliedTaxes?: Array<{ __typename?: 'CreditNoteAppliedTax', id: string, amountCents: any, baseAmountCents: any, taxRate: number, taxName: string }> | null, items: Array<{ __typename?: 'CreditNoteItem', amountCents: any, amountCurrency: CurrencyEnum, fee: { __typename?: 'Fee', id: string, amountCents: any, eventsCount?: any | null, units: number, feeType: FeeTypesEnum, itemName: string, groupedBy: any, invoiceName?: string | null, appliedTaxes?: Array<{ __typename?: 'FeeAppliedTax', id: string, tax: { __typename?: 'Tax', id: string, rate: number } }> | null, trueUpParentFee?: { __typename?: 'Fee', id: string } | null, charge?: { __typename?: 'Charge', id: string, billableMetric: { __typename?: 'BillableMetric', id: string, name: string, aggregationType: AggregationTypeEnum } } | null, subscription?: { __typename?: 'Subscription', id: string, name?: string | null, plan: { __typename?: 'Plan', id: string, name: string, invoiceDisplayName?: string | null } } | null, chargeFilter?: { __typename?: 'ChargeFilter', invoiceDisplayName?: string | null, values: any } | null } }> } | null }; + +export type IntegrationsListForCreditNoteDetailsQueryVariables = Exact<{ + limit?: InputMaybe; +}>; + + +export type IntegrationsListForCreditNoteDetailsQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename: 'NetsuiteIntegration', id: string, accountId?: string | null, name: string } | { __typename?: 'OktaIntegration' }> } | null }; + +export type SyncIntegrationCreditNoteMutationVariables = Exact<{ + input: SyncIntegrationCreditNoteInput; +}>; + + +export type SyncIntegrationCreditNoteMutation = { __typename?: 'Mutation', syncIntegrationCreditNote?: { __typename?: 'SyncIntegrationCreditNotePayload', creditNoteId?: string | null } | null }; export type CustomerDetailsFragment = { __typename?: 'Customer', id: string, name?: string | null, externalId: string, hasActiveWallet: boolean, currency?: CurrencyEnum | null, hasCreditNotes: boolean, creditNotesCreditsAvailableCount: number, creditNotesBalanceAmountCents: any, applicableTimezone: TimezoneEnum, addressLine1?: string | null, addressLine2?: string | null, canEditAttributes: boolean, city?: string | null, country?: CountryCode | null, email?: string | null, externalSalesforceId?: string | null, legalName?: string | null, legalNumber?: string | null, taxIdentificationNumber?: string | null, paymentProvider?: ProviderTypeEnum | null, phone?: string | null, state?: string | null, timezone?: TimezoneEnum | null, zipcode?: string | null, url?: string | null, paymentProviderCode?: string | null, providerCustomer?: { __typename?: 'ProviderCustomer', id: string, providerCustomerId?: string | null, syncWithProvider?: boolean | null, providerPaymentMethods?: Array | null } | null, netsuiteCustomer?: { __typename?: 'NetsuiteCustomer', id: string, integrationId?: string | null, externalCustomerId?: string | null, integrationCode?: string | null, integrationType?: IntegrationTypeEnum | null, subsidiaryId?: string | null, syncWithProvider?: boolean | null } | null, metadata?: Array<{ __typename?: 'CustomerMetadata', id: string, key: string, value: string, displayInInvoice: boolean }> | null }; @@ -6712,7 +6762,7 @@ export type GetAuthIntegrationsQueryVariables = Exact<{ }>; -export type GetAuthIntegrationsQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, premiumIntegrations: Array } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration' } | { __typename?: 'OktaIntegration', id: string, domain: string, clientId?: string | null, clientSecret?: string | null, organizationName: string, name: string }> } | null }; +export type GetAuthIntegrationsQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, premiumIntegrations: Array } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration' } | { __typename?: 'OktaIntegration', id: string, domain: string, clientId?: string | null, clientSecret?: string | null, organizationName: string, name: string }> } | null }; export type OktaIntegrationDetailsFragment = { __typename?: 'OktaIntegration', id: string, clientId?: string | null, clientSecret?: string | null, code: string, organizationName: string, domain: string, name: string }; @@ -6721,7 +6771,7 @@ export type GetOktaIntegrationQueryVariables = Exact<{ }>; -export type GetOktaIntegrationQuery = { __typename?: 'Query', integration?: { __typename?: 'NetsuiteIntegration' } | { __typename?: 'OktaIntegration', id: string, clientId?: string | null, clientSecret?: string | null, code: string, organizationName: string, domain: string, name: string } | null }; +export type GetOktaIntegrationQuery = { __typename?: 'Query', integration?: { __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration' } | { __typename?: 'OktaIntegration', id: string, clientId?: string | null, clientSecret?: string | null, code: string, organizationName: string, domain: string, name: string } | null }; export type GocardlessIntegrationDetailsFragment = { __typename?: 'GocardlessProvider', id: string, code: string, name: string, successRedirectUrl?: string | null, webhookSecret?: string | null }; @@ -6758,7 +6808,7 @@ export type IntegrationsSettingQueryVariables = Exact<{ }>; -export type IntegrationsSettingQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, euTaxManagement: boolean, country?: CountryCode | null, premiumIntegrations: Array } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string }> } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; +export type IntegrationsSettingQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, euTaxManagement: boolean, country?: CountryCode | null, premiumIntegrations: Array } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string }> } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; export type GetOrganizationSettingsQueryVariables = Exact<{ appliedToOrganization?: InputMaybe; @@ -6808,7 +6858,7 @@ export type GetNetsuiteIntegrationsDetailsQueryVariables = Exact<{ }>; -export type GetNetsuiteIntegrationsDetailsQuery = { __typename?: 'Query', integration?: { __typename?: 'NetsuiteIntegration', id: string, name: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, code: string, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; +export type GetNetsuiteIntegrationsDetailsQuery = { __typename?: 'Query', integration?: { __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string, name: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, code: string, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' }> } | null }; export type NetsuiteIntegrationsFragment = { __typename?: 'NetsuiteIntegration', id: string, name: string, code: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null }; @@ -6818,7 +6868,7 @@ export type GetNetsuiteIntegrationsListQueryVariables = Exact<{ }>; -export type GetNetsuiteIntegrationsListQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'NetsuiteIntegration', id: string, name: string, code: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' }> } | null }; +export type GetNetsuiteIntegrationsListQuery = { __typename?: 'Query', integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration' } | { __typename?: 'NetsuiteIntegration', id: string, name: string, code: string, accountId?: string | null, clientId?: string | null, clientSecret?: string | null, scriptEndpointUrl: string, syncCreditNotes?: boolean | null, syncInvoices?: boolean | null, syncPayments?: boolean | null, syncSalesOrders?: boolean | null } | { __typename?: 'OktaIntegration' }> } | null }; export type OrganizationInformationsFragment = { __typename?: 'CurrentOrganization', id: string, logoUrl?: string | null, name: string, legalName?: string | null, legalNumber?: string | null, taxIdentificationNumber?: string | null, email?: string | null, addressLine1?: string | null, addressLine2?: string | null, zipcode?: string | null, city?: string | null, state?: string | null, country?: CountryCode | null, timezone?: TimezoneEnum | null }; @@ -9946,9 +9996,6 @@ export const IntegrationsListForCustomerMainInfosDocument = gql` name accountId } - ... on OktaIntegration { - id - } } } } @@ -10447,11 +10494,7 @@ export const IntegrationsListForCustomerEditExternalAppsAccordionDocument = gql` integrations(limit: $limit, page: $page, type: $type) { collection { ... on NetsuiteIntegration { - id - code - name - } - ... on OktaIntegration { + __typename id code name @@ -14904,9 +14947,6 @@ export const IntegrationsListForCustomerInvoiceDetailsDocument = gql` id ...NetsuiteIntegrationInfosForInvoiceOverview } - ... on OktaIntegration { - id - } } } } @@ -15717,11 +15757,16 @@ export const GetCreditNoteDocument = gql` refundStatus subTotalExcludingTaxesAmountCents totalAmountCents + integrationSyncable + externalIntegrationId customer { id name deletedAt applicableTimezone + netsuiteCustomer { + integrationId + } } invoice { id @@ -15815,6 +15860,86 @@ export type GetCreditNoteQueryHookResult = ReturnType; export type GetCreditNoteSuspenseQueryHookResult = ReturnType; export type GetCreditNoteQueryResult = Apollo.QueryResult; +export const IntegrationsListForCreditNoteDetailsDocument = gql` + query integrationsListForCreditNoteDetails($limit: Int) { + integrations(limit: $limit) { + collection { + ... on NetsuiteIntegration { + __typename + id + accountId + name + } + } + } +} + `; + +/** + * __useIntegrationsListForCreditNoteDetailsQuery__ + * + * To run a query within a React component, call `useIntegrationsListForCreditNoteDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useIntegrationsListForCreditNoteDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useIntegrationsListForCreditNoteDetailsQuery({ + * variables: { + * limit: // value for 'limit' + * }, + * }); + */ +export function useIntegrationsListForCreditNoteDetailsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(IntegrationsListForCreditNoteDetailsDocument, options); + } +export function useIntegrationsListForCreditNoteDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(IntegrationsListForCreditNoteDetailsDocument, options); + } +export function useIntegrationsListForCreditNoteDetailsSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(IntegrationsListForCreditNoteDetailsDocument, options); + } +export type IntegrationsListForCreditNoteDetailsQueryHookResult = ReturnType; +export type IntegrationsListForCreditNoteDetailsLazyQueryHookResult = ReturnType; +export type IntegrationsListForCreditNoteDetailsSuspenseQueryHookResult = ReturnType; +export type IntegrationsListForCreditNoteDetailsQueryResult = Apollo.QueryResult; +export const SyncIntegrationCreditNoteDocument = gql` + mutation syncIntegrationCreditNote($input: SyncIntegrationCreditNoteInput!) { + syncIntegrationCreditNote(input: $input) { + creditNoteId + } +} + `; +export type SyncIntegrationCreditNoteMutationFn = Apollo.MutationFunction; + +/** + * __useSyncIntegrationCreditNoteMutation__ + * + * To run a mutation, you first call `useSyncIntegrationCreditNoteMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSyncIntegrationCreditNoteMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [syncIntegrationCreditNoteMutation, { data, loading, error }] = useSyncIntegrationCreditNoteMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useSyncIntegrationCreditNoteMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SyncIntegrationCreditNoteDocument, options); + } +export type SyncIntegrationCreditNoteMutationHookResult = ReturnType; +export type SyncIntegrationCreditNoteMutationResult = Apollo.MutationResult; +export type SyncIntegrationCreditNoteMutationOptions = Apollo.BaseMutationOptions; export const GetCustomerDocument = gql` query getCustomer($id: ID!) { customer(id: $id) { diff --git a/src/layouts/CustomerInvoiceDetails.tsx b/src/layouts/CustomerInvoiceDetails.tsx index d103b087d..8fd3cd1ed 100644 --- a/src/layouts/CustomerInvoiceDetails.tsx +++ b/src/layouts/CustomerInvoiceDetails.tsx @@ -127,11 +127,6 @@ gql` id ...NetsuiteIntegrationInfosForInvoiceOverview } - - # Only added to satisfy type check in the code bellow - ... on OktaIntegration { - id - } } } } @@ -256,7 +251,11 @@ const CustomerInvoiceDetails = () => { skip: !data?.invoice?.customer?.netsuiteCustomer?.integrationId, }) - const connectedNetsuiteIntegration = integrationsData?.integrations?.collection?.find( + const allNetsuiteIntegrations = integrationsData?.integrations?.collection.filter( + (i) => i.__typename === 'NetsuiteIntegration', + ) as NetsuiteIntegration[] | undefined + + const connectedNetsuiteIntegration = allNetsuiteIntegrations?.find( (integration) => integration?.id === data?.invoice?.customer?.netsuiteCustomer?.integrationId, ) as NetsuiteIntegration diff --git a/src/pages/CreditNoteDetails.tsx b/src/pages/CreditNoteDetails.tsx index aa15da3a3..0db30f098 100644 --- a/src/pages/CreditNoteDetails.tsx +++ b/src/pages/CreditNoteDetails.tsx @@ -1,4 +1,5 @@ import { gql } from '@apollo/client' +import { Stack } from '@mui/material' import React, { useRef } from 'react' import { generatePath, useParams } from 'react-router-dom' import { Link } from 'react-router-dom' @@ -21,6 +22,7 @@ import { } from '~/components/designSystem' import { GenericPlaceholder } from '~/components/GenericPlaceholder' import { addToast } from '~/core/apolloClient' +import { buildNetsuiteCreditNoteUrl } from '~/core/constants/externalUrls' import formatCreditNotesItems from '~/core/formats/formatCreditNotesItems' import { composeChargeFilterDisplayName, @@ -43,8 +45,11 @@ import { CreditNoteRefundStatusEnum, CurrencyEnum, FeeTypesEnum, + NetsuiteIntegration, useDownloadCreditNoteMutation, useGetCreditNoteQuery, + useIntegrationsListForCreditNoteDetailsQuery, + useSyncIntegrationCreditNoteMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' import { useLocationHistory } from '~/hooks/core/useLocationHistory' @@ -73,11 +78,16 @@ gql` refundStatus subTotalExcludingTaxesAmountCents totalAmountCents + integrationSyncable + externalIntegrationId customer { id name deletedAt applicableTimezone + netsuiteCustomer { + integrationId + } } invoice { id @@ -138,12 +148,31 @@ gql` } } + query integrationsListForCreditNoteDetails($limit: Int) { + integrations(limit: $limit) { + collection { + ... on NetsuiteIntegration { + __typename + id + accountId + name + } + } + } + } + mutation downloadCreditNote($input: DownloadCreditNoteInput!) { downloadCreditNote(input: $input) { id fileUrl } } + + mutation syncIntegrationCreditNote($input: SyncIntegrationCreditNoteInput!) { + syncIntegrationCreditNote(input: $input) { + creditNoteId + } + } ` const creditedMapStatus = (type?: CreditNoteCreditStatusEnum | null | undefined) => { @@ -192,6 +221,20 @@ const CreditNoteDetails = () => { const { hasPermissions } = usePermissions() const { customerId, invoiceId, creditNoteId } = useParams() const voidCreditNoteDialogRef = useRef(null) + + const [syncIntegrationCreditNote, { loading: loadingSyncIntegrationCreditNote }] = + useSyncIntegrationCreditNoteMutation({ + variables: { input: { creditNoteId: creditNoteId || '' } }, + onCompleted({ syncIntegrationCreditNote: syncIntegrationCreditNoteResult }) { + if (syncIntegrationCreditNoteResult?.creditNoteId) { + addToast({ + severity: 'success', + translateKey: 'text_6655a88569eed300ee8c4d44', + }) + } + }, + }) + const [downloadCreditNote, { loading: loadingCreditNoteDownload }] = useDownloadCreditNoteMutation({ onCompleted({ downloadCreditNote: downloadCreditNoteData }) { @@ -221,6 +264,20 @@ const CreditNoteDetails = () => { variables: { id: creditNoteId as string }, skip: !creditNoteId || !customerId, }) + const { data: integrationsData } = useIntegrationsListForCreditNoteDetailsQuery({ + variables: { limit: 1000 }, + skip: !data?.creditNote?.customer?.netsuiteCustomer?.integrationId, + }) + + const allNetsuiteIntegrations = integrationsData?.integrations?.collection.filter( + (i) => i.__typename === 'NetsuiteIntegration', + ) as NetsuiteIntegration[] | undefined + + const connectedNetsuiteIntegration = allNetsuiteIntegrations?.find( + (integration) => + integration?.id === data?.creditNote?.customer?.netsuiteCustomer?.integrationId, + ) as NetsuiteIntegration + const creditNote = data?.creditNote const creditedFormattedStatus = creditedMapStatus(creditNote?.creditStatus) const consumedFormattedStatus = consumedMapStatus(creditNote?.refundStatus) @@ -317,6 +374,21 @@ const CreditNoteDetails = () => { > {translate('text_637655cb50f04bf1c8379cee')} + + {!!data?.creditNote?.integrationSyncable && ( + + )} )} @@ -813,6 +885,34 @@ const CreditNoteDetails = () => { )} + + {connectedNetsuiteIntegration && creditNote?.externalIntegrationId && ( + + + {translate('text_6650b36fc702a4014c878996')} + + +
+ + + {translate('text_6650b36fc702a4014c87899a')} + + + + {creditNote?.externalIntegrationId} + + + +
+
+ )} )} @@ -821,6 +921,8 @@ const CreditNoteDetails = () => { ) } +export default CreditNoteDetails + const HeaderLeft = styled.div` display: flex; align-items: center; @@ -993,4 +1095,11 @@ const SkeletonLine = styled.div` margin-top: ${theme.spacing(7)}; ` -export default CreditNoteDetails +const InlineLink = styled(Link)` + width: fit-content; + line-break: anywhere; + + &:hover { + text-decoration: none; + } +`