# Запись JEXL-макросов

Сервис записи JEXL-макросов преобразует действия пользователя в готовые JEXL-скрипты. Инструмент создает сценарии автоматизации без ручного написания кода и глубокого изучения JEXL.

**Основные сценарии использования:**
- Массовое создание объектов — добавление позиций номенклатуры, контрагентов, других сущностей.
- Пакетное обновление данных — изменение реквизитов у группы объектов.
- Формирование отчетных форм — создание печатных форм с настройкой шаблонов.
- Импорт данных из файлов — обработка XLSX-файлов для переноса информации.

```{note}
Записываются только вызовы `Api` и `Pkg`. Макросы не содержат вызовы интерактивной логики.
```

## Создание макросов

### Начало записи

1. Перейдите в **Сервис** -> **Инструменты** -> **Начать запись JEXL-скрипта**.
2. В главном меню появится красный флаг — индикатор записи.

```{note}
Перед записью сохраните или откатите все несохраненные данные во всех формах.

При несохраненных данных система выдаст ошибку о невозможности изменения активности перехвата вызовов методов.
```

### Запись действий

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

### Завершение и сохранение

1. Остановите запись через:
   - Красный флаг в главном меню.
   - **Сервис** -> **Инструменты** -> **Закончить запись JEXL-скрипта**.
2. Просмотрите записанный скрипт в появившемся окне.
3. При закрытии окна подтвердите сохранение.
4. Выберите папку (по умолчанию — **Личные**).
5. Скрипт сохранится в Библиотеке скриптов.

## Просмотр и редактирование

Для хранения, просмотра, редактирования и запуска сохраненных скриптов используется библиотека JEXL.

Путь: `Настройка системы > Настройки и сервисы > Сервисы JEXL > Библиотека JEXL`.

В библиотеке отображается дерево каталогов и скриптов. По умолчанию доступны два каталога:

- **Личные** — содержит скрипты, доступные только текущему пользователю.
- **Общего назначения** — содержит скрипты, доступные всем пользователям.

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

Чтобы открыть скрипт в редакторе, дважды нажмите на нужный пункт дерева. Открытый скрипт можно просмотреть, изменить и выполнить.

**Аудит запусков скрипта**

На вкладке **Аудит** отображаются результаты запуска выбранного скрипта.

Данные можно отфильтровать:

- по дате выполнения;
- по пользователю, который выполнил запуск.

Если запуск завершился с ошибкой, подробности отображаются в окне **Стек ошибок**.

В окне **Дополнительная информация** отображаются данные, которые автор скрипта добавил в лог с помощью команды `audInfo`.

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

## Примеры скриптов

**Создание печатной формы:**
```
var rop_Rpt_Report1 = Rpt_ReportApi.insert();
Btk_ObjectGroupApi.register(rop_Rpt_Report1, 27851L, 1B, 1B);
Rpt_ReportApi.setsSystemName(rop_Rpt_Report1, 'SomeReportName');
Rpt_ReportApi.setsCaption(rop_Rpt_Report1, 'Отчет');
Rpt_ReportApi.setidModule(rop_Rpt_Report1, 901L);
var rop_Rpt_ReportVersion1 = Rpt_ReportVersionApi.insertByParent(rop_Rpt_Report1);
Rpt_ReportVersionApi.setidReportType(rop_Rpt_ReportVersion1, 451L);
```
В сформированном скрипте все идентификаторы имеют исходный вид. Если этот скрипт планируется использовать на других базах данных, где идентификаторы будут отличаться, необходимо заменить их получение в скрипте - например, на метод `findByMnemoCode`.

**Видоизмененный скрипт:**
```
var rop_Rpt_Report1 = Rpt_ReportApi.insert();
Btk_ObjectGroupApi.register(rop_Rpt_Report1, 27851L, 1B, 1B);
Rpt_ReportApi.setsSystemName(rop_Rpt_Report1, 'SomeReportName');
Rpt_ReportApi.setsCaption(rop_Rpt_Report1, 'Отчет');
Rpt_ReportApi.setidModule(rop_Rpt_Report1, Btk_ModuleApi.findByMnemoCode('btk'));
var rop_Rpt_ReportVersion1 = Rpt_ReportVersionApi.insertByParent(rop_Rpt_Report1);
Rpt_ReportVersionApi.setidReportType(rop_Rpt_ReportVersion1, Rpt_ReportTypeApi.findByMnemoCode('jasper'));
```
## Обработка данных Excel

