Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b3a3426
feat: implement distance rate name edit page
mkzie2 May 22, 2025
42eb88d
add rate name to rate list
mkzie2 May 22, 2025
f2dc003
show rate value
mkzie2 May 22, 2025
f0b594c
fix existing name error
mkzie2 May 22, 2025
ff9ea78
fix lint
mkzie2 May 22, 2025
f5a1808
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jun 9, 2025
391ffa9
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jun 26, 2025
acfd692
remove default rate name
mkzie2 Jun 26, 2025
2d964c7
generate translations
mkzie2 Jun 26, 2025
ec42019
fix namespace imports and canBeMissing lints
mkzie2 Jun 26, 2025
cf38004
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 7, 2025
1d34f71
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 15, 2025
28ebe49
remove /settings from /workspaces route
mkzie2 Jul 16, 2025
710746b
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 23, 2025
8f83a60
sort the rates by creation time
mkzie2 Jul 23, 2025
1bd4411
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 24, 2025
6100f93
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 28, 2025
cd99ef4
extract common distance rate update logic
mkzie2 Jul 28, 2025
49da150
Merge branch 'main' of github.com-mkzie2:mkzie2/App into mkzie2-issue…
mkzie2 Jul 29, 2025
989750d
fix lint
mkzie2 Jul 29, 2025
9b780a8
generate translations
mkzie2 Jul 29, 2025
b9e5b9f
remove TODO
mkzie2 Jul 30, 2025
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
2 changes: 2 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3076,7 +3076,9 @@ const CONST = {
DISTANCE_UNIT_MILES: 'mi',
DISTANCE_UNIT_KILOMETERS: 'km',
MILEAGE_IRS_RATE: 0.7,
// The first created rate is called "Default rate", others are called "New Rate `i`"
DEFAULT_RATE: 'Default Rate',
NEW_RATE: 'New Rate',
RATE_DECIMALS: 3,
FAKE_P2P_ID: '_FAKE_P2P_ID_',
MILES_TO_KILOMETERS: 1.609344,
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,11 @@ const ONYXKEYS = {
POLICY_CREATE_DISTANCE_RATE_FORM: 'policyCreateDistanceRateForm',
POLICY_CREATE_DISTANCE_RATE_FORM_DRAFT: 'policyCreateDistanceRateFormDraft',
POLICY_DISTANCE_RATE_EDIT_FORM: 'policyDistanceRateEditForm',
POLICY_DISTANCE_RATE_NAME_EDIT_FORM: 'policyDistanceRateNameEditForm',
POLICY_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT_FORM: 'policyDistanceRateTaxReclaimableOnEditForm',
POLICY_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT_FORM_DRAFT: 'policyDistanceRateTaxReclaimableOnEditFormDraft',
POLICY_DISTANCE_RATE_EDIT_FORM_DRAFT: 'policyDistanceRateEditFormDraft',
POLICY_DISTANCE_RATE_NAME_EDIT_FORM_DRAFT: 'policyDistanceRateNameEditFormDraft',
CLOSE_ACCOUNT_FORM: 'closeAccount',
CLOSE_ACCOUNT_FORM_DRAFT: 'closeAccountDraft',
PROFILE_SETTINGS_FORM: 'profileSettingsForm',
Expand Down Expand Up @@ -927,6 +929,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.WORKSPACE_NEW_TAX_FORM]: FormTypes.WorkspaceNewTaxForm;
[ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM]: FormTypes.PolicyCreateDistanceRateForm;
[ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_EDIT_FORM]: FormTypes.PolicyDistanceRateEditForm;
[ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_NAME_EDIT_FORM]: FormTypes.PolicyDistanceRateNameEditForm;
[ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT_FORM]: FormTypes.PolicyDistanceRateTaxReclaimableOnEditForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_NAME_FORM]: FormTypes.WorkspaceTaxNameForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_CODE_FORM]: FormTypes.WorkspaceTaxCodeForm;
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,10 @@ const ROUTES = {
route: 'workspaces/:policyID/distance-rates/:rateID/edit',
getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/edit` as const,
},
WORKSPACE_DISTANCE_RATE_NAME_EDIT: {
route: 'workspaces/:policyID/distance-rates/:rateID/name/edit',
getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/name/edit` as const,
},
WORKSPACE_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT: {
route: 'workspaces/:policyID/distance-rates/:rateID/tax-reclaimable/edit',
getRoute: (policyID: string, rateID: string) => `workspaces/${policyID}/distance-rates/${rateID}/tax-reclaimable/edit` as const,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ const SCREENS = {
DISTANCE_RATES_SETTINGS: 'Distance_Rates_Settings',
DISTANCE_RATE_DETAILS: 'Distance_Rate_Details',
DISTANCE_RATE_EDIT: 'Distance_Rate_Edit',
DISTANCE_RATE_NAME_EDIT: 'Distance_Rate_Name_Edit',
DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT: 'Distance_Rate_Tax_Reclaimable_On_Edit',
DISTANCE_RATE_TAX_RATE_EDIT: 'Distance_Rate_Tax_Rate_Edit',
UPGRADE: 'Workspace_Upgrade',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5232,6 +5232,10 @@ const translations = {
one: 'Möchten Sie diesen Satz wirklich löschen?',
other: 'Möchten Sie diese Tarife wirklich löschen?',
}),
errors: {
rateNameRequired: 'Ratenname ist erforderlich',
existingRateName: 'Ein Entfernungsrate mit diesem Namen existiert bereits.',
},
},
editor: {
descriptionInputLabel: 'Beschreibung',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5209,6 +5209,10 @@ const translations = {
one: 'Are you sure you want to delete this rate?',
other: 'Are you sure you want to delete these rates?',
}),
errors: {
rateNameRequired: 'Rate name is required',
existingRateName: 'A distance rate with this name already exists',
},
},
editor: {
descriptionInputLabel: 'Description',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5223,6 +5223,10 @@ const translations = {
one: '¿Estás seguro de que quieres eliminar esta tasa?',
other: '¿Estás seguro de que quieres eliminar estas tasas?',
}),
errors: {
rateNameRequired: 'El nombre de la tasa es obligatorio',
existingRateName: 'Ya existe una tasa de distancia con este nombre',
},
Comment on lines +5226 to +5229

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is translations confirmed for all languages?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't had an official process to confirm translations for all languages. If you knew it, could you share? For Spanish, I'm checking it in this thread.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spanish confirmed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use deepl.com and confirm translations are accurate, in the meantime i have asked internally if there is formal process

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mkzie2 mkzie2 Jul 28, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I remembered the last time I ran the script, it cost me around $20 within 15 minutes before reaching tier limit because it requests translations for all the copies. Let me try again to check if that was fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with generateTranslations script.

},
editor: {
nameInputLabel: 'Nombre',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5245,6 +5245,10 @@ const translations = {
one: 'Êtes-vous sûr de vouloir supprimer ce tarif ?',
other: 'Êtes-vous sûr de vouloir supprimer ces tarifs ?',
}),
errors: {
rateNameRequired: 'Le nom du tarif est requis',
existingRateName: 'Un tarif de distance avec ce nom existe déjà.',
},
},
editor: {
descriptionInputLabel: 'Description',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5245,6 +5245,10 @@ const translations = {
one: 'Sei sicuro di voler eliminare questa tariffa?',
other: 'Sei sicuro di voler eliminare queste tariffe?',
}),
errors: {
rateNameRequired: 'Il nome della tariffa è obbligatorio',
existingRateName: 'Esiste già una tariffa di distanza con questo nome',
},
},
editor: {
descriptionInputLabel: 'Descrizione',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5219,6 +5219,10 @@ const translations = {
one: 'このレートを削除してもよろしいですか?',
other: 'これらのレートを削除してもよろしいですか?',
}),
errors: {
rateNameRequired: 'レート名は必須です',
existingRateName: 'この名前の距離レートはすでに存在します',
},
},
editor: {
descriptionInputLabel: '説明',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5245,6 +5245,10 @@ const translations = {
one: 'Weet je zeker dat je dit tarief wilt verwijderen?',
other: 'Weet je zeker dat je deze tarieven wilt verwijderen?',
}),
errors: {
rateNameRequired: 'Tariefnaam is vereist',
existingRateName: 'Er bestaat al een afstandstarief met deze naam.',
},
},
editor: {
descriptionInputLabel: 'Beschrijving',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5233,6 +5233,10 @@ const translations = {
one: 'Czy na pewno chcesz usunąć tę stawkę?',
other: 'Czy na pewno chcesz usunąć te stawki?',
}),
errors: {
rateNameRequired: 'Nazwa stawki jest wymagana',
existingRateName: 'Stawka odległości o tej nazwie już istnieje',
},
},
editor: {
descriptionInputLabel: 'Opis',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5241,6 +5241,10 @@ const translations = {
one: 'Tem certeza de que deseja excluir esta taxa?',
other: 'Tem certeza de que deseja excluir essas taxas?',
}),
errors: {
rateNameRequired: 'O nome da taxa é obrigatório',
existingRateName: 'Já existe uma tarifa de distância com este nome.',
},
},
editor: {
descriptionInputLabel: 'Descrição',
Expand Down
4 changes: 4 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5157,6 +5157,10 @@ const translations = {
one: '您确定要删除此费率吗?',
other: '您确定要删除这些费率吗?',
}),
errors: {
rateNameRequired: '费率名称是必需的',
existingRateName: '具有此名称的距离费率已存在',
},
},
editor: {
descriptionInputLabel: '描述',
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ const WRITE_COMMANDS = {
SET_CUSTOM_UNIT_DEFAULT_CATEGORY: 'SetCustomUnitDefaultCategory',
ENABLE_DISTANCE_REQUEST_TAX: 'EnableDistanceRequestTax',
UPDATE_POLICY_DISTANCE_RATE_VALUE: 'UpdatePolicyDistanceRateValue',
UPDATE_POLICY_DISTANCE_RATE_NAME: 'UpdatePolicyDistanceRateName',
UPDATE_POLICY_DISTANCE_TAX_RATE_VALUE: 'UpdateDistanceTaxRate',
UPDATE_DISTANCE_TAX_CLAIMABLE_VALUE: 'UpdateDistanceTaxClaimableValue',
SET_POLICY_DISTANCE_RATES_ENABLED: 'SetPolicyDistanceRatesEnabled',
Expand Down Expand Up @@ -807,6 +808,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_MANY_POLICY_CONNECTION_CONFIGS]: Parameters.UpdateManyPolicyConnectionConfigurationsParams;
[WRITE_COMMANDS.REMOVE_POLICY_CONNECTION]: Parameters.RemovePolicyConnectionParams;
[WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_RATE_VALUE]: Parameters.UpdatePolicyDistanceRateValueParams;
[WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_RATE_NAME]: Parameters.UpdatePolicyDistanceRateValueParams;
[WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_TAX_RATE_VALUE]: Parameters.UpdatePolicyDistanceRateValueParams;
[WRITE_COMMANDS.UPDATE_DISTANCE_TAX_CLAIMABLE_VALUE]: Parameters.UpdatePolicyDistanceRateValueParams;
[WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED]: Parameters.SetPolicyDistanceRatesEnabledParams;
Expand Down
4 changes: 3 additions & 1 deletion src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ function getMileageRates(policy: OnyxInputOrEntry<Policy>, includeDisabledRates

/**
* Retrieves the default mileage rate based on a given policy.
* Default rate is the first created rate when you create the policy.
* It's NOT always the rate whose name is "Default rate" because rate name is now changeable.
*
* @param policy - The policy from which to extract the default mileage rate.
*
Expand All @@ -96,7 +98,7 @@ function getDefaultMileageRate(policy: OnyxInputOrEntry<Policy>): MileageRate |
}
const mileageRates = Object.values(getMileageRates(policy));

const distanceRate = mileageRates.find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE) ?? mileageRates.at(0) ?? ({} as MileageRate);
const distanceRate = mileageRates.at(0) ?? ({} as MileageRate);

return {
customUnitRateID: distanceRate.customUnitRateID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.DISTANCE_RATES_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage').default,
[SCREENS.WORKSPACE.DISTANCE_RATE_DETAILS]: () => require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRateDetailsPage').default,
[SCREENS.WORKSPACE.DISTANCE_RATE_EDIT]: () => require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRateEditPage').default,
[SCREENS.WORKSPACE.DISTANCE_RATE_NAME_EDIT]: () => require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRateNameEditPage').default,
[SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT]: () =>
require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRateTaxReclaimableEditPage').default,
[SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RATE_EDIT]: () => require<ReactComponentModule>('../../../../pages/workspace/distanceRates/PolicyDistanceRateTaxRateEditPage').default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const WORKSPACE_TO_RHP: Partial<Record<keyof WorkspaceSplitNavigatorParamList, s
SCREENS.WORKSPACE.CREATE_DISTANCE_RATE,
SCREENS.WORKSPACE.DISTANCE_RATES_SETTINGS,
SCREENS.WORKSPACE.DISTANCE_RATE_EDIT,
SCREENS.WORKSPACE.DISTANCE_RATE_NAME_EDIT,
SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT,
SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RATE_EDIT,
SCREENS.WORKSPACE.DISTANCE_RATE_DETAILS,
Expand Down
3 changes: 3 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,9 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
[SCREENS.WORKSPACE.DISTANCE_RATE_EDIT]: {
path: ROUTES.WORKSPACE_DISTANCE_RATE_EDIT.route,
},
[SCREENS.WORKSPACE.DISTANCE_RATE_NAME_EDIT]: {
path: ROUTES.WORKSPACE_DISTANCE_RATE_NAME_EDIT.route,
},
[SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT]: {
path: ROUTES.WORKSPACE_DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT.route,
},
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ type SettingsNavigatorParamList = {
policyID: string;
rateID: string;
};
[SCREENS.WORKSPACE.DISTANCE_RATE_NAME_EDIT]: {
policyID: string;
rateID: string;
};
[SCREENS.WORKSPACE.DISTANCE_RATE_TAX_RECLAIMABLE_ON_EDIT]: {
policyID: string;
rateID: string;
Expand Down
117 changes: 103 additions & 14 deletions src/libs/PolicyDistanceRatesUtils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type {NullishDeep, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
import CONST from '@src/CONST';
import type ONYXKEYS from '@src/ONYXKEYS';
import type {Rate} from '@src/types/onyx/Policy';
import ONYXKEYS from '@src/ONYXKEYS';
import type {CustomUnit, Rate, TaxRateAttributes} from '@src/types/onyx/Policy';
import type {OnyxData} from '@src/types/onyx/Request';
import {getMicroSecondOnyxErrorWithTranslationKey} from './ErrorUtils';
import getPermittedDecimalSeparator from './getPermittedDecimalSeparator';
import * as Localize from './Localize';
import * as MoneyRequestUtils from './MoneyRequestUtils';
import * as NumberUtils from './NumberUtils';
import {translateLocal} from './Localize';
import {replaceAllDigits} from './MoneyRequestUtils';
import {parseFloatAnyLocale} from './NumberUtils';

type RateValueForm = typeof ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM | typeof ONYXKEYS.FORMS.POLICY_DISTANCE_RATE_EDIT_FORM;

Expand All @@ -18,7 +22,7 @@ function validateRateValue(
currentRateValue?: number,
): FormInputErrors<RateValueForm> {
const errors: FormInputErrors<RateValueForm> = {};
const parsedRate = MoneyRequestUtils.replaceAllDigits(values.rate, toLocaleDigit);
const parsedRate = replaceAllDigits(values.rate, toLocaleDigit);
const decimalSeparator = toLocaleDigit('.');
const ratesList = Object.values(customUnitRates)
.filter((rate) => currentRateValue !== rate.rate)
Expand All @@ -38,11 +42,11 @@ function validateRateValue(
// 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');
errors.rate = translateLocal('common.error.invalidRateError');
} else if (ratesList.some((r) => r.rate === convertedRate)) {
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');
errors.rate = translateLocal('workspace.perDiem.errors.existingRateError', {rate: Number(values.rate)});
} else if (parseFloatAnyLocale(parsedRate) <= 0) {
errors.rate = translateLocal('common.error.lowRateError');
}
return errors;
}
Expand All @@ -51,7 +55,7 @@ function validateTaxClaimableValue(values: FormOnyxValues<TaxReclaimableForm>, r
const errors: FormInputErrors<TaxReclaimableForm> = {};

if (rate?.rate && Number(values.taxClaimableValue) >= rate.rate / 100) {
errors.taxClaimableValue = Localize.translateLocal('workspace.taxes.error.updateTaxClaimableFailureMessage');
errors.taxClaimableValue = translateLocal('workspace.taxes.error.updateTaxClaimableFailureMessage');
}
return errors;
}
Expand All @@ -61,8 +65,93 @@ function validateTaxClaimableValue(values: FormOnyxValues<TaxReclaimableForm>, r
* @param rates
*/
function getOptimisticRateName(rates: Record<string, Rate>): string {
const existingRatesWithSameName = Object.values(rates ?? {}).filter((rate) => (rate.name ?? '').startsWith(CONST.CUSTOM_UNITS.DEFAULT_RATE));
return existingRatesWithSameName.length ? `${CONST.CUSTOM_UNITS.DEFAULT_RATE} ${existingRatesWithSameName.length}` : CONST.CUSTOM_UNITS.DEFAULT_RATE;
if (Object.keys(rates).length === 0) {
return CONST.CUSTOM_UNITS.DEFAULT_RATE;
}
const newRateCount = Object.values(rates).filter((rate) => rate.name?.startsWith(CONST.CUSTOM_UNITS.NEW_RATE)).length;
return newRateCount === 0 ? CONST.CUSTOM_UNITS.NEW_RATE : `${CONST.CUSTOM_UNITS.NEW_RATE} ${newRateCount}`;
}

type PolicyDistanceRateUpdateField = keyof Pick<Rate, 'name' | 'rate'> | keyof TaxRateAttributes;

/**
* Builds optimistic, success, and failure Onyx data for policy distance rate updates
* @param policyID - The policy ID
* @param customUnit - The custom unit being updated
* @param customUnitRates - The rates being updated
* @param fieldName - The field name being updated
* @returns Object containing optimisticData, successData, and failureData arrays
*/
function buildOnyxDataForPolicyDistanceRateUpdates(policyID: string, customUnit: CustomUnit, customUnitRates: Rate[], fieldName: PolicyDistanceRateUpdateField): OnyxData {
const currentRates = customUnit.rates;
const optimisticRates: Record<string, NullishDeep<Rate>> = {};
const successRates: Record<string, NullishDeep<Rate>> = {};
const failureRates: Record<string, NullishDeep<Rate>> = {};
const rateIDs = customUnitRates.map((rate) => rate.customUnitRateID);

for (const rateID of Object.keys(customUnit.rates)) {
if (rateIDs.includes(rateID)) {
const foundRate = customUnitRates.find((rate) => rate.customUnitRateID === rateID);
optimisticRates[rateID] = {
...foundRate,
pendingFields: {[fieldName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
};
successRates[rateID] = {
...foundRate,
pendingFields: {[fieldName]: null},
};
failureRates[rateID] = {
...currentRates[rateID],
pendingFields: {[fieldName]: null},
errorFields: {[fieldName]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')},
};
}
}

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
customUnits: {
[customUnit.customUnitID]: {
rates: optimisticRates,
},
},
},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
customUnits: {
[customUnit.customUnitID]: {
rates: successRates,
},
},
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
customUnits: {
[customUnit.customUnitID]: {
rates: failureRates,
},
},
},
},
];

return {optimisticData, successData, failureData};
}

export {validateRateValue, getOptimisticRateName, validateTaxClaimableValue};
export {validateRateValue, getOptimisticRateName, validateTaxClaimableValue, buildOnyxDataForPolicyDistanceRateUpdates};
export type {PolicyDistanceRateUpdateField};
Loading