Как распределить сумму по двум табличным частям документа 1С пропорционально их базам распределения?

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

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

Рассмотрим подробнее, как реализовать распределение единой "СуммыРаспределения" по двум табличным частям документа ("Запасы" и "Услуги") пропорционально "СуммеБазыРаспределения" каждой из этих частей.

Осознаем основную проблему

Автор изначально располагал процедурой для распределения суммы по одной табличной части. Основная трудность заключалась в том, что "СуммаРаспределения" является одной величиной, но ее необходимо разнести по строкам двух разных табличных частей. Прямое применение процедуры к каждой ТЧ с общей суммой привело бы к некорректному двойному распределению или неправильному расчету коэффициентов, так как каждая ТЧ не "знает" о существовании другой и их общем вкладе в базу распределения.

Ключевая мысль заключается в том, что для корректного распределения общей суммы по двум табличным частям, необходимо сначала эту общую сумму разделить на две части, пропорционально весу каждой табличной части в общей базе распределения. Только после этого каждую из полученных "под-сумм" можно распределять внутри своей табличной части с помощью уже существующего или модифицированного алгоритма.

Пошаговое решение: Распределение суммы по двум табличным частям

Давайте разберем процесс решения этой задачи по шагам, основываясь на эффективных подходах, предложенных в обсуждении.

  1. Определяем общую базу распределения по всем табличным частям.

    Нам потребуется суммировать значения колонки, по которой происходит пропорциональное распределение, из обеих табличных частей. Пусть эта колонка называется ИмяКолонкиБазы.

    
    ИтогБазыТЧ1 = 0;
    // Проверяем наличие табличной части "Запасы" для универсальности
    Если Объект.Метаданные().ТабличныеЧасти.Найти("Запасы") <> Неопределено Тогда
        ИтогБазыТЧ1 = Объект.Запасы.Итог("ИмяКолонкиБазы");
    КонецЕсли;
    
    ИтогБазыТЧ2 = 0;
    // Проверяем наличие табличной части "Услуги"
    Если Объект.Метаданные().ТабличныеЧасти.Найти("Услуги") <> Неопределено Тогда
        ИтогБазыТЧ2 = Объект.Услуги.Итог("ИмяКолонкиБазы");
    КонецЕсли;
    
    ОбщаяБазаРаспределения = ИтогБазыТЧ1 + ИтогБазыТЧ2;
    

    Важно: Проверка на наличие табличных частей (Объект.Метаданные().ТабличныеЧасти.Найти("ИмяТЧ")) обеспечивает устойчивость кода, если в разных документах или сценариях одна из табличных частей может отсутствовать. Если табличная часть отсутствует, ее итог будет равен 0, что корректно для дальнейших расчетов.

  2. Делим общую сумму распределения пропорционально между табличными частями.

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

    
    // Исходная СуммаРаспределения, которую нужно разнести
    СуммаРаспределения = ... ; 
    
    СуммаРаспределенияТЧ1 = 0;
    СуммаРаспределенияТЧ2 = 0;
    
    Если ОбщаяБазаРаспределения = 0 Тогда
        // Если общая база распределения равна нулю, пропорциональное распределение невозможно.
        // В зависимости от бизнес-логики, можно:
        // 1. Не распределять ничего (оставить СуммаРаспределенияТЧ1 и СуммаРаспределенияТЧ2 равными 0)
        // 2. Распределить всю СуммуРаспределения поровну на каждую ТЧ, если это уместно
        // 3. Вызвать ошибку или предупреждение
        // В данном примере мы исходим из того, что если база 0, то и распределять пропорционально нечего.
        СуммаРаспределенияТЧ1 = 0;
        СуммаРаспределенияТЧ2 = 0;
    Иначе
        // Рассчитываем сумму для первой ТЧ и округляем ее до двух знаков
        СуммаРаспределенияТЧ1 = Окр(СуммаРаспределения * ИтогБазыТЧ1 / ОбщаяБазаРаспределения, 2);
        // Сумма для второй ТЧ рассчитывается вычитанием первой из общей,
        // чтобы нивелировать возможные ошибки округления
        СуммаРаспределенияТЧ2 = СуммаРаспределения - СуммаРаспределенияТЧ1; 
    КонецЕсли;
    

    Данный подход гарантирует, что СуммаРаспределенияТЧ1 + СуммаРаспределенияТЧ2 будет точно равна исходной СуммаРаспределения, поскольку весь остаток от округления для первой части переносится на вторую. Это лучшая практика для таких сценариев.

  3. Вызываем существующую процедуру распределения для каждой табличной части.

    Теперь, когда у нас есть конкретная сумма для каждой табличной части, мы можем вызвать нашу процедуру распределения для каждой из них по отдельности. Важно передавать в процедуру в качестве параметра СуммаБазыРаспределения именно итог базы текущей табличной части, а не общую базу.

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

    Это финальный этап, после которого все суммы будут распределены согласно требуемой логике.

