Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 5 additions & 7 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTop
import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types';
import Performance from '@libs/Performance';
import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
import {canAddOrDeleteTransactions, canEditFieldOfMoneyRequest, canHoldUnholdReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils';
import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils';
import {buildCannedSearchQuery, buildSearchQueryJSON, buildSearchQueryString} from '@libs/SearchQueryUtils';
import {
createAndOpenSearchTransactionThread,
Expand Down Expand Up @@ -103,7 +103,7 @@ function mapTransactionItemToSelectedEntry(
item.keyForList,
{
isSelected: true,
canDelete: canAddOrDeleteTransactions(item.report, item.policy),
canDelete: item.canDelete,
canHold: canHoldRequest,
isHeld: isOnHold(item),
canUnhold: canUnholdRequest,
Expand Down Expand Up @@ -193,7 +193,7 @@ function prepareTransactionsList(
...selectedTransactions,
[item.keyForList]: {
isSelected: true,
canDelete: canAddOrDeleteTransactions(item.report, item.policy),
canDelete: item.canDelete,
canHold: canHoldRequest,
isHeld: isOnHold(item),
canUnhold: canUnholdRequest,
Expand Down Expand Up @@ -535,8 +535,7 @@ function Search({
),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID]?.isSelected || isExpenseReportType,
canDelete: canAddOrDeleteTransactions(transactionItem.report, transactionItem.policy),

canDelete: transactionItem.canDelete,
reportID: transactionItem.reportID,
policyID: transactionItem.report?.policyID,
amount: transactionItem.modifiedAmount ?? transactionItem.amount,
Expand Down Expand Up @@ -586,8 +585,7 @@ function Search({
),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
isSelected: areAllMatchingItemsSelected || selectedTransactions[transactionItem.transactionID].isSelected,
canDelete: canAddOrDeleteTransactions(transactionItem.report, transactionItem.policy),

canDelete: transactionItem.canDelete,
reportID: transactionItem.reportID,
policyID: transactionItem.report?.policyID,
amount: transactionItem.modifiedAmount ?? transactionItem.amount,
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useSelectedTransactionsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import Navigation from '@libs/Navigation/Navigation';
import {getIOUActionForTransactionID, getReportAction, isDeletedAction} from '@libs/ReportActionsUtils';
import {isMergeAction, isSplitAction} from '@libs/ReportSecondaryActionUtils';
import {
canAddOrDeleteTransactions,
canDeleteCardTransactionByLiabilityType,
canDeleteTransaction,
canEditFieldOfMoneyRequest,
canHoldUnholdReportAction,
canUserPerformWriteAction as canUserPerformWriteActionReportUtils,
Expand Down Expand Up @@ -335,7 +335,7 @@ function useSelectedTransactionsActions({
return canRemoveTransaction && isIOUActionOwner && !isActionDeleted;
});

const canRemoveReportTransaction = canAddOrDeleteTransactions(report, policy, isReportArchived);
const canRemoveReportTransaction = canDeleteTransaction(report, isReportArchived);

if (canRemoveReportTransaction && canAllSelectedTransactionsBeRemoved) {
options.push({
Expand Down
3 changes: 1 addition & 2 deletions src/libs/ReportSecondaryActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
hasOnlyNonReimbursableTransactions,
hasReportBeenReopened as hasReportBeenReopenedUtils,
hasReportBeenRetracted as hasReportBeenRetractedUtils,
isArchivedReport, // eslint-disable-next-line @typescript-eslint/no-deprecated
isArchivedReport,
isAwaitingFirstLevelApproval,
isClosedReport as isClosedReportUtils,
isCurrentUserSubmitter,
Expand Down Expand Up @@ -125,7 +125,6 @@ function isSplitAction(report: OnyxEntry<Report>, reportTransactions: Array<Onyx
}

// Hide split option for the submitter if the report is forwarded
// eslint-disable-next-line @typescript-eslint/no-deprecated
return (isSubmitter && isAwaitingFirstLevelApproval(report)) || isAdmin || isManager;
}

Expand Down
93 changes: 20 additions & 73 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ import {
getAccountIDsByLogins,
getDisplayNameOrDefault,
getEffectiveDisplayName,
getLoginByAccountID,
getLoginsByAccountIDs,
getPersonalDetailByEmail,
getPersonalDetailsByIDs,
Expand All @@ -147,7 +146,6 @@ import {
getCleanedTagName,
getConnectedIntegration,
getCorrectedAutoReportingFrequency,
getDefaultApprover,
getForwardsToAccount,
getManagerAccountEmail,
getManagerAccountID,
Expand All @@ -157,7 +155,6 @@ import {
getRuleApprovers,
getSubmitToAccountID,
hasDependentTags as hasDependentTagsPolicyUtils,
hasDynamicExternalWorkflow,
isExpensifyTeam,
isInstantSubmitEnabled,
isPaidGroupPolicy as isPaidGroupPolicyPolicyUtils,
Expand Down Expand Up @@ -1441,7 +1438,7 @@ function isIOUReport(reportOrID: OnyxInputOrEntry<Report> | string): boolean {
/**
* Checks if a report is an IOU report using report
*/
function isIOUReportUsingReport(report: OnyxEntry<Report>): boolean {
function isIOUReportUsingReport(report: OnyxEntry<Report>): report is Report {
return report?.type === CONST.REPORT.TYPE.IOU;
}

Expand Down Expand Up @@ -1966,9 +1963,6 @@ function requiresManualSubmission(report: OnyxEntry<Report>, policy: OnyxEntry<P
return isManualSubmitEnabled || (isOpenReport(report) && isInstantSubmitEnabled(policy) && isSubmitAndClose(policy));
}

/**
* @deprecated This function will be removed soon. Please use isAwaitingFirstLevelApprovalNew instead
*/
function isAwaitingFirstLevelApproval(report: OnyxEntry<Report>): boolean {
if (!report) {
return false;
Expand All @@ -1981,63 +1975,6 @@ function isAwaitingFirstLevelApproval(report: OnyxEntry<Report>): boolean {
return isProcessingReport(report) && submitsToAccountID === report.managerID;
}

function isAwaitingFirstLevelApprovalNew(report: OnyxEntry<Report>, reportActions: ReportAction[], policy: OnyxEntry<Policy>): boolean {
if (!report) {
return false;
}

if (!isProcessingReport(report)) {
return false;
}

if (isIOUReportUsingReport(report)) {
return true;
}

if (hasDynamicExternalWorkflow(policy)) {
return false;
}

if (policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.BASIC) {
return true;
}

// If the report is part of a policy with Instant Submit, this data should be stored in the CREATED action
// as Instant Submit reports do not have a SUBMITTED action.
// For all other cases, use the most recent SUBMITTED action instead.
const usedReportAction = isInstantSubmitEnabled(policy)
? reportActions?.find((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED)
: reportActions
?.filter((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.SUBMITTED)
?.sort((a, b) => {
if (!a.created || !b.created) {
return !a.created ? 1 : -1;
}
return a.created < b.created ? 1 : -1;
})
?.at(0);

const originalMessage = getOriginalMessage(usedReportAction);
if (!originalMessage) {
return false;
}
let submittedTo: number | undefined = 'submittedTo' in originalMessage ? originalMessage?.submittedTo : undefined;

if (!submittedTo) {
const submittedToLogin = 'to' in originalMessage ? (originalMessage?.to ?? '') : '';
submittedTo = getAccountIDsByLogins([submittedToLogin])?.at(0);
}

if (!submittedTo) {
const managerID = 'managerOnVacation' in originalMessage && originalMessage?.managerOnVacation ? originalMessage?.managerOnVacation : report.managerID;
const approverAccountID =
policy?.employeeList?.[getLoginByAccountID(report?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID) ?? '']?.submitsTo ?? getAccountIDsByLogins([getDefaultApprover(policy)])?.at(0);
return managerID === approverAccountID;
}

return report.managerID === submittedTo;
}

/**
* Updates optimistic transaction violations to OnyxData for the given policy and categories onyx update.
*
Expand Down Expand Up @@ -2747,17 +2684,21 @@ function getChildReportNotificationPreference(reportAction: OnyxInputOrEntry<Rep
return isActionCreator(reportAction) ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN;
}

function canAddOrDeleteTransactions(moneyRequestReport: OnyxEntry<Report>, policy?: OnyxEntry<Policy>, isReportArchived = false): boolean {
function canAddOrDeleteTransactions(moneyRequestReport: OnyxEntry<Report>, isReportArchived = false): boolean {
if (!isMoneyRequestReport(moneyRequestReport) || isReportArchived) {
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);

// Adding or deleting transactions is not allowed on a closed report
if (moneyRequestReport?.statusNum === CONST.REPORT.STATUS_NUM.CLOSED && !isOpenReport(moneyRequestReport)) {
return false;
}

if (isInstantSubmitEnabled(policy) && isProcessingReport(moneyRequestReport)) {
return isAwaitingFirstLevelApprovalNew(moneyRequestReport, Object.values(allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`] ?? {}), policy);
return isAwaitingFirstLevelApproval(moneyRequestReport);
}

if (isReportApproved({report: moneyRequestReport}) || isClosedReport(moneyRequestReport) || isSettled(moneyRequestReport?.reportID)) {
Expand Down Expand Up @@ -2786,7 +2727,17 @@ function canAddTransaction(moneyRequestReport: OnyxEntry<Report>, isReportArchiv
return false;
}

return canAddOrDeleteTransactions(moneyRequestReport, policy, isReportArchived);
return canAddOrDeleteTransactions(moneyRequestReport, isReportArchived);
}

/**
* Checks whether the supplied report supports deleting more transactions from it.
* Return true if:
* - report is a non-settled IOU
* - report is a non-approved IOU
*/
function canDeleteTransaction(moneyRequestReport: OnyxEntry<Report>, isReportArchived = false): boolean {
return canAddOrDeleteTransactions(moneyRequestReport, isReportArchived);
}

/**
Expand Down Expand Up @@ -2816,7 +2767,6 @@ function isMoneyRequestReportEligibleForMerge(reportID: string, isAdmin: boolean
}

if (isSubmitter) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
return isOpenReport(report) || (isIOUReport(report) && isProcessingReport(report)) || isAwaitingFirstLevelApproval(report);
}

Expand Down Expand Up @@ -3003,7 +2953,7 @@ function canDeleteReportAction(

if (isActionOwner) {
if (!isEmptyObject(report) && (isMoneyRequestReport(report) || isInvoiceReport(report))) {
return canAddOrDeleteTransactions(report, policy ?? undefined) && canCardTransactionBeDeleted;
return canDeleteTransaction(report) && canCardTransactionBeDeleted;
}
if (isTrackExpenseAction(reportAction)) {
return canCardTransactionBeDeleted;
Expand Down Expand Up @@ -4715,7 +4665,6 @@ function canEditReportPolicy(report: OnyxEntry<Report>, reportPolicy: OnyxEntry<
}

if (isSubmitted) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
return (isSubmitter && isAwaitingFirstLevelApproval(report)) || isManager || isAdmin;
}

Expand Down Expand Up @@ -13038,7 +12987,7 @@ export {
canAccessReport,
isReportNotFound,
canAddTransaction,
canAddOrDeleteTransactions,
canDeleteTransaction,
canBeAutoReimbursed,
canCreateRequest,
canCreateTaskInReport,
Expand Down Expand Up @@ -13254,9 +13203,7 @@ export {
isOpenReport,
requiresManualSubmission,
isReportIDApproved,
// eslint-disable-next-line @typescript-eslint/no-deprecated
isAwaitingFirstLevelApproval,
isAwaitingFirstLevelApprovalNew,
isPublicAnnounceRoom,
isPublicRoom,
isReportApproved,
Expand Down
1 change: 1 addition & 0 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2659,6 +2659,7 @@ function getTransactionFromTransactionListItem(item: TransactionListItemType): O
isTaxAmountColumnWide,
violations,
hash,
canDelete,
accountID,
policyID,
...transaction
Expand Down
4 changes: 2 additions & 2 deletions src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
import {getOneTransactionThreadReportID, getOriginalMessage, getTrackExpenseActionableWhisper, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils';
import {getReportName as getReportNameFromReportNameUtils} from '@libs/ReportNameUtils';
import {
canAddOrDeleteTransactions,
canDeleteCardTransactionByLiabilityType,
canDeleteTransaction,
canEditReportDescription as canEditReportDescriptionUtil,
canJoinChat,
canLeaveChat,
Expand Down Expand Up @@ -291,7 +291,7 @@
!isClosedReport(report) &&
isTaskModifiable &&
isTaskActionable;
const canDeleteRequest = isActionOwner && (canAddOrDeleteTransactions(moneyRequestReport, policy, isMoneyRequestReportArchived) || isSelfDMTrackExpenseReport) && !isDeletedParentAction;
const canDeleteRequest = isActionOwner && (canDeleteTransaction(moneyRequestReport, isMoneyRequestReportArchived) || isSelfDMTrackExpenseReport) && !isDeletedParentAction;
const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined;
const [iouTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${iouTransactionID}`, {canBeMissing: true});
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(iouTransactionID ? [iouTransactionID] : []);
Expand Down Expand Up @@ -546,7 +546,7 @@
}

return items;
}, [

Check warning on line 549 in src/pages/ReportDetailsPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useMemo has missing dependencies: 'expensifyIcons.Bug', 'expensifyIcons.Building', 'expensifyIcons.Checkmark', 'expensifyIcons.Exit', 'expensifyIcons.Folder', 'expensifyIcons.Gear', 'expensifyIcons.Pencil', 'expensifyIcons.Send', 'expensifyIcons.UserPlus', and 'expensifyIcons.Users'. Either include them or remove the dependency array
isSelfDM,
isArchivedRoom,
isGroupChat,
Expand Down Expand Up @@ -651,7 +651,7 @@
style={[styles.w100, styles.mb3]}
/>
);
}, [

Check warning on line 654 in src/pages/ReportDetailsPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useMemo has a missing dependency: 'expensifyIcons.Camera'. Either include it or remove the dependency array
isChatRoom,
isThread,
isGroupChat,
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ type SearchTransaction = {
/** The transaction amount */
amount: number;

/** If the transaction can be deleted */
canDelete: boolean;

/** The edited transaction amount */
modifiedAmount: number;

Expand Down
1 change: 1 addition & 0 deletions tests/unit/MoneyRequestReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const transactionItemBaseMock: TransactionListItemType = {
policy: policyBaseMock,
reportAction: reportActionBaseMock,
holdReportAction: undefined,
canDelete: true,
cardID: undefined,
cardName: undefined,
category: '',
Expand Down
13 changes: 6 additions & 7 deletions tests/unit/ReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ import {
buildOptimisticReportPreview,
buildParticipantsFromAccountIDs,
buildTransactionThread,
canAddOrDeleteTransactions,
canAddTransaction,
canCreateRequest,
canDeleteMoneyRequestReport,
canDeleteReportAction,
canDeleteTransaction,
canEditMoneyRequest,
canEditReportDescription,
canEditRoomVisibility,
Expand Down Expand Up @@ -2950,7 +2950,6 @@ describe('ReportUtils', () => {
submitsTo: currentUserEmail,
},
},
approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC,
};
Promise.all([
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${paidPolicy.id}`, paidPolicy),
Expand Down Expand Up @@ -5985,7 +5984,7 @@ describe('ReportUtils', () => {
});
});

describe('canAddOrDeleteTransactions', () => {
describe('canDeleteTransaction', () => {
it('should return true for a non-archived report', async () => {
// Given a non-archived expense report
const report: Report = {
Expand All @@ -5997,7 +5996,7 @@ describe('ReportUtils', () => {
// When it's checked if the transactions can be deleted
// Simulate how components determined if a report is archived by using this hook
const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.reportID));
const result = canAddOrDeleteTransactions(report, policy, isReportArchived.current);
const result = canDeleteTransaction(report, isReportArchived.current);

// Then the result is true
expect(result).toBe(true);
Expand All @@ -6014,7 +6013,7 @@ describe('ReportUtils', () => {

// When it's checked if the transactions can be deleted
const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.reportID));
const result = canAddOrDeleteTransactions(report, policy, isReportArchived.current);
const result = canDeleteTransaction(report, isReportArchived.current);

// Then the result is false
expect(result).toBe(false);
Expand Down Expand Up @@ -6063,7 +6062,7 @@ describe('ReportUtils', () => {

await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${openReport.reportID}`, openReport);

expect(canAddOrDeleteTransactions(openReport, policy, false)).toBe(true);
expect(canDeleteTransaction(openReport, false)).toBe(true);
});

it('should return false for closed report when workflow is disabled', async () => {
Expand All @@ -6076,7 +6075,7 @@ describe('ReportUtils', () => {

await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${closedReport.reportID}`, closedReport);

expect(canAddOrDeleteTransactions(closedReport, policy, false)).toBe(false);
expect(canDeleteTransaction(closedReport, false)).toBe(false);
});
});
});
Expand Down
Loading
Loading