Skip to content

Commit 24e105a

Browse files
authored
Merge pull request Expensify#65596 from hoangzinh/merge-expenses-phase-1
Merge expenses phase 1
2 parents 9174da5 + 613de45 commit 24e105a

59 files changed

Lines changed: 3754 additions & 39 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

assets/images/simple-illustrations/simple-illustration__empty-shelves.svg

Lines changed: 194 additions & 0 deletions
Loading

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"ADDCOMMENT",
99
"Addendums",
1010
"ADFS",
11+
"aeiou",
1112
"Aeroplan",
1213
"águero",
1314
"Aircall",

src/CONST/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ const CONST = {
10851085
REOPEN: 'reopen',
10861086
EXPORT: 'export',
10871087
PAY: 'pay',
1088+
MERGE: 'merge',
10881089
},
10891090
PRIMARY_ACTIONS: {
10901091
SUBMIT: 'submit',
@@ -1116,6 +1117,7 @@ const CONST = {
11161117
SPLIT: 'split',
11171118
VIEW_DETAILS: 'viewDetails',
11181119
DELETE: 'delete',
1120+
MERGE: 'merge',
11191121
},
11201122
ADD_EXPENSE_OPTIONS: {
11211123
CREATE_NEW_EXPENSE: 'createNewExpense',
@@ -3097,6 +3099,7 @@ const CONST = {
30973099
FinancialForce: 'https://help.expensify.com/articles/expensify-classic/connections/certinia/Connect-To-Certinia',
30983100
'Sage Intacct': 'https://help.expensify.com/articles/new-expensify/connections/sage-intacct/Configure-Sage-Intacct',
30993101
Certinia: 'https://help.expensify.com/articles/expensify-classic/connections/certinia/Connect-To-Certinia',
3102+
MERGE_EXPENSES: 'https://help.expensify.com/articles/new-expensify/reports-and-expenses/Merging-expenses',
31003103
},
31013104

31023105
CUSTOM_UNITS: {

src/ONYXKEYS.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ const ONYXKEYS = {
607607
SKIP_CONFIRMATION: 'skipConfirmation_',
608608
TRANSACTION_BACKUP: 'transactionsBackup_',
609609
SPLIT_TRANSACTION_DRAFT: 'splitTransactionDraft_',
610+
MERGE_TRANSACTION: 'mergeTransaction_',
610611
PRIVATE_NOTES_DRAFT: 'privateNotesDraft_',
611612
NEXT_STEP: 'reportNextStep_',
612613

@@ -1008,6 +1009,7 @@ type OnyxCollectionValuesMapping = {
10081009
[ONYXKEYS.COLLECTION.TRANSACTION_BACKUP]: OnyxTypes.Transaction;
10091010
[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations;
10101011
[ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction;
1012+
[ONYXKEYS.COLLECTION.MERGE_TRANSACTION]: OnyxTypes.MergeTransaction;
10111013
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags;
10121014
[ONYXKEYS.COLLECTION.OLD_POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags;
10131015
[ONYXKEYS.COLLECTION.SELECTED_TAB]: OnyxTypes.SelectedTabRequest;

src/ROUTES.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,15 @@ const ROUTES = {
20732073

20742074
TRANSACTION_RECEIPT: {
20752075
route: 'r/:reportID/transaction/:transactionID/receipt/:action?/:iouType?',
2076-
getRoute: (reportID: string | undefined, transactionID: string | undefined, readonly = false, isFromReviewDuplicates = false, action?: IOUAction, iouType?: IOUType) => {
2076+
getRoute: (
2077+
reportID: string | undefined,
2078+
transactionID: string | undefined,
2079+
readonly = false,
2080+
isFromReviewDuplicates = false,
2081+
action?: IOUAction,
2082+
iouType?: IOUType,
2083+
mergeTransactionID?: string,
2084+
) => {
20772085
if (!reportID) {
20782086
Log.warn('Invalid reportID is used to build the TRANSACTION_RECEIPT route');
20792087
}
@@ -2082,7 +2090,7 @@ const ROUTES = {
20822090
}
20832091
return `r/${reportID}/transaction/${transactionID}/receipt${action ? `/${action}` : ''}${iouType ? `/${iouType}` : ''}?readonly=${readonly}${
20842092
isFromReviewDuplicates ? '&isFromReviewDuplicates=true' : ''
2085-
}` as const;
2093+
}${mergeTransactionID ? `&mergeTransactionID=${mergeTransactionID}` : ''}` as const;
20862094
},
20872095
},
20882096

@@ -2122,6 +2130,22 @@ const ROUTES = {
21222130
route: 'r/:threadReportID/duplicates/confirm',
21232131
getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/confirm` as const, backTo),
21242132
},
2133+
MERGE_TRANSACTION_LIST_PAGE: {
2134+
route: 'r/:transactionID/merge',
2135+
getRoute: (transactionID: string | undefined, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge` as const, backTo),
2136+
},
2137+
MERGE_TRANSACTION_RECEIPT_PAGE: {
2138+
route: 'r/:transactionID/merge/receipt',
2139+
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/receipt` as const, backTo),
2140+
},
2141+
MERGE_TRANSACTION_DETAILS_PAGE: {
2142+
route: 'r/:transactionID/merge/details',
2143+
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/details` as const, backTo),
2144+
},
2145+
MERGE_TRANSACTION_CONFIRMATION_PAGE: {
2146+
route: 'r/:transactionID/merge/confirmation',
2147+
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/confirmation` as const, backTo),
2148+
},
21252149
POLICY_ACCOUNTING_XERO_IMPORT: {
21262150
route: 'workspaces/:policyID/accounting/xero/import',
21272151
getRoute: (policyID: string) => `workspaces/${policyID}/accounting/xero/import` as const,

src/SCREENS.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ const SCREENS = {
224224
DEBUG: 'Debug',
225225
ADD_UNREPORTED_EXPENSE: 'AddUnreportedExpense',
226226
SCHEDULE_CALL: 'ScheduleCall',
227+
MERGE_TRANSACTION: 'MergeTransaction',
227228
},
228229
PUBLIC_CONSOLE_DEBUG: 'Console_Debug',
229230
SIGN_IN_WITH_APPLE_DESKTOP: 'AppleSignInDesktop',
@@ -291,6 +292,13 @@ const SCREENS = {
291292
CONFIRMATION: 'Transaction_Duplicate_Confirmation',
292293
},
293294

295+
MERGE_TRANSACTION: {
296+
LIST_PAGE: 'Merge_Transaction_List_Page',
297+
RECEIPT_PAGE: 'Merge_Transaction_Receipt_Page',
298+
DETAILS_PAGE: 'Merge_Transaction_Details_Page',
299+
CONFIRMATION_PAGE: 'Merge_Transaction_Confirmation_Page',
300+
},
301+
294302
IOU_SEND: {
295303
ADD_BANK_ACCOUNT: 'IOU_Send_Add_Bank_Account',
296304
ADD_DEBIT_CARD: 'IOU_Send_Add_Debit_Card',

src/components/Icon/Illustrations.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustrat
9696
import CreditCardEyes from '@assets/images/simple-illustrations/simple-illustration__creditcardeyes.svg';
9797
import CreditCardsNewGreen from '@assets/images/simple-illustrations/simple-illustration__creditcards--green.svg';
9898
import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg';
99+
import EmptyShelves from '@assets/images/simple-illustrations/simple-illustration__empty-shelves.svg';
99100
import EmptyState from '@assets/images/simple-illustrations/simple-illustration__empty-state.svg';
100101
import Encryption from '@assets/images/simple-illustrations/simple-illustration__encryption.svg';
101102
import EnvelopeReceipt from '@assets/images/simple-illustrations/simple-illustration__envelopereceipt.svg';
@@ -326,4 +327,5 @@ export {
326327
ReceiptsStackedOnPin,
327328
PaperAirplane,
328329
CardReplacementSuccess,
330+
EmptyShelves,
329331
};

src/components/MoneyReportHeader.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
1616
import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsActions';
1717
import useTheme from '@hooks/useTheme';
1818
import useThemeStyles from '@hooks/useThemeStyles';
19+
import {setupMergeTransactionData} from '@libs/actions/MergeTransaction';
1920
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
2021
import {deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported, openReport, openUnreportedExpense} from '@libs/actions/Report';
2122
import {queueExportSearchWithTemplate} from '@libs/actions/Search';
@@ -306,6 +307,7 @@ function MoneyReportHeader({
306307
allTransactionsLength: transactions.length,
307308
session,
308309
onExportFailed: () => setIsDownloadErrorModalVisible(true),
310+
policy,
309311
beginExportWithTemplate: (templateName, templateType, transactionIDList) => beginExportWithTemplate(templateName, templateType, transactionIDList),
310312
});
311313

@@ -892,6 +894,20 @@ function MoneyReportHeader({
892894
initSplitExpense(currentTransaction);
893895
},
894896
},
897+
[CONST.REPORT.SECONDARY_ACTIONS.MERGE]: {
898+
text: translate('common.merge'),
899+
icon: Expensicons.ArrowCollapse,
900+
value: CONST.REPORT.SECONDARY_ACTIONS.MERGE,
901+
onSelected: () => {
902+
const currentTransaction = transactions.at(0);
903+
if (!currentTransaction) {
904+
return;
905+
}
906+
907+
setupMergeTransactionData(currentTransaction.transactionID, {targetTransactionID: currentTransaction.transactionID});
908+
Navigation.navigate(ROUTES.MERGE_TRANSACTION_LIST_PAGE.getRoute(currentTransaction.transactionID, Navigation.getActiveRoute()));
909+
},
910+
},
895911
[CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE]: {
896912
text: translate('iou.changeWorkspace'),
897913
icon: Expensicons.Buildings,

src/components/MoneyRequestHeader.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useTheme from '@hooks/useTheme';
1313
import useThemeStyles from '@hooks/useThemeStyles';
1414
import useTransactionViolations from '@hooks/useTransactionViolations';
1515
import {deleteMoneyRequest, deleteTrackExpense, initSplitExpense} from '@libs/actions/IOU';
16+
import {setupMergeTransactionData} from '@libs/actions/MergeTransaction';
1617
import Navigation from '@libs/Navigation/Navigation';
1718
import {getOriginalMessage, getReportActions, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils';
1819
import {getTransactionThreadPrimaryAction} from '@libs/ReportPrimaryActionUtils';
@@ -244,6 +245,19 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
244245
initSplitExpense(transaction);
245246
},
246247
},
248+
[CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MERGE]: {
249+
text: translate('common.merge'),
250+
icon: Expensicons.ArrowCollapse,
251+
value: CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.MERGE,
252+
onSelected: () => {
253+
if (!transaction) {
254+
return;
255+
}
256+
257+
setupMergeTransactionData(transaction.transactionID, {targetTransactionID: transaction.transactionID});
258+
Navigation.navigate(ROUTES.MERGE_TRANSACTION_LIST_PAGE.getRoute(transaction.transactionID, Navigation.getActiveRoute()));
259+
},
260+
},
247261
[CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]: {
248262
value: CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS,
249263
text: translate('iou.viewDetails'),

src/components/RadioButton.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,45 @@ type RadioButtonProps = {
2121

2222
/** Should the input be disabled */
2323
disabled?: boolean;
24+
25+
/** Whether to use new radio button style */
26+
// See https://expensify.slack.com/archives/C07HPDRELLD/p1752500012040139?thread_ts=1751637205.950179&cid=C07HPDRELLD
27+
shouldUseNewStyle?: boolean;
2428
};
2529

26-
function RadioButton({isChecked, onPress, accessibilityLabel, hasError = false, disabled = false}: RadioButtonProps) {
30+
function RadioButton({isChecked, onPress, accessibilityLabel, hasError = false, disabled = false, shouldUseNewStyle = false}: RadioButtonProps) {
2731
const theme = useTheme();
2832
const styles = useThemeStyles();
2933

34+
if (shouldUseNewStyle) {
35+
return (
36+
<PressableWithFeedback
37+
disabled={disabled}
38+
onPress={onPress}
39+
hoverDimmingValue={1}
40+
pressDimmingValue={1}
41+
accessibilityLabel={accessibilityLabel}
42+
role={CONST.ROLE.RADIO}
43+
style={[
44+
styles.newRadioButtonContainer,
45+
hasError && styles.borderColorDanger,
46+
disabled && styles.cursorDisabled,
47+
isChecked && styles.checkedContainer,
48+
isChecked && styles.borderColorFocus,
49+
]}
50+
>
51+
{isChecked && (
52+
<Icon
53+
src={Expensicons.Checkmark}
54+
fill={theme.textLight}
55+
height={14}
56+
width={14}
57+
/>
58+
)}
59+
</PressableWithFeedback>
60+
);
61+
}
62+
3063
return (
3164
<PressableWithFeedback
3265
disabled={disabled}

0 commit comments

Comments
 (0)