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

ReportAction text for transactions edited in OldDot #30603

Merged
merged 52 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
94e2dee
ReportAction text for transactions edited in OldDot
cristipaval Oct 30, 2023
6e5d6b0
Make Lint happy.
cristipaval Oct 30, 2023
a7f3f38
Run prettier.
cristipaval Oct 30, 2023
a445ea6
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Nov 7, 2023
a0a856c
Improve message for MODIFIEDEXPENSE action
cristipaval Nov 7, 2023
960d00a
Add a safety check
cristipaval Nov 7, 2023
a4e82a8
Improve MODIFIEDEXPENSE action message
cristipaval Nov 7, 2023
b9b7ef4
Improve MODIFIEDEXPENSE action message
cristipaval Nov 7, 2023
5190539
Remove redundancy.
cristipaval Nov 7, 2023
b5518be
Update function doc
cristipaval Nov 8, 2023
be978f9
Create reusable function
cristipaval Nov 8, 2023
b31ba92
Run prettier
cristipaval Nov 8, 2023
376944c
Fix lint.
cristipaval Nov 8, 2023
b9f4243
Update src/libs/ReportUtils.js
cristipaval Nov 8, 2023
16df2d9
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Nov 20, 2023
bab1de4
Move logic in a separate unit
cristipaval Nov 20, 2023
4ea44ee
Move policy tags logic to PolicyUtils
cristipaval Nov 20, 2023
b9229a7
Better naming
cristipaval Nov 20, 2023
72714ad
Dry up code.
cristipaval Nov 20, 2023
18d1bf0
Run prettier
cristipaval Nov 20, 2023
5444abd
Fix lint errors
cristipaval Nov 20, 2023
371f138
Make it a ts file.
cristipaval Nov 21, 2023
ceaa915
Migrate to TS
cristipaval Nov 21, 2023
3bec4fd
Make Lint happy.
cristipaval Nov 21, 2023
b350c6b
Update src/libs/ModifiedExpenseMessage.ts
cristipaval Nov 28, 2023
03931a2
Update src/libs/ModifiedExpenseMessage.ts
cristipaval Nov 28, 2023
4e9a118
Update src/libs/ModifiedExpenseMessage.ts
cristipaval Nov 28, 2023
d855c90
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Nov 29, 2023
41f8a94
Resolve conflicts after merge.
cristipaval Nov 29, 2023
ceff5c1
Move policyTags to ModifiedExpenseMessage
cristipaval Nov 29, 2023
57f0837
Remove unused function.
cristipaval Nov 29, 2023
7dd7afb
Make lint happy.
cristipaval Nov 29, 2023
1cb7f47
Better checks.
cristipaval Nov 29, 2023
388962f
Update src/libs/ReportUtils.ts
cristipaval Nov 29, 2023
9cc7637
Update src/libs/ModifiedExpenseMessage.ts
cristipaval Nov 29, 2023
60a1493
Improve function doc.
cristipaval Nov 29, 2023
8dd3c99
Add return type declaration
cristipaval Nov 29, 2023
4606a46
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Nov 30, 2023
0d83c68
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Dec 13, 2023
a136b74
Fix conflicts after merge
cristipaval Dec 13, 2023
262cb52
RUn prettier
cristipaval Dec 13, 2023
cd28c61
Performance test for ModifiedExpenseMessage
cristipaval Dec 13, 2023
5219b6a
Fix typecheck failures
cristipaval Dec 13, 2023
df90850
Unit test for ModifiedExpenseMessage
cristipaval Dec 14, 2023
db1f935
More tests.
cristipaval Dec 14, 2023
8babd0e
One more test
cristipaval Dec 14, 2023
02c09f2
One more test
cristipaval Dec 14, 2023
a6a28a6
3 more tests
cristipaval Dec 14, 2023
27945e8
3 more tests
cristipaval Dec 14, 2023
8aad9bd
Make lint happy an run prettier
cristipaval Dec 14, 2023
d22c617
Merge remote-tracking branch 'origin/main' into cristi_modified-expen…
cristipaval Dec 18, 2023
13f7b9c
Add changes from main
cristipaval Dec 18, 2023
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
10 changes: 6 additions & 4 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,10 @@ export default {
noReimbursableExpenses: 'This report has an invalid amount',
pendingConversionMessage: "Total will update when you're back online",
changedTheRequest: 'changed the request',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `set the ${valueName} to ${newValueToDisplay}`,
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `the ${valueName} to ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) => `set the distance to ${newDistanceToDisplay}, which set the amount to ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `removed the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) =>
`changed the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) => `the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`changed the distance to ${newDistanceToDisplay} (previously ${oldDistanceToDisplay}), which updated the amount to ${newAmountToDisplay} (previously ${oldAmountToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} request${comment ? ` for ${comment}` : ''}`,
Expand All @@ -622,6 +621,9 @@ export default {
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Started settling up, payment is held until ${submitterDisplayName} enables their Wallet`,
enableWallet: 'Enable Wallet',
set: 'set',
changed: 'changed',
removed: 'removed',
},
notificationPreferencesPage: {
header: 'Notification preferences',
Expand Down
10 changes: 6 additions & 4 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,13 +587,12 @@ export default {
noReimbursableExpenses: 'El importe de este informe no es válido',
pendingConversionMessage: 'El total se actualizará cuando estés online',
changedTheRequest: 'cambió la solicitud',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `estableció ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) =>
`estableció la distancia a ${newDistanceToDisplay}, lo que estableció el importe a ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) =>
`eliminó ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} (previamente ${oldValueToDisplay})`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} (previamente ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) =>
`cambió ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
`${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`cambió la distancia a ${newDistanceToDisplay} (previamente ${oldDistanceToDisplay}), lo que cambió el importe a ${newAmountToDisplay} (previamente ${oldAmountToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Solicitud de ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
Expand All @@ -617,6 +616,9 @@ export default {
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Inició el pago, pero no se procesará hasta que ${submitterDisplayName} active su Billetera`,
enableWallet: 'Habilitar Billetera',
set: 'estableció',
changed: 'cambió',
removed: 'eliminó',
},
notificationPreferencesPage: {
header: 'Preferencias de avisos',
Expand Down
226 changes: 226 additions & 0 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import {format} from 'date-fns';
import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {PolicyTags, ReportAction} from '@src/types/onyx';
import * as CurrencyUtils from './CurrencyUtils';
import * as Localize from './Localize';
import * as PolicyUtils from './PolicyUtils';
import * as ReportUtils from './ReportUtils';
import {ExpenseOriginalMessage} from './ReportUtils';

let allPolicyTags: Record<string, PolicyTags | null> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
if (!value) {
allPolicyTags = {};
return;
}
allPolicyTags = value;
},
});

