При разработке в современных конфигурациях на базе платформы 1С:Предприятие 8.3 (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») программисты часто сталкиваются с ситуацией, когда простые манипуляции с реквизитами объекта не приводят к желаемому результату. Рассмотрим типичный сценарий: необходимо при определенном условии (например, заполнении дополнительного реквизита «Причина отказа») программно отметить все товары в заказе как отмененные и перевести документ в статус «Закрыт».
Разберем ситуацию, когда стандартный код в подписке на событие ПередЗаписью не срабатывает, и система выдает ошибку: «Закрытие заказа возможно только с полностью оплаченными/отмененными строками». Выясним причины такого поведения и найдем архитектурно правильное решение.
Рассмотрим пример кода, который изначально предложил автор темы для решения задачи в подписке на событие ПередЗаписью:
Процедура аз_ЗакрытьЗаказПоПричинеОтказа(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
ДопРеквезит = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Причина отказа");
Значение = Источник.ДополнительныеРеквизиты.Найти(ДопРеквезит, "Свойство");
Если ЗначениеЗаполнено(Значение) И ЗначениеЗаполнено(Значение.Значение) Тогда
Для Каждого Товар Из Источник.Товары Цикл
Товар.Отменено = Истина;
КонецЦикла;
Источник.Статус = Перечисления.СтатусыЗаказовКлиентов.Закрыт;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Проанализируем, почему этот код, несмотря на свою логичность, вызывает ошибку при проведении. Основная причина заключается в том, что в современных конфигурациях 1С документ ЗаказКлиента — это сложный объект с множеством зависимых механизмов. Простого изменения поля Отменено в табличной части Товары недостаточно для того, чтобы система «поверила» в отмену строки.
В типовых решениях (УТ 11, ERP) для корректной отмены строки недостаточно установить флаг Отменено = Истина. В каждой строке табличной части Товары должен быть заполнен реквизит ПричинаОтмены. Если флаг стоит, а ссылка на причину отсутствует, механизмы контроля проведения считают строку «недоотмененной» и продолжают требовать по ней оплату или отгрузку. Именно это и приводит к ошибке контроля взаиморасчетов. Иногда стандартных реквизитов не хватает, и тогда полезно знать про Дополнительные реквизиты табличных частей объекта.
Проанализируем ситуацию с оплатой. При проведении заказа система сверяет общую сумму документа с данными в табличной части ЭтапыОплаты. Когда мы программно отменяем строки, сумма заказа уменьшается (или обнуляется). Однако табличная часть с этапами оплаты автоматически не пересчитывается внутри события ПередЗаписью. В итоге система видит: «Сумма к оплате по графику — 100 рублей, а сумма товаров в заказе — 0 рублей (так как всё отменено)». Возникает конфликт, и документ блокирует проведение из-за нарушения условий оплаты.
Выясним важный нюанс работы событий. Подписка на событие ПередЗаписью выполняется после того, как отработал одноименный обработчик в модуле самого объекта. Если в модуле объекта уже прошли основные проверки или подготовка данных для движений, изменения, внесенные в подписке, могут быть проигнорированы системными механизмами, которые уже «запомнили» старое состояние документа.
Посмотрим на решение, к которому пришел автор темы: использование Источник.Записать(РежимЗаписиДокумента.Запись) прямо внутри процедуры ПередЗаписью. Это позволяет «протолкнуть» изменения в базу данных, чтобы последующие проверки увидели обновленную информацию.
Важно: Это крайне плохая практика (антипаттерн). Вызов метода Записать() внутри события ПередЗаписью того же самого объекта приводит к рекурсии. Система пытается записать объект, снова вызывает событие ПередЗаписью, которое снова вызывает Записать(). В лучшем случае 1С остановит это по превышению глубины стека, в худшем — это приведет к трудноуловимым ошибкам транзакции и порче данных.
Рассмотрим, как реализовать данную задачу корректно, следуя архитектуре типовых конфигураций. Нам необходимо выполнить три условия:
Отменено.Причину отмены в каждой строке (это должен быть элемент справочника ПричиныОтменыЗаказовКлиентов).Посмотрим на пример правильной реализации логики в подписке на событие:
// Пример правильного заполнения строк при отмене
Для Каждого СтрокаТовар Из Источник.Товары Цикл
СтрокаТовар.Отменено = Истина;
// Нужно заранее найти или создать подходящую причину отмены
СтрокаТовар.ПричинаОтмены = Справочники.ПричиныОтменыЗаказовКлиентов.НайтиПоНаименованию("Отказ клиента");
КонецЦикла;
// Вызов типового механизма пересчета графика оплаты (для УТ 11 / ERP)
// Обычно это требует подключения серверных модулей конфигурации
ПараметрыПересчета = РаспределениеЗапасовИГрафикаОплаты.ПараметрыРаспределения();
РаспределениеЗапасовИГрафикаОплаты.РаспределитьВсе(Источник, ПараметрыПересчета);
Если логика требует изменения статуса на «Закрыт», проанализируем возможность использования события ПриЗаписи или даже создание отдельной обработки. Однако, если мы хотим остаться в рамках подписки на ПередЗаписью, следует придерживаться следующего алгоритма:
Отменено = Истина И заполняем ПричинаОтмены.Источник.Статус = Перечисления.СтатусыЗаказовКлиентов.Закрыт.ЗаказыКлиентовСервер или РаспределениеЗапасовИГрафикаОплаты для приведения документа в консистентное состояние.Проанализируем еще один важный момент, упомянутый в обсуждении: обращение к перечислениям. Использование конструкции Перечисления.СтатусыЗаказовКлиентов[3] является крайне ненадежным, так как порядок элементов в перечислении может измениться при обновлении или доработке конфигурации. Всегда используйте прямое обращение по имени: Перечисления.СтатусыЗаказовКлиентов.Закрыт.
Если ошибка «Нарушены условия оплаты» сохраняется, рекомендуем воспользоваться советом из обсуждения: поставить точку останова в общем модуле, где выводится это сообщение (обычно это модули контроля проведения, такие как ЗаказыКлиентовСервер). Исследование стека вызовов позволит увидеть, какой именно механизм проверки срабатывает и какие данные (старые из базы или новые из объекта) он анализирует в данный момент.
Подводя итог, можно сказать: 1С «видит» вашу галку, но встроенные бизнес-правила конфигурации не позволяют провести документ, который находится в противоречивом состоянии (отменен без причины или с некорректным графиком платежей). Соблюдение целостности данных объекта — ключ к решению проблемы.