using BusinessLogic.Entities; using BusinessLogic.Enums; using BusinessLogic.Extensions; using BusinessLogic.Interfaces.Services; using log4net; using System; using System.Collections.Generic; using System.Linq; namespace BusinessLogic.ImportProcessing { public class RuleEngine : IRuleEngine { private readonly ILog _log; private readonly IImportProcessingRuleService _importProcessingRuleService; private readonly IOperations _operations; private IList _rules = new List(); private ProcessedTransaction _transaction; private bool _groupResult = false; private bool _processRuleActions = false; public RuleEngine(ILog log, IImportProcessingRuleService importProcessingRuleService, IOperations operations) { _log = log; _importProcessingRuleService = importProcessingRuleService; _operations = operations; } public ProcessedTransaction Process(ProcessedTransaction transaction, int importTypeId) { LoadImportTypeRules(importTypeId); return Process(transaction); } public ProcessedTransaction Process(ProcessedTransaction transaction) { _transaction = transaction; LoadGlobalRules(); ProcessRules(); return transaction; } private void LoadImportTypeRules(int importTypeId) { var transactionImportTypeRules = _importProcessingRuleService.GetByImportType(importTypeId); _rules = _rules.Union(transactionImportTypeRules).ToList(); } private void LoadGlobalRules() { var globalRules = _importProcessingRuleService.Search(new Models.ImportProcessingRule.SearchCriteria() { IsGlobal = true }); _rules = _rules.Union(globalRules.Items).ToList(); } private void ProcessRules() { foreach (var rule in _rules) { InitialiseRuleCheck(); CheckTheRule(rule); ProcessActions(rule.Actions); } } private void InitialiseRuleCheck() { _processRuleActions = false; } private void CheckTheRule(ImportProcessingRule rule) { if(rule.Conditions.IsNullOrEmpty()) { _processRuleActions = true; return; } foreach (var group in rule.Groups()) { InitialiseGroupProcessing(); ProcessGroup(rule.GroupConditions(group)); // If we know we need to process the actions there is no point processing // any more groups as the groups are combined using OR logic if (_processRuleActions) break; } } private void InitialiseGroupProcessing() { _groupResult = false; } private void ProcessGroup(IOrderedEnumerable groupConditions) { foreach (var condition in groupConditions) { ProcessCondition(condition); } if (_groupResult) _processRuleActions = true; } private void ProcessCondition(ImportProcessingRuleCondition condition) { var operation = GetOperation(condition); var operationArgs = GetOperationArgs(condition); var result = operation(operationArgs); ProcessResult(result, condition); } private Func GetOperation(ImportProcessingRuleCondition condition) { IDictionary> operations = new Dictionary>(); switch (condition.Field.Type) { case FieldType.Text: operations = _operations.TextOperators; break; case FieldType.Decimal: operations = _operations.TextOperators; break; default: throw new NotImplementedException($"The field type '{condition.Field.Type}' has not been implemented"); } return operations[(OperationType)condition.ImportProcessingRuleOperatorId]; } private OperationArgs GetOperationArgs(ImportProcessingRuleCondition condition) { return new OperationArgs() { FieldValue = _transaction.GetPropertyValue(condition.Field.Name), Value = condition.Value }; } private void ProcessResult(bool result, ImportProcessingRuleCondition condition) { switch (condition.LogicalOperator) { case null: _groupResult = result; break; case LogicalOperationType.And: _groupResult = _groupResult && result; break; case LogicalOperationType.Or: _groupResult = _groupResult || result; break; default: throw new NotImplementedException($"The logical operation '{condition.LogicalOperator}' is not yet implemented"); } } private void ProcessActions(ICollection actions) { if (!_processRuleActions) return; foreach (var action in actions) { _transaction.SetPropertyValue(action.Field.Name, action.Value); } } } public class OperationArgs { public dynamic FieldValue { get; set; } public dynamic Value { get; set; } } }