Страницы: 1 2 След.
RSS
Перераспределение товара между складами
 
Добрый день! очень прошу помощи, нужно написать формулу для перераспределения товара между складами.
В таблице две вкладке берут и отдают.
Товар перераспределяется по коду ГРП в первую очередь внутри ТО (по территориальному признаку), далее между всеми точками.
Есть точки, в которых товара нет и им он нужен (вкладка берут), а есть кто не продает товар и от них его необходимо забрать (отдают).
Когда в одной ТТ товар закончился, перемещаем из другой, далее из третий и так пока весь товар из ТТ (отдают) не будет распределен по точкам (берут). Ну или не останется ТТ, которые готовы взять товар.
Прошу помогите
 
Добрый день. Формулами такие задачи не решаются, потребуется макрос. С макросами последнее время есть проблемы, во многих организациях они запрещены. У вас то как?  
 
Цитата
написал:
во многих организациях они запрещены
Удивил такой новостью! Давно такое? В чем причина запретов? (впервые услышал)
познакомился с Excel
 
Цитата
написал:
Давно такое?
Точно не могу сказать, но уже лет 10 с этим сталкивался, причина простая - в макрос можно встроить какой то  вредоносный код, который антивирус не может распознать. Ну и ретивые сисадмины, чтобы не усложнять себе жизнь просто запретили скачивание и использование файлов с макросами.  
 
Цитата
написал:
просто запретили скачивание и использование файлов с макросами
Понятно, спасибо за комментарий.
познакомился с Excel
 
Добрый день. Формулами такие задачи не решаются, потребуется макрос. С макросами последнее время есть проблемы, во многих организациях они запрещены. У вас то как?

У нас можно. Я сама макросы к сожалению писать не могу. Если поможете, буду благодарна
 
Цитата
написал:
Добрый день. Формулами такие задачи не решаются, потребуется макрос. С макросами последнее время есть проблемы, во многих организациях они запрещены. У вас то как?  






Сообщение  
E-mail  


У нас можно использовать макросы. Я сама макросы, к сожалению, писать не могу. Если поможете, буду благодарна
 
Здравствуйте.
Можете показать пример как должны "поступать" товары на лист "Берут"? Мне не понятно какие колонки должны заполняться на листе "Берут" и какие изменения должны быть на листе "Отдают". И как отобразить что товар поступил из разных точек? Наверное, может быть так что один поставщик не может полностью закрыть потребность в товаре у того, кто получает товар?
Изменено: Aлeкceй - 12.04.2026 19:55:37
 
Цитата
написал:
Здравствуйте.Можете показать пример как должны "поступать" товары на лист "Берут"? Мне не понятно какие колонки должны заполняться на листе "Берут" и какие изменения должны быть на листе "Отдают". И как отобразить что товар поступил из разных точек? Наверное, может быть так что один поставщик не может полностью закрыть потребность в товаре у того, кто получает товар?
Добрый вечер! Внесла комментарии в таблице, для наглядности на одном примере. Расписала как в идеале должно быть (по сути строка дублируется и подбираются две ТТ отправители), но не уверена что в рамках экселя это возможно. Может быть и такое, что товар не кому переместить и он остается не распределенным
 
Вроде все понятно. Попробую завтра сделать (это к тому, чтобы Вы не подумали что я позадавал вопросов и пропал).
 
Цитата
написал:
это к тому, чтобы Вы не подумали что я позадавал вопросов и пропал
Доброе утро! Ни в коем разе. Спасибо что отозвались
 
Здравствуйте.
Проверьте правильно ли распределяю. Файл в архиве т.к. превышает допустимый объем (300 кБ). Распределение вывожу пока на лист "Результат". Как отладим макрос тогда буду выводить на лист "Берут".
Изменено: Aлeкceй - 13.04.2026 22:25:38
 
Цитата
написал:
Распределение вывожу пока на лист "Результат". Как отладим макрос тогда буду выводить на лист "Берут".
Доброе утро! Ошибка есть, Аптека №37-Дзержинск, Петрищева 29 отдает три раза, а на остатках к распределению 1 уп. Это как пример, Где то надо нулить остатки, если уже товар распределился
Изменено: vatuz23 - 14.04.2026 07:40:52
 
