Генератор отчетов для excel

Приступая к работе¶

Принцип работы¶

Движок генератора написан на J2SE с использованием библиотеки Apache POI.
Более вменяемого и бесплатного средства для работы с Excel на момент разработки не нашлось.
Поэтому, при использовании генератора в системе требуется наличие
JRE версии не 1.6 и новее.

Формирование отчета заключается в сборке данных, их сериализации,
добавлении дополнительных настроечных параметров и отправке в java-часть генератора.
Производится выполнение jar-файла и обмен данными между ним и Python’ом через STDIN/STDOUT и внешние файлы.
Настройки не требуется,
скомпилированный jar файл и необходимые файлы Apache POI лежат внутри пакета и запускается автоматически.

Исходным материалом является шаблон в формате Excel-97 (xls) со специальными тегами внутри.
Этот шаблон открывается и сериализованные данные расставляются в соответствии с тегами в документе.
Далее сохраняется новый файл, шаблон остается неизменным.
Пользователю не нужно самому заниматься сериализаций, достаточно предоставить правильно сформированный объект.

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

Для работы с генератором нужно всего лишь импортировать модуль:

import m3.core.excel_reporting.report_gen

Есть 2 фундаментальных метода, с помощью которых работает весь python-код генератора:

Базовый класс для отчетов.¶

Самым удобным способом создания новых отчетов является определение их как классов.
Для этого служит класс BaseReport.

Пример использования:

class TestReport(BaseReport):
    template_name = "report\excel\test.xls"
    result_name = "report\excel\OUT.xls"

    def collect(self):
        result = {}

        # Получаем данные тут
        # ...

        return result

    def test():
        rep = TestReport()
        rep.make_report();

Структура объекта данных¶

Структура объекта проектировалась таким образом,
чтобы обеспечить максимальную совместимость со штатным движком шаблонов Django и легко использоваться в HTML шаблонах.
Он представляет собой словарь, в который в виде значений могут быть вложены другие словари и списки.

Пример структуры объекта:

{"name": "валюты",
     "TEMPLATE_FILE_PATH": "d:\_DEV\workspace\JavaReport\media\test.xls",
     "OUTPUT_FILE_PATH": "d:\_DEV\workspace\JavaReport\media\OUT.xls",

     "Число": 13,
     "Булево": "true",
     "ФИО": "Иванов Иван Иваныч",
     "Дата": "21.12.1990",

     "М3Дата": "#m3dd#21.12.1920",
     "М3Время": "#m3tt#21:12:33",
     "М3ДатаВремя": "#m3dt#21.12.1990 21:12:33",

     "currency": [{
        "name": "RUB",
        "value": 1.00},
        {
        "name": "USR",
        "value": 30},
        {
        "name": "EUR",
        "value": 40}],

     "country": [{
        "name": "Россия",
        "city": [{
        "name": "Москва",
        "street": [{
            "name": "Ямская"},
            {
            "name": "Тверская"}]},

        {
        "name": "Казань",
        "street": [{
            "name": "Ферма-2"},
            {
            "name": "Баумана"}]
        }]
        },

        {
        "name": "Америка",
        "city": [{
        "name": "Вашингтон",
        "street": [{
            "name": "Валли стрит"
            }]
        }]
        }]
    }

На примере мы видим одиночную таблицу currency, содержащую 3 строки.
И 2 вложенных таблицы: street вложена в city, city вложена country.

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

JSON поддерживает только примитивные типы данных, к таким не относятся дата и время.
В Python они представлены отдельными классами datetime, date и time.
Для их сериализации используется специальный класс ReportJSONEncoder.
Он добавляет в строковому представлению даты и времени префикс #m3dt#,
#m3dd# и прочие, для того, чтобы на java-стороне можно было правильно понять их их.

Приоритет операций¶

Чтобы лучше понимать показанные в дальнейшем примеры, нужно знать в какой последовательности выполняются операции при генерации отчета:

  1. Горизонтальная развертка с подстановкой
  2. Вертикальная развертка
  3. Подстановка значений в строках
  4. Проецирование матрицы
  5. Наложение повторяющихся строк
  6. Наложение смежных регионов

Подстановка значений в ячейки¶

Для подстановки значения в ячейку используется тег $ИмяПеременной$.
Знак $ дает понять, что в этом месте нужно подставить значение переменной из JSON контекста с именем ИмяПеременной.

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

Примеры подстановки:

_images/replace_tag.png

Результат

_images/replace_result.png

Вертикальная развертка¶

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

Развертка приоритетнее чем подстановка. Поэтому теги подстановки копируются и обрабатываются после подстановки.
Это позволяет легко строить таблицы с заведомо известным количеством колонок.

Пример данных для развертки:

{"name": "курсы валют по отношению к рублю",
        "currency": [{
         "name": "JPY",
         "value": 35.99},
         {
         "name": "USR",
         "value": 30.12},
         {
         "name": "EUR",
         "value": 40.4}]
}

Пример развертки:

_images/vertical_tag.png

Как видно из примера, регион currency состоит из одной строки.
В JSON данных currency представляет из себя список из 3-х словарей.
Следовательно строка будет скопирована 3 раза.
Далее в строки будут подставлены значения из словаря с соответствующим номером в списке.
Первой строке первый словарь, второй строке — второй словарь и т.д.
Т.е. контекст строки изменяется в зависимости от вложенности развертки и номера строки.

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

Результат:

_images/vertical_result.png

Горизонтальная развертка¶

Горизонтальная развертка работает аналогично вертикальной,
но копируются не строки, а прямоугольные регионы (не колонки).
Задается развертка тегами %ИмяРегиона Ширина и %%ИмяРегиона Ширина.

Где ИмяРегиона — переменная в контексте, а Ширина — ширина копируемого региона.
Прямоугольный регион начинается со строки с тегом %,
заканчивается строкой %% и имеет ширину в правую сторону Ширина.

Подстановка в копируемом регионе тоже отличается.
Вместе с именем переменной нужно указвать имя региона с точкой. Например, вместо $Год$ писать $Годы.Год$.

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

Пример данных:

{"Годы": [
        {"Год": 2010, "Культура": "Пшеница", "Урожай": 500},
        {"Год": 2011, "Культура": "Рожь", "Урожай": 600},
        {"Год": 2012, "Культура": "Горох", "Урожай": 700},
        {"Год": 2013, "Культура": "Соя", "Урожай": 800}
]}

Пример:

_images/goriz_tag.png

Результат:

_images/goriz_result.png

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

Проецирование матрицы¶

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

Задается матрица тегом #Матрица ИмяПеременной в комментарии к ячейке.

Пример данных:

{"ЛПУ": [
        {"name": "Центральная поликлиника"},
        {"name": "Детская больница"},
        {"name": "Родильные дом"}
    ],
    "МоиДанные": [
        ["ЛПУ1", 1000, 2000, 3000, 9],
        ["ЛПУ2", 4000, 5000, 6000, 99],
        ["ЛПУ3", 7000, 8000, 9000, 999]
    ],
}

Обратите внимание, что вместо словарей в списке МоиДанные находятся списки.
Это связано с тем, адресация идет не по ключу, как при подстановке, а по номеру колонки.

Список ЛПУ не отобразился в результирующем файл, он мог быть вообще пустым.
Он был нужен лишь для вертикальной развертки таблицы, проще говоря чтобы появились рамки.
Данные все равно взялись из матрицы. См. пункт приоритет операций.

Пример шаблона:

_images/matrix_tag.png

Как видно из примера, в строке региона ЛПУ нет тегов подстановки $, зато есть матрица в комментарии.
Горизонтальная развертка используется только для формирования шапки.

Результат:

_images/matrix_result.png

Автовысота¶

Для того чтобы Excel сам определял высоту ячейки нужно использовать
тег #Автовысота в комментарии к ячейке и следовать правилам:

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

Лучше вставить новую строку. Иначе в Excel-файл сохраняется фиксированная высота и тег не будет работать.
* В свойствах ячейки нужно включить перенос по словам.
* Высота определяется в строке в момент открытия в Microsoft Office или Open Office,
и может визуально отличаться. Внутри Apache POI её тоже точно определить нельзя.
Связано это с тем, что все они используют разные библиотеки для рендеринга шрифтов.

_images/autoheight_tag.png

Форматирование по условию¶

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

Условие задается в комментарии к ячейке тегом #ЕСЛИ Условие ТО СписокИзменений.

Пример:

#ЕСЛИ Число=10 ТО ШРИФТ:ЖИРНЫЙ, ЦВЕТ: КРАСНЫЙ, ШРИФТ: ПОДЧЕРКНУТЫЙ

#ЕСЛИ Булево=true ТО ШРИФТ:ЖИРНЫЙ, ШРИФТ:ЗАЧЕРКНУТЫЙ

Поддерживаемые стили директивы ШРИФТ: ЖИРНЫЙ, ПОДЧЕРКНУТЫЙ, ЗАЧЕРКНУТЫЙ.

Поддерживаемые цвета директивы ЦВЕТ: КРАСНЫЙ, ЗЕЛЕНЫЙ, ЖЕЛТЫЙ, ЧЕРНЫЙ, СИНИЙ.
Недостающие можно легко добавить в пределах палитры Office 97.

Смежные регионы¶

Генератор полностью копирует все смежные регионы из листов шаблона в листы результирующего файла.
Также смежные регионы копируются при развертках.
Но иногда этого бывает недостаточно и тогда помощь приходит тег #Объединить НомерОбласти.

Принцип работы прост. При обработке листа собираются все теги #Объединить и группируются по номеру области.
Таким образом используя разные номера можно задавать несколько списков тегов, формирующих разные смежные области.

Далее координаты ячеек из списка тегов объединяются в прямоугольники, которые и являются созданными смежными областями.
Например, у нас есть 3 ячейки с координатами (3,4), (5,6), (1,2) — их область будет иметь координаты (1,2)-(5,6).

Пример:

_images/merged_region_tag.png

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

_images/merged_region_result.png

Повторение строк для каждого листа¶

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

Report designer and generation tool developed exclusively within Excel using VBA.

The tool allows the user to:
— design a business report specifying report, page, group, subgroup and details (headers and footers where applicable)
— format report in Excel (using Excel standard functionality)
— preview report
— print reports in various formats e.g. pdf, word, excel, printer, internet (google docs), etc.

Features

  • Design Excel Report
  • Preview Excel Report
  • Print Excel Report
  • Export Excel Report
  • Publish Excel Report

License

GNU General Public License version 3.0 (GPLv3)

Open LMS delivers an effective and engaging learning experience. Icon

As the largest commercial provider of hosting and support services for the open-source Moodle™ learning platform, we help organizations and institutions deliver great learning experiences without complexities

User Ratings


1.0

out of 5 stars

★★★★★

★★★★

★★★

★★

ease
1 of 5
2 of 5
3 of 5
4 of 5
5 of 5

0 / 5

features
1 of 5
2 of 5
3 of 5
4 of 5
5 of 5

0 / 5

design
1 of 5
2 of 5
3 of 5
4 of 5
5 of 5

0 / 5

support
1 of 5
2 of 5
3 of 5
4 of 5
5 of 5

0 / 5

Additional Project Details

Intended Audience

Advanced End Users

User Interface

Win32 (MS Windows), Excel

Programming Language

Visual Basic for Applications (VBA)

Database Environment

Microsoft SQL Server, Microsoft Access

2008-05-20

Зачем нужна система формирования отчетов

Отчеты Excel из DelphiВ свое время, когда для системы учета потребовалось сделать систему вывода информации на печать перебрал много разных систем формирования отчетов — некоторые, как FastReport, имели в составе редакторы, и замечательно формировали печатные формы, но чаще всего пользователям нравится, когда можно быстро и красиво получить информацию в ихнем любимом Excel, не прибегая к конверторам и т.п.

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

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

Основу шаблона составляют “бэнды”. Бэнд это одна или более строк, расположенных подряд и имеющих одну и ту-же метку — название бэнда. Оно проставляется в первой колонке каждой строки. Вот так примерно будет выглядеть готовый шаблон отчета:

