Как реализовать запись разных видов движений (приход и расход) в регистр накопления из одного документа?

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

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

Анализ проблемы: почему стандартный код конструктора не работает

Рассмотрим ситуацию, с которой столкнулся автор темы. В процедуре ОбработкаПроведения был размещен код, созданный стандартным Конструктором движений. Проблема заключалась в том, что конструктор просто последовательно добавил два цикла: один для вида движения Расход, другой — для Приход.

Проанализируем фрагмент ошибочного кода:


// регистр Мэтр_НакопленияПоКартамЛояльности Расход
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Для Каждого ТекСтрока Из КартыЛояльности Цикл
    Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
    Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
    // ... заполнение свойств
КонецЦикла;

// регистр Мэтр_НакопленияПоКартамЛояльности Приход
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Для Каждого ТекСтрока Из КартыЛояльности Цикл
    Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
    Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
    // ... заполнение свойств
КонецЦикла;

При таком подходе система 1С выполнит оба цикла. В результате в регистр запишутся сразу две записи на каждую строку документа: одна с приходом, другая с расходом. Они взаимно уничтожат друг друга в остатках, либо просто создадут избыточные обороты. Для решения задачи нам необходимо внедрить логическое условие Если...Иначе.

Способ №1: Использование реквизита документа (Рекомендуемый)

Самый надежный способ — завести в документе реквизит (например, ТипОперации с типом ПеречислениеСсылка или просто Сторно с типом Булево). Это позволит системе «помнить», в каком режиме был проведен документ, даже если вы решите перепровести его программно через групповую обработку или фоновое задание.

Разберем по шагам реализацию с реквизитом Сторно:

  1. Добавим реквизит Сторно (Тип: Булево) в метаданные документа.
  2. Вынесем этот реквизит на форму, чтобы пользователь мог управлять логикой.
  3. Изменим процедуру ОбработкаПроведения в модуле объекта.

Проанализируем пример кода с использованием условия:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // Очищаем движения, если это необходимо
    Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
    Движения.Мэтр_НакопленияПоКартамЛояльности.Очистить();
    
    Для Каждого ТекСтрока Из КартыЛояльности Цикл
        Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
        Движение.Период = Дата;
        Движение.КартаЛояльности = ТекСтрока.ОснКарта;
        
        // Главное условие выбора вида движения
        Если Сторно Тогда
            Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
            Движение.Сумма = ТекСтрока.Сумма;
        Иначе
            Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
            Движение.Сумма = ТекСтрока.СуммаВиртИФиз;
        КонецЕсли;
        
        Движение.ЧекККМ = ТекСтрока.ЧекККМ;
    КонецЦикла;
    
КонецПроцедуры

Способ №2: Использование ДополнительныхСвойств

Если вы не хотите менять структуру метаданных (добавлять реквизит), можно использовать механизм ДополнительныеСвойства. Однако помните, что этот флаг живет только в течение одной транзакции записи, инициированной с формы.

Рассмотрим подробнее реализацию этого метода:

Шаг 1. На форме документа в обработчике команды:


&НаКлиенте
Процедура КомандаСторно(Команда)
    // Устанавливаем признак в дополнительные свойства перед записью
    ЭтаФорма.Объект.ДополнительныеСвойства.Вставить("РежимСторно", Истина);
    ЭтаФорма.Записать(ПараметрыЗаписи);
КонецПроцедуры

Шаг 2. В модуле объекта в обработке проведения:


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

Важное ограничение: Если такой документ будет перепроводиться обработкой «Групповое перепроведение документов», свойство РежимСторно не будет установлено, и документ всегда будет проводиться по ветке Иначе. Поэтому первый способ с реквизитом является более профессиональным.

Особенности работы с Конструктором движений

Выясним причину, почему ручные правки часто «пропадают». В коде 1С есть специальные комментарии-маркеры: //{{__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ и //}}__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ. Если вы вносите изменения внутри этих тегов, то при следующем запуске конструктора ваши условия Если...Тогда будут полностью удалены, и код вернется к стандартному виду.

Золотое правило: Как только ваша логика становится сложнее, чем простое копирование данных из таблицы в регистр, откажитесь от конструктора. Либо вынесите весь код за пределы этих комментариев, либо удалите их совсем. Программирование вручную дает вам полный контроль над процессом формирования записей.

Методологический нюанс: Сторно или Расход?

В системе 1С существует два подхода к уменьшению показателей в регистрах накопления:

Для корректного формирования управленческой отчетности (например, чтобы возвраты не раздували обороты продаж) часто лучше использовать именно отрицательный приход. Выбор зависит от требований бизнес-логики вашей задачи.

Оптимизация: Запросный метод

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

Такой подход используется во всех типовых конфигурациях (ERP, УТ 11, КА) и считается «стандартом качества» для разработчика 1С.

← На главную