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