fix: update financial forms to use new formatted amount utilities and add adjustment fields

This commit is contained in:
Ahmed Bouhuolia
2024-12-03 17:53:37 +02:00
parent 542763ddf5
commit 3a19518440
21 changed files with 128 additions and 133 deletions

View File

@@ -2,18 +2,22 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { Money } from '@/components';
import '@/style/components/BigAmount.scss'; import '@/style/components/BigAmount.scss';
export function PageFormBigNumber({ label, amount, currencyCode }) { interface PageFormBigNumberProps {
label: string;
amount: string | number;
}
export function PageFormBigNumber({
label,
amount,
}: PageFormBigNumberProps) {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
<div class="big-amount"> <div class="big-amount">
<span class="big-amount__label">{label}</span> <span class="big-amount__label">{label}</span>
<h1 class="big-amount__number"> <h1 class="big-amount__number">{amount}</h1>
<Money amount={amount} currency={currencyCode} />
</h1>
</div> </div>
</div> </div>
); );

View File

@@ -42,6 +42,12 @@ export function BillDetailTableFooter() {
textStyle={TotalLineTextStyle.Regular} textStyle={TotalLineTextStyle.Regular}
/> />
)} )}
{bill.adjustment > 0 && (
<TotalLine
title={'Adjustment'}
value={bill.adjustment_formatted}
/>
)}
<TotalLine <TotalLine
title={<T id={'bill.details.total'} />} title={<T id={'bill.details.total'} />}
value={bill.total_formatted} value={bill.total_formatted}

View File

