Sort and replace macro

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Sort and replace macro

Post by Rise »

Well, this is part three of the macro trilogy. I'm officially in over my head but at this point there's no turning back! :flee:

The goal of this post is to at least properly convey what I want to do to avoid driving a certain clog wearing Office guru nuts :innocent:

I have an inventory spreadsheet that tracks rack locations. The system works like this: fork truck drivers move pallets of parts in and out of a rack. They write down on a log sheet 5 pieces of info: part number, quantity, rack location, date, and whether it is going into the rack, or out. At the end of their shift, they end up with a list of rack moves both IN and OUT. A data entry person enters the list into a spreadsheet, into the "Log_Sheet_Entry" sheet. The info she enters go into the columns "Product", "IN or OUT", "Date", "Quantity", "Rack Location". That's where it stops, for now.

What then needs to be done is updating of the inventory sheet in the spreadsheet. The inventory sheet has the same columns, with the exception of "IN or OUT" since it only tracks what is currently IN the racks. This then gets handed to the fork truck drivers so they can find stuff in lieu of a barcode system (for now).

What I want to be able to do next is the tricky part. I want to automatically update the inventory based on the "Log_Sheet_Entry" line items. But it's not completely straight forward since there are different scenarios that I need to be prepared for. Here is the logic I've come up with so far (psuedo code):

Code: Select all

SORT LOG_SHEET_ENTRY DATA BY DATE, THEN SORT SO THAT "OUT" IS THE FIRST STATUS ENCOUNTERED FOR EACH DATE

FOR LOOP FROM LOG_SHEET_ENTRY ROW = 2 TO LASTROW IN LOG_SHEET_ENTRY
RACK = LOG_SHEET_ENTRY.ROW.RACK VALUE 'E.G. CX1
SEARCH INVENTORY FOR RACK
IF INVRACK MATCH IS FOUND
  IF STATE = "IN"
    IF ROW.PRODUCT = INVPRODUCT
      IF INVQTY = QUANTITY THEN
        IGNORE QTY
        INVDATE.VALUE = DATE 'INSERT UPDATED DATE
      ELSE
        UPDATE INVQTY = QUANTITY
        INVDATE = DATE
      END IF
      LOG_SHEET_ENTRY.ROW.DELETE
      EXIT SUB
    ELSE
      INVPRODUCT = ROW.PRODUCT
      INVQTY = ROW.QUANTITY
      INVDATE = DATE
      LOG_SHEET_ENTRY.ROW.DELETE
      EXIT SUB
    END IF
    ROW.ADD PRODUCT, QUANTITY, DATE
  ELSE
    LOG_SHEET_ENTRY.ROW.DEL
ELSE
  INVENTORY.ROW.ADD RACK, PRODUCT, QUANTITY, DATE
END IF
NEXT ROW
I will now attempt to hopefully clarify what that psuedo code is looking to do. We start at row 2 on the Log_Sheet_Entry sheet which is the first row of actual data that has been entered. We take the Rack Location from Log_Sheet_Entry and go try and find it in the Inventory sheet. If the "In or Out" status is "Out" we delete the matching row if it exists. It's posible it doesn't exist though due to the manual nature of the system so if there's nothing found, we just delete that line from the Log_Sheet_Entry sheet and move on to the next row. If the status is "In" we still need to search for the row first to make sure that we aren't doubling up on our rack inventory. So again, we search for the Rack Location in our Inventory sheet and if we don't find any matches, great! We just add the data line from the Log_Sheet_Entry sheet to the Inventory sheet and remove the line from Log_Sheet_Entry and move on to the next row of data. If we do find it, we need to do some due diligence. :groan:

So in the case that the rack exists in the inventory sheet and we are trying to add something, the first thing we need to do is see if it's the same Part Number. If it's the same part number, we need to check the Quantity. If the Quantity is different, we update the Quantity field and the Date and we go on to the next row of data in the Log_Sheet_Entry sheet. If it's a different Part Number, we basically just write over it with the next Part Number from the Log_Sheet_Entry data and move on to the next row. Actually that's all we do, maybe it's not that complicated after all :scratch:

After each row, the row on Log_Sheet_Entry is deleted until we have completed the entire list, leaving us with nothing but the 1st row (column labels) intact.

This is what I've got so far by just messing around with the record macro function... which seems to add a lot of bloat to code since it's very specific in what it copies from you.

Code: Select all

Sub Update_Inventory()
'The point of this macro is to quickly update the list so that no one has
'to do the tedious task of updating the inventory list manually.