/**
* Builds the partial message fragment for a modified field on the expense.
*/
function buildMessageFragmentForValue(
newValue: string,
oldValue: string,
valueName: string,
valueInQuotes: boolean,
setFragments: string[],
removalFragments: string[],
changeFragments: string[],
shouldConvertToLowercase = true,
) {
const newValueToDisplay = valueInQuotes ? `"${newValue}"` : newValue;
const oldValueToDisplay = valueInQuotes ? `"${oldValue}"` : oldValue;
const displayValueName = shouldConvertToLowercase ? valueName.toLowerCase() : valueName;

if (!oldValue) {
const fragment = Localize.translateLocal('iou.setTheRequest', {valueName: displayValueName, newValueToDisplay});
setFragments.push(fragment);
} else if (!newValue) {
const fragment = Localize.translateLocal('iou.removedTheRequest', {valueName: displayValueName, oldValueToDisplay});
removalFragments.push(fragment);
} else {
const fragment = Localize.translateLocal('iou.updatedTheRequest', {valueName: displayValueName, newValueToDisplay, oldValueToDisplay});
changeFragments.push(fragment);
}
}

/**
* Get the message line for a modified expense.
*/
function getMessageLine(prefix: string, messageFragments: string[]): string {
if (messageFragments.length === 0) {
return '';
}
return messageFragments.reduce((acc, value, index) => {
if (index === messageFragments.length - 1) {
if (messageFragments.length === 1) {
return `${acc} ${value}.`;
}
if (messageFragments.length === 2) {
return `${acc} ${Localize.translateLocal('common.and')} ${value}.`;
}
return `${acc}, ${Localize.translateLocal('common.and')} ${value}.`;
}
if (index === 0) {
return `${acc} ${value}`;
}
return `${acc}, ${value}`;
}, prefix);
}

