Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions cypress/e2e/10-resources/t100-payment-terms.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { CENTRALIZED_DIALOG_CONFIRM_BUTTON_TEST_ID } from '~/components/dialogs/const'
import {
EDIT_PAYMENT_TERM_SUBMIT_BUTTON_TEST_ID,
PAYMENT_TERM_ADD_BUTTON_TEST_ID,
PAYMENT_TERM_DELETE_BUTTON_TEST_ID,
PAYMENT_TERM_EDIT_BUTTON_TEST_ID,
PAYMENT_TERM_SETTINGS_ROW_TEST_ID,
} from '~/components/paymentTerms/dataTestConstants'
import { PaymentTermTypeEnum } from '~/generated/graphql'

import { customerName } from '../../support/reusableConstants'

type TermFields = { days?: string; dayOfMonth?: string; monthOffset?: string }

/**
* One row per term type. A new term option is a new row here, never a new spec: the
* billing-entity and customer flows below are both driven off this table.
*/
const TERM_CASES: Array<{ termType: PaymentTermTypeEnum; fields: TermFields; label: string }> = [
{ termType: PaymentTermTypeEnum.DueOnReceipt, fields: {}, label: 'Due on receipt' },
{ termType: PaymentTermTypeEnum.Net, fields: { days: '30' }, label: 'Net 30 days' },
{ termType: PaymentTermTypeEnum.EndOfMonth, fields: {}, label: 'End of month' },
{
termType: PaymentTermTypeEnum.NetEndOfMonth,
fields: { days: '30' },
label: '30 net days after end of month',
},
{
termType: PaymentTermTypeEnum.DaysEndOfMonth,
fields: { days: '45' },
label: '45 days end of month',
},
{
termType: PaymentTermTypeEnum.DayOfMonth,
fields: { dayOfMonth: '15', monthOffset: '1' },
label: '15 MFI, 1 month offset',
},
]

const CUSTOMER_TERM_CASE = TERM_CASES[1]

const fillTermForm = ({
termType,
fields,
}: {
termType: PaymentTermTypeEnum
fields: TermFields
}) => {
// The dialog opens its term-type combo box on entry, so the options are already listed.
// Each option row carries the term type as its own `data-test`.
cy.get('[data-test="form-dialog"]').should('exist')
cy.get(`[data-test="${termType}"]`).click()

Object.entries(fields).forEach(([name, value]) => {
cy.get(`input[name="${name}"]`).clear().type(value)
})

cy.get(`[data-test="${EDIT_PAYMENT_TERM_SUBMIT_BUTTON_TEST_ID}"]`).click()
cy.get('[data-test="form-dialog"]').should('not.exist')
}

const paymentTermRow = () => cy.get(`[data-test="${PAYMENT_TERM_SETTINGS_ROW_TEST_ID}"]`)