Анализ и модификация процедуры распределения по одной табличной части

Рассмотрим исходную процедуру, предоставленную автором, и внесем в нее необходимые уточнения и улучшения для надежной работы.


Процедура РаспределитьСуммуПропорциональноБазе(Таблица, СуммаБазыРаспределенияТЧ, ИмяКолонкиБазы, ИмяКолонкиДляРаспределения, СуммаРаспределенияНаТЧ, Объект)

    // Если база для распределения в текущей ТЧ равна 0 или сумма для распределения равна 0,
    // то пропорциональное распределение не имеет смысла или не требуется.
    // Просто обнуляем или устанавливаем в 0 значение в колонке для распределения.
    Если СуммаБазыРаспределенияТЧ = 0 И СуммаРаспределенияНаТЧ = 0 Тогда
        Для каждого СтрокаТаблицы ИЗ Таблица Цикл
            СтрокаТаблицы[ИмяКолонкиДляРаспределения] = 0;
            // Обновление сопутствующих полей (скидки, суммы)
            СтрокаТаблицы.ПроцентСкидкиНаценки = 0;
            СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Цена * СтрокаТаблицы.Количество, 0, РежимОкругления.Окр15как20);
            СтрокаТаблицы.Всего = СтрокаТаблицы.Сумма;
        КонецЦикла;
        Возврат;
    ИначеЕсли СуммаБазыРаспределенияТЧ = 0 И СуммаРаспределенияНаТЧ <> 0 Тогда
        // Здесь можно реализовать логику, если база 0, но сумму все равно надо распределить (например, равномерно).
        // В текущей задаче это не предполагается, поэтому мы также просто обнулим.
        // Или можно равномерно распределить СуммаРаспределенияНаТЧ по всем строкам ТЧ, 
        // если Таблица.Количество() > 0.
        СуммаНаСтроку = 0;
        Если Таблица.Количество() > 0 Тогда
            СуммаНаСтроку = Окр(СуммаРаспределенияНаТЧ / Таблица.Количество(), 2);
        КонецЕсли;

        ОстатокРаспределения = СуммаРаспределенияНаТЧ;
        Для каждого СтрокаТаблицы ИЗ Таблица Цикл
            Если ОстатокРаспределения > 0 И СуммаНаСтроку > 0 Тогда
                СтрокаТаблицы[ИмяКолонкиДляРаспределения] = СуммаНаСтроку;
                ОстатокРаспределения = ОстатокРаспределения - СуммаНаСтроку;
            Иначе
                СтрокаТаблицы[ИмяКолонкиДляРаспределения] = 0;
            КонецЕсли;
            // Обновление сопутствующих полей (скидки, суммы)
            ИсходнаяСуммаСтроки = СтрокаТаблицы.Цена * СтрокаТаблицы.Количество;
            Если ИсходнаяСуммаСтроки > 0 Тогда
                СтрокаТаблицы.ПроцентСкидкиНаценки = (СтрокаТаблицы[ИмяКолонкиДляРаспределения] * 100) / ИсходнаяСуммаСтроки;
                СтрокаТаблицы.Сумма = ИсходнаяСуммаСтроки * (1 - СтрокаТаблицы.ПроцентСкидкиНаценки / 100);
            Иначе
                СтрокаТаблицы.ПроцентСкидкиНаценки = 0;
                СтрокаТаблицы.Сумма = 0; 
            КонецЕсли;
            СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Сумма, 0, РежимОкругления.Окр15как20);
            СтрокаТаблицы.Всего = СтрокаТаблицы.Сумма;
        КонецЦикла;
        // Распределяем оставшийся мелкий остаток на последнюю строку
        Если Абс(ОстатокРаспределения) > 0.01 И Таблица.Количество() > 0 Тогда
            ПоследняяСтрока = Таблица[Таблица.Количество() - 1]; // Берем последнюю строку
            ПоследняяСтрока[ИмяКолонкиДляРаспределения] = ПоследняяСтрока[ИмяКолонкиДляРаспределения] + ОстатокРаспределения;
            
            ИсходнаяСуммаСтроки = ПоследняяСтрока.Цена * ПоследняяСтрока.Количество;
            Если ИсходнаяСуммаСтроки > 0 Тогда
                ПоследняяСтрока.ПроцентСкидкиНаценки = (ПоследняяСтрока[ИмяКолонкиДляРаспределения] * 100) / ИсходнаяСуммаСтроки;
                ПоследняяСтрока.Сумма = ИсходнаяСуммаСтроки * (1 - ПоследняяСтрока.ПроцентСкидкиНаценки / 100);
            Иначе
                ПоследняяСтрока.ПроцентСкидкиНаценки = 0;
                ПоследняяСтрока.Сумма = 0;
            КонецЕсли;
            ПоследняяСтрока.Сумма = Окр(ПоследняяСтрока.Сумма, 0, РежимОкругления.Окр15как20);
            ПоследняяСтрока.Всего = ПоследняяСтрока.Сумма;
        КонецЕсли;
        Возврат;
    КонецЕсли;

    // Переменные для отслеживания остатков в процессе распределения
    ОстатокСуммыДляРаспределения = СуммаРаспределенияНаТЧ; 
    ОстатокБазыДляРаспределения = СуммаБазыРаспределенияТЧ; 

    Для каждого СтрокаТаблицы ИЗ Таблица Цикл
        Если СтрокаТаблицы[ИмяКолонкиБазы] > 0 Тогда
            // Вычисляем коэффициент распределения для текущей строки
            // Если ОстатокБазыДляРаспределения = 0, но СуммаРаспределенияНаТЧ все еще есть (из-за округления),
            // то К может стать некорректным. Добавим проверку.
            КоэффициентРаспределения = 0;
            Если ОстатокБазыДляРаспределения > 0 Тогда
                КоэффициентРаспределения = ОстатокСуммыДляРаспределения / ОстатокБазыДляРаспределения;
            КонецЕсли;
            
            СуммаКРаспределению = Окр(СтрокаТаблицы[ИмяКолонкиБазы] * КоэффициентРаспределения, 2);
            
            // Если это последняя строка, на которую можно распределить, 
            // переносим на нее весь оставшийся СуммаРаспределенияНаТЧ (с учетом округлений)
            Если ОстатокБазыДляРаспределения - СтрокаТаблицы[ИмяКолонкиБазы] < 0.01 Тогда // Почти последняя строка с базой
                СуммаКРаспределению = ОстатокСуммыДляРаспределения;
            КонецЕсли;

            СтрокаТаблицы[ИмяКолонкиДляРаспределения] = СуммаКРаспределению;
            
            // Обновление сопутствующих полей (скидки, суммы)
            ИсходнаяСуммаСтроки = СтрокаТаблицы.Цена * СтрокаТаблицы.Количество;
            Если ИсходнаяСуммаСтроки > 0 Тогда
                СтрокаТаблицы.ПроцентСкидкиНаценки = (СуммаКРаспределению * 100) / ИсходнаяСуммаСтроки;
                СтрокаТаблицы.Сумма = ИсходнаяСуммаСтроки * (1 - СтрокаТаблицы.ПроцентСкидкиНаценки / 100);
            Иначе
                СтрокаТаблицы.ПроцентСкидкиНаценки = 0;
                СтрокаТаблицы.Сумма = 0; 
            КонецЕсли;
            
            СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Сумма, 0, РежимОкругления.Окр15как20);
            СтрокаТаблицы.Всего = СтрокаТаблицы.Сумма;
            
            // Корректируем оставшиеся суммы и базы для следующей итерации
            ОстатокСуммыДляРаспределения = ОстатокСуммыДляРаспределения - СуммаКРаспределению;
            ОстатокБазыДляРаспределения = ОстатокБазыДляРаспределения - СтрокаТаблицы[ИмяКолонкиБазы];
            
        Иначе // Если ИмяКолонкиБазы = 0 для данной строки, не распределяем на нее
            СтрокаТаблицы[ИмяКолонкиДляРаспределения] = 0;
            СтрокаТаблицы.ПроцентСкидкиНаценки = 0;
            СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Цена * СтрокаТаблицы.Количество, 0, РежимОкругления.Окр15как20);
            СтрокаТаблицы.Всего = СтрокаТаблицы.Сумма;
        КонецЕсли;
    КонецЦикла;

    // Проверка и распределение оставшегося остатка округления (если он есть)
    // на последнюю строку, которая имеет базу для распределения.
    Если Абс(ОстатокСуммыДляРаспределения) > 0.01 И Таблица.Количество() > 0 Тогда 
        ПоследняяСтрокаДляРаспределения = Неопределено;
        Для каждого СтрокаТаблицы ИЗ Таблица Цикл
            Если СтрокаТаблицы[ИмяКолонкиБазы] > 0 Тогда
                ПоследняяСтрокаДляРаспределения = СтрокаТаблицы;
            КонецЕсли;
        КонецЦикла;
        
        Если ПоследняяСтрокаДляРаспределения <> Неопределено Тогда
            ПоследняяСтрокаДляРаспределения[ИмяКолонкиДляРаспределения] = ПоследняяСтрокаДляРаспределения[ИмяКолонкиДляРаспределения] + ОстатокСуммыДляРаспределения;
            
            // Пересчет процентов и сумм для последней строки
            ИсходнаяСуммаСтроки = ПоследняяСтрокаДляРаспределения.Цена * ПоследняяСтрокаДляРаспределения.Количество;
            Если ИсходнаяСуммаСтроки > 0 Тогда
                ПоследняяСтрокаДляРаспределения.ПроцентСкидкиНаценки = (ПоследняяСтрокаДляРаспределения[ИмяКолонкиДляРаспределения] * 100) / ИсходнаяСуммаСтроки;
                ПоследняяСтрокаДляРаспределения.Сумма = ИсходнаяСуммаСтроки * (1 - ПоследняяСтрокаДляРаспределения.ПроцентСкидкиНаценки / 100);
            Иначе
                ПоследняяСтрокаДляРаспределения.ПроцентСкидкиНаценки = 0;
                ПоследняяСтрокаДляРаспределения.Сумма = 0;
            КонецЕсли;
            ПоследняяСтрокаДляРаспределения.Сумма = Окр(ПоследняяСтрокаДляРаспределения.Сумма, 0, РежимОкругления.Окр15как20);
            ПоследняяСтрокаДляРаспределения.Всего = ПоследняяСтрокаДляРаспределения.Сумма;
        КонецЕсли;
    КонецЕсли;

    // Исходная строка автора "Объект.ИмяКолонкиБазы = Объект.Запасы.Итог("ИмяКолонкиБазы");"
    // не относится непосредственно к логике распределения внутри одной ТЧ и должна быть удалена
    // или перенесена в вызывающий код, если она выполняет какую-то итоговую функцию для всего документа.