исправил
 
На примере все того же кода ГРП. Нижегородское ТО берут 10 уп отдают 11, по распределению получилось только 4, хотя должно быть 10.
Прошлая ошибка поправлена
 
попробуйте теперь
 
Почти супер. Внутри ТО все идет отлично.
Когда внутри ТО все распределено, но остались точки отправители и получатели, то товар распределяется по остаточному принципу.
В нашем примере, все тот же ГРП, после сцепа и ВПР остались 2 точки (пометила желтым), у которых товар остался, но его нужно забрать. И есть получатели, кто готов забрать и к кому товар не попал. Кто не получил и выше в списке, тот и получает (пометила зеленым)
 
Код
Option Explicit

Sub Заполнить_отправителей()
    Application.StatusBar = "Ждите..."
    Dim shBeru As Worksheet
    Set shBeru = Sheets("Берут")

    Dim shOtda As Worksheet
    Set shOtda = Sheets("Отдают")

    Dim dicOtda As Dictionary
    Set dicOtda = GetDicOtda(shOtda, xGrp:=1, xToo:=5, xQua:=11)
    
    Dim rTarget As Range
    Set rTarget = shBeru.Cells(1, 10)
    rTarget.Resize(shBeru.UsedRange.Rows.Count, shBeru.UsedRange.Columns.Count).Clear
    
    Dim aPered As Variant
    aPered = GetPeredArray(dicOtda:=dicOtda, shOtda:=shOtda, xOtdaTtt:=3, xOtdaTch:=4, xOtdaQua:=11, shBeru:=shBeru, xBeruGrp:=1, xBeruToo:=5, xBeruQua:=8, rTarget:=rTarget)
    If IsEmpty(aPered) Then
        Application.StatusBar = False
        Exit Sub
    End If
    
    PrintArray rTarget, aPered
    Application.StatusBar = False
End Sub

Private Sub PrintArray(rTarget As Range, arr As Variant)
    rTarget.Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr
End Sub

Private Function GetPeredArray(dicOtda As Dictionary, shOtda As Worksheet, xOtdaTtt As Long, xOtdaTch As Long, xOtdaQua As Long, shBeru As Worksheet, xBeruGrp As Long, xBeruToo As Long, xBeruQua As Long, rTarget As Range) As Variant
    Dim aOtdaTt As Variant, aOtdaTo As Variant, aOtdaQu As Variant
    With shOtda
        aOtdaTt = .Cells(1, xOtdaTtt).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        aOtdaTo = .Cells(1, xOtdaTch).Resize(UBound(aOtdaTt, 1), 1).Value
        aOtdaQu = .Cells(1, xOtdaQua).Resize(UBound(aOtdaTt, 1), 1).Value
    End With
    
    Dim aBeruGr As Variant, aBeruTo As Variant, aBeruQu As Variant
    With shBeru
        aBeruGr = .Cells(1, xBeruGrp).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        aBeruTo = .Cells(1, xBeruToo).Resize(UBound(aBeruGr, 1), 1).Value
        aBeruQu = .Cells(1, xBeruQua).Resize(UBound(aBeruGr, 1), 1).Value
    End With
    
    Dim aPered As Variant
    ReDim aPered(1 To UBound(aBeruGr, 1))
    
    Dim yb As Long, aOtdaY As Variant, yOtda As Variant, yOpt As Long, dd As Double
    For yb = 1 To UBound(aBeruGr, 1)
        If IsNumeric(aBeruQu(yb, 1)) Then
            If aBeruQu(yb, 1) > 0 Then
                If dicOtda.Exists(aBeruTo(yb, 1)) Then
                    If dicOtda(aBeruTo(yb, 1)).Exists(aBeruGr(yb, 1)) Then
                        aOtdaY = dicOtda(aBeruTo(yb, 1))(aBeruGr(yb, 1)).Keys()
                        
                        Do
                            If aBeruQu(yb, 1) <= 0 Then Exit Do
                            
                            yOpt = 0
                            For Each yOtda In aOtdaY
                                If aOtdaQu(yOtda, 1) > 0 Then
                                    If yOpt = 0 Then
                                        yOpt = yOtda
                                    ElseIf aOtdaQu(yOtda, 1) = aBeruQu(yb, 1) Then
                                        yOpt = yOtda
                                        Exit For
                                    Else
                                        If Abs(aOtdaQu(yOtda, 1) - aBeruQu(yb, 1)) < Abs(aOtdaQu(yOpt, 1) - aBeruQu(yb, 1)) Then
                                            yOpt = yOtda
                                        ElseIf Abs(aOtdaQu(yOtda, 1) - aBeruQu(yb, 1)) = Abs(aOtdaQu(yOpt, 1) - aBeruQu(yb, 1)) Then
                                            If aOtdaQu(yOtda, 1) > aOtdaQu(yOpt, 1) Then
                                                yOpt = yOtda
                                            End If
                                        End If
                                    End If
                                End If
                            Next
                            If yOpt = 0 Then Exit Do
                            
                            dd = aBeruQu(yb, 1)
                            If dd > aOtdaQu(yOpt, 1) Then dd = aOtdaQu(yOpt, 1)
                            aBeruQu(yb, 1) = aBeruQu(yb, 1) - dd
                            aOtdaQu(yOpt, 1) = aOtdaQu(yOpt, 1) - dd
                            If IsEmpty(aPered(yb)) Then
                                ReDim aTmp(1 To 1)
                            Else
                                aTmp = aPered(yb)
                                ReDim Preserve aTmp(LBound(aTmp) To UBound(aTmp) + 1)
                            End If
                            aTmp(UBound(aTmp)) = Array(yOpt, dd)
                            aPered(yb) = aTmp
                            DoEvents
                        Loop
                    End If
                End If
            End If
        End If
    Next
    
    Dim xPered As Long
    For yb = 1 To UBound(aPered)
        If Not IsEmpty(aPered(yb)) Then
            aTmp = aPered(yb)
            If xPered < UBound(aTmp) Then
                xPered = UBound(aTmp)
            End If
        End If
    Next
    If xPered = 0 Then Exit Function
    
    Dim bPered As Variant, xp As Long
    ReDim bPered(1 To UBound(aPered), 1 To 3 * xPered)
    For xp = 1 To xPered
        bPered(2, 3 * (xp - 1) + 1) = "К перемещению"
        bPered(2, 3 * (xp - 1) + 2) = "Код ТТ"
        bPered(2, 3 * (xp - 1) + 3) = "Точка отправитель"
    Next
    
    For yb = 1 To UBound(aPered)
        If Not IsEmpty(aPered(yb)) Then
            aTmp = aPered(yb)
            xPered = 0
            For xp = LBound(aTmp) To UBound(aTmp)
                yOtda = aTmp(xp)(0)
                bPered(yb, xPered + 1) = aTmp(xp)(1)
                bPered(yb, xPered + 2) = aOtdaTt(yOtda, 1)
                bPered(yb, xPered + 3) = aOtdaTo(yOtda, 1)
                rTarget.Columns(xPered + 1).Resize(UBound(aPered)).Interior.Color = RGB(189, 215, 238)
                xPered = xPered + 3
            Next
        End If
    Next
    GetPeredArray = bPered
End Function

Private Function GetDicOtda(sh As Worksheet, xGrp As Long, xToo As Long, xQua As Long) As Dictionary
    Dim agr As Variant, ato As Variant, aqu As Variant
    With sh
        agr = .Cells(1, xGrp).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        ato = .Cells(1, xToo).Resize(UBound(agr, 1), 1).Value
        aqu = .Cells(1, xQua).Resize(UBound(agr, 1), 1).Value
    End With
    
    Dim dic As New Dictionary
    Dim yg As Long
    For yg = 1 To UBound(agr, 1)
        If Not IsEmpty(agr(yg, 1)) Then
            If IsNumeric(aqu(yg, 1)) Then
                If aqu(yg, 1) > 0 Then
                    If Not dic.Exists(ato(yg, 1)) Then
                        Set dic(ato(yg, 1)) = New Dictionary
                    End If
                    If Not dic(ato(yg, 1)).Exists(agr(yg, 1)) Then
                        Set dic(ato(yg, 1))(agr(yg, 1)) = New Dictionary
                    End If
                    dic(ato(yg, 1))(agr(yg, 1))(yg) = Empty
                End If
            End If
        End If
    Next
    Set GetDicOtda = dic