Пример шаблона отчета Excel для генератора отчетов

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

Сам класс отчета будет очень простым:

 TA7xReport = class(TComponent)
  private
    Excel, TemplateSheet: Variant;
    Progress: TWcProgress;
    CurrentLine: integer; // текущая строка построения отчета
    FirstBandLine, LastBandLine: integer; // положение последнего добавленного бэнда
  protected
  public
    procedure OpenTemplate(FileName: string);
    procedure PasteBand(BandName: string);
    procedure SetValue(VarName: string; Value: Variant);
    procedure Show;
    destructor Destroy; override;
  published
  end;

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

  Excel := CreateOleObject('Excel.Application');
  Excel.Workbooks.Open(FileName, True, True);
  TemplateSheet := Excel.Workbooks[1].Sheets[1];
  Excel.DisplayAlerts := False; // Чтобы подавить сообщение об ошибке при замене ненайденного значения в операции SetValue
  CurrentLine := 1;
  Progress := TA7xProgress.Create(Self);
  Application.ProcessMessages;

Здесь упоминается объект TA7xProgress, который представляет собой просто форму, с парой меток, для вывода пользователю прогресса построения отчета, чтобы он не подумал что программа висит.

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

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

  FirstBandLine := 0; LastBandLine := 0;
  i := CurrentLine;
  while ((LastBandLine = 0) and (i < CurrentLine + MaxBandLines)) do begin
    v := Variant(TemplateSheet.Cells[i, 1].Value);
    if (varType(v) = varOleStr) and (FirstBandLine = 0) then begin
      if v = BandName then begin // нашли начало бенда
        FirstBandLine := i;
      end;
    end;
    if (FirstBandLine <> 0) then begin
      if not ((varType(v) = varOleStr) and (v = BandName)) then LastBandLine := i - 1;
    end;
    inc(i);
  end;

Далее этот найденный шаблон копипастим сразу за последним вставленным нами бэндом, если такой уже был:

  Range := TemplateSheet.Rows[IntToStr(FirstBandLine) + ':' + IntToStr(LastBandLine)];
  Range.Copy;
  Range := TemplateSheet.Rows[IntToStr(CurrentLine) + ':' + IntToStr(CurrentLine)];
  Range.Insert;

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

  CurrentLine := CurrentLine + (LastBandLine - FirstBandLine) + 1;
  // вычисляем позицию куда был скопирован бэнд
  FirstBandLine := CurrentLine - (LastBandLine - FirstBandLine) - 1;
  LastBandLine := CurrentLine - 1;

Следующий метод, который будет использоваться для вывода значений на уже скопированный кусок шаблона – SetValue. Код этого метода предельно прост, так как выполняет поиск-замену значения в заданном диапазоне средствами самого экселя:

  Range := TemplateSheet.Rows[IntToStr(FirstBandLine) + ':' + IntToStr(LastBandLine)];
  Range.Replace(VarName, s);

Метод SetValue должен вызываться столько раз, сколько у нас находится значений на нужном нам бэнде. Ну и наконец после того как все бэнды будут выведены, вызывается последнй метод – Show, в котором наконец удаляется сам шаблон, который вытеснялся в конец документа, и эксель перевдится из невидимого состояния в видимое.

Здесь стоит упомянуть, что если скажем прервать отладку отчета до вызова Excel.Visible := true; то эксель так и останется висеть в памяти невидимкой, а для следующего отчета будет создана новый экземпляр Excel.

Как выглядит применение генератора отчетов в Delphi-коде

Теперь самое интересное – пример использования этого генератора отчетов:

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

procedure TdrashodForm.Print(Template: string);
var i: integer;
  summa_: double;
  h : string;
begin
  Rep.OpenTemplate(Template);
  Rep.PasteBand('TITLE');
  Rep.SetValue('#ID_RASHOD#', ID_RASHOD);
  Rep.SetValue('#PB_NAME#', PbFrame.PbEdit.Text);
  Rep.SetValue('#D#', DateToStr(NaklDTP.Date));
  Rep.SetValue('#POSTNAME#', PostEdit.Text);
  i := 1; summa_ := 0;
  NaklQuery.First;
  while not NaklQuery.Eof do begin
      Rep.PasteBand('STR');
      Rep.SetValue('#N#', i);
      h := NaklQuery['KH_NAME'];
      if h<>'' then h := '['+h+']';
      Rep.SetValue('#NM_NAME#', NaklQuery['NM_NAME']+' '+h);
      Rep.SetValue('#QUANT#', NaklQuery['RSD_QUANT']);
      Rep.SetValue('#SUMMA#', coalesce(NaklQuery['RSD_OSUMMA'],0));
      Rep.SetValue('#NM_UNIT#', coalesce(NaklQuery['NM_UNIT'],''));
      Rep.SetValue('#ZK_NUMBER#', coalesce(NaklQuery['ZK_NUMBER'],''));
      Rep.SetValue('#TW_NAME#', coalesce(NaklQuery['TW_NAME'],''));
      summa_ := summa_ + coalesce(NaklQuery['RSD_OSUMMA'],0);
      inc(i);
    NaklQuery.Next;
  end;
  Rep.PasteBand('FOOT');
  Rep.SetValue('#SUMMA_#', summa_);
  Rep.Show;
end;

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

И теперь самое главное…

Где взять эту замечательную систему да еще и бесплатно

Домашняя страница проекта, находится на a7in.com

Время на прочтение
7 мин

Количество просмотров 5.1K

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

В настоящее время все большую популярность набирают облачные решения для визуализации данных, демонстрируя двузначный рост год-к-году по большинству показателей. Однако не все компании — клиенты поставщиков облачных решений могут позволить себе использовать “облака” по самым разным причинам: от требований безопасности данных до недостаточной функциональности или даже более высокой стоимости владения по сравнению с on-premise. 

Поэтому время от времени возникают задачи подготовки отчетности для визуализации в on-premise-инструментах. Автор долгое время работал и продолжает работать с решениями SAP, поэтому именно решения SAP (SAP BW/4, SAP S/4), как поставщики данных для отчетности, наиболее близки. Однако предлагаемый подход может быть скопирован и на другие системы-источники. Никаких препятствий к этому нет.