КонецПроцедуры

В этой модифицированной процедуре мы учли несколько важных моментов:

  1. Переменные СуммаК и СуммаБазыК из исходного кода автора были переименованы в более явные ОстатокСуммыДляРаспределения и ОстатокБазыДляРаспределения.
  2. Исправлена опечатка: вместо К = Сумма / СуммаБазыРаспределения; теперь используется КоэффициентРаспределения = ОстатокСуммыДляРаспределения / ОстатокБазыДляРаспределения;, что позволяет более точно распределять остатки.
  3. Добавлена обработка случая, когда ИмяКолонкиБазы в строке равна 0, чтобы избежать деления на ноль и корректно установить значение для распределения в этой строке.
  4. Реализован механизм "добрасывания" оставшейся суммы округления на последнюю подходящую строку в табличной части, что гарантирует точное совпадение итогов распределения внутри одной ТЧ.
  5. Обработан граничный случай, когда СуммаБазыРаспределенияТЧ для данной табличной части равна нулю.
  6. Строка Объект.ИмяКолонкиБазы = Объект.Запасы.Итог("ИмяКолонкиБазы"); была удалена из процедуры, поскольку она не относится к логике распределения внутри конкретной табличной части. Если такая логика нужна, ее следует вынести в основной код, который вызывает процедуру для обеих ТЧ.

Полный пример реализации

Теперь соберем весь код, который будет вызывать нашу усовершенствованную процедуру распределения, воедино. Этот код обычно размещается в модуле объекта документа или в общем модуле, если процедура РаспределитьСуммуПропорциональноБазе является экспортной.


// Предположим, что мы находимся в модуле объекта документа.
// "Объект" - это сам документ.
// "СуммаДляРаспределения" - это исходная общая сумма, которую нужно распределить.
// "ИмяКолонкиБазы" - это имя колонки в ТЧ, по которой идет распределение (например, "Сумма", "Вес", "Количество").
// "ИмяКолонкиДляРаспределения" - это имя колонки в ТЧ, куда записывается распределенная сумма/скидка (например, "СуммаСкидки", "ДопРасходы").

Перем СуммаДляРаспределения ЭКСПОРТ; // Или получаем из реквизита документа, регистра сведений и т.п.

// ... В какой-то процедуре, например, ПередЗаписью или ПриИзменении табличной части

Процедура РаспределитьДополнительныеРасходыПоДвумТЧ()

    // 1. Рассчитываем итоги базы распределения для каждой табличной части
    // Учитываем возможность отсутствия табличных частей
    ИтогБазыТЧЗапасы = 0;
    Если Объект.Метаданные().ТабличныеЧасти.Найти("Запасы") <> Неопределено Тогда
        ИтогБазыТЧЗапасы = Объект.Запасы.Итог("ИмяКолонкиБазы");
    КонецЕсли;

    ИтогБазыТЧУслуги = 0;
    Если Объект.Метаданные().ТабличныеЧасти.Найти("Услуги") <> Неопределено Тогда
        ИтогБазыТЧУслуги = Объект.Услуги.Итог("ИмяКолонкиБазы");
    КонецЕсли;

    ОбщаяБазаРаспределенияВсехТЧ = ИтогБазыТЧЗапасы + ИтогБазыТЧУслуги;

    // 2. Делим общую СуммаДляРаспределения на две части для каждой ТЧ
    СуммаРаспределенияНаТЧЗапасы = 0;
    СуммаРаспределенияНаТЧУслуги = 0;

    Если ОбщаяБазаРаспределенияВсехТЧ = 0 Тогда
        // Если общая база равна нулю, распределение пропорционально не имеет смысла.
        // Суммы для распределения остаются нулевыми.
        // Дополнительно можно обнулить все ранее распределенные суммы в ТЧ.
        // Например:
        Если Объект.Метаданные().ТабличныеЧасти.Найти("Запасы") <> Неопределено Тогда
            Для каждого СтрокаТЧ ИЗ Объект.Запасы Цикл
                СтрокаТЧ["ИмяКолонкиДляРаспределения"] = 0;
                // Пересчет остальных полей, связанных с распределением
                СтрокаТЧ.ПроцентСкидкиНаценки = 0;
                СтрокаТЧ.Сумма = Окр(СтрокаТЧ.Цена * СтрокаТЧ.Количество, 0, РежимОкругления.Окр15как20);
                СтрокаТЧ.Всего = СтрокаТЧ.Сумма;
            КонецЦикла;
        КонецЕсли;
        Если Объект.Метаданные().ТабличныеЧасти.Найти("Услуги") <> Неопределено Тогда
            Для каждого СтрокаТЧ ИЗ Объект.Услуги Цикл
                СтрокаТЧ["ИмяКолонкиДляРаспределения"] = 0;
                // Пересчет остальных полей, связанных с распределением
                СтрокаТЧ.ПроцентСкидкиНаценки = 0;
                СтрокаТЧ.Сумма = Окр(СтрокаТЧ.Цена * СтрокаТЧ.Количество, 0, РежимОкругления.Окр15как20);
                СтрокаТЧ.Всего = СтрокаТЧ.Сумма;
            КонецЦикла;
        КонецЕсли;
    Иначе
        // Расчет и округление для первой ТЧ
        СуммаРаспределенияНаТЧЗапасы = Окр(СуммаДляРаспределения * ИтогБазыТЧЗапасы / ОбщаяБазаРаспределенияВсехТЧ, 2);
        // Остаток на вторую ТЧ, чтобы обеспечить точное соответствие общей сумме
        СуммаРаспределенияНаТЧУслуги = СуммаДляРаспределения - СуммаРаспределенияНаТЧЗапасы; 
    КонецЕсли;

    // 3. Вызываем процедуру распределения для каждой табличной части
    Если Объект.Метаданные().ТабличныеЧасти.Найти("Запасы") <> Неопределено Тогда
        // Вызываем только если есть табличная часть "Запасы"
        РаспределитьСуммуПропорциональноБазе(Объект.Запасы, ИтогБазыТЧЗапасы, "ИмяКолонкиБазы", "ИмяКолонкиДляРаспределения", СуммаРаспределенияНаТЧЗапасы, Объект);
    КонецЕсли;

    Если Объект.Метаданные().ТабличныеЧасти.Найти("Услуги") <> Неопределено Тогда
        // Вызываем только если есть табличная часть "Услуги"
        РаспределитьСуммуПропорциональноБазе(Объект.Услуги, ИтогБазыТЧУслуги, "ИмяКолонкиБазы", "ИмяКолонкиДляРаспределения", СуммаРаспределенияНаТЧУслуги, Объект);
    КонецЕсли;