Для работы с `xlsx` в `jexl` используется библиотека [poi.apache.org](https://poi.apache.org/).

Для удобства загрузки файлов написана [функциональная библиотека](../020_common/040_языки_разработки.md#написание-библиотек) `Bts_XlsxPkg`. Метод `uploadParseFiles` открывает диалог выбора файла и парсит его, предоставляя разработчику возможность его обработки. Создается объект класса `org.apache.poi.ss.usermodel.Workbook`.

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

```jexl
lib("Btk_XlsxLib").uploadParseFiles(fun);
```
Где `fun` - это функция Jexl, которая будет выполняться для обработки данных из Excel.

Пример обработки xlsx:
```jexl
var fun = x -> {
  var sheet = x.getSheet("Материалы");
  var lastRowNum = sheet.getLastRowNum();
  var i = 1;
  while (i <= lastRowNum ){
    var svNomName = sheet.getRow(i).getCell(3).getStringCellValue(); //  Условное наименование
    var idvMSRItem = sheet.getRow(i).getCell(4).getNumberCellValue().toBigDeсimal(); // ЕИ
    var idvGost = sheet.getRow(i).getCell(5).getStringCellValue(); // ГОСТ
    var idvSortamentType = sheet.getRow(i).getCell(6).getNumberCellValue().toBigDeсimal(); // Тип сортамента

   var rop_Bs_Goods1 = Bs_GoodsApi.insert();
   Bs_GoodsApi.setsNomName(rop_Bs_Goods1, svNomName); // установка условного наименования
   Bs_GoodsApi.setidMeasureItem(rop_Bs_Goods1, idvMSRItem); // установка ЕИ
   Bs_GoodsApi.setsGost(rop_Bs_Goods1, svNomName); // установка условного наименования
   Bs_GoodsApi.setsNomName(rop_Bs_Goods1, svNomName); // установка условного наименования

   commit();
   i = i+1;
  }
  true;
}

lib("Btk_XlsxLib").uploadParseFiles(fun);
```

Приведенный ниже пример функции `fun` иллюстрирует, как обработать данные из файла Excel и создать объекты в системе на их основе.

Расширенная обработка с разными типами данных:
```jexl
var fun = x -> {
// Получение листа "Лист1" из файла Excel
var sheet = x.getSheet("Лист1");

// Определение количества заполненных строк в файле
var lastRowNum = sheet.getLastRowNum();
var i = 0;

while (i <= lastRowNum) {
// Класс для которого мы создаем объекты
var rop_RplTst_AllDbTypes1 = RplTst_AllDbTypesApi.insert();

    // Получение значений из ячеек Excel и преобразование их к необходимым типам
    // Для получения значения типа Long нужно дополнительно использовать метод toLong
    var vjson = sheet.getRow(i).getCell(0).getStringCellValue();
    var vsystemname = sheet.getRow(i).getCell(1).getStringCellValue();
    var vcaption = sheet.getRow(i).getCell(2).getStringCellValue();
    var vgidrefclassany = sheet.getRow(i).getCell(3).getStringCellValue();
    var vidobjecttype = sheet.getRow(i).getCell(4).getNumericCellValue().toLong();
    var vdate = toDate(sheet.getRow(i).getCell(5).getStringCellValue());
  
    // Преобразование для NNumber 
    var vNumber = sheet.getRow(i).getCell(6).getNumericCellValue().toBigDecimal();

    // Установка значений для созданного объекта
    RplTst_AllDbTypesApi.setjJson(rop_RplTst_AllDbTypes1, vjson);
    RplTst_AllDbTypesApi.setsSystemName(rop_RplTst_AllDbTypes1, vsystemname);
    RplTst_AllDbTypesApi.setsCaption(rop_RplTst_AllDbTypes1, vcaption);
    RplTst_AllDbTypesApi.setgidRefAny(rop_RplTst_AllDbTypes1, vgidrefclassany);
    RplTst_AllDbTypesApi.setNumber(rop_RplTst_AllDbTypes1, vNumber);
    
    // Проверка и установка idObjectType только если он не равен 0
    if (vidobjecttype != 0) {
      RplTst_AllDbTypesApi.setidObjectType(rop_RplTst_AllDbTypes1, vidobjecttype);
    }
    
    // Установка даты
    RplTst_AllDbTypesApi.setdDate(rop_RplTst_AllDbTypes1, vdate);

    // Сохранение изменений
    commit();
    
    // Увеличение счетчика строк
    i = i + 1;
}

// Возврат булевого значения, необходимого для выполнения скрипта
true;
}
```

**Запуск скрипта**

После написания функции `fun`, выполните скрипт, и появится модальное окно для выбора файлов Excel. Выберите необходимые файлы, и функция начнет обработку данных и создание объектов в системе на основе данных из Excel.

С помощью этой библиотеки вы можете эффективно работать с данными Excel и использовать записанные Jexl-скрипты для массовой обработки и создания объектов по сложным сценариям.

Полный тестовый скрипт обработки Excel:
```jexl
var fun = x -> {
  var sheet = x.getSheet("Лист1");
  var lastRowNum = sheet.getLastRowNum();
  var i = 0;
  while (i <= lastRowNum ){
    var rop_RplTst_AllDbTypes1 = RplTst_AllDbTypesApi.insert();
    var vjson = sheet.getRow(i).getCell(0).getStringCellValue();
    var vsystemname = sheet.getRow(i).getCell(1).getStringCellValue();
    var vcaption = sheet.getRow(i).getCell(2).getStringCellValue();
    var vgidrefclassany = sheet.getRow(i).getCell(3).getStringCellValue();
    var vidobjecttype = sheet.getRow(i).getCell(4).getNumericCellValue().toLong();
    var vdate = toDate(sheet.getRow(i).getCell(5).getStringCellValue());

    RplTst_AllDbTypesApi.setjJson(rop_RplTst_AllDbTypes1, vjson);
    RplTst_AllDbTypesApi.setsSystemName(rop_RplTst_AllDbTypes1, vsystemname);
    RplTst_AllDbTypesApi.setsCaption(rop_RplTst_AllDbTypes1, vcaption);
    RplTst_AllDbTypesApi.setgidRefAny(rop_RplTst_AllDbTypes1, vgidrefclassany);
    if (vidobjecttype != 0) RplTst_AllDbTypesApi.setidObjectType(rop_RplTst_AllDbTypes1, vidobjecttype);
    RplTst_AllDbTypesApi.setdDate(rop_RplTst_AllDbTypes1, vdate);

    commit();
    i = i+1;
  }
true;
}

lib("Btk_XlsxLib").uploadParseFiles(fun);
```

```{attention}
- **Пустые ячейки**: могут возвращать 0 вместо null — добавляйте проверки.
- **Типы данных**: следите за соответствием типов Excel и системы. В приведенном примере, используется метод getStringCellValue() для получения текстовых значений, и getNumericCellValue().toLong() для числовых значений.
- **Возвращаемое булево значение**: функция должна возвращать true/false.
```

## Работа с файлами

### Загрузка во временный каталог
```
files.uploadFileToTemp(res -> {
  var file = res.file(); //java.io.File
  //чтение содержимого файла
  var utils = new('org.apache.commons.io.FileUtils');
  var content = utils.readFileToString(file, "UTF-8"); 

  dialogs.showMessage(content);
  true;
});
```
```{note}
Работает только для jexl-скриптов в контексте выборки.
```

### Выгрузка строки как файла
```
var text = "Привет!"
files.downloadBytes(text.getBytes("UTF-8"), "Имя_Файла.txt", true, false);
```
```{note}
Работает только для jexl-скриптов в контексте выборки.
```

## Необходимые права

**Для использования сервиса:**
1. Стандартные права на приложение и доступ к **Сервис** -> **Инструменты** -> **Закончить запись JEXL-скрипта**.
2. Привилегия **Доступ к инструментам меню Сервиса** для объекта `Btk_ManagementPkg`.