Как реализовать алгоритм распределения суммы в 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. Преимущества такого подхода
-
Обеспечивает строгую точность.
-
Прост в реализации.
-
Подходит для любых пропорциональных распределений.
-
Устраняет проблемы округления и ошибок учёта.