В организации часто печатают сметы и это множество файлов, но часто бывают ситуации когда последняя страница распечатанного документа содержит только подписи, но не содержит ни одной цифры стоимости, потому что все они остаются на предыдущем листе. Директора в такой ситуации обычно отказываются подписывать документы и у таких смет приходится перепечатывать последний лист. Типичный пример такой сметы показан на скриншоте ниже:

Проблема, когда последняя страница содержит только подписи
Проблема, когда последняя страница содержит только подписи

К исполнителю, который печатает сметы они поступают в виде обычного Эксель файла, который надо просто распечатать. Чтобы не просматривать перед печатью каждый файл было решено автоматизировать процесс и как-то автоматически определять - остаются ли на последнем листе только подписанты или есть хотя бы одна цифра?

Самым простым виделось сделать это, пользуясь средствами самого Excel. Для этого подходило Visual Basic для приложений (VBA) в Office.

Проблема

Сметы создаются в приложении ГРАНД-Смета, версия 2024.2 - это указывается в колонтитуле Эксель файла и, возможно, у этого приложения есть такая кнопка, чтобы сделать всё красиво и не делать так, чтобы на последнем листе оставались только подписи.

Но организация, которая составляет эти сметы, и передаёт их на печать к нам в другую организацию, похоже с этой кнопкой не знакома. Поэтому в Excel часто сталкивались с ситуацией, когда последняя страница содержит только строки подписи, а цифры цен отображается на предыдущей странице.

Такой макет может вызвать недовольство или отказ от подписи со стороны директоров, которые предпочитают, чтобы цена была на той же странице, что и подписи.

Решение

Сценарий VBA под названием «AdjustRowHeightsForSignaturePage» предназначен для обработки множества книг Excel, в том числе в текущем и во вложенных каталогах, и изменения высоты последних нескольких строк, чтобы гарантировать, что окончательная цифра цены и строки подписи появятся на одной странице. Эта корректировка предотвращает ситуацию, когда последняя страница сметы лишена цифр.

Подготовка Microsoft Excel

Убедитесь, что Microsoft Excel установлен. Включение вкладки «Разработчик» в Excel позволяет получить доступ к инструментам и функциям, связанным с разработкой, включая макросы, Visual Basic для приложений (VBA). Вот как включить вкладку «Разработчик» в Excel:

1. Запустите Excel на своем компьютере.

2. Перейдите к параметрам. В зависимости от вашей версии Excel действия могут незначительно отличаться:
➡️ Для Excel 2010 и более поздних версий: нажмите вкладку «Файл» в верхнем левом углу, затем выберите «Параметры» в нижней части меню.
➡️ Для Excel 2007: нажмите круглую кнопку Office в верхнем левом углу, затем нажмите «Параметры Excel» в нижней части меню.

3. В диалоговом окне «Параметры Excel» слева вы увидите список категорий. Найдите и нажмите «Настроить ленту» (для Excel 2010 и более поздних версий) или «Популярные» (для Excel 2007).

4. Найдите параметр «Разработчик» в списке справа в разделе «Основные вкладки». Убедитесь, что флажок рядом с «Разработчик» установлен.

Включение вкладки «Разработчик» в Excel
Включение вкладки «Разработчик» в Excel

4. Установив флажок «Разработчик», нажмите кнопку «ОК» в нижней части диалогового окна «Параметры Excel», чтобы сохранить изменения и закрыть диалоговое окно.

5. После того как вы включили вкладку «Разработчик», вы должны увидеть ее на ленте Excel в верхней части окна Excel вместе с другими вкладками, такими как «Главная», «Вставка» и т.д.

Сценарий VBA для изменения высоты последних строк, если это требуется

Перед любыми массовыми манипуляциями с файлами рекомендую создать резервные копии файлов Excel простым копированием каталогов.

Чтобы вставить сценарий VBA в Excel и выполнить его, выполните следующие действия:

1. Включите вкладку «Разработчик» (если она еще не включена).

