При доработке печатных форм кассовых документов (приходного и расходного кассовых ордеров — ПКО и РКО) в программе 1С:Комплексная автоматизация 2 (а также в 1С:ERP и 1С:Управление торговлей 11) разработчики часто сталкиваются с задачей вывода актуальных подписантов: Руководителя, Главного бухгалтера и Кассира. Сложность заключается в том, что в справочнике ответственных лиц могут храниться записи с пересекающимися периодами действия, открытыми датами окончания или записями, заведенными «на будущее».
Разберем, почему возникают ошибки при прямом обращении к данным через запросы, как устроено хранение подписантов в архитектуре КА 2, и рассмотрим правильные варианты решения этой задачи.
Проанализируем структуру хранения данных. В отличие от конфигурации 1C:Бухгалтерия предприятия, где лица с правом подписи хранятся в периодическом регистре сведений и к ним применима виртуальная таблица СрезПоследних, в конфигурациях на базе ERP-ядра используется подчиненный справочник Справочник.ОтветственныеЛицаОрганизаций (в пользовательском интерфейсе — «Лица с правом подписи»).
Так как это справочник, платформа не предоставляет встроенного механизма среза последних. Период действия каждой записи определяется реквизитами ДатаНачала и ДатаОкончания. При этом на практике встречаются следующие варианты заполнения:
ДатаНачала и ДатаОкончания.ДатаОкончания не заполнена (равна пустой дате 01.01.0001 00:00:00), что означает бессрочное действие полномочий.Посмотрим на частую ошибку в тексте запроса при попытке сопоставить дату документа и период действия ответственного лица:
И (ДанныеДокумента.Дата МЕЖДУ ОтветственныеЛицаОрганизаций.ДатаНачала И ОтветственныеЛицаОрганизаций.ДатаОкончания
ИЛИ ОтветственныеЛицаОрганизаций.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1))
Выясним причину некорректной работы такого условия. Если ДатаОкончания не заполнена, условие после ИЛИ возвращает Истина независимо от того, наступила ли ДатаНачала. В выборку попадут записи с датой начала в будущем (например, 01.11.2030), что исказит данные документа за текущий период.
Кроме того, использование конструкции ВЫБРАТЬ ПЕРВЫЕ 1 при пакетной печати документов (когда в параметре передан массив документов &МассивДокументов) приведет к тому, что запрос вернет данные только для одного-единственного документа из всего списка.
Рассмотрим штатный подход. В типовой конфигурации механизм получения подписантов для печатных форм уже реализован и отлажен с учетом всех краевых случаев. Не требуется писать сложные запросы вручную — достаточно применить методы общего модуля ОтветственныеЛицаСервер.
Если печатная форма формируется для массива документов, разберем по шагам вызов штатной процедуры, которая заполняет менеджер временных таблиц актуальными данными на дату каждого документа:
// Создаем или используем существующий менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
// Вызываем штатную процедуру формирования временной таблицы
ОтветственныеЛицаСервер.СформироватьВременнуюТаблицуОтветственныхЛицДокументов(МассивОбъектов, МенеджерВТ);
Эта процедура создает временную таблицу, содержащую реквизиты ответственных лиц (включая руководителя, главного бухгалтера и кассира), которую затем можно соединить в основном запросе печатной формы по ссылке на документ.
Если же требуется точечно получить подписантов на конкретную дату без формирования временных таблиц, воспользуемся функцией:
СтруктураОтветственных = ОтветственныеЛицаСервер.ПолучитьОтветственныеЛицаОрганизации(Организация, ДатаДокумента);
// Функция возвратит структуру с актуальными физическими лицами и их должностями
Если по условиям доработки необходимо получить данные именно через текст запроса без использования типовых модулей, сформируем правильную логику среза актуальных записей на дату документа.
Для корректного среза разобьем задачу на два этапа:
Посмотрим на пример построения такого запроса:
ВЫБРАТЬ
ДанныеДокумента.Ссылка КАК Ссылка,
ДанныеДокумента.Организация КАК Организация,
ДанныеДокумента.Дата КАК ДатаДокумента,
ОтветственныеЛица.ОтветственноеЛицо КАК ОтветственноеЛицо,
МАКСИМУМ(ОтветственныеЛица.ДатаНачала) КАК ДатаНачала
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
Документ.РасходныйКассовыйОрдер КАК ДанныеДокумента
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ОтветственныеЛицаОрганизаций КАК ОтветственныеЛица
ПО ДанныеДокумента.Организация = ОтветственныеЛица.Владелец
И ОтветственныеЛица.ДатаНачала <= ДанныеДокумента.Дата
И (ОтветственныеЛица.ДатаОкончания >= ДанныеДокумента.Дата
ИЛИ ОтветственныеЛица.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1))
ГДЕ
ДанныеДокумента.Ссылка В(&МассивДокументов)
СГРУППИРОВАТЬ ПО
ДанныеДокумента.Ссылка,
ДанныеДокумента.Организация,
ДанныеДокумента.Дата,
ОтветственныеЛица.ОтветственноеЛицо
;
ВЫБРАТЬ
ВТ_МаксимальныеДаты.Ссылка КАК Ссылка,
ВЫБОР
КОГДА ВТ_МаксимальныеДаты.ОтветственноеЛицо = ЗНАЧЕНИЕ(Перечисление.ОтветственныеЛицаОрганизаций.Руководитель)
ТОГДА ОтветственныеЛица.ФизическоеЛицо.Наименование
ИНАЧЕ NULL
КОНЕЦ КАК РуководительФИО,
ВЫБОР
КОГДА ВТ_МаксимальныеДаты.ОтветственноеЛицо = ЗНАЧЕНИЕ(Перечисление.ОтветственныеЛицаОрганизаций.Руководитель)
ТОГДА ОтветственныеЛица.Должность
ИНАЧЕ NULL
КОНЕЦ КАК РуководительДолжность,
ВЫБОР
КОГДА ВТ_МаксимальныеДаты.ОтветственноеЛицо = ЗНАЧЕНИЕ(Перечисление.ОтветственныеЛицаОрганизаций.ГлавныйБухгалтер)
ТОГДА ОтветственныеЛица.ФизическоеЛицо.Наименование
ИНАЧЕ NULL
КОНЕЦ КАК ГлавныйБухгалтерФИО,
ВЫБОР
КОГДА ВТ_МаксимальныеДаты.ОтветственноеЛицо = ЗНАЧЕНИЕ(Перечисление.ОтветственныеЛицаОрганизаций.Кассир)
ТОГДА ОтветственныеЛица.ФизическоеЛицо.Наименование
ИНАЧЕ NULL
КОНЕЦ КАК КассирФИО
ПОМЕСТИТЬ ВТ_ПодписантыИтого
ИЗ
ВТ_МаксимальныеДаты КАК ВТ_МаксимальныеДаты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ОтветственныеЛицаОрганизаций КАК ОтветственныеЛица
ПО ВТ_МаксимальныеДаты.Организация = ОтветственныеЛица.Владелец
И ВТ_МаксимальныеДаты.ОтветственноеЛицо = ОтветственныеЛица.ОтветственноеЛицо
И ВТ_МаксимальныеДаты.ДатаНачала = ОтветственныеЛица.ДатаНачала
;
Обратим внимание на условие отбора по датам:
ОтветственныеЛица.ДатаНачала <= ДанныеДокумента.Дата
И (ОтветственныеЛица.ДатаОкончания >= ДанныеДокумента.Дата
ИЛИ ОтветственныеЛица.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1))
Такая конструкция строго исключает будущие назначения и корректно обрабатывает открытые периоды с незаполненной датой окончания.
Проанализируем также поведение программы на уровне интерфейса:
ОтветственныеЛицаОрганизаций на один и тот же период может быть заведено несколько сотрудников с одинаковой ролью. В таком случае необходимо проверять статус или флаг основного лица, чтобы исключить дублирование строк при соединении.