@@ -30,7 +30,7 @@ export default function CreditNoteDetailHeader() {
<CommercialDocTopHeader> <CommercialDocTopHeader>
<DetailsMenu> <DetailsMenu>
<AmountItem label={intl.get('amount')}> <AmountItem label={intl.get('amount')}>
<span class="big-number">{creditNote.formatted_amount}</span> <span class="big-number">{creditNote.total_formatted}</span>
</AmountItem> </AmountItem>
<StatusItem> <StatusItem>

View File

@@ -21,6 +21,7 @@ export default function CreditNoteDetailTableFooter() {
<TotalLine <TotalLine
title={<T id={'credit_note.drawer.label_subtotal'} />} title={<T id={'credit_note.drawer.label_subtotal'} />}
value={creditNote.formatted_subtotal} value={creditNote.formatted_subtotal}
borderStyle={TotalLineBorderStyle.SingleDark}
/> />
{creditNote.discount_amount > 0 && ( {creditNote.discount_amount > 0 && (
<TotalLine <TotalLine
@@ -30,7 +31,12 @@ export default function CreditNoteDetailTableFooter() {
: 'Discount' : 'Discount'
} }
value={creditNote.discount_amount_formatted} value={creditNote.discount_amount_formatted}
borderStyle={TotalLineBorderStyle.Dark} />
)}
{creditNote.adjustment > 0 && (
<TotalLine
title={'Adjustment'}
value={creditNote.adjustment_formatted}
/> />
)} )}
<TotalLine <TotalLine

View File

@@ -37,6 +37,13 @@ export function InvoiceDetailTableFooter() {
textStyle={TotalLineTextStyle.Regular} textStyle={TotalLineTextStyle.Regular}
/> />
)} )}
{invoice.adjustment > 0 && (
<TotalLine
title="Adjustment"
value={invoice.adjustment_formatted}
textStyle={TotalLineTextStyle.Regular}
/>
)}
{invoice.taxes.map((taxRate) => ( {invoice.taxes.map((taxRate) => (
<TotalLine <TotalLine
key={taxRate.id} key={taxRate.id}
@@ -45,7 +52,6 @@ export function InvoiceDetailTableFooter() {
textStyle={TotalLineTextStyle.Regular} textStyle={TotalLineTextStyle.Regular}
/> />
))} ))}
<TotalLine <TotalLine
title={<T id={'invoice.details.total'} />} title={<T id={'invoice.details.total'} />}
value={invoice.total_formatted} value={invoice.total_formatted}

View File

@@ -31,7 +31,7 @@ export default function ReceiptDetailHeader() {
<CommercialDocTopHeader> <CommercialDocTopHeader>
<DetailsMenu> <DetailsMenu>
<AmountReceiptItem label={intl.get('amount')}> <AmountReceiptItem label={intl.get('amount')}>
<h3 class="big-number">{receipt.formatted_amount}</h3> <h3 class="big-number">{receipt.total_formatted}</h3>
</AmountReceiptItem> </AmountReceiptItem>
<StatusReceiptItem> <StatusReceiptItem>

View File

@@ -23,7 +23,7 @@ export default function ReceiptDetailTableFooter() {
<ReceiptTotalLines labelColWidth={'180px'} amountColWidth={'180px'}> <ReceiptTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
<TotalLine <TotalLine
title={<T id={'receipt.details.subtotal'} />} title={<T id={'receipt.details.subtotal'} />}
value={receipt.formatted_subtotal} value={receipt.subtotal_formatted}
/> />
{receipt.discount_amount > 0 && ( {receipt.discount_amount > 0 && (
<TotalLine <TotalLine
@@ -36,9 +36,16 @@ export default function ReceiptDetailTableFooter() {
textStyle={TotalLineTextStyle.Regular} textStyle={TotalLineTextStyle.Regular}
/> />
)} )}
{receipt.adjustment > 0 && (
<TotalLine
title={'Adjustment'}
value={receipt.adjustment_formatted}
textStyle={TotalLineTextStyle.Regular}
/>
)}
<TotalLine <TotalLine
title={<T id={'receipt.details.total'} />} title={<T id={'receipt.details.total'} />}
value={receipt.formatted_amount} value={receipt.total_formatted}
borderStyle={TotalLineBorderStyle.DoubleDark} borderStyle={TotalLineBorderStyle.DoubleDark}
textStyle={TotalLineTextStyle.Bold} textStyle={TotalLineTextStyle.Bold}
/> />

View File

@@ -8,21 +8,25 @@ import {
TotalLineBorderStyle, TotalLineBorderStyle,
TotalLineTextStyle, TotalLineTextStyle,
} from '@/components'; } from '@/components';
import { useExpensesTotals } from './utils'; import {
useExpenseSubtotalFormatted,
useExpenseTotalFormatted,
} from './utils';
export function ExpenseFormFooterRight() { export function ExpenseFormFooterRight() {
const { formattedSubtotal, formattedTotal } = useExpensesTotals(); const totalFormatted = useExpenseTotalFormatted();
const subtotalFormatted = useExpenseSubtotalFormatted();
return ( return (
<ExpensesTotalLines> <ExpensesTotalLines>
<TotalLine <TotalLine
title={<T id={'expense.label.subtotal'} />} title={<T id={'expense.label.subtotal'} />}
value={formattedSubtotal} value={subtotalFormatted}
borderStyle={TotalLineBorderStyle.None} borderStyle={TotalLineBorderStyle.None}
/> />
<TotalLine <TotalLine
title={<T id={'expense.label.total'} />} title={<T id={'expense.label.total'} />}
value={formattedTotal} value={totalFormatted}
textStyle={TotalLineTextStyle.Bold} textStyle={TotalLineTextStyle.Bold}
/> />
</ExpensesTotalLines> </ExpensesTotalLines>

View File

@@ -1,34 +1,23 @@
// @ts-nocheck // @ts-nocheck
import React, { useMemo } from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { sumBy } from 'lodash';
import { useFormikContext } from 'formik';
import { FormattedMessage as T } from '@/components'; import { FormattedMessage as T } from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import ExpenseFormHeaderFields from './ExpenseFormHeaderFields'; import ExpenseFormHeaderFields from './ExpenseFormHeaderFields';
import { PageFormBigNumber } from '@/components'; import { PageFormBigNumber } from '@/components';
import { useExpenseTotalFormatted } from './utils';
// Expense form header. // Expense form header.
export default function ExpenseFormHeader() { export default function ExpenseFormHeader() {
const { const totalFormatted = useExpenseTotalFormatted();
values: { currency_code, categories },
} = useFormikContext();
// Calculates the expense entries amount.
const totalExpenseAmount = useMemo(
() => sumBy(categories, 'amount'),
[categories],
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<ExpenseFormHeaderFields /> <ExpenseFormHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={<T id={'expense_amount'} />} label={<T id={'expense_amount'} />}
amount={totalExpenseAmount} amount={totalFormatted}
currencyCode={currency_code}
/> />
</div> </div>
); );

View File

@@ -166,30 +166,52 @@ export const useSetPrimaryBranchToForm = () => {
}; };
/** /**
* Retreives the Journal totals. * Retrieves the expense subtotal.
* @returns {number}
*/ */
export const useExpensesTotals = () => { export const useExpenseSubtotal = () => {
const { const {
values: { categories, currency_code: currencyCode }, values: { categories },
} = useFormikContext(); } = useFormikContext();
const total = sumBy(categories, 'amount'); // Calculates the expense entries amount.
return React.useMemo(() => sumBy(categories, 'amount'), [categories]);
};
// Retrieves the formatted total money. /**
const formattedTotal = React.useMemo( * Retrieves the expense subtotal formatted.
() => formattedAmount(total, currencyCode), * @returns {string}
[total, currencyCode], */
); export const useExpenseSubtotalFormatted = () => {
// Retrieves the formatted subtotal. const subtotal = useExpenseSubtotal();
const formattedSubtotal = React.useMemo( const {
() => formattedAmount(total, currencyCode, { money: false }), values: { currency_code },
[total, currencyCode], } = useFormikContext();
);
return { return formattedAmount(subtotal, currency_code);
formattedTotal, };
formattedSubtotal,
}; /**
* Retrieves the expense total.
* @returns {number}
*/
export const useExpenseTotal = () => {
const subtotal = useExpenseSubtotal();
return subtotal;
};
/**
* Retrieves the expense total formatted.
* @returns {string}
*/
export const useExpenseTotalFormatted = () => {
const total = useExpenseTotal();
const {
values: { currency_code },
} = useFormikContext();
return formattedAmount(total, currency_code);
}; };
/** /**

View File

@@ -1,13 +1,12 @@
// @ts-nocheck // @ts-nocheck
import React, { useMemo } from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import classNames from 'classnames'; import classNames from 'classnames';
import { sumBy } from 'lodash';
import { useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { PageFormBigNumber } from '@/components'; import { PageFormBigNumber } from '@/components';
import BillFormHeaderFields from './BillFormHeaderFields'; import BillFormHeaderFields from './BillFormHeaderFields';
import { useBillTotalFormatted } from './utils';
/** /**
* Fill form header. * Fill form header.
@@ -22,19 +21,10 @@ function BillFormHeader() {
} }
function BillFormBigTotal() { function BillFormBigTotal() {
const { const totalFormatted = useBillTotalFormatted();
values: { currency_code, entries },
} = useFormikContext();
// Calculate the total due amount of bill entries.
const totalDueAmount = useMemo(() => sumBy(entries, 'amount'), [entries]);
return ( return (
<PageFormBigNumber <PageFormBigNumber label={intl.get('due_amount')} amount={totalFormatted} />
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
); );
} }

View File

@@ -325,7 +325,7 @@ export const useBillSubtotal = () => {
*/ */
export const useBillSubtotalFormatted = () => { export const useBillSubtotalFormatted = () => {
const subtotal = useBillSubtotal(); const subtotal = useBillSubtotal();
const { values} = useFormikContext(); const { values } = useFormikContext();
return formattedAmount(subtotal, values.currency_code); return formattedAmount(subtotal, values.currency_code);
}; };
@@ -336,8 +336,12 @@ export const useBillSubtotalFormatted = () => {
*/ */
export const useBillDiscountAmount = () => { export const useBillDiscountAmount = () => {
const { values } = useFormikContext(); const { values } = useFormikContext();
const subtotal = useBillSubtotal();
const discount = toSafeNumber(values.discount);
return toSafeNumber(values.discount); return values?.discount_type === 'percentage'
? (subtotal * discount) / 100
: discount;
}; };
/** /**
@@ -384,7 +388,6 @@ export const useBillTotalTaxAmount = () => {
.filter((entry) => entry.tax_amount) .filter((entry) => entry.tax_amount)
.sumBy('tax_amount') .sumBy('tax_amount')
.value(); .value();
}, [values.entries]); }, [values.entries]);
}; };

View File

@@ -2,33 +2,23 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import classNames from 'classnames'; import classNames from 'classnames';
import { useFormikContext } from 'formik';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import VendorCreditNoteFormHeaderFields from './VendorCreditNoteFormHeaderFields'; import VendorCreditNoteFormHeaderFields from './VendorCreditNoteFormHeaderFields';
import { getEntriesTotal } from '@/containers/Entries/utils';
import { PageFormBigNumber } from '@/components'; import { PageFormBigNumber } from '@/components';
import { useVendorCreditTotalFormatted } from './utils';
/** /**
* Vendor Credit note header. * Vendor Credit note header.
*/ */
function VendorCreditNoteFormHeader() { function VendorCreditNoteFormHeader() {
const { values:{entries ,currency_code} } = useFormikContext(); const totalFormatted = useVendorCreditTotalFormatted();
// Calculate the total amount.
const totalAmount = React.useMemo(
() => getEntriesTotal(entries),
[entries],
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<VendorCreditNoteFormHeaderFields /> <VendorCreditNoteFormHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={intl.get('vendor_credits.label.amount_to_credit')} label={intl.get('vendor_credits.label.amount_to_credit')}
amount={totalAmount} amount={totalFormatted}
currencyCode={currency_code}
/> />
</div> </div>
); );

View File

@@ -206,8 +206,12 @@ export const useVendorCreditSubtotal = () => {
*/ */
export const useVendorCreditDiscountAmount = () => { export const useVendorCreditDiscountAmount = () => {
const { values } = useFormikContext(); const { values } = useFormikContext();
const subtotal = useVendorCreditSubtotal();
const discount = toSafeNumber(values.discount);
return toSafeNumber(values.discount); return values.discount_type === 'percentage'
? (subtotal * discount) / 100
: discount;
}; };
/** /**

View File

@@ -1,11 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useFormikContext } from 'formik';
import CreditNoteFormHeaderFields from './CreditNoteFormHeaderFields'; import CreditNoteFormHeaderFields from './CreditNoteFormHeaderFields';
import { getEntriesTotal } from '@/containers/Entries/utils';
import { Group, PageFormBigNumber } from '@/components'; import { Group, PageFormBigNumber } from '@/components';
import { useCreditNoteTotalFormatted } from './utils';
/** /**
* Credit note header. * Credit note header.
@@ -31,18 +29,12 @@ function CreditNoteFormHeader() {
* @returns {React.ReactNode} * @returns {React.ReactNode}
*/ */
function CreditNoteFormBigNumber() { function CreditNoteFormBigNumber() {
const { const totalFormatted = useCreditNoteTotalFormatted();
values: { entries, currency_code },
} = useFormikContext();
// Calculate the total amount.
const totalAmount = React.useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<PageFormBigNumber <PageFormBigNumber
label={intl.get('credit_note.label_amount_to_credit')} label={intl.get('credit_note.label_amount_to_credit')}
amount={totalAmount} amount={totalFormatted}
currencyCode={currency_code}
/> />
); );
} }

View File

@@ -1,5 +1,6 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik';
import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog'; import EstimateNumberDialog from '@/containers/Dialogs/EstimateNumberDialog';
/** /**

View File

@@ -1,12 +1,10 @@
// @ts-nocheck // @ts-nocheck
import React, { useMemo } from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useFormikContext } from 'formik';
import { x } from '@xstyled/emotion';
import EstimateFormHeaderFields from './EstimateFormHeaderFields'; import EstimateFormHeaderFields from './EstimateFormHeaderFields';
import { getEntriesTotal } from '@/containers/Entries/utils';
import { Group, PageFormBigNumber } from '@/components'; import { Group, PageFormBigNumber } from '@/components';
import { useEstimateTotalFormatted } from './utils';
// Estimate form top header. // Estimate form top header.
function EstimateFormHeader() { function EstimateFormHeader() {
@@ -29,19 +27,10 @@ function EstimateFormHeader() {
* @returns {React.ReactNode} * @returns {React.ReactNode}
*/ */
function EstimateFormBigTotal() { function EstimateFormBigTotal() {
const { const totalFormatted = useEstimateTotalFormatted();
values: { entries, currency_code },
} = useFormikContext();
// Calculate the total due amount of bill entries.
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<PageFormBigNumber <PageFormBigNumber label={intl.get('amount')} amount={totalFormatted} />
label={intl.get('amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
); );
} }

View File

@@ -245,6 +245,7 @@ export const useEstimateSubtotalFormatted = () => {
*/ */
export const useEstimateDiscount = () => { export const useEstimateDiscount = () => {
const { values } = useFormikContext(); const { values } = useFormikContext();
const subtotal = useEstimateSubtotal();
const discount = toSafeNumber(values.discount); const discount = toSafeNumber(values.discount);
return values?.discount_type === 'percentage' return values?.discount_type === 'percentage'

View File

@@ -1,10 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useFormikContext } from 'formik';
import { Group, PageFormBigNumber } from '@/components'; import { Group, PageFormBigNumber } from '@/components';
import InvoiceFormHeaderFields from './InvoiceFormHeaderFields'; import InvoiceFormHeaderFields from './InvoiceFormHeaderFields';
import { useInvoiceDueAmount } from './utils'; import { useInvoiceTotalFormatted } from './utils';
/** /**
* Invoice form header section. * Invoice form header section.
@@ -29,19 +28,11 @@ function InvoiceFormHeader() {
* @returns {React.ReactNode} * @returns {React.ReactNode}
*/ */
function InvoiceFormBigTotal() { function InvoiceFormBigTotal() {
const {
values: { currency_code },
} = useFormikContext();
// Calculate the total due amount of invoice entries. // Calculate the total due amount of invoice entries.
const totalDueAmount = useInvoiceDueAmount(); const totalFormatted = useInvoiceTotalFormatted();
return ( return (
<PageFormBigNumber <PageFormBigNumber label={intl.get('due_amount')} amount={totalFormatted} />
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
); );
} }
export default InvoiceFormHeader; export default InvoiceFormHeader;

View File

@@ -324,7 +324,7 @@ export const useInvoiceSubtotalFormatted = () => {
export const useInvoiceDiscountAmount = () => { export const useInvoiceDiscountAmount = () => {
const { values } = useFormikContext(); const { values } = useFormikContext();
const subtotal = useInvoiceSubtotal(); const subtotal = useInvoiceSubtotal();
const discount = parseFloat(values.discount); const discount = toSafeNumber(values.discount);
return values?.discount_type === 'percentage' return values?.discount_type === 'percentage'
? (subtotal * discount) / 100 ? (subtotal * discount) / 100
@@ -350,7 +350,7 @@ export const useInvoiceDiscountAmountFormatted = () => {
*/ */
export const useInvoiceAdjustmentAmount = () => { export const useInvoiceAdjustmentAmount = () => {
const { values } = useFormikContext(); const { values } = useFormikContext();
const adjustment = parseFloat(values.adjustment); const adjustment = toSafeNumber(values.adjustment);
return adjustment; return adjustment;
}; };

View File

@@ -1,10 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React, { useMemo } from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useFormikContext } from 'formik';
import { Group, PageFormBigNumber } from '@/components'; import { Group, PageFormBigNumber } from '@/components';
import ReceiptFormHeaderFields from './ReceiptFormHeaderFields'; import ReceiptFormHeaderFields from './ReceiptFormHeaderFields';
import { getEntriesTotal } from '@/containers/Entries/utils'; import { useReceiptTotalFormatted } from './utils';
/** /**
* Receipt form header section. * Receipt form header section.
@@ -35,19 +34,10 @@ function ReceiptFormHeader({
* @returns {React.ReactNode} * @returns {React.ReactNode}
*/ */
function ReceiptFormHeaderBigTotal() { function ReceiptFormHeaderBigTotal() {
const { const totalFormatted = useReceiptTotalFormatted();
values: { currency_code, entries },
} = useFormikContext();
// Calculate the total due amount of bill entries.
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<PageFormBigNumber <PageFormBigNumber label={intl.get('due_amount')} amount={totalFormatted} />
label={intl.get('due_amount')}
amount={totalDueAmount}
currencyCode={currency_code}
/>
); );
} }