Разработка генераторов документов
Перед разработкой настройте плагин Document. Для заполнения шаблонов используются контекстные переменные.
Генерация документа
| Это описание способа генерирования документов с использованием билдеров и контекстных переменных с описанием методов получения каждого поля. |
Для генерирования документа используется ContentBaseDocumentGenerator либо DocumentGenerator. Если вы хотите использовать этот способ генерации, в конфигурации паттерна документа в параметре выбора скрипта необходимо указать:
document:pattern.N.script=ru.bgcrm.plugin.document.docgen.ContentBaseDocumentGenerator
| Подробнее о том, как создать свой билдер и дополнять методы для получения данных описано в документации к Билдерам полей. |
| Документация по DocumentGenerator. |
Описание работы
Генератор использует подход, при котором описываются данные для записи в документ, т.е. единицей является информация, заполняемая в, заранее подготовленные, интерактивные поля документа.
Шаги работы:
-
Из шаблона, указанного в конфиге, берется список интерактивных полей для заполнения. Получение происходит через метод интерфейса ru.bgcrm.plugin.document.docgen.DocBuilder#getFieldNames
-
По имени поля сначала ищется соответствующая контекстная переменная, причём происходит комбинированный поиск. Так, для сущности процесса ("process"), происходит поиск не только по собственным переменным, но и по переменным первой найденной связанной сущности единого договора ("bgbilling-commonContract"), а также первой найденной сущности контрагента ("customer"). Похожая логика работает в отношении единого договора - к его контекстным переменным добавляются переменные контрагента.
-
Если для данного поля находится подходящая контекстная переменная, её значение записывается в Map - отношение имени поля, к его значению.
-
Иначе по имени поля, определяется метод билдера, который вызывается, для получения значения поля
-
Данный метод вызывается, а полученное значение преобразовывается в String
-
Значение записываются в Map - отношение имени поля, к его значению.
-
По полученному отношению заполняется шаблон документа. Для заполнения испльзуется интерфейс DocBuilder#writeValues, который имеет имплементации для DOCX и PDF.
-
Сгенерированный документ записывается в объект DocumentGenerateEvent в виде байтов.
Билдеры полей
Билдер полей представляет из себя класс содержащий методы получения данных конкретного поля. Как правило класс насле́дуется от класса FieldDataBuilder в котором есть общие методы по работе с получением данных для докуметов. Новый класс билдера следует помещать в пакет ru.bgcrm.dyn.ufanet.document.builder.
Создание билдера:
-
Создать класс в пакете ru.bgcrm.dyn.ufanet.document.builder
-
Наследовать от FieldDataBuilder
-
Добавить новый класс в конфигурацию "Плагин Document" в параметр handler.{@inc:id}={class path}
public class TmcFieldsBuilder extends FieldDataBuilder
{}
Объект билдер создается тогда, когда требуется получить значение поле у одного из его методов, описанных с помощью аннотации @FieldName("field_name"). За создание объекта билдера отвечает класс DocMapper. Какой конкретно объект билдера нужно создать определяется по имени очередного поля - по нему получается Class объекта из DocHandlersRegister.getBuilderMethod("field_name").
| После создания, объект кешируется в DocMapper до конца генерации всего документа. При генерации следующего документа, билдер будет создан заново. |
При создании, билдеру поступает контекст. Контекст состоит из DocumentGenerateEvent и Connection.
В методе билдера можно использовать данный контекст, уже некоторые определенные DAO и методы (например получения родительского процесса) из суперкласса.
Вызовы методов билдера
Когда вам необходимо добавить обработчик для поля, которого еще нет, вы можете либо добавить его в уже существующий класс билдера, или, если вы можете выделить группу полей в отдельный класс, создать новый билдер.
Создание метода обработчика
Пример метода:
@FieldName("serv_city")
public String getCity(){
return getAddress()
.map(this::extractCity)
.orElse(null);
}
Метод для получения значения по имени поля строится по следующим правилам:
-
метод должен быть помечен аннотацией FieldName;
-
метод не имеет входных параметров.
Возвращаемые значения
Метод может возвращать любые значения. Не обязательно (даже не рекомендуется) приводить их к String самостоятельно. Например для методов возвращающих стоимость (чье лучшее представление BigDecimal) или состояния чекбокса (представленное boolean). Полученные значения приводятся к String с помощью FieldDataMapper.getValue. С актуальным списком классов для преобразования лучше ознакамливаться в данном методе.
| Методы билдеров должны заботиться лишь о данных, а не об их представлении. |
Возвращаемые коллекции
Метод может возвращать любого наследника Collection. Это может быть полезно при:
-
Заполнении таблиц. Имена полей в таблицах (списках) формируются с индексами через нижнее подчеркивание (field_1, field_2). В таком случае в качестве названия поля следует использовать имя поля без индекса (field) и возвращать коллекцию.
Пример:
@FieldName("monthly_price")
public List<RentCost> monthlyPrices() throws BGException {
List<StorageLeftoverMoving> tmcList = getSpecTmc();
return defineRentCosts(tmcList);
}
В данном примере возвращается список арендных плат для полей "monthly_price_n", где n индекс поля.
-
когда поле может состоять из нескольких составляющих и они все объединяются в одно поле.
Пример:
@FieldName(value = "fio_si", fillingMode = SINGLE_FIELD)
public Set<String> companyRepresentatives() throws BGException {
Process parentProcess = getParentProcess();
return getProcessExecutors(parentProcess).stream()
.map(this::getUserTitle)
.collect(Collectors.toSet());
}
Код выше возвращает список представителей компании или СИ, которых может быть несколько, но они все должны заполняться в одно поле. Для подобного поведения используется дополнительный параметр аннотации @FieldName - fillingMode. По умолчанию это значение равно TABLE - что предполагает, что данные будут заполняться в листовые поля с индексами как в примере выше. Так же в аннотации есть параметр delimiter, который определяет разделитель для списка полученных данных. По умолчанию это ", ".
Передача дополнительных данных в билдер
На данный момент единственный способ передать данные внутрь билдера - через DynActionForm. Необходимо иметь в виду что такой способ является достаточно накладным, так как необходимо преобразовывать данные в текстовый формат. Однако если запрос на генерацию приходит из вне, например через API работы с ЭДО ('EDOProcessAction, EDOAgreementAction'), и эти данные уже в форме, то можно их сразу получить в виде класса через методы 'parseBody' или 'parseBodyFromParam'.
Важное замечание по работе с ЭДО
При текущей модели электронных документов, они оборачиваются в процесс "Документ", обработчик которого вызывает генерацию документа. Из этого следует, что при создании метода билдера поля необходимо учитывать, что при получении данных из процесса они находятся не в текущем, а в родительском процессе. (Получить его можно через метод FieldDataBuilder#getParentProcess())