2. Получите доступ к редактору Visual Basic:
Нажмите вкладку «Разработчик» на ленте Excel.
В группе «Код» нажмите «Visual Basic» (или нажмите ALT+F11).

3. Вставьте скрипт:
В окне редактора Visual Basic убедитесь, что ваша книга выбрана в окне Project Explorer обычно с именем «VBAProject (имя файла)».
Щелкните правой кнопкой мыши на имя проекта или любую существующую папку модуля.
Выберите «Вставка» > «Модуль» в контекстном меню.
Вставьте код VBA:

' Подробнее: https://habr.com/ru/articles/820249/

Sub AdjustRowHeightsForSignaturePage()
    Dim FileSystem  As Object
    Dim HostFolder  As String
    Dim FileName    As String
    Dim wb          As Workbook
    Dim ws          As Worksheet
    Dim rng         As Range
    
    ' Определить путь к папке
    HostFolder = ThisWorkbook.Path
    
    ' Создать экземпляр FileSystemObject
    Set FileSystem = CreateObject("Scripting.FileSystemObject")
    
    ' Рекурсивная функция для поиска во вложенных каталогах
    ProcessFiles HostFolder, FileSystem
    
    MsgBox "Обработка всех файлов завершена", vbInformation
End Sub

Sub ProcessFiles(ByVal folderPath As String, ByVal fs As Object)
    Dim subFolder   As Object
    Dim file        As Object
    Dim ws          As Worksheet
    Dim lastRow     As Long
    Dim startRow    As Long
    Dim pageBreaks  As HPageBreaks
    Dim breakCount  As Long
    Dim lastPageBreakRow As Long
    Dim i           As Long
      
    On Error GoTo ErrorHandler
      
    ' Обработка файлов в текущем каталоге
    For Each file In fs.GetFolder(folderPath).Files
        If LCase(file.Name Like "*.xlsx*") Then
            ' Открыть книгу
            Dim wb  As Workbook
            Set wb = Workbooks.Open(file.Path)
            
            ' Цикл по каждому листу в книге
            For Each ws In wb.Worksheets
                ActiveWindow.View = xlPageBreakPreview
                
                ' Найти последнюю использованную строку на рабочем листе, где 2 номер столбца
                lastRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
                ' MsgBox "Последняя использованная строка в столбце 2 на рабочем листе: " & lastRow
                
                ' Определить начальную строку последних 20 строк
                startRow = lastRow - 20
                If startRow < 1 Then startRow = 1 ' Убедитесь, что начальная строка не меньше 1
                ' MsgBox "Начальная строка последних 20 строк: " & startRow
                
                ' Получить горизонтальные разрывы страниц
                Set pageBreaks = ws.HPageBreaks
                breakCount = 0
                
                ' Подсчитать количество разрывов документа в пределах последних 15 строк
                ' MsgBox "Количество разрывов документа: " & pageBreaks.Count
                For i = 1 To pageBreaks.Count
                    ' MsgBox "Разрыв страницы " & i & " находится в строке " & pageBreaks(i).Location.Row & vbNewLine & "Значение lastRow " & lastRow & " - 15 = " & lastRow - 15
                    If pageBreaks(i).Location.Row > lastRow - 14 Then
                        breakCount = breakCount + 1
                    End If
                Next i
                
                ' Если последние 15 строк начинаются на новой странице с менее чем 15 строками, изменить высоту последних 20 строк
                If breakCount > 0 And breakCount < 15 Then
                    For i = startRow To lastRow
                        ws.Rows(i).RowHeight = 25
                    Next i
                    MsgBox "Высота последних строк изменена для файла " & file.Name & vbNewLine & file.Path
                End If
                
            Next ws
            
            ' Сохранить и закрыть книгу
            wb.Save
            wb.Close
        End If
    Next file
    
    ' Обработка вложенных папок
    For Each subFolder In fs.GetFolder(folderPath).SubFolders
        ProcessFiles subFolder.Path, fs
    Next subFolder
Exit Sub

ErrorHandler:
    MsgBox "Произошла ошибка: " & Err.Description, vbExclamation
End Sub