End Function
 
Добрый день! Спасибо, что откликнулись. Когда внутри ТО все распределено, но остались точки отправители и получатели, то товар распределяется по остаточному принципу (отдают сверху вниз). Отправители берутся сверху вниз Остались торговые точки готовые забрать товар и точки готовые отдать (скрины приложила).  
 
vatuz23, добрый день. Вариант PQ. C именами столбцов в финальной таблице не заморачивался. Рез-т находится в последнем столбце ("К перемещению"). Если последние столбцы пустые (не нашли источник), то в "К перемещению" находится (нераспределенный) остаток первоначального запроса.
код
Изменено: AlienSx - 15.04.2026 21:03:08
Пришелец-прораб.
 
Цитата
написал:
Остались торговые точки готовые забрать товар и точки готовые отдать (скрины приложила).  
Возьмём к примеру строку 300 из скрина 2026-04-15_14-54-20.png. Для этой строки нет точки, готовой отдать, так как точка уже отдала для строки 155.
 
Цитата
написал:
Возьмём к примеру строку 300 из скрина 2026-04-15_14-54-20.png. Для этой строки нет точки, готовой отдать, так как точка уже отдала для строки 155.
Конечно, тогда для пустых строк 300, 332, 368 нужно забрать товар из другого скрина строки 428,570 и далее, пока не закончатся точки получатели
 
попробуйте теперь
 
Цитата
написал:
добрый день. Вариант PQ. C именами столбцов в финальной таблице не заморачивался. Рез-т находится в последнем столбце ("К перемещению"). Если последние столбцы пустые (не нашли источник), то в "К перемещению" находится (нераспределенный) остаток первоначального запроса.
Доброе утро! Спасибо, вроде все работает как нужно. Подскажите пож-та, если в таблице залить новую информацию и строк будет больше, формула сработает?
 
Цитата
написал:
попробуйте теперь
Спасибо! Работает на ура!!!  
 
Всем огромное спасибо, что откликнулись и помогли в решении моей задачи!!! Вы все по настоящему профессионалы своего дела!
Посоветуйте, пожалуйста, где можно поучиться написанию макросов с нуля?
 
Цитата
написал:
тогда для пустых строк 300, 332, 368 нужно забрать товар из другого скрина строки 428,570 и далее, пока не закончатся точки получатели
Но там другие ТО.
ТОСтрока на листе Берут
Кстовское ТО300
Семеновское ТО332
Семеновское ТО368
ТОСтрока на листе Отдают
Заволжское ТО428
Дзержинское ТО570
Можно забирать из других ТО?

Цитата
написал:
Посоветуйте, пожалуйста, где можно поучиться написанию макросов с нуля?
Прямо тут)
Лайт-вариант       - в этой ветке форума решать самому, спрашивать у других.
Хардкор-вариант - Тренинг "Программирование макросов на VBA в Excel" (3 дня) и Тренинг "VBA Pro: Профессиональная разработка на VBA в Excel".
 
Цитата
написал:
Но там другие ТО.ТОСтрока на листе БерутКстовское ТО300Семеновское ТО332Семеновское ТО368ТОСтрока на листе ОтдаютЗаволжское ТО428Дзержинское ТО570Можно забирать из других ТО?
Да можно. Первый приоритет внутри ТО, далее по остаточному принципу сверху вниз
 
Цитата
написал:
Прямо тут)Лайт-вариант       - в этой ветке форума решать самому, спрашивать у других.Хардкор-вариант -  Тренинг "Программирование макросов на VBA в Excel" (3 дня)  и  Тренинг "VBA Pro: Профессиональная разработка на VBA в Excel" .
Отлично, спасибо
 
