Skip to content

Commit

Permalink
Merge pull request #48958 from Krishna2323/krishna2323/issue/47796
Browse files Browse the repository at this point in the history
fix: [Dupe detection] Tax code review step shows empty option when workspace does not have tax rate
  • Loading branch information
pecanoro authored Nov 6, 2024
2 parents 413c9db + 5c6f079 commit 8a411ff
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,13 @@ function MoneyRequestPreviewContent({

const navigateToReviewFields = () => {
const backTo = route.params.backTo;
const comparisonResult = TransactionUtils.compareDuplicateTransactionFields(reviewingTransactionID);
Transaction.setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID ?? ''});

// Clear the draft before selecting a different expense to prevent merging fields from the previous expense
// (e.g., category, tag, tax) that may be not enabled/available in the new expense's policy.
Transaction.abandonReviewDuplicateTransactions();
const comparisonResult = TransactionUtils.compareDuplicateTransactionFields(reviewingTransactionID, transaction?.reportID ?? '');
Transaction.setReviewDuplicatesKey({...comparisonResult.keep, duplicates, transactionID: transaction?.transactionID ?? '', reportID: transaction?.reportID});

if ('merchant' in comparisonResult.change) {
Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE.getRoute(route.params?.threadReportID, backTo));
} else if ('category' in comparisonResult.change) {
Expand Down
54 changes: 52 additions & 2 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import lodashSet from 'lodash/set';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import {getPolicyCategoriesData} from '@libs/actions/Policy/Category';
import {getPolicyTagsData} from '@libs/actions/Policy/Tag';
import type {TransactionMergeParams} from '@libs/API/parameters';
import {getCurrencyDecimals} from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
Expand Down Expand Up @@ -1034,7 +1036,7 @@ function removeSettledAndApprovedTransactions(transactionIDs: string[]) {
* 6. It returns the 'keep' and 'change' objects.
*/

function compareDuplicateTransactionFields(transactionID: string): {keep: Partial<ReviewDuplicates>; change: FieldsToChange} {
function compareDuplicateTransactionFields(transactionID: string, reportID: string): {keep: Partial<ReviewDuplicates>; change: FieldsToChange} {
const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`];
const duplicates = transactionViolations?.find((violation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)?.data?.duplicates ?? [];
const transactions = removeSettledAndApprovedTransactions([transactionID, ...duplicates]).map((item) => getTransaction(item));
Expand Down Expand Up @@ -1095,7 +1097,10 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia
const keys = fieldsToCompare[fieldName];
const firstTransaction = transactions.at(0);
const isFirstTransactionCommentEmptyObject = typeof firstTransaction?.comment === 'object' && firstTransaction?.comment?.comment === '';
const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null;
const policy = PolicyUtils.getPolicy(report?.policyID);

const areAllFieldsEqualForKey = areAllFieldsEqual(transactions, (item) => keys.map((key) => item?.[key]).join('|'));
if (fieldName === 'description') {
const allCommentsAreEqual = areAllCommentsEqual(transactions, firstTransaction);
const allCommentsAreEmpty = isFirstTransactionCommentEmptyObject && transactions.every((item) => getDescription(item) === '');
Expand All @@ -1110,7 +1115,52 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia
} else {
processChanges(fieldName, transactions, keys);
}
} else if (areAllFieldsEqual(transactions, (item) => keys.map((key) => item?.[key]).join('|'))) {
} else if (fieldName === 'taxCode') {
const differentValues = getDifferentValues(transactions, keys);
const validTaxes = differentValues?.filter((taxID) => {
const tax = PolicyUtils.getTaxByID(policy, (taxID as string) ?? '');
return tax?.name && !tax.isDisabled && tax.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
});

if (!areAllFieldsEqualForKey && validTaxes.length > 1) {
change[fieldName] = validTaxes;
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
} else if (fieldName === 'category') {
const differentValues = getDifferentValues(transactions, keys);
const policyCategories = getPolicyCategoriesData(report?.policyID ?? '-1');
const availableCategories = Object.values(policyCategories)
.filter((category) => differentValues.includes(category.name) && category.enabled && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
.map((e) => e.name);

if (!areAllFieldsEqualForKey && policy?.areCategoriesEnabled && (availableCategories.length > 1 || (availableCategories.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableCategories, ...(differentValues.includes('') ? [''] : [])];
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
} else if (fieldName === 'tag') {
const policyTags = getPolicyTagsData(report?.policyID ?? '-1');
const isMultiLevelTags = PolicyUtils.isMultiLevelTags(policyTags);
if (isMultiLevelTags) {
if (areAllFieldsEqualForKey || !policy?.areTagsEnabled) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
} else {
processChanges(fieldName, transactions, keys);
}
} else {
const differentValues = getDifferentValues(transactions, keys);
const policyTagsObj = Object.values(Object.values(policyTags).at(0)?.tags ?? {});
const availableTags = policyTagsObj
.filter((tag) => differentValues.includes(tag.name) && tag.enabled && tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
.map((e) => e.name);
if (!areAllFieldsEqualForKey && policy?.areTagsEnabled && (availableTags.length > 1 || (availableTags.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableTags, ...(differentValues.includes('') ? [''] : [])];
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
}
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
} else {
processChanges(fieldName, transactions, keys);
Expand Down
5 changes: 5 additions & 0 deletions src/libs/actions/Policy/Category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,10 @@ function setPolicyCategoryTax(policyID: string, categoryName: string, taxID: str
API.write(WRITE_COMMANDS.SET_POLICY_CATEGORY_TAX, parameters, onyxData);
}

function getPolicyCategoriesData(policyID: string) {
return allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`] ?? {};
}

export {
getPolicyCategories,
openPolicyCategoriesPage,
Expand All @@ -1370,4 +1374,5 @@ export {
setPolicyCategoryTax,
importPolicyCategories,
downloadCategoriesCSV,
getPolicyCategoriesData,
};
5 changes: 5 additions & 0 deletions src/libs/actions/Policy/Tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,10 @@ function downloadTagsCSV(policyID: string, onDownloadFailed: () => void) {
fileDownload(ApiUtils.getCommandURL({command: WRITE_COMMANDS.EXPORT_TAGS_CSV}), fileName, '', false, formData, CONST.NETWORK.METHOD.POST, onDownloadFailed);
}

function getPolicyTagsData(policyID: string) {
return allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {};
}

export {
buildOptimisticPolicyRecentlyUsedTags,
setPolicyRequiresTag,
Expand All @@ -1058,6 +1062,7 @@ export {
setPolicyTagApprover,
importPolicyTags,
downloadTagsCSV,
getPolicyTagsData,
};

export type {NewCustomUnit};
2 changes: 1 addition & 1 deletion src/pages/TransactionDuplicate/Confirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function Confirmation() {
const [reviewDuplicates, reviewDuplicatesResult] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const transaction = useMemo(() => TransactionUtils.buildNewTransactionAfterReviewingDuplicates(reviewDuplicates), [reviewDuplicates]);
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const {goBack} = useReviewDuplicatesNavigation(Object.keys(compareResult.change ?? {}), 'confirmation', route.params.threadReportID, route.params.backTo);
const [report, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.threadReportID}`);
const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`);
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewBillable.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation';
import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -16,7 +18,8 @@ function ReviewBillable() {
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.TAG>>();
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewCategory.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation';
import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -16,7 +18,8 @@ function ReviewCategory() {
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.CATEGORY>>();
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation';
import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -16,7 +18,8 @@ function ReviewDescription() {
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.DESCRIPTION>>();
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewMerchant.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation';
import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -16,7 +18,8 @@ function ReviewMerchant() {
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.TAG>>();
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewReimbursable.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import useReviewDuplicatesNavigation from '@hooks/useReviewDuplicatesNavigation';
import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -16,7 +18,8 @@ function ReviewReimbursable() {
const route = useRoute<RouteProp<TransactionDuplicateNavigatorParamList, typeof SCREENS.TRANSACTION_DUPLICATE.TAG>>();
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
5 changes: 4 additions & 1 deletion src/pages/TransactionDuplicate/ReviewTag.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {RouteProp} from '@react-navigation/native';
import {useRoute} from '@react-navigation/native';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -9,6 +10,7 @@ import {setReviewDuplicatesKey} from '@libs/actions/Transaction';
import type {TransactionDuplicateNavigatorParamList} from '@libs/Navigation/types';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {FieldItemType} from './ReviewFields';
import ReviewFields from './ReviewFields';
Expand All @@ -18,7 +20,8 @@ function ReviewTag() {
const {translate} = useLocalize();
const transactionID = TransactionUtils.getTransactionID(route.params.threadReportID ?? '');

const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID);
const [reviewDuplicates] = useOnyx(ONYXKEYS.REVIEW_DUPLICATES);
const compareResult = TransactionUtils.compareDuplicateTransactionFields(transactionID, reviewDuplicates?.reportID ?? '-1');
const stepNames = Object.keys(compareResult.change ?? {}).map((key, index) => (index + 1).toString());
const {currentScreenIndex, goBack, navigateToNextScreen} = useReviewDuplicatesNavigation(
Object.keys(compareResult.change ?? {}),
Expand Down
Loading

0 comments on commit 8a411ff

Please sign in to comment.