При разработке аналитических отчетов в среде 1С начинающие программисты часто сталкиваются с архитектурными трудностями. Типичный пример такой задачи — необходимость автоматизировать учет недовезенных водителями заказов и построить отчет по показателям выполнения. В рамках этой задачи в документ РасходнаяНакладная добавляются реквизиты: Водитель, Состояние, ПричинаНедовоза. У каждой причины недовоза (элемента справочника) существует логический флаг УважительнаяПричина (тип Булево). Нам необходимо разобраться, как правильно организовать движения документа по регистру накопления, чтобы учесть этот булев флаг, и как корректно вывести сводные аналитические показатели в отчет.
В этой статье мы подробно проанализируем архитектурные подходы к решению данной задачи, разберем грубые ошибки в коде проведения и управляемой формы, которые часто совершают новички, и предложим оптимальные, чистые варианты реализации.
---
Прежде чем переходить к написанию кода, определимся с типом регистра накопления. В обсуждении на форуме автору темы справедливо указывают на недопустимость использования регистра остатков для аналитических целей.
Почему нельзя использовать регистр остатков? Если мы спроектируем регистр накопления с видом «Остатки», нам придется фиксировать движения прихода и расхода. Поскольку выполненные, отмененные или недовезенные заказы — это свершившиеся события (транзакции), они не имеют материального остатка, который должен «закрываться» в ноль. Использование регистра остатков приведет к бесконечному накоплению итогов в базе данных, что со временем критически снизит производительность системы и неоправданно увеличит размер базы данных.
Правильное решение: Для фиксации фактов хозяйственной жизни и последующего анализа за период необходимо использовать Оборотный регистр накопления. В оборотном регистре отсутствует понятие вида движения (приход/расход), он просто накапливает обороты числовых показателей в разрезе измерений за указанный период.
---
Основной вопрос автора темы касается учета булева реквизита: «Как добавить булево в регистр накопления? В ресурсах же хранятся только числовые значения?»
Действительно, ресурсы регистра накопления предназначены для суммирования, поэтому они должны иметь числовой тип данных. Напрямую тип Булево в ресурсах использовать не получится. Для решения этой проблемы рассмотрим два архитектурных подхода.
Мы можем создать оборотный регистр со следующей структурой:
Водитель (СправочникСсылка.ФизическиеЛица).КоличествоЗаказов (Число, 10, 0) — при проведении всегда пишем 1;Выполнено (Число, 1, 0) — пишем 1, если заказ выполнен, иначе 0;НеВыполненоУважительно (Число, 1, 0) — пишем 1, если причина недовоза уважительная;НеВыполненоНеуважительно (Число, 1, 0) — пишем 1, если причина недовоза неуважительная.Этот подход прост в реализации и позволяет легко суммировать показатели в Системе компоновки данных (СКД). Любая колонка отчета будет представлять собой простую сумму соответствующего ресурса.
Если в будущем планируется расширение списка состояний заказов или аналитики причин, лучше спроектировать структуру более универсально:
Водитель (СправочникСсылка.ФизическиеЛица);СтатусВыполнения (ПеречислениеСсылка.СтатусыВыполнения) — например, значения: Выполнен, НеВыполненУважительно, НеВыполненНеуважительно.Количество (Число, 10, 0) — всегда записываем значение 1.В этом случае при проведении документа мы определяем статус и записываем одну строчку с количеством 1. В отчете СКД группировка по измерению СтатусВыполнения позволит получить любые аналитические разрезы без изменения структуры метаданных регистра.
---
Рассмотрим фрагмент кода проведения, который предложил автор темы (сообщение 47), и выясним, какие критические ошибки он совершил:
Если УважительнаяПричина Тогда
Движение.НеУважительно = 0 ;
Движение.УважительнаяПричина = 1;
Иначе
Движение.НеУважительно = 1;
КонецЕсли;
Анализ ошибок в коде автора:
Если УважительнаяПричина Тогда..., предполагая, что это реквизит шапки документа. Однако по условию задачи этот флаг является реквизитом справочника ПричиныНедовоза. Напрямую обращаться к нему нельзя. Необходимо получать его через реквизит-ссылку: ПричинаНедовоза.УважительнаяПричина.Ложь), однако для явного и безопасного кода рекомендуется делать предварительную проверку с помощью функции ЗначениеЗаполнено().Если, что затрудняет чтение и отладку кода.Разберем правильный, чистый вариант написания обработчика проведения в модуле документа РасходнаяНакладная для первого варианта структуры регистра:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Инициализируем запись в наш оборотный регистр накопления
Движения.ВыполнениеЗаказов.Записывать = Истина;
Движение = Движения.ВыполнениеЗаказов.Добавить();
Движение.Период = Дата;
Движение.Водитель = Водитель;
// Каждый проведенный документ — это один заказ в статистике
Движение.КоличествоЗаказов = 1;
// Анализируем состояние заказа
Если Состояние = Перечисления.СостоянияЗаказов.Выполнен Тогда
Движение.Выполнено = 1;
ИначеЕсли Состояние = Перечисления.СостоянияЗаказов.Недовезен
Или Состояние = Перечисления.СостоянияЗаказов.Отменен Тогда
// Проверяем уважительность причины, обращаясь к реквизиту справочника
Если ЗначениеЗаполнено(ПричинаНедовоза) И ПричинаНедовоза.УважительнаяПричина Тогда
Движение.НеВыполненоУважительно = 1;
Иначе
Движение.НеВыполненоНеуважительно = 1;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
По условию задачи, поле Причина недовоза должно быть доступно для редактирования только в том случае, если состояние заказа установлено в значение Недовезён или Отменён. В противном случае редактирование должно быть запрещено.
Посмотрим на код изменения состояния, написанный автором темы (сообщение 48):
&НаКлиенте
Процедура СостояниеПриИзменении(Элемент)
Если Элементы.Состояние.ВыделенныйТекст = "Принят" или ... Тогда
Элементы.Причина.Доступность = Ложь;
Иначе
Элементы.Причина.Доступность = Истина;
КонецЕсли;
КонецПроцедуры
Почему этот код не будет работать в реальной конфигурации 1С?
ВыделенныйТекст: Это грубейшая ошибка. Данное свойство содержит текст, который физически выделен синим маркером в поле ввода в текущий момент. При обычном выборе значения из списка это свойство будет пустым. Проверять необходимо значение реквизита объекта, то есть Объект.Состояние."Принят" недопустимо. Если синоним перечисления изменится или программа будет запущена на другом языке, логика сломается. Сравнение должно производиться непосредственно со значениями перечисления.ПриИзменении срабатывает только тогда, когда пользователь интерактивно меняет значение на форме. Если мы откроем уже записанный документ, поле Причина окажется доступным, даже если статус заказа — Выполнен. Нам необходимо настраивать доступность элементов не только при изменении, но и при открытии формы.Рассмотрим правильный вариант реализации логики формы документа:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Устанавливаем доступность элементов при открытии формы
УстановитьДоступностьПолейНаСервере();
КонецПроцедуры
&НаСервере
Процедура УстановитьДоступностьПолейНаСервере()
// Проверяем значение реквизита объекта
НужноЗаполнятьПричину = (Объект.Состояние = Перечисления.СостоянияЗаказов.Недовезен)
Или (Объект.Состояние = Перечисления.СостоянияЗаказов.Отменен);
Элементы.ПричинаНедовоза.Доступность = НужноЗаполнятьПричину;
КонецПроцедуры
&НаКлиенте
Процедура СостояниеПриИзменении(Элемент)
УстановитьДоступностьПолейНаКлиенте();
КонецПроцедуры
&НаКлиенте
Процедура УстановитьДоступностьПолейНаКлиенте()
// На клиенте для получения значений перечисления используем ПредопределенноеЗначение
НужноЗаполнятьПричину = (Объект.Состояние = ПредопределенноеЗначение("Перечисление.СостоянияЗаказов.Недовезен"))
Или (Объект.Состояние = ПредопределенноеЗначение("Перечисление.СостоянияЗаказов.Отменен"));
Элементы.ПричинаНедовоза.Доступность = НужноЗаполнятьПричину;
// Если состояние изменилось на успешное, очистим ранее введенную причину недовоза
Если Не НужноЗаполнятьПричину Тогда
Объект.ПричинаНедовоза = Неопределено;
КонецЕсли;
КонецПроцедуры
---
Для построения красивого аналитического отчета часто требуется скомбинировать несколько показателей в одной колонке. Например, вывести процент невыполненных заказов и их физическое количество в формате: 15.3% (5). Рассмотрим, как решить эту задачу в Системе компоновки данных (СКД).
Этот способ оптимален, поскольку само поле в наборе данных остается числовым (по нему можно рассчитывать итоги и использовать в других формулах), но для пользователя оно выводится в нужном текстовом формате.
ПроцентНевыполненных.
Строка(Формат(ПроцентНевыполненных, "ЧДЦ=1")) + "% (" + Строка(КоличествоНевыполненных) + ")"
Если нам не требуется в дальнейшем производить математические вычисления над этим полем, мы можем сразу сформировать текстовое поле на вкладке Вычисляемые поля.
АналитикаПроцента.
Представление(Формат(ВыражениеПроцента, "ЧДЦ=1")) + "% " + Представление(КоличествоНевыполненных)
Используя эти методы проектирования регистров и разработки интерфейсов, мы получаем надежное, масштабируемое решение, полностью соответствующее стандартам разработки на платформе 1С:Предприятие 8.3.