Цитата
написал:
Да можно. Первый приоритет внутри ТО, далее по остаточному принципу сверху вниз
Код
Option Explicit
'v4
Sub Заполнить_отправителей()
    Application.StatusBar = "Ждите..."
    Dim shBeru As Worksheet
    Set shBeru = Sheets("Берут")

    Dim shOtda As Worksheet
    Set shOtda = Sheets("Отдают")

    Dim dicOtda As Dictionary
    Set dicOtda = GetDicOtda(shOtda, xGrp:=1, xToo:=5, xQua:=11)
    
    Dim rTarget As Range
    Set rTarget = shBeru.Cells(1, 10)
    rTarget.Resize(shBeru.UsedRange.Rows.Count, shBeru.UsedRange.Columns.Count).Clear
    
    Dim aPered As Variant
    aPered = GetPeredArray(dicOtda:=dicOtda, shOtda:=shOtda, xOtdaTtt:=3, xOtdaTch:=4, xOtdaToo:=5, xOtdaQua:=11, shBeru:=shBeru, xBeruGrp:=1, xBeruToo:=5, xBeruQua:=8, rTarget:=rTarget)
    If IsEmpty(aPered) Then
        Application.StatusBar = False
        Exit Sub
    End If
    
    PrintArray rTarget, aPered
    Application.StatusBar = False
End Sub

Private Sub PrintArray(rTarget As Range, arr As Variant)
    rTarget.Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr
End Sub

Private Function GetPeredArray(dicOtda As Dictionary, shOtda As Worksheet, xOtdaTtt As Long, xOtdaTch As Long, xOtdaToo As Long, xOtdaQua As Long, shBeru As Worksheet, xBeruGrp As Long, xBeruToo As Long, xBeruQua As Long, rTarget As Range) As Variant
    Dim aOtdaTt As Variant, aOtdaTo As Variant, aOtdaTc As Variant, aOtdaQu As Variant
    With shOtda
        aOtdaTt = .Cells(1, xOtdaTtt).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        aOtdaTo = .Cells(1, xOtdaTch).Resize(UBound(aOtdaTt, 1), 1).Value
        aOtdaTc = .Cells(1, xOtdaToo).Resize(UBound(aOtdaTt, 1), 1).Value
        aOtdaQu = .Cells(1, xOtdaQua).Resize(UBound(aOtdaTt, 1), 1).Value
    End With
    
    Dim aBeruGr As Variant, aBeruTo As Variant, aBeruQu As Variant
    With shBeru
        aBeruGr = .Cells(1, xBeruGrp).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        aBeruTo = .Cells(1, xBeruToo).Resize(UBound(aBeruGr, 1), 1).Value
        aBeruQu = .Cells(1, xBeruQua).Resize(UBound(aBeruGr, 1), 1).Value
    End With
    
    Dim aPered As Variant
    ReDim aPered(1 To UBound(aBeruGr, 1))
    
    Dim sTO As String, allTO As Variant
    Dim yb As Long, aOtdaY As Variant, yOtda As Variant, yOpt As Long, dd As Double
    For Each allTO In Array(False, True)
        For yb = 1 To UBound(aBeruGr, 1)
            If IsNumeric(aBeruQu(yb, 1)) Then
                If aBeruQu(yb, 1) > 0 Then
                    If allTO Then
                        sTO = "All"
                    Else
                        sTO = aBeruTo(yb, 1)
                    End If
                
                    If dicOtda.Exists(sTO) Then
                        If dicOtda(sTO).Exists(aBeruGr(yb, 1)) Then
                            aOtdaY = dicOtda(sTO)(aBeruGr(yb, 1)).Keys()
                            
                            Do
                                If aBeruQu(yb, 1) <= 0 Then Exit Do
                                
                                yOpt = 0
                                For Each yOtda In aOtdaY
                                    If aOtdaQu(yOtda, 1) > 0 Then
                                        If yOpt = 0 Then
                                            yOpt = yOtda
                                        ElseIf aOtdaQu(yOtda, 1) = aBeruQu(yb, 1) Then
                                            yOpt = yOtda
                                            Exit For
                                        Else
                                            If Abs(aOtdaQu(yOtda, 1) - aBeruQu(yb, 1)) < Abs(aOtdaQu(yOpt, 1) - aBeruQu(yb, 1)) Then
                                                yOpt = yOtda
                                            ElseIf Abs(aOtdaQu(yOtda, 1) - aBeruQu(yb, 1)) = Abs(aOtdaQu(yOpt, 1) - aBeruQu(yb, 1)) Then
                                                If aOtdaQu(yOtda, 1) > aOtdaQu(yOpt, 1) Then
                                                    yOpt = yOtda
                                                End If
                                            End If
                                        End If
                                    End If
                                Next
                                If yOpt = 0 Then Exit Do
                                
                                dd = aBeruQu(yb, 1)
                                If dd > aOtdaQu(yOpt, 1) Then dd = aOtdaQu(yOpt, 1)
                                aBeruQu(yb, 1) = aBeruQu(yb, 1) - dd
                                aOtdaQu(yOpt, 1) = aOtdaQu(yOpt, 1) - dd
                                If IsEmpty(aPered(yb)) Then
                                    ReDim aTmp(1 To 1)
                                Else
                                    aTmp = aPered(yb)
                                    ReDim Preserve aTmp(LBound(aTmp) To UBound(aTmp) + 1)
                                End If
                                aTmp(UBound(aTmp)) = Array(yOpt, dd)
                                aPered(yb) = aTmp
                                DoEvents
                            Loop
                        End If
                    End If
                End If
            End If
        Next
    Next
    
    Dim xPered As Long
    For yb = 1 To UBound(aPered)
        If Not IsEmpty(aPered(yb)) Then
            aTmp = aPered(yb)
            If xPered < UBound(aTmp) Then
                xPered = UBound(aTmp)
            End If
        End If
    Next
    If xPered = 0 Then Exit Function
    
    Const N_COL = 4
    
    Dim bPered As Variant, xp As Long
    ReDim bPered(1 To UBound(aPered), 1 To N_COL * xPered)
    For xp = 1 To xPered
        bPered(2, N_COL * (xp - 1) + 1) = "К перемещению"
        bPered(2, N_COL * (xp - 1) + 2) = "Код ТТ"
        bPered(2, N_COL * (xp - 1) + 3) = "Точка отправитель"
        bPered(2, N_COL * (xp - 1) + 4) = "ТО"
    Next
    
    For yb = 1 To UBound(aPered)
        If Not IsEmpty(aPered(yb)) Then
            aTmp = aPered(yb)
            xPered = 0
            For xp = LBound(aTmp) To UBound(aTmp)
                yOtda = aTmp(xp)(0)
                bPered(yb, xPered + 1) = aTmp(xp)(1)
                bPered(yb, xPered + 2) = aOtdaTt(yOtda, 1)
                bPered(yb, xPered + 3) = aOtdaTo(yOtda, 1)
                bPered(yb, xPered + 4) = aOtdaTc(yOtda, 1)
                rTarget.Columns(xPered + 1).Resize(UBound(aPered)).Interior.Color = RGB(189, 215, 238)
                xPered = xPered + N_COL
            Next
        End If
    Next
    GetPeredArray = bPered