function getForDistanceRequest(newDistance: string, oldDistance: string, newAmount: string, oldAmount: string): string {
if (!oldDistance) {
return Localize.translateLocal('iou.setTheDistance', {newDistanceToDisplay: newDistance, newAmountToDisplay: newAmount});
}
return Localize.translateLocal('iou.updatedTheDistance', {
newDistanceToDisplay: newDistance,
oldDistanceToDisplay: oldDistance,
newAmountToDisplay: newAmount,
oldAmountToDisplay: oldAmount,
});
}

/**
* Get the report action message when expense has been modified.
*
* ModifiedExpense::getNewDotComment in Web-Expensify should match this.
* If we change this function be sure to update the backend as well.
*/
function getForReportAction(reportAction: ReportAction): string {
if (reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE) {
return '';
}
const reportActionOriginalMessage = reportAction.originalMessage as ExpenseOriginalMessage | undefined;
const policyID = ReportUtils.getReportPolicyID(reportAction.reportID) ?? '';
Copy link
Contributor

Choose a reason for hiding this comment

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

MODIFIEDEXPENSE report actions don't have the reportID property, so we fail to get the policyTagListName here. This caused #33493.

const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {};
const policyTagListName = PolicyUtils.getTagListName(policyTags) || Localize.translateLocal('common.tag');

const removalFragments: string[] = [];
const setFragments: string[] = [];
const changeFragments: string[] = [];

const hasModifiedAmount =
reportActionOriginalMessage &&
'oldAmount' in reportActionOriginalMessage &&
'oldCurrency' in reportActionOriginalMessage &&
'amount' in reportActionOriginalMessage &&
'currency' in reportActionOriginalMessage;

const hasModifiedMerchant = reportActionOriginalMessage && 'oldMerchant' in reportActionOriginalMessage && 'merchant' in reportActionOriginalMessage;
if (hasModifiedAmount) {
const oldCurrency = reportActionOriginalMessage?.oldCurrency ?? '';
const oldAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency);

const currency = reportActionOriginalMessage?.currency ?? '';
const amount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency);

// Only Distance edits should modify amount and merchant (which stores distance) in a single transaction.
// We check the merchant is in distance format (includes @) as a sanity check
if (hasModifiedMerchant && (reportActionOriginalMessage?.merchant ?? '').includes('@')) {
return getForDistanceRequest(reportActionOriginalMessage?.merchant ?? '', reportActionOriginalMessage?.oldMerchant ?? '', amount, oldAmount);
}

buildMessageFragmentForValue(amount, oldAmount, Localize.translateLocal('iou.amount'), false, setFragments, removalFragments, changeFragments);
}

