Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LHN - RBR is missing from the report when it fails to export to QBO #54981

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
3 changes: 2 additions & 1 deletion src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@
if ((Object.values(CONST.REPORT.UNSUPPORTED_TYPE) as string[]).includes(report?.type ?? '')) {
return;
}
const parentReportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '-1', report?.parentReportActionID ?? '-1');

Check failure on line 126 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Check failure on line 126 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

const doesReportHaveViolations = ReportUtils.shouldDisplayViolationsRBRInLHN(report, transactionViolations);
const isHidden = ReportUtils.isHiddenForCurrentUser(report);
const isFocused = report.reportID === currentReportId;
const hasErrorsOtherThanFailedReceipt = ReportUtils.hasReportErrorsOtherThanFailedReceipt(report, doesReportHaveViolations, transactionViolations);
const isReportInAccessible = report?.errorFields?.notFound;
if (ReportUtils.isOneTransactionThread(report.reportID, report.parentReportID ?? '0', parentReportAction)) {

Check failure on line 132 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

return;
}
if (hasErrorsOtherThanFailedReceipt && !isReportInAccessible) {
Expand All @@ -155,7 +155,7 @@
if (
ReportUtils.shouldReportBeInOptionList({
report,
currentReportId: currentReportId ?? '-1',

Check failure on line 158 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

isInFocusMode,
betas,
policies: policies as OnyxCollection<Policy>,
Expand Down Expand Up @@ -200,13 +200,13 @@
};

const isPinned = report?.isPinned ?? false;
const reportAction = ReportActionsUtils.getReportAction(report?.parentReportID ?? '-1', report?.parentReportActionID ?? '-1');

Check failure on line 203 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Check failure on line 203 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

const reportNameValuePairs = ReportUtils.getReportNameValuePairs(report?.reportID);
if (isPinned || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) {
pinnedAndGBRReports.push(miniReport);
} else if (report?.hasErrorsOtherThanFailedReceipt) {
errorReports.push(miniReport);
} else if (hasValidDraftComment(report?.reportID ?? '-1')) {

Check failure on line 209 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

draftReports.push(miniReport);
} else if (ReportUtils.isArchivedRoom(report, reportNameValuePairs)) {
archivedReports.push(miniReport);
Expand Down Expand Up @@ -239,7 +239,7 @@
// Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID.
// The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar.

const LHNReports = [...pinnedAndGBRReports, ...errorReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report?.reportID ?? '-1');

Check failure on line 242 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check


Performance.markEnd(CONST.TIMING.GET_ORDERED_REPORT_IDS);
return LHNReports;
Expand All @@ -256,7 +256,8 @@
hasViolations: boolean,
transactionViolations?: OnyxCollection<TransactionViolation[]>,
): ReasonAndReportActionThatHasRedBrickRoad | null {
const {errors, reportAction} = ReportUtils.getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions);
const {reportAction} = ReportUtils.getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions);
const errors = ReportUtils.getAllReportErrors(report, reportActions);
const hasErrors = Object.keys(errors).length !== 0;

if (ReportUtils.shouldDisplayViolationsRBRInLHN(report, transactionViolations)) {
Expand Down Expand Up @@ -385,7 +386,7 @@
result.iouReportID = report.iouReportID;
result.keyForList = String(report.reportID);
result.hasOutstandingChildRequest = report.hasOutstandingChildRequest;
result.parentReportID = report.parentReportID ?? '-1';

Check failure on line 389 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

result.isWaitingOnBankAccount = report.isWaitingOnBankAccount;
result.notificationPreference = ReportUtils.getReportNotificationPreference(report);
result.isAllowedToComment = ReportUtils.canUserPerformWriteAction(report);
Expand Down Expand Up @@ -518,7 +519,7 @@
}

if (!hasMultipleParticipants) {
result.accountID = personalDetail?.accountID ?? -1;

Check failure on line 522 in src/libs/SidebarUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

result.login = personalDetail?.login ?? '';
result.phoneNumber = personalDetail?.phoneNumber ?? '';
}
Expand Down
7 changes: 6 additions & 1 deletion src/types/onyx/TransactionViolation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type ONYXKEYS from '@src/ONYXKEYS';
import type CollectionDataSet from '@src/types/utils/CollectionDataSet';

/**
* Names of violations.
Expand Down Expand Up @@ -109,5 +111,8 @@ type TransactionViolation = {
/** Collection of transaction violations */
type TransactionViolations = TransactionViolation[];

export type {TransactionViolation, ViolationName, ViolationType, ViolationDataType, TransactionViolationData};
/** Collection of mock transaction violations, indexed by transactionViolations_${transactionID} */
type TransactionViolationsCollectionDataSet = CollectionDataSet<typeof ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS>;

export type {TransactionViolation, ViolationName, ViolationType, ViolationDataType, TransactionViolationData, TransactionViolationsCollectionDataSet};
export default TransactionViolations;
337 changes: 337 additions & 0 deletions tests/unit/SidebarUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
/* eslint-disable @typescript-eslint/naming-convention */
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import SidebarUtils from '@libs/SidebarUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report, ReportAction, ReportActions, TransactionViolation, TransactionViolations} from '@src/types/onyx';
import type {ReportCollectionDataSet} from '@src/types/onyx/Report';
import type {TransactionViolationsCollectionDataSet} from '@src/types/onyx/TransactionViolation';

describe('SidebarUtils', () => {
beforeAll(() =>
Onyx.init({
keys: ONYXKEYS,
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
}),
);

describe('getReasonAndReportActionThatHasRedBrickRoad', () => {
it('returns correct reason when report has transaction thread violations', async () => {
const MOCK_REPORT: Report = {
reportID: '1',
ownerAccountID: 12345,
chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
policyID: '6',
};

const MOCK_REPORTS: ReportCollectionDataSet = {
[`${ONYXKEYS.COLLECTION.REPORT}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT,
};

const MOCK_REPORT_ACTIONS: ReportActions = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'1': {
reportActionID: '1',
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
actorAccountID: 12345,
created: '2024-08-08 18:20:44.171',
},
};

const MOCK_TRANSACTION = {
transactionID: '1',
amount: 10,
modifiedAmount: 10,
reportID: MOCK_REPORT.reportID,
};

const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = {
[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [
{
type: CONST.VIOLATION_TYPES.VIOLATION,
name: CONST.VIOLATIONS.MISSING_CATEGORY,
},
],
};

await Onyx.multiSet({
...MOCK_REPORTS,
...MOCK_TRANSACTION_VIOLATIONS,
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT_ACTIONS,
[ONYXKEYS.SESSION]: {
accountID: 12345,
},
[`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION,
});

const {reason} =
SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection<TransactionViolations>) ?? {};

expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS);
});

it('returns correct reason when report has errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
errorFields: {
someField: {
error: 'Some error occurred',
},
},
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {};

expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS);
});

it('returns correct reason when report has violations', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS) ?? {};

expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS);
});