End Function

Private Function GetDicOtda(sh As Worksheet, xGrp As Long, xToo As Long, xQua As Long) As Dictionary
    Dim agr As Variant, ato As Variant, aqu As Variant
    With sh
        agr = .Cells(1, xGrp).Resize(.UsedRange.Row + .UsedRange.Rows.Count - 1, 1).Value
        ato = .Cells(1, xToo).Resize(UBound(agr, 1), 1).Value
        aqu = .Cells(1, xQua).Resize(UBound(agr, 1), 1).Value
    End With
    
    Dim dic As New Dictionary
    Dim yg As Long, vTO As Variant
    For yg = 1 To UBound(agr, 1)
        If Not IsEmpty(agr(yg, 1)) Then
            If IsNumeric(aqu(yg, 1)) Then
                If aqu(yg, 1) > 0 Then
                    For Each vTO In Array("All", ato(yg, 1))
                        If Not dic.Exists(vTO) Then
                            Set dic(vTO) = New Dictionary
                        End If
                        If Not dic(vTO).Exists(agr(yg, 1)) Then
                            Set dic(vTO)(agr(yg, 1)) = New Dictionary
                        End If
                        dic(vTO)(agr(yg, 1))(yg) = Empty
                    Next
                End If
            End If
        End If
    Next
    Set GetDicOtda = dic
End Function
Страницы: 1 2 След.
Читают тему
Наверх