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

Implement ViolationUtils lib #31083 #73

Merged
1 change: 1 addition & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ const ONYXKEYS = {
REPORT_USER_IS_LEAVING_ROOM: 'reportUserIsLeavingRoom_',
SECURITY_GROUP: 'securityGroup_',
TRANSACTION: 'transactions_',
TRANSACTION_VIOLATIONS: 'transactionViolations_',
SPLIT_TRANSACTION_DRAFT: 'splitTransactionDraft_',
PRIVATE_NOTES_DRAFT: 'privateNotesDraft_',
NEXT_STEP: 'reportNextStep_',
Expand Down
69 changes: 69 additions & 0 deletions src/libs/Violations/ViolationsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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';
import possibleViolationsByField, {ViolationField} from './possibleViolationsByField';

const ViolationsUtils = {
getViolationForField(transactionViolations: TransactionViolation[], field: ViolationField, translate: (key: string) => string): string[] {
return transactionViolations.filter((violation) => possibleViolationsByField[field]?.includes(violation.name)).map((violation) => translate(violation.name));
},

getViolationsOnyxData(
/** The transaction to check for policy violations. */
transaction: Transaction,
/** An array of existing transaction violations. */
transactionViolations: TransactionViolation[],
/** Indicates if the policy requires tags. */
policyRequiresTags: boolean,
/** Collection of policy tags and their enabled states. */
policyTags: PolicyTags,
/** Indicates if the policy requires categories. */
policyRequiresCategories: boolean,
/** Collection of policy categories and their enabled states. */
policyCategories: PolicyCategories,
): {
onyxMethod: string;
key: string;
value: TransactionViolation[];
} {
let newTransactionViolations = [...transactionViolations];

if (policyRequiresCategories) {
const categoryViolationExists = transactionViolations.some((violation) => violation.name === 'categoryOutOfPolicy');
const categoryIsInPolicy = policyCategories[transaction.category]?.enabled;

// Add 'categoryOutOfPolicy' violation if category is not in policy
if (!categoryViolationExists && transaction.category && !categoryIsInPolicy) {
newTransactionViolations.push({name: 'categoryOutOfPolicy', type: 'violation', userMessage: ''});
}

// Remove 'missingCategory' violation if category is valid according to policy
if (categoryIsInPolicy) {
newTransactionViolations = reject(newTransactionViolations, {name: 'missingCategory'});
}
}

if (policyRequiresTags) {
// Add 'tagOutOfPolicy' violation if tag is not in policy
const tagViolationExists = transactionViolations.some((violation) => violation.name === 'tagOutOfPolicy');
const tagInPolicy = policyTags[transaction.tag]?.enabled;
if (!tagViolationExists && transaction.tag && !tagInPolicy) {
newTransactionViolations.push({name: 'tagOutOfPolicy', type: 'violation', userMessage: ''});
}

// Remove 'missingTag' violation if tag is valid according to policy
if (tagInPolicy) {
newTransactionViolations = reject(newTransactionViolations, {name: 'missingTag'});
}
}

return {
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`,
value: newTransactionViolations,
};
},
};

export default ViolationsUtils;
3 changes: 3 additions & 0 deletions src/libs/Violations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as ViolationsUtils from './ViolationsUtils';

export default ViolationsUtils;
41 changes: 41 additions & 0 deletions src/libs/Violations/possibleViolationsByField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import invertBy from 'lodash/invertBy';
import {ViolationName} from '@src/types/onyx';

/**
* Map from Violation Names to the field where that violation can occur
*/
const violationFields: Record<ViolationName, ViolationField> = {
perDayLimit: 'amount',
maxAge: 'date',
overLimit: 'amount',
overLimitAttendee: 'amount',
overCategoryLimit: 'amount',
receiptRequired: 'receipt',
missingCategory: 'category',
categoryOutOfPolicy: 'category',
missingTag: 'tag',
tagOutOfPolicy: 'tag',
missingComment: 'comment',
taxRequired: 'tax',
taxOutOfPolicy: 'tax',
billableExpense: 'billable',
};

/**
* Names of Fields where violations can occur
*/
type ViolationField = 'merchant' | 'amount' | 'category' | 'date' | 'tag' | 'comment' | 'billable' | 'receipt' | 'tax';

/**
* Map from field name to array of violation types that can occur on that field.
* @example
* {
* // ...
* category: ['missingCategory', 'categoryOutOfPolicy']
* // ...
* }
*/
const possibleViolationsByField = invertBy(violationFields, (value) => value) as Record<ViolationField, ViolationName[]>;

export default possibleViolationsByField;
export type {ViolationField};
2 changes: 2 additions & 0 deletions src/types/onyx/PolicyCategory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ type PolicyCategory = {
origin: string;
};

type PolicyCategories = Record<string, PolicyCategory>;
export default PolicyCategory;
export type {PolicyCategories};
34 changes: 34 additions & 0 deletions src/types/onyx/TransactionViolation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @module TransactionViolation
* @description Transaction Violation
*/

/**
* Names of the various Transaction Violation types
*/
type ViolationName =
| 'perDayLimit'
| 'maxAge'
| 'overLimit'
| 'overLimitAttendee'
| 'overCategoryLimit'
| 'receiptRequired'
| 'missingCategory'
| 'categoryOutOfPolicy'
| 'missingTag'
| 'tagOutOfPolicy'
| 'missingComment'
| 'taxRequired'
| 'taxOutOfPolicy'
| 'billableExpense';

type ViolationType = string;

type TransactionViolation = {
type: ViolationType;
name: ViolationName;
userMessage: string;
data?: Record<string, string>;
};

export type {TransactionViolation, ViolationName, ViolationType};
7 changes: 6 additions & 1 deletion src/types/onyx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import PersonalBankAccount from './PersonalBankAccount';
import PersonalDetails from './PersonalDetails';
import PlaidData from './PlaidData';
import Policy from './Policy';
import PolicyCategory from './PolicyCategory';
import PolicyCategory, {PolicyCategories} from './PolicyCategory';
import PolicyMember, {PolicyMembers} from './PolicyMember';
import PolicyTag, {PolicyTags} from './PolicyTag';
import PrivatePersonalDetails from './PrivatePersonalDetails';
Expand All @@ -42,6 +42,7 @@ import SecurityGroup from './SecurityGroup';
import Session from './Session';
import Task from './Task';
import Transaction from './Transaction';
import {TransactionViolation, ViolationName, ViolationType} from './TransactionViolation';
import User from './User';
import UserWallet from './UserWallet';
import WalletAdditionalDetails from './WalletAdditionalDetails';
Expand Down Expand Up @@ -78,6 +79,7 @@ export type {
PlaidData,
Policy,
PolicyCategory,
PolicyCategories,
PolicyMember,
PolicyMembers,
PolicyTag,
Expand All @@ -101,8 +103,11 @@ export type {
Session,
Task,
Transaction,
TransactionViolation,
User,
UserWallet,
ViolationName,
ViolationType,
WalletAdditionalDetails,
WalletOnfido,
WalletStatement,
Expand Down