const hasModifiedComment = reportActionOriginalMessage && 'oldComment' in reportActionOriginalMessage && 'newComment' in reportActionOriginalMessage;
if (hasModifiedComment) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.newComment ?? '',
reportActionOriginalMessage?.oldComment ?? '',
Localize.translateLocal('common.description'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedCreated = reportActionOriginalMessage && 'oldCreated' in reportActionOriginalMessage && 'created' in reportActionOriginalMessage;
if (hasModifiedCreated) {
// Take only the YYYY-MM-DD value as the original date includes timestamp
let formattedOldCreated: Date | string = new Date(reportActionOriginalMessage?.oldCreated ? reportActionOriginalMessage.oldCreated : 0);
formattedOldCreated = format(formattedOldCreated, CONST.DATE.FNS_FORMAT_STRING);
buildMessageFragmentForValue(
reportActionOriginalMessage?.created ?? '',
formattedOldCreated,
Localize.translateLocal('common.date'),
false,
setFragments,
removalFragments,
changeFragments,
);
}

if (hasModifiedMerchant) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.merchant ?? '',
reportActionOriginalMessage?.oldMerchant ?? '',
Localize.translateLocal('common.merchant'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedCategory = reportActionOriginalMessage && 'oldCategory' in reportActionOriginalMessage && 'category' in reportActionOriginalMessage;
if (hasModifiedCategory) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.category ?? '',
reportActionOriginalMessage?.oldCategory ?? '',
Localize.translateLocal('common.category'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedTag = reportActionOriginalMessage && 'oldTag' in reportActionOriginalMessage && 'tag' in reportActionOriginalMessage;
if (hasModifiedTag) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.tag ?? '',
reportActionOriginalMessage?.oldTag ?? '',
policyTagListName,
true,
setFragments,
removalFragments,
changeFragments,
policyTagListName === Localize.translateLocal('common.tag'),
);
}

const hasModifiedBillable = reportActionOriginalMessage && 'oldBillable' in reportActionOriginalMessage && 'billable' in reportActionOriginalMessage;
if (hasModifiedBillable) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.billable ?? '',
reportActionOriginalMessage?.oldBillable ?? '',
Localize.translateLocal('iou.request'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const message =
getMessageLine(`\n${Localize.translateLocal('iou.changed')}`, changeFragments) +
getMessageLine(`\n${Localize.translateLocal('iou.set')}`, setFragments) +
getMessageLine(`\n${Localize.translateLocal('iou.removed')}`, removalFragments);
if (message === '') {
return Localize.translateLocal('iou.changedTheRequest');
}
return `${message.substring(1, message.length)}`;
Comment on lines +218 to +221
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (message === '') {
return Localize.translateLocal('iou.changedTheRequest');
}
return `${message.substring(1, message.length)}`;
return message ? message.substring(1) : Localize.translateLocal('iou.changedTheRequest');

}

export default {
getForReportAction,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Str from 'expensify-common/lib/str';
import {ImageSourcePropType} from 'react-native';
import EXPENSIFY_ICON_URL from '@assets/images/expensify-logo-round-clearspace.png';
import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage';
import * as ReportUtils from '@libs/ReportUtils';
import * as AppUpdate from '@userActions/AppUpdate';
import {Report, ReportAction} from '@src/types/onyx';
Expand Down Expand Up @@ -108,7 +109,7 @@ export default {

pushModifiedExpenseNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, usesIcon = false) {
const title = reportAction.person?.map((f) => f.text).join(', ') ?? '';
const body = ReportUtils.getModifiedExpenseMessage(reportAction);
const body = ModifiedExpenseMessage.getForReportAction(reportAction);
const icon = usesIcon ? EXPENSIFY_ICON_URL : '';
const data = {
reportID: report.reportID,
Expand Down
3 changes: 2 additions & 1 deletion src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as ErrorUtils from './ErrorUtils';
import * as LocalePhoneNumber from './LocalePhoneNumber';
import * as Localize from './Localize';
import * as LoginUtils from './LoginUtils';
import ModifiedExpenseMessage from './ModifiedExpenseMessage';
import Navigation from './Navigation/Navigation';
import Permissions from './Permissions';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
Expand Down Expand Up @@ -407,7 +408,7 @@ function getLastMessageTextForReport(report) {
} else if (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) {
lastMessageTextFromReport = `[${Localize.translateLocal(report.lastMessageTranslationKey || 'common.attachment')}]`;
} else if (ReportActionUtils.isModifiedExpenseAction(lastReportAction)) {
const properSchemaForModifiedExpenseMessage = ReportUtils.getModifiedExpenseMessage(lastReportAction);
const properSchemaForModifiedExpenseMessage = ModifiedExpenseMessage.getForReportAction(lastReportAction);
lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true);
} else if (
lastActionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED ||
Expand Down
Loading
Loading