Problem situation Warehouse administrator has a sheet ("Record") where every logistics movement performed by the facility is stored, he may filter as needed, although, this is very time consuming and he would like to improve the process in order to calculate inquiries faster, for example: How many "pulp" do we have now (in all racks)? How many pulp do we have now (in rack #5)? Filters are a great tool but, they are somewhat limited to answer these kind of question in matter of seconds.
(opens new window)
Macro solution: The coder knows that autofilters are the best, fast and most reliable solution in these kind of scenarios since the data exists already in the worksheet and the input for them can be obtained easily -in this case, by user input-.
The approach used is to create a sheet called "SmartFilter" where administrator can
easily filter multiple data as needed and calculation will be performed instantly as well.
He uses 2 modules and the Worksheet_Change event for this matter
Code For SmartFilter Worksheet:
PrivateSub Worksheet_Change(ByVal Target As Range)Dim ItemInRange As Range
Const CellsFilters AsString="C2,E2,G2"Call ExcelBusy
ForEach ItemInRange In Target
IfNot Intersect(ItemInRange, Range(CellsFilters))IsNothingThenCall Inventory_Filter
Next ItemInRange
Call ExcelNormal
EndSub
Code for module 1, called "General_Functions"
Sub ExcelNormal()With Excel.Application
.EnableEvents =True.Cursor = xlDefault
.ScreenUpdating =True.DisplayAlerts =True.StatusBar =False.CopyObjectsWithCells =TrueEndWithEndSubSub ExcelBusy()With Excel.Application
.EnableEvents =False.Cursor = xlWait
.ScreenUpdating =False.DisplayAlerts =False.StatusBar =False.CopyObjectsWithCells =TrueEndWithEndSubSub Select_Sheet(NameSheet AsString,Optional VerifyExistanceOnly AsBoolean)OnErrorGoTo Err01Select_Sheet
Sheets(NameSheet).Visible =TrueIf VerifyExistanceOnly =FalseThen' 1. If VerifyExistanceOnly = False
Sheets(NameSheet).Select
Sheets(NameSheet).AutoFilterMode =False
Sheets(NameSheet).Cells.EntireRow.Hidden =False
Sheets(NameSheet).Cells.EntireColumn.Hidden =FalseEndIf' 1. If VerifyExistanceOnly = FalseIf1=2Then'99. If error
Err01Select_Sheet:
MsgBox "Err01Select_Sheet: Sheet "& NameSheet &" doesn't exist!", vbCritical:Call ExcelNormal:OnErrorGoTo-1:EndEndIf'99. If errorEndSubFunction General_Functions_Find_Title(InSheet AsString, TitleToFind AsString,Optional InRange As Range,Optional IsNeededToExist AsBoolean,Optional IsWhole AsBoolean)As Range
Dim DummyRange As Range
OnErrorGoTo Err01General_Functions_Find_Title
If InRange IsNothingThen' 1. If InRange Is NothingSet DummyRange = IIf(IsWhole =True, Sheets(InSheet).Cells.Find(TitleToFind, LookAt:=xlWhole), Sheets(InSheet).Cells.Find(TitleToFind, LookAt:=xlPart))Else' 1. If InRange Is NothingSet DummyRange = IIf(IsWhole =True, Sheets(InSheet).Range(InRange.Address).Find(TitleToFind, LookAt:=xlWhole), Sheets(InSheet).Range(InRange.Address).Find(TitleToFind, LookAt:=xlPart))EndIf' 1. If InRange Is NothingSet General_Functions_Find_Title = DummyRange
If1=2Or DummyRange IsNothingThen'99. If error
Err01General_Functions_Find_Title:If IsNeededToExist =TrueThen MsgBox "Err01General_Functions_Find_Title: Ttile '"& TitleToFind &"' was not found in sheet '"& InSheet &"'", vbCritical:Call ExcelNormal:OnErrorGoTo-1:EndEndIf'99. If errorEndFunction
Code for module 2, called "Inventory_Handling"
Const TitleDesc AsString="DESCRIPTION"Const TitleLocation AsString="LOCATION"Const TitleActn AsString="ACTION"Const TitleQty AsString="QUANTITY"Const SheetRecords AsString="Record"Const SheetSmartFilter AsString="SmartFilter"Const RowFilter AsLong=2Const ColDataToPaste AsLong=2Const RowDataToPaste AsLong=7Const RangeInResult AsString="K1"Const RangeOutResult AsString="K2"Sub Inventory_Filter()Dim ColDesc AsLong: ColDesc = General_Functions_Find_Title(SheetSmartFilter, TitleDesc, IsNeededToExist:=True, IsWhole:=True).Column
Dim ColLocation AsLong: ColLocation = General_Functions_Find_Title(SheetSmartFilter, TitleLocation, IsNeededToExist:=True, IsWhole:=True).Column
Dim ColActn AsLong: ColActn = General_Functions_Find_Title(SheetSmartFilter, TitleActn, IsNeededToExist:=True, IsWhole:=True).Column
Dim ColQty AsLong: ColQty = General_Functions_Find_Title(SheetSmartFilter, TitleQty, IsNeededToExist:=True, IsWhole:=True).Column
Dim CounterQty AsLongDim TotalQty AsLongDim TotalIn AsLongDim TotalOut AsLongDim RangeFiltered As Range
Call Select_Sheet(SheetSmartFilter)If Cells(Rows.Count, ColDataToPaste).End(xlUp).Row > RowDataToPaste -1Then Rows(RowDataToPaste &":"& Cells(Rows.Count,"B").End(xlUp).Row).Delete
Sheets(SheetRecords).AutoFilterMode =FalseIf Cells(RowFilter, ColDesc).Value <>""Or Cells(RowFilter, ColLocation).Value <>""Or Cells(RowFilter, ColActn).Value <>""Then' 1. If Cells(RowFilter, ColDesc).Value <> "" Or Cells(RowFilter, ColLocation).Value <> "" Or Cells(RowFilter, ColActn).Value <> ""With Sheets(SheetRecords).UsedRange
If Sheets(SheetSmartFilter).Cells(RowFilter, ColDesc).Value <>""Then.AutoFilter Field:=General_Functions_Find_Title(SheetRecords, TitleDesc, IsNeededToExist:=True, IsWhole:=True).Column, Criteria1:=Sheets(SheetSmartFilter).Cells(RowFilter, ColDesc).Value
If Sheets(SheetSmartFilter).Cells(RowFilter, ColLocation).Value <>""Then.AutoFilter Field:=General_Functions_Find_Title(SheetRecords, TitleLocation, IsNeededToExist:=True, IsWhole:=True).Column, Criteria1:=Sheets(SheetSmartFilter).Cells(RowFilter, ColLocation).Value
If Sheets(SheetSmartFilter).Cells(RowFilter, ColActn).Value <>""Then.AutoFilter Field:=General_Functions_Find_Title(SheetRecords, TitleActn, IsNeededToExist:=True, IsWhole:=True).Column, Criteria1:=Sheets(SheetSmartFilter).Cells(RowFilter, ColActn).Value
'If we don't use a filter we would need to use a cycle For/to or For/Each Cell in range'to determine whether or not the row meets the criteria that we are looking and then'save it on an array, collection, dictionary, etc'IG: For CounterRow = 2 To TotalRows'If Sheets(SheetSmartFilter).Cells(RowFilter, ColDesc).Value <> "" and Sheets(SheetRecords).cells(CounterRow,ColDescInRecords).Value= Sheets(SheetSmartFilter).Cells(RowFilter, ColDesc).Value then'Redim Preserve MyUnecessaryArray(UnecessaryNumber) ''Save to array: (UnecessaryNumber)=MyUnecessaryArray. Or in a dictionary, etc. At the end, we would transpose this values into the sheet, at the end'both are the same, but, just try to see the time invested on each logic.If.Cells(1,1).End(xlDown).Value <>""ThenSet RangeFiltered =.Rows("2:"& Sheets(SheetRecords).Cells(Rows.Count,"A").End(xlUp).Row).SpecialCells(xlCellTypeVisible)'If it is not <>"" means that there was not filtered data!If RangeFiltered IsNothingThen MsgBox "Err01Inventory_Filter: No data was found with the given criteria!", vbCritical:Call ExcelNormal:End
RangeFiltered.Copy Destination:=Cells(RowDataToPaste, ColDataToPaste)
TotalQty = Cells(Rows.Count, ColQty).End(xlUp).Row
For CounterQty = RowDataToPaste +1To TotalQty
If Cells(CounterQty, ColActn).Value ="In"Then' 2. If Cells(CounterQty, ColActn).Value = "In"
TotalIn = Cells(CounterQty, ColQty).Value + TotalIn
ElseIf Cells(CounterQty, ColActn).Value ="Out"Then' 2. If Cells(CounterQty, ColActn).Value = "In"
TotalOut = Cells(CounterQty, ColQty).Value + TotalOut
EndIf' 2. If Cells(CounterQty, ColActn).Value = "In"Next CounterQty
Range(RangeInResult).Value = TotalIn
Range(RangeOutResult).Value =-(TotalOut)EndWithEndIf' 1. If Cells(RowFilter, ColDesc).Value <> "" Or Cells(RowFilter, ColLocation).Value <> "" Or Cells(RowFilter, ColActn).Value <> ""EndSub
Testing and results:
(opens new window) As we saw in the previous image, this task has been achieved easily. By using autofilters a solution was provided that just takes seconds to compute, is easy to explain to the user -since s/he is familiar with this command- and took a few lines to the coder.
Remarks
'To use Autofilter within VBA we need to call with at least the following parameters: