diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 589f001b4061..ed1047e85dd8 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -394,8 +394,7 @@ const ViolationsUtils = { const hasCategoryReceiptRequiredViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.RECEIPT_REQUIRED && !violation.data); const hasItemizedReceiptRequiredViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.ITEMIZED_RECEIPT_REQUIRED); const hasOverLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_LIMIT); - // TODO: Uncomment when the OVER_TRIP_LIMIT violation is implemented - // const hasOverTripLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_TRIP_LIMIT); + const hasOverTripLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_TRIP_LIMIT); const hasCategoryOverLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_CATEGORY_LIMIT); const hasMissingCommentViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.MISSING_COMMENT); const hasMissingAttendeesViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.MISSING_ATTENDEES); @@ -463,6 +462,10 @@ const ViolationsUtils = { isMaxExpenseAmountRuleEnabled(overLimitAmount) && expenseAmount > overLimitAmount && isControlPolicy; + // Ensure we are comparing amounts in the same currency + const isSameCurrency = updatedTransaction.currency === currency; + const shouldShowOverTripLimitViolation = + canCalculateAmountViolations && !isInvoiceTransaction && TransactionUtils.hasReservationList(updatedTransaction) && isSameCurrency && expenseAmount > -updatedTransaction.amount; const shouldCategoryShowOverLimitViolation = canCalculateAmountViolations && !isInvoiceTransaction && typeof categoryOverLimit === 'number' && expenseAmount > categoryOverLimit && isControlPolicy; const shouldShowMissingComment = @@ -573,21 +576,20 @@ const ViolationsUtils = { }); } - // TODO: Uncomment when the OVER_TRIP_LIMIT violation is implemented - // if (canCalculateAmountViolations && !hasOverTripLimitViolation && Math.abs(updatedTransaction.amount) < Math.abs(amount) && TransactionUtils.hasReservationList(updatedTransaction)) { - // newTransactionViolations.push({ - // name: CONST.VIOLATIONS.OVER_TRIP_LIMIT, - // data: { - // formattedLimit: CurrencyUtils.convertAmountToDisplayString(updatedTransaction.amount, updatedTransaction.currency), - // }, - // type: CONST.VIOLATION_TYPES.VIOLATION, - // showInReview: true, - // }); - // } - - // if (canCalculateAmountViolations && hasOverTripLimitViolation && Math.abs(updatedTransaction.amount) >= Math.abs(amount) && TransactionUtils.hasReservationList(updatedTransaction)) { - // newTransactionViolations = reject(newTransactionViolations, {name: CONST.VIOLATIONS.OVER_TRIP_LIMIT}); - // } + if (canCalculateAmountViolations && !hasOverTripLimitViolation && shouldShowOverTripLimitViolation) { + newTransactionViolations.push({ + name: CONST.VIOLATIONS.OVER_TRIP_LIMIT, + data: { + formattedLimit: CurrencyUtils.convertAmountToDisplayString(-updatedTransaction.amount, updatedTransaction.currency), + }, + type: CONST.VIOLATION_TYPES.VIOLATION, + showInReview: true, + }); + } + + if (canCalculateAmountViolations && hasOverTripLimitViolation && !shouldShowOverTripLimitViolation) { + newTransactionViolations = reject(newTransactionViolations, {name: CONST.VIOLATIONS.OVER_TRIP_LIMIT}); + } if (!hasMissingCommentViolation && shouldShowMissingComment) { newTransactionViolations.push({ diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index a3078f98f0ec..f5745ad45f52 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -67,6 +67,15 @@ const categoryOverLimitViolation = { }, }; +const overTripLimitViolation = { + name: CONST.VIOLATIONS.OVER_TRIP_LIMIT, + type: CONST.VIOLATION_TYPES.VIOLATION, + showInReview: true, + data: { + formattedLimit: convertAmountToDisplayString(400), + }, +}; + const categoryMissingCommentViolation = { name: CONST.VIOLATIONS.MISSING_COMMENT, type: CONST.VIOLATION_TYPES.VIOLATION, @@ -974,6 +983,62 @@ describe('getViolationsOnyxData', () => { }); }); }); + + describe('overTripLimit violation', () => { + it('should add overTripLimit violation if the modified transaction amount is over the original transaction amount', () => { + policy.outputCurrency = CONST.CURRENCY.USD; + transaction.amount = -400; + transaction.modifiedAmount = -600; + transaction.receipt = { + reservationList: [ + { + start: {date: '2023-07-24'}, + end: {date: '2023-07-25'}, + type: 'train', + }, + ], + }; + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false); + expect(result.value).toEqual(expect.arrayContaining([overTripLimitViolation, ...transactionViolations])); + }); + + it('should not add overTripLimit violation if the modified transaction currency is different from the original transaction currency', () => { + policy.outputCurrency = CONST.CURRENCY.USD; + transaction.amount = -400; + transaction.modifiedAmount = -600; + transaction.currency = CONST.CURRENCY.USD; + transaction.modifiedCurrency = CONST.CURRENCY.GBP; + transaction.receipt = { + reservationList: [ + { + start: {date: '2023-07-24'}, + end: {date: '2023-07-25'}, + type: 'train', + }, + ], + }; + const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false); + expect(result.value).toEqual([]); + }); + + it('should remove overTripLimit violation if the modified transaction amount is not over the original transaction amount', () => { + policy.outputCurrency = CONST.CURRENCY.USD; + transaction.amount = -400; + transaction.modifiedAmount = -300; + transaction.receipt = { + reservationList: [ + { + start: {date: '2023-07-24'}, + end: {date: '2023-07-25'}, + type: 'train', + }, + ], + }; + const modifiedTransactionViolations = [overTripLimitViolation, ...transactionViolations]; + const result = ViolationsUtils.getViolationsOnyxData(transaction, modifiedTransactionViolations, policy, policyTags, policyCategories, false, false); + expect(result.value).toEqual([]); + }); + }); }); const getFakeTransaction = (transactionID: string, comment?: Transaction['comment']) => ({