describe('Payment terms', () => {
beforeEach(() => {
cy.login()
})

describe('billing entity', () => {
const visitInvoiceSettings = () => {
// `/settings` redirects to the default billing entity once its query resolves.
cy.visitApp('/settings')
cy.url().should('include', '/billing-entity/')

cy.url().then((url) => {
const billingEntityCode = url.match(/billing-entity\/([^/]+)/)?.[1] as string

cy.visitApp(`/settings/billing-entity/${billingEntityCode}/invoice-settings`)
cy.url().should('match', /\/settings\/billing-entity\/[^/]+\/invoice-settings$/)
})
}

TERM_CASES.forEach(({ termType, fields, label }) => {
it(`should set a ${termType} term and show it on the row`, () => {
visitInvoiceSettings()

paymentTermRow().find(`[data-test="${PAYMENT_TERM_EDIT_BUTTON_TEST_ID}"]`).click()
fillTermForm({ termType, fields })

paymentTermRow().should('contain', label)
})
})
})

describe('customer', () => {
const visitCustomerSettings = () => {
cy.visitApp('/customers')
cy.contains(customerName).click()
cy.url().should('include', '/customer/')
cy.get('button[role="tab"]').contains('Settings').click()
}

it('should override the billing entity term, then delete it to inherit again', () => {
visitCustomerSettings()

paymentTermRow().find(`[data-test="${PAYMENT_TERM_ADD_BUTTON_TEST_ID}"]`).click()
fillTermForm(CUSTOMER_TERM_CASE)

paymentTermRow()
.should('contain', CUSTOMER_TERM_CASE.label)
.and('not.contain', 'inherit from billing entity')

paymentTermRow().find(`[data-test="${PAYMENT_TERM_DELETE_BUTTON_TEST_ID}"]`).click()
cy.get(`[data-test="${CENTRALIZED_DIALOG_CONFIRM_BUTTON_TEST_ID}"]`).click()

paymentTermRow().should('contain', 'inherit from billing entity')
})
})
})
92 changes: 33 additions & 59 deletions src/components/customers/CustomerSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useMemo } from 'react'
import { useDeleteCustomerDocumentLocaleDialog } from '~/components/customers/DeleteCustomerDocumentLocaleDialog'
import { useDeleteCustomerFinalizeZeroAmountInvoiceDialog } from '~/components/customers/DeleteCustomerFinalizeZeroAmountInvoiceDialog'
import { useDeleteCustomerGracePeriodeDialog } from '~/components/customers/DeleteCustomerGracePeriodeDialog'
import { useDeleteCustomerNetPaymentTermDialog } from '~/components/customers/DeleteCustomerNetPaymentTermDialog'
import { useDeleteCustomerPaymentTermDialog } from '~/components/customers/DeleteCustomerPaymentTermDialog'
import { useDeleteCustomerVatRateDialog } from '~/components/customers/DeleteCustomerVatRateDialog'
import { useEditCustomerDocumentLocaleDialog } from '~/components/customers/EditCustomerDocumentLocaleDialog'
import { useEditCustomerDunningCampaignDialog } from '~/components/customers/EditCustomerDunningCampaignDialog'
Expand All @@ -29,8 +29,14 @@ import {
SettingsListWrapper,
SettingsPaddedContainer,
} from '~/components/layouts/Settings'
import { useEditFinalizeZeroAmountInvoiceDialog } from '~/components/settings/invoices/EditFinalizeZeroAmountInvoiceDialog'
import { useEditNetPaymentTermDialog } from '~/components/settings/invoices/EditNetPaymentTermDialog'
import {
PAYMENT_TERM_ADD_BUTTON_TEST_ID,
PAYMENT_TERM_DELETE_BUTTON_TEST_ID,
PAYMENT_TERM_EDIT_BUTTON_TEST_ID,
PAYMENT_TERM_SETTINGS_ROW_TEST_ID,
} from '~/components/paymentTerms/dataTestConstants'
import { useEditFinalizeZeroAmountInvoiceDialog } from '~/components/settings/invoices/EditFinalizeZeroAmountInvoiceDialog/EditFinalizeZeroAmountInvoiceDialog'
import { useEditPaymentTermDialog } from '~/components/settings/invoices/EditPaymentTermDialog/EditPaymentTermDialog'
import {
INVOICE_ISSUING_DATE_ADJUSTMENT_SETTING_KEYS,
INVOICE_ISSUING_DATE_ANCHOR_SETTING_KEYS,
Expand All @@ -45,12 +51,13 @@ import {
CustomerSubscriptionInvoiceIssuingDateAnchorEnum,
DeleteCustomerDocumentLocaleFragmentDoc,
DeleteCustomerGracePeriodFragmentDoc,
DeleteCustomerNetPaymentTermFragmentDoc,
DeleteCustomerPaymentTermFragmentDoc,
EditCustomerDocumentLocaleFragmentDoc,
EditCustomerDunningCampaignFragmentDoc,
EditCustomerInvoiceCustomSectionFragmentDoc,
EditCustomerInvoiceGracePeriodFragmentDoc,
EditCustomerIssuingDatePolicyDialogFragmentDoc,
EditCustomerPaymentTermForDialogFragmentDoc,
EditCustomerVatRateFragmentDoc,
FinalizeZeroAmountInvoiceEnum,
PremiumIntegrationTypeEnum,
Expand All @@ -59,6 +66,7 @@ import {
import { useInternationalization } from '~/hooks/core/useInternationalization'
import { useCurrentUser } from '~/hooks/useCurrentUser'
import { useOrganizationInfos } from '~/hooks/useOrganizationInfos'
import { usePaymentTerm } from '~/hooks/usePaymentTerm'
import { usePermissions } from '~/hooks/usePermissions'
import ErrorImage from '~/public/images/maneki/error.svg'
import { MenuPopper } from '~/styles'
Expand Down Expand Up @@ -103,12 +111,10 @@ gql`
customer(id: $id) {
id
invoiceGracePeriod
netPaymentTerm
finalizeZeroAmountInvoice

billingEntity {
id
netPaymentTerm
finalizeZeroAmountInvoice
billingConfiguration {
id
Expand Down Expand Up @@ -146,7 +152,8 @@ gql`
...DeleteCustomerGracePeriod
...DeleteCustomerDocumentLocale
...CustomerForDeleteVatRateDialog
...DeleteCustomerNetPaymentTerm
...DeleteCustomerPaymentTerm
...EditCustomerPaymentTermForDialog
...EditCustomerIssuingDatePolicyDialog
}
}
Expand All @@ -159,7 +166,8 @@ gql`
${DeleteCustomerGracePeriodFragmentDoc}
${DeleteCustomerDocumentLocaleFragmentDoc}
${CustomerForDeleteVatRateDialogFragmentDoc}
${DeleteCustomerNetPaymentTermFragmentDoc}
${DeleteCustomerPaymentTermFragmentDoc}
${EditCustomerPaymentTermForDialogFragmentDoc}
${EditCustomerIssuingDatePolicyDialogFragmentDoc}
`

Expand Down Expand Up @@ -189,9 +197,9 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {
useEditCustomerInvoiceCustomSectionsDialog(customerId)
const { openDeleteCustomerDocumentLocaleDialog } = useDeleteCustomerDocumentLocaleDialog()
const { open: openPremiumWarningDialog } = usePremiumWarningDialog()
const { openEditNetPaymentTermDialog } = useEditNetPaymentTermDialog()
const netPaymentTermDialogDescription = translate('text_64c7a89b6c67eb6c988980eb')
const { openDeleteCustomerNetPaymentTermDialog } = useDeleteCustomerNetPaymentTermDialog()
const { openEditPaymentTermDialog } = useEditPaymentTermDialog()
const { openDeleteCustomerPaymentTermDialog } = useDeleteCustomerPaymentTermDialog()
const { getPaymentTermCopy } = usePaymentTerm()
const { openEditFinalizeZeroAmountInvoiceDialog } = useEditFinalizeZeroAmountInvoiceDialog()
const { openDeleteCustomerFinalizeZeroAmountInvoiceDialog } =
useDeleteCustomerFinalizeZeroAmountInvoiceDialog()
Expand Down Expand Up @@ -273,35 +281,6 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {

const isInvoiceCustomSectionConfigurable = !!customer?.configurableInvoiceCustomSections?.length

function getNetPaymentTermCopy(
customerNetPaymentTerm: number | null | undefined,
billingEntityNetPaymentTerm: number,
): string {
const isCustomerNetPaymentTermDefined = typeof customerNetPaymentTerm === 'number'

if (!isCustomerNetPaymentTermDefined) {
return translate(
'text_64c7a89b6c67eb6c98898241',
{
days: billingEntityNetPaymentTerm ?? 0,
},
billingEntityNetPaymentTerm ?? 0,
)
}

if (customerNetPaymentTerm === 0) {
return translate('text_64c7a89b6c67eb6c98898125')
}

return translate(
'text_64c7a89b6c67eb6c9889815f',
{
days: customerNetPaymentTerm,
},
customerNetPaymentTerm,
)
}

function getDunningCampaignContent(): React.ReactNode {
if (!dunningCampaign || customer?.excludeFromDunningCampaign) {
return (
Expand Down Expand Up @@ -725,24 +704,20 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {
)}
</SettingsListItem>

{/* Net payment term */}
<SettingsListItem>
{/* Payment terms */}
<SettingsListItem dataTest={PAYMENT_TERM_SETTINGS_ROW_TEST_ID}>
<SettingsListItemHeader
label={translate('text_64c7a89b6c67eb6c98898167')}
label={translate('text_17876033821633o4yokqvqdl')}
sublabel={translate('text_1728031300577aivplw3hqav')}
action={
hasPermissions(['customersUpdate']) ? (
<>
{typeof customer?.netPaymentTerm !== 'number' ? (
{!customer?.paymentTerm ? (
<Button
disabled={loading}
variant="inline"
onClick={() =>
openEditNetPaymentTermDialog({
model: customer,
description: netPaymentTermDialogDescription,
})
}
onClick={() => openEditPaymentTermDialog({ model: customer })}
data-test={PAYMENT_TERM_ADD_BUTTON_TEST_ID}
>
{translate('text_645bb193927b375079d28ad2')}
</Button>
Expand All @@ -765,12 +740,10 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {
variant="quaternary"
align="left"
onClick={() => {
openEditNetPaymentTermDialog({
model: customer,
description: netPaymentTermDialogDescription,
})
openEditPaymentTermDialog({ model: customer })
closePopper()
}}
data-test={PAYMENT_TERM_EDIT_BUTTON_TEST_ID}
>
{translate('text_63aa15caab5b16980b21b0b8')}
</Button>
Expand All @@ -781,10 +754,11 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {
align="left"
onClick={() => {
if (customer) {
openDeleteCustomerNetPaymentTermDialog({ customer })
openDeleteCustomerPaymentTermDialog({ customer })
}
closePopper()
}}
data-test={PAYMENT_TERM_DELETE_BUTTON_TEST_ID}
>
{translate('text_63aa15caab5b16980b21b0ba')}
</Button>
Expand All @@ -798,10 +772,10 @@ export const CustomerSettings = ({ customerId }: CustomerSettingsProps) => {
/>

<Typography variant="body" color="grey700">
{getNetPaymentTermCopy(
customer?.netPaymentTerm,
billingEntity?.netPaymentTerm || 0,
)}
{getPaymentTermCopy({
ownTerm: customer?.paymentTerm,
parentTerm: billingEntity?.paymentTerm,
})}
</Typography>
</SettingsListItem>

Expand Down
Loading
Loading