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
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ function ExpenseReportListItemInner<TItem extends ListItem>({
delegateEmail,
delegateAccountID,
isTrackIntentUser,
// Pass the row-scoped, live violations (keyed by this report's snapshot transactions) instead of the
// whole TRANSACTION_VIOLATIONS collection, so the Approve action reads live data without re-rendering
// every row on unrelated violation changes.
allViolations: liveViolationsForSnapshotTransactions,
conciergeChat,
});
}, [
Expand Down Expand Up @@ -346,6 +350,7 @@ function ExpenseReportListItemInner<TItem extends ListItem>({
delegateEmail,
delegateAccountID,
isTrackIntentUser,
liveViolationsForSnapshotTransactions,
conciergeChat,
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type {ColorValue} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';

import {isTrackIntentUserSelector} from '@selectors/Onboarding';
import {transactionViolationsByIDsSelector} from '@selectors/TransactionViolations';
import React, {useMemo} from 'react';
import {View} from 'react-native';
// Use the original useOnyx hook to get the real-time personal details list data from Onyx and not from the snapshot
Expand Down Expand Up @@ -282,6 +283,9 @@ function ReportListItemHeaderInner<TItem extends ListItem>({
);
const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector});

const reportTransactionIDs = (reportItem.transactions ?? []).map((transaction) => transaction.transactionID);
const [allViolations] = originalUseOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {selector: transactionViolationsByIDsSelector(reportTransactionIDs)});

const {currentUserAccountID, currentUserLogin, introSelected, betas, isSelfTourViewed, activePolicy, chatReportPolicy, amountOwed, delegateEmail, delegateAccountID, conciergeChat} =
useReportPaymentContext({
chatReportPolicyID: chatReport?.policyID,
Expand Down Expand Up @@ -333,6 +337,7 @@ function ReportListItemHeaderInner<TItem extends ListItem>({
delegateEmail,
delegateAccountID,
isTrackIntentUser,
allViolations,
conciergeChat,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {personalDetailsLoginSelector} from '@src/selectors/PersonalDetails';
import {isActionLoadingSelector} from '@src/selectors/ReportMetaData';
import type {Policy, Report, ReportAction, ReportActions} from '@src/types/onyx';
import type {Policy, Report, ReportAction, ReportActions, TransactionViolations} from '@src/types/onyx';
import type {TransactionViolation} from '@src/types/onyx/TransactionViolation';

import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';

import {isTrackIntentUserSelector} from '@selectors/Onboarding';
// NOTE: The narrow-layout rendering of this component has a static twin in
Expand Down Expand Up @@ -132,7 +132,9 @@ function TransactionListItemInner<TItem extends ListItem>({
const [transactionThreadReport] = originalUseOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionItem?.reportAction?.childReportID}`);
const [submitterLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: personalDetailsLoginSelector(transactionItem?.report?.ownerAccountID)});
const [transaction] = originalUseOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionItem.transactionID)}`);
const [transactionViolationsForRow] = originalUseOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getNonEmptyStringOnyxID(transactionItem.transactionID)}`);
const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getNonEmptyStringOnyxID(transactionItem.transactionID)}` as const;
const [transactionViolationsForRow] = originalUseOnyx(transactionViolationsKey);
const allViolations: OnyxCollection<TransactionViolations> = {[transactionViolationsKey]: transactionViolationsForRow};
const parentReportActionID = transactionItem?.reportAction?.reportActionID;
const [parentReportAction] = originalUseOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(transactionItem.reportID)}`, {
selector: (reportActions: OnyxEntry<ReportActions>): OnyxEntry<ReportAction> => reportActions?.[`${parentReportActionID}`],
Expand Down Expand Up @@ -240,6 +242,7 @@ function TransactionListItemInner<TItem extends ListItem>({
delegateEmail,
delegateAccountID,
isTrackIntentUser,
allViolations,
conciergeChat,
});
};
Expand Down
17 changes: 13 additions & 4 deletions src/libs/actions/IOU/UpdateMoneyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ type UpdateMoneyRequestVendorParams = {
parentReport?: OnyxEntry<OnyxTypes.Report>;
policy?: OnyxEntry<OnyxTypes.Policy>;
delegateAccountID: number | undefined;
transactionViolations: OnyxEntry<OnyxTypes.TransactionViolations>;
};

/**
Expand All @@ -631,7 +632,17 @@ type UpdateMoneyRequestVendorParams = {
*
* Passing `vendorID=''` clears the vendor from the transaction.
*/
function updateMoneyRequestVendor({transactionID, vendorID, vendorName, transaction, transactionThreadReport, parentReport, policy, delegateAccountID}: UpdateMoneyRequestVendorParams) {
function updateMoneyRequestVendor({
transactionID,
vendorID,
vendorName,
transaction,
transactionThreadReport,
parentReport,
policy,
delegateAccountID,
transactionViolations,
}: UpdateMoneyRequestVendorParams) {
// Fall back to the cached Onyx transaction when the caller doesn't pass one so failureData can
// restore the actual previous vendor on API failure instead of clearing it.
const resolvedTransaction = transaction ?? getAllTransactions()?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
Expand Down Expand Up @@ -759,9 +770,7 @@ function updateMoneyRequestVendor({transactionID, vendorID, vendorName, transact
// resolves it (no vendor → no inactive-vendor). Without this, the stale violation persists
// in Onyx until some unrelated recalculation fires, keeping the expense incorrectly flagged.
const violationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as const;
// TODO: https://github.com/Expensify/App/issues/66512
// eslint-disable-next-line @typescript-eslint/no-deprecated
const currentViolations = getAllTransactionViolations()[violationsKey] ?? [];
const currentViolations = transactionViolations ?? [];
if (currentViolations.some((violation) => violation.name === CONST.VIOLATIONS.INACTIVE_VENDOR)) {
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
Expand Down
10 changes: 7 additions & 3 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import type {
ReportActions,
SaveSearch,
Transaction,
TransactionViolations,
} from '@src/types/onyx';
import type {PaymentInformation} from '@src/types/onyx/LastPaymentMethod';
import type {ConnectionName} from '@src/types/onyx/Policy';
Expand All @@ -106,7 +107,6 @@ import Onyx from 'react-native-onyx';
import type {AdditionalPayOnyxData} from './IOU/PayMoneyRequest';
import type {RejectMoneyRequestData} from './IOU/RejectMoneyRequest';

import {getAllTransactionViolations} from './IOU';
import {payMoneyRequest} from './IOU/PayMoneyRequest';
import {prepareRejectMoneyRequestData, rejectMoneyRequest} from './IOU/RejectMoneyRequest';
import {approveMoneyRequest} from './IOU/ReportWorkflow';
Expand Down Expand Up @@ -240,6 +240,7 @@ type HandleActionButtonPressParams = {
delegateEmail?: string;
delegateAccountID: number | undefined;
isTrackIntentUser: boolean | undefined;
allViolations: OnyxCollection<TransactionViolations>;
conciergeChat: OnyxEntry<Report>;
getCurrencyDecimals: CurrencyListActionsContextType['getCurrencyDecimals'];
};
Expand Down Expand Up @@ -279,6 +280,7 @@ function handleActionButtonPress({
delegateEmail,
delegateAccountID,
isTrackIntentUser,
allViolations,
conciergeChat,
getCurrencyDecimals,
}: HandleActionButtonPressParams) {
Expand Down Expand Up @@ -366,6 +368,7 @@ function handleActionButtonPress({
delegateAccountID,
isTrackIntentUser,
ownerLogin: submitterLogin,
allViolations,
getCurrencyDecimals,
});
return;
Expand Down Expand Up @@ -665,6 +668,7 @@ type GetApproveActionCallbackParams = {
delegateAccountID: number | undefined;
isTrackIntentUser: boolean | undefined;
ownerLogin: string | undefined;
allViolations: OnyxCollection<TransactionViolations>;
getCurrencyDecimals: CurrencyListActionsContextType['getCurrencyDecimals'];
};

Expand All @@ -685,15 +689,15 @@ function getApproveActionCallback({
delegateAccountID,
isTrackIntentUser,
ownerLogin,
allViolations,
getCurrencyDecimals,
}: GetApproveActionCallbackParams) {
if (!item.reportID) {
return;
}

const reportPolicy = policy ?? snapshotPolicy;
// eslint-disable-next-line @typescript-eslint/no-deprecated -- using deprecated getAllTransactionViolations until #66512 migrates this call
const hasViolations = hasViolationsReportUtils(item.reportID, getAllTransactionViolations(), currentUserAccountID, currentUserLogin ?? '');
const hasViolations = hasViolationsReportUtils(item.reportID, allViolations, currentUserAccountID, currentUserLogin ?? '');
const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, betas);

approveMoneyRequest({
Expand Down
2 changes: 2 additions & 0 deletions src/pages/iou/request/step/IOURequestStepVendor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function IOURequestStepVendor({
isPerDiemRequest: isPerDiemRequest(transaction),
});
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID)}`);
const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getNonEmptyStringOnyxID(transactionID)}`);
const delegateAccountID = useDelegateAccountID();

const isFeatureAvailable = hasVendorFeature(policy, isBetaEnabled(CONST.BETAS.VENDOR_MATCHING));
Expand Down Expand Up @@ -120,6 +121,7 @@ function IOURequestStepVendor({
parentReport,
policy,
delegateAccountID,
transactionViolations,
});
}
navigateBack();
Expand Down
Loading
Loading