Задача формулируется так: реализовать on-premise решение по автоматической и регулярной подготовке отчетов по бизнес-данным SAP-систем (BW/4 или S/4).

Технические требования к отчетам:

  • минимальное время отклика при открытии отчета пользователем (2-4 сек)

  • возможность смотреть отчеты как на настольном, так и на мобильном устройстве (планшет, телефон)

  • комбинация табличных и графических представления данных на одной странице

  • несколько страниц (вкладок) с отчетами

  • корректное разбиение на страницы при печати на принтере/PDF.

  • отдельные табличные представления могут содержать большое число строк и/или столбцов (более 1 млн ячеек совокупно).

  • табличные представления 

    • реализуют кросс-табличный формат

    • содержат итоги, подитоги по строкам и столбцам.

    • содержат иерархические группировки

    • содержат данные нарастающим итогом по календарным аналитикам

    • поддерживают функции “автофильтр”, “фиксация прокрутки” и некоторые другие функции Excel (например, комментарии к ячейкам, merge cells)

    • добавление статических картинок

    • условное форматирование ячеек

  • построены по csv-выгрузкам данных из исходных систем. Это означает, что отчеты содержат данные, сформированные на момент выгрузки данных из исходных систем. Чем чаще выполняется выгрузка, тем более актуальные данные в отчетах.

Наиболее близкими стандартными инструментами SAP, подходящими для этой задачи, является a)SAP BW on HANA + Broadcaster или b)SAP BW/4 HANA + SAP BI Platform + SAP Crystal Report Enterprise + Publishing.

  • Вариант a) подходит для клиентов, остающихся пока на решении SAP BW on HANA. 

  • Вариант b) — для клиентов, перешедших на новое перспективное решение SAP BW/4 HANA и имеющих лицензии SAP BI Platform

Однако, оба этих варианта имеют те или иные ограничения, которые может быть крайне непросто или даже невозможно преодолеть, в т.ч. даже с добавлением “точечной” разработки (макросы VBA, ABAP-программы или кода на встроенном script-языке в Crystal Reports). Даже если в первоначальной постановке задачи нерешаемых требований нет, это не означает, что в будущем такие требования не появятся. Это означает, что лучше с самого начала выбрать подход, достаточно гибкий для решения как известных сейчас, так и неизвестных, но ожидаемых будущих требований. 

Очевидно, что реализация третьего варианта, варианта с) будет с высокой долей собственной разработки отдельных компонентов целевого решения. Какие же опции могут существовать для технологических решений SAP в этом случае?

Существуют классы из библиотеки ABAP2XLSX (Apache2 licence model). Написанные на ABAP, они содержат методы по генерации XLSX-файла без использования Excel: XLSX-файлы создаются напрямую из кода на ABAP. Создав программную “обертку” для этих классов внутри S/4 или BW, вы можете генерировать XLSX-файлы с требуемым форматированием. Запускать эту генерацию возможно как диалоговом, так и в пакетном (batch) режиме, что позволяет настроить автоматическую публикацию, рассылку и пр. по расписанию или событиям. 

Таким образом, все стадии процесса: извлечение данных, преобразования в формат отчета и генерация XLSX реализуются на одном языке (ABAP), и являются неотъемлемой частью хорошо знакомой в SAP-мире инфраструктуры жизненного цикла и среды выполнения ABAP-кода.

Но представляет интерес и альтернативный подход, который при определенном масштабе более эффективен и функционален. Речь идет о генерации XLSX-файла в коде на Python. Существует минимум две очень популярные python-библиотеки генерации Excel-кода: openpyxl и xlsxwriter. Это дает больше гибкости при выборе и развитии вашего решения, чем использование единственно возможной библиотеки на ABAP.  

Процессы целевого решения могут выглядеть, например, так:

  • Данные системы-источника выгружаются на регулярной основе в текстовые csv-файлы. Файлы выгружаются в каталоги сервера приложений SAP или на выделенный файловый сервер или даже в облачный каталог, если это позволяют политики безопасности.

  • csv-файлы считываются Python-программой, которая затем выполняет обработку данных (расчет подитогов, итогов, расчет нарастающих итогов, объединение нескольких наборов данных, подтягивание наименований к кодам аналитик и т.п и т.д.), генерацию и публикацию XLSX-файла

Архитектура целевого решения состоит из:

  • SAP-системы-источника данных, которая генерирует csv-файлы. Генератором файлов может быть: 

    • разработанные ABAP-программы, 

    • SAP BW OpenHub. 

В последнем случае легко настроить считывание данных из т.н. BW-запросов, в которых нередко реализуют сложную логику расчета бизнес-показателей пользовательских отчетов. Инструмент создания BW-моделей и -запросов (SAP HANA Studio BWMT) нельзя назвать инструментом “конечного пользователя”, но тем не менее он обладает мощным функционалом, позволяющим в создании даже сложных объектов во многих случаях обходиться без программирования. Однако, верно и то, что с помощью этого инструмента добиться желаемой производительности (см. требования выше) практически не представляется возможным. Поэтому результат работы BW-запроса — [кросс-]табличное представление данных — приходится визуализировать в Excel не стандартным инструментом SAP Analysis for Excel, а просто Excel. Но XLSX-файл нужно ещё подготовить и где-то разместить. А для этого применяются….

  • Python-программы, использующие библиотеки:

    • работы с данными — pandas;

    • генерации XLSX-файлов  — openpyxl или/и xlsxwriter;

    • любые другие библиотеки Python для дополнительных преобразований считанных из файлов данных;

  • Сервер для запуска Python-программ. При наличии лицензий, можно рассмотреть SAP Data Intelligence, но подойдут и другие решения. На самых начальных этапах, при небольшом количестве Python-программ — генераторов XLSX-файлов можно обойтись средствами планирования задач операционной системы

  • Полученный XLSX-файл конечный пользователь может просмотреть как в Excel, так и его opensource-аналогах (LibreOffice, OpenOffice). Последнее особенно важно для компаний, в которых не используются продукты Microsoft Office или планируется от них отказаться. Использование же стандартного on-premise SAP-инструмента для визуализации данных BW-отчетов — SAP Analysis for Excel невозможно без Excel, т.к. технически он является его addOn.

