-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Implement ViolationUtils lib #31656
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
Merged
Merged
Implement ViolationUtils lib #31656
Changes from all commits
Commits
Show all changes
78 commits
Select commit
Hold shift + click to select a range
f251978
feat(Violations): Add PolicyCategories type to match PolicyTags
trevor-coleman 51c2e7e
feat(Violations): Create stub for ViolationUtils
trevor-coleman f001066
feat(Violations): Add TRANSACTION_VIOLATION to ONYXKEYS.COLLECTIONS
trevor-coleman 2705280
feat(Violations): Add TransactionViolation and related types to src/t…
trevor-coleman e2aaa6a
feat(Violations): create possibleViolationsByField lookup
trevor-coleman 7f39f5a
feat(Violations): implement getViolationForField and getViolationsOny…
trevor-coleman ccb4206
prettier
trevor-coleman 6a67fb8
Merge pull request #73 from infinitered/trevorcoleman/violations/viol…
trevor-coleman 0a04347
feat(Violations): remove unnecessary callback
trevor-coleman 15f43ba
Merge remote-tracking branch 'origin/main' into trevorcoleman/violati…
trevor-coleman 575ea9b
update package-lock.json
trevor-coleman 17ef290
Merge remote-tracking branch 'upstream/main' into trevorcoleman/viola…
trevor-coleman cf65dbe
Merge remote-tracking branch 'upstream/main' into violation-utils
trevor-coleman 0353afa
restore package-lock.json
trevor-coleman 8c16f79
Merge remote-tracking branch 'upstream/main' into violation-utils
trevor-coleman 1e2e7c8
Update src/types/onyx/TransactionViolation.ts
trevor-coleman 0c8d067
Update src/libs/Violations/ViolationsUtils.ts
trevor-coleman 434fd9f
Update src/types/onyx/TransactionViolation.ts
trevor-coleman d251b9a
Update src/types/onyx/TransactionViolation.ts
trevor-coleman 19b11b1
Merge branch 'Expensify:main' into violation-utils
trevor-coleman 7f83963
feat(Violations): remove ViolationType, sort violation names
trevor-coleman e4a56b9
feat(Violations): add dummy translations for complete list
trevor-coleman 12a2aa3
feat(Violations): remove barrel file
trevor-coleman 12682bb
feat(Violations): extract getViolationsForField into useViolations hook
trevor-coleman d7ca133
Merge branch 'violation-utils' into trevorcoleman/violations/violatio…
trevor-coleman 58ea8d8
feat(Violations): export violationFields for testing
trevor-coleman 8f951e0
feat(Violations): add tests for useViolations
trevor-coleman 04c23f6
feat(Violations): remove comments on arguments and replace with JSDoc
trevor-coleman d1c8792
Merge remote-tracking branch 'origin/trevorcoleman/violations/violati…
trevor-coleman ddbe029
feat(Violations): lint
trevor-coleman 7054d05
package-lock
trevor-coleman 532c96a
prettier
trevor-coleman 6568133
feat(Violations): remove possibleViolationsByField
trevor-coleman 6aef0ba
Merge pull request #77 from infinitered/trevorcoleman/violations/viol…
trevor-coleman dfb444b
feat(Violations): update violationFields
trevor-coleman ae8acc5
feat(Violations): update translations for violations
trevor-coleman a796c2c
feat(Violations): update spanish translations for violations
trevor-coleman f2792e5
feat(Violations): create ViolationUtilsTest with stubs.
trevor-coleman 3710f82
feat(Violations): fix missing tags and categories logic
trevor-coleman 5158f20
feat(Violations): add tests for ViolationUtils
trevor-coleman eaa9c85
feat(Violations): remove test for case sensitivity
trevor-coleman 4fcc994
feat(Violations): add tests for ViolationUtils
trevor-coleman a66d253
feat(Violations): return dummy translations
trevor-coleman f504508
feat(Violations): remove unused imports
trevor-coleman ae1731b
feat(Violations): memoize return values for hasViolations, and return…
trevor-coleman de45660
feat(Violations): update tests for new return values.
trevor-coleman e239e41
Merge pull request #78 from infinitered/trevor-coleman/violations/vio…
trevor-coleman 2b27509
feat(Violations): remove index
trevor-coleman 47afbe9
feat(Violations): add quotes
trevor-coleman bc3b872
feat(Violations): Remove unnecessary casts
trevor-coleman 9fd9bf9
feat(Violations): capitalize
trevor-coleman e72bacc
feat(Violations): Add condition
trevor-coleman 2de9615
feat(Violations): Remove cast
trevor-coleman 478f934
feat(Violations): add condition
trevor-coleman ef5ebb3
feat(Violations): remove unused types
trevor-coleman 68c5485
feat(Violations): harmonize tag and category handling
trevor-coleman 75f003a
feat(Violations): reorder types
trevor-coleman 016b438
feat(Violations): Apply suggestions from code review
trevor-coleman 9c356f2
feat(Violations): remove hasViolationsMap
trevor-coleman 9b0c88a
feat(Violations): refactor useViolations tests
trevor-coleman e0d8d97
feat(Violations): refactor ViolationUtils tests
trevor-coleman 96544c9
Merge remote-tracking branch 'origin/violation-utils' into violation-…
trevor-coleman 06b7012
Merge branch 'main' into violation-utils
trevor-coleman ebb5751
feat(Violations): revert package.json
trevor-coleman 74b80df
prettier
trevor-coleman d3be1f6
feat(Violations): remove execute
trevor-coleman 4ab9931
feat(Violations): refactor tests
trevor-coleman 3737752
feat(Violations): refactor tests
trevor-coleman 1876a0b
feat(Violations): refactor tests
trevor-coleman 72667ec
feat(Violations): refactor tests to have simpler base case
trevor-coleman 27cfac1
Update tests/unit/ViolationUtilsTest.js
trevor-coleman e5ae614
feat(Violations): Suggested changes from PR Review
trevor-coleman 8498e80
feat(Violations): remove useViolationsTest
trevor-coleman e6e7612
feat(Violations): fixes from PR Comments
trevor-coleman 0aa36b8
Merge remote-tracking branch 'origin/violation-utils' into violation-…
trevor-coleman 391822c
prettier
trevor-coleman dd48fe1
Merge remote-tracking branch 'upstream/main' into violation-utils
trevor-coleman d2cfd33
feat(Violations): remove useViolationsTest
trevor-coleman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import {useCallback, useMemo} from 'react'; | ||
| import {TransactionViolation, ViolationName} from '@src/types/onyx'; | ||
|
|
||
| /** | ||
| * Names of Fields where violations can occur | ||
| */ | ||
| type ViolationField = 'amount' | 'billable' | 'category' | 'comment' | 'date' | 'merchant' | 'receipt' | 'tag' | 'tax'; | ||
|
|
||
| /** | ||
| * Map from Violation Names to the field where that violation can occur | ||
| */ | ||
| const violationFields: Record<ViolationName, ViolationField> = { | ||
| allTagLevelsRequired: 'tag', | ||
| autoReportedRejectedExpense: 'merchant', | ||
| billableExpense: 'billable', | ||
| cashExpenseWithNoReceipt: 'receipt', | ||
| categoryOutOfPolicy: 'category', | ||
| conversionSurcharge: 'amount', | ||
| customUnitOutOfPolicy: 'merchant', | ||
| duplicatedTransaction: 'merchant', | ||
| fieldRequired: 'merchant', | ||
| futureDate: 'date', | ||
| invoiceMarkup: 'amount', | ||
| maxAge: 'date', | ||
| missingCategory: 'category', | ||
| missingComment: 'comment', | ||
| missingTag: 'tag', | ||
| modifiedAmount: 'amount', | ||
| modifiedDate: 'date', | ||
| nonExpensiworksExpense: 'merchant', | ||
| overAutoApprovalLimit: 'amount', | ||
| overCategoryLimit: 'amount', | ||
| overLimit: 'amount', | ||
| overLimitAttendee: 'amount', | ||
| perDayLimit: 'amount', | ||
| receiptNotSmartScanned: 'receipt', | ||
| receiptRequired: 'receipt', | ||
| rter: 'merchant', | ||
| smartscanFailed: 'receipt', | ||
| someTagLevelsRequired: 'tag', | ||
| tagOutOfPolicy: 'tag', | ||
| taxAmountChanged: 'tax', | ||
| taxOutOfPolicy: 'tax', | ||
| taxRateChanged: 'tax', | ||
| taxRequired: 'tax', | ||
| }; | ||
|
|
||
| type ViolationsMap = Map<ViolationField, TransactionViolation[]>; | ||
|
|
||
| function useViolations(violations: TransactionViolation[]) { | ||
| const violationsByField = useMemo((): ViolationsMap => { | ||
| const violationGroups = new Map<ViolationField, TransactionViolation[]>(); | ||
|
|
||
| for (const violation of violations) { | ||
| const field = violationFields[violation.name]; | ||
| const existingViolations = violationGroups.get(field) ?? []; | ||
| violationGroups.set(field, [...existingViolations, violation]); | ||
| } | ||
|
|
||
| return violationGroups ?? new Map(); | ||
| }, [violations]); | ||
|
|
||
| const hasViolations = useCallback((field: ViolationField) => Boolean(violationsByField.get(field)?.length), [violationsByField]); | ||
| const getViolationsForField = useCallback((field: ViolationField) => violationsByField.get(field) ?? [], [violationsByField]); | ||
|
|
||
| return { | ||
| hasViolations, | ||
| getViolationsForField, | ||
| }; | ||
| } | ||
|
|
||
| export default useViolations; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import reject from 'lodash/reject'; | ||
| import Onyx from 'react-native-onyx'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import {PolicyCategories, PolicyTags, Transaction, TransactionViolation} from '@src/types/onyx'; | ||
|
|
||
| const ViolationsUtils = { | ||
| /** | ||
| * Checks a transaction for policy violations and returns an object with Onyx method, key and updated transaction | ||
| * violations. | ||
| */ | ||
| getViolationsOnyxData( | ||
| transaction: Transaction, | ||
| transactionViolations: TransactionViolation[], | ||
| policyRequiresTags: boolean, | ||
| policyTags: PolicyTags, | ||
| policyRequiresCategories: boolean, | ||
| policyCategories: PolicyCategories, | ||
| ): { | ||
| onyxMethod: string; | ||
| key: string; | ||
| value: TransactionViolation[]; | ||
| } { | ||
| let newTransactionViolations = [...transactionViolations]; | ||
|
|
||
| if (policyRequiresCategories) { | ||
| const hasCategoryOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'categoryOutOfPolicy'); | ||
| const hasMissingCategoryViolation = transactionViolations.some((violation) => violation.name === 'missingCategory'); | ||
| const isCategoryInPolicy = Boolean(policyCategories[transaction.category]?.enabled); | ||
|
|
||
| // Add 'categoryOutOfPolicy' violation if category is not in policy | ||
| if (!hasCategoryOutOfPolicyViolation && transaction.category && !isCategoryInPolicy) { | ||
| newTransactionViolations.push({name: 'categoryOutOfPolicy', type: 'violation', userMessage: ''}); | ||
| } | ||
|
|
||
| // Remove 'categoryOutOfPolicy' violation if category is in policy | ||
| if (hasCategoryOutOfPolicyViolation && transaction.category && isCategoryInPolicy) { | ||
| newTransactionViolations = reject(newTransactionViolations, {name: 'categoryOutOfPolicy'}); | ||
| } | ||
|
|
||
| // Remove 'missingCategory' violation if category is valid according to policy | ||
| if (hasMissingCategoryViolation && isCategoryInPolicy) { | ||
| newTransactionViolations = reject(newTransactionViolations, {name: 'missingCategory'}); | ||
| } | ||
|
|
||
| // Add 'missingCategory' violation if category is required and not set | ||
| if (!hasMissingCategoryViolation && policyRequiresCategories && !transaction.category) { | ||
| newTransactionViolations.push({name: 'missingCategory', type: 'violation', userMessage: ''}); | ||
| } | ||
| } | ||
|
|
||
| if (policyRequiresTags) { | ||
| const hasTagOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'tagOutOfPolicy'); | ||
| const hasMissingTagViolation = transactionViolations.some((violation) => violation.name === 'missingTag'); | ||
| const isTagInPolicy = Boolean(policyTags[transaction.tag]?.enabled); | ||
|
|
||
| // Add 'tagOutOfPolicy' violation if tag is not in policy | ||
| if (!hasTagOutOfPolicyViolation && transaction.tag && !isTagInPolicy) { | ||
| newTransactionViolations.push({name: 'tagOutOfPolicy', type: 'violation', userMessage: ''}); | ||
| } | ||
|
|
||
| // Remove 'tagOutOfPolicy' violation if tag is in policy | ||
| if (hasTagOutOfPolicyViolation && transaction.tag && isTagInPolicy) { | ||
| newTransactionViolations = reject(newTransactionViolations, {name: 'tagOutOfPolicy'}); | ||
| } | ||
|
|
||
| // Remove 'missingTag' violation if tag is valid according to policy | ||
| if (hasMissingTagViolation && isTagInPolicy) { | ||
| newTransactionViolations = reject(newTransactionViolations, {name: 'missingTag'}); | ||
| } | ||
|
|
||
| // Add 'missingTag violation' if tag is required and not set | ||
| if (!hasMissingTagViolation && !transaction.tag && policyRequiresTags) { | ||
| newTransactionViolations.push({name: 'missingTag', type: 'violation', userMessage: ''}); | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| onyxMethod: Onyx.METHOD.SET, | ||
| key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`, | ||
| value: newTransactionViolations, | ||
| }; | ||
| }, | ||
| }; | ||
|
|
||
| export default ViolationsUtils; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /** | ||
| * Names of transaction violations | ||
| */ | ||
| type ViolationName = | ||
| | 'allTagLevelsRequired' | ||
| | 'autoReportedRejectedExpense' | ||
| | 'billableExpense' | ||
| | 'cashExpenseWithNoReceipt' | ||
| | 'categoryOutOfPolicy' | ||
| | 'conversionSurcharge' | ||
| | 'customUnitOutOfPolicy' | ||
| | 'duplicatedTransaction' | ||
| | 'fieldRequired' | ||
| | 'futureDate' | ||
| | 'invoiceMarkup' | ||
| | 'maxAge' | ||
| | 'missingCategory' | ||
| | 'missingComment' | ||
| | 'missingTag' | ||
| | 'modifiedAmount' | ||
| | 'modifiedDate' | ||
| | 'nonExpensiworksExpense' | ||
| | 'overAutoApprovalLimit' | ||
| | 'overCategoryLimit' | ||
| | 'overLimit' | ||
| | 'overLimitAttendee' | ||
| | 'perDayLimit' | ||
| | 'receiptNotSmartScanned' | ||
| | 'receiptRequired' | ||
| | 'rter' | ||
| | 'smartscanFailed' | ||
| | 'someTagLevelsRequired' | ||
| | 'tagOutOfPolicy' | ||
| | 'taxAmountChanged' | ||
| | 'taxOutOfPolicy' | ||
| | 'taxRateChanged' | ||
| | 'taxRequired'; | ||
|
|
||
| type TransactionViolation = { | ||
| type: string; | ||
| name: ViolationName; | ||
| userMessage: string; | ||
| data?: Record<string, string>; | ||
| }; | ||
|
|
||
| export type {TransactionViolation, ViolationName}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.