Почему 1С «не видит» программно установленную галочку отмены в заказе клиента и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке в современных конфигурациях на базе платформы 1С:Предприятие 8.3 (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») программисты часто сталкиваются с ситуацией, когда простые манипуляции с реквизитами объекта не приводят к желаемому результату. Рассмотрим типичный сценарий: необходимо при определенном условии (например, заполнении дополнительного реквизита «Причина отказа») программно отметить все товары в заказе как отмененные и перевести документ в статус «Закрыт».

Разберем ситуацию, когда стандартный код в подписке на событие ПередЗаписью не срабатывает, и система выдает ошибку: «Закрытие заказа возможно только с полностью оплаченными/отмененными строками». Выясним причины такого поведения и найдем архитектурно правильное решение.

Анализ исходной проблемы и кода

Рассмотрим пример кода, который изначально предложил автор темы для решения задачи в подписке на событие ПередЗаписью:


Процедура аз_ЗакрытьЗаказПоПричинеОтказа(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        ДопРеквезит = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Причина отказа");
        Значение = Источник.ДополнительныеРеквизиты.Найти(ДопРеквезит, "Свойство");
        
        Если ЗначениеЗаполнено(Значение) И ЗначениеЗаполнено(Значение.Значение) Тогда
            Для Каждого Товар Из Источник.Товары Цикл
                Товар.Отменено = Истина;
            КонецЦикла;
            Источник.Статус = Перечисления.СтатусыЗаказовКлиентов.Закрыт;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Проанализируем, почему этот код, несмотря на свою логичность, вызывает ошибку при проведении. Основная причина заключается в том, что в современных конфигурациях 1С документ ЗаказКлиента — это сложный объект с множеством зависимых механизмов. Простого изменения поля Отменено в табличной части Товары недостаточно для того, чтобы система «поверила» в отмену строки.

Причина №1: Отсутствие «Причины отмены» в строках

В типовых решениях (УТ 11, ERP) для корректной отмены строки недостаточно установить флаг Отменено = Истина. В каждой строке табличной части Товары должен быть заполнен реквизит ПричинаОтмены. Если флаг стоит, а ссылка на причину отсутствует, механизмы контроля проведения считают строку «недоотмененной» и продолжают требовать по ней оплату или отгрузку. Именно это и приводит к ошибке контроля взаиморасчетов. Иногда стандартных реквизитов не хватает, и тогда полезно знать про Дополнительные реквизиты табличных частей объекта.

Причина №2: Рассогласование с графиком оплаты

Проанализируем ситуацию с оплатой. При проведении заказа система сверяет общую сумму документа с данными в табличной части ЭтапыОплаты. Когда мы программно отменяем строки, сумма заказа уменьшается (или обнуляется). Однако табличная часть с этапами оплаты автоматически не пересчитывается внутри события ПередЗаписью. В итоге система видит: «Сумма к оплате по графику — 100 рублей, а сумма товаров в заказе — 0 рублей (так как всё отменено)». Возникает конфликт, и документ блокирует проведение из-за нарушения условий оплаты.

Причина №3: Особенности работы подписки «ПередЗаписью»

Выясним важный нюанс работы событий. Подписка на событие ПередЗаписью выполняется после того, как отработал одноименный обработчик в модуле самого объекта. Если в модуле объекта уже прошли основные проверки или подготовка данных для движений, изменения, внесенные в подписке, могут быть проигнорированы системными механизмами, которые уже «запомнили» старое состояние документа.

Разбор «вредного» совета: Источник.Записать() внутри подписки

Посмотрим на решение, к которому пришел автор темы: использование Источник.Записать(РежимЗаписиДокумента.Запись) прямо внутри процедуры ПередЗаписью. Это позволяет «протолкнуть» изменения в базу данных, чтобы последующие проверки увидели обновленную информацию.

Важно: Это крайне плохая практика (антипаттерн). Вызов метода Записать() внутри события ПередЗаписью того же самого объекта приводит к рекурсии. Система пытается записать объект, снова вызывает событие ПередЗаписью, которое снова вызывает Записать(). В лучшем случае 1С остановит это по превышению глубины стека, в худшем — это приведет к трудноуловимым ошибкам транзакции и порче данных.

Правильная стратегия решения задачи

Рассмотрим, как реализовать данную задачу корректно, следуя архитектуре типовых конфигураций. Нам необходимо выполнить три условия:

  1. Установить флаг Отменено.
  2. Заполнить Причину отмены в каждой строке (это должен быть элемент справочника ПричиныОтменыЗаказовКлиентов).
  3. Пересчитать график оплаты, чтобы сумма этапов соответствовала сумме неотмененных строк.

Посмотрим на пример правильной реализации логики в подписке на событие:


// Пример правильного заполнения строк при отмене
Для Каждого СтрокаТовар Из Источник.Товары Цикл
    СтрокаТовар.Отменено = Истина;
    // Нужно заранее найти или создать подходящую причину отмены
    СтрокаТовар.ПричинаОтмены = Справочники.ПричиныОтменыЗаказовКлиентов.НайтиПоНаименованию("Отказ клиента");
КонецЦикла;

// Вызов типового механизма пересчета графика оплаты (для УТ 11 / ERP)
// Обычно это требует подключения серверных модулей конфигурации
ПараметрыПересчета = РаспределениеЗапасовИГрафикаОплаты.ПараметрыРаспределения();
РаспределениеЗапасовИГрафикаОплаты.РаспределитьВсе(Источник, ПараметрыПересчета);

Использование правильных событий

Если логика требует изменения статуса на «Закрыт», проанализируем возможность использования события ПриЗаписи или даже создание отдельной обработки. Однако, если мы хотим остаться в рамках подписки на ПередЗаписью, следует придерживаться следующего алгоритма:

  1. Проверка условий: Убеждаемся, что мы находимся в режиме проведения и все необходимые доп. реквизиты заполнены.
  2. Обновление ТЧ Товары: Устанавливаем Отменено = Истина И заполняем ПричинаОтмены.
  3. Корректировка статуса: Устанавливаем Источник.Статус = Перечисления.СтатусыЗаказовКлиентов.Закрыт.
  4. Актуализация расчетов: Обязательно вызываем процедуры типового модуля ЗаказыКлиентовСервер или РаспределениеЗапасовИГрафикаОплаты для приведения документа в консистентное состояние.

Проанализируем еще один важный момент, упомянутый в обсуждении: обращение к перечислениям. Использование конструкции Перечисления.СтатусыЗаказовКлиентов[3] является крайне ненадежным, так как порядок элементов в перечислении может измениться при обновлении или доработке конфигурации. Всегда используйте прямое обращение по имени: Перечисления.СтатусыЗаказовКлиентов.Закрыт.

Рекомендация по отладке

Если ошибка «Нарушены условия оплаты» сохраняется, рекомендуем воспользоваться советом из обсуждения: поставить точку останова в общем модуле, где выводится это сообщение (обычно это модули контроля проведения, такие как ЗаказыКлиентовСервер). Исследование стека вызовов позволит увидеть, какой именно механизм проверки срабатывает и какие данные (старые из базы или новые из объекта) он анализирует в данный момент.

Подводя итог, можно сказать: 1С «видит» вашу галку, но встроенные бизнес-правила конфигурации не позволяют провести документ, который находится в противоречивом состоянии (отменен без причины или с некорректным графиком платежей). Соблюдение целостности данных объекта — ключ к решению проблемы.

← На главную