Несколько слов про центральную часть архитектуры —  Python-программы. Принимая во внимание, что генерировать XLSX-файл можно и с помощью ABAP-библиотеки, в Python это делать во многих аспектах удобнее:

  • существенно больше программистов на Python, чем на ABAP. Python изучают чуть ли не в школах, не говоря уже о ВУЗах. Нетрудно предположить, что и уровень зарплатных ожиданий для Python также ниже при прочих равных

  • Существенно больше готовых и бесплатных Python-библиотек, которые выполнят необходимые преобразования и расчеты.

  • возможно разрабатывать Python-код для генерации XLSX-файлов без доступа к SAP-системам, практически в offline-режиме. Так удобнее отдавать работу на outsource.

  • проще пользоваться всеми возможностями git-репозиториев. Да, в ABAP это тоже возможно, но это гораздо менее распространено.

  • Более лаконичный код, чем на ABAP. 

Например, при таких исходных данных для визуализации

src =  ['1;1;2020;1;10;30',
        '1;1;2020;2;11;31',
        '1;1;2021;1;12;32',
        '1;1;2021;2;13;33',
        '1;2;2020;1;14;34',
        '1;2;2020;2;15;35',
        '1;2;2021;1;16;36',
        '1;2;2021;2;17;37',
        '2;1;2020;1;18;38',
        '2;1;2020;2;19;39',
        '2;1;2021;1;20;40',
        '2;1;2021;2;21;41',
        '2;2;2020;1;22;42',
        '2;2;2020;2;23;43',
        '2;2;2021;1;24;44',
        '2;2;2021;2;25;45',
        ]

следующий компактный код

char_count  = 4 # первые 4 колонки (поля) в каждой строке данных соответствуют аналитикам. Остальные (2 в этом примере) - показатели.
column_count = src[0].count(";")+1 # общее число полей в строке с данными
src = list(map(lambda s: s.split(';'), src))
src = list(map(lambda row: list(map(lambda e, i: float(e) if i >= char_count else e, row, range(len(row)))), src))
df = pd.DataFrame.from_records(src, columns = list(map(lambda i: chr(65+i), range(column_count))))                
table = pd.pivot_table(df, values='E', index=['A', 'B'], columns=['C', 'D'], aggfunc=np.sum, margins = False)

выполняет преобразование строк в исходном массиве: разбивает строку на поля, разделенные ;. Последние 2 поля, которые по смыслу есть “показатели”, сохраняются как числа float. Первые 4 поля — это аналитики и хранятся как строки.

На выходе кода — кросс-таблица, в которой по строкам — развертка по полям A и B, а по столбцам — развертка по полям C и D. Поле E — один (из двух в исходных данных) показателей, используемый в кросс-таблице.

C    2020        2021
D       1     2     1     2
A B
1 1  10.0  11.0  12.0  13.0
  2  14.0  15.0  16.0  17.0
2 1  18.0  19.0  20.0  21.0
  2  22.0  23.0  24.0  25.0

Если добавить код для расчета подитогов и итогов по строкам и столбцам (еще 5 — 6 строк кода), получится

C                    2020                   2021                  all total
D                       1     2 total-2020     1     2 total-2021 all total
A         B
1         1          10.0  11.0       21.0  12.0  13.0       25.0      46.0
          2          14.0  15.0       29.0  16.0  17.0       33.0      62.0
          1 total    24.0  26.0       50.0  28.0  30.0       58.0     108.0
2         1          18.0  19.0       37.0  20.0  21.0       41.0      78.0
          2          22.0  23.0       45.0  24.0  25.0       49.0      94.0
          2 total    40.0  42.0       82.0  44.0  46.0       90.0     172.0
all total all total  64.0  68.0      132.0  72.0  76.0      148.0     280.0

Очевидно, что это уже почти готовая “матрица” для публикации в XLSX, после чего останется только применить форматирование [опять же, методами Python-библиотеки генерации XLSX] и файл готов. Файл можно публиковать средствами Python или операционной системы, и затем открывать в Excel, LibreOffice и т п.

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

Например, если Python-код потребляет слишком много памяти при выполнении подготовки данных (слишком сложные расчеты или/и миллионы строк в выгрузке), то можно, не меняя степени параллелизации выполнения Python, и не выполняя аппаратного масштабирования, рассмотреть перенос вычислений или/и большее уплотнение данных на стороне SAP-сервера. Возможно и обратное, когда уменьшать нагрузку следует уже на SAP-сервер.

Генерация документов и отчетов из реестра или базы данных Excel с помощью кода VBA для просмотра, анализа и вывода на печать. Создание, заполнение, удаление.

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

Создание документа или отчета из шаблона

Допустим, у нас есть реестр документов или база данных Excel с какой-либо информацией. Чтобы создать документ или отчет и заполнить его данными, необходим его шаблон (печатная форма).

Шаблон создаем вручную на отдельном рабочем листе или с помощью кода VBA при заполнении документа. Способы создания шаблона определяют место его хранения:

  • В коде VBA. Такой способ возможен для несложных печатных форм.
  • В рабочей книге Excel с реестром или базой данных на скрытом листе.
  • В отдельном файле, который не обязательно сохранять как шаблон Excel (*.xlt*).

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

Из редких исключений можно назвать печать ценников на основании товарной накладной, сохраненной в Excel. Шаблон ценника и программный код VBA приходится хранить в отдельном файле, по команде из которого открывается и обрабатывается товарная накладная.

Назовем лист с шаблоном документа или отчета — «Shablon». Для создания новой печатной формы скрытый лист с шаблоном необходимо скопировать:

With Worksheets(«Shablon»)

  .Visible = True

  .Copy After:=Worksheets(Worksheets.Count)

  .Visible = False

End With

Данный код вставит новый лист с шаблоном в ту же книгу после всех имеющихся листов. Чтобы шаблон скопировать в новую книгу, заменяем строку .Copy After:=Worksheets(Worksheets.Count) на .Copy. Печатная форма откроется в новом файле.