КонецПроцедуры

// Пример процедуры РаспределитьСуммуПропорциональноБазе (размещается в общем модуле или модуле объекта)
// (Используем ту же модифицированную процедуру, что была описана выше)
Процедура РаспределитьСуммуПропорциональноБазе(Таблица, СуммаБазыРаспределенияТЧ, ИмяКолонкиБазы, ИмяКолонкиДляРаспределения, СуммаРаспределенияНаТЧ, Объект)
    // ... Код процедуры, приведенный выше
КонецПроцедуры

Обработка граничных случаев и оптимизация

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

  1. Нулевая база распределения: Если ОбщаяБазаРаспределенияВсехТЧ равна нулю, это означает, что ни в одной строке обеих табличных частей нет значения в колонке ИмяКолонкиБазы, по которой нужно распределять. В этом случае пропорциональное распределение теряет смысл. В нашем примере мы обнуляем все суммы распределения. Другим вариантом может быть равномерное распределение всей СуммаДляРаспределения по всем строкам обеих ТЧ, если это соответствует вашей бизнес-логике. Внимательно проанализируйте, что должно происходить в такой ситуации.

  2. Отсутствие одной из табличных частей: Использование Объект.Метаданные().ТабличныеЧасти.Найти("ИмяТЧ") <> Неопределено позволяет избежать ошибок при доступе к несуществующим табличным частям, что делает код более универсальным.

  3. Точность округления: Мы применяем метод переноса остатка округления на одну из частей (СуммаРаспределенияНаТЧУслуги = СуммаДляРаспределения - СуммаРаспределенияНаТЧЗапасы;) при делении общей суммы. Это гарантирует, что сумма распределенных частей точно совпадет с исходной. Аналогичный подход используется и внутри процедуры РаспределитьСуммуПропорциональноБазе для распределения по строкам одной ТЧ, доначислением остатка на последнюю строку с ненулевой базой. Это стандартный и рекомендуемый способ борьбы с ошибками округления в 1С.

Заключение

Мы успешно разобрали, как распределить единую сумму по двум табличным частям документа 1С пропорционально их базам распределения. Ключевые шаги включают предварительное разделение общей суммы, эффективное использование существующей процедуры распределения для каждой части и внимательное отношение к ошибкам округления и граничным случаям. Применяя предложенные подходы, вы сможете создать надежный и корректно работающий механизм распределения в ваших конфигурациях 1С.

← На главную