При разработке алгоритмов проведения документов часто возникает задача: формировать движения по регистру накопления в зависимости от состояния реквизитов в табличной части. Например, если в строке заполнено только время начала работы, необходимо выполнить запись с видом движения Приход, а если заполнены и время начала, и время окончания — движение с видом Расход.
Разберем распространенную ошибку, когда при наличии нескольких строк в документе происходит задвоение записей или неверное назначение вида движения, а также проанализируем правильные способы решения этой задачи в платформе 1С:Предприятие 8.
Рассмотрим типовую ошибку, с которой сталкиваются начинающие разработчики. При переносе логики из платформы 1С 7.7 или при использовании вспомогательных сервисных процедур типовых конфигураций (например, ВыполнитьПриход() и ВыполнитьРасход()) код обхода табличной части пишут следующим образом:
Для Каждого ТекСтрока Из ТехнологическиеОперации Цикл
Если ЗначениеЗаполнено(ТекСтрока.ВремяНачала) И Не ЗначениеЗаполнено(ТекСтрока.ВремяОкончания) Тогда
Движения.ВыполнениеМаршрутныхКарт.ВыполнитьПриход();
ИначеЕсли ЗначениеЗаполнено(ТекСтрока.ВремяНачала) И ЗначениеЗаполнено(ТекСтрока.ВремяОкончания) Тогда
Движения.ВыполнениеМаршрутныхКарт.ВыполнитьРасход();
КонецЕсли;
КонецЦикла;
Проанализируем, почему этот код работает некорректно при наличии более чем одной строки в табличной части:
ВыполнитьПриход() в модулях наборов записей 1С 8 обрабатывают всю предварительно подготовленную таблицу движений целиком.Чтобы избежать задвоения и четко увязать каждую строку табличной части с конкретной записью регистра, откажемся от вызова групповых процедур и сформируем движения напрямую через объект НаборЗаписей.
Разберем по шагам алгоритм проведения:
Добавить().ВидДвижения (значение из системного перечисления ВидДвиженияНакопления.Приход или ВидДвиженияНакопления.Расход).Рассмотрим пример кода в процедуре ОбработкаПроведения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Включаем флаг записи движений для регистра
Движения.ВыполнениеМаршрутныхКарт.Записывать = Истина;
// Получаем доступ к набору записей
НаборДвижений = Движения.ВыполнениеМаршрутныхКарт;
Для Каждого ТекСтрока Из ТехнологическиеОперации Цикл
// Проверяем заполнение дат
ВремяНачалаЗаполнено = ЗначениеЗаполнено(ТекСтрока.ВремяНачала);
ВремяОкончанияЗаполнено = ЗначениеЗаполнено(ТекСтрока.ВремяОкончания);
// Если дата начала не заполнена вовсе — пропускаем строку
Если Не ВремяНачалаЗаполнено Тогда
Продолжить;
КонецЕсли;
// Добавляем новую движение в набор
Движение = НаборДвижений.Добавить();
Движение.Период = Дата;
// Определяем вид движения по условию
Если ВремяНачалаЗаполнено И Не ВремяОкончанияЗаполнено Тогда
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
ИначеЕсли ВремяНачалаЗаполнено И ВремяОкончанияЗаполнено Тогда
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
КонецЕсли;
// Заполняем измерения и ресурсы регистра
Движение.МаршрутнаяКарта = Ссылка;
Движение.НомерОперации = ТекСтрока.НомерОперации;
Движение.ТехнологическаяОперация = ТекСтрока.Операция;
Движение.Количество = 1;
Движение.Получил = ТекСтрока.Получил;
Движение.Выполнил = ТекСтрока.Выполнил;
КонецЦикла;
КонецПроцедуры
Если в конфигурации принят стандарт формирования движений через запросы к табличным частям (оптимальный с точки зрения производительности подход), мы можем вычислить вид движения прямо в тексте запроса с помощью конструкции ВЫБОР КОГДА ... ТОГДА.
Рассмотрим, как это выглядит в запросе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК МаршрутнаяКарта,
| Док.Дата КАК Период,
| ТЧ.НомерОперации КАК НомерОперации,
| ТЧ.Операция КАК ТехнологическаяОперация,
| 1 КАК Количество,
| ВЫБОР
| КОГДА ЗНАЧЕНИЕЗАПОЛНЕНО(ТЧ.ВремяНачала) И НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(ТЧ.ВремяОкончания)
| ТОГДА ЗНАЧЕНИЕ(Перечисление.ВидыДвиженийНакопления.Приход)
| КОГДА ЗНАЧЕНИЕЗАПОЛНЕНО(ТЧ.ВремяНачала) И ЗНАЧЕНИЕЗАПОЛНЕНО(ТЧ.ВремяОкончания)
| ТОГДА ЗНАЧЕНИЕ(Перечисление.ВидыДвиженийНакопления.Расход)
| КОНЕЦ КАК ВидДвижения
|ИЗ
| Документ.МаршрутнаяКартаПроизводства.ТехнологическиеОперации КАК ТЧ
|ГДЕ
| ТЧ.Ссылка = &Ссылка
| И ЗНАЧЕНИЕЗАПОЛНЕНО(ТЧ.ВремяНачала)";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
ТаблицаДвижений = Запрос.Выполнить().Выгрузить();
// Загружаем полученную таблицу прямо в набор записей регистра
Движения.ВыполнениеМаршрутныхКарт.Загрузить(ТаблицаДвижений);
Движения.ВыполнениеМаршрутныхКарт.Записывать = Истина;
Важное примечание: При выгрузке результата запроса в набор записей имена колонок в запросе должны полностью совпадать с именами измерений, ресурсов и реквизитов регистра накопления, а также содержать служебные колонки Период и ВидДвижения.
Предупреждение()) в обработке проведения. Серверный контекст проведения требует применения объекта СообщениеПользователю или установки флага Отказ = Истина с функцией Сообщить().Записывать = Истина для используемых регистров накопления, иначе платформенный механизм проведения не сохранит сформированные записи в базе данных.