Сразу после создания, обращаться к новому листу можно как к активному (ActiveSheet), даже в том случае, если он скопирован в новую книгу. Чтобы воспользоваться этой возможностью, порядок действий в программе должен быть следующим:

  1. Копирование информации в переменную массива из реестра или базы данных.
  2. Создание нового документа или отчета из шаблона.
  3. Заполнение документа или отчета, обращаясь к нему как к ActiveSheet.

Копирование информации и заполнение формы

Копирование информации

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

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

Допустим, что лист называется «Reestr», а необходимая информация расположена в десяти первых ячейках каждой строки:

Dim nomer As Long, massiv() As Variant

nomer = ActiveCell.Row

massiv = Range(Cells(nomer, 1), Cells(nomer, 10))

Если для отчета требуется информация из нескольких строк, можно выделить их вертикально по любому столбцу и использовать следующий код:

Dim nomer1 As Long, nomer2 As Long, massiv() As Variant

nomer1 = Selection.Cells(1).Row

nomer2 = Selection.Cells(Selection.Cells.Count).Row

massiv = Range(Cells(nomer1, 1), Cells(nomer2, 10))

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

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

Создание и заполнение формы

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

Заполняем ячейки печатной формы информацией из массива, обращаясь к листу с формой как к ActiveSheet. Если новый документ или отчет создан в той же книге, ее можно автоматически сохранить:

Чтобы не наблюдать за мерцаниями экрана во время выполнения процедуры VBA Excel, можно временно отключить его обновления:

Sub GeneratsiyaDokumenta()

Application.ScreenUpdating = False

операторы

Application.ScreenUpdating = True

End Sub

Удаление печатной формы

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

Для этого необходимо на лист шаблона правее печатной формы вставить кнопку из коллекции «Элементы управления ActiveX». Дважды кликнуть по ней и в открывшемся модуле листа внутрь автоматически сгенерированного объявления процедуры вставить строку Call UdaleniyeLista:

Sub CommandButton1_Click()

  Call UdaleniyeLista

End Sub

Затем в модуль текущей книги добавляем процедуру:

Sub UdaleniyeLista()

  Application.DisplayAlerts = False

    ActiveSheet.Delete

  Application.DisplayAlerts = True

End Sub

Кнопка вместе с шаблоном будет копироваться в новую печатную форму. При нажатии кнопки активный лист будет удаляться кодом VBA без предупреждения от Excel.

Many small-scale businesses do not use database management systems to generate reports. Most of them stick to spreadsheet packages. Unfortunately Spreadsheet softwares are not equipped with  advanced query and report generating features. However, some Excel users struggle with Reports that they update, save and print manually. It is a very tedious job, but VBA can make it better. I have created a spreadsheet application you may used to fill a template/report with different “Records“, save each in a separate workbook, and print automatically.

Borrowing a few terms from Database Packages, first, we need to organize the data that goes into the report in a neat ‘Table’. A database entry, also called a ‘Record‘, has information under various headers called ‘Fields‘. A record needs to be uniquely Identifiable with a ‘Key‘. In this application, the ‘Table‘ is stored in the ‘Data‘ Sheet. The ‘Field‘ names are stored in a Row named ‘Field Names‘. Each Row corresponds to a ‘Record‘ and the ‘Keys‘ are generated automatically when the user clicks the ‘Set-up‘ button.

The next thing we need to do is to set-up the Report which needs to be filled with data from the ‘Table‘.  This is done in the ‘Template‘ sheet. The cells in this sheet need to be linked to ‘Fields‘ corresponding to the required ‘Record‘. This is accomplished by using an intermediate ‘Mapping‘ Sheet. In this sheet, the user can extract all the fields of a particular record, by specifying its ‘Key‘. Clicking the ‘Export‘ button loops through all the ‘Keys‘, updates the ‘Mapping‘ sheet and exports the ‘Template’ into individual workbooks.

Excel Report Generator

I urge you to download the application from the section below, and give it a whirl.


How to use this application?

Step 1 : Import your records into the ‘Data’ sheet.

  • Fill in the Field Names/Column Headers
  • Copy paste records. Preferably as Values.
  • Click the ‘Set Up’ Button

Step 2 : Setup your Template

  • Paste your Template into the ‘Template’ Sheet. Copy pasting all (Ctrl+V).
  • Link the “Fields” to the cells in the ‘Mapping’ spreadsheet
  • Print Page Setup the sheet. Also set the Default Printer to the available one.

Step 3 : Set Preferences in the ‘Mapping’ sheet

  • Which records you want to save?  – Set the Start and Stop Record Numbers
  • Do you want to print each export when they are created? – Yes/No
  • Set up a file name tag that is unique for each record. Example: =”Record – “&TEXT(RecordNumber,”000”). Feel free to use the mapping ranges to add in any unique Identification Numbers. Make sure it does not contain any special characters. Set up a similar name for the sheet name.
  • Choose if you’d like to protect the sheets, workbooks and File; and specify the respective password. Remember Protecting the sheet does not allow users to make changes to the sheet; protecting the workbook does not allow the user to change the structure of the workbook. And protecting the file does not allow the user to open the workbook without keying in the correct password.
  • While saving a workbook, Excel usually prompts the user for confirmation if a workbook with the same name exists already. Setting the ‘Replace Without Prompt’ option to ‘Yes’ will replace existing files without the prompt.
  • Click the browse button to specify where the output files should be saved. If left blank, the files would be saved along with this workbook.
  • Hit the ‘Reset’ button to bring back the default options.

Step 4 : Hit the Export Button


How to incorporate these into your other projects?

I understand that you May not be interested in using this Spreadsheet application in its entirety. However, you may interested in incorporate the Loop-and-Export functionality into your existing applications. Therefore, I have two versatile Macros that you can use in your applications.

Macro 1 : Macro that exports a Spreadsheet into a Workbook.

