Почему не получается делить значения напрямую из выборки запроса и как правильно рассчитать себестоимость

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто встречается ситуация, когда простая арифметическая операция с полями выборки запроса приводит к ошибке или неожиданному результату (например, значению Неопределено). Особенно остро этот вопрос встает при подготовке к экзамену 1С:Специалист, где важна точность расчетов и соблюдение методологии. Рассмотрим подробнее, почему возникают такие сложности и как правильно организовать алгоритм списания себестоимости.

Анализ проблемы типизации в выборке

Разберем ситуацию, когда программист пытается выполнить расчет напрямую: Себестоимость = Выборка.Сумма / Выборка.Количество. На первый взгляд, это логично, но платформа может выдать ошибку. Выясним причину этого поведения. Обращение к Выборка.ИмяПоля — это обращение к свойству объекта выборки. В момент выполнения арифметической операции система должна неявно преобразовать это свойство в числовой тип данных.

Если в результате запроса по какой-то причине оказалось значение NULL (например, из-за левого соединения с регистром остатков), попытка деления приведет к ошибке. Даже использование функции ЕСТЬNULL в тексте запроса не всегда спасает, если используются Итоги. В иерархических выборках (по группировкам) значения ресурсов на разных уровнях могут интерпретироваться платформой специфическим образом.

Решение через промежуточные переменные, которое применил автор темы (ВыбКол = Выборка.Количество; Себестоимость = ВыбСум / ВыбКол;), принудительно типизирует значение. Присваивая значение локальной переменной, мы заставляем систему извлечь примитивное значение из объекта выборки и поместить его в память как Число. После этого математические операции проходят корректно.

Правильный алгоритм расчета себестоимости при списании

Проанализируем программный код, который часто используется в задачах бухгалтерского учета. Важно помнить о двух критических моментах: потере точности и закрытии остатков "в ноль".

Рассмотрим по шагам правильную логику расчета суммы списания:

  1. Проверка на полное списание количества: Если количество товара в документе совпадает с текущим остатком на складе (в регистре), мы не должны заниматься делением. В этом случае нужно списать всю оставшуюся сумму остатка полностью. Это предотвращает появление "зависших копеек" (ситуация, когда количество равно 0, а сумма на счете осталась).
  2. Формула расчета: Если списывается только часть товара, используйте формулу СуммаСписания = СуммаОстаток * КоличествоСписания / КоличествоОстаток.

Посмотрим на пример кода, реализующего эту логику:


Если ВыборкаНоменклатура.КоличествоОстаток <> 0 Тогда    
    
    Если ВыборкаНоменклатура.Количество = ВыборкаНоменклатура.КоличествоОстаток Тогда
        // Списываем всё под чистую
        СуммаСписания = ВыборкаНоменклатура.СуммаОстаток; 
    Иначе
        // Рассчитываем пропорционально
        // Важно: сначала умножаем, потом делим для минимизации погрешности
        СуммаСписания = ВыборкаНоменклатура.СуммаОстаток * Выборка.Количество / ВыборкаНоменклатура.КоличествоОстаток; 
    КонецЕсли;
    
Иначе
    СуммаСписания = 0;
КонецЕсли;

Особенности использования конструкции ИТОГИ в запросе

Проанализируем ситуацию с использованием ключевого слова ИТОГИ. Если ваш запрос содержит расчет остатков и вы делаете обход по группировкам (ОбходРезультатаЗапроса.ПоГруппировкам), помните, что поля остатков (например, СуммаОстатокДт) должны быть агрегированы в секции итогов.

Если в запросе используются ИТОГИ, но при обходе выборки вы обращаетесь к детальным записям через вложенный цикл Выборка = ВыборкаНоменклатура.Выбрать(), убедитесь, что вы не пытаетесь делить значения итогов на значения детальных записей без предварительной проверки. Часто ошибку вызывает попытка получить остаток там, где его нет (на уровне итогов по всей выборке, если не установлены соответствующие фильтры).

Методики контроля отрицательных остатков

В современных конфигурациях 1С рекомендуется использовать так называемую "Новую методику проведения". Рассмотрим её отличия от классической:

При использовании старой методики (как в примере автора темы) крайне важно использовать объект БлокировкаДанных. Это гарантирует, что пока вы считываете остатки и принимаете решение о списании, другой пользователь не успеет забрать этот же товар.


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрБухгалтерии.Управленческий");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Счет", ПланыСчетов.Управленческий.Товары); 
ЭлементБлокировки.УстановитьЗначение("Субконто2", Склад);
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Субконто1", "Номенклатура"); 
Блокировка.Заблокировать();

Подводя итог, отметим: если вы столкнулись с невозможностью выполнить арифметическую операцию напрямую из выборки, в первую очередь проверьте наличие NULL в данных и убедитесь, что вы не делите на ноль. Использование промежуточных переменных (явная типизация) — надежный способ избежать проблем с динамическим определением типов в сложных выражениях платформы 1С.

← На главную