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