Attribute VB_Name = "Module1" Option Explicit ' Главный субрутине Sub DistributeItemsToSets() Dim wsMap As Worksheet, wsItems As Worksheet, wsSets As Worksheet Dim wsOut As Worksheet, wsErr As Worksheet Dim lastRow As Long Dim dictItems As Object ' ITEM_GTIN -> Collection of item codes available Dim dictSets As Object ' SET_GTIN -> Collection of set codes available Dim dictMap As Object ' SET_GTIN -> Collection of (ITEM_GTIN, COUNT) pairs Dim r As Long, i As Long Dim key As Variant Application.ScreenUpdating = False Application.Calculation = xlCalculationManual ' Листы (поменяйте названия, если нужно) Set wsMap = ThisWorkbook.Worksheets("Map") Set wsItems = ThisWorkbook.Worksheets("Units") Set wsSets = ThisWorkbook.Worksheets("Sets") ' Создать/очистить выходные листы On Error Resume Next Application.DisplayAlerts = False ThisWorkbook.Worksheets("Assignments").Delete ThisWorkbook.Worksheets("Errors").Delete Application.DisplayAlerts = True On Error GoTo 0 Set wsOut = ThisWorkbook.Worksheets.Add wsOut.Name = "Assignments" wsOut.Cells.NumberFormat = "@" wsOut.Range("A1:D1").Value = Array("SET_GTIN", "SET_CODE", "ITEM_GTIN", "ITEM_CODE") Set wsErr = ThisWorkbook.Worksheets.Add wsErr.Name = "Errors" wsErr.Cells.NumberFormat = "@" wsErr.Range("A1:D1").Value = Array("ITEM_GTIN", "NEEDED", "AVAILABLE", "MISSING") ' Инициализация словарей Set dictItems = CreateObject("Scripting.Dictionary") Set dictSets = CreateObject("Scripting.Dictionary") Set dictMap = CreateObject("Scripting.Dictionary") ' ---- 1) Считать Items -> dictItems lastRow = wsItems.Cells(wsItems.Rows.Count, "A").End(xlUp).Row For r = 2 To lastRow Dim itemCode As String, itemGTIN As String itemCode = Trim(CStr(wsItems.Cells(r, 1).Text)) itemGTIN = Trim(CStr(wsItems.Cells(r, 2).Text)) If itemGTIN <> "" And itemCode <> "" Then If Not dictItems.Exists(itemGTIN) Then dictItems.Add itemGTIN, New Collection End If dictItems(itemGTIN).Add itemCode End If Next r ' ---- 2) Считать Sets -> dictSets lastRow = wsSets.Cells(wsSets.Rows.Count, "A").End(xlUp).Row For r = 2 To lastRow Dim setCode As String, setGTIN As String setCode = Trim(CStr(wsSets.Cells(r, 1).Text)) setGTIN = Trim(CStr(wsSets.Cells(r, 2).Text)) If setGTIN <> "" And setCode <> "" Then If Not dictSets.Exists(setGTIN) Then dictSets.Add setGTIN, New Collection End If dictSets(setGTIN).Add setCode End If Next r ' ---- 3) Считать Map -> dictMap ' Предполагаются столбцы: A - set gtin, D - ITEM GTIN, E - COUNT (поменяйте индексы если разные) lastRow = wsMap.Cells(wsMap.Rows.Count, "A").End(xlUp).Row For r = 2 To lastRow Dim mapSetGTIN As String, mapItemGTIN As String Dim cnt As Long mapSetGTIN = Trim(CStr(wsMap.Cells(r, 1).Text)) mapItemGTIN = Trim(CStr(wsMap.Cells(r, 4).Text)) cnt = 1 If IsNumeric(wsMap.Cells(r, 5).Value) Then cnt = CLng(wsMap.Cells(r, 5).Value) If mapSetGTIN <> "" And mapItemGTIN <> "" Then If Not dictMap.Exists(mapSetGTIN) Then dictMap.Add mapSetGTIN, New Collection End If ' добавим запись в формате "ITEMGTIN|COUNT" dictMap(mapSetGTIN).Add mapItemGTIN & "|" & CStr(cnt) End If Next r ' ---- 4) Опция: перемешать пулы item-кодов для случайного распределения Dim doShuffle As Boolean doShuffle = False ' <-- поставьте True если хотите случайное распределение If doShuffle Then For Each key In dictItems.Keys dictItems(key) = ShuffleCollection(dictItems(key)) Next key End If ' ---- 5) Основной цикл: для каждого SET_GTIN, для каждого SET_CODE - присвоить items по dictMap Dim outRow As Long: outRow = 2 Dim errRow As Long: errRow = 2 For Each key In dictSets.Keys Dim curSetGTIN As String: curSetGTIN = key If Not dictMap.Exists(curSetGTIN) Then ' Если для данного set gtin нет записей в Map — пропускаем (можно логировать) GoTo NextSetGTIN End If Dim setCodesColl As Collection: Set setCodesColl = dictSets(curSetGTIN) Dim mapColl As Collection: Set mapColl = dictMap(curSetGTIN) Dim si As Long For si = 1 To setCodesColl.Count Dim curSetCode As String: curSetCode = setCodesColl(si) ' Для данного экземпляра набора пройдём по всем itemGTIN,COUNT и отрежем нужное количество Dim mi As Long For mi = 1 To mapColl.Count Dim parts As Variant parts = Split(mapColl(mi), "|") Dim neededItemGTIN As String: neededItemGTIN = parts(0) Dim neededCnt As Long: neededCnt = CLng(parts(1)) Dim j As Long For j = 1 To neededCnt If dictItems.Exists(neededItemGTIN) Then Dim col As Collection: Set col = dictItems(neededItemGTIN) If col.Count > 0 Then ' взять первый элемент Dim takeCode As String takeCode = CStr(col(1)) ' записать в результат wsOut.Cells(outRow, 1).Value = CStr(curSetGTIN) wsOut.Cells(outRow, 2).Value = CStr(curSetCode) wsOut.Cells(outRow, 3).Value = CStr(neededItemGTIN) wsOut.Cells(outRow, 4).Value = CStr(takeCode) outRow = outRow + 1 ' удалить использованный код из пула RemoveAtCollection col, 1 Else ' Нету доступных — лог ошибки (недостаёт 1) LogError wsErr, errRow, neededItemGTIN, 1, 0 errRow = errRow + 1 End If Else ' Нет вообще пула для этого ITEM GTIN LogError wsErr, errRow, neededItemGTIN, neededCnt, 0 errRow = errRow + 1 Exit For End If Next j Next mi Next si NextSetGTIN: Next key ' ---- 6) После распределения можно вывести оставшиеся в пуле (неиспользованные) ' (опционально) — закомментировал Application.Calculation = xlCalculationAutomatic Application.ScreenUpdating = True MsgBox "Готово. Результат в листе 'Assignments'. Ошибки — в листе 'Errors' (если есть).", vbInformation End Sub ' Утилита: удалить элемент по индексу из Collection Sub RemoveAtCollection(ByRef coll As Collection, ByVal idx As Long) Dim i As Long Dim tmp As Collection Set tmp = New Collection For i = 1 To coll.Count If i <> idx Then tmp.Add coll(i) Next i ' очистить исходную и переложить элементы обратно Do While coll.Count > 0 coll.Remove 1 Loop For i = 1 To tmp.Count coll.Add tmp(i) Next i End Sub ' Утилита: логирование ошибки (недостаёт) Sub LogError(ws As Worksheet, ByVal ByRefRow As Long, itemGTIN As String, needed As Long, available As Long) ws.Cells.NumberFormat = "@" ws.Cells(ByRefRow, 1).Value = CStr(itemGTIN) ws.Cells(ByRefRow, 2).Value = CStr(needed) ws.Cells(ByRefRow, 3).Value = CStr(available) ws.Cells(ByRefRow, 4).Value = CStr(needed - available) End Sub ' Утилита: перемешать Collection -> возвращает новую Collection Function ShuffleCollection(collIn As Collection) As Collection Dim arr() As Variant Dim n As Long, i As Long n = collIn.Count ReDim arr(1 To n) For i = 1 To n arr(i) = collIn(i) Next i Dim rndIndex As Long, tmp As Variant Randomize For i = n To 2 Step -1 rndIndex = Int(Rnd() * i) + 1 tmp = arr(i) arr(i) = arr(rndIndex) arr(rndIndex) = tmp Next i Dim outColl As Collection Set outColl = New Collection For i = 1 To n outColl.Add arr(i) Next i Set ShuffleCollection = outColl End Function