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