'====================================================================================
'A macro that exports a sheet to a workbook. It has a few optional arguments that
'make it versatile.
'Arguments:
'WhichSheet - Worksheet Object. The sheet that needs to be exported. If not specified,
'               The active sheet will be exported.
'ToWhichBook - Workbook Object. The sheet specifed earlier will be copied to this
'               workbook. If not specified, a new workbook will be created.
'               If you'd like a reference to the workbook that was created, pass an
'               empty workbook object as argument to this sub. Will come on handy while
'               exporting multiple WorkSheets to a new workbook. And it will also help
'               in saving the created workbook later.
'SheetName - String Variable. An optional arguemnt that will be used to set the name of
'               the newly created sheet. If not specified, it will be set to the name
'               of 'WhichSheet'
'OnlyValues - Boolean Variable. An optional argument that can be set to True to convert
'               all the references to values. It is set to True by defauly. Set it to
'               False if you'd like to retain the formulas.
'ProtectSheet - Boolean Variable. Set to true if you'd like to protect the newly created
'               WorkSheet.
'SheetPassword - String Variable. Password to protect the sheet with.

'Author : Ejaz Ahmed - ejaz.ahmed.1989@gmail.com
'Date: 30 January 2013
'Website : http://strugglingtoexcel.wordpress.com/
'====================================================================================
Sub ExportSheetToBook(Optional ByRef WhichSheet As Worksheet, _
                        Optional ByRef ToWhichBook As Workbook, _
                        Optional ByVal SheetName As String = "", _
                        Optional ByVal OnlyValues As Boolean = True, _
                        Optional ByVal ProtectSheet As Boolean = False, _
                        Optional ByVal SheetPassword As String = "")

'Declare Sub Level Objects/Variables

'A newly created workbook contains a number of sheets. All of them cannot
'be deleted, one has to remain. It has to be deleted only after the intended
'sheet has been imported.
Dim shtDeleteFinal As Worksheet
'A WorkSheet object to use in the for loops
Dim shtEach As Worksheet
'A WorkSheet object to hold the sheet that was imported
Dim shtCopied As Worksheet
'A long variable to use in for loops
Dim lngCount As Long
'A Boolean variable to hold the DisplayAllert status temporarily
Dim booStatusAlerts As Boolean

'Set the boolean variable to the current DisplayAlerts status
booStatusAlerts = Application.DisplayAlerts

'Set WhichSheet to ActiveSheet if a sheet was not passed to the sub
If WhichSheet Is Nothing Then
    Set WhichSheet = Application.ActiveSheet
End If

'Create a new workbook if no workbook was passed to the sub. A new
'workbook will be created even if an uninitialized workbook object
'was passed to the sub.
If ToWhichBook Is Nothing Then
    'Create a new Workbook
    Set ToWhichBook = Application.Workbooks.Add
    'Set the First Sheet to a WorkSheet object, for deleting it later
    Set shtDeleteFinal = ToWhichBook.Sheets(1)
        'Supress Alerts, for excel prompts for a confirmation when the
        'user deletes a WorkSheet
        Application.DisplayAlerts = False
        'Delete all the WorkSheets except the first one
        For Each shtEach In ToWhichBook.Sheets
            If Not shtEach Is shtDeleteFinal Then
                shtEach.Delete
            End If
        Next shtEach
End If

'Copy the WorkSheet to the intended workbook before the first WorkSheet
WhichSheet.Copy Before:=ToWhichBook.Worksheets(ToWhichBook.Sheets(1).Name)
'Now the newly copied WorkSheet will be the first sheet in the workbook
'Initialize a WorkSheet object to remember the sheet
Set shtCopied = ToWhichBook.Worksheets(ToWhichBook.Sheets(1).Name)

'If the user wants only Values, convert formula to values.
If OnlyValues Then
    Call modSupport.ChangeValueToFormula(shtCopied.Cells.SpecialCells(xlCellTypeFormulas))
End If

'Delete the One sheet that was not deleted earlier
If Not shtDeleteFinal Is Nothing Then
    shtDeleteFinal.Delete
    Set shtDeleteFinal = Nothing
End If

'Restore the DisplayAlerts status
Application.DisplayAlerts = booStatusAlerts

'If the user specified a WorkSheet name, then rename it
If Not SheetName = vbNullString Then
    shtCopied.Name = SheetName
End If

'If the user wants to protect the sheet, Protect it with the
'specified password.
If ProtectSheet Then
    shtCopied.Protect Password:=SheetPassword
End If

End Sub

Macro 2 : Loops through a specified range of ‘Keys’ and Exports the ‘Templates’.

'====================================================================================
'A macro that loops through a specified range of 'Key' and exports the 'Templates'.
'It contains a lot of optional arguments that may be used the customize the export
'Process
'Arguments:
'LngStart - Long Variable. Starting 'Key' number
'LngEnd - Long Variable. Ending 'Key' number
'rngUpdate - Range Object - The cell that needs to be updated with the current
'               'Key' number that is being exported. The Index functions used to
'               lookup the records depend on this cell.
'shtTemplate = WorkSheet object. The Template sheet that needs to be exported
'strOutputFolder - String Variable - Folder path  where the files need to be
'                   saved. This workbook's path will be used if left blank.
'booProtectSheet, booProtectBook, booProtectFile - Boolean Variables. The
'                   respective objects will be protected with corresponding
'                   passwords specified in strPwdSheet, strPwdBoook, strPwdFile
'                   respectively.
'strPwdSheet, strPwdBoook, strPwdFile - String Variables. Passwords to protect the
'                   sheet, book and file respectively.
'rngSheetName - Range Object - The user has the option to set up a cell that will
'                   create a unique name for the WorkSheet that is exported. If
'                   this arguement is not specified a default name is generated.
'rngFileName - Range Object - The user has the option to set up a cell that will
'                   create a unique name for the WorkBook that is created. If this
'                   arguement is not specified a default name is generated.
'booSaveFile - Boolean Variable. User can choose if each export is saved as an
'               individual file.
'booCloseFile - Boolean Variable. The user can choose if the workbook should be
'               closed after Saving/Printing.
'booPrint - Boolean Variable. The user can choose if the sxported sheet should be
'               printed using the default printer.
'booReplace - Boolean Variable. The user can choose to supress the "Do you want to
'               replace the file? prompt.
'booUpdateStatus - Boolean Variable. The user can choose if the status bar should
'               show a quick update on which record is being processed.
'booPromptComplete - Boolean Variable. The user can choose to dispaly an alert when
'               the loop is done
'
'Author : Ejaz Ahmed - ejaz.ahmed.1989@gmail.com
'Date: 30 January 2013
'Website : http://strugglingtoexcel.wordpress.com/
'====================================================================================
Sub ExportRun(ByVal LngStart As Long, _
                ByVal LngEnd As Long, _
                ByRef rngUpdate As Range, _
                ByRef shtTemplate As Worksheet, _
                Optional ByVal strOutputFolder As String = vbNullString, _
                Optional ByVal booProtectSheet As Boolean = False, _
                Optional ByVal strPwdSheet As String = vbNullString, _
                Optional ByVal booProtectBook As Boolean = False, _
                Optional ByVal strPwdBook As String = vbNullString, _
                Optional ByVal booProtectFile As Boolean = False, _
                Optional ByVal strPwdFile As String = vbNullString, _
                Optional ByRef rngSheetName As Range, _
                Optional ByRef rngFileName As Range, _
                Optional ByVal booSaveFile As Boolean = True, _
                Optional ByVal booCloseFile As Boolean = True, _
                Optional ByVal booPrint As Boolean = False, _
                Optional ByVal booReplace As Boolean = True, _
                Optional ByVal booUpdateStatus As Boolean = False, _
                Optional ByVal booPromptComplete As Boolean = True)