it('returns correct reason when report has report action errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'1': {
reportActionID: '1',
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
actorAccountID: 12345,
created: '2024-08-08 18:20:44.171',
message: [
{
type: '',
text: '',
},
],
errors: {
someError: 'Some error occurred',
},
},
};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {};

expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS);
});

it('returns correct reason when report has export errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
errorFields: {
export: {
error: 'Some error occurred',
},
},
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {};

expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS);
});

it('returns correct report action when report has report action errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTION = {
reportActionID: '1',
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
actorAccountID: 12345,
created: '2024-08-08 18:20:44.171',
message: [
{
type: '',
text: '',
},
],
errors: {
someError: 'Some error occurred',
},
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'1': MOCK_REPORT_ACTION,
};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const {reportAction} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {};

expect(reportAction).toMatchObject<ReportAction>(MOCK_REPORT_ACTION);
});

it('returns null when report has no errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBeNull();
});
});

describe('shouldShowRedBrickRoad', () => {
it('returns true when report has transaction thread violations', async () => {
const MOCK_REPORT: Report = {
reportID: '1',
ownerAccountID: 12345,
chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
policyID: '6',
};

const MOCK_REPORTS: ReportCollectionDataSet = {
[`${ONYXKEYS.COLLECTION.REPORT}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT,
};

const MOCK_REPORT_ACTIONS: ReportActions = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'1': {
reportActionID: '1',
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
actorAccountID: 12345,
created: '2024-08-08 18:20:44.171',
},
};

const MOCK_TRANSACTION = {
transactionID: '1',
amount: 10,
modifiedAmount: 10,
reportID: MOCK_REPORT.reportID,
};

const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = {
[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [
{
type: CONST.VIOLATION_TYPES.VIOLATION,
name: CONST.VIOLATIONS.MISSING_CATEGORY,
},
],
};

await Onyx.multiSet({
...MOCK_REPORTS,
...MOCK_TRANSACTION_VIOLATIONS,
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT_ACTIONS,
[ONYXKEYS.SESSION]: {
accountID: 12345,
},
[`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION,
});

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection<TransactionViolations>);

expect(result).toBe(true);
});

it('returns true when report has errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
errorFields: {
someField: {
error: 'Some error occurred',
},
},
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBe(true);
});

it('returns true when report has violations', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBe(true);
});

it('returns true when report has report action errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'1': {
reportActionID: '1',
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
actorAccountID: 12345,
created: '2024-08-08 18:20:44.171',
message: [
{
type: '',
text: '',
},
],
errors: {
someError: 'Some error occurred',
},
},
};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBe(true);
});

it('returns true when report has export errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
errorFields: {
export: {
error: 'Some error occurred',
},
},
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBe(true);
});

it('returns false when report has no errors', () => {
const MOCK_REPORT: Report = {
reportID: '1',
};
const MOCK_REPORT_ACTIONS: OnyxEntry<ReportActions> = {};
const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection<TransactionViolation[]> = {};

const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS);

expect(result).toBe(false);
});
});
});
Loading