При разработке конфигураций на платформе 1С:Предприятие 8 часто возникает задача, когда один и тот же документ должен выполнять разные функции в зависимости от контекста. Например, при обычной продаже документ должен делать движение Приход в регистр накопления (начисление бонусов), а при оформлении возврата или корректировки — Расход (сторно бонусов). В данной статье мы подробно разберем, как правильно реализовать такую логику, избежав типичных ошибок начинающих программистов.
Рассмотрим ситуацию, с которой столкнулся автор темы. В процедуре ОбработкаПроведения был размещен код, созданный стандартным Конструктором движений. Проблема заключалась в том, что конструктор просто последовательно добавил два цикла: один для вида движения Расход, другой — для Приход.
Проанализируем фрагмент ошибочного кода:
// регистр Мэтр_НакопленияПоКартамЛояльности Расход
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Для Каждого ТекСтрока Из КартыЛояльности Цикл
Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
// ... заполнение свойств
КонецЦикла;
// регистр Мэтр_НакопленияПоКартамЛояльности Приход
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Для Каждого ТекСтрока Из КартыЛояльности Цикл
Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
// ... заполнение свойств
КонецЦикла;
При таком подходе система 1С выполнит оба цикла. В результате в регистр запишутся сразу две записи на каждую строку документа: одна с приходом, другая с расходом. Они взаимно уничтожат друг друга в остатках, либо просто создадут избыточные обороты. Для решения задачи нам необходимо внедрить логическое условие Если...Иначе.
Самый надежный способ — завести в документе реквизит (например, ТипОперации с типом ПеречислениеСсылка или просто Сторно с типом Булево). Это позволит системе «помнить», в каком режиме был проведен документ, даже если вы решите перепровести его программно через групповую обработку или фоновое задание.
Разберем по шагам реализацию с реквизитом Сторно:
Сторно (Тип: Булево) в метаданные документа.ОбработкаПроведения в модуле объекта.Проанализируем пример кода с использованием условия:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Очищаем движения, если это необходимо
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Движения.Мэтр_НакопленияПоКартамЛояльности.Очистить();
Для Каждого ТекСтрока Из КартыЛояльности Цикл
Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
Движение.Период = Дата;
Движение.КартаЛояльности = ТекСтрока.ОснКарта;
// Главное условие выбора вида движения
Если Сторно Тогда
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Сумма = ТекСтрока.Сумма;
Иначе
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Сумма = ТекСтрока.СуммаВиртИФиз;
КонецЕсли;
Движение.ЧекККМ = ТекСтрока.ЧекККМ;
КонецЦикла;
КонецПроцедуры
Если вы не хотите менять структуру метаданных (добавлять реквизит), можно использовать механизм ДополнительныеСвойства. Однако помните, что этот флаг живет только в течение одной транзакции записи, инициированной с формы.
Рассмотрим подробнее реализацию этого метода:
Шаг 1. На форме документа в обработчике команды:
&НаКлиенте
Процедура КомандаСторно(Команда)
// Устанавливаем признак в дополнительные свойства перед записью
ЭтаФорма.Объект.ДополнительныеСвойства.Вставить("РежимСторно", Истина);
ЭтаФорма.Записать(ПараметрыЗаписи);
КонецПроцедуры
Шаг 2. В модуле объекта в обработке проведения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Проверяем наличие флага
ЭтоСторно = Ложь;
ДополнительныеСвойства.Свойство("РежимСторно", ЭтоСторно);
Движения.Мэтр_НакопленияПоКартамЛояльности.Записывать = Истина;
Для Каждого ТекСтрока Из КартыЛояльности Цикл
Движение = Движения.Мэтр_НакопленияПоКартамЛояльности.Добавить();
// Применяем логику флага
Если ЭтоСторно Тогда
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Иначе
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
КонецЕсли;
// ... заполнение остальных полей
КонецЦикла;
КонецПроцедуры
Важное ограничение: Если такой документ будет перепроводиться обработкой «Групповое перепроведение документов», свойство РежимСторно не будет установлено, и документ всегда будет проводиться по ветке Иначе. Поэтому первый способ с реквизитом является более профессиональным.
Выясним причину, почему ручные правки часто «пропадают». В коде 1С есть специальные комментарии-маркеры: //{{__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ и //}}__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ. Если вы вносите изменения внутри этих тегов, то при следующем запуске конструктора ваши условия Если...Тогда будут полностью удалены, и код вернется к стандартному виду.
Золотое правило: Как только ваша логика становится сложнее, чем простое копирование данных из таблицы в регистр, откажитесь от конструктора. Либо вынесите весь код за пределы этих комментариев, либо удалите их совсем. Программирование вручную дает вам полный контроль над процессом формирования записей.
В системе 1С существует два подхода к уменьшению показателей в регистрах накопления:
ВидДвиженияНакопления.Расход. В отчетах это увеличит колонку «Расход».ВидДвиженияНакопления.Приход, но сумму записываем со знаком минус. Это уменьшает колонку «Приход».Для корректного формирования управленческой отчетности (например, чтобы возвраты не раздували обороты продаж) часто лучше использовать именно отрицательный приход. Выбор зависит от требований бизнес-логики вашей задачи.
Если в документе сотни или тысячи строк, перебор в цикле может работать медленно. Разберем, как проанализировать ситуацию и ускорить проведение. Вместо цикла можно использовать Запрос к табличной части документа. Мы можем добавить в запрос вычисляемое поле, которое будет определять вид движения на уровне базы данных, и затем выгрузить результат запроса напрямую в набор записей регистра.
Такой подход используется во всех типовых конфигурациях (ERP, УТ 11, КА) и считается «стандартом качества» для разработчика 1С.