Обзор наиболее крупных и "вкусных" блоков функциональности
I. Запросы:
1. Существующая функциональность:
- Универсальные, легко переопределяемые запросы к физическим и виртуальным таблицам.
- Конструктор параметров запросов. Отборы, поля, сортировки, итоги и др. Возможность слияния и/или дополнения нескольких параметров (например, дефолтные и пользовательские).
- Автогенерируемые запросы. Декларативные запросы к любым физическим и виртуальным таблицам с любыми отборами.
- Автогенерируемые запросы. Возможность запроса по нескольким таблицам одного класса (документы, справочники и.т.п.).
- Повторное использование единожды написанных запросов. Конструктор позволяющий получить результат запроса или сам запрос на основании существующего шаблона запроса и переданных параметров.
- Возможность построения единого интерфейса обращения к данным, с использованием прокси поддерживающего автогененируемые, перепределенные разработчиком и произвольные запросы через один и тот же интерфейс. То есть обращение к данным всегда осуществляется через одну и ту же точку входа, будь то поиск документа, получение стандартной или переопределенной выборки из таблицы БД или абсолютно произвольного запроса по его имени.
- Удобная опция по помещению результата запроса в контейнер (РезультатЗапроса, ТаблицаЗначений, ДеревоЗначений, Массив, СписокЗначений, Структура). По умолчанию включена в проксируемые запросах, но доступна для использования и независимо.
2. Планы развития (уже есть решения в тестовых ветках, но не было большой потребности в доведении до релиза):
а. Пакетные запросы.
б. Запрос-объединение и/или пакетный запрос с независимыми наборами таблиц и параметров.
II. Расширение классов ДокументОбъект, справочникОбъект, ПВХОбъект и частично их менеджеров.
1. Существующая функциональность:
- Улучшенный поиск взамен функций НайтиПо... Поддерживает сложные отборы.
- Улучшенный поиск взамен функции Выбрать(). Поддерживает более сложные отборы, позволяет более тонко настраивать результат запроса. Для деталей см. параграф I
- Новый конструктор для инициализации новых объектов как при создании, так и по ссылке. При создании сразу можно передать данные заполнения.
- Упрощенный интерфейс для работы с БД. Функции Провести(), Распровести(), ПометитьНаУдаление(), СнятьПометкуНаУдаление() возвратом флага успеха/неуспеха операции
д. Логирование ошибок. Возможность переопределения формата записи ошибки в ЖР (при инициализации объекта). Также, текст исключения может быть получен для иных операций.
- Безопасная запись с соблюдением правил работы с транзакций. Функциональность по умолчанию работает так как заложено платформой, но предусмотрено гибкое переопределение поведения объекта в случае ошибок. Например не бросать исключение если запись не удалась, а пробросить информацию об исключении в вызывающий код или не регистрировать ошибку в ЖР, а лишь сообщить о ней пользователю.
- Расширение событийной модели. Добавлена возможность обработки событий: ДоНачалаТранзакции, ПослеФиксацииТранзакции, ПослеОтменыТранзакции.
- Мелкие удобства типа встроенной проверки наличия реквизита в объекте и изначально заполненного реквизита ссылка сразу после инициализации объекта.
III. Класс менеджер для упрощенной работы с внешними обработчиками.
1. Параметризуемая инициализация с учетом опций безопасного режима и защиты от опасных действий.
2. Ведение списка инициализированных объектов
3. Выборочная или полная деструкция инициализированных менеджером объектов.
IV. Движение по регистрам.
1. Движение по регистрам накопления без описания движений в документе. В обработке проведения вызывается функция ДвиженияПоРегистрам, куда передаются метаданные регистра, таблица движений (как правило это выгрузка из ТЧ).
2. Движения можно выполнять в режиме эмуляции. В этом случае движения не будут записаны, а вернутся в виде таблицы значений.
3. Автоматически накладывается управляемая блокировка на регистр. Если блокировка недоступна, то накладывается автоматическая блокировка средствами платформы.
4. Для регистров остатков предусмотрен подбор и контроль отрицательных остатков по набору измерений.
5. При необходимости, алгоритм подбора остатков может быть переопределен для выбранных регистров. В данном случае, специализированный класс-расширение для менеджера регистра накопления, выполнит не собственный универсальный метод, а запросит переопределенный регистр и вернет полученный результат. Подробнее см. параграф V.
V. Расширение классов РегистрНакопленияМенеджер и РегистрСведенийМенеджер.
1. Существующая функциональность:
- Возможность получить срезы данных со сложным отбором, как на определенную дату/момент времени, так и актуальные.
- Возможность выполнения движений по регистру с подбором остатков при списании (для РН с видом остатки). Подбор производится по классической схеме с контролем остатков до движения, при этом можно переопределить поведение выбранного регистра и отключить контроль. При подборе автоматически подбираются измерения и ресурсы.
- Возможность для гибкого переопределения. Может быть переопределено всё - от текста запроса остатков (по-умолчанию генерится автоматически), до набора измерений и параметров списания ресурсов. Переопределение осуществляется созданием соответствующего API в переопределяемом регистре. Интерфейс создаётся через прилагаемый к библиотеке каталог шаблонов без написания кода вручную.
2. Планы развития (уже есть решения в тестовых ветках, но не было большой потребности в доведении до релиза):
- Добавить поддержку прочих регистров, по которым могут выполняться движения. Приоритет реализации: регистры бухгалтерии, регистры расчета.
VI Динамическая подмена кода.
1. Существующая функциональность:
- Возможность подмены модулей объектов, менеджера и и.т.д. Как целиком так и фрагментарно (здесь потребуются точки входа в отдельных методах)
- Подмена реализуется через создание точек входа в часто изменяемых участках кода. При первом обращении выполняется правка, создание точки входа и обновление. При повторном обращении код подменяется без обновления.
- В случае получения отчетов/обработок через единый метод подсистемы, точки входа не нужны.
- При следующем (плановом) обновлении, замененный код, снова будет выполняться из "родного" источника, а не из внешнего обработчиками. То есть не нужно беспокоиться о том, когда и в каких объёмах будет обновление. Достаточно, чтобы велся версионный контроль в репозитории конфигурации. Принцип работы подсистемы - "подменил и забыл", по аналогии с системами управляемого вооружения класса "выстрелил и забыл".
VII Расширение класса таблиц. Преимущественно ТЗ, но в ряде случаев поддерживаются все возможные таблицы от дерева значений до результата запроса и табличной части.
1. Существующая функциональность:
- Конструкторы для новой пустой ТЗ по результату запроса и по метаданным.
- Конструктор кросс-таблицы из обычной таблицы с возможностью выбора списка измерений строк и целевого показателя.
- Конструктор по выборке. В отличие от конструктора по результату запроса позволяет сформировать или дополнить таблицу по части данных (если выборка с итогами), а не по всему результату запроса.
- Сложение/вычитание таблиц по аналогии со сложением и вычитанием матриц, но с поддержкой матриц разного размера, где матрицы приводятся к единому размеру виртуально по набору измерений.
- Добавление недостающих и расширение несуществующих методов как то: отбор по сложному отбору, дополнение таблицы не только строками но и колонками, итоги по отбору, удаление по отбору, нумерация, индексация, конвертация в массив массивов и обратно, вывод в табличный документ и др.
- Объединение таблиц "как в запросе" с объединением типов колонок. Добавление колонки с объединением типов, если колонка уже существует.
VIII Каталог шаблонов для использования наиболее популярной функциональности. Помимо готового кода содержит примеры реализации с подробными комментариями.
Библиотека позволяет решать комплекс проблем за счет нескольких стратегических решений, которые вы можете принять и осуществить с использованием библиотеки:
I Стандартизация и сокращение объема кода. Повышение его предсказуемости и безопасности
Данный продукт родился как решение по автоматизации и стандартизации труда программистов. Вместо запоминания, или подвешивания десятков, а то и сотен строк кода на шаблоны кода или иные кодогенераторы, была реализована легкая библиотека содержащая часто используемые методы. Как результат, скорость реализации прикладных решений может возрасти в несколько раз:
- За счет сокращения объёма кода, который необходимо написать
- За счет сокращения объёма кода, который необходимо перечитать хотя бы раз (в реальной разработке бывает намного чаще)
- За счет стандартизации методологии разработки, что сокращает число обидных ошибок, когда забыл обработать исключение или неверно откатил транзакцию.
Пример I-1. Сравнение способов записи группы объектов.
Задача
1. Есть два комплекта данных заполнения для записи элемента справочника Контрагенты и связанного с ним (через поле владелец) Договора контрагента
2. В модуле заполнения обоих объектов предусмотрено заполнение по переданным данным. Просто использовать метод "Заполнить"
3. Объекты должны записаться или оба или ни один
4. В случае возникновения исключений необходимо перехватить его
4.1 Информациию об ошибке передать в параметр пИнформацияОбОшибке
4.2 Ошибку зарегистрировать в журнале регистрации. Имя события: "ФоновоеЗаданиеЗагрузкаДанныхССайта.СозданиеКонтрагентовИзФайлаОбмена". Комментарий должен содержать информацию о точном месте и контексте возникновения исключения
5. Функция должна вернуть истину если оба объекта записались успешно, и ложь в обратном случае
Решение 1. Штатными средствами платформы
Функция Демо_1(пДанныеЗаполненияКонтрагент, пДанныеЗаполненияДоговор, пИнформацияОбОшибке = Неопределено)
Отказ = Ложь;
ОбщийКонтекст = "ФоновоеЗаданиеЗагрузкаДанныхССайта"; // Заполнять если важно отличать где именно произошла ошибка
МестоОшибки = "СозданиеКонтрагентовИзФайлаОбмена"; // Заполнять если слишком много событий в общем контексте. Описание конкретного места возникновения ошибки имя функции или краткое описание фрагмента кода
ОписаниеКонтекста = ОбщийКонтекст + "." + МестоОшибки;
КонтрагентОбъект = Справочники.Контрагенты.СоздатьЭлемент();
КонтрагентОбъект.Заполнить(пДанныеЗаполненияКонтрагент);
КонтрагентСсылка = Справочники.Контрагенты.ПолучитьСсылку();
КонтрагентОбъект.УстановитьСсылкуНового(КонтрагентСсылка);
ДоговорОбъект = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
ДоговорОбъект.Заполнить(пДанныеЗаполненияДоговор);
ДоговорОбъект.Владелец = КонтрагентСсылка;
НачатьТранзакцию();
Попытка
КонтрагентОбъект.Записать();
Исключение
Отказ = Истина;
пИнформацияОбОшибке = ИнформацияОбОшибке();
ПодробноеПредставлениеОшибки = ПодробноеПредставлениеОшибки(пИнформацияОбОшибке);
ТекстСообщения = ОписаниеКонтекста + ". Ошибка выполнения:"
+ Символы.ПС + ПодробноеПредставлениеОшибки(пИнформацияОбОшибке);
Сообщить(ТекстСообщения);
УровеньЖР = УровеньЖурналаРегистрации.Ошибка;
Метаданные = КонтрагентОбъект.Метаданные();
ЗаписьЖурналаРегистрации(ОписаниеКонтекста, УровеньЖР, Метаданные, КонтрагентОбъект, ПодробноеПредставлениеОшибки);
КонецПопытки;
Если Не Отказ Тогда
Попытка
ДоговорОбъект.Записать();
Исключение
Отказ = Истина;
пИнформацияОбОшибке = ИнформацияОбОшибке();
ПодробноеПредставлениеОшибки = ПодробноеПредставлениеОшибки(пИнформацияОбОшибке);
ТекстСообщения = ОписаниеКонтекста + ". Ошибка выполнения:"
+ Символы.ПС + ПодробноеПредставлениеОшибки(пИнформацияОбОшибке);
Сообщить(ТекстСообщения);
УровеньЖР = УровеньЖурналаРегистрации.Ошибка;
Метаданные = ДоговорОбъект.Метаданные();
ЗаписьЖурналаРегистрации(ОписаниеКонтекста, УровеньЖР, Метаданные, ДоговорОбъект, ПодробноеПредставлениеОшибки);
КонецПопытки;
КонецЕсли;
Если Не Отказ Тогда
Попытка
ЗафиксироватьТранзакцию();
Исключение
Отказ = Истина;
пИнформацияОбОшибке = ИнформацияОбОшибке();
ОтменитьТранзакцию();
КонецПопытки;
Иначе
ОтменитьТранзакцию();
КонецЕсли;
Возврат Не Отказ;
КонецФункции
Решение 2. С ограниченным использованием библиотеки
Функция Демо_2(пДанныеЗаполненияКонтрагент, пДанныеЗаполненияДоговор, пИнформацияОбОшибке = Неопределено)
Отказ = Ложь;
ОбщийКонтекст = "ФоновоеЗаданиеЗагрузкаДанныхССайта"; // Заполнять если важно отличать где именно произошла ошибка
МестоОшибки = "СозданиеКонтрагентовИзФайлаОбмена"; // Заполнять если слишком много событий в общем контексте. Описание конкретного места возникновения ошибки имя функции или краткое описание фрагмента кода
ОписаниеКонтекста = СобытияЖР_ауф.СформироватьОписаниеСобытия(ОбщийКонтекст, МестоОшибки);
КонтрагентОбъект = Справочники.Контрагенты.СоздатьЭлемент();
КонтрагентОбъект.Заполнить(пДанныеЗаполненияКонтрагент);
КонтрагентСсылка = Справочники.Контрагенты.ПолучитьСсылку();
КонтрагентОбъект.УстановитьСсылкуНового(КонтрагентСсылка);
ДоговорОбъект = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
ДоговорОбъект.Заполнить(пДанныеЗаполненияДоговор);
ДоговорОбъект.Владелец = КонтрагентСсылка;
БросатьИсключениеПриОшибке = Ложь; // информацию об отказе необходимо пробросить в вызывающий код
ПараметрыЗаписи = ОбщегоНазначения_ауф.ПараметрыБезопаснойЗаписи(ОписаниеКонтекста, , , БросатьИсключениеПриОшибке);
НачатьТранзакцию();
Если ОбщегоНазначения_ауф.ЗаписатьБезопасноСРегистрацией(КонтрагентОбъект, ПараметрыЗаписи, пИнформацияОбОшибке) Тогда
Если Не ОбщегоНазначения_ауф.ЗаписатьБезопасноСРегистрацией(ДоговорОбъект, ПараметрыЗаписи, пИнформацияОбОшибке) Тогда
пИнформацияОбОшибке = КонтрагентОбъект.ИнфоОбОшибке();
Отказ = Истина;
КонецЕсли;
Иначе
пИнформацияОбОшибке = КонтрагентОбъект.ИнфоОбОшибке();
Отказ = Истина;
КонецЕсли;
Если Не Отказ Тогда
Если Не ОбщегоНазначения_ауф.ФиксацияТранзакции(пИнформацияОбОшибке, Отказ) Тогда
ОтменитьТранзакцию();
КонецЕсли;
Иначе
ОтменитьТранзакцию();
КонецЕсли;
Возврат Не Отказ;
КонецФункции
Решение 3. На все деньги. С использованием всех возможностей библиотеки
Функция Демо_3(пДанныеЗаполненияКонтрагент, пДанныеЗаполненияДоговор, пИнформацияОбОшибке = Неопределено)
Отказ = Ложь;
ОбщийКонтекст = "ФоновоеЗаданиеЗагрузкаДанныхССайта"; // Заполнять если важно отличать где именно произошла ошибка
МестоОшибки = "СозданиеКонтрагентовИзФайлаОбмена"; // Заполнять если слишком много событий в общем контексте. Описание конкретного места возникновения ошибки имя функции или краткое описание фрагмента кода
ОписаниеКонтекста = СобытияЖР_ауф.СформироватьОписаниеСобытия(ОбщийКонтекст, МестоОшибки);
Контрагент = Объекты_ауф.НовыйСправочник(Справочники.Контрагенты, пДанныеЗаполненияКонтрагент, Ложь, Ложь, , ОписаниеКонтекста);
Договор = Объекты_ауф.НовыйСправочник(Справочники.ДоговорыКонтрагентов, пДанныеЗаполненияДоговор, Ложь, Ложь, , ОписаниеКонтекста);
Договор.УстановитьСвойство("Владелец", Контрагент.Ссылка);
БросатьИсключениеПриОшибке = Ложь; // информацию об отказе необходимо пробросить в вызывающий код
Контрагент.ИзменитьПараметрыЗаписи(,, БросатьИсключениеПриОшибке);
Договор.ИзменитьПараметрыЗаписи(,, БросатьИсключениеПриОшибке);
НачатьТранзакцию();
Если Контрагент.Записать() Тогда
Если Не Договор.Записать() Тогда
пИнформацияОбОшибке = Договор.ИнфоОбОшибке();
Отказ = Истина;
КонецЕсли;
Иначе
пИнформацияОбОшибке = Контрагент.ИнфоОбОшибке();
Отказ = Истина;
КонецЕсли;
Если Не Отказ Тогда
Если Не ОбщегоНазначения_ауф.ФиксацияТранзакции(пИнформацияОбОшибке, Отказ) Тогда
ОтменитьТранзакцию();
КонецЕсли;
Иначе
ОтменитьТранзакцию();
КонецЕсли;
Возврат Не Отказ;
КонецФункции
Пример I-2. Движения по любому регистру накопления для любого документа в 1-2 строки
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения_ДемоРегистрНакопления(ЭтотОбъект, РежимПроведения, Отказ);
КонецПроцедуры
Процедура Движения_ДемоРегистрНакопления(пДокОбъект, пРежимПроведения, пОтказ)
ТабСписания = пДокОбъект.Товары.Выгрузить();
МетаРег = Метаданные.РегистрыНакопления.ДемоРегистрНакопления;
ДвижениеПоРегистрам_ауф.РН_ДвижениеРасход(пДокОбъект, МетаРег, ТабСписания, пРежимПроведения, пОтказ);
// Под капотом метода РН_ДвижениеРасход():
// 1. Установка управляемой блокировки изменяемые записи.
// Если управляемые блокировки отключены или не поддерживаются платформой, то накладываются автоматические блокировки
// 2. Подбор остатков (контроль отрицательных) с учетом режима проведения. Может быть отключен.
// - Параметры подбора могут быть переопределены. Автоматически подбор идет по измерениям регистра.
// Можно переопределить, изменив набор полей
// - Сам запрос остатков для подбора и иных целей тоже может быть переопределен.
// Например, можно добавить соединения с другими таблицами.
// - По-умолчанию, при наличии отрицательных остатков вызывается исключение. Но это переопределяется тоже
// 3. Набор записей заполняется подобранными остатками
// 4. Если необходимо, то можно получить таблицу для списания, так как РН_ДвижениеРасход() это функция
// - также можно отключить запись движений и просто получить таблицу.
КонецПроцедуры
II Сокращение числа зависимостей между интерфейсами и архитектурой БД.
Отказ от десятков, а то и сотен зависимостей между кодом и архитектурой, за счет повторного использования текстов запроса или их автоматической генерации. Это также позволяет экономить на поддержке текстов запросов.
Также, благодаря декларативному интерфейсу, написание рутинных запросов можно доверить и новичку и он не допустит обидных ошибок, приводящих к падению производительности.
В руках опытного специалиста, библиотека станет надежным помощником в деле изоляции интерфейсов от архитектуры БД. Как следствие - повышение гибкости программы и облегчение её сопровождения.
Например, можно написать ровно одну функцию возвращающую результат запроса по какой-либо физической таблице. При этом, текст запроса писать не обязательно. Появится новое поле в таблице или переименуется старое - не беда. Не нужно переписывать все тексты. Их или нет, или он хранится в одном месте. Там где список полей определен явно, потребуется внести изменения. остальные запросы автоматически добавят новое поле в выборку.
Нет необходимости собирать тексты запроса программно или делать костыли типа Не &ОтборПоОрганизации Или Организация = &Организация. Запросы параметризуются как по вашим настройкам, так и автоматически.
Фактически, это является реализацией на 1с MVC паттерна (MVC - ссылка вики) с предусмотренной защитой от наиболее распространенной ошибки применения паттерна - толстые контроллеры.
Пример II-1. Запросы данных
Данные решения избавляют от необходимости программной сборки запросов и в большинстве случаев от написания и поддержки актуальности текстов запросов вообще.
При этом, все запросы формируемые через специальные интерфейсы (Прокси-запросы, модули менеджеров, движения по регистрам), являются переопределяемыми "из коробки". В данном случае, если не существует определенного разработчиком текста запроса для физической или виртуальной таблицы, то он сгенерируется автоматически. Если же потребности изменятся, то и текст запроса можно создать самому, добавив недостающие данные. Предыдущие обращения, при этом не потребуется модифицировать - они продолжат работать, но результат будут получать не из автосгенерированного, а из определенного разработчиком текста запроса.
Решение 1. Запрос данных из физической таблицы (справочники, документы, регистры и.т.д.)
Функция ЗапросНастроекОбменаСБанком(пОрганизация, пБанковскиеСчета)
ИмяРегистра = Метаданные.РегистрыСведений.НастройкиОбменаСКлиентомБанка.Имя;
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьОтбор("Организация", "Равно", пОрганизация);
ПараметрыЗапроса.ДобавитьОтбор("БанковскийСчет", "ВСписке", пБанковскиеСчета);
РезультатЗапроса = Запросы_ауф.ЗапросДанных("РегистрыСведений", ИмяРегистра, ПараметрыЗапроса);
Возврат РезультатЗапроса;
КонецФункции
Функция НастройкаОбменаСБанком(пОрганизация, пБанковскийСчет)
ИмяРегистра = Метаданные.РегистрыСведений.НастройкиОбменаСКлиентомБанка.Имя;
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьОтбор("Организация", "Равно", пОрганизация);
ПараметрыЗапроса.ДобавитьОтбор("БанковскийСчет", "Равно", пБанковскийСчет);
РезультатЗапроса = Запросы_ауф.ЗапросДанных("РегистрыСведений", ИмяРегистра, ПараметрыЗапроса);
НастройкаОбмена = ОбщегоНазначения_ауф.РезультатЗапросаВСтруктуру(РезультатЗапроса);
Возврат НастройкаОбмена;
КонецФункции
// Данный вариант расширяет возможности переопределения,
// так как любой объект БД имеет возможность переопределить запрос к своим данным
// Следовательно, обращаясь к данным через прокси,
// не придется переписывать код после переопределения запроса
Функция НастройкаОбменаСБанком_ВариантПроксиЗапроса(пОрганизация, пБанковскийСчет)
МетаданныеРегистра = Метаданные.РегистрыСведений.НастройкиОбменаСКлиентомБанка;
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьОтбор("Организация", "Равно", пОрганизация);
ПараметрыЗапроса.ДобавитьОтбор("БанковскийСчет", "Равно", пБанковскийСчет);
НастройкаОбмена = ПроксиЗапросы_ауф.ЗапросДанных(МетаданныеРегистра, ПараметрыЗапроса, "Структура");
Возврат НастройкаОбмена;
КонецФункции
Решение 2. Запрос данных из нескольких физических таблиц одного класса (несколько документов или справочников и.т.д.)
// Для наглядности весь код помещен в одну функцию
Функция ЗапросРегистраторовПоОтбору()
// получить имена регистраторов с организацией
РегистрМенеджер = РегистрыНакопления.Заказы;
ВсеИменаРегистраторов = ОбщегоНазначения_ауф.СписокИменРегистраторовРегистра(РегистрМенеджер);
ИменаРегистраторов = Новый Массив;
Для Каждого ИмяДок Из ВсеИменаРегистраторов Цикл
Если ОбщегоНазначения_ауф.ЕстьРеквизитОбъекта(МетаДок, "Организация") Тогда
ИменаРегистраторов.Добавить(ИмяДок);
КонецЕсли;
КонецЦикла;
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьОтбор("Дата", ВидСравнения.ИнтервалВключаяГраницы, НачалоПериода, КонецПериода);
ПараметрыЗапроса.ДобавитьОтбор("Проведен", "=", Истина)
ПараметрыЗапроса.ДобавитьОтбор("Организация", "=", Организация);
ПараметрыЗапроса.ДобавитьПолеСортировки("Дата");
ПараметрыЗапросаАУФ = ПараметрыЗапроса.ПолучитьАУФ();
РезультатЗапроса = Запросы_ауф.ЗапросОбъектов("Документы", ИменаРегистраторов, ПараметрыЗапросаАУФ);
// допустим, мы забыли, что у документов основной реквизит это ссылка
ОсновноеПоле = Запросы_ауф.ОсновновнойРеквизитКоллекцииВЗапросе("Документы");
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл // пример обработки результата
ДокСсылка = Выборка[ОсновноеПоле];
ДокДата = Выборка.Дата;
КонецЦикла;
КонецФункции
Решение 3. Запрос данных из виртуальной таблицы (остатки, обороты, срез последних и.т.д.)
Функция ДолгиВРазрезеКонтрагентов()
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ДатаОстатков = ТекущаяДата(); // дата или граница
МетаРег = Метаданные.РегистрыНакопления.ВзаиморасчетыСКонтрагентами;
ПараметрыЗапроса.ДобавитьОтбор("СуммаВзаиморасчетов", "НеРавно", 0);
// Сгруппируем только по двум измерениям без учета договоров
ПараметрыЗапроса.ДобавитьПоле("Организация");
ПараметрыЗапроса.ДобавитьПоле("Контрагент");
// Итоги по контрагенту. Организации в детальных записях
ПараметрыЗапроса.ДобавитьИзмерениеИтогов("Контрагент");
ПараметрыЗапроса.ДобавитьРесурсИтогов("СуммаВзаиморасчетов", "Сумма");
ПараметрыЗапросаАУФ = ПараметрыЗапроса.ПолучитьАУФ();
РезультатЗапроса = Запросы_ауф.ОстаткиРН(МетаРег, ДатаОстатков, ПараметрыЗапросаАУФ);
Возврат РезультатЗапроса;
КонецФункции
Решение 4. Запрос данных с повторным использованием текста запроса
В данном решении есть некий запрос хранящий в себе всю возможную логику связанную с разрезом учета. Различные клиенты (отчеты, формы объектов, веб-запросы, формы список обращаются к нему, передавая различные параметры, в звисимости от которых генерируется кастомный запрос.
Функция ЗаказыКПолучению(пКлиент)
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьПоле("Заказ");
ПараметрыЗапроса.ДобавитьПоле("Сумма");
ПараметрыЗапроса.ДобавитьОтбор("Клиент", "Равно", пКлиент);
ПараметрыЗапроса.ДобавитьОтбор("Оплачен", "Равно", Истина); //истина если заказ оплачен полностью. Считается по взаиморасчетам
Менеджер = РегистрыНакопления.ЗаказыКлиентов;
РезультатЗапроса = Менеджер.ЗапросДанных(ПараметрыЗапроса.ПолучитьАУФ());
ТаблицаЗаказов = РезультатЗапроса.Выгрузить();
Возврат ТаблицаЗаказов;
КонецФункции
// Общий метод для запроса данных
// для форм списка рекомендуется ограничивать набор полей, так как часть полей расчетные
// если они не нужны, то можно ускорить работу списка исключением лишних вычислений
//
// Параметры
//
// пПараметры - Структура описывающая параметры запроса. Представляет собой АУФ-совместимый фильтр: "Отбор", "УсловияОтбора", "Поля"
// Если не задано, то отбираются все элементы и все поля
// *Отбор - Структура,Соответствие - Ключ имя реквизита, значение - значение отбора.
// Если не задан, то отбираются все элементы
// *УсловияОтбора - Структура - Ключ - имя реквизита, значение - способ сравнения (тип - ВидСравнения).
// Если не задан, то все условия отбираются по виду сравнения "Равно"
// *Поля - Массив - имена полей, которые должны быть выведены в результирующую выборку
// Если не задано, выводятся все поля предусмотренные текстом запроса
// *Сортировки - Массив - имена полей, по которым будет выведена сортировка. После имени поля возможно указание направление сортировки
// *Ресурсы - Массив - имена полей и агрегатов, которые должны быть рассчитаны в качестве итогов. Например - Максимум(Дата)
// *Измерения - Массив - имена полей, по которым должны быть рассчитаны итоги.
//
// Возвращаемое значение:
// РезультатЗапроса - результат выполнения запроса с учетом переданных параметров
//
Функция ЗапросДанных(пПараметрыЗапроса = Неопределено) Экспорт
Если пПараметрыЗапроса <> Неопределено Тогда
ПараметрыЗапроса.ДополнитьИзДругихПараметров(пПараметрыЗапроса);
КонецЕсли;
ПараметрыЗапросаАУФ = ПараметрыЗапроса.ПолучитьАУФ();
ТекстЗапроса = ТекстЗапроса_РеестрЗаказов(); // текст запроса без отборов используется для форм списков, например
Запрос = Запросы_ауф.НовыйЗапрос(ТекстЗапроса, ПараметрыЗапросаАУФ);
РезультатЗапроса = Запрос.Выполнить();
Возврат РезультатЗапроса;
КонецФункции
Решение 5. Поиск ссылок по отбору
Функция НайтиДокумент()
ПараметрыЗапроса = МастерскаяАПИ_ауф.КонструкторПараметровЗапроса();
ПараметрыЗапроса.ДобавитьОтбор("Дата", ВидСравнения.ИнтервалВключаяГраницы, НачалоПериода, КонецПериода);
ПараметрыЗапроса.ДобавитьОтбор("Сумма", "Больше", 10000);
ПараметрыЗапроса.ДобавитьОтбор("Проведен", "=", Истина)
ПараметрыЗапроса.ДобавитьОтбор("Организация", "=", Организация);
ПараметрыЗапроса.ДобавитьПолеСортировки("Дата");
ПараметрыЗапросаАУФ = ПараметрыЗапроса.ПолучитьАУФ();
Фильтр = ПараметрыЗапроса.ПолучитьАУФ();
ДокМенеджер = Документы.РеализацияТоваровУслуг;
// работает аналогично методам НайтиПо..., но поддерживает более сложные отборы
// также все объектные запросы работают через прокси и поддерживают переопределение
ДокСсылка = Объекты_ауф.ДокументНайти(ДокМенеджер, Фильтр.Отбор, Фильтр.УсловияОтбора);
КонецФункции
Пример II-2. Регистрация исключений
Исключение
ИнфоОбОшибке = ИнформацияОбОшибке();
ПредставлениеОшибки = ПодробноеПредставлениеОшибки(ИнфоОбОшибке);
ОбщийКонтекст = "Расчет себестоимости";
РазделУчетаСебестоимости = ДокРасчетов.Метаданные().Синоним;
ОписаниеСобытияСтрокой = СобытияЖР_ауф.СформироватьОписаниеСобытияСтрокой(ОбщийКонтекст, РазделУчетаСебестоимости);
ТекстОшибки = ОписаниеСобытияСтрокой + " Ошибка в документе " + ДокРасчетов + ": " + Символы.ПС + ПредставлениеОшибки;
Сообщить(ТекстОшибки);
РегистрацияОшибок_ауф.ПроизвольнаяОшибка(ИнфоОбОшибке, ОписаниеСобытияСтрокой, ДокРасчетов);
КонецПопытки;
III Повышение степени повторного использования кода для рутинных операций, в том числе при работе со стандартными коллекциями.
Ассортимент методов по работе со стандартными коллекциями представлен в той мере, которые требовались для решения прикладных задач. Это преимущественно или создание недостающего функционала или вольное/невольное воспроизведение в случаях интеграций в нетиповые конфигурации не использующих БСП. Некоторые методы, могут показаться знакомыми по БСП, но это или переопределение недостаточно функциональных методов или результат конвергентной эволюции. Некоторые методы в этой библиотеке ровесники первых версий БСП.
Пример III-1. Работа с таблицами
// по другой таблице, включая результаты запроса, деревья и табличные части
ИсключаемыеКолонки = "Сумма";
Результат = Таблицы_ауф.ТаблицаЗначенийПоРезультатуЗапроса(РезультатЗапроса,, ИсключаемыеКолонки);
// по метаданным
ВидыРеквизитов = "Измерения,Ресурсы"; //если не заполнять, то сформируется по всем доступным полям физической таблицы
МетаОбъект = Метаданные.РегистрыНакопления.ОстаткиТоваров;
Результат = Таблицы_ауф.ТаблицаЗначенийПоМетаданным(МетаОбъект, ВидыРеквизитов);
// формирование кросс таблицы по ТЗ, ТЧ, данным формы
ИзмеренияСтрок = "Клиент,Договор";
ИзмерениеКолонок = "Организация";
Показатель = "Сумма";
Результат = Таблицы_ауф.КроссТаблица(Таблица, ИзмеренияСтрок, ИзмерениеКолонок, Показатель);
// формирование таблицы путём объединения других таблиц. Количество не ограничено.
// Работает как Объединить в запросе
Таблицы = МассивТаблицДляОбъединения();
ТолькоУникальныеЗаписи = Истина;
Результат = Таблицы_ауф.КроссТаблица(Таблицы , ТолькоУникальныеЗаписи);
И много других полезных методов расширяющих возможности таблиц
Функция МатричноеСложениеВычитаниеТаблиц(пТаблица1, пТаблица2, пИзмерения, пРесурсы, пОперация = "Сложение")
Функция ОтобратьДанныеТаблицыЗначений(пТаблица, пПараметрыЗапросаАУФ, пЗаполнятьЗаголовки = Истина)
Функция ИтогТаблицыПоОтбору(пТаблица, пОтбор, пРесурс)
Функция ВыгрузитьКолонку(пТаблица, пИмяКолонки, пТолькоУникальные = Истина, пПриемник = "Массив")
Процедура ПроиндексироватьПоляТаблицы(пТаблица, пИменаПолей)
Функция ДобавитьКолонкуВТаблицу(пТаблица, пИмяПоля, пТип = Неопределено)
Функция ДобавитьЗначениеВТаблицу(пТаблица, пИмяПоля, пЗначение, пТип = Неопределено)
Функция ПронумероватьТаблицу(пТаблица, пИмяПоляНомер = "НомерСтроки", пПредыдущийНомер = 0)
Процедура ДополнитьТаблицу(пПриемник, пИсточник, пТолькоУникальные = Ложь, пПереноситьКолонки = Ложь)
Функция ИменаПолейТаблицы(пТаблица)
Функция МассивКолонок(пТаблица, пИсточникЗначений = "Имя", пПодставитьИмяЕслиПолеКолонкиНеЗаполнено = Истина)
Процедура УдалитьСтрокиТаблицыПоОтбору(пТаблица, пОтбор)
Функция ТаблицаВМассив(пТаблица, пКолонкиВПервуюСтроку = Ложь, пИсточникЗначенийВКолонках = "Имя")
Функция ТаблицаЗначенийВФайл(пТаблица, пПараметрыЗаписи) Экспорт
Функция ТаблицаЗначенийВТабличныйДокумент(пТаблица, пОтбор = Неопределено, пУсловияОтбора = Неопределено, пМакеты = Неопределено, пПараметрыМакетов = Неопределено, пТабДок = Неопределено)
Пример III-2. Работа с массивами
Процедура СортироватьМассив(пМассив, пНаправление = "ВОЗР")
Процедура СортироватьОбъекты(пМассив, пИмяРек, пНаправление = "ВОЗР")
Функция МассивВТаблицу(пИсточник, пЗаголовкиИзПервойСтроки = Истина)
Функция МассивыРавны(пМассив1, пМассив2)
Процедура УдалитьПустыеЗначенияМассива(пМассив)
Процедура ДополнитьМассив(пПриемник, пИсточник, пТолькоУникальные = Ложь)
Функция УдалитьЗначенияИзМассива(пМассив, пЗначенияКУдалению)
Функция СкопироватьМассив(пМассив, пТолькоУникальные = Ложь)
А также множество других функций типа преобразования числа из десятичной системы счисления и обратно с возможностью использования собственного набора кодовых символов не ограничиваясь разрядностью 8 16 или другой.
Различные конвертации и конструкторы коллекций и значений. Например, конструктор массива с поддержкой широкого ассортимента исходных типов, включающего строки, универсальные коллекции и отдельные объекты.
IV Облегчение тестирования и отказ от динамического обновления.
Подсистема динамической подмены кода идет особняком. Это единственная функциональность, которую я не рекомендую использовать. Разрабатывалась она для тестирования, и неудобства обновления конфигурации в процессе тестирования целиком . Но, видимо, это то самое нечаянно гениальное решение, которое пришлось по вкусу всем кому пришлось с ней работать. Данная подсистема позволяет обеспечить простой и прозрачный способ отказа от динамических обновлений без отказа от контроля версий и централизованной разработки. При использовании в продуктиве, подсистема требует известной степени организации процессов и самодисциплины, но об это ниже. Сейчас же расскажу, о возможностях:
Загрузка заменяемого обработчика, выполяется через специальную форму. Сам обработчик это обычная внешняя обработка содержащая необходимые для режима симуляции (подмены) свойства и методы. Подсистема контролирует актуальность замененного обработчика, и когда наконец-то пройдет обычное обновление, то вызов будет производиться уже для настоящего обработчика.
Подменить можно отдельную функцию, фрагмент кода, целый модуль (общий модуль, объекта, менеджера и.т.д.), обработку/отчет целиком, а при некоторых ограничениях и документ/справочник и другие ссылочные типы.
Система позволяет обновлять не всю конфигурацию целиком, а лишь необходимые фрагменты. Данная функциональность зарекомендовала себя как незаменимый помощник при внедрениях и сложных доработках, когда количество правок в единицу времени превышает возможности обновления. Также не стоит забывать о рисках связанных с динамическим обновлением.
При этом обеспечивается версионная целостность данных и плановое обновление, когда оно всё таки произойдет, не сломает внеочередные заплатки, а дополнит их.
В связи с популярностью данного решения, подсистема может быть куплена отдельно как компонент библиотеки. Поставка предоставляется в виде файла обновления и в виде расширения.
ВАЖНО! Позитивные свойства данной системы обеспечиваются версионированием данных и централизованной разработкой. Если данный механизм будет использоваться бессистемно в рамках парадигмы "хренак-хренак и в продакшн", то данная подсистема станет отличным способом отстрелить себе ногу.
То есть, если вы поместили соответствующие изменения в релизный репозиторий, то нет ничего плохого, если вы эти изменения оперативно загрузите в базу без динамического обновления. Но если вы просто создали файл и загрузили в продуктив мимо репозитория или вообще не используете хранилища, то... я не рекомендую использовать данную функциональность.
В рекомендованных сценариях данная подсистема может быть использована для отладки и тестирования в тестовой среде и как средство экстренного и очень быстрого обновления в продуктовых средах. В отличие от динамического обновления, данная подсистема выполняет обновление сразу же и не требует перезапуска программы для всех заинтересованных пользователей. Следовательно, таким образом можно оперативно исправить ошибку без перезапуска сеансов.
// Например для тестирования алгоритма или для обхода конкретной ошибки в конкретном случае
Если НужноЗаменитьМодуль Тогда
ШифрМодуля = Метаданные.ОбщиеМодули.ОбщегоНазначения.ПолноеИмя();
РабочийМодуль = ЗаменяемыйКод.ОбъектНаВыполнение(ШифрМодуля, ОбщегоНазначения);
Иначе
РабочийМодуль = ОбщегоНазначения_ауф;
КонецЕсли;
// Здесь можем использовать модуль.
// После обновления конфигурации замена не будет требоваться и во всех случаях выполнится оригинальный код
// ВАЖНО!!! Рекомендуется пользоваться прилагаемыми к поставке шаблонами текста
V Совместимость
Библиотека работает даже на 8.1. На более старых нет опыта интеграции.
Для раритетных версий платформы в комплект поставки включены опциональные модули обеспечивающие совместимость.
В случае проблем, автор берет на себя обязательство обеспечить обратную совместимость в счет стоимости библиотеки. В случае невозможности гарантируется возврат денег.
Использование модальности
Незначительное число интерактивных методов использует модальные вызовы. Постепенно ведется работа по адаптации. Но если в вашей конфигурации запрещены модальные вызовы, то вы можете или подождать пока будет реализовано поддержка вашей конфигурации или использовать библиотеку ради множества других, не менее полезных, но однозначно совместимых функций.
Если вы хотите больше иллюстраций, то в комментариях можете оставить задание, реализация которого вам не нравится и вы бы хотели посмотреть, как с данной задачей можно справиться, используя библиотеку.
Таблицы:
- Исправлен баг в методе записи таблицы значений в файл.
- Добавлен метод КолонкиТаблицыЗначенийВМассивСтруктур() выгружающий колонки в массив структур с реквизитами колонок
- Расширен метод таблицы ВыгрузитьКолонку() - теперь он может не только выгружать значения в переданный тип списка, но и дополнять переданную коллекцию. Состав поддерживаемых типов коллекций не изменился.
- Добавлен метод ТаблицаФормыВМассивСтруктур()
Работа с двоичным данными:
- Добавлен общий модуль для простейшей работы с данными (только текст).
Реализован механизм работы с произвольными настройками:
- Инициализация состава и типов значений настроек
- Размещение их на формах
Реализован класс JSON-конвертер
- Однопальцевая конвертация в JSON и обратно.
- Поддержка всех немутабельных значений
Поставляется в составе библиотеки или отдельно. Подробное описание здесь
Общего назначения:
- Добавлен. СтруктураПоМассиву - формирование структуры по переданному массиву ключей
- Добавлен. УниверсальнаяКоллекция - полиморфная функция создающая новую или возвращающая переданную универсальную коллекцию
- Добавлены два метода общего назначения для получения наименьшего и наибольшего элемента массива, соответственно.
- Метод "СкопироватьМассив", теперь умеет возвращать массивы только с уникальными значениями, при указании соответствующего параметра.
- Реализованы методы фиксации и снятия фиксации для стандартных коллекций ЗафиксироватьКоллекциюРекусивно() и СнятьФиксациюКоллекцииРекурсивно(). Функциональные аналоги функций БСП.
- Оптимизирована работа счётчика выполнения. Состояние теперь обновляется только тогда, когда состояние индикатора меняется
Конструктор параметров запроса:
- Реализован метод, добавляющий поля физической таблицы. Это упрощает управление сложными переопределяемыми запросами, позволяя не описывать список нужных реквизитов из числа стандартных
- Реализован метод, добавляющий несколько полей списком. Это упрощает работу с конструктором в ряде случав.
- Вид сравнения для списка или равно определяется автоматически по значению отбора, если вид сравнения не указан явно
- Исправлена ошибка, когда при дополнении параметров запроса затирались ФиксированныеПараметры
Причины купить
Даже полный комплект поставки для одного программиста окупится максимум через месяц использования. Только за счет экономии времени. Кумулятивный эффект по сокращению числа зависимостей и объёмов кода будет действовать дольше, но тоже будет давать позитивный эффект.
Реализованная в библиотеке поддержка правильного MVC паттерна позволит реализовать гибкие программные решения.
Сравнение версий
Библиотека никогда не планировалась и не позиционировалась как замена БСП. В некоторых моментах они могут пересекаться в функциональности, но в целом у них разное назначение. БСП содержит в себе набор готовой функциональности, снабженнной служебным кодом, который тоже можно использовать. Библиотека UFL содержит в себе шаблоны-кирпичики из которых вы можете создавать собственные решения. Помимо этого в библиотеку заложена определенная идеология архитектуры с изоляцией данных, кода и пользовательских интерфейсов друг от друга. Компактность библиотеки позволяет разворачивать небольшие решения не беспокоясь об объёме и быстродействии базы данных.