Forum Discussion
Client list to not move order
Wondering if this is even possible. So let’s say I have a master client list on one tab and I have a tab for each employee. On the master list tab, I have a column where I assign the client to an employee. I used filter to place their assigned client list on each employees tab automatically. Then each employees tab will be filled out with the appropriate info for each client.
So my issue is this, if I have to move a client to a different employee by changing it on the main tab.. that new client ends up in a middle row and the previous data entered for the other clients are now no longer in the appropriate row for the client,.. in both the old employees lists and the new employees list. My goal is for that list not to change order and maybe add to the bottom of the filtered list. I came across this before for work and ended up doing a work around which worked for that but I can’t use the same workaround this time.
Anyone smarter than me know an easy fix/ formula or a work around I haven’t thought of?
1 Reply
- SnowMan55Bronze Contributor
Let's see if I understand this situation by checking a few points:
- The master Client List contains one row per client.
- If the client name is not unique (e.g., there are multiple "John Smith" clients), the master Client List row contains a column containing some other identifier that is unique.
- The employee-specific Client Lists currently contain just one row per client -- for clients either currently assigned or previously assigned. (But see my later note on my implementation.)
- The rows in the employee-specific Client List contain the client's name/identifier only as the result of your FILTER formula(s).
- All client-related data in the employee lists other than the name/identifier is entered manually.
- You want the order of entries (rows) in each employee's Client List to be retained as-now, with newly-assigned/reassigned clients to be added at the bottom.
If any of those statements are incorrect, let us know.
It seems that you need to segregate the uniqueness of the clients (master Client List) from their possible recurrence because of the employee reassignments. I propose an Assignment History List, on its own worksheet. This sheet would contain at a minimum columns for Client Name (or ID) and Employee Name and a Flag, but preferably include also Assignment Date (values do not have to be precise); rows would be ordered by Assignment Date. The employees' lists can be extended based on the content of this new list. The order of entries on the Assignment History list controls the order of entries on the employees' lists; the order of entries on the master list becomes irrelevant.
You did not identify which version of Excel you are using. VBA code is ~required, to automatically generate rows in the Assignment History List as you make reassignments, and VBA requires a desktop version of Excel. The Worksheet_Change event handler is the key asset with which to implement this.
Of course, using VBA introduces potential security issues. If yours is a single-user workbook, then that's less of a concern.
Note that in my implementation (code shown below, and in the attached workbook):
⦁ Upon reassignment, an indicator will appear for the client's row in the Client List for the previously-assigned employee. That's making use of the Flag column noted earlier. I use conditional formatting to highlight those indicators for the reassigned clients.
⦁ If a client were assigned to an employee, that employee's Client List would get an entry (as you would expect). If that client were reassigned to another employee, that client row would show a reassignment indicator (as noted above). And if that client were reassigned back to the employee again, a second row the client row would be added. If that's not acceptable, more VBA coding (which I probably will not supply) and different row management would be required.
⦁ If the client changes their name, you'll need to consider whether to update the name across existing data, or to create a new row in the master list as though this were a new client, and "de-assign" the old client name for the employee that is currently assigned.If that solution appears good to you, you can try out the attached workbook. But if you are suspicious of the content (fair enough):
- Copy the code shown below into an editor (e.g., Notepad, WordPad, Word).
- Review the code to satisfy yourself that it does what you expect, and that it does not contain malware. (The code does not access other files or workbooks, and makes no API calls.) Get assistance if needed.
- Make a test copy of your workbook.
- Based on your existing data, create and populate the Assignment History worksheet; its name is your choice. Save your work.
- Back to the code, in the Const[ant] lines at the top, change the worksheet name literals, column literals, and row literals as appropriate.
- Right-click the tab of the worksheet that contains your master client list. Select View Code. This open the VBA editor. Paste all of the edited code into the code editor. (If this results in two "Option Explicit" statements, remove one.) Again, save the copy.
Then you are ready to test/experiment
Option Explicit '==== CONSTANTS '---- Workbook structure: Private Const strWORKSHEET_CLIENT_MASTER = "Clients" Private Const strCM_COL_CLIENT_NAME_OR_ID = "A" Private Const strCM_COL_ASSIGNED_EMPEE = "C" Private Const in4CM_ROW_FIRST_CLIENT As Long = 2 ' Private Const strWORKSHEET_ASSIGNMENT_HISTORY = "AssignmentHistory" Private Const strAH_COL_CLIENT_NAME_OR_ID = "A" Private Const strAH_COL_ASSIGNED_EMPEE = "B" Private Const strAH_COL_ASSIGNMT_DATE = "C" Private Const strAH_COL_FLAG = "D" Private Const in4AH_ROW_FIRST_CLIENT As Long = 2 Private Sub Worksheet_Change(ByVal Target As Range) Dim objThisWorksheet As Worksheet Dim strRangeForAssignedEmpee As String Dim objRangeForAssignedEmpee As Range Dim objEditedAssignments As Range ' Dim objCell As Range '---- Populate an object for this worksheet. Set objThisWorksheet = ActiveSheet '...which likely works; but because one sheet's code can change ' another sheet, this is better: Set objThisWorksheet = Sheets(strWORKSHEET_CLIENT_MASTER) CheckForChangesInAssignedEmpee: '---- Determine the range of possible Assigned Employee cells. strRangeForAssignedEmpee = strCM_COL_ASSIGNED_EMPEE & ":" _ & strCM_COL_ASSIGNED_EMPEE 'We don't need to exclude rows for _ the column header or empty rows, because they have no corres- _ ponding client, and later code will handle them differently. Set objRangeForAssignedEmpee = objThisWorksheet.Range( _ strRangeForAssignedEmpee) '---- Identify the Assigned Employee cells that were changed... Set objEditedAssignments = Intersect(objRangeForAssignedEmpee _ , Target) If objEditedAssignments Is Nothing Then GoTo CheckForChangesInNextSetOfData End If ' -- ...and process them individually. For Each objCell In objEditedAssignments Call ProcessPossibleReassignment(objCell) Next objCell CheckForChangesInNextSetOfData: '...as needed WkshtChange_Exit: Set objCell = Nothing Set objEditedAssignments = Nothing Set objRangeForAssignedEmpee = Nothing Set objThisWorksheet = Nothing Exit Sub End Sub Private Sub ProcessPossibleReassignment(ByVal EditedCell As Range) ' This procedure does the processing for changes in the value of the ' specified spreadsheet cell, which should/will contain Assigned ' Employee identifiers (presumed to be names). The changes will ' typically be initial assignments, but may also be reassignments ' or de-assignments (including removal of junk data). Dim in4CMRow As Long Dim strNewAssignee As String Dim strClientName As String Dim objAssigneeWksht As Worksheet ' Dim objAssignmtHistWksht As Worksheet Dim in4AHRow As Long Dim strAHRow As String Dim in4AHLastRow As Long Dim in4AHLatestRowForClient As Long Dim strPriorAssignee As String ' Dim strMessage As String Dim in4UserResponse As VbMsgBoxResult '---- Capture info from/about this cell... in4CMRow = EditedCell.Row If in4CMRow < in4CM_ROW_FIRST_CLIENT Then '...a column header is being changed. Allow that. Exit Sub End If ' strNewAssignee = EditedCell.Value ' strClientName = EditedCell.Worksheet.Range(strCM_COL_CLIENT_NAME_OR_ID _ & CStr(in4CMRow)).Value ' -- ...and about a related worksheet which may or may not exist: On Error Resume Next Set objAssigneeWksht = Sheets(strNewAssignee) On Error GoTo 0 '---- Handle some bad entries. If strNewAssignee <> "" _ And strClientName = "" Then strMessage = "You are entering a value into a cell in row " _ & CStr(in4CMRow) & " that is reserved for employee assignments." _ & vbCrLf & vbCrLf & "Are you sure you want to do that?" in4UserResponse = MsgBox(strMessage, vbExclamation Or _ vbYesNo Or vbDefaultButton2) If in4UserResponse = vbNo Then ' Remove the entry. Application.EnableEvents = False EditedCell.Value = "" Application.EnableEvents = True End If GoTo ProcessPossReassign_Exit End If ' -- If strNewAssignee <> "" _ And objAssigneeWksht Is Nothing Then strMessage = "There is no worksheet for the employee you entered: " _ & strNewAssignee Call MsgBox(strMessage, vbExclamation Or vbOKOnly) ' Remove the entry. Application.EnableEvents = False EditedCell.Value = "" Application.EnableEvents = True ' GoTo ProcessPossReassign_Exit End If ' -- If an invalid entry is being removed, allow it. If strNewAssignee = "" Then If strClientName = "" Then GoTo ProcessPossReassign_Exit End If End If ' -- Removal of a valid entry will be checked after this... '---- Find the last Assignment History row for this client. Set objAssignmtHistWksht = Sheets(strWORKSHEET_ASSIGNMENT_HISTORY) With objAssignmtHistWksht in4AHLastRow = .Range(strAH_COL_CLIENT_NAME_OR_ID & in4AH_ROW_FIRST_CLIENT _ ).End(xlDown).Row For in4AHRow = in4AHLastRow To in4AH_ROW_FIRST_CLIENT Step -1 strAHRow = CStr(in4AHRow) If StrComp(.Range(strAH_COL_CLIENT_NAME_OR_ID & strAHRow).Value _ , strClientName, vbTextCompare) = 0 Then 'a case-insensitive comparison in4AHLatestRowForClient = in4AHRow strPriorAssignee = .Range(strAH_COL_ASSIGNED_EMPEE & strAHRow).Value Exit For End If Next in4AHRow 'If in4AHLatestRowForClient = 0 Then '...this is probably a new client. 'End If End With '---- If strNewAssignee = "" Then strMessage = "You are removing the assignment of " & strClientName _ & " to " & strPriorAssignee & ". Chaos may ensue. Are you sure?" in4UserResponse = MsgBox(strMessage, vbExclamation Or vbYesNo Or _ vbDefaultButton2) If in4UserResponse = vbNo Then GoTo ProcessPossReassign_Exit End If End If '---- Mark the latest Assignment History row for this client ' as Reassigned. If in4AHLatestRowForClient <> 0 Then strAHRow = CStr(in4AHLatestRowForClient) With objAssignmtHistWksht.Range(strAH_COL_FLAG & strAHRow) .Value = .Value & "R" End With End If '---- Conditionally create a new Assignment History row. If strNewAssignee <> "" Then With objAssignmtHistWksht in4AHLastRow = in4AHLastRow + 1 strAHRow = CStr(in4AHLastRow) .Range(strAH_COL_CLIENT_NAME_OR_ID & strAHRow).Value = strClientName .Range(strAH_COL_ASSIGNED_EMPEE & strAHRow).Value = strNewAssignee .Range(strAH_COL_ASSIGNMT_DATE & strAHRow).Value = Now .Range(strAH_COL_FLAG & strAHRow).Value = "" End With End If ProcessPossReassign_Exit: Set objAssignmtHistWksht = Nothing Set objAssigneeWksht = Nothing Exit Sub End SubI still think that point #4 is a bad design decision. It leaves your employee-specific data fragile. But once VBA is introduced, it becomes an easy matter to store (possibly discreetly) the client's unique identifier in the employees' worksheets during an assignment. In fact, it might be advisable to store all relevant data in the employees' worksheets and remove the FILTER formulas.