From ee6ebeadf41d65f5dfef718775ae4d1ab86a656f Mon Sep 17 00:00:00 2001 From: Civolilah Date: Tue, 24 Sep 2024 20:10:48 +0200 Subject: [PATCH 1/9] Adjusted number formatting --- src/common/hooks/useNumericFormatter.ts | 26 +++++++++++++++++ src/common/hooks/useUserNumberPrecision.ts | 21 ++++++++++++++ .../common/hooks/useInvoiceProducts.ts | 10 +++++-- .../common/hooks/usePurchaseOrderProducts.ts | 6 +++- .../common/hooks/useInvoiceProject.tsx | 29 +++++++++++++------ .../components/AdditionalInfo.tsx | 6 +++- .../tasks/common/components/TaskSlider.tsx | 5 +++- .../common/hooks/useAddTasksOnInvoice.ts | 26 ++++++++++++----- .../tasks/common/hooks/useInvoiceTask.ts | 19 ++++++++---- 9 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 src/common/hooks/useNumericFormatter.ts create mode 100644 src/common/hooks/useUserNumberPrecision.ts diff --git a/src/common/hooks/useNumericFormatter.ts b/src/common/hooks/useNumericFormatter.ts new file mode 100644 index 000000000..ffc71de38 --- /dev/null +++ b/src/common/hooks/useNumericFormatter.ts @@ -0,0 +1,26 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { numericFormatter } from 'react-number-format'; +import { useCurrentCompany } from './useCurrentCompany'; +import { useUserNumberPrecision } from './useUserNumberPrecision'; + +export function useNumericFormatter() { + const company = useCurrentCompany(); + const userNumberPrecision = useUserNumberPrecision(); + + return (numStr: string) => { + return numericFormatter(numStr, { + thousandSeparator: company?.use_comma_as_decimal_place ? '.' : ',', + decimalSeparator: company?.use_comma_as_decimal_place ? ',' : '.', + decimalScale: userNumberPrecision, + }); + }; +} diff --git a/src/common/hooks/useUserNumberPrecision.ts b/src/common/hooks/useUserNumberPrecision.ts new file mode 100644 index 000000000..8bc47455e --- /dev/null +++ b/src/common/hooks/useUserNumberPrecision.ts @@ -0,0 +1,21 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { useReactSettings } from './useReactSettings'; + +export function useUserNumberPrecision() { + const reactSettings = useReactSettings(); + + return reactSettings?.number_precision && + reactSettings?.number_precision > 0 && + reactSettings?.number_precision <= 100 + ? reactSettings.number_precision + : 2; +} diff --git a/src/pages/products/common/hooks/useInvoiceProducts.ts b/src/pages/products/common/hooks/useInvoiceProducts.ts index df9ee2111..28d1e6268 100644 --- a/src/pages/products/common/hooks/useInvoiceProducts.ts +++ b/src/pages/products/common/hooks/useInvoiceProducts.ts @@ -10,6 +10,7 @@ import { blankLineItem } from '$app/common/constants/blank-line-item'; import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { InvoiceItemType } from '$app/common/interfaces/invoice-item'; import { Product } from '$app/common/interfaces/product'; import { useBlankInvoiceQuery } from '$app/common/queries/invoices'; @@ -22,11 +23,12 @@ interface Params { } export const useInvoiceProducts = (params?: Params) => { - const navigate = useNavigate(); - const { onlyAddToInvoice } = params || {}; + const navigate = useNavigate(); + const company = useCurrentCompany(); + const userNumberPrecision = useUserNumberPrecision(); const { data: blankInvoice } = useBlankInvoiceQuery(); @@ -40,7 +42,9 @@ export const useInvoiceProducts = (params?: Params) => { product_key: product.product_key, quantity: company?.fill_products ? product.quantity : 1, ...(company?.fill_products && { - line_total: Number((product.price * product.quantity).toFixed(2)), + line_total: Number( + (product.price * product.quantity).toFixed(userNumberPrecision) + ), cost: product.price, notes: product.notes, tax_name1: product.tax_name1, diff --git a/src/pages/products/common/hooks/usePurchaseOrderProducts.ts b/src/pages/products/common/hooks/usePurchaseOrderProducts.ts index 1da5f997e..73474e17a 100644 --- a/src/pages/products/common/hooks/usePurchaseOrderProducts.ts +++ b/src/pages/products/common/hooks/usePurchaseOrderProducts.ts @@ -10,6 +10,7 @@ import { blankLineItem } from '$app/common/constants/blank-line-item'; import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { InvoiceItemType } from '$app/common/interfaces/invoice-item'; import { Product } from '$app/common/interfaces/product'; import { useBlankPurchaseOrderQuery } from '$app/common/queries/purchase-orders'; @@ -21,6 +22,7 @@ export const usePurchaseOrderProducts = () => { const navigate = useNavigate(); const company = useCurrentCompany(); + const userNumberPrecision = useUserNumberPrecision(); const { data: blankPurchaseOrder } = useBlankPurchaseOrderQuery(); @@ -34,7 +36,9 @@ export const usePurchaseOrderProducts = () => { product_key: product.product_key, quantity: company?.fill_products ? product.quantity : 1, ...(company?.fill_products && { - line_total: Number((product.price * product.quantity).toFixed(2)), + line_total: Number( + (product.price * product.quantity).toFixed(userNumberPrecision) + ), cost: product.price, notes: product.notes, tax_name1: product.tax_name1, diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index 6895b0068..1f9397e88 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -28,8 +28,10 @@ import { useSetAtom } from 'jotai'; import { useCompanyTimeFormat } from '$app/common/hooks/useCompanyTimeFormat'; import { toast } from '$app/common/helpers/toast/toast'; import { useTranslation } from 'react-i18next'; +import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; -export const calculateTaskHours = (timeLog: string) => { +export const calculateTaskHours = (timeLog: string, precision: number) => { const parsedTimeLogs = parseTimeLog(timeLog); let hoursSum = 0; @@ -41,7 +43,7 @@ export const calculateTaskHours = (timeLog: string) => { const unixStop = dayjs.unix(stop); hoursSum += Number( - (unixStop.diff(unixStart, 'seconds') / 3600).toFixed(4) + (unixStop.diff(unixStart, 'seconds') / 3600).toFixed(precision) ); } }); @@ -53,11 +55,15 @@ export const calculateTaskHours = (timeLog: string) => { export function useInvoiceProject() { const [t] = useTranslation(); const navigate = useNavigate(); + + const numericFormatter = useNumericFormatter(); + const company = useCurrentCompany(); + const userNumberPrecision = useUserNumberPrecision(); - const { dateFormat } = useCurrentCompanyDateFormats(); - const { timeFormat } = useCompanyTimeFormat(); const { data } = useBlankInvoiceQuery(); + const { timeFormat } = useCompanyTimeFormat(); + const { dateFormat } = useCurrentCompanyDateFormats(); const setInvoice = useSetAtom(invoiceAtom); @@ -106,9 +112,9 @@ export function useInvoiceProject() { const unixStart = dayjs.unix(start); const unixStop = dayjs.unix(stop); - const hours = ( - unixStop.diff(unixStart, 'seconds') / 3600 - ).toFixed(4); + const hours = numericFormatter( + (unixStop.diff(unixStart, 'seconds') / 3600).toString() + ); hoursDescription = `• ${hours} ${t('hours')}`; } @@ -147,14 +153,19 @@ export function useInvoiceProject() { } }); - const taskQuantity = calculateTaskHours(task.time_log); + const taskQuantity = calculateTaskHours( + task.time_log, + userNumberPrecision + ); const item: InvoiceItem = { ...blankLineItem(), type_id: InvoiceItemType.Task, cost: task.rate, quantity: taskQuantity, - line_total: Number((task.rate * taskQuantity).toFixed(2)), + line_total: Number( + (task.rate * taskQuantity).toFixed(userNumberPrecision) + ), task_id: task.id, tax_id: '', }; diff --git a/src/pages/recurring-expenses/components/AdditionalInfo.tsx b/src/pages/recurring-expenses/components/AdditionalInfo.tsx index 128023788..536213113 100644 --- a/src/pages/recurring-expenses/components/AdditionalInfo.tsx +++ b/src/pages/recurring-expenses/components/AdditionalInfo.tsx @@ -23,6 +23,7 @@ import { useTranslation } from 'react-i18next'; import { useReactSettings } from '$app/common/hooks/useReactSettings'; import { RecurringExpenseCardProps } from './Details'; import { NumberInputField } from '$app/components/forms/NumberInputField'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; export function AdditionalInfo(props: RecurringExpenseCardProps) { const [t] = useTranslation(); @@ -30,6 +31,7 @@ export function AdditionalInfo(props: RecurringExpenseCardProps) { const company = useCurrentCompany(); const reactSettings = useReactSettings(); + const userNumberPrecision = useUserNumberPrecision(); const resolveCurrency = useResolveCurrency(); const resolveCurrencySeparator = useResolveCurrencySeparator(); @@ -240,7 +242,9 @@ export function AdditionalInfo(props: RecurringExpenseCardProps) { handleChange('exchange_rate', parseFloat(value)) } diff --git a/src/pages/tasks/common/components/TaskSlider.tsx b/src/pages/tasks/common/components/TaskSlider.tsx index 92beeb2cd..9341c930a 100644 --- a/src/pages/tasks/common/components/TaskSlider.tsx +++ b/src/pages/tasks/common/components/TaskSlider.tsx @@ -46,6 +46,7 @@ import { calculateTaskHours } from '$app/pages/projects/common/hooks/useInvoiceP import { date as formatDate } from '$app/common/helpers'; import { useFormatTimeLog } from '../../kanban/common/hooks'; import { TaskClock } from '../../kanban/components/TaskClock'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; export const taskSliderAtom = atom(null); export const taskSliderVisibilityAtom = atom(false); @@ -104,6 +105,7 @@ export function TaskSlider() { showEditAction: true, }); const { dateFormat } = useCurrentCompanyDateFormats(); + const userNumberPrecision = useUserNumberPrecision(); const formatMoney = useFormatMoney(); const formatTimeLog = useFormatTimeLog(); @@ -157,7 +159,8 @@ export function TaskSlider() { {task ? formatMoney( - task.rate * calculateTaskHours(task.time_log), + task.rate * + calculateTaskHours(task.time_log, userNumberPrecision), task.client?.country_id, task.client?.settings.currency_id ) diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index 2f36db29b..84a6088ca 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -27,6 +27,8 @@ import { Task } from '$app/common/interfaces/task'; import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany'; import { cloneDeep } from 'lodash'; import { useTranslation } from 'react-i18next'; +import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; interface Params { tasks: Task[]; @@ -34,13 +36,16 @@ interface Params { export function useAddTasksOnInvoice(params: Params) { const [t] = useTranslation(); - const navigate = useNavigate(); const { tasks } = params; - const company = useCurrentCompany(); - const { dateFormat } = useCurrentCompanyDateFormats(); + const navigate = useNavigate(); + const numericFormatter = useNumericFormatter(); + + const company = useCurrentCompany(); const { timeFormat } = useCompanyTimeFormat(); + const userNumberPrecision = useUserNumberPrecision(); + const { dateFormat } = useCurrentCompanyDateFormats(); const setInvoiceAtom = useSetAtom(invoiceAtom); @@ -64,9 +69,9 @@ export function useAddTasksOnInvoice(params: Params) { const unixStart = dayjs.unix(start); const unixStop = dayjs.unix(stop); - const hours = ( - unixStop.diff(unixStart, 'seconds') / 3600 - ).toFixed(4); + const hours = numericFormatter( + (unixStop.diff(unixStart, 'seconds') / 3600).toString() + ); hoursDescription = `• ${hours} ${t('hours')}`; } @@ -105,14 +110,19 @@ export function useAddTasksOnInvoice(params: Params) { } }); - const taskQuantity = calculateTaskHours(task.time_log); + const taskQuantity = calculateTaskHours( + task.time_log, + userNumberPrecision + ); const item: InvoiceItem = { ...blankLineItem(), type_id: InvoiceItemType.Task, cost: task.rate, quantity: taskQuantity, - line_total: Number((task.rate * taskQuantity).toFixed(2)), + line_total: Number( + (task.rate * taskQuantity).toFixed(userNumberPrecision) + ), task_id: task.id, tax_id: '', custom_value1: task.custom_value1, diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index ca7a44dcc..c28ba05c3 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -29,6 +29,8 @@ import { useTranslation } from 'react-i18next'; import { toast } from '$app/common/helpers/toast/toast'; import { useCompanyTimeFormat } from '$app/common/hooks/useCompanyTimeFormat'; import { useFormatNumber } from '$app/common/hooks/useFormatNumber'; +import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; +import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; interface Params { onlyAddToInvoice?: boolean; @@ -36,13 +38,16 @@ interface Params { export function useInvoiceTask(params?: Params) { const [t] = useTranslation(); - const navigate = useNavigate(); const { onlyAddToInvoice } = params || {}; + const navigate = useNavigate(); + const numericFormatter = useNumericFormatter(); + const company = useCurrentCompany(); const { data } = useBlankInvoiceQuery(); const { timeFormat } = useCompanyTimeFormat(); + const userNumberPrecision = useUserNumberPrecision(); const { dateFormat } = useCurrentCompanyDateFormats(); const setInvoice = useSetAtom(invoiceAtom); @@ -65,7 +70,7 @@ export function useInvoiceTask(params?: Params) { const unixStop = dayjs.unix(stop); hoursSum += unixStop.diff(unixStart, 'seconds') / 3600; - hoursSum = Number(hoursSum.toFixed(4)); + hoursSum = Number(hoursSum.toFixed(userNumberPrecision)); } }); } @@ -123,9 +128,9 @@ export function useInvoiceTask(params?: Params) { const unixStart = dayjs.unix(start); const unixStop = dayjs.unix(stop); - const hours = ( - unixStop.diff(unixStart, 'seconds') / 3600 - ).toFixed(4); + const hours = numericFormatter( + (unixStop.diff(unixStart, 'seconds') / 3600).toString() + ); hoursDescription = `• ${formatNumber(hours)} ${t('hours')}`; } @@ -171,7 +176,9 @@ export function useInvoiceTask(params?: Params) { type_id: InvoiceItemType.Task, cost: task.rate, quantity: taskQuantity, - line_total: Number((task.rate * taskQuantity).toFixed(2)), + line_total: Number( + (task.rate * taskQuantity).toFixed(userNumberPrecision) + ), task_id: task.id, tax_id: '', custom_value1: task.custom_value1, From 0aaf0852c5ddae75190674d7b285a667ef2e94fd Mon Sep 17 00:00:00 2001 From: Civolilah Date: Wed, 25 Sep 2024 18:12:54 +0200 Subject: [PATCH 2/9] Adjusted solution --- src/common/hooks/useGetCurrencySeparators.ts | 100 +++++++++++------- src/common/hooks/useNumericFormatter.ts | 21 +++- .../common/hooks/useInvoiceProject.tsx | 26 +++-- .../components/AdditionalInfo.tsx | 8 +- .../common/hooks/useAddTasksOnInvoice.ts | 20 +++- .../tasks/common/hooks/useInvoiceTask.ts | 23 +++- 6 files changed, 135 insertions(+), 63 deletions(-) diff --git a/src/common/hooks/useGetCurrencySeparators.ts b/src/common/hooks/useGetCurrencySeparators.ts index 10ba41c5a..7fb4f71c9 100644 --- a/src/common/hooks/useGetCurrencySeparators.ts +++ b/src/common/hooks/useGetCurrencySeparators.ts @@ -21,7 +21,7 @@ import { useResolveCountry } from './useResolveCountry'; import { useVendorResolver } from './vendors/useVendorResolver'; export function useGetCurrencySeparators( - setInputCurrencySeparators: React.Dispatch< + setInputCurrencySeparators?: React.Dispatch< React.SetStateAction > ) { @@ -33,61 +33,87 @@ export function useGetCurrencySeparators( const currencyResolver = useCurrencyResolver(); const resolveCountry = useResolveCountry(); - return (relationId: string, relationType: RelationType) => { + return async (relationId: string, relationType: RelationType) => { + let separators: DecimalInputSeparators | undefined; + if (relationId.length >= 1 && relationType === 'client_id') { - clientResolver.find(relationId).then((client: Client) => - currencyResolver + await clientResolver.find(relationId).then(async (client: Client) => { + await currencyResolver .find(client.settings.currency_id || company.settings?.currency_id) .then((currency: Currency | undefined) => { const companyCountry = resolveCountry(company.settings.country_id); - currency && - setInputCurrencySeparators({ - thousandSeparator: - companyCountry?.thousand_separator || - currency.thousand_separator, - decimalSeparator: - companyCountry?.decimal_separator || - currency.decimal_separator, - precision: currency.precision, - }); - }) - ); + const currentSeparators = { + thousandSeparator: + companyCountry?.thousand_separator || + currency?.thousand_separator || + ',', + decimalSeparator: + companyCountry?.decimal_separator || + currency?.decimal_separator || + '.', + precision: currency?.precision || 2, + }; + + if (setInputCurrencySeparators) { + setInputCurrencySeparators(currentSeparators); + } else { + separators = currentSeparators; + } + }); + }); } else if (relationId.length >= 1 && relationType === 'vendor_id') { - vendorResolver.find(relationId).then((vendor: Vendor) => + await vendorResolver.find(relationId).then((vendor: Vendor) => currencyResolver .find(vendor.currency_id || company.settings?.currency_id) .then((currency: Currency | undefined) => { const companyCountry = resolveCountry(company.settings.country_id); - currency && - setInputCurrencySeparators({ - thousandSeparator: - companyCountry?.thousand_separator || - currency.thousand_separator, - decimalSeparator: - companyCountry?.decimal_separator || - currency.decimal_separator, - precision: currency.precision, - }); + const currentSeparators = { + thousandSeparator: + companyCountry?.thousand_separator || + currency?.thousand_separator || + ',', + decimalSeparator: + companyCountry?.decimal_separator || + currency?.decimal_separator || + '.', + precision: currency?.precision || 2, + }; + + if (setInputCurrencySeparators) { + setInputCurrencySeparators(currentSeparators); + } else { + separators = currentSeparators; + } }) ); } else { - currencyResolver + await currencyResolver .find(company.settings?.currency_id) .then((currency: Currency | undefined) => { const companyCountry = resolveCountry(company.settings.country_id); - currency && - setInputCurrencySeparators({ - thousandSeparator: - companyCountry?.thousand_separator || - currency.thousand_separator, - decimalSeparator: - companyCountry?.decimal_separator || currency.decimal_separator, - precision: currency.precision, - }); + const currentSeparators = { + thousandSeparator: + companyCountry?.thousand_separator || + currency?.thousand_separator || + ',', + decimalSeparator: + companyCountry?.decimal_separator || + currency?.decimal_separator || + '.', + precision: currency?.precision || 2, + }; + + if (setInputCurrencySeparators) { + setInputCurrencySeparators(currentSeparators); + } else { + separators = currentSeparators; + } }); } + + return separators; }; } diff --git a/src/common/hooks/useNumericFormatter.ts b/src/common/hooks/useNumericFormatter.ts index ffc71de38..6b0e286d6 100644 --- a/src/common/hooks/useNumericFormatter.ts +++ b/src/common/hooks/useNumericFormatter.ts @@ -16,11 +16,24 @@ export function useNumericFormatter() { const company = useCurrentCompany(); const userNumberPrecision = useUserNumberPrecision(); - return (numStr: string) => { + return ( + numStr: string, + thousandSeparator?: string, + decimalSeparator?: string, + precision?: number + ) => { return numericFormatter(numStr, { - thousandSeparator: company?.use_comma_as_decimal_place ? '.' : ',', - decimalSeparator: company?.use_comma_as_decimal_place ? ',' : '.', - decimalScale: userNumberPrecision, + thousandSeparator: thousandSeparator + ? thousandSeparator + : company?.use_comma_as_decimal_place + ? '.' + : ',', + decimalSeparator: decimalSeparator + ? decimalSeparator + : company?.use_comma_as_decimal_place + ? ',' + : '.', + decimalScale: precision || userNumberPrecision, }); }; } diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index 1f9397e88..9722fb931 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -30,9 +30,11 @@ import { toast } from '$app/common/helpers/toast/toast'; import { useTranslation } from 'react-i18next'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; +import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; -export const calculateTaskHours = (timeLog: string, precision: number) => { +export const calculateTaskHours = (timeLog: string, precision?: number) => { const parsedTimeLogs = parseTimeLog(timeLog); + const userNumberPrecision = useUserNumberPrecision(); let hoursSum = 0; @@ -43,7 +45,9 @@ export const calculateTaskHours = (timeLog: string, precision: number) => { const unixStop = dayjs.unix(stop); hoursSum += Number( - (unixStop.diff(unixStart, 'seconds') / 3600).toFixed(precision) + (unixStop.diff(unixStart, 'seconds') / 3600).toFixed( + precision || userNumberPrecision + ) ); } }); @@ -57,9 +61,9 @@ export function useInvoiceProject() { const navigate = useNavigate(); const numericFormatter = useNumericFormatter(); + const getCurrencySeparators = useGetCurrencySeparators(); const company = useCurrentCompany(); - const userNumberPrecision = useUserNumberPrecision(); const { data } = useBlankInvoiceQuery(); const { timeFormat } = useCompanyTimeFormat(); @@ -67,7 +71,7 @@ export function useInvoiceProject() { const setInvoice = useSetAtom(invoiceAtom); - return (tasks: Task[], clientId: string, projectId: string) => { + return async (tasks: Task[], clientId: string, projectId: string) => { if (data) { const invoice: Invoice = { ...data }; @@ -96,6 +100,11 @@ export function useInvoiceProject() { invoice.client_id = clientId; invoice.line_items = []; + const currencySeparators = await getCurrencySeparators( + clientId, + 'client_id' + ); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -113,7 +122,10 @@ export function useInvoiceProject() { const unixStop = dayjs.unix(stop); const hours = numericFormatter( - (unixStop.diff(unixStart, 'seconds') / 3600).toString() + (unixStop.diff(unixStart, 'seconds') / 3600).toString(), + currencySeparators?.thousandSeparator, + currencySeparators?.decimalSeparator, + currencySeparators?.precision ); hoursDescription = `• ${hours} ${t('hours')}`; @@ -155,7 +167,7 @@ export function useInvoiceProject() { const taskQuantity = calculateTaskHours( task.time_log, - userNumberPrecision + currencySeparators?.precision ); const item: InvoiceItem = { @@ -164,7 +176,7 @@ export function useInvoiceProject() { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed(userNumberPrecision) + (task.rate * taskQuantity).toFixed(currencySeparators?.precision) ), task_id: task.id, tax_id: '', diff --git a/src/pages/recurring-expenses/components/AdditionalInfo.tsx b/src/pages/recurring-expenses/components/AdditionalInfo.tsx index 536213113..eaef9e6bf 100644 --- a/src/pages/recurring-expenses/components/AdditionalInfo.tsx +++ b/src/pages/recurring-expenses/components/AdditionalInfo.tsx @@ -23,7 +23,6 @@ import { useTranslation } from 'react-i18next'; import { useReactSettings } from '$app/common/hooks/useReactSettings'; import { RecurringExpenseCardProps } from './Details'; import { NumberInputField } from '$app/components/forms/NumberInputField'; -import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; export function AdditionalInfo(props: RecurringExpenseCardProps) { const [t] = useTranslation(); @@ -31,7 +30,6 @@ export function AdditionalInfo(props: RecurringExpenseCardProps) { const company = useCurrentCompany(); const reactSettings = useReactSettings(); - const userNumberPrecision = useUserNumberPrecision(); const resolveCurrency = useResolveCurrency(); const resolveCurrencySeparator = useResolveCurrencySeparator(); @@ -241,10 +239,8 @@ export function AdditionalInfo(props: RecurringExpenseCardProps) { - handleChange('exchange_rate', parseFloat(value)) } diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index 84a6088ca..d09f9cd30 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -29,6 +29,7 @@ import { cloneDeep } from 'lodash'; import { useTranslation } from 'react-i18next'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; +import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; interface Params { tasks: Task[]; @@ -41,6 +42,7 @@ export function useAddTasksOnInvoice(params: Params) { const navigate = useNavigate(); const numericFormatter = useNumericFormatter(); + const getCurrencySeparators = useGetCurrencySeparators(); const company = useCurrentCompany(); const { timeFormat } = useCompanyTimeFormat(); @@ -49,10 +51,15 @@ export function useAddTasksOnInvoice(params: Params) { const setInvoiceAtom = useSetAtom(invoiceAtom); - return (invoice: Invoice) => { + return async (invoice: Invoice) => { const updatedInvoice = cloneDeep(invoice); if (tasks) { + const currencySeparators = await getCurrencySeparators( + tasks[0].client_id, + 'client_id' + ); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -70,7 +77,10 @@ export function useAddTasksOnInvoice(params: Params) { const unixStop = dayjs.unix(stop); const hours = numericFormatter( - (unixStop.diff(unixStart, 'seconds') / 3600).toString() + (unixStop.diff(unixStart, 'seconds') / 3600).toString(), + currencySeparators?.thousandSeparator, + currencySeparators?.decimalSeparator, + currencySeparators?.precision ); hoursDescription = `• ${hours} ${t('hours')}`; @@ -112,7 +122,7 @@ export function useAddTasksOnInvoice(params: Params) { const taskQuantity = calculateTaskHours( task.time_log, - userNumberPrecision + currencySeparators?.precision ); const item: InvoiceItem = { @@ -121,7 +131,9 @@ export function useAddTasksOnInvoice(params: Params) { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed(userNumberPrecision) + (task.rate * taskQuantity).toFixed( + currencySeparators?.precision || userNumberPrecision + ) ), task_id: task.id, tax_id: '', diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index c28ba05c3..98cc1d1da 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -31,6 +31,7 @@ import { useCompanyTimeFormat } from '$app/common/hooks/useCompanyTimeFormat'; import { useFormatNumber } from '$app/common/hooks/useFormatNumber'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; +import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; interface Params { onlyAddToInvoice?: boolean; @@ -43,6 +44,7 @@ export function useInvoiceTask(params?: Params) { const navigate = useNavigate(); const numericFormatter = useNumericFormatter(); + const getCurrencySeparators = useGetCurrencySeparators(); const company = useCurrentCompany(); const { data } = useBlankInvoiceQuery(); @@ -54,7 +56,7 @@ export function useInvoiceTask(params?: Params) { const formatNumber = useFormatNumber(); - const calculateTaskHours = (timeLog: string) => { + const calculateTaskHours = (timeLog: string, precision?: number) => { const parsedTimeLogs = parseTimeLog(timeLog); let hoursSum = 0; @@ -70,7 +72,7 @@ export function useInvoiceTask(params?: Params) { const unixStop = dayjs.unix(stop); hoursSum += unixStop.diff(unixStart, 'seconds') / 3600; - hoursSum = Number(hoursSum.toFixed(userNumberPrecision)); + hoursSum = Number(hoursSum.toFixed(precision || userNumberPrecision)); } }); } @@ -112,6 +114,11 @@ export function useInvoiceTask(params?: Params) { invoice.project_id = tasks[0]?.project_id; } + const currencySeparators = await getCurrencySeparators( + tasks[0]?.client_id, + 'client_id' + ); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -129,7 +136,10 @@ export function useInvoiceTask(params?: Params) { const unixStop = dayjs.unix(stop); const hours = numericFormatter( - (unixStop.diff(unixStart, 'seconds') / 3600).toString() + (unixStop.diff(unixStart, 'seconds') / 3600).toString(), + currencySeparators?.thousandSeparator, + currencySeparators?.decimalSeparator, + currencySeparators?.precision ); hoursDescription = `• ${formatNumber(hours)} ${t('hours')}`; @@ -169,7 +179,10 @@ export function useInvoiceTask(params?: Params) { } }); - const taskQuantity = calculateTaskHours(task.time_log); + const taskQuantity = calculateTaskHours( + task.time_log, + currencySeparators?.precision + ); const item: InvoiceItem = { ...blankLineItem(), @@ -177,7 +190,7 @@ export function useInvoiceTask(params?: Params) { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed(userNumberPrecision) + (task.rate * taskQuantity).toFixed(currencySeparators?.precision) ), task_id: task.id, tax_id: '', From bdffdad397334d7ae9a0d1ff9a79e04c1c4e305f Mon Sep 17 00:00:00 2001 From: Civolilah Date: Wed, 25 Sep 2024 18:56:43 +0200 Subject: [PATCH 3/9] Changed precision --- src/common/hooks/useGetCurrencySeparators.ts | 8 ++++---- src/pages/projects/common/hooks/useInvoiceProject.tsx | 5 +++-- src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts | 8 +++----- src/pages/tasks/common/hooks/useInvoiceTask.ts | 4 ++-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/common/hooks/useGetCurrencySeparators.ts b/src/common/hooks/useGetCurrencySeparators.ts index 7fb4f71c9..c84335f44 100644 --- a/src/common/hooks/useGetCurrencySeparators.ts +++ b/src/common/hooks/useGetCurrencySeparators.ts @@ -63,8 +63,8 @@ export function useGetCurrencySeparators( }); }); } else if (relationId.length >= 1 && relationType === 'vendor_id') { - await vendorResolver.find(relationId).then((vendor: Vendor) => - currencyResolver + await vendorResolver.find(relationId).then(async (vendor: Vendor) => { + await currencyResolver .find(vendor.currency_id || company.settings?.currency_id) .then((currency: Currency | undefined) => { const companyCountry = resolveCountry(company.settings.country_id); @@ -86,8 +86,8 @@ export function useGetCurrencySeparators( } else { separators = currentSeparators; } - }) - ); + }); + }); } else { await currencyResolver .find(company.settings?.currency_id) diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index 9722fb931..b7a4b42a9 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -64,6 +64,7 @@ export function useInvoiceProject() { const getCurrencySeparators = useGetCurrencySeparators(); const company = useCurrentCompany(); + const userNumberPrecision = useUserNumberPrecision(); const { data } = useBlankInvoiceQuery(); const { timeFormat } = useCompanyTimeFormat(); @@ -167,7 +168,7 @@ export function useInvoiceProject() { const taskQuantity = calculateTaskHours( task.time_log, - currencySeparators?.precision + userNumberPrecision ); const item: InvoiceItem = { @@ -176,7 +177,7 @@ export function useInvoiceProject() { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed(currencySeparators?.precision) + (task.rate * taskQuantity).toFixed(userNumberPrecision) ), task_id: task.id, tax_id: '', diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index d09f9cd30..eec465cab 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -56,7 +56,7 @@ export function useAddTasksOnInvoice(params: Params) { if (tasks) { const currencySeparators = await getCurrencySeparators( - tasks[0].client_id, + tasks[0]?.client_id, 'client_id' ); @@ -122,7 +122,7 @@ export function useAddTasksOnInvoice(params: Params) { const taskQuantity = calculateTaskHours( task.time_log, - currencySeparators?.precision + userNumberPrecision ); const item: InvoiceItem = { @@ -131,9 +131,7 @@ export function useAddTasksOnInvoice(params: Params) { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed( - currencySeparators?.precision || userNumberPrecision - ) + (task.rate * taskQuantity).toFixed(userNumberPrecision) ), task_id: task.id, tax_id: '', diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index 98cc1d1da..f93d5bb38 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -181,7 +181,7 @@ export function useInvoiceTask(params?: Params) { const taskQuantity = calculateTaskHours( task.time_log, - currencySeparators?.precision + userNumberPrecision ); const item: InvoiceItem = { @@ -190,7 +190,7 @@ export function useInvoiceTask(params?: Params) { cost: task.rate, quantity: taskQuantity, line_total: Number( - (task.rate * taskQuantity).toFixed(currencySeparators?.precision) + (task.rate * taskQuantity).toFixed(userNumberPrecision) ), task_id: task.id, tax_id: '', From ec5fe634bc64ac05e95f14650380458c3a42fc5f Mon Sep 17 00:00:00 2001 From: Civolilah Date: Thu, 26 Sep 2024 19:20:56 +0200 Subject: [PATCH 4/9] Implemented timezone formatting when invoicing project --- .../helpers/dates/date-format-resolver.ts | 25 ++++++++ .../helpers/timezone/timezone-resolver.ts | 23 +++++++ src/common/hooks/useCompanyTimeZone.ts | 8 ++- .../useResolveDateAndTimeClientFormat.ts | 62 ++++++++++++++++++ .../common/hooks/useInvoiceProject.tsx | 63 ++++++++++++++++--- 5 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 src/common/helpers/dates/date-format-resolver.ts create mode 100644 src/common/helpers/timezone/timezone-resolver.ts create mode 100644 src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts diff --git a/src/common/helpers/dates/date-format-resolver.ts b/src/common/helpers/dates/date-format-resolver.ts new file mode 100644 index 000000000..3135b217f --- /dev/null +++ b/src/common/helpers/dates/date-format-resolver.ts @@ -0,0 +1,25 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { useStaticsQuery } from '../../queries/statics'; + +export function useResolveDateFormat() { + const statics = useStaticsQuery(); + + return (id: string) => { + if (statics) { + return statics.data?.date_formats.find( + (dateFormat) => dateFormat.id === id + ); + } + + return undefined; + }; +} diff --git a/src/common/helpers/timezone/timezone-resolver.ts b/src/common/helpers/timezone/timezone-resolver.ts new file mode 100644 index 000000000..3f5ebfb33 --- /dev/null +++ b/src/common/helpers/timezone/timezone-resolver.ts @@ -0,0 +1,23 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { useStaticsQuery } from '../../queries/statics'; + +export function useResolveTimezone() { + const statics = useStaticsQuery(); + + return (id: string) => { + if (statics) { + return statics.data?.timezones.find((timezone) => timezone.id === id); + } + + return undefined; + }; +} diff --git a/src/common/hooks/useCompanyTimeZone.ts b/src/common/hooks/useCompanyTimeZone.ts index 4260c0094..c7eadce0c 100644 --- a/src/common/hooks/useCompanyTimeZone.ts +++ b/src/common/hooks/useCompanyTimeZone.ts @@ -11,6 +11,7 @@ import { useStaticsQuery } from '$app/common/queries/statics'; import { useEffect, useState } from 'react'; import { useCurrentCompany } from './useCurrentCompany'; +import { Timezone } from '../interfaces/timezone'; export function useCompanyTimeZone() { const company = useCurrentCompany(); @@ -18,7 +19,7 @@ export function useCompanyTimeZone() { const { data: statics } = useStaticsQuery(); const [timeZoneId, setTimeZoneId] = useState('1'); - const [timeZone, setTimZone] = useState('America/Tijuana'); + const [timeZone, setTimeZone] = useState(); useEffect(() => { if (statics?.timezones) { @@ -27,7 +28,7 @@ export function useCompanyTimeZone() { ); if (result) { - setTimZone(result.name); + setTimeZone(result); setTimeZoneId(result.id); } } @@ -35,6 +36,7 @@ export function useCompanyTimeZone() { return { timeZoneId, - timeZone, + timeZone: timeZone?.name, + timeZoneOffset: timeZone?.utc_offset, }; } diff --git a/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts new file mode 100644 index 000000000..a58f88402 --- /dev/null +++ b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts @@ -0,0 +1,62 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */ + +import { useResolveDateFormat } from '$app/common/helpers/dates/date-format-resolver'; +import { useResolveTimezone } from '$app/common/helpers/timezone/timezone-resolver'; +import { useClientResolver } from '$app/common/hooks/clients/useClientResolver'; +import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany'; +import { Client } from '$app/common/interfaces/client'; + +export function useResolveDateAndTimeClientFormat() { + const company = useCurrentCompany(); + const clientResolver = useClientResolver(); + + const resolveTimezone = useResolveTimezone(); + const resolveDateFormat = useResolveDateFormat(); + + const getTimeFormat = (militaryTime: boolean) => { + return militaryTime ? 'HH:mm:ss' : 'hh:mm:ss A'; + }; + + return async (relationId: string) => { + const timeZone = resolveTimezone(company?.settings.timezone_id); + const dateFormat = resolveDateFormat(company?.settings.date_format_id); + + const dateTimeFormats = { + dateFormat, + timeZone, + timeFormat: getTimeFormat(Boolean(company?.settings.military_time)), + }; + + if (relationId.length >= 1) { + await clientResolver.find(relationId).then((client: Client) => { + if (client.settings.date_format_id) { + dateTimeFormats.dateFormat = resolveDateFormat( + client.settings.date_format_id + ); + } + + if (client.settings.timezone_id) { + dateTimeFormats.timeZone = resolveTimezone( + client.settings.timezone_id + ); + } + + if (client.settings.military_time) { + dateTimeFormats.timeFormat = getTimeFormat( + Boolean(client.settings.military_time) + ); + } + }); + } + + return dateTimeFormats; + }; +} diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index b7a4b42a9..5f5a217b3 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -31,10 +31,11 @@ import { useTranslation } from 'react-i18next'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; +import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; +import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; export const calculateTaskHours = (timeLog: string, precision?: number) => { const parsedTimeLogs = parseTimeLog(timeLog); - const userNumberPrecision = useUserNumberPrecision(); let hoursSum = 0; @@ -45,9 +46,7 @@ export const calculateTaskHours = (timeLog: string, precision?: number) => { const unixStop = dayjs.unix(stop); hoursSum += Number( - (unixStop.diff(unixStart, 'seconds') / 3600).toFixed( - precision || userNumberPrecision - ) + (unixStop.diff(unixStart, 'seconds') / 3600).toFixed(precision) ); } }); @@ -62,6 +61,7 @@ export function useInvoiceProject() { const numericFormatter = useNumericFormatter(); const getCurrencySeparators = useGetCurrencySeparators(); + const resolveDateAndTimeClientFormat = useResolveDateAndTimeClientFormat(); const company = useCurrentCompany(); const userNumberPrecision = useUserNumberPrecision(); @@ -70,6 +70,8 @@ export function useInvoiceProject() { const { timeFormat } = useCompanyTimeFormat(); const { dateFormat } = useCurrentCompanyDateFormats(); + const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); + const setInvoice = useSetAtom(invoiceAtom); return async (tasks: Task[], clientId: string, projectId: string) => { @@ -106,6 +108,12 @@ export function useInvoiceProject() { 'client_id' ); + const { + dateFormat: clientDateFormat, + timeFormat: clientTimeFormat, + timeZone: clientTimezone, + } = await resolveDateAndTimeClientFormat(clientId); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -139,15 +147,56 @@ export function useInvoiceProject() { } if (company.invoice_task_datelog) { - description.push(dayjs.unix(start).format(dateFormat)); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format( + clientDateFormat?.format_moment + ? clientDateFormat.format_moment + : dateFormat + ) + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(start).format(timeFormat) + ' - '); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + + ' - ' + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(stop).format(timeFormat)); + description.push( + dayjs + .unix(stop) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ); } if (company.invoice_task_hours) { From aa03343bd9be31c32ec47de19f0d43fccfe33412 Mon Sep 17 00:00:00 2001 From: Civolilah Date: Thu, 26 Sep 2024 20:37:01 +0200 Subject: [PATCH 5/9] Applied logic on invoicing tasks and adding task to invoices --- .../useResolveDateAndTimeClientFormat.ts | 8 +-- .../common/hooks/useAddTasksOnInvoice.ts | 58 ++++++++++++++++++- .../tasks/common/hooks/useInvoiceTask.ts | 57 +++++++++++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts index a58f88402..cc6789f8a 100644 --- a/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts +++ b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts @@ -49,11 +49,9 @@ export function useResolveDateAndTimeClientFormat() { ); } - if (client.settings.military_time) { - dateTimeFormats.timeFormat = getTimeFormat( - Boolean(client.settings.military_time) - ); - } + dateTimeFormats.timeFormat = getTimeFormat( + Boolean(client.settings.military_time) + ); }); } diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index eec465cab..644d0b91b 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -30,6 +30,8 @@ import { useTranslation } from 'react-i18next'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; +import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; +import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; interface Params { tasks: Task[]; @@ -43,12 +45,15 @@ export function useAddTasksOnInvoice(params: Params) { const navigate = useNavigate(); const numericFormatter = useNumericFormatter(); const getCurrencySeparators = useGetCurrencySeparators(); + const resolveDateAndTimeClientFormat = useResolveDateAndTimeClientFormat(); const company = useCurrentCompany(); const { timeFormat } = useCompanyTimeFormat(); const userNumberPrecision = useUserNumberPrecision(); const { dateFormat } = useCurrentCompanyDateFormats(); + const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); + const setInvoiceAtom = useSetAtom(invoiceAtom); return async (invoice: Invoice) => { @@ -60,6 +65,12 @@ export function useAddTasksOnInvoice(params: Params) { 'client_id' ); + const { + dateFormat: clientDateFormat, + timeFormat: clientTimeFormat, + timeZone: clientTimezone, + } = await resolveDateAndTimeClientFormat(tasks[0]?.client_id); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -93,15 +104,56 @@ export function useAddTasksOnInvoice(params: Params) { } if (company.invoice_task_datelog) { - description.push(dayjs.unix(start).format(dateFormat)); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format( + clientDateFormat?.format_moment + ? clientDateFormat.format_moment + : dateFormat + ) + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(start).format(timeFormat) + ' - '); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + + ' - ' + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(stop).format(timeFormat)); + description.push( + dayjs + .unix(stop) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ); } if (company.invoice_task_hours) { diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index f93d5bb38..bceb1921c 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -32,6 +32,8 @@ import { useFormatNumber } from '$app/common/hooks/useFormatNumber'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; +import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; +import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; interface Params { onlyAddToInvoice?: boolean; @@ -51,10 +53,12 @@ export function useInvoiceTask(params?: Params) { const { timeFormat } = useCompanyTimeFormat(); const userNumberPrecision = useUserNumberPrecision(); const { dateFormat } = useCurrentCompanyDateFormats(); + const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); const setInvoice = useSetAtom(invoiceAtom); const formatNumber = useFormatNumber(); + const resolveDateAndTimeClientFormat = useResolveDateAndTimeClientFormat(); const calculateTaskHours = (timeLog: string, precision?: number) => { const parsedTimeLogs = parseTimeLog(timeLog); @@ -119,6 +123,12 @@ export function useInvoiceTask(params?: Params) { 'client_id' ); + const { + dateFormat: clientDateFormat, + timeFormat: clientTimeFormat, + timeZone: clientTimezone, + } = await resolveDateAndTimeClientFormat(tasks[0]?.client_id); + tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); const parsed: string[] = []; @@ -152,15 +162,56 @@ export function useInvoiceTask(params?: Params) { } if (company.invoice_task_datelog) { - description.push(dayjs.unix(start).format(dateFormat)); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format( + clientDateFormat?.format_moment + ? clientDateFormat.format_moment + : dateFormat + ) + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(start).format(timeFormat) + ' - '); + description.push( + dayjs + .unix(start) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + + ' - ' + ); } if (company.invoice_task_timelog) { - description.push(dayjs.unix(stop).format(timeFormat)); + description.push( + dayjs + .unix(stop) + .add( + clientTimezone?.utc_offset + ? clientTimezone.utc_offset + : companyTimezoneOffset + ? companyTimezoneOffset + : 0, + 'seconds' + ) + .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ); } if (company.invoice_task_hours) { From fc07845aac0f3a98b10a46fffd2f68adbbca3f17 Mon Sep 17 00:00:00 2001 From: Civolilah Date: Thu, 26 Sep 2024 20:52:12 +0200 Subject: [PATCH 6/9] Cleanup --- src/common/hooks/useCompanyTimeZone.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/common/hooks/useCompanyTimeZone.ts b/src/common/hooks/useCompanyTimeZone.ts index c7eadce0c..2f3a71259 100644 --- a/src/common/hooks/useCompanyTimeZone.ts +++ b/src/common/hooks/useCompanyTimeZone.ts @@ -18,7 +18,6 @@ export function useCompanyTimeZone() { const { data: statics } = useStaticsQuery(); - const [timeZoneId, setTimeZoneId] = useState('1'); const [timeZone, setTimeZone] = useState(); useEffect(() => { @@ -29,14 +28,13 @@ export function useCompanyTimeZone() { if (result) { setTimeZone(result); - setTimeZoneId(result.id); } } }, [company, statics]); return { - timeZoneId, - timeZone: timeZone?.name, - timeZoneOffset: timeZone?.utc_offset, + timeZoneId: timeZone?.id || '1', + timeZone: timeZone?.name || '', + timeZoneOffset: timeZone?.utc_offset || 0, }; } From e5baeffdaca7c489c44292f244b3ee1b833cc4c5 Mon Sep 17 00:00:00 2001 From: Civolilah Date: Fri, 27 Sep 2024 11:51:32 +0200 Subject: [PATCH 7/9] Deleted unnecessary parts of the logic --- .../helpers/timezone/timezone-resolver.ts | 23 ------------- src/common/hooks/useCompanyTimeZone.ts | 12 +++---- .../useResolveDateAndTimeClientFormat.ts | 10 ------ .../common/hooks/useInvoiceProject.tsx | 34 ++----------------- .../common/hooks/useAddTasksOnInvoice.ts | 34 ++----------------- .../tasks/common/hooks/useInvoiceTask.ts | 33 ++---------------- 6 files changed, 12 insertions(+), 134 deletions(-) delete mode 100644 src/common/helpers/timezone/timezone-resolver.ts diff --git a/src/common/helpers/timezone/timezone-resolver.ts b/src/common/helpers/timezone/timezone-resolver.ts deleted file mode 100644 index 3f5ebfb33..000000000 --- a/src/common/helpers/timezone/timezone-resolver.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com). - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */ - -import { useStaticsQuery } from '../../queries/statics'; - -export function useResolveTimezone() { - const statics = useStaticsQuery(); - - return (id: string) => { - if (statics) { - return statics.data?.timezones.find((timezone) => timezone.id === id); - } - - return undefined; - }; -} diff --git a/src/common/hooks/useCompanyTimeZone.ts b/src/common/hooks/useCompanyTimeZone.ts index 2f3a71259..4260c0094 100644 --- a/src/common/hooks/useCompanyTimeZone.ts +++ b/src/common/hooks/useCompanyTimeZone.ts @@ -11,14 +11,14 @@ import { useStaticsQuery } from '$app/common/queries/statics'; import { useEffect, useState } from 'react'; import { useCurrentCompany } from './useCurrentCompany'; -import { Timezone } from '../interfaces/timezone'; export function useCompanyTimeZone() { const company = useCurrentCompany(); const { data: statics } = useStaticsQuery(); - const [timeZone, setTimeZone] = useState(); + const [timeZoneId, setTimeZoneId] = useState('1'); + const [timeZone, setTimZone] = useState('America/Tijuana'); useEffect(() => { if (statics?.timezones) { @@ -27,14 +27,14 @@ export function useCompanyTimeZone() { ); if (result) { - setTimeZone(result); + setTimZone(result.name); + setTimeZoneId(result.id); } } }, [company, statics]); return { - timeZoneId: timeZone?.id || '1', - timeZone: timeZone?.name || '', - timeZoneOffset: timeZone?.utc_offset || 0, + timeZoneId, + timeZone, }; } diff --git a/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts index cc6789f8a..c9dcd1e63 100644 --- a/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts +++ b/src/pages/clients/common/hooks/useResolveDateAndTimeClientFormat.ts @@ -9,7 +9,6 @@ */ import { useResolveDateFormat } from '$app/common/helpers/dates/date-format-resolver'; -import { useResolveTimezone } from '$app/common/helpers/timezone/timezone-resolver'; import { useClientResolver } from '$app/common/hooks/clients/useClientResolver'; import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany'; import { Client } from '$app/common/interfaces/client'; @@ -18,7 +17,6 @@ export function useResolveDateAndTimeClientFormat() { const company = useCurrentCompany(); const clientResolver = useClientResolver(); - const resolveTimezone = useResolveTimezone(); const resolveDateFormat = useResolveDateFormat(); const getTimeFormat = (militaryTime: boolean) => { @@ -26,12 +24,10 @@ export function useResolveDateAndTimeClientFormat() { }; return async (relationId: string) => { - const timeZone = resolveTimezone(company?.settings.timezone_id); const dateFormat = resolveDateFormat(company?.settings.date_format_id); const dateTimeFormats = { dateFormat, - timeZone, timeFormat: getTimeFormat(Boolean(company?.settings.military_time)), }; @@ -43,12 +39,6 @@ export function useResolveDateAndTimeClientFormat() { ); } - if (client.settings.timezone_id) { - dateTimeFormats.timeZone = resolveTimezone( - client.settings.timezone_id - ); - } - dateTimeFormats.timeFormat = getTimeFormat( Boolean(client.settings.military_time) ); diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index 5f5a217b3..4ff8ae0c8 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -32,7 +32,6 @@ import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; -import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; export const calculateTaskHours = (timeLog: string, precision?: number) => { const parsedTimeLogs = parseTimeLog(timeLog); @@ -70,8 +69,6 @@ export function useInvoiceProject() { const { timeFormat } = useCompanyTimeFormat(); const { dateFormat } = useCurrentCompanyDateFormats(); - const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); - const setInvoice = useSetAtom(invoiceAtom); return async (tasks: Task[], clientId: string, projectId: string) => { @@ -108,11 +105,8 @@ export function useInvoiceProject() { 'client_id' ); - const { - dateFormat: clientDateFormat, - timeFormat: clientTimeFormat, - timeZone: clientTimezone, - } = await resolveDateAndTimeClientFormat(clientId); + const { dateFormat: clientDateFormat, timeFormat: clientTimeFormat } = + await resolveDateAndTimeClientFormat(clientId); tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); @@ -150,14 +144,6 @@ export function useInvoiceProject() { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format( clientDateFormat?.format_moment ? clientDateFormat.format_moment @@ -170,14 +156,6 @@ export function useInvoiceProject() { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ' - ' ); @@ -187,14 +165,6 @@ export function useInvoiceProject() { description.push( dayjs .unix(stop) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) ); } diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index 644d0b91b..0a45ea3f1 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -31,7 +31,6 @@ import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; -import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; interface Params { tasks: Task[]; @@ -52,8 +51,6 @@ export function useAddTasksOnInvoice(params: Params) { const userNumberPrecision = useUserNumberPrecision(); const { dateFormat } = useCurrentCompanyDateFormats(); - const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); - const setInvoiceAtom = useSetAtom(invoiceAtom); return async (invoice: Invoice) => { @@ -65,11 +62,8 @@ export function useAddTasksOnInvoice(params: Params) { 'client_id' ); - const { - dateFormat: clientDateFormat, - timeFormat: clientTimeFormat, - timeZone: clientTimezone, - } = await resolveDateAndTimeClientFormat(tasks[0]?.client_id); + const { dateFormat: clientDateFormat, timeFormat: clientTimeFormat } = + await resolveDateAndTimeClientFormat(tasks[0]?.client_id); tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); @@ -107,14 +101,6 @@ export function useAddTasksOnInvoice(params: Params) { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format( clientDateFormat?.format_moment ? clientDateFormat.format_moment @@ -127,14 +113,6 @@ export function useAddTasksOnInvoice(params: Params) { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ' - ' ); @@ -144,14 +122,6 @@ export function useAddTasksOnInvoice(params: Params) { description.push( dayjs .unix(stop) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) ); } diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index bceb1921c..d7af2fd4c 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -33,7 +33,6 @@ import { useUserNumberPrecision } from '$app/common/hooks/useUserNumberPrecision import { useNumericFormatter } from '$app/common/hooks/useNumericFormatter'; import { useGetCurrencySeparators } from '$app/common/hooks/useGetCurrencySeparators'; import { useResolveDateAndTimeClientFormat } from '$app/pages/clients/common/hooks/useResolveDateAndTimeClientFormat'; -import { useCompanyTimeZone } from '$app/common/hooks/useCompanyTimeZone'; interface Params { onlyAddToInvoice?: boolean; @@ -53,7 +52,6 @@ export function useInvoiceTask(params?: Params) { const { timeFormat } = useCompanyTimeFormat(); const userNumberPrecision = useUserNumberPrecision(); const { dateFormat } = useCurrentCompanyDateFormats(); - const { timeZoneOffset: companyTimezoneOffset } = useCompanyTimeZone(); const setInvoice = useSetAtom(invoiceAtom); @@ -123,11 +121,8 @@ export function useInvoiceTask(params?: Params) { 'client_id' ); - const { - dateFormat: clientDateFormat, - timeFormat: clientTimeFormat, - timeZone: clientTimezone, - } = await resolveDateAndTimeClientFormat(tasks[0]?.client_id); + const { dateFormat: clientDateFormat, timeFormat: clientTimeFormat } = + await resolveDateAndTimeClientFormat(tasks[0]?.client_id); tasks.forEach((task: Task) => { const logs = parseTimeLog(task.time_log); @@ -165,14 +160,6 @@ export function useInvoiceTask(params?: Params) { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format( clientDateFormat?.format_moment ? clientDateFormat.format_moment @@ -185,14 +172,6 @@ export function useInvoiceTask(params?: Params) { description.push( dayjs .unix(start) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) + ' - ' ); @@ -202,14 +181,6 @@ export function useInvoiceTask(params?: Params) { description.push( dayjs .unix(stop) - .add( - clientTimezone?.utc_offset - ? clientTimezone.utc_offset - : companyTimezoneOffset - ? companyTimezoneOffset - : 0, - 'seconds' - ) .format(clientTimeFormat ? clientTimeFormat : timeFormat) ); } From c50c779d9249b6c4ff78f9d60919cdb7148de1c1 Mon Sep 17 00:00:00 2001 From: Civolilah Date: Sun, 29 Sep 2024 22:47:44 +0200 Subject: [PATCH 8/9] Added additional formatting of description --- src/pages/projects/common/hooks/useInvoiceProject.tsx | 8 +++++--- src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts | 8 +++++--- src/pages/tasks/common/hooks/useInvoiceTask.ts | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/pages/projects/common/hooks/useInvoiceProject.tsx b/src/pages/projects/common/hooks/useInvoiceProject.tsx index 4ff8ae0c8..af279a393 100644 --- a/src/pages/projects/common/hooks/useInvoiceProject.tsx +++ b/src/pages/projects/common/hooks/useInvoiceProject.tsx @@ -137,7 +137,7 @@ export function useInvoiceProject() { const description = []; if (company.invoice_task_datelog || company.invoice_task_timelog) { - description.push('
'); + description.push('
\n'); } if (company.invoice_task_datelog) { @@ -173,11 +173,13 @@ export function useInvoiceProject() { description.push(hoursDescription); } - if (company.invoice_task_item_description) { - description.push(intervalDescription); + if (company.invoice_task_item_description && intervalDescription) { + description.push(`\n\n${intervalDescription}`); } if (company.invoice_task_datelog || company.invoice_task_timelog) { + description.push('\n'); + description.push('
\n'); } diff --git a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts index 0a45ea3f1..9c5ff59af 100644 --- a/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts +++ b/src/pages/tasks/common/hooks/useAddTasksOnInvoice.ts @@ -94,7 +94,7 @@ export function useAddTasksOnInvoice(params: Params) { const description = []; if (company.invoice_task_datelog || company.invoice_task_timelog) { - description.push('
'); + description.push('
\n'); } if (company.invoice_task_datelog) { @@ -130,11 +130,13 @@ export function useAddTasksOnInvoice(params: Params) { description.push(hoursDescription); } - if (company.invoice_task_item_description) { - description.push(intervalDescription); + if (company.invoice_task_item_description && intervalDescription) { + description.push(`\n\n${intervalDescription}`); } if (company.invoice_task_datelog || company.invoice_task_timelog) { + description.push('\n'); + description.push('
\n'); } diff --git a/src/pages/tasks/common/hooks/useInvoiceTask.ts b/src/pages/tasks/common/hooks/useInvoiceTask.ts index d7af2fd4c..8747fecd6 100644 --- a/src/pages/tasks/common/hooks/useInvoiceTask.ts +++ b/src/pages/tasks/common/hooks/useInvoiceTask.ts @@ -153,7 +153,7 @@ export function useInvoiceTask(params?: Params) { const description = []; if (company.invoice_task_datelog || company.invoice_task_timelog) { - description.push('
'); + description.push('
\n'); } if (company.invoice_task_datelog) { @@ -189,11 +189,13 @@ export function useInvoiceTask(params?: Params) { description.push(hoursDescription); } - if (company.invoice_task_item_description) { - description.push(intervalDescription); + if (company.invoice_task_item_description && intervalDescription) { + description.push(`\n\n${intervalDescription}`); } if (company.invoice_task_datelog || company.invoice_task_timelog) { + description.push('\n'); + description.push('
\n'); } From 932a70895c30cb83b2663397638ff2c30ad1c5ba Mon Sep 17 00:00:00 2001 From: Civolilah Date: Tue, 1 Oct 2024 17:39:12 +0200 Subject: [PATCH 9/9] Refactored code --- src/common/hooks/useNumericFormatter.ts | 38 ++++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/common/hooks/useNumericFormatter.ts b/src/common/hooks/useNumericFormatter.ts index 6b0e286d6..c383b3635 100644 --- a/src/common/hooks/useNumericFormatter.ts +++ b/src/common/hooks/useNumericFormatter.ts @@ -16,6 +16,32 @@ export function useNumericFormatter() { const company = useCurrentCompany(); const userNumberPrecision = useUserNumberPrecision(); + const getThousandSeparator = ( + currentThousandSeparator: string | undefined + ) => { + if (currentThousandSeparator) { + return currentThousandSeparator; + } + + if (company?.use_comma_as_decimal_place) { + return '.'; + } + + return ','; + }; + + const getDecimalSeparator = (currentDecimalSeparator: string | undefined) => { + if (currentDecimalSeparator) { + return currentDecimalSeparator; + } + + if (company?.use_comma_as_decimal_place) { + return ','; + } + + return '.'; + }; + return ( numStr: string, thousandSeparator?: string, @@ -23,16 +49,8 @@ export function useNumericFormatter() { precision?: number ) => { return numericFormatter(numStr, { - thousandSeparator: thousandSeparator - ? thousandSeparator - : company?.use_comma_as_decimal_place - ? '.' - : ',', - decimalSeparator: decimalSeparator - ? decimalSeparator - : company?.use_comma_as_decimal_place - ? ',' - : '.', + thousandSeparator: getThousandSeparator(thousandSeparator), + decimalSeparator: getDecimalSeparator(decimalSeparator), decimalScale: precision || userNumberPrecision, }); };