4. Запустите сценарий VBA:
➡️ Запуск макроса.Можете запустить его, перейдя на вкладку «Разработчик», нажав «Макросы», выбрав макрос и нажав «Выполнить».

➡️ Запуск из VB: Можете запустить ее непосредственно из редактора Visual Basic, поместив курсор внутри процедуры и нажав F5 или выбрав «Выполнить» > «Выполнить Sub / UserForm» из меню.

Готово

Теперь надо проверить результаты и убедится, что скрипт выполнил нужные действия и изменил высоту последних строк, где это необходимо в других книгах Excel.

Пример обработанного файла
Пример обработанного файла

Итоги

Использование сценариев VBA позволяет решить проблему, когда последняя страница содержит только строки подписи без цен. Зачастую это приводит к недовольству или прямому отказу подписывать документ. Чтобы решить эту проблему сценарий Visual Basic для приложений (VBA) в Office может гарантировать то, что этого не случится, для этого сценарий регулирует высоту последних строк.

Пошаговое руководство, изложенное в этой статье, дает инструмент, позволяющий за минуты сделать преобразование в сотнях документов и выполнить то, что в противном случае потребовало бы часов выполнения вручную.

Автор: Михаил Шардин,

10 июня 2024 г.

Комментарии (17)


  1. TomskDiver
    10.06.2024 03:09
    +2

    Как я понимаю, на предпоследней странице получится что несколько последних строк больше по высоте? Как вариант можно просто HPageBreaks вставить свой где надо. Тогда высоты будут одинаковые, но предпоследний лист будет чуть-чуть не весь заполнен на бумаге.


    1. empenoso Автор
      10.06.2024 03:09

      Да, на последней странице несколько последних строк будут больше по высоте


      1. Beast2040
        10.06.2024 03:09
        +1

        Проще чуть уменьшить масштаб листа чтобы подписи влезли на полный лист.


  1. Kahelman
    10.06.2024 03:09
    +1

    Делюсь лайк-хаком: пишите письмо на одной странице с основными параметрами, пишите детали как в приложении 1.

    Руководитель подписывает письмо а сама смета -по барабану.

    Если надо очень официально-то все равно каждая страница внизу подписывается


    1. empenoso Автор
      10.06.2024 03:09
      +1

      Хороший совет, но в конце каждой сметы всё равно подписи ставят - там форма такая


  1. Kahelman
    10.06.2024 03:09
    +1

    Форму поменяйте. На многих документах подпись стоит на первом листе с пометкой «утверждаю».


    1. necr0x
      10.06.2024 03:09
      +1

      Итог важен, а иначе в середину можно понапихать разного.


    1. wepp
      10.06.2024 03:09
      +1

      Форма определена НПА, это не просто смета к КП. Да, на первом (титульном) листе подписи и печати выглядят солидно, но тут смысл в итоговых цифрах, поэтому вполне логично именно такое расположение подписи.


  1. Kahelman
    10.06.2024 03:09
    +1

    Есть ещё лайфх: посылаем по email и без подписи.

    Весь загнивающий запад так работает. Подпись только под договором, детали по e-mail.


  1. qyix7z
    10.06.2024 03:09
    +1

    For Each ws In wb.Worksheets

    ActiveWindow.View = xlPageBreakPreview

    Перебор листов в книге не делает лист активным. Зачем в цикле каждый раз выполняется ActiveWindow.View = xlPageBreakPreview?
    Лучше написать ws.DisplayPageBreaks = True - это заставит эксель пересчитать разделение на страницы на каждом листе.

    Вообще, если директору так надо видеть сумму перед подписью, то я в таких случаях просто ставлю HPageBreaks над строкой с суммой и не надо строчки раздвигать.


  1. necr0x
    10.06.2024 03:09
    +1

    Обоже, я думал в Германии бюрократия, ан нет, в России)) в Германии достаточно отправить на электронку ангебот (ценовое предложение, в котором будет перечень услуг и цен за квадратный метр, а так же общее количество метров). И всё. Даже подпись не нужна ни с чьей стороны. Это и является официальным договором по ценам для объекта в контексте которого идёт переписка.

    Есть конечно так называемый Verhandlungstermin на котором самой сутью встречи является не наличие подписи, а переговоры с целью понизить некоторые позиции. Таким образом цены координируются сторонами и уже в этой версии документа необходимы подписи обоих участников.

    Но как можно подписывать смету не видя при этом всего перечня? Это звучит как какая-то формальность. Если ты подписываешь видя только итоговую сумму, то это чисто формальная подпись, в таком случае можно и не видя сумму подписать.

    Идиоты))


    1. Kahelman
      10.06.2024 03:09

      «Безумие и отвага»

      Сами себе проблемы создаём, потом их героически преодолеваем.

      Все таки не ясно о какого уровня руководителе идёт речь. Если директор- то по идее он не должен подписывать сметы только договоры с приложениями.

      Если начальник уровня отдела, то надо бы директору настучать ему по голове, чтобы на ровном месте проблему не создавали.


      1. wepp
        10.06.2024 03:09
        +1

        Речь про сметы в составе проектно-сметной документации на строительство.

        Образец рекомендуемый, но в целом все придерживаются
        Образец рекомендуемый, но в целом все придерживаются

        Подпись можно и "электронно" поставить, но тут даже по оформлению будет выглядеть некрасиво, если подпись будет в отрыве от остального содержимого.


    1. qyix7z
      10.06.2024 03:09
      +1

      Если ты подписываешь видя только итоговую сумму, то это чисто формальная подпись, в таком случае можно и не видя сумму подписать.

      Смысл в том, что подписывается один лист. Остальные листы ретивые исполнители могут заменить. Если на листе с подписями будет итоговая сумма, то хотя бы она останется неизменной.

      В целом предложенное решение автором - костыль, так как проблема не в технической плоскости, а в административной. Если уж ты при подписании не доверяешь, то подписывай каждую страницу или требуй, чтобы сшивали документ.


  1. enabokov
    10.06.2024 03:09
    +2

    Ооо, у меня была такая проблема в Crystal Reports в 2007 году для банковского отчёта. Нормального решения в CR не было, и я как-то извратился. Не помню уже как. Вроде я вставлял принудительный page break при некоторых условиях, чтобы часть строк и Итого попадали на новую страницу. Форму поменять было нельзя, потому как она была утверждена ЦБ.


  1. KEugene
    10.06.2024 03:09
    +1

    Я знаю, как случайно поломать скрипт. На любой строке ниже "визульно" последней (383 на примере) в ячейку ввести пробел. Например, где-то на четырехсотой.

    Если люди, создающие сметы, не способны сразу сделать нормальную разметку, то от них вполне можно ожидать подобных сюрпризов.

    Наверное, в скрипт надо ввести проверку, что выявленная последняя строка содержит значимую информацию. Можно сцепить значения ячеек, удалить пробелы и проверить длину того, что останется. Ну и определить порог. Если длина больше, то продолжаем. Меньше-ошибку в лог и к следующему файлу.

    Кстати, при массовой обработке лучше не использовать msgbox, а писать лог или в консоль, или в текстовик. Нельзя отойти от компьютера, постоянно требуется нажатие кнопки. А уже в конце полноценное уведомление, типа, Успешно обработано Х файлов, ошибок Y. Детали в логе.


    1. qyix7z
      10.06.2024 03:09
      +1

      Если люди, создающие сметы, не способны сразу сделать нормальную разметку, то от них вполне можно ожидать подобных сюрпризов.

      Это не люди:

      Сметы создаются в приложении ГРАНД-Смета, версия 2024.2 - это указывается в колонтитуле Эксель файла и, возможно, у этого приложения есть такая кнопка, чтобы сделать всё красиво и не делать так, чтобы на последнем листе оставались только подписи.

      Но организация, которая составляет эти сметы, и передаёт их на печать к нам в другую организацию, похоже с этой кнопкой не знакома.

      Не будет там пробела в 400й строке.

      Про msgbox и логи согласен.