Распределение суммы по базе
Разработка - Практика программирования
Афиняне! Повсему вижу я, что Вы как-то по-особеному набожны, ибо проходя и осматривая Ваши святыни, я наткнулся и на жертвенник неведомому богу...
Где-то в библии в адрес древних греков...
В общем и целом написать данную статью подвигла меня очередная лекция на тему себестоимости. Кстати, крайне рекомендую курс для ИТ-менеджеров в открытом университете, который там сейчас находится в открытом доступе.
Итак, классика!
Суть в том, что везде, где я встречаю код распределения (размазывания) одной суммы на другую по некому базису, всё всегда сводится к нахождению коэффициента распределения (когда мы делим распределяемую сумму на сумму базы) и последующего умножения этого коэффициента на базу по строке (например, если мы распределяем пропорционально количеству, то на количество).
Таким образом все сводится к такому вот методу:
Сумма | Количество | Распределенная сумма |
100 | 1 | 16,(6) * 1 = 16,67 |
200 | 2 | 16,(6) * 2 = 33,33 |
300 | 3 | 16,(6) * 3 = 50 |
итого: 600 | итого: 6 | итого: 100, к = 100/6 = 16,(6) |
Здесь базой является количество, сумма базы = 6, распределяемая сумма = 100. Коэффициент = распределяемая сумма / сумма базы = 100 / 6 = 16,(6) ("Шесть в скобках" - это то, как нас учили записывать периодичские дроби. Если кого-то учили иначе - проьба иметь это ввиду). Далее в каждой строке я округляю результат до копеек.
В принципе мы получили то, что хотели - распределили нужную сумму пропорционально количеству. В данном случае у нас крайне удачно получилось с округлением - в первой строке мы округлили вверх и получили одну лишнюю копейку, во второй строке мы округлили вниз и потеряли копейку. И то, что нам так повезло - это воля парня, сказавшего парню из эпиграфа сказать древним грекам все те умные вещи, о которых он им сказал...
Давайте рассмотрим случай, когда тот парень был к нам не так благосклонен, а именно - давайте распределим 10 на 3:
Сумма | Количество | Распределенная сумма |
100 | 1 | 3,(3) * 1 = 3,33 |
200 | 1 | 3,(3) * 1 = 3,33 |
250 | 1 | 3,(3) * 1 = 3,33 - добавим разницу 0,01 = 3,34 |
итого: 550 | итого: 3 | итого: 10? нет! 9,99 + 0,01 = 10, к = 10/3 = 3,(3) |
В итоге у нас не хватило одной копейки. Для того, чтобы решить эту проблему, необходимо учесть остаточек в конце. У нас распределенная сумма получилась равна 9,99, а сумма, которую нужно распределить - 10. Разницу, обычно, добавляют к последней строке. Т.е. в последней строке у нас будет 3,34, "чтобы не нарушать отчетности" (с).
Все хрошо, пока потерянная в ходе округления сумма мала и не играет большой роли. Но если мы попытаемся таким же образом распределить 10 на 30 строк, то внезапно окажется, что к последней строке нам нужно прибавить уже не 1 копейку, а 10. Можно, конечно, прибавить сумму остатка к последней строке:
№ п/п | Сумма | Количество | Распределенная сумма |
1 | 100 | 1 | 0,(3) * 1 = 0,33 |
2 | 200 | 1 | 0,(3) * 1 = 0,33 |
3 | 250 | 1 | 0,(3) * 1 = 0,33 - добавим разницу 0,01 = 3,34 |
... | ... | ... | ... |
29 | 200 | 1 | 0,(3) * 1 = 0,33 |
30 | 100 | 1 | 0,(3) * 1 = 0,33 |
итого: 30 | итого: 10? нет! 9,90! |
В последней строке в итоге будет сумма 0,33 + 0,10 = 0,43. Если мы распределяем какие-нибудь ксвенные затраты на количество выпуска, то для каждой статьи затрат может набраться весьма большое отклонение, которое все целиком упадет на последнюю строчку. Таким образом продукт, выпущенный нами в последнюю очередь, вберет в свою себестоимость все те отклонения и станет "золотым" )))
Если мы будем дораспределять остаток, то, в принципе, мы также можем попасть на округление и дораспределять нам придется до тх пор, пока все копейки не израсходуются. Это, как мне кажется, несколько неудобно, непрозрачно да и затратно.
Новое решение!
Давным-давно, кажется в позапрошлую работу, меня попросили создать обработку, которая бы перекраивала контуры полей, перераспределяя на их новую площадь какие-то старые остатки на счетах учета затрат на дату распределения. Там как раз сумма распределялась между новыми площадями пропорционально новому метражу. Звучит пространно, но примите на веру (как древние греки), что это относится к обсуждаемой нами задаче распределения суммы по базе. И тогда я как раз "родил" (ага, прям как Авраам Исаака) алгоритм распределения, после которого нет остатка. Странно, но тогдашний мой руководитель так и не понял суть алгоритма, хотя после теста сказал, что все работает и оставил как есть. Западные программисты в таких случаях просто стараются не использовать подобные алгоритмы, так что честь и хвала программистам российским, которые используют и то, в чем не понимают )))
В принципе все просто: мы каждую итерацию должны пересчитывать коэффициент распределения. Давайте построим таблицу с 30-ю записями и добавим колонки для нового коэффициента и по-новому распределенной суммы:
№ п/п | Сумма | Количество | Распределенная сумма | Плавающий коэффициент | По-новому распределенная сумма |
1 | 100 | 1 | 0,(3) * 1 = 0,33 | 10/30 = 0,(3) | 0,33 |
2 | 200 | 1 | 0,(3) * 1 = 0,33 | 9,67/29 = 0,333448... | 0,33 |
3 | 250 | 1 | 0,(3) * 1 = 0,33 | 9,34/28 = 0,333571... | 0,33 |
... | ... | ... | ... | ||
29 | 200 | 1 | 0,(3) * 1 = 0,33 | 0,67/2 = 0,34 | 0,34 |
30 | 100 | 1 | 0,(3) * 1 = 0,33 | 0,33/1 = 0,33 | 0,33 |
итого: 30 | итого: 9,90 | итого: 10 |
Таким образом у нас больше нет остатка!
Через практическое мессианство! Или перейдем на ты к практике.
Давайте попробуем написать код на языке 1С, который бы распределял сумму ппропорционально базовой колонке таблицы.
Процедура РаспределитьСуммуПропорциональноБазе(Таблица, ИмяКолонкиБазы, ИмяКолонкиДляРаспределения, Сумма)
СуммаБазы = Таблица.Итог(ИмяКолонкиБазы);
Для каждого СтрокаТаблицы ИЗ Таблица Цикл
К = Сумма / СуммаБазы;
СуммаКРаспределению = Окр(СтрокаТаблицы[ИмяКолонкиБазы] * К, 2);
СтрокаТаблицы[ИмяКолонкиДляРаспределения] = СтрокаТаблицы[ИмяКолонкиДляРаспределения] + СуммаКРаспределению;
Сумма = Сумма - СуммаКРаспределению;
СуммаБазы = СуммаБазы - СтрокаТаблицы[ИмяКолонкиБазы]
КонецЦикла
КонецПроцедуры
Вот такой вот незамысловатый код получился. И можно забыть про контроль остатка нераспределившейся суммы.
В качестве постскриптума...
Этот алгоритм был навеян мне целочисленным алгоритмом построения линии, т.к. в нем Х распределяется на У (или наоборот - при оптимизации вообще пишут два варианта, учитывая, какое смещение больше - по Х или по У).
Специальные предложения
См. также
Полезные процедуры и функции для программиста 140
07.10.2019 8992 HostHost 23
Таблица значений. Нюансы 191
01.10.2019 8538 Yashazz 35
[Шпаргалка] Программное создание элементов формы 283
06.09.2019 9806 rpgshnik 41
Агрегатные функции СКД, о которых мало кто знает 342
05.09.2019 13062 ids79 44
Отслеживание выполнения фонового задания 141
17.08.2019 10494 ids79 16
Функции СКД: ВычислитьВыражение, ВычислитьВыражениеСГруппировкойМассив 253
08.08.2019 14160 ids79 30
СКД - наборы данных и связи между ними, создание собственной иерархии, вложенные отчеты 131
26.07.2019 12734 ids79 6
Обработчики событий при записи объектов. Зачем и что за чем? 202
25.07.2019 12814 4 AlbinaAAA 23
Управление качеством кода 136
22.07.2019 8260 Stepa86 29
СКД - использование расширений языка запросов, секция ХАРАКТЕРИСТИКИ 146
17.07.2019 11219 ids79 27
Регистры сведений. За кулисами 129
09.07.2019 8771 YPermitin 12
"Меньше копипаста!", или как Вася универсальную процедуру писал 183
04.07.2019 7972 SeiOkami 49
Создание отчетов с помощью СКД - основные понятия и элементы 208
25.06.2019 21052 ids79 17
Многопоточное ускорение однопользовательских нагрузок в 1С + Microsoft SQL Server 2017 179
11.06.2019 12722 dmurk 134
Регистры накопления. Структура хранения в базе данных 176
16.05.2019 19039 YPermitin 27
Выполнение внешней обработки в фоновом задании 149
11.05.2019 11309 Eret1k 23
Выгрузка документа по условию 5
25.04.2019 6099 m-rv 2
Как прикрутить ГУИД к регистру сведений 23
16.04.2019 8729 m-rv 16
О расширениях замолвите слово... 193
07.04.2019 17853 ellavs 122
Git-репозитории для 1С-кода (опыт использования при небольших проектах) 202
28.03.2019 13972 ellavs 83
Трюки с внешними источниками данных 166
14.03.2019 14271 YPermitin 52
Возможности типовых шаблонов ограничения доступа на уровне записей (RLS) 166
03.02.2019 17349 ids79 9
Разработка и сценарное тестирование с Vanessa-ADD. Концепция, теория и сквозной пример создания сценария 222
09.01.2019 27596 Vladimir Litvinenko 69
EnterpriseData – часть 2. Процесс выгрузки данных 127
26.12.2018 13809 ids79 27
Новый подход к обмену данными EnterpriseData 207
14.12.2018 23187 ids79 72
Программное заполнение пользовательских параметров и отборов СКД 136
13.11.2018 22186 Unk92 19
Автоматические и управляемые блокировки применительно к типовым конфигурациям 1С 127
10.11.2018 22282 ids79 40
Вспомогательные инструкции в коде 1С 105
15.10.2018 21529 tormozit 100
Произвольный код в фоновом режиме 165
03.09.2018 15755 nikita0832 42
Основные понятия и механизмы оптимизации клиент-серверного взаимодействия в 1C 147
23.08.2018 22914 Rain88 42
Тестер: частые вопросы 156
25.07.2018 21000 grumagargler 24
Повышаем эффективность разработки правил обмена 124
25.06.2018 20245 olegtymko 47
Введение в механизм представлений в ЗУП ред. 3 156
04.06.2018 25622 xrrg 82
Как сделать запрос на изменение данных 75
01.06.2018 22166 m-rv 21
Строим графы средствами 1С (без GraphViz) 43
23.05.2018 17958 slozhenikin_com 19
Распределение расходов пропорционально продажам 9
13.05.2018 12062 Rustig 9
Просмотр временных таблиц запроса в отладчике без изменения кода 129
24.04.2018 26537 [email protected] 19
[ВсеПросто] "Оперативный" информатор из 1С за 5 мин. 198
22.02.2018 21455 DarkAn 25
Минимализмы 3 355
19.02.2018 37134 ildarovich 44
Этюды по программированию. Взаимодействие с Microsoft Word 109
11.12.2017 26564 milkers 23
Метод формирования движений в типовых регистрах нетиповыми регистраторами 31
05.12.2017 21994 itriot11 34
1С: Конвертация данных 3. Инструкции и примеры. EnterpriseData (универсальный формат обмена) 737
19.11.2017 142816 MaxS 251
Заполнение данных по ИНН контрагента с помощью альтернативного сервиса огрн.онлайн 131
01.11.2017 23733 slava_1c 49
Программные перечисления, ч.2: приемы кэширования при разработке 67
30.10.2017 22061 unichkin 18
Разбираемся с настройками компоновки данных 161
29.10.2017 25126 json 9
Работа с Excel 298
23.10.2017 27125 arakelyan 39
Добавление команд печати в конфигурациях на БСП 2.4.3 (в частности, в самописных документах в Бухгалтерии 3.0 после релиза 3.0.52.35) 144
18.09.2017 48883 bugtester 43
Как сделать из &НаКлиентеНаСервереБезКонтекста почти &НаКлиентеНаСервере 127
10.09.2017 35165 tormozit 72