diff --git a/Fruits 50.1.xlsm b/Fruits 50.1.xlsm new file mode 100644 index 0000000..ffd213a Binary files /dev/null and b/Fruits 50.1.xlsm differ diff --git a/Main.bas b/Main.bas index 7fb6110..2928cf5 100644 --- a/Main.bas +++ b/Main.bas @@ -31,70 +31,95 @@ Sub Main() '1. Add arithmetic operators like +/- '2. Try Macro record to general basic macros +'Tasks +'===== +' +'1. [ ] Add NotEq <> operator +'2. [ ] Understand Str and CStr functions in VBA +' +' +' +'Others +'====== +'1. [ ] Simplify Select Case in RuleEngine +'2. [ ] Improve AND/OR logic + + ' Read values from worksheet and rules + apply rules - - Dim row As RowInfo + Dim engine As New RuleEngine Set engine.Rows = ReadAllRows("Groceries") Set engine.rules = ReadAllRules("Rules") Call engine.Apply - + ' Update category column in Groceries worksheet. + UpdateCategory engine + UpdateSummary engine - ' Updating worksheet based on results + ' MsgBox (GetTotal(New totalProvider, engine)) - +End Sub + +' Updates the category column with update rule results. +Private Sub UpdateCategory(engine As RuleEngine) + + ' Updating worksheet based on results + Dim row As RowInfo Dim ws As Worksheet Set ws = Worksheets("Groceries") Dim r As Range - Dim Total As Double - Dim fruitTotal As Double - fruitTotal = 0 - Total = 0 - - Dim totalFormula As String - totalFormula = "=SUM(" - ' C4,C5,C8 + Dim Total As Double: Total = 0 + For Each row In engine.Rows Set r = ws.Range("A" & row.RowNumber) - 'COLUMN WHERE CATEGORY IS PRINTED - ws.Range("M" & row.RowNumber).ClearContents ws.Range("M" & row.RowNumber).Value = row.Category 'Debug.Print row.Columns("Id") & "=" & row.Category r.Interior.ColorIndex = 0 + + Next +End Sub + +Private Sub UpdateSummary(engine As RuleEngine) + Dim row As RowInfo + Dim pRule As rule + Dim offset As Integer + Dim colHeaders As New Collection + colHeaders.Add ("PriceL") + colHeaders.Add ("PriceB") + + Dim colHeader1 As String + Dim colHeader2 As String + Dim colHeader3 As String + Dim colHeader4 As String + + colHeader1 = Worksheets("Summary").Range("B1").Value + colHeader2 = Worksheets("Summary").Range("C1").Value + ' go through all available rules + For Each pRule In engine.rules + Worksheets("Summary").Range("A2").offset(offset, 0) = pRule.Category + ' Get total for each rule + Dim pTotal1 As Double: pTotal1 = 0 + Dim pTotal2 As Double: pTotal2 = 0 + For Each row In engine.Rows + If row.Category = pRule.Category Then + pTotal1 = pTotal1 + row.Columns(colHeader1) + pTotal2 = pTotal2 + row.Columns(colHeader2) + End If + Next + Worksheets("Summary").Range("B2").offset(offset, 0) = pTotal1 + Worksheets("Summary").Range("C2").offset(offset, 0) = pTotal2 - - If row.Category = "RuleIV" Then - r.Interior.ColorIndex = 6 - Total = Total + row.Columns("PriceL") - totalFormula = totalFormula & ws.Range("C" & row.RowNumber).Address & "," - End If - - If row.Category = "FruitI" Then - r.Interior.ColorIndex = 4 - fruitTotal = fruitTotal + row.Columns("PriceL") - End If - + offset = offset + 1 Next - 'MsgBox total - 'row.PriceL - ws.Range("C19").Value = Total - ws.Range("C20").Value = fruitTotal - totalFormula = Left(totalFormula, Len(totalFormula) - 1) - totalFormula = totalFormula & ")" - - ' check if there were no matching cells for total formula - If totalFormula <> "=SUM)" Then - ws.Range("C21").Value = totalFormula - Else - ws.Range("C21").Value = "" - End If End Sub +'Private Function GetTotal(totalProvider As totalProvider, engine As RuleEngine) As Double +' GetTotal = totalProvider.GetTotal(engine) +'End Function diff --git a/RuleColumn.cls b/RuleColumn.cls index 3d52059..d1bf39f 100644 --- a/RuleColumn.cls +++ b/RuleColumn.cls @@ -8,7 +8,7 @@ Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False -Public Header As String +Public Name As String 'Public Operator As String Public Operator As Operators Public Value As String @@ -16,11 +16,8 @@ Public Value As String Public Link As String Public ReturnValue As String Public ReturnID As String +Public PrintAble As Boolean + -Enum ColumnType - [String] = 1 - [Integer] = 2 - [Double] = 3 -End Enum diff --git a/RuleEngine.cls b/RuleEngine.cls index 3256d4b..ac6f67a 100644 --- a/RuleEngine.cls +++ b/RuleEngine.cls @@ -13,24 +13,14 @@ Public Rows As New Collection Public Sub Apply() - - Dim row As RowInfo Dim rule As rule - For Each row In Rows ' goes through all rows - ' Test all rules against selected row Call ResetRules For Each rule In rules TestRule row, rule ' takes 1 row and apply 1 rule to it Next - -' For Each rule In Rules -' TestRule row, rule, Category -' Next - - Next End Sub @@ -38,8 +28,9 @@ End Sub Sub TestRule(row As RowInfo, rule As rule) Dim ruleCol As RuleColumn - Dim FoundMatch As Boolean - FoundMatch = False + Dim FoundMatch As Boolean: FoundMatch = False ' match in overall rule + Dim NewMatch As Boolean: NewMatch = False ' match in current rule column + Dim Link As String: Link = "" ' Link with next condition in the rule Dim SkipFor As Boolean Dim rule1value As Boolean Dim rule2value As Boolean @@ -53,130 +44,71 @@ Sub TestRule(row As RowInfo, rule As rule) rule1value = False rule2value = False + Dim strRowVal As String + Dim numRowVal As Double + Dim strRuleVal As String + Dim numRuleVal As Double + Dim IsNum As Boolean + If IsNumeric(ruleCol.Value) Then + numRowVal = Val(row.Columns(ruleCol.Name)) + numRuleVal = Val(ruleCol.Value) + IsNum = True + Else + strRowVal = row.Columns(ruleCol.Name) + strRuleVal = ruleCol.Value + IsNum = False + End If + If SkipFor Then GoTo ContinueFor Select Case ruleCol.Operator Case Operators.Eq - 'Debug.Print ruleCol.Name & "=" & row.Columns(ruleCol.Name) - If IsNumeric(ruleCol.Value) Then - If Val(row.Columns(ruleCol.Name)) = Val(ruleCol.Value) Then - - If ruleCol.Link <> "AND" Then - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - Else - 'If Str(row.Columns(ruleCol.Name)) = Str(ruleCol.Value) Then - If row.Columns(ruleCol.Name) = ruleCol.Value Then - If ruleCol.Link <> "AND" Then - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - End If - + If IsNum Then NewMatch = (numRowVal = numRuleVal) Else NewMatch = (strRowVal = strRuleVal) Case Operators.Gt - -' If rule.RuleID = "II" Then -' Debug.Print -' End If - - - If IsNumeric(ruleCol.Value) Then - If Val(row.Columns(ruleCol.Name)) > Val(ruleCol.Value) Then - - If ruleCol.Link = "AND" Then - - Else - - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - Else - If Str(row.Columns(ruleCol.Name)) > Str(ruleCol.Value) Then - - If ruleCol.Link = "AND" Then - - Else - - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - End If - - + If IsNum Then NewMatch = (numRowVal > numRuleVal) Else NewMatch = (strRowVal > strRuleVal) Case Operators.Lt - If IsNumeric(ruleCol.Value) Then - If Val(row.Columns(ruleCol.Name)) < Val(ruleCol.Value) Then - - If ruleCol.Link = "AND" Then - - Else - - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - Else - If Str(row.Columns(ruleCol.Name)) < Str(ruleCol.Value) Then - - If ruleCol.Link = "AND" Then - - Else - - FoundMatch = True - End If - - Else - FoundMatch = False - SkipFor = True - End If - End If - + If IsNum Then NewMatch = (numRowVal < numRuleVal) Else NewMatch = (strRowVal < strRuleVal) + Case Operators.NoEQ + If IsNum Then NewMatch = (numRowVal <> numRuleVal) Else NewMatch = (strRowVal <> strRuleVal) + Case Operators.GtEq + If IsNum Then NewMatch = (numRowVal >= numRuleVal) Else NewMatch = (strRowVal >= strRuleVal) + Case Operators.LtEq + If IsNum Then NewMatch = (numRowVal <= numRuleVal) Else NewMatch = (strRowVal <= strRuleVal) Case Operators.And ' Get Success value of given rule ' E.g. RuleI = True And RuleII = False rule1value = IsRuleSuccess(ruleCol.Name) rule2value = IsRuleSuccess(ruleCol.Value) + NewMatch = rule1value And rule2value - FoundMatch = rule1value And rule2value Case Operators.Or rule1value = IsRuleSuccess(ruleCol.Name) rule2value = IsRuleSuccess(ruleCol.Value) - - FoundMatch = rule1value Or rule2value + NewMatch = rule1value Or rule2value End Select - If FoundMatch Then - 'Debug.Print "Match!" - row.Category = rule.Category - - ' Update Rule - rule.Success = True + ' Evaluate results + If Link = "AND" Then + If FoundMatch And NewMatch Then + row.Category = rule.Category + rule.Success = True + End If + ElseIf Link = "OR" Then + If FoundMatch Or NewMatch Then + row.Category = rule.Category + rule.Success = True + End If Else - 'row.Category = "" + If NewMatch Then + row.Category = rule.Category + rule.Success = True + End If End If + FoundMatch = NewMatch + Link = ruleCol.Link ' Link for next rule column in for loop. ContinueFor: Next @@ -189,6 +121,9 @@ Function IsRuleSuccess(ruleName As String) As Boolean For Each r In rules If ruleName = "Rule" & r.RuleID Then result = r.Success + If result Then + Debug.Print result + End If End If Next IsRuleSuccess = result