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


Реализация алгоритма распределения суммы в 1С с точностью до рубля и пропорционально определённому показателю (например, количеству) требует последовательных шагов: сбора данных, расчёта пропорций, округления, компенсации расхождений и обновления значений. Это особенно актуально при расчётах налога, распределении скидок, затрат, бюджета и т.д.

1. Постановка задачи

Имеется:

  • Общая сумма, например 40 рублей.

  • Таблица значений с колонкой Количество.

Необходимо:

  • Пропорционально распределить сумму по строкам в зависимости от Количество.

  • Результат округлить до рублей.

  • Обеспечить, чтобы сумма распределённых значений строго соответствовала исходной.

2. Создание таблицы значений

Создаётся таблица значений с колонками:

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура");
Таблица.Колонки.Добавить("Количество");
Таблица.Колонки.Добавить("Доля");
Таблица.Колонки.Добавить("Распределено");
Таблица.Колонки.Добавить("Остаток");

Пример добавления данных:

Стр = Таблица.Добавить();
Стр.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("001");
Стр.Количество = 10;
Стр = Таблица.Добавить();
Стр.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("002");
Стр.Количество = 5;
Стр = Таблица.Добавить();
Стр.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("003");
Стр.Количество = 3;

### **3\. Расчёт базы распределения**

Сначала рассчитывается общая база распределения:
```python
ОбщаяБаза = 0;
Для каждого Стр из Таблица Цикл
ОбщаяБаза = ОбщаяБаза + Стр.Количество;
КонецЦикла;

4. Расчёт долей и предварительных сумм

Рассчитываем долю и исходную сумму:

ОбщаяСумма = 40;
Для каждого Стр из Таблица Цикл
Стр.Доля = Стр.Количество / ОбщаяБаза;
Стр.Распределено = Цел(Стр.Доля \* ОбщаяСумма); // округление вниз
Стр.Остаток = (Стр.Доля \* ОбщаяСумма) - Стр.Распределено;
КонецЦикла;

5. Вычисление расхождения и корректировка

Подсчитываем сумму округлённых значений:

СуммаПослеОкругления = 0;
Для каждого Стр из Таблица Цикл
СуммаПослеОкругления = СуммаПослеОкругления + Стр.Распределено;
КонецЦикла;
НеХватает = ОбщаяСумма - СуммаПослеОкругления;
Если НеХватает > 0, добавляем недостающие рубли в строки с наибольшим Остаток:
Таблица.Сортировать("Остаток Убыв");
Для Индекс = 0 По НеХватает - 1 Цикл
Таблица\[Индекс\].Распределено = Таблица\[Индекс\].Распределено + 1;
КонецЦикла;

6. Проверка корректности

Проверяем, что итоговая сумма соответствует заданной:

Контроль = 0;
Для каждого Стр из Таблица Цикл
Контроль = Контроль + Стр.Распределено;
КонецЦикла;
Если Контроль <> ОбщаяСумма Тогда
ВызватьИсключение("Ошибка распределения. Итоговая сумма не совпадает.");
КонецЕсли;

7. Вывод или возврат результата

Вы можете использовать распределённую сумму по каждой строке для записи в документ, формирования движений, печатных форм и пр.

Пример вывода в окно сообщений:

Для каждого Стр из Таблица Цикл
Сообщить("Номенклатура: " + Стр.Номенклатура.Наименование +
", Количество: " + Стр.Количество +
", Распределено: " + Стр.Распределено);
КонецЦикла;

8. Возможные модификации

  • Вместо Количество можно использовать любую базу распределения (вес, объем, длительность).

  • Округление можно заменить на Окр(...) с разными режимами.

  • При необходимости, можно ввести дополнительное поле “ИзначальнаяСумма” (до округления).

9. Вариант использования в документе

Алгоритм может быть вызван:

  • В обработчике команды формы документа.

  • В общем модуле обработки данных.

  • Как часть движения документа при проведении.

10. Преимущества такого подхода

  • Обеспечивает строгую точность.

  • Прост в реализации.

  • Подходит для любых пропорциональных распределений.

  • Устраняет проблемы округления и ошибок учёта.