Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Distance rate- WS owner can create multiple distance rate with same amount. #53104

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type {
ConnectionParams,
CurrencyCodeParams,
CustomersOrJobsLabelParams,
CustomUnitRateParams,
DateParams,
DateShouldBeAfterParams,
DateShouldBeBeforeParams,
Expand Down Expand Up @@ -2556,6 +2557,9 @@ const translations = {
title: 'Per diem',
subtitle: 'Set per diem rates to control daily employee spend. Import rates from a spreadsheet to get started.',
},
errors: {
existingRateError: ({rate}: CustomUnitRateParams) => `A rate with value ${rate} already exists.`,
},
},
qbd: {
exportOutOfPocketExpensesDescription: 'Set how out-of-pocket expenses export to QuickBooks Desktop.',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import type {
ConnectionParams,
CurrencyCodeParams,
CustomersOrJobsLabelParams,
CustomUnitRateParams,
DateParams,
DateShouldBeAfterParams,
DateShouldBeBeforeParams,
Expand Down Expand Up @@ -2580,6 +2581,9 @@ const translations = {
title: 'Per diem',
subtitle: 'Establece dietas per diem para controlar el gasto diario de los empleados. Importa las tarifas desde una hoja de cálculo para comenzar.',
},
errors: {
existingRateError: ({rate}: CustomUnitRateParams) => `Ya existe una tasa con el valor ${rate}.`,
},
},
qbd: {
exportOutOfPocketExpensesDescription: 'Establezca cómo se exportan los gastos de bolsillo a QuickBooks Desktop.',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,10 @@ type CompanyNameParams = {
companyName: string;
};

type CustomUnitRateParams = {
rate: number;
};

type ChatWithAccountManagerParams = {
accountManagerDisplayName: string;
};
Expand Down Expand Up @@ -770,5 +774,6 @@ export type {
CurrencyCodeParams,
WorkspaceLockedPlanTypeParams,
CompanyNameParams,
CustomUnitRateParams,
ChatWithAccountManagerParams,
};
20 changes: 19 additions & 1 deletion src/libs/PolicyDistanceRatesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,33 @@ type RateValueForm = typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM | ty

type TaxReclaimableForm = typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT_FORM;

function validateRateValue(values: FormOnyxValues<RateValueForm>, currency: string, toLocaleDigit: (arg: string) => string): FormInputErrors<RateValueForm> {
function validateRateValue(
values: FormOnyxValues<RateValueForm>,
customUnitRates: Record<string, Rate>,
toLocaleDigit: (arg: string) => string,
currentRateValue?: number,
): FormInputErrors<RateValueForm> {
const errors: FormInputErrors<RateValueForm> = {};
const parsedRate = MoneyRequestUtils.replaceAllDigits(values.rate, toLocaleDigit);
const decimalSeparator = toLocaleDigit('.');
const ratesList = Object.values(customUnitRates);
// The following logic replicates the backend's handling of rates:
// - Multiply the rate by 100 (CUSTOM_UNIT_RATE_BASE_OFFSET) to scale it, ensuring precision.
// - This ensures rates are converted as follows:
// 12 -> 1200
// 12.1 -> 1210
// 12.01 -> 1201
// 12.001 -> 1200.1
// 12.0001 -> 1200.01
// - Using parseFloat and toFixed(10) retains the necessary precision.
const convertedRate = parseFloat((Number(values.rate || 0) * CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET).toFixed(10));

// Allow one more decimal place for accuracy
const rateValueRegex = RegExp(String.raw`^-?\d{0,8}([${getPermittedDecimalSeparator(decimalSeparator)}]\d{0,${CONST.MAX_TAX_RATE_DECIMAL_PLACES}})?$`, 'i');
if (!rateValueRegex.test(parsedRate) || parsedRate === '') {
errors.rate = Localize.translateLocal('common.error.invalidRateError');
} else if (ratesList.some((r) => r.rate === convertedRate && !(currentRateValue && currentRateValue === r.rate))) {
errors.rate = Localize.translateLocal('workspace.perDiem.errors.existingRateError', {rate: Number(values.rate)});
} else if (NumberUtils.parseFloatAnyLocale(parsedRate) <= 0) {
errors.rate = Localize.translateLocal('common.error.lowRateError');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ function CreateDistanceRatePage({route}: CreateDistanceRatePageProps) {
const FullPageBlockingView = !customUnitID ? FullPageOfflineBlockingView : View;

const validate = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM>) => validateRateValue(values, currency, toLocaleDigit),
[currency, toLocaleDigit],
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM>) => validateRateValue(values, customUnit?.rates ?? {}, toLocaleDigit),
[toLocaleDigit, customUnit?.rates],
);

const submit = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ function PolicyDistanceRateEditPage({route}: PolicyDistanceRateEditPageProps) {
};

const validate = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_EDIT_FORM>) => validateRateValue(values, currency, toLocaleDigit),
[currency, toLocaleDigit],
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_EDIT_FORM>) => validateRateValue(values, customUnit?.rates ?? {}, toLocaleDigit, rate?.rate),
[toLocaleDigit, customUnit?.rates, rate?.rate],
);

if (!rate) {
Expand Down
Loading