From 7ed380d6e79479859eb7c54f7fe7ae5b644d96b6 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Fri, 21 Nov 2025 20:06:24 +0300 Subject: [PATCH 01/10] disallow adding transaction if payment is disabled for submit and close policy with instant submit --- src/libs/ReportUtils.ts | 9 +++- .../step/IOURequestEditReportCommon.tsx | 2 +- tests/unit/ReportUtilsTest.ts | 49 +++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a0601c75a27c..68dc3f9a3c73 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2699,14 +2699,19 @@ function canAddOrDeleteTransactions(moneyRequestReport: OnyxEntry, isRep * Returns false if: * - if current user is not the submitter of an expense report */ -function canAddTransaction(moneyRequestReport: OnyxEntry, isReportArchived = false): boolean { +function canAddTransaction(moneyRequestReport: OnyxEntry, isReportArchived = false, isMovingTransaction = false): boolean { if (!isMoneyRequestReport(moneyRequestReport) || (isExpenseReport(moneyRequestReport) && !isCurrentUserSubmitter(moneyRequestReport))) { return false; } // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line @typescript-eslint/no-deprecated const policy = getPolicy(moneyRequestReport?.policyID); - if (isInstantSubmitEnabled(policy) && isSubmitAndClose(policy) && hasOnlyNonReimbursableTransactions(moneyRequestReport?.reportID)) { + if ( + isInstantSubmitEnabled(policy) && + isSubmitAndClose(policy) && + (hasOnlyNonReimbursableTransactions(moneyRequestReport?.reportID) || + (!isMovingTransaction && !isOpenExpenseReport(moneyRequestReport) && policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO)) + ) { return false; } diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 30a9e88c098c..0480a7a40e77 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -158,7 +158,7 @@ function IOURequestEditReportCommon({ return true; }) .filter((report) => { - if (canAddTransaction(report)) { + if (canAddTransaction(report, undefined, true)) { return true; } diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 25fe39149f70..8953c88b35ee 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5765,6 +5765,55 @@ describe('ReportUtils', () => { expect(result).toBe(false); }); + it('should return false for a submitted report when the policy is submit and close with payment disabled', async () => { + // Given the policy is submit and close with payment disabled + const workflowDisabledPolicy: Policy = { + ...createRandomPolicy(2962), + autoReporting: true, + autoReportingFrequency: CONST.POLICY.AUTO_REPORTING_FREQUENCIES.INSTANT, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO, + employeeList: { + [currentUserEmail]: {email: currentUserEmail, submitsTo: currentUserEmail}, + }, + approver: currentUserEmail, + }; + const report: Report = { + ...createRandomReport(10002, undefined), + type: CONST.REPORT.TYPE.EXPENSE, + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + policyID: workflowDisabledPolicy.id, + ownerAccountID: currentUserAccountID, + managerID: currentUserAccountID, + }; + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${workflowDisabledPolicy.id}`, workflowDisabledPolicy); + await Onyx.set(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, { + [currentUserAccountID]: { + accountID: currentUserAccountID, + displayName: 'Lagertha Lothbrok', + firstName: 'Lagertha', + login: currentUserEmail, + pronouns: 'She/her', + }, + }); + + const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.reportID)); + + // If the canAddTransaction is used for the case of adding expense into the report + const result = canAddTransaction(report, isReportArchived.current); + + // Then the result should be false + expect(result).toBe(false); + + // If the canAddTransaction is used for the case of moving transaction into the report + const result2 = canAddTransaction(report, isReportArchived.current, true); + + // Then the result should be true + expect(result2).toBe(true); + }); + it('should return false for an archived report', async () => { // Given an archived expense report const report: Report = { From 8cb2b3beb0ac1a0a4b4affce8198b897016a80c8 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Tue, 9 Dec 2025 18:18:17 +0300 Subject: [PATCH 02/10] fix test --- src/types/onyx/OriginalMessage.ts | 8 +++++++- tests/unit/ReportUtilsTest.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index ca94b22a6ed8..63d2a6d5e112 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -237,6 +237,12 @@ type OriginalMessageSubmitted = { cc?: string; }; +/** Model of `created` report action */ +type OriginalMessageCreated = { + /** The account id of the user the report is submitted to */ + submittedTo?: number; +}; + /** Model of `closed` report action */ type OriginalMessageClosed = { /** Name of the policy */ @@ -1056,7 +1062,7 @@ type OriginalMessageMap = { [CONST.REPORT.ACTIONS.TYPE.CHANGE_TYPE]: never; [CONST.REPORT.ACTIONS.TYPE.CHRONOS_OOO_LIST]: OriginalMessageChronosOOOList; [CONST.REPORT.ACTIONS.TYPE.CLOSED]: OriginalMessageClosed; - [CONST.REPORT.ACTIONS.TYPE.CREATED]: never; + [CONST.REPORT.ACTIONS.TYPE.CREATED]: OriginalMessageCreated; [CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION]: OriginalMessageDismissedViolation; [CONST.REPORT.ACTIONS.TYPE.EXPENSIFY_CARD_SYSTEM_MESSAGE]: never; [CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_CSV]: never; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 8ba02c0f9cfe..6d6cca583329 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5972,7 +5972,9 @@ describe('ReportUtils', () => { ownerAccountID: currentUserAccountID, managerID: currentUserAccountID, }; + const createdAction: ReportAction = {...createRandomReportAction(123), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, originalMessage: {submittedTo: currentUserAccountID}}; await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, {[createdAction.reportActionID]: createdAction}); await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${workflowDisabledPolicy.id}`, workflowDisabledPolicy); await Onyx.set(`${ONYXKEYS.PERSONAL_DETAILS_LIST}`, { [currentUserAccountID]: { From b2278d6b39a849949ce6cbe61ba02f387e75dfb6 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 15:19:05 +0300 Subject: [PATCH 03/10] fix lint --- src/libs/ReportUtils.ts | 5 +++-- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5a68b81d8cfc..81ab402d112a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -252,6 +252,7 @@ import { wasActionTakenByCurrentUser, } from './ReportActionsUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; +import {computeReportName} from './ReportNameUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; import { getAttendees, @@ -6979,7 +6980,7 @@ function getMovedTransactionMessage(action: ReportAction) { const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; const report = fromReport ?? toReport; - const reportName = getReportName(report) ?? report?.reportName ?? ''; + const reportName = computeReportName(report) ?? report?.reportName ?? ''; let reportUrl = getReportURLForCurrentContext(report?.reportID); if (typeof fromReportID === 'undefined') { // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -7008,7 +7009,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; - const reportName = getReportName(fromReport) ?? fromReport?.reportName ?? ''; + const reportName = computeReportName(fromReport) ?? fromReport?.reportName ?? ''; let reportUrl = `${environmentURL}/r/${fromReport?.reportID}`; diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index ba17fe2670bf..a2ac2ccaa71e 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -18,6 +18,7 @@ import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useReportTransactions from '@hooks/useReportTransactions'; import Navigation from '@libs/Navigation/Navigation'; import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; +import {computeReportName} from '@libs/ReportNameUtils'; import { canAddTransaction, getOutstandingReportsForUser, @@ -188,7 +189,7 @@ function IOURequestEditReportCommon({ // We set it to null here to prevent showing RBR for reports https://github.com/Expensify/App/issues/65960. brickRoadIndicator: null, alternateText: getPolicyName({report}) ?? matchingOption?.alternateText, - text: getReportName(report), + text: computeReportName(report), value: report.reportID, keyForList: report.reportID, isSelected: report.reportID === selectedReportID, From 5caa9457cc3c2bd14bad1860462b182d9ab7d4a5 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 15:20:32 +0300 Subject: [PATCH 04/10] fix lint --- .../iou/request/step/IOURequestEditReportCommon.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index a2ac2ccaa71e..edf936ccc3fd 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -19,17 +19,7 @@ import useReportTransactions from '@hooks/useReportTransactions'; import Navigation from '@libs/Navigation/Navigation'; import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import {computeReportName} from '@libs/ReportNameUtils'; -import { - canAddTransaction, - getOutstandingReportsForUser, - getPolicyName, - getReportName, - isIOUReport, - isOpenReport, - isReportOwner, - isSelfDM, - sortOutstandingReportsBySelected, -} from '@libs/ReportUtils'; +import {canAddTransaction, getOutstandingReportsForUser, getPolicyName, isIOUReport, isOpenReport, isReportOwner, isSelfDM, sortOutstandingReportsBySelected} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; From 620e4300629a4a9a48e1f8223d37a5eba2c61a21 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 15:27:28 +0300 Subject: [PATCH 05/10] fix lint --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 81ab402d112a..5ec05ff87fa5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -252,6 +252,7 @@ import { wasActionTakenByCurrentUser, } from './ReportActionsUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; +// eslint-disable-next-line import/no-cycle import {computeReportName} from './ReportNameUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; import { From f5c3d3cd4817907e6831a306d230cd70a42e15c1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 15:56:18 +0300 Subject: [PATCH 06/10] fix lint --- src/libs/ReportNameUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index f06635a3e259..25d692e71487 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -25,6 +25,7 @@ import {convertToDisplayString} from './CurrencyUtils'; import {formatPhoneNumber as formatPhoneNumberPhoneUtils} from './LocalePhoneNumber'; // eslint-disable-next-line @typescript-eslint/no-deprecated import {translateLocal} from './Localize'; +// eslint-disable-next-line import/no-cycle import {getForReportAction, getMovedReportID} from './ModifiedExpenseMessage'; import Parser from './Parser'; import {getDisplayNameOrDefault} from './PersonalDetailsUtils'; From 70b4fd7903b41c10ba0aa59dd752a6fba6dda613 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 19:33:32 +0300 Subject: [PATCH 07/10] Revert "fix lint" This reverts commit f5c3d3cd4817907e6831a306d230cd70a42e15c1. --- src/libs/ReportNameUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 25d692e71487..f06635a3e259 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -25,7 +25,6 @@ import {convertToDisplayString} from './CurrencyUtils'; import {formatPhoneNumber as formatPhoneNumberPhoneUtils} from './LocalePhoneNumber'; // eslint-disable-next-line @typescript-eslint/no-deprecated import {translateLocal} from './Localize'; -// eslint-disable-next-line import/no-cycle import {getForReportAction, getMovedReportID} from './ModifiedExpenseMessage'; import Parser from './Parser'; import {getDisplayNameOrDefault} from './PersonalDetailsUtils'; From 767634f26fd9e80f2eea1af2de38f5e8bdb7be04 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 19:33:48 +0300 Subject: [PATCH 08/10] Revert "fix lint" This reverts commit 5caa9457cc3c2bd14bad1860462b182d9ab7d4a5. --- .../iou/request/step/IOURequestEditReportCommon.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index edf936ccc3fd..a2ac2ccaa71e 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -19,7 +19,17 @@ import useReportTransactions from '@hooks/useReportTransactions'; import Navigation from '@libs/Navigation/Navigation'; import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import {computeReportName} from '@libs/ReportNameUtils'; -import {canAddTransaction, getOutstandingReportsForUser, getPolicyName, isIOUReport, isOpenReport, isReportOwner, isSelfDM, sortOutstandingReportsBySelected} from '@libs/ReportUtils'; +import { + canAddTransaction, + getOutstandingReportsForUser, + getPolicyName, + getReportName, + isIOUReport, + isOpenReport, + isReportOwner, + isSelfDM, + sortOutstandingReportsBySelected, +} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; From 5a5a038ba5d551fb0cb401530bbf33090afbf5b1 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 19:35:53 +0300 Subject: [PATCH 09/10] Revert "fix lint" This reverts commit b2278d6b39a849949ce6cbe61ba02f387e75dfb6. --- src/libs/ReportUtils.ts | 6 ++---- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5ec05ff87fa5..5a68b81d8cfc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -252,8 +252,6 @@ import { wasActionTakenByCurrentUser, } from './ReportActionsUtils'; import type {LastVisibleMessage} from './ReportActionsUtils'; -// eslint-disable-next-line import/no-cycle -import {computeReportName} from './ReportNameUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; import { getAttendees, @@ -6981,7 +6979,7 @@ function getMovedTransactionMessage(action: ReportAction) { const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; const report = fromReport ?? toReport; - const reportName = computeReportName(report) ?? report?.reportName ?? ''; + const reportName = getReportName(report) ?? report?.reportName ?? ''; let reportUrl = getReportURLForCurrentContext(report?.reportID); if (typeof fromReportID === 'undefined') { // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -7010,7 +7008,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; - const reportName = computeReportName(fromReport) ?? fromReport?.reportName ?? ''; + const reportName = getReportName(fromReport) ?? fromReport?.reportName ?? ''; let reportUrl = `${environmentURL}/r/${fromReport?.reportID}`; diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index a2ac2ccaa71e..ba17fe2670bf 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -18,7 +18,6 @@ import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useReportTransactions from '@hooks/useReportTransactions'; import Navigation from '@libs/Navigation/Navigation'; import {canSubmitPerDiemExpenseFromWorkspace, getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; -import {computeReportName} from '@libs/ReportNameUtils'; import { canAddTransaction, getOutstandingReportsForUser, @@ -189,7 +188,7 @@ function IOURequestEditReportCommon({ // We set it to null here to prevent showing RBR for reports https://github.com/Expensify/App/issues/65960. brickRoadIndicator: null, alternateText: getPolicyName({report}) ?? matchingOption?.alternateText, - text: computeReportName(report), + text: getReportName(report), value: report.reportID, keyForList: report.reportID, isSelected: report.reportID === selectedReportID, From ec086e3af96f9a0661e51d7dd4e49509919e55d8 Mon Sep 17 00:00:00 2001 From: FitseTLT Date: Wed, 10 Dec 2025 19:38:35 +0300 Subject: [PATCH 10/10] suppress deprecated lint error --- src/libs/ReportUtils.ts | 3 ++- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5a68b81d8cfc..3d05747ee224 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6979,6 +6979,7 @@ function getMovedTransactionMessage(action: ReportAction) { const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; const report = fromReport ?? toReport; + // eslint-disable-next-line @typescript-eslint/no-deprecated const reportName = getReportName(report) ?? report?.reportName ?? ''; let reportUrl = getReportURLForCurrentContext(report?.reportID); if (typeof fromReportID === 'undefined') { @@ -7007,7 +7008,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const {fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; const fromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; - + // eslint-disable-next-line @typescript-eslint/no-deprecated const reportName = getReportName(fromReport) ?? fromReport?.reportName ?? ''; let reportUrl = `${environmentURL}/r/${fromReport?.reportID}`; diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index ba17fe2670bf..0358f27f99c9 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -188,6 +188,7 @@ function IOURequestEditReportCommon({ // We set it to null here to prevent showing RBR for reports https://github.com/Expensify/App/issues/65960. brickRoadIndicator: null, alternateText: getPolicyName({report}) ?? matchingOption?.alternateText, + // eslint-disable-next-line @typescript-eslint/no-deprecated text: getReportName(report), value: report.reportID, keyForList: report.reportID,