В практике разработки на платформе 1С:Предприятие 8 часто возникает задача, когда один документ должен выполнять разнонаправленные движения по регистру накопления в зависимости от того, в какую табличную часть пользователь вносит данные. Типичный пример — документ корректировки запасов или сложного перемещения, где на одной вкладке («Товары к поступлению») регистрируется приход, а на другой («Товары к списанию») — расход.
В этой статье мы подробно разберем, как решить эту задачу двумя способами: с помощью интерактивного встроенного инструмента платформы (Конструктор движений) и путем написания кода вручную в модуле объекта документа. Также мы выясним причины типичных ошибок, из-за которых система может «выдавать ошибку» при проведении.
Прежде чем переходить к реализации, проанализируем ситуацию с точки зрения архитектуры метаданных 1С. В системе существует два типа регистров накопления:
Направление движения для регистра остатков задается системным перечислением ВидДвиженияНакопления, которое имеет ровно два значения: ВидДвиженияНакопления.Приход (обозначается знаком плюс «+») и ВидДвиженияНакопления.Расход (обозначается знаком минус «-»).
Многие начинающие разработчики ошибочно полагают, что Конструктор движений позволяет привязать к регистру только одну табличную часть. На самом деле инструмент обладает скрытой, но очень полезной функциональностью. Разберем по шагам, как настроить систему визуальными средствами:
ТоварыНаСкладах) установлена галочка. Это означает, что документ является регистратором для данного регистра.ТоварыПриход).ТоварыРасход).Платформа автоматически сгенерирует весь необходимый программный код в модуле объекта документа в процедуре ОбработкаПроведения. Этот способ идеален, так как минимизирует вероятность совершить синтаксическую ошибку.
Рассмотрим подробнее программный метод. Если нам требуется реализовать сложную логику (например, делать движения только при выполнении определенных условий или выполнять предварительные расчеты), то писать код придется вручную.
Посмотрим на пример реализации в процедуре ОбработкаПроведения модуля объекта документа. Мы будем использовать обход коллекций строк наших табличных частей во вложенных циклах:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Обязательно взводим флаг автоматической записи движений при завершении транзакции проведения
Движения.ТоварыНаСкладах.Записывать = Истина;
// Очищаем движения, если документ проводится повторно
Движения.ТоварыНаСкладах.Очистить();
// 2. Обработка ПРИХОДНОЙ табличной части
Для Каждого ТекСтрокаПриход Из ТоварыПриход Цикл
// Создаем новую запись в наборе движений регистра
НовоеДвижение = Движения.ТоварыНаСкладах.ДобавитьПриход();
// Заполняем обязательные свойства движения
НовоеДвижение.Период = Дата;
// Заполняем измерения регистра
НовоеДвижение.Номенклатура = ТекСтрокаПриход.Номенклатура;
НовоеДвижение.Склад = Склад;
// Заполняем ресурсы регистра
НовоеДвижение.Количество = ТекСтрокаПриход.Количество;
КонецЦикла;
// 3. Обработка РАСХОДНОЙ табличной части
Для Каждого ТекСтрокаРасход Из ТоварыРасход Цикл
// Создаем новую запись со знаком Расход
НовоеДвижение = Движения.ТоварыНаСкладах.ДобавитьРасход();
// Заполняем обязательные свойства движения
НовоеДвижение.Период = Дата;
// Заполняем измерения регистра
НовоеДвижение.Номенклатура = ТекСтрокаРасход.Номенклатура;
НовоеДвижение.Склад = Склад;
// Заполняем ресурсы регистра
НовоеДвижение.Количество = ТекСтрокаРасход.Количество;
КонецЦикла;
КонецПроцедуры
Обратите внимание на использование методов ДобавитьПриход() и ДобавитьРасход(). Это специализированные методы коллекции движений, которые не только добавляют новую строку в набор, но и автоматически устанавливают свойство ВидДвижения в соответствующее значение.
В качестве альтернативы можно использовать стандартный метод Добавить(), но тогда вид движения придется назначать явно:
НовоеДвижение = Движения.ТоварыНаСкладах.Добавить();
НовоеДвижение.ВидДвижения = ВидДвиженияНакопления.Расход;
Проанализируем ситуацию, когда разработчик вроде бы написал код или настроил конструктор, но при попытке проведения программа аварийно завершает работу с ошибкой. Наиболее частыми причинами сбоев являются три фактора:
1. Ошибка типа регистра. Самая частая ошибка новичков — попытка установить свойство ВидДвижения для регистра накопления, который имеет тип «Обороты». У оборотного регистра нет остатков, а значит, физически отсутствует реквизит направления движения. Попытка выполнить такой код вызовет ошибку времени выполнения: «Поле объекта не обнаружено (ВидДвижения)». Решение: убедитесь, что ваш регистр имеет вид «Остатки».
2. Ошибка типов данных при заполнении свойств. Если в измерение регистра с типом СправочникСсылка.Номенклатура попытаться передать строковое значение из табличной части (например, текстовое наименование товара вместо ссылки), платформа выдаст ошибку несоответствия типов при записи набора движений в базу данных.
3. Отсутствие флага записи. Если вы заполняете движения программно, но забыли указать Движения.ИмяРегистра.Записывать = Истина;, транзакция проведения завершится успешно, но в физические таблицы базы данных движения так и не запишутся.
Используя предложенные методы, вы сможете гибко управлять поведением регистров накопления и без труда реализовывать бизнес-логику любой сложности, распределяя потоки данных из разных интерфейсных форм документа.