'An empty WorkBook object to retain a reference to the new
'workbook that would be created by the 'ExportSheetToBook' macro
Dim wkbBook As Workbook
'A Long variable used in the For Loops
Dim lngCount As Long
'String Variables to hold the Sheet, Book and File names.
Dim strSheetName As String
Dim strBookName As String
Dim strBookPath As String

'If the user did not specify a part, use ThisWorkBook's path
If strOutputFolder = vbNullString Then
    strOutputFolder = shtTemplate.Parent.Path &amp; ""
End If

'If the user chooses to Replace Files without Prompt
'Suppress the Alerts
If booReplace Then
    Dim booStatusAlerts As Boolean
    booStatusAlerts = Application.DisplayAlerts
    Application.DisplayAlerts = False
End If

'Start the Loop
For lngCount = LngStart To LngEnd
    'Update the Record Number
    rngUpdate.Value = lngCount
    'Recalculate Worksheet (in case the user is in Manual Calculate Mode)
    rngUpdate.Worksheet.Calculate

    'If the user has specifed a Cell that contains a SheetName Tag,
    'Use it, else set it to Null
    If Not rngSheetName Is Nothing Then
        strSheetName = rngSheetName.Value
    Else
        strSheetName = vbNullString
    End If

    'If the SheetName tag is empty, create a Generic Name using the
    'Record Number
    If strSheetName = vbNullString Then
        strSheetName = shtTemplate.Name &amp; "_" &amp; _
                        Format(lngCount, String(Len(CStr(LngEnd)), "0"))
    End If

    'If the user wants the status bar to be updated, update it
    'with Record Number and Sheetname
    If booUpdateStatus Then
        Application.StatusBar = "Record " &amp; _
                        Format(lngCount, String(Len(CStr(LngEnd)), "0")) &amp; _
                        " of " &amp; LngEnd &amp; " | " &amp; strSheetName
    End If

    'Export the Sheet using an Empty WorkBook argument to retain a reference
    'to the newly created WorkBook
    Call ExportSheetToBook(shtTemplate, wkbBook, strSheetName, True, _
                                booProtectSheet, strPwdSheet)

    'If the user has specifed a range that contains a FileName tag,
    'then use it. Else, set it to Null.
    If Not rngFileName Is Nothing Then
        strBookName = rngFileName.Value
    Else
        strBookName = ""
    End If

    'If the FileName tag is empty, create a Generic Name using the
    'Record Number
    If strBookName = "" Then
        strBookName = strSheetName
    End If

    'Update a String Variable to hold the full Path
    strBookPath = strOutputFolder &amp; strBookName

    'Protect Workbook
    If booProtectBook Then
        wkbBook.Protect Password:=strPwdBook
    End If

    'Save the file
    If booSaveFile Then
        'If the user chooses to Password protect the file,
        'protect it with the specifed password
        If booProtectFile Then
            wkbBook.SaveAs FileName:=strBookPath, Password:=strPwdFile
        Else
            wkbBook.SaveAs FileName:=strBookPath
        End If
    End If

    'Print the worksheet
    If booPrint Then
        wkbBook.Worksheets(strSheetName).PrintOut
    End If

    'Close the File
    If booCloseFile Then
            wkbBook.Close
    End If

    'Set the WorkBook object to Nothing for the next iteration
    Set wkbBook = Nothing
Next lngCount

'Restore the StatusBar Message
If booUpdateStatus Then
    Application.StatusBar = False
End If

'Restore the Dsiplay Alerts Status
If booReplace Then
    Application.DisplayAlerts = booStatusAlerts
End If

'Show the user a Completed Message
If booPromptComplete Then
    Dim strMsgbox As String
    strMsgbox = "The Export is now done." &amp; vbNewLine &amp; _
                "Records " &amp; LngStart &amp; " to " &amp; LngEnd &amp; _
                " have been exported." &amp; _
                vbNewLine &amp; "The files are saved in : " &amp; _
                strOutputFolder
    MsgBox strMsgbox, vbInformation
End If

End Sub

In fact, even in my application, I have used a macro that fetches all the arguments that need to be passed on to the aforementioned macros. I have added in detailed comments to help you follow my code.


Download

Download


Update

I extended this tool to handle upto 500 fields for a fellow struggler. Here is the link to that file. It has not been tested as much as I would have liked.

Download

Понравилась статья? Поделить с друзьями:
  • Генератор неповторяющихся случайных чисел excel
  • Генератор макросов для excel
  • Генератор кодов для word
  • Генератор ключей для word 2016
  • Генератор ключей для microsoft word 2019