В практике автоматизации учета производственных или торговых предприятий мы часто сталкиваемся с операцией "Разукомплектация". Это обратный процесс сборки, когда из одной позиции (комплекта) получается несколько новых позиций (комплектующих). Ключевым моментом здесь является распределение себестоимости исходного комплекта на полученные части. В конфигурациях 1С (например, "Управление торговлей" ред. 10.3, взаимодействующей с "Бухгалтерией предприятия" 3.0) для этого используется реквизит ДоляСтоимости.
Однако существует неочевидная проблема: при определенном соотношении долей некоторые строки могут просто исчезнуть из бухгалтерских проводок при выгрузке, хотя в товарном учете они присутствуют. Давайте разберем эту ситуацию подробно, выясним математические причины ошибки и рассмотрим способы решения.
Рассмотрим ситуацию, описанную пользователями. В документе "Комплектация номенклатуры" с видом операции "Разукомплектация" есть табличная часть, где мы указываем полученные товары и их долю стоимости. Обычно это целое число. Себестоимость комплекта распределяется пропорционально этим долям.
Пример ситуации:
Допустим, мы разбираем дорогостоящее оборудование. Основная часть забирает на себя почти всю стоимость, а какой-то мелкий винтик или упаковка — минимальную часть. Вы указываете:
Общая сумма долей = 2001. При распределении стоимости система должна рассчитать коэффициенты. Для "Винтика" коэффициент будет равен: 1 / 2001 = 0,0004997...
Проблема возникает на этапе формирования проводок в "Бухгалтерии предприятия". В типовых алгоритмах распределения часто используются типизированные переменные или реквизиты с типом Число(15, 3). Это стандартная точность для количественных и некоторых суммовых показателей.
Давайте посмотрим, что происходит с нашим коэффициентом при приведении к типу Число(15, 3):
В результате, когда система пытается сформировать проводку для "Винтика", она умножает общую себестоимость на коэффициент 0. Получается нулевая сумма. Типовой механизм проведения документов в 1С часто устроен так, что если сумма проводки равна нулю, то движение по регистру бухгалтерии не записывается вовсе. Строка "пропадает".
Важно понимать, где проходит граница, за которой начинаются проблемы. Опытным путем и математически мы можем вычислить "магическое число".
Чтобы число при округлении до 3 знаков дало хотя бы 0,001, исходное значение должно быть не меньше 0,0005.
Составим неравенство, где N — это общая сумма долей:
1 / N >= 0,0005
Отсюда:
N <= 1 / 0,0005
N <= 2000
Вывод: Если сумма всех долей стоимости в документе превышает 2000, то любая строка с долей, равной 1, превратится в ноль и исчезнет из учета. Если доля равна 2, то критическая сумма долей будет 4000, и так далее.
Если мы не хотим или не можем менять конфигурацию "Бухгалтерия предприятия" (например, она находится на полной поддержке и мы не хотим использовать расширения), нам необходимо гарантировать, что из "Управления торговлей" (УТ) придут данные, которые корректно обработаются. Для этого нужно искусственно повысить долю стоимости для мелких позиций так, чтобы их вес в общей сумме превышал 0,05%.
Разберем алгоритм, который можно внедрить в обработку заполнения документа или перед выгрузкой данных:
Однако можно пойти упрощенным путем без сложных итераций, определив минимальный порог сразу.
Формула для минимальной доли строки (MinShare), чтобы она не обнулилась при текущей сумме долей (TotalSum):
MinShare = ОкрВверх(TotalSum / 2000)
Посмотрим на пример реализации такой логики на встроенном языке 1С:
Процедура СкорректироватьДолиСтоимости(ДокументОбъект)
// Получаем таблицу комплектующих
ТаблицаКомплектующих = ДокументОбъект.Комплектующие;
// Считаем общую сумму долей
ОбщаяСуммаДолей = ТаблицаКомплектующих.Итог("ДоляСтоимости");
// Определяем порог безопасности.
// Если ОбщаяСуммаДолей > 2000, то доля 1 уже опасна.
// Нам нужно, чтобы Доля / ОбщаяСумма >= 0.0005
Для Каждого СтрокаТЧ Из ТаблицаКомплектующих Цикл
ТекущаяДоля = СтрокаТЧ.ДоляСтоимости;
// Вычисляем коэффициент, который получится в бухгалтерии
РасчетныйКоэффициент = ТекущаяДоля / ОбщаяСуммаДолей;
// Если коэффициент попадает в зону риска округления до нуля
Если РасчетныйКоэффициент < 0.0005 Тогда
// Нам нужно найти новую долю X, такую что X / (ОбщаяСуммаДолей - ТекущаяДоля + X) >= 0.0005
// Для упрощения можно просто установить минимально безопасную долю
// исходя из текущей суммы, понимая, что при увеличении доли общая сумма тоже вырастет,
// что еще больше "размоет" доли, но незначительно.
// Простой вариант:
МинимальноНеобходимаяДоля = Окр(ОбщаяСуммаДолей / 2000) + 1;
СтрокаТЧ.ДоляСтоимости = МинимальноНеобходимаяДоля;
// ВАЖНО: После изменения одной строки, ОбщаяСуммаДолей изменилась!
// Нужно обновить переменную для следующих итераций цикла
ОбщаяСуммаДолей = ТаблицаКомплектующих.Итог("ДоляСтоимости");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Недостаток метода: Мы вмешиваемся в экономику процесса. Увеличивая долю "мусора" или дешевых комплектующих, мы немного занижаем себестоимость основного изделия. Однако, как справедливо заметили участники обсуждения, погрешность в пределах долей процента обычно менее критична, чем полное исчезновение позиции из бухгалтерского учета и возникновение пересортицы или зависших остатков без суммы.
Более правильным с точки зрения методологии и сохранения точности данных является исправление причины, а не следствия. Причина кроется в использовании типа Число(15, 3) в промежуточных расчетах на стороне "1С:Бухгалтерии".
В современных конфигурациях (БП 3.0 и выше) мы можем использовать механизм Расширений (Extensions). Это позволяет изменить поведение системы без снятия конфигурации с поддержки.
Что нужно сделать:
&Вместо или скопировав логику в собственную подписку/функцию), заменив типизацию промежуточных переменных.Вместо жесткой типизации используйте тип Число с большей разрядностью или вообще не типизируйте переменную явно, позволяя платформе использовать максимальную точность Число(32, ...).
Пример концепции изменения кода:
// Было в типовом коде (условно):
// Перем Коэффициент Распределения Тип Число(15,3);
// КоэффициентРаспределения = Строка.Доля / ОбщаяСумма; // Здесь происходит округление до 0.000
// Стало в нашем исправлении:
// Не задаем тип явно или задаем с запасом
КоэффициентРаспределения = Окр(Строка.Доля / ОбщаяСумма, 10); // Оставляем высокую точность
СуммаСписания = ОбщаяСебестоимость * КоэффициентРаспределения;
Этот метод позволяет корректно распределить суммы даже при соотношении долей 1 к 10000, не искажая пропорции, заданные технологами или пользователями.
Иногда проблему проще решить на уровне правил ведения учета, не прибегая к программированию. Если при разукомплектации образуется множество мелких деталей с ничтожной стоимостью, стоит пересмотреть подход к их оценке.
Рекомендации:
Проблема исчезновения строк при разукомплектации вызвана математическим округлением коэффициентов меньше 0.0005 в типовых конфигурациях.
Для исправления ситуации: