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