Skip to content
Open
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
17 changes: 16 additions & 1 deletion src/components/ReportActionItem/MoneyRequestReceiptView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useActiveRoute from '@hooks/useActiveRoute';
import useAncestors from '@hooks/useAncestors';
import useCardFeedErrors from '@hooks/useCardFeedErrors';
import useConfirmModal from '@hooks/useConfirmModal';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDelegateAccountID from '@hooks/useDelegateAccountID';
import useEnvironment from '@hooks/useEnvironment';
Expand Down Expand Up @@ -127,6 +128,7 @@ function MoneyRequestReceiptView({
}: MoneyRequestReceiptViewProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const {environmentURL} = useEnvironment();
const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout();
const {getReportRHPActiveRoute} = useActiveRoute();
Expand Down Expand Up @@ -293,6 +295,7 @@ function MoneyRequestReceiptView({
const violationMessage = ViolationsUtils.getViolationTranslation({
violation,
translate,
convertToDisplayString,
canEdit: isActionTakenByCurrentUser && isEditable,
companyCardPageURL,
connectionLink,
Expand All @@ -308,7 +311,19 @@ function MoneyRequestReceiptView({
}
}
return [imageViolations, allViolations];
}, [transactionViolations, translate, isActionTakenByCurrentUser, isEditable, companyCardPageURL, connectionLink, cardList, isMarkAsCash, routeDistanceMeters, distanceUnit]);
}, [
transactionViolations,
translate,
convertToDisplayString,
isActionTakenByCurrentUser,
isEditable,
companyCardPageURL,
connectionLink,
cardList,
isMarkAsCash,
routeDistanceMeters,
distanceUnit,
]);

const receiptRequiredViolation = transactionViolations?.some((violation) => violation.name === CONST.VIOLATIONS.RECEIPT_REQUIRED);
const itemizedReceiptRequiredViolation = transactionViolations?.some((violation) => violation.name === CONST.VIOLATIONS.ITEMIZED_RECEIPT_REQUIRED);
Expand Down
1 change: 1 addition & 0 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ function MoneyRequestView({
return ViolationsUtils.getViolationTranslation({
violation,
translate,
convertToDisplayString,
canEdit,
companyCardPageURL,
connectionLink,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ function TransactionPreviewContent({
? ViolationsUtils.getViolationTranslation({
violation: firstViolation,
translate,
convertToDisplayString,
canEdit,
companyCardPageURL,
connectionLink,
Expand Down
3 changes: 3 additions & 0 deletions src/components/TransactionItemRow/TransactionItemRowRBR.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {View} from 'react-native';
import Icon from '@components/Icon';
import RenderHTML from '@components/RenderHTML';
import Text from '@components/Text';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useEnvironment from '@hooks/useEnvironment';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
Expand Down Expand Up @@ -52,6 +53,7 @@ type TransactionItemRowRBRProps = TransactionItemRowRBRInnerProps & {
function TransactionItemRowRBRInner({transaction, violations, report, containerStyles, missingFieldError, shouldUseNarrowLayout}: TransactionItemRowRBRInnerProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const theme = useTheme();
const {environmentURL} = useEnvironment();
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction.reportID}`);
Expand All @@ -72,6 +74,7 @@ function TransactionItemRowRBRInner({transaction, violations, report, containerS
transaction,
transactionViolations: isSettled(report) ? [] : (violations ?? []),
translate,
convertToDisplayString,
missingFieldError,
transactionThreadActions: Object.values(transactionThreadActions ?? {}),
tags: policyTags,
Expand Down
5 changes: 4 additions & 1 deletion src/components/ViolationMessages.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useMemo} from 'react';
import {View} from 'react-native';
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function ViolationMessages({
}: ViolationMessagesProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const [cardList] = useOnyx(ONYXKEYS.CARD_LIST);

const filteredViolations = useMemo(() => filterReceiptViolations(violations), [violations]);
Expand All @@ -51,6 +53,7 @@ export default function ViolationMessages({
ViolationsUtils.getViolationTranslation({
violation,
translate,
convertToDisplayString,
canEdit,
companyCardPageURL,
connectionLink,
Expand All @@ -61,7 +64,7 @@ export default function ViolationMessages({
}),
];
}),
[canEdit, translate, filteredViolations, companyCardPageURL, connectionLink, cardList, isMarkAsCash, routeDistanceMeters, distanceUnit],
[canEdit, translate, convertToDisplayString, filteredViolations, companyCardPageURL, connectionLink, cardList, isMarkAsCash, routeDistanceMeters, distanceUnit],
);

return (
Expand Down
2 changes: 2 additions & 0 deletions src/libs/DebugUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,8 @@ function validateTransactionViolationDraftProperty(key: keyof TransactionViolati
rejectedBy: 'string',
rejectReason: 'string',
formattedLimit: 'string',
amount: 'number',
currency: 'string',
surcharge: 'number',
invoiceMarkup: 'number',
maxAge: 'number',
Expand Down
38 changes: 22 additions & 16 deletions src/libs/Violations/ViolationsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import reject from 'lodash/reject';
import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {CurrencyListActionsContextType} from '@hooks/useCurrencyList';
import {convertAttendeesToArray, getIsMissingAttendeesViolation} from '@libs/AttendeeUtils';
import {isPersonalCard} from '@libs/CardUtils';
import {getDecodedCategoryName, isCategoryMissing} from '@libs/CategoryUtils';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import {isReceiptError} from '@libs/ErrorUtils';
Expand All @@ -30,11 +30,13 @@ import type {Card, CardList, Policy, PolicyCategories, PolicyTagLists, PolicyTag
import type {Errors} from '@src/types/onyx/OnyxCommon';
import type {Unit} from '@src/types/onyx/Policy';
import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type ViolationFixParams from './types';

type ViolationTranslationParams = {
violation: TransactionViolation;
translate: LocaleContextProps['translate'];
convertToDisplayString: CurrencyListActionsContextType['convertToDisplayString'];
canEdit?: boolean;
tags?: PolicyTagLists;
companyCardPageURL?: string;
Expand Down Expand Up @@ -586,7 +588,8 @@ const ViolationsUtils = {
shouldShowCategoryItemizedReceiptRequiredViolation || !policy.maxExpenseAmountNoItemizedReceipt
? undefined
: {
formattedLimit: CurrencyUtils.convertToDisplayString(policy.maxExpenseAmountNoItemizedReceipt, policy.outputCurrency, true),
amount: policy.maxExpenseAmountNoItemizedReceipt,
currency: policy.outputCurrency,
},
type: CONST.VIOLATION_TYPES.VIOLATION,
showInReview: true,
Expand All @@ -608,7 +611,8 @@ const ViolationsUtils = {
shouldShowCategoryReceiptRequiredViolation || !policy.maxExpenseAmountNoReceipt
? undefined
: {
formattedLimit: CurrencyUtils.convertToDisplayString(policy.maxExpenseAmountNoReceipt, policy.outputCurrency, true),
amount: policy.maxExpenseAmountNoReceipt,
currency: policy.outputCurrency,
},
type: CONST.VIOLATION_TYPES.VIOLATION,
showInReview: true,
Expand All @@ -627,7 +631,8 @@ const ViolationsUtils = {
newTransactionViolations.push({
name: shouldCategoryShowOverLimitViolation ? CONST.VIOLATIONS.OVER_CATEGORY_LIMIT : CONST.VIOLATIONS.OVER_LIMIT,
data: {
formattedLimit: CurrencyUtils.convertAmountToDisplayString(shouldCategoryShowOverLimitViolation ? categoryOverLimit : policy.maxExpenseAmount, policy.outputCurrency),
amount: shouldCategoryShowOverLimitViolation ? categoryOverLimit : policy.maxExpenseAmount,
currency: policy.outputCurrency,
},
type: CONST.VIOLATION_TYPES.VIOLATION,
showInReview: true,
Expand All @@ -638,7 +643,8 @@ const ViolationsUtils = {
newTransactionViolations.push({
name: CONST.VIOLATIONS.OVER_TRIP_LIMIT,
data: {
formattedLimit: CurrencyUtils.convertAmountToDisplayString(-updatedTransaction.amount, updatedTransaction.currency),
amount: -updatedTransaction.amount,
currency: updatedTransaction.currency,
},
type: CONST.VIOLATION_TYPES.VIOLATION,
showInReview: true,
Expand Down Expand Up @@ -698,14 +704,16 @@ const ViolationsUtils = {
* functions.
*/
getViolationTranslation(params: ViolationTranslationParams): string {
const {violation, translate, canEdit = true, tags, companyCardPageURL, connectionLink, card, isMarkAsCash, routeDistanceMeters, distanceUnit} = params;
const {violation, translate, convertToDisplayString, canEdit = true, tags, companyCardPageURL, connectionLink, card, isMarkAsCash, routeDistanceMeters, distanceUnit} = params;
const {
brokenBankConnection = false,
isAdmin = false,
isTransactionOlderThan7Days = false,
member,
category,
formattedLimit = '',
amount = 0,
currency = CONST.CURRENCY.USD,
surcharge = 0,
invoiceMarkup = 0,
maxAge = 0,
Expand Down Expand Up @@ -766,21 +774,21 @@ const ViolationsUtils = {
case 'overAutoApprovalLimit':
return translate('violations.overAutoApprovalLimit', formattedLimit);
case 'overCategoryLimit':
return translate('violations.overCategoryLimit', formattedLimit);
return translate('violations.overCategoryLimit', convertToDisplayString(amount, currency));
case 'overLimit':
return translate('violations.overLimit', formattedLimit);
return translate('violations.overLimit', convertToDisplayString(amount, currency));
case 'overTripLimit':
return translate('violations.overTripLimit', formattedLimit);
return translate('violations.overTripLimit', convertToDisplayString(amount, currency));
case 'overLimitAttendee':
return translate('violations.overLimitAttendee', formattedLimit);
case 'perDayLimit':
return translate('violations.perDayLimit', formattedLimit);
case 'receiptNotSmartScanned':
return translate('violations.receiptNotSmartScanned');
case 'receiptRequired':
return translate('violations.receiptRequired', formattedLimit, getDecodedCategoryName(category ?? ''));
return translate('violations.receiptRequired', !isEmptyObject(violation.data) ? convertToDisplayString(amount, currency) : undefined, getDecodedCategoryName(category ?? ''));
case 'itemizedReceiptRequired':
return translate('violations.itemizedReceiptRequired', formattedLimit);
return translate('violations.itemizedReceiptRequired', !isEmptyObject(violation.data) ? convertToDisplayString(amount, currency) : undefined);
case 'customRules':
return translate('violations.customRules', message);
case 'rter': {
Expand Down Expand Up @@ -834,15 +842,11 @@ const ViolationsUtils = {
}
},

// We have to use regex, because Violation limit is given in a inconvenient form: "$2,000.00"
getViolationAmountLimit(violation: TransactionViolation): number {
return Number(violation.data?.formattedLimit?.replace(CONST.VIOLATION_LIMIT_REGEX, ''));
},

getRBRMessages({
transaction,
transactionViolations,
translate,
convertToDisplayString,
missingFieldError,
transactionThreadActions,
tags,
Expand All @@ -855,6 +859,7 @@ const ViolationsUtils = {
transaction: Transaction;
transactionViolations: TransactionViolation[];
translate: LocaleContextProps['translate'];
convertToDisplayString: CurrencyListActionsContextType['convertToDisplayString'];
missingFieldError?: string;
transactionThreadActions?: ReportAction[];
tags?: PolicyTagLists;
Expand All @@ -878,6 +883,7 @@ const ViolationsUtils = {
const message = ViolationsUtils.getViolationTranslation({
violation,
translate,
convertToDisplayString,
canEdit,
tags,
companyCardPageURL,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/IOU/UpdateMoneyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,7 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U
// Update violation limit, if we modify attendees. The given limit value is for a single attendee, if we have multiple attendees we should multiply limit by attendee count
const overLimitViolation = violations?.find((violation) => violation.name === 'overLimit');
if (overLimitViolation) {
const limitForSingleAttendee = ViolationsUtils.getViolationAmountLimit(overLimitViolation);
const limitForSingleAttendee = overLimitViolation.data?.amount ?? 0;
if (limitForSingleAttendee * (transactionChanges?.attendees?.length ?? 1) > Math.abs(getAmount(transaction))) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/YourSpendSection/CardRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {View} from 'react-native';
import CardFeedIcon from '@components/CardFeedIcon';
import Icon from '@components/Icon';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useHover from '@hooks/useHover';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {convertToDisplayString} from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import ROUTES from '@src/ROUTES';
Expand All @@ -25,6 +25,7 @@ function CardRow({cardRow, wrapperStyle}: CardRowProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const icons = useMemoizedLazyExpensifyIcons(['ArrowRight']);
const {hovered, bind} = useHover();
const {onMouseEnter, onMouseLeave} = bind;
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/YourSpendSection/SpendSummaryRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Icon from '@components/Icon';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import SkeletonRect from '@components/SkeletonRect';
import ItemListSkeletonView from '@components/Skeletons/ItemListSkeletonView';
import {useCurrencyListActions} from '@hooks/useCurrencyList';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {convertToDisplayString} from '@libs/CurrencyUtils';
import variables from '@styles/variables';
import type IconAsset from '@src/types/utils/IconAsset';
import YOUR_SPEND_ROW_STATE from './const';
Expand Down Expand Up @@ -60,6 +60,7 @@ function SpendSummaryRow({state, testIDPrefix, description, totals, iconSrc, onP
const styles = useThemeStyles();
const theme = useTheme();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {convertToDisplayString} = useCurrencyListActions();

if (state === YOUR_SPEND_ROW_STATE.LOADING) {
const horizontalPadding = shouldUseNarrowLayout ? SKELETON_NARROW_OFFSET_X : SKELETON_WIDE_OFFSET_X;
Expand Down
6 changes: 6 additions & 0 deletions src/types/onyx/TransactionViolation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type TransactionViolationData = {
/** Limit that the transaction violated */
formattedLimit?: string;

/** Currency of the transaction */
currency?: string;

/** Limit amount that the transaction violated */
amount?: number;

/** Percentage amount of conversion surcharge applied to the transaction */
surcharge?: number;

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/TransactionPreviewUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ describe('TransactionPreviewUtils', () => {
name: CONST.VIOLATIONS.ITEMIZED_RECEIPT_REQUIRED,
type: CONST.VIOLATION_TYPES.VIOLATION,
showInReview: true,
data: {formattedLimit: '$75.00'},
data: {amount: 7500, currency: CONST.CURRENCY.USD},
};

const mockViolations = (count: number) =>
Expand Down
Loading
Loading