Как реализовать цикл для распределения суммы пропорционально количеству?
В 1С цикл для распределения суммы пропорционально количеству строится на основе таблицы значений, в которой каждая строка содержит информацию о единице распределения (например, товар или подразделение) и количество, по которому должна быть произведена пропорциональная разбивка общей суммы. Задача состоит в том, чтобы каждое значение распределённой суммы было округлено до рубля, а вся сумма в итоге точно совпадала с исходной.
Исходные данные
Предположим, у нас есть:
-
Общая сумма: СуммаКРаспределению = 40 (в рублях).
-
Таблица значений с колонками Номенклатура, Количество, Сумма (последняя заполняется в процессе).
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Количество");
ТЗ.Колонки.Добавить("Сумма"); // для результата
// Примерные данные
Стр = ТЗ.Добавить(); Стр.Номенклатура = "Товар1"; Стр.Количество = 2;
Стр = ТЗ.Добавить(); Стр.Номенклатура = "Товар2"; Стр.Количество = 3;
Стр = ТЗ.Добавить(); Стр.Номенклатура = "Товар3"; Стр.Количество = 5;
### **Шаг 1. Рассчёт базы распределения**
```python
База распределения — это сумма всех количеств:
База = ТЗ.Итог("Количество");
Если База = 0 Тогда
ВызватьИсключение "Невозможно распределить сумму: база равна нулю.";
КонецЕсли;
Шаг 2. Первичный цикл — вычисление необрезанных значений и их округление
На этом шаге вычисляется доля каждой строки, и распределяется сумма с округлением до целого рубля. Также можно сразу сохранить остаток (ошибку округления), чтобы учесть это позже.
СуммаОкругленная = 0;
Для Каждого Строка Из ТЗ Цикл
// Вычисляем точную долю
Пропорция = Строка.Количество / База;
Неокругл = СуммаКРаспределению \* Пропорция;
// Округляем до рубля
Округл = Окр(Неокругл, 0);
Строка.Сумма = Округл;
// Суммируем то, что уже распределили
СуммаОкругленная = СуммаОкругленная + Округл;
// Сохраняем погрешность для коррекции
Строка.Погрешность = Неокругл - Округл;
КонецЦикла;
Шаг 3. Корректировка остатка
После округления общая сумма может отличаться от заданной. Например, распределили 39 вместо 40. Нужно дораспределить остаток. Для этого можно отсортировать строки по убыванию погрешности (у кого ошибка округления была наибольшей) и прибавить по 1 рублю, пока сумма не сравняется с исходной.
Остаток = СуммаКРаспределению - СуммаОкругленная;
Если Остаток <> 0 Тогда
// Сортировка по погрешности
ТЗ.СортироватьПо("Погрешность", Истина); // Истина = убывание
Индекс = 0;
Пока Остаток <> 0 Цикл
Строка = ТЗ\[Индекс\];
Строка.Сумма = Строка.Сумма + Знак(Остаток);
Остаток = Остаток - Знак(Остаток);
Индекс = (Индекс + 1) % ТЗ.Количество();
КонецЦикла;
КонецЕсли;
Результат
Теперь в каждой строке таблицы значений есть колонка Сумма, в которой хранится округлённая сумма, пропорциональная количеству, а итоговая сумма по всем строкам точно равна исходной СуммаКРаспределению.
Обработка ошибок и защита
-
Нужно учитывать деление на 0, если база распределения равна нулю.
-
При больших объёмах данных не рекомендуется выполнять сортировку вручную — лучше использовать коллекции или временные таблицы с правильной структурой.
Альтернативный способ — использовать вспомогательные структуры
Можно использовать массивы, словари (Соответствие), если требуется хранить более сложные промежуточные данные, например: индекс строки, остаток округления и пр. Это бывает удобно, если распределение применяется к десяткам тысяч строк.
Таким образом, цикл для распределения суммы реализуется в три этапа:
-
Рассчитать доли.
-
Округлить и накопить сумму.
-
Дораспределить разницу между округлённой и исходной суммой, основываясь на максимальной ошибке округления.