Skip to content
Merged
2 changes: 1 addition & 1 deletion src/hooks/useOptimisticNextStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function useOptimisticNextStep(reportID: string | undefined) {
}
return acc;
}, {});
const {errors} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(moneyRequestReport, reportActionsObject);
const {errors} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(moneyRequestReport, reportActionsObject, reportTransactions);

if (errors?.dewSubmitFailed) {
optimisticNextStep = buildOptimisticNextStepForDynamicExternalWorkflowSubmitError(theme.danger);
Expand Down
47 changes: 33 additions & 14 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@
};

let conciergeReportIDOnyxConnect: OnyxEntry<string>;
Onyx.connect({

Check warning on line 1048 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => {
conciergeReportIDOnyxConnect = value;
Expand All @@ -1053,7 +1053,7 @@
});

const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon';
Onyx.connect({

Check warning on line 1056 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -1071,7 +1071,7 @@
let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({

Check warning on line 1074 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
if (deprecatedCurrentUserAccountID) {
Expand All @@ -1083,7 +1083,7 @@
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 1086 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
Expand All @@ -1091,7 +1091,7 @@

let allPolicies: OnyxCollection<Policy>;
let policiesArray: Policy[] = [];
Onyx.connect({

Check warning on line 1094 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1101,7 +1101,7 @@
});

let allPolicyDrafts: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 1104 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_DRAFTS,
waitForCollectionCallback: true,
callback: (value) => (allPolicyDrafts = value),
Expand All @@ -1109,7 +1109,7 @@

let deprecatedAllReports: OnyxCollection<Report>;
let deprecatedReportsByPolicyID: ReportByPolicyMap;
Onyx.connect({

Check warning on line 1112 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -1145,14 +1145,14 @@
});

let betaConfiguration: OnyxEntry<BetaConfiguration> = {};
Onyx.connect({

Check warning on line 1148 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.BETA_CONFIGURATION,
callback: (value) => (betaConfiguration = value ?? {}),
});

let deprecatedAllTransactions: OnyxCollection<Transaction> = {};
let deprecatedReportsTransactions: Record<string, Transaction[]> = {};
Onyx.connect({

Check warning on line 1155 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1178,7 +1178,7 @@
});

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 1181 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -5258,13 +5258,18 @@
/**
* Get report action which is missing smartscan fields
*/
function getReportActionWithMissingSmartscanFields(iouReport: OnyxEntry<Report>, iouReportID: string | undefined): ReportAction | undefined {
function getReportActionWithMissingSmartscanFields(
iouReport: OnyxEntry<Report>,
iouReportID: string | undefined,
iouReportTransactions: OnyxCollection<Transaction>,
): ReportAction | undefined {
const reportActions = Object.values(getAllReportActions(iouReportID));
return reportActions.find((action) => {
if (!isMoneyRequestAction(action)) {
return false;
}
const transaction = getLinkedTransaction(action);
const transactionID = getOriginalMessage(action)?.IOUTransactionID;
const transaction = iouReportTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
if (isEmptyObject(transaction)) {
return false;
}
Expand All @@ -5278,8 +5283,8 @@
/**
* Check if iouReportID has required missing fields
*/
function shouldShowRBRForMissingSmartscanFields(iouReport: OnyxEntry<Report>, iouReportID: string | undefined): boolean {
return !!getReportActionWithMissingSmartscanFields(iouReport, iouReportID);
function shouldShowRBRForMissingSmartscanFields(iouReport: OnyxEntry<Report>, iouReportID: string | undefined, iouReportTransactions: OnyxCollection<Transaction>): boolean {
return !!getReportActionWithMissingSmartscanFields(iouReport, iouReportID, iouReportTransactions);
}

/**
Expand Down Expand Up @@ -9356,6 +9361,7 @@
function getAllReportActionsErrorsAndReportActionThatRequiresAttention(
report: OnyxEntry<Report>,
reportActions: OnyxEntry<ReportActions>,
allTransactions: OnyxCollection<Transaction>,
isReportArchived = false,
reports?: OnyxCollection<Report>,
): ReportErrorsAndReportActionThatRequiresAttention {
Expand All @@ -9373,9 +9379,9 @@
}
}

if (!isReportArchived && hasSmartscanError(reportActionsArray, report, reports)) {
if (!isReportArchived && hasSmartscanError(reportActionsArray, report, allTransactions, reports)) {
reportActionErrors.smartscan = getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericSmartscanFailureMessage');
reportAction = getReportActionWithSmartscanError(reportActionsArray, report, reports);
reportAction = getReportActionWithSmartscanError(reportActionsArray, report, allTransactions, reports);
}

if (!isReportArchived && isReportOwner(report) && report?.statusNum === CONST.REPORT.STATUS_NUM.OPEN) {
Expand All @@ -9395,9 +9401,15 @@
/**
* Get an object of error messages keyed by microtime by combining all error objects related to the report.
*/
function getAllReportErrors(report: OnyxEntry<Report>, reportActions: OnyxEntry<ReportActions>, isReportArchived = false, reports?: OnyxCollection<Report>): Errors {
function getAllReportErrors(
report: OnyxEntry<Report>,
reportActions: OnyxEntry<ReportActions>,
allTransactions: OnyxCollection<Transaction>,
isReportArchived = false,
reports?: OnyxCollection<Report>,
): Errors {
const reportErrorFields = report?.errorFields ?? {};
const {errors: reportActionErrors} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, isReportArchived, reports);
const {errors: reportActionErrors} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, allTransactions, isReportArchived, reports);

// All error objects related to the report. Each object in the sources contains error messages keyed by microtime
const errorSources = {
Expand Down Expand Up @@ -10754,7 +10766,12 @@
);
}

function getReportActionWithSmartscanError(reportActions: ReportAction[], report: OnyxEntry<Report>, reports?: OnyxCollection<Report>): ReportAction | undefined {
function getReportActionWithSmartscanError(
reportActions: ReportAction[],
report: OnyxEntry<Report>,
allTransactions: OnyxCollection<Transaction>,
reports?: OnyxCollection<Report>,
): ReportAction | undefined {
return reportActions.find((action) => {
const isReportPreview = isReportPreviewAction(action);
const isSplitOrTrackAction = isSplitBillReportAction(action) || isTrackExpenseAction(action);
Expand All @@ -10763,13 +10780,13 @@
}
const IOUReportID = getIOUReportIDFromReportActionPreview(action);
const iouReport = IOUReportID ? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${IOUReportID}`] : undefined;
const isReportPreviewError = isReportPreview && shouldShowRBRForMissingSmartscanFields(iouReport ?? report, IOUReportID) && !isSettled(IOUReportID);
const isReportPreviewError = isReportPreview && shouldShowRBRForMissingSmartscanFields(iouReport ?? report, IOUReportID, allTransactions) && !isSettled(IOUReportID);
if (isReportPreviewError) {
return true;
}

const transactionID = isSplitOrTrackAction ? getOriginalMessage(action)?.IOUTransactionID : undefined;
const transaction = deprecatedAllTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {};
const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? {};
const isTransactionThreadError = isSplitOrTrackAction && hasMissingSmartscanFieldsTransactionUtils(transaction as Transaction, report);

return isTransactionThreadError;
Expand All @@ -10779,8 +10796,8 @@
/**
* Checks if report action has error when smart scanning
*/
function hasSmartscanError(reportActions: ReportAction[], report: OnyxEntry<Report>, reports?: OnyxCollection<Report>): boolean {
return !!getReportActionWithSmartscanError(reportActions, report, reports);
function hasSmartscanError(reportActions: ReportAction[], report: OnyxEntry<Report>, allTransactions: OnyxCollection<Transaction>, reports?: OnyxCollection<Report>): boolean {
return !!getReportActionWithSmartscanError(reportActions, report, allTransactions, reports);
}

function shouldAutoFocusOnKeyPress(event: KeyboardEvent): boolean {
Expand Down Expand Up @@ -12852,13 +12869,15 @@
reportActions,
transactionViolations,
isReportArchived = false,
allTransactions,
reports,
}: {
report: OnyxEntry<Report>;
chatReport: OnyxEntry<Report>;
reportActions?: OnyxCollection<ReportActions>;
transactionViolations: OnyxCollection<TransactionViolation[]>;
isReportArchived: boolean;
allTransactions: OnyxCollection<Transaction>;
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
actionTargetReportActionID?: string;
reports?: OnyxCollection<Report>;
Expand All @@ -12867,7 +12886,7 @@
const parentReportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`];
const hasViolationsToDisplayInLHN = !!getViolatingReportIDForRBRInLHN(report, transactionViolations);
const hasAnyTypeOfViolations = hasViolationsToDisplayInLHN;
const reportErrors = getAllReportErrors(report, reportActionsList, isReportArchived, reports);
const reportErrors = getAllReportErrors(report, reportActionsList, allTransactions, isReportArchived, reports);
const hasErrors = Object.entries(reportErrors ?? {}).length > 0;
const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActionsList);
const parentReportAction = report?.parentReportActionID ? parentReportActionsList?.[report.parentReportActionID] : undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ function getReasonAndReportActionThatHasRedBrickRoad(
};
}

const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, isReportArchived, reports);
const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, transactions, isReportArchived, reports);
const errors = reportErrors;
const hasErrors = Object.keys(errors).length !== 0;

Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/OnyxDerived/configs/reportAttributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export default createOnyxDerivedValueConfig({
reportActions,
transactionViolations,
isReportArchived,
allTransactions: transactions,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How about this place? Should we filter the transactions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No need, transactions is an object, so we don’t need to filter it.

reports,
});

Expand Down
62 changes: 49 additions & 13 deletions tests/unit/DebugUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {renderHook} from '@testing-library/react-native';
import type {OnyxCollection} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import useReportIsArchived from '@hooks/useReportIsArchived';
import DateUtils from '@libs/DateUtils';
Expand Down Expand Up @@ -1171,6 +1172,10 @@ describe('DebugUtils', () => {
});
});
describe('getReasonAndReportActionForRBRInLHNRow', () => {
const sharedTransaction = createRandomTransaction(77777);
const sharedAllTransactions: OnyxCollection<Transaction> = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}${sharedTransaction.transactionID}`]: sharedTransaction,
};
beforeAll(() => {
Onyx.init({
keys: ONYXKEYS,
Expand All @@ -1188,7 +1193,7 @@ describe('DebugUtils', () => {
},
chatReportR14932,
undefined,
{},
sharedAllTransactions,
undefined,
false,
{},
Expand Down Expand Up @@ -1238,13 +1243,20 @@ describe('DebugUtils', () => {
modifiedCreated: '',
},
});
const transactionForTest: OnyxCollection<Transaction> = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}1`]: {
amount: 100,
created: '',
modifiedCreated: '',
} as Transaction,
};
const {reportAction} =
DebugUtils.getReasonAndReportActionForRBRInLHNRow(
// eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
MOCK_REPORTS[`${ONYXKEYS.COLLECTION.REPORT}1`] as Report,
chatReportR14932,
undefined,
{},
transactionForTest,
undefined,
false,
{},
Expand Down Expand Up @@ -1306,9 +1318,17 @@ describe('DebugUtils', () => {
accountID: 12345,
},
});
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS);
const mockTransactions: OnyxCollection<Transaction> = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}1`]: {
amount: 100,
created: '',
modifiedCreated: '',
} as Transaction,
};
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, mockTransactions);
const {reportAction} =
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_CHAT_REPORT_ACTIONS, {}, undefined, false, reportErrors) ?? {};
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_CHAT_REPORT_ACTIONS, mockTransactions, undefined, false, reportErrors) ??
{};
expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']);
});
it('returns correct report action which is a split bill and has an error', async () => {
Expand Down Expand Up @@ -1371,9 +1391,16 @@ describe('DebugUtils', () => {
accountID: 12345,
},
});
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS);
const mockTransactions: OnyxCollection<Transaction> = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}1`]: {
amount: 100,
created: '',
modifiedCreated: '',
} as Transaction,
};
const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, mockTransactions);
const {reportAction} =
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_REPORT_ACTIONS, {}, undefined, false, reportErrors) ?? {};
DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, chatReportR14932, MOCK_REPORT_ACTIONS, mockTransactions, undefined, false, reportErrors) ?? {};
expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']);
});
});
Expand Down Expand Up @@ -1419,15 +1446,15 @@ describe('DebugUtils', () => {
],
},
};
const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS);
const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS, sharedAllTransactions);
const {reportAction} =
DebugUtils.getReasonAndReportActionForRBRInLHNRow(
{
reportID: '1',
},
chatReportR14932,
MOCK_REPORT_ACTIONS,
{},
sharedAllTransactions,
undefined,
false,
reportErrors,
Expand Down Expand Up @@ -1457,8 +1484,9 @@ describe('DebugUtils', () => {
},
};

const reportErrors = getAllReportErrors(mockedReport, mockedReportActions);
const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, chatReportR14932, mockedReportActions, {}, undefined, false, reportErrors) ?? {};
const reportErrors = getAllReportErrors(mockedReport, mockedReportActions, sharedAllTransactions);
const {reason} =
DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, chatReportR14932, mockedReportActions, sharedAllTransactions, undefined, false, reportErrors) ?? {};
expect(reason).toBe('debug.reasonRBR.hasErrors');
});
it('returns correct reason when there are violations', () => {
Expand All @@ -1469,7 +1497,7 @@ describe('DebugUtils', () => {
},
chatReportR14932,
undefined,
{},
sharedAllTransactions,
undefined,
true,
{},
Expand All @@ -1484,7 +1512,7 @@ describe('DebugUtils', () => {
},
chatReportR14932,
undefined,
{},
sharedAllTransactions,
undefined,
true,
{},
Expand Down Expand Up @@ -1536,7 +1564,15 @@ describe('DebugUtils', () => {
},
],
});
const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, chatReportR14932, {}, {}, transactionViolations, false, {}) ?? {};
const violationTransactions: OnyxCollection<Transaction> = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}1`]: {
transactionID: '1',
amount: 10,
modifiedAmount: 10,
reportID: '1',
} as Transaction,
};
const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, chatReportR14932, {}, violationTransactions, transactionViolations, false, {}) ?? {};
expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations');
});
});
Expand Down
Loading
Loading