'log variables are from the log sheet list
Dim logRack As String
Dim logPart As String
Dim logDate As Date
Dim logQty As Integer
'Inv variables are from the Inventory sheet list
Dim InvRack As String
Dim InvPart As String
Dim InvDate As Date
Dim InvQty As Integer
Dim lRow As Integer

'Sort Log sheet based on Date, and then OUT, so that all parts are
'taken out of the system first to avoid taking out items that should
'actually be put in. for example, part was taken out and put back
'in after consuming a portion, but not all of it, within 1 day.
Range("A1:B4").Select
Range(Selection, Selection.End(xlDown)).Select
lRow = Range(Selection, Selection.End(xlDown)).Select 'Row count
Range(Selection, Selection.End(xlToRight)).Select
ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
    "C2:C83"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
    xlSortNormal
ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
    "B2:B83"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
    xlSortNormal
With ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort
    .SetRange Range("A1:E83")
    .Header = xlYes
    .MatchCase = False
    .Orientation = xlTopToBottom
    .SortMethod = xlPinYin
    .Apply
End With
ActiveWindow.SmallScroll Down:=-15
Range("H3").Select

'Start going through the log sheet list to add and remove inventory items
Dim xRow As Integer
Dim lRow As Integer
'lRow = end of list
For xRow = 2 To lRow
    'Find xRow.logRack in inventory sheet
    Range("E2").Select
    Selection.Copy
    Sheets("Inventory").Select
    Cells.Find(What:="CAL5", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
        :=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
        False, SearchFormat:=False).Activate
    Sheets("Log_Sheet_Entry").Select
    Range("E3").Select
    Application.CutCopyMode = False
    Selection.Copy
    Sheets("Inventory").Select
    Cells.Find(What:="FZ5", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
        xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
        , SearchFormat:=False).Activate
    Sheets("Log_Sheet_Entry").Select
    Range("E4").Select
    Application.CutCopyMode = False
    Selection.Copy
    Sheets("Inventory").Select
    Cells.Find(What:="CW4", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
        xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
        , SearchFormat:=False).Activate
    Range("C190:F190").Select
    Range("F190").Activate

End Sub
So I have the extremely messy basics of what I'm looking to do (recorded sort, recorded find), but I don't know how to structure the For, Next statement. I also don't know if I should try to do nested if's or maybe a Select case would be easier to work with - and that's what I'm looking for help on. I think I might be able to get it going from there.

Sorry for the super long post, but hopefully everything is at least clear on what I'm trying to do!

-John

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

Could you post a copy of your workbook with some dummy data? It's impossible - for me at least - to understand what you're doing just from your description.
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

Sure, here's the inventory sheet & data entry sheet in it's current state. There's no sensitive data in this sheet.
dummydata.xlsm
You do not have the required permissions to view the files attached to this post.

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

I notice that there are multiple rows with the same rack location in the Inventory sheet. How should we handle these while searching?
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

Basically, in some instances they stack parts on top of each other which creates that issue. I was thinking of just removing the ability for them to do that which would solve the issue (there is plenty of rack space for them not to).

Let's just assume that they can't do that in the future.

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

See if this does what you want. Try it out on a copy of your workbook!

Code: Select all

Sub Update_Inventory()
  Dim wshLog As Worksheet
  Dim wshInv As Worksheet
  Dim s As Long
  Dim n As Long
  Dim strRack As String
  Dim rngMatch As Range
  Application.ScreenUpdating = False
  ' Last row in inventory sheet
  Set wshInv = Worksheets("Inventory")
  n = wshInv.Cells.Find(What:="*", LookIn:=xlValues, _
    Lookat:=xlWhole, SearchOrder:=xlByRows, _
    SearchDirection:=xlPrevious).Row
  ' Last row in log sheet
  Set wshLog = Worksheets("Log_Sheet_Entry")
  ' Loop
  Do While wshLog.Range("E2") <> ""
    ' Rack location
    strRack = wshLog.Range("E2").Value
    ' Inout status
    Select Case UCase(wshLog.Range("B2"))
      Case "IN"
        ' Try to find match
        Set rngMatch = wshInv.Range("F:F").Find(What:=strRack, _
          LookIn:=xlValues, Lookat:=xlWhole, MatchCase:=False)
        If rngMatch Is Nothing Then
          ' Not found, so add row to inventory sheet
          n = n + 1
          wshInv.Range("A" & n).Formula = "=G" & n
          wshInv.Range("B" & n).Formula = "=F" & n
          wshInv.Range("C" & n).Value = wshLog.Range("A2").Value
          wshInv.Range("D" & n).Value = wshLog.Range("C2").Value
          wshInv.Range("E" & n).Value = wshLog.Range("D2").Value
          wshInv.Range("F" & n).Value = wshLog.Range("E2").Value
          wshInv.Range("G" & n).FormulaArray = "=1*MID(C" & n & _
            ",MATCH(FALSE,ISERROR(1*MID(C" & n & _
            ",ROW($1:$10),1)),0),10)"
        Else
          ' Found, so update inventory row
          s = rngMatch.Row
          wshInv.Range("C" & s).Value = wshLog.Range("A2").Value
          wshInv.Range("D" & s).Value = wshLog.Range("C2").Value
          wshInv.Range("E" & s).Value = wshLog.Range("D2").Value
        End If
      Case "OUT"
        ' Try to find match
        Set rngMatch = wshInv.Range("F:F").Find(What:=strRack, _
          LookIn:=xlValues, Lookat:=xlWhole, MatchCase:=False)
        If rngMatch Is Nothing Then
          ' Not found, just ignore
        Else
          ' Found, so delete inventory row
          rngMatch.EntireRow.Delete
          n = n - 1
        End If
    End Select
    ' Delete log row
    wshLog.Range("A2").EntireRow.Delete
  Loop
  Application.ScreenUpdating = True
End Sub
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

is there a way to run it step by step to see what happens in the spreadsheet?

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

Yes:
- Activate the Visual Basic Editor (Alt+F11)
- Arrange the Excel and VBE windows so that you can see both.
- Click anywhere in the Update_Inventory macro.
- Each time you press F8, a single step will be executed.
- If you want to let the macro continue uninterruptedly, press F5.
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

wow that's awesome. yeah it does everything I want - I just need to add the sort of the Log_Sheet_Entry table before the macro runs.

Hans, do you happen to need brake pads? :thankyou:

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

Rise wrote:Hans, do you happen to need brake pads?
Do you think I'm posting too fast? :grin:
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

HansV wrote:
Rise wrote:Hans, do you happen to need brake pads?
Do you think I'm posting too fast? :grin:
If you're not careful, those clogs might catch on fire!

One last thing for this macro - Is my sort function alright? I don't quite understand how to find the last row and implement it properly so I just selected a range that should never be exceeded unless people stopped updating the inventory for a month.

Code: Select all

Sub Update_Inventory()
  Dim wshLog As Worksheet
  Dim wshInv As Worksheet
  Dim s As Long
  Dim n As Long
  Dim strRack As String
  Dim rngMatch As Range
  Dim LRow As Long
  
  Application.ScreenUpdating = False
  ' Last row in inventory sheet
  Set wshInv = Worksheets("Inventory")
  n = wshInv.Cells.Find(What:="*", LookIn:=xlValues, _
    Lookat:=xlWhole, SearchOrder:=xlByRows, _
    SearchDirection:=xlPrevious).Row
  ' Last row in log sheet
  Set wshLog = Worksheets("Log_Sheet_Entry")
  
  ' Sort everything first
  ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
        "C2:C1000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
    ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
        "B2:B1000"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
        xlSortNormal
    With ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort
        .SetRange Range("A1:E1000")
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
    
  ' Loop
  Do While wshLog.Range("E2") <> ""
    ' Rack location
    strRack = wshLog.Range("E2").Value
    ' Inout status
    Select Case UCase(wshLog.Range("B2"))
      Case "IN"
        ' Try to find match
        Set rngMatch = wshInv.Range("F:F").Find(What:=strRack, _
          LookIn:=xlValues, Lookat:=xlWhole, MatchCase:=False)
        If rngMatch Is Nothing Then
          ' Not found, so add row to inventory sheet
          n = n + 1
          wshInv.Range("A" & n).Formula = "=G" & n
          wshInv.Range("B" & n).Formula = "=F" & n
          wshInv.Range("C" & n).Value = wshLog.Range("A2").Value
          wshInv.Range("D" & n).Value = wshLog.Range("C2").Value
          wshInv.Range("E" & n).Value = wshLog.Range("D2").Value
          wshInv.Range("F" & n).Value = wshLog.Range("E2").Value
          wshInv.Range("G" & n).FormulaArray = "=1*MID(C" & n & _
            ",MATCH(FALSE,ISERROR(1*MID(C" & n & _
            ",ROW($1:$10),1)),0),10)"
        Else
          ' Found, so update inventory row
          s = rngMatch.Row
          wshInv.Range("C" & s).Value = wshLog.Range("A2").Value
          wshInv.Range("D" & s).Value = wshLog.Range("C2").Value
          wshInv.Range("E" & s).Value = wshLog.Range("D2").Value
        End If
      Case "OUT"
        ' Try to find match
        Set rngMatch = wshInv.Range("F:F").Find(What:=strRack, _
          LookIn:=xlValues, Lookat:=xlWhole, MatchCase:=False)
        If rngMatch Is Nothing Then
          ' Not found, just ignore
        Else
          ' Found, so delete inventory row
          rngMatch.EntireRow.Delete
          n = n - 1
        End If
    End Select
    ' Delete log row
    wshLog.Range("A2").EntireRow.Delete
  Loop
  Application.ScreenUpdating = True
End Sub

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

You can replace the sort code

Code: Select all

  ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
        "C2:C1000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
    ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
        "B2:B1000"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
        xlSortNormal
    With ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort
        .SetRange Range("A1:E1000")
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
with the following:

Code: Select all

  wshLog.Range("A1").CurrentRegion.Sort _
    Key1:=wshLog.Range("C1"), Order1:=xlAscending, _
    Key2:=wshLog.Range("B1"), Order2:=xlDescending, _
    Header:=xlYes
The use of CurrentRegion will let Excel figure out the range to sort, so that you don't have to specify it explicitly.
Best wishes,
Hans

Rise
StarLounger
Posts: 56
Joined: 07 Jun 2010, 21:36

Re: Sort and replace macro

Post by Rise »

You're amazing. Thanks Hans!

and seriously, let me know if you need brake pads :wink:

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

Thanks, but my bicycle brakes OK.
Best wishes,
Hans

User avatar
Goshute
3StarLounger
Posts: 397
Joined: 24 Jan 2010, 19:43
Location: Salt Lake City, Utah, USA

Re: Sort and replace macro

Post by Goshute »

HansV wrote:You can replace the sort code

Code: Select all

  ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("Log_Sheet_Entry").Sort.SortFields.Add Key:=Range( _
        "C2:C1000"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
        <etc> ...
        .Apply
    End With
with the following:

Code: Select all

  wshLog.Range("A1").CurrentRegion.Sort _
    Key1:=wshLog.Range("C1"), Order1:=xlAscending, _
    Key2:=wshLog.Range("B1"), Order2:=xlDescending, _
    Header:=xlYes
Hans, I'm jumping on this thread to ask a more general sort question - am I correct in understanding that your proposal is to replace code using the 2007+ Sort Object with code using the older Sort Method?

I'm working through coding a sort and ran into this issue: the older Sort Method is specifically a Range.Sort, where the parent of the new 2007+ Sort Object appears to a Worksheet. Using the 2007+ Sort Object appears to require that, even though I have a Typed Range I wish to sort, I have write it with a reference to the Range.Parent like this:

Code: Select all

With rngAllData.Parent.Sort ' < note reference to Range.Parent, which is the Range's workbook
    With .SortFields
      .Clear
      .Add Key:=Intersect(rngCol1, rngAllData), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
      .Add Key:=Intersect(rngCol2, rngAllData), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
    End With
    .SetRange rngAllData
    .Header = xlYes
    .MatchCase = False
    .Orientation = xlTopToBottom
    .Apply
  End With
...but perhaps I'm not following how the Sort Object works.
Goshute
I float in liquid gardens

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

I replaced the Sort object with the Sort method because I still use Excel 2003 at work, so I have to write code that will work in both Excel 2003 and 2007. And to be honest, I'm not familiar with the Sort object yet...

The Sort object is a property of the AutoFilter, ListObject, QueryTable and Worksheet objects, but not of the Range object (that would obviously have interfered with the Sort method of the Range object).
Best wishes,
Hans

User avatar
Goshute
3StarLounger
Posts: 397
Joined: 24 Jan 2010, 19:43
Location: Salt Lake City, Utah, USA

Re: Sort and replace macro

Post by Goshute »

HansV wrote:... I have to write code that will work in both Excel 2003 and 2007.
That's an issue for me also, I think I'll revert to the Range.Sort Method.
HansV wrote:The Sort object is a property of the AutoFilter, ListObject, QueryTable and Worksheet objects, but not of the Range object ...
Thanks, the 2007 Help was not clear on this (or I didn't understand it).

(Brakes still good? :smile:)
Last edited by Goshute on 30 Jul 2010, 13:56, edited 2 times in total.
Goshute
I float in liquid gardens

User avatar
HansV
Administrator
Posts: 78485
Joined: 16 Jan 2010, 00:14
Status: Microsoft MVP
Location: Wageningen, The Netherlands

Re: Sort and replace macro

Post by HansV »

Goshute wrote:(Brakes still good? :smile:)
I hope so - I haven't used my bike this last month...
Best wishes,
Hans