Skip to content

Commit

Permalink
feat: Credit note prepaid credit invoices (#1808)
Browse files Browse the repository at this point in the history
* feat: add tooltip to disabled issue credit note button

* chore: add translations

* feat: remove taxes from prepaid credits credit notes

* feat: update credit note creation to support prepaid credits

* chore: generate types

* fix: remove tax part from label

* fix: remove tax part from invoice overview

* fix(button): make button full width

* fix: show credit notes tab / include button disabling logic

* chore: generate types

* fix(translations): remove conflict marker

* chore(graphql): generate types
  • Loading branch information
stephenlago99 authored Oct 24, 2024
1 parent 53def83 commit 4172bbb
Show file tree
Hide file tree
Showing 12 changed files with 527 additions and 243 deletions.
1 change: 1 addition & 0 deletions src/components/creditNote/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface CreditNoteForm {
description?: string
fees?: FeesPerInvoice
addOnFee?: FromFee[]
creditFee?: FromFee[]
}

export enum CreditNoteFeeErrorEnum {
Expand Down
214 changes: 120 additions & 94 deletions src/components/invoices/InvoiceCreditNotesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import {
import { intlFormatNumber } from '~/core/formats/intlFormatNumber'
import { CUSTOMER_INVOICE_CREDIT_NOTE_DETAILS_ROUTE } from '~/core/router'
import { deserializeAmount } from '~/core/serializers/serializeAmount'
import { CreditNote, CreditNoteItem, CurrencyEnum, FeeTypesEnum } from '~/generated/graphql'
import {
CreditNote,
CreditNoteItem,
CurrencyEnum,
FeeTypesEnum,
InvoiceTypeEnum,
} from '~/generated/graphql'
import { useInternationalization } from '~/hooks/core/useInternationalization'
import { theme } from '~/styles'

Expand Down Expand Up @@ -89,12 +95,15 @@ interface InvoiceCreditNotesTableProps {
creditNote: CreditNote
items: CreditNoteItem[][][]
}[]
invoiceType?: InvoiceTypeEnum
}

export const InvoiceCreditNotesTable = memo(
({ customerId, formatedCreditNotes, invoiceId }: InvoiceCreditNotesTableProps) => {
({ customerId, formatedCreditNotes, invoiceId, invoiceType }: InvoiceCreditNotesTableProps) => {
const { translate } = useInternationalization()

const isPrepaidCreditsInvoice = invoiceType === InvoiceTypeEnum.Credit

return (
<Wrapper>
{formatedCreditNotes.map((formatedCreditNote, i) => {
Expand Down Expand Up @@ -131,11 +140,13 @@ export const InvoiceCreditNotesTable = memo(
noWrap
/>
</th>
<th>
<Typography variant="captionHl" color="grey600">
{translate('text_636bedf292786b19d3398f06')}
</Typography>
</th>
{!isPrepaidCreditsInvoice && (
<th>
<Typography variant="captionHl" color="grey600">
{translate('text_636bedf292786b19d3398f06')}
</Typography>
</th>
)}
<th>
<Typography variant="captionHl" color="grey600">
{translate('text_637cd81348c50c26dd05a769')}
Expand All @@ -157,50 +168,58 @@ export const InvoiceCreditNotesTable = memo(
>
<tr key={`formatedCreditNote-${i}-charge-${j}-item-${k}`}>
<td>
<Typography variant="bodyHl" color="grey700">
{item?.fee?.feeType === FeeTypesEnum.AddOn
? translate('text_6388baa2e514213fed583611', {
name: item.fee.invoiceName || item?.fee?.itemName,
})
: item?.fee?.feeType === FeeTypesEnum.Commitment
? item.fee.invoiceName ||
'Minimum commitment - True up'
: composeMultipleValuesWithSepator([
item.fee?.invoiceName ||
item.fee.charge?.billableMetric.name ||
creditNoteDisplayName,
composeGroupedByDisplayName(item.fee.groupedBy),
composeChargeFilterDisplayName(
item.fee.chargeFilter,
),
item?.fee?.trueUpParentFee?.id
? ` - ${translate(
'text_64463aaa34904c00a23be4f7',
)}`
: '',
])}
</Typography>
</td>
<td>
<Typography variant="body" color="grey700">
{item.fee.appliedTaxes?.length
? item.fee.appliedTaxes?.map((appliedTaxe) => (
<Typography
key={`fee-${item.fee.id}-applied-taxe-${appliedTaxe.id}`}
variant="body"
color="grey700"
>
{intlFormatNumber(
appliedTaxe.taxRate / 100 || 0,
{
style: 'percent',
},
)}
</Typography>
))
: '0%'}
</Typography>
{invoiceType === InvoiceTypeEnum.Credit ? (
<Typography variant="bodyHl" color="grey700">
{translate('text_1729262241097k3cnpci6p5j')}
</Typography>
) : (
<Typography variant="bodyHl" color="grey700">
{item?.fee?.feeType === FeeTypesEnum.AddOn
? translate('text_6388baa2e514213fed583611', {
name: item.fee.invoiceName || item?.fee?.itemName,
})
: item?.fee?.feeType === FeeTypesEnum.Commitment
? item.fee.invoiceName ||
'Minimum commitment - True up'
: composeMultipleValuesWithSepator([
item.fee?.invoiceName ||
item.fee.charge?.billableMetric.name ||
creditNoteDisplayName,
composeGroupedByDisplayName(item.fee.groupedBy),
composeChargeFilterDisplayName(
item.fee.chargeFilter,
),
item?.fee?.trueUpParentFee?.id
? ` - ${translate(
'text_64463aaa34904c00a23be4f7',
)}`
: '',
])}
</Typography>
)}
</td>
{!isPrepaidCreditsInvoice && (
<td>
<Typography variant="body" color="grey700">
{item.fee.appliedTaxes?.length
? item.fee.appliedTaxes?.map((appliedTaxe) => (
<Typography
key={`fee-${item.fee.id}-applied-taxe-${appliedTaxe.id}`}
variant="body"
color="grey700"
>
{intlFormatNumber(
appliedTaxe.taxRate / 100 || 0,
{
style: 'percent',
},
)}
</Typography>
))
: '0%'}
</Typography>
</td>
)}
<td>
<Typography variant="body" color="success600">
-
Expand Down Expand Up @@ -231,19 +250,45 @@ export const InvoiceCreditNotesTable = memo(

<table>
<tfoot>
{Number(creditNote?.couponsAdjustmentAmountCents || 0) > 0 && (
{!isPrepaidCreditsInvoice &&
Number(creditNote?.couponsAdjustmentAmountCents || 0) > 0 && (
<tr>
<td></td>
<td>
<Typography variant="bodyHl" color="grey600">
{translate('text_644b9f17623605a945cafdbb')}
</Typography>
</td>
<td>
<Typography variant="body" color="grey700">
{intlFormatNumber(
deserializeAmount(
creditNote?.couponsAdjustmentAmountCents || 0,
creditNote?.currency || CurrencyEnum.Usd,
),
{
currencyDisplay: 'symbol',
currency: creditNote?.currency || CurrencyEnum.Usd,
},
)}
</Typography>
</td>
</tr>
)}
{!isPrepaidCreditsInvoice && (
<tr>
<td></td>
<td>
<Typography variant="bodyHl" color="grey600">
{translate('text_644b9f17623605a945cafdbb')}
{translate('text_637ccf8133d2c9a7d11ce73d')}
</Typography>
</td>
<td>
<Typography variant="body" color="grey700">
<Typography variant="body" color="success600">
-
{intlFormatNumber(
deserializeAmount(
creditNote?.couponsAdjustmentAmountCents || 0,
creditNote?.subTotalExcludingTaxesAmountCents || 0,
creditNote?.currency || CurrencyEnum.Usd,
),
{
Expand All @@ -255,29 +300,6 @@ export const InvoiceCreditNotesTable = memo(
</td>
</tr>
)}
<tr>
<td></td>
<td>
<Typography variant="bodyHl" color="grey600">
{translate('text_637ccf8133d2c9a7d11ce73d')}
</Typography>
</td>
<td>
<Typography variant="body" color="success600">
-
{intlFormatNumber(
deserializeAmount(
creditNote?.subTotalExcludingTaxesAmountCents || 0,
creditNote?.currency || CurrencyEnum.Usd,
),
{
currencyDisplay: 'symbol',
currency: creditNote?.currency || CurrencyEnum.Usd,
},
)}
</Typography>
</td>
</tr>
{!!creditNote.appliedTaxes?.length ? (
<>
{creditNote.appliedTaxes.map((appliedTax) => (
Expand Down Expand Up @@ -322,23 +344,27 @@ export const InvoiceCreditNotesTable = memo(
))}
</>
) : (
<tr>
<td></td>
<td>
<Typography variant="bodyHl" color="grey600">
{translate('text_637ccf8133d2c9a7d11ce741')}
</Typography>
</td>
<td>
<Typography variant="body" color="success600">
-
{intlFormatNumber(0, {
currencyDisplay: 'symbol',
currency: creditNote?.currency || CurrencyEnum.Usd,
})}
</Typography>
</td>
</tr>
<>
{!isPrepaidCreditsInvoice && (
<tr>
<td></td>
<td>
<Typography variant="bodyHl" color="grey600">
{translate('text_637ccf8133d2c9a7d11ce741')}
</Typography>
</td>
<td>
<Typography variant="body" color="success600">
-
{intlFormatNumber(0, {
currencyDisplay: 'symbol',
currency: creditNote?.currency || CurrencyEnum.Usd,
})}
</Typography>
</td>
</tr>
)}
</>
)}
<tr>
<td></td>
Expand Down
12 changes: 11 additions & 1 deletion src/core/serializers/serializeCreditNoteInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const serializeCreditNoteInput: (
formValues: CreditNoteForm,
currency: CurrencyEnum,
) => CreateCreditNoteInput = (invoiceId, formValues, currency) => {
const { reason, description, payBack, fees = [], addOnFee } = formValues
const { reason, description, payBack, fees = [], addOnFee, creditFee } = formValues

return {
invoiceId: invoiceId as string,
Expand Down Expand Up @@ -47,6 +47,16 @@ export const serializeCreditNoteInput: (

return acc
}, []) || []),
...(creditFee?.reduce<CreditNoteItemInput[]>((acc, fee) => {
if (fee.checked && Number(fee.value) > 0) {
acc.push({
feeId: fee.id,
amountCents: serializeAmount(fee.value, currency),
})
}

return acc
}, []) || []),
...Object.keys(fees).reduce<CreditNoteItemInput[]>((subAcc, subKey) => {
const subChild = (fees as FeesPerInvoice)[subKey]

Expand Down
10 changes: 10 additions & 0 deletions src/formValidation/feesSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,13 @@ export const generateAddOnFeesSchema = (formikInitialFees: FromFee[], currency:

return tuple(validationObject)
}

export const generateCreditFeesSchema = (formikInitialFees: FromFee[], currency: CurrencyEnum) => {
const validationObject: [ISchema<unknown>] = [{} as ISchema<unknown>]

formikInitialFees.forEach((fee, i) => {
validationObject[i] = simpleFeeSchema(fee.maxAmount, currency)
})

return tuple(validationObject)
}
Loading

0 comments on commit 4172bbb

Please sign in to comment.