From 84782467543ba7fd4d4b9f808469e2a4112b1b25 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Date: Tue, 20 Oct 2026 08:14:14 +0530 Subject: [PATCH] Show reclaimable tax details in the added distance rate change log When the log has taxClaimablePercentage and taxRateName, append the reclaimable amount per unit (rate x taxClaimablePercentage) and the tax rate name. --- src/languages/de.ts | 1 + src/languages/el.ts | 1 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/languages/fr.ts | 1 + src/languages/it.ts | 1 + src/languages/ja.ts | 1 + src/languages/nl.ts | 1 + src/languages/pl.ts | 1 + src/languages/pt-BR.ts | 1 + src/languages/zh-hans.ts | 1 + src/libs/ReportActionsUtils.ts | 21 +++++---- src/types/onyx/OriginalMessage.ts | 6 +++ tests/unit/ReportActionsUtilsTest.ts | 65 ++++++++++++++++++++++++++++ 14 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index aff85e69838e..d38dcb0a9455 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -9953,6 +9953,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `„${rateName}“-Satz von ${rateValue} hinzugefügt, gültig bis ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `hat den Tarif „${rateName}“ mit ${rateValue} hinzugefügt, gültig vom ${startDate} bis ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, mit ${taxReclaimableAmount} erstattungsfähiger Steuer unter „${taxRateName}“`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `hat den Entfernungs­tarif „${rateName}“ aktualisiert, sodass er für ${newDateRange} gilt (zuvor ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/el.ts b/src/languages/el.ts index cda27383978a..fde4358ef977 100644 --- a/src/languages/el.ts +++ b/src/languages/el.ts @@ -9683,6 +9683,7 @@ ${reportName}`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `προστέθηκε συντελεστής «${rateName}» αξίας ${rateValue}, σε ισχύ έως ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `προστέθηκε συντελεστής «${rateName}» αξίας ${rateValue}, σε ισχύ από ${startDate} έως ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, με ${taxReclaimableAmount} ανακτήσιμο φόρο στο «${taxRateName}»`, deleteCustomUnitRate: (customUnitName: string, rateName: string) => `αφαίρεσε τον συντελεστή «${customUnitName}» «${rateName}»`, updateCustomUnitSubRate: (customUnitName: string, customUnitRateName: string, customUnitSubRateName: string, oldValue: string, newValue: string, updatedField: string) => `άλλαξε την τιμή της μονάδας «${customUnitName}», της υποτιμής «${customUnitRateName}» του υποποσού «${customUnitSubRateName}», ${updatedField}, σε «${newValue}» (προηγουμένως «${oldValue}»)`, diff --git a/src/languages/en.ts b/src/languages/en.ts index 237106085e90..b728e756c420 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -9698,6 +9698,7 @@ const translations = { addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `added "${rateName}" rate of ${rateValue}, valid until ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `added "${rateName}" rate of ${rateValue}, valid from ${startDate} - ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, with ${taxReclaimableAmount} tax reclaimable under "${taxRateName}"`, deleteCustomUnitRate: (customUnitName: string, rateName: string) => `removed the "${customUnitName}" rate "${rateName}"`, updateCustomUnitSubRate: (customUnitName: string, customUnitRateName: string, customUnitSubRateName: string, oldValue: string, newValue: string, updatedField: string) => `changed "${customUnitName}" rate "${customUnitRateName}" sub-rate "${customUnitSubRateName}" ${updatedField} to "${newValue}" (previously "${oldValue}")`, diff --git a/src/languages/es.ts b/src/languages/es.ts index d7ad5a7a7bd2..30fd882f17c3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -9771,6 +9771,7 @@ ${reportName}`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `añadió la tasa "${rateName}" de ${rateValue}, válida hasta ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `añadió la tasa "${rateName}" de ${rateValue}, válida del ${startDate} al ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, con ${taxReclaimableAmount} de impuesto recuperable en "${taxRateName}"`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `actualizó la tasa de distancia «${rateName}» para aplicar ${newDateRange} (previamente ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index a089625e7b51..350632235689 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -9984,6 +9984,7 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `a ajouté le taux « ${rateName} » de ${rateValue}, valable jusqu’au ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `a ajouté le taux « ${rateName} » de ${rateValue}, valable du ${startDate} au ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, avec ${taxReclaimableAmount} de taxe récupérable au titre de « ${taxRateName} »`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `a mis à jour le taux kilométrique « ${rateName} » pour s’appliquer à ${newDateRange} (précédemment ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/it.ts b/src/languages/it.ts index 6eb2a2e9909e..31e176b189f8 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -9903,6 +9903,7 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `ha aggiunto la tariffa "${rateName}" di ${rateValue}, valida fino al ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `ha aggiunto la tariffa "${rateName}" di ${rateValue}, valida dal ${startDate} al ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, con ${taxReclaimableAmount} di imposta recuperabile in "${taxRateName}"`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `ha aggiornato la tariffa chilometrica "${rateName}" per applicarla a ${newDateRange} (in precedenza ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 58a13bfcc91e..5e1e16604ce4 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -9762,6 +9762,7 @@ ${reportName}`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `「${rateName}」レート(${rateValue})を${endDate}まで有効として追加しました`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `「${rateName}」レート(${rateValue})を追加しました。有効期間:${startDate}〜${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `。「${taxRateName}」で${taxReclaimableAmount}の税額を還付できます`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `距離レート「${rateName}」を更新し、${newDateRange} に適用しました(以前は ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 55d382cb1f87..e3b636e0c28b 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -9872,6 +9872,7 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `heeft tarief „${rateName}” van ${rateValue} toegevoegd, geldig tot ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `heeft tarief "${rateName}" van ${rateValue} toegevoegd, geldig van ${startDate} - ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, met ${taxReclaimableAmount} terugvorderbare belasting onder "${taxRateName}"`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `heeft het kilometertarief "${rateName}" bijgewerkt zodat het geldt van ${newDateRange} (voorheen ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 17a89282fbea..c587e8b33203 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -9882,6 +9882,7 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `dodano stawkę „${rateName}” w wysokości ${rateValue}, obowiązującą do ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `dodano stawkę „${rateName}” w wysokości ${rateValue}, obowiązującą od ${startDate} do ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, z ${taxReclaimableAmount} podatku do odzyskania w ramach „${taxRateName}”`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `zaktualizowano stawkę za dystans „${rateName}”, aby obowiązywała ${newDateRange} (wcześniej ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 274d62903144..1fb22ace82ad 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -9869,6 +9869,7 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `adicionou a taxa "${rateName}" de ${rateValue}, válida até ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `adicionou a tarifa "${rateName}" de ${rateValue}, válida de ${startDate} a ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `, com ${taxReclaimableAmount} de imposto recuperável em "${taxRateName}"`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `atualizou a taxa de distância "${rateName}" para se aplicar a ${newDateRange} (anteriormente ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 79dafb4321f5..ba85ae320c98 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -9504,6 +9504,7 @@ ${reportName}`, addCustomUnitRateWithAmountAndEndDate: (rateName: string, rateValue: string, endDate: string) => `已添加“${rateName}”费率 ${rateValue},有效期至 ${endDate}`, addCustomUnitRateWithAmountAndDates: (rateName: string, rateValue: string, startDate: string, endDate: string) => `已添加“${rateName}”费率 ${rateValue},有效期为 ${startDate} - ${endDate}`, + addCustomUnitRateTaxReclaimableSuffix: (taxReclaimableAmount: string, taxRateName: string) => `,其中 ${taxReclaimableAmount} 可按“${taxRateName}”退税`, updatedCustomUnitRateDateRange: (rateName: string, newDateRange: string, oldDateRange: string) => `已将距离费率“${rateName}”更新为适用于 ${newDateRange}(此前为 ${oldDateRange})`, customUnitRateDateRangeStartToEnd: (startDate: string, endDate: string) => `${startDate} - ${endDate}`, customUnitRateDateRangeFrom: (date: string) => `自 ${date} 起`, diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 78befcadc834..e4ef4322377d 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -3590,7 +3590,7 @@ function getWorkspaceCustomUnitRateImportedMessage(translate: LocalizedTranslate } function getWorkspaceCustomUnitRateAddedMessage(translate: LocalizedTranslate, dateFnsLocale: DateFnsLocale | undefined, action: ReportAction): string { - const {customUnitName, rateName, rate, currency, unit, startDate, endDate} = + const {customUnitName, rateName, rate, currency, unit, startDate, endDate, taxClaimablePercentage, taxRateName} = getOriginalMessage(action as ReportAction) ?? {}; if (rateName && rate !== undefined && currency && unit) { @@ -3598,17 +3598,20 @@ function getWorkspaceCustomUnitRateAddedMessage(translate: LocalizedTranslate, d const formattedRate = `${convertAmountToDisplayString(rate, currency)}/${unitLabel}`; const formattedStartDate = startDate ? DateUtils.formatToReadableString(startDate, dateFnsLocale) : undefined; const formattedEndDate = endDate ? DateUtils.formatToReadableString(endDate, dateFnsLocale) : undefined; + const taxReclaimableSuffix = + taxClaimablePercentage !== undefined && taxRateName + ? translate('workspaceActions.addCustomUnitRateTaxReclaimableSuffix', `${convertAmountToDisplayString(rate * taxClaimablePercentage, currency)}/${unitLabel}`, taxRateName) + : ''; + let addedRateMessage = translate('workspaceActions.addCustomUnitRateWithAmount', rateName, formattedRate); if (formattedStartDate && formattedEndDate) { - return translate('workspaceActions.addCustomUnitRateWithAmountAndDates', rateName, formattedRate, formattedStartDate, formattedEndDate); + addedRateMessage = translate('workspaceActions.addCustomUnitRateWithAmountAndDates', rateName, formattedRate, formattedStartDate, formattedEndDate); + } else if (formattedStartDate) { + addedRateMessage = translate('workspaceActions.addCustomUnitRateWithAmountAndStartDate', rateName, formattedRate, formattedStartDate); + } else if (formattedEndDate) { + addedRateMessage = translate('workspaceActions.addCustomUnitRateWithAmountAndEndDate', rateName, formattedRate, formattedEndDate); } - if (formattedStartDate) { - return translate('workspaceActions.addCustomUnitRateWithAmountAndStartDate', rateName, formattedRate, formattedStartDate); - } - if (formattedEndDate) { - return translate('workspaceActions.addCustomUnitRateWithAmountAndEndDate', rateName, formattedRate, formattedEndDate); - } - return translate('workspaceActions.addCustomUnitRateWithAmount', rateName, formattedRate); + return `${addedRateMessage}${taxReclaimableSuffix}`; } if (customUnitName && rateName) { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index eef39c2e363b..6552078ba120 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -591,6 +591,12 @@ type OriginalMessagePolicyChangeLog = { /** End date of the custom unit rate (yyyy-MM-dd), used in ADD actions */ endDate?: string; + /** Share of the custom unit rate that is tax reclaimable, used in ADD actions */ + taxClaimablePercentage?: number; + + /** Name of the tax rate the custom unit rate's reclaimable tax falls under, used in ADD actions */ + taxRateName?: string; + /** New start date of the custom unit rate (yyyy-MM-dd), used in UPDATE actions */ newStartDate?: string; diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 8f8d67060d0d..dc4358e42198 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -103,6 +103,8 @@ type TakeControlOriginalMessageFixture = NonNullable; +type AddCustomUnitRateAction = ReportAction; + type LegacyReportActionFields = { message?: string; originalMessage?: string; @@ -1785,6 +1787,69 @@ describe('ReportActionsUtils', () => { }); }); + describe('getWorkspaceCustomUnitRateAddedMessage', () => { + function buildAddCustomUnitRateAction(originalMessage: AddCustomUnitRateAction['originalMessage']): AddCustomUnitRateAction { + return { + actionName: CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_CUSTOM_UNIT_RATE, + reportActionID: 'add-custom-unit-rate-1', + created: '2026-09-01 12:00:00.000', + message: [{type: CONST.REPORT.MESSAGE.TYPE.COMMENT, text: '', html: ''}], + originalMessage, + }; + } + + const fuelBandRateMessage = { + customUnitName: CONST.CUSTOM_UNITS.NAME_DISTANCE, + rateName: 'Cars & Vans (Petrol/Hybrid) - Up to 1400cc', + rate: 55, + currency: CONST.CURRENCY.GBP, + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + startDate: '2026-09-01', + }; + + it('should append the reclaimable tax amount and tax rate name when the log has tax details', () => { + // Given an added rate log for a UK fuel-band rate, whose reclaimable share is the 14p fuel rate out of the 55p rate + const action = buildAddCustomUnitRateAction({...fuelBandRateMessage, taxClaimablePercentage: 14 / 55, taxRateName: 'Standard VAT (20%)'}); + + // When building the message + const message = ReportActionsUtils.getWorkspaceCustomUnitRateAddedMessage(translateLocal, undefined, action); + + // Then it should show the reclaimable amount per mile, rate × taxClaimablePercentage, under the tax rate name + expect(message).toBe( + 'added "Cars & Vans (Petrol/Hybrid) - Up to 1400cc" rate of £0.55/mile, valid from September 1, 2026, with £0.14/mile tax reclaimable under "Standard VAT (20%)"', + ); + }); + + it('should show a zero reclaimable amount when the reclaimable share is zero', () => { + // Given an added rate log for a fuel band with no reclaimable fuel cost + const action = buildAddCustomUnitRateAction({ + ...fuelBandRateMessage, + rateName: 'Cars & Vans (Electric) - Home charged', + taxClaimablePercentage: 0, + taxRateName: 'Standard VAT (20%)', + }); + + // When building the message + const message = ReportActionsUtils.getWorkspaceCustomUnitRateAddedMessage(translateLocal, undefined, action); + + // Then the tax details should still be shown, because the log carries them + expect(message).toBe( + 'added "Cars & Vans (Electric) - Home charged" rate of £0.55/mile, valid from September 1, 2026, with £0.00/mile tax reclaimable under "Standard VAT (20%)"', + ); + }); + + it('should not append tax details when the log has no tax rate name', () => { + // Given an added rate log with a reclaimable share but no tax rate name + const action = buildAddCustomUnitRateAction({...fuelBandRateMessage, taxClaimablePercentage: 14 / 55}); + + // When building the message + const message = ReportActionsUtils.getWorkspaceCustomUnitRateAddedMessage(translateLocal, undefined, action); + + // Then it should render the same text as a log without tax details + expect(message).toBe('added "Cars & Vans (Petrol/Hybrid) - Up to 1400cc" rate of £0.55/mile, valid from September 1, 2026'); + }); + }); + describe('getMessageOfOldDotReportAction', () => { it('should return the ACH bounce message with return reason when provided', () => { const returnReason = 'R03 - No Account/Unable to Locate Account';