Как стать автором
Обновить

Комментарии 241

Дополню.
Алгоритм округления может отличаться в разных программах. Это приводит к тому что поставщик, выписывающий документы в Аксапте например, и покупатель, приходующий товар в одноэсе получат определенные расхождения в суммах товара.
Поэтому в 1с можно ввести и сумму а не только цену.
А потом «потанцевать» с фискальным чеком =)
Не только поэтому. Есть ещё НДС, который нацело никогда не вычисляется.
Если у вас есть табличка товар | цена | кол-во | сумма | ндс, и последней строкой у вас идёт «итого», то сумма по столбцу ндс чаще всего не будет равна итого*18%. И налоговая это нормально хавает.
Честно говоря ожидал статью как раз по округлениям и хитростям с ними связанным. А тут только самые общие моменты.
нормальные пацаны ведут учет НДС отдельно. То есть считаем НДС один раз для ИТОГО.
Ну конечно. Счета-фактуры эти «нормальные пацаны» не принимают, не выставляют и не сдают в налоговую, потому как работают на помойке, так?
Ну, ладно вам. Это вам просто законодательство такое не позволяет. В нашей же помойке, если речь не идёт про клиента-физика, все позиции счета идут без НДС, но в конце считаем НДС от всей суммы. Для физиков же позиции указываем цену с НДС, и так же для общей суммы считаем общий НДС. Но это конечно только в нашей помойке такое удобство…
Я не понимаю за что заминусили Ваш пост выше любители «красивых счетов-фактур любой ценой», но в какой «помойке» законодательство позволяет (или даже настаивает?) считать НДС построчно и потом суммировать это? Такой подход противоречит самой сути данного налога.
А как, извините, применить ваш алгоритм для покупки в которой присутствуют товары со ставками НДС в 18%, 10% и 0%?
вам ничто не мешает написать: НДС 18%: 10 руб, НДС 10%: 5 руб
А если НДС 0%, то зачем его указывать?
Или вы что-то другое имели ввиду?
Внутри чека разные ставки НДС, т.е.
То есть считаем НДС один раз для ИТОГО.
Не получится просто из-за отсутствия единой ставки.
Специально для минусителя законодательство РФ:

Налоговый кодекс
С т а т ь я 153. Налоговая база

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


Т.е. как раз внутри одного документа: «налоговая база
определяется отдельно по каждому виду товаров (работ, услуг),
облагаемых по разным ставкам»
А такие нюансы регулируются подзаконными актами. Прямого указания выписывать одну накладную на товары/услуги с разными ставками НДС я в вашем Налоговом кодексе не нашел. Как, впрочем, и запрет на выписку раздельных накладных.

И внимательно читайте второе выделение.
Вот именно, в одной накладной можно выписывать разные ставки. А ваша придумка про разные накладные — не нужна.
я не вижу проблемы посчитать НДС для каждой ставки.
То есть у вас в чеке есть по одной строке для каждой ставки НДС.
Но если вы хотели меня поймать на том, что я сказал «считаем НДС один раз для ИТОГО», то считайте что вы победили, но мы говорили немного о другом.
Зачем ловить, вы сами так написали.

Следующей итерацией, можно дойти и до «не вижу проблемы посчитать НДС для каждой строки»
вот как раз и бессмысленность следующей итерации я и хотел показать.
Вообще-то НДС 0% нужно указывать, это не то же самое, что отсутствие НДС (ст. 164 НК). По нулевому НДС также сдается декларация.
А как, извините, применить ваш алгоритм для покупки в которой присутствуют товары со ставками НДС в 18%, 10% и 0%?

Тупое минусение извинению не подлежит.

Речь не об алгоритмах, а о требованиях закона. Товары с разными ставками НДС будьте добры отпускать разными расходными накладными.
«Тупое минусение извинению не подлежит.» это был эвфемизм.
А, извините, где и когда законы требуют такую, извините, странную операцию производить как «Товары с разными ставками НДС отпускать разными расходными накладными.»?
А давайте вот у vanxant и спросим. Это же ведь он написал:
Если у вас есть табличка товар | цена | кол-во | сумма | ндс, и последней строкой у вас идёт «итого»,

итого для строки это сумма + НДС. Так что, несмотря, на отсутствие поля хранения итого в строке(что плохо, но возможно опущено, также как и ставка НДС), такая ситуация возможна.
Более того — алгоритм округления реально может работать по разному на разном железе. Было что при переносе с теста на продуктив получали совершенно неожиданный результат.
Такое обычно происходит если вычисления выполняются во float/double (и отдаются на откуп железу). А если использовать decimal-типы, то вам гарантируется одинаковый результат на любом вменяемом железе.
В яве можно добавить модификатор strictfp, чтобы эти граблей обойти.
Если бы только в этом проблема была! В AXAPTA «не одобряется» использование более 2-х знаков после запятой, зато для 1сников 4...6 знаков для получения «красивых» сумм из-за НДС — почти норма.
И сколько «этим» не рассказывай, что при ставке НДС 20% и цене товара 100 грн с НДС (83,[3] грн без НДС) будут копеечки гулять, — они все равно проводят «округления» в 4...6 знаке и потом удивляются, что остальные такие тупые и так не делают.

Не нужно 1С-ников под одну гребенку чесать, иногда ситуации патовые, и что там одобряет Аксапта с 1С — дело десятое. С сайта госзакупок приходят цены с торгов, которые по закону включающих в себя налоги. И будьте добры, чтобы итоговая цена в реализациях совпадала с контрактной. Клиенты выкручиваются тем, что правят циферки в Экселе. И не волнует никого, что правильным расчётом эту цену с учётом НДС не получить. Так и появляются эти дикие дроби. Конечно, исходная проблема в том, что нет проверки правильного расчёта цен.

Вместо double надо использовать представление числа на основе степеней с основанием 10.

Почему бы просто не использовать long и вести все вычисления в монетах наименьшей ценности? Для рубля — в копейках, для доллара — в центах.
Какую монету наименьшей ценности выберем для биткоина?
У биткоина есть минимальная неделимая единица «сатоши», равная 0.00000001 биткоина.
Биткоин в данном случае был плохим примером. А хорошим было бы, например, золото, которое меряется в унциях, граммах, фунтах, килограмах, тоннах — какую наименьшую неделимую массу золота можно предложить эталоном?
Так речь-то шла про валюты, а золото валютой даже с очень большой натяжкой не является.
В отличие от валют, у весовых товаров нет не только минимальной неделимой единицы, но и вообще какой-то естественной единицы.
Для весовых товаров будут свои подходы, отличные от подходов к представлению валют.
Помимо того что золото немножко не валюта — квант золота обычно прописан в вашей учетной политике, и часто совпадает с ценой деления ваших весов.
Учтите, что учитывается только химически чистое золото. Что в общем случае не соответствует взвешенным граммам пробных слитков и изделий.
3.27 x 10^-22 грамма.
Ну если уж брать в молекулах, то обязательно получим преценденты, когда часть золота может испариться)
В атомах, у золота нет молекул, ибо это металл.
Ну а вам не все ли равно, в каком виде золото пришло — в твердом, жидком или газообразном? =)
Ну если уж учитывать разные атомарные состояния, то молекулы у металлов всё-таки есть, правда это одноатомные(и иногда двухатомные) молекулы для газообразного состояния)
Но за поправку спасибо — перечитаю на досуге советскую энциклопедию по химии, освежу память)
Сколько центов дадим за одну копейку?

Это уже вопрос представления данных.

Нет, это вопрос предметной области, т.к. часто курсы даются с точностью до сотых долей цента/копейки.

В случае внутреннего хранения курсов валют как рациональных дробей вопросы вида "сколько центов дадим за одну копейку" становятся именно что вопросом представления — в каком виде показывать курс пользователю.

Курс не имеет денежной размерности, а имеет размерность отношения.
А суммы везде отражаются с округлением до минимальной единицы. Цента, копейки или сатоши. Взяли курс, взяли сумму в одной валюте, вычислили сумму в другой с учетом наших правил округления, и получили целое число минимальных единиц. Другое дело что могут быть промежуточные этапы вычислений, и могут образоваться нетривиальные ситуации с округлением. Но дело в том, что само округление в любом случае есть.
курсы даются с точностью до сотых долей цента/копейки


Курс в теории не ограничен количеством знаков после запятой.
ЦБ на сайте выдает до 4 знаков после запятой.
Но при при конвертации миллионов $, с таким курсом можно потерять несколько тысяч рублей.
Поэтому иногда используют курс с 6 — 8 знаками после запятой.
Поэтому иногда используют курс с 6 — 8 знаками после запятой.

Любые 2-4 к ЦБшному дописывают или строго нули?
Я это встречал только в банках при хранении внутренного курса банка. Там дописывают те, которые считаю нужным (да и первые цифры тоже от ЦБшных могут отличаться).
40 nano центов
Ни одного, и вообще такие операции откажемся проводить — так сейчас считают большинство обменников, с которыми я сталкивался… Есть минимальная сумма, от которой операция разрешается — это округлённое в большую сторону количество копеек за один цент.

Лог лучше или хуже в данном случае? А то у себя для денег как раз лонг выбрал.

Это вопрос необходимого уровня абстракции

Плюсую. В одном конкретном известном мне банке так и делается (про остальные банки просто не в курсе).
Веселье начинается обычно с вычисляемыми денежными суммами. Например, процентами на остаток на счете за некоторый период. Они изначально имеют не целый тип. Но про хранение копеек в лонге все равно плюсую.
в банках в абс деньги хранятся в базе в обычных decimal. Сам виде.. делал
Боюсь, что в банках в этом плане полный зоопарк. В пределах одной АБС может быть в одной таблице decimal, а в другом bigint или даже float. Поскольку нигде нет документации, где сказано: «Вот делайте так, и да пребудет с вами Cила».
то, что decimal лучше подходит для хранения денежных сумм, это да, не спорю. Но если, например, от 1353 рублей 65 копеек нужно взять 18,5 % за несколько дней, то результат легко может быть разным в случае округления до целых копеек до суммирования по дням и в другом случае, когда округление выполняется после суммирования.
Ниже уже написали, все это должно быть прописано в нормативных документах банка.
ну реальность такова, что на все расчеты нормативки не напасешься. Плюс в половине случаев даже аналитики не знают где ее искать. Но это так, лирика.
В основном в кредитных договорах формула тщательно расписана, включая краевые эффекты, прямо в договоре. Полагаю формула в договоре согласована с формулой в софте.
Цена с НДС 2311 «монеток» — сколько это будет без НДС? а сколько будет величина НДС? -)
Стоимость без НДС = Стоимость с НДС / 118 * 100 (если НДС 18%)
НДС = Стоимость с НДС / 118 * 18 (если НДС 18%)
Hint: не все числа с точностью 2 знака после запятой можно безболезненно разделить на 1.18 =)

Ну или попроще «3 шарикоподшипника на сумму 100 руб.» -)
Всегда можно найти компромиссы: либо стоимость с НДС делать кратным 1.18, либо к любой стоимости без НДС добавлять НДС (избавить себя от лишних расчётов).
Ога-ога =)
Заодно построить поставщиков на предмет кратности -)

p.s. Next Level: счет-фактура и фискальный чек — там несколько взаимоисключающе вычисления -)

p.p.s. Собственно решения для всех этих случаев есть, не всегда бескостыльные, но тут увы…

Точно. Особенно, если контракт с сайта госзакупок пришёл

Хватает даже просто контрактов с полугосами, по которым после отгрузки вылезает отказ в оплате типа «в документе хххх на сумму 100500 миллионов рублей 37 копеек в строчке 47645 указана цена за единицу 13.11 копеек и количество 0.01, а в контракте на странице 873 оговаривалась цена 13.10 копеек без НДС» -))
я человек простой. прочел. понравилось. говорю автору спасибо!
з.ы. и это, друг, в 3:30 лучше спасть, а не на хабр писать xD

А может он из Владивостока

где-то тут была статья про время…
Те кто не когда не работал в банках может что то и откроют для себя новое, но реально все гораздо сложнее.
В нормальных системах существует цела иерархия объектов и классов предназначенных для работы с денежными объектами. И она существенно сложнее чем вы описываете.
Есть правила расчетов – то с какой точностью ведутся расчёты для примера для рубля РФ это чаще всего 4 знака после запятой(т.е. с точностью до 0,01 коп.)
Правила округления, правила отображения и печати сумм. Вводится понятие операция: кредитовая или дебетовая. И еще много вроде бы очевидных вещей, но наличие «стандарта» на них обязательно.
А уж если вводился понятие счет (объект для хранения сумм и правил работы с ними) то описания подобных вещей реально взрывает мозг. Там и связанные счета и активные и пассивные. А сверху это цементируется понятием «проводка» чтобы исполнялся закон сохранения энергии — закон сохранения денег.
И все это ноу-хау о которых разработчики не очень любят распространяться.
Это никакое не ноу-хау, это знание предметной области.
При всем уважении к производителям банковского софта я бы не решился назвать описанные вами нюансы очень сложными. И что-то пока даже по работе ведущих банков не заметно, чтобы разработчики сами хорошо владели своим ноу-хау.
ну в статье даже термина «проводка» нету. Реально лайт. А двойная запись и баланс — основа денежного учета в нашем мире.
Не так давно в новостях много рассказывалось о так называемом «техническом овердрафте», который якобы образовался на карточных счетах клиентов в одном российском банке.

Технический овердрафт по карточному счету это немного другое.
Сложилось так, что кредитовый карточные продукты (с точки зрения платежной системы) выдают в банках клиентам, у которых нет кредитного лимита. В результате возникают такие уродцы как "Золотые дебетовые карты Visa и MasterCard".
Кредитная карта (по правилам платежной системы) допускает операции в offline без жесткого контроля лимита, а у клиента кредитного лимита в банке нет.
И такие ситуации (на счете 0, а приходит presentment по операции сделанной ранее в offline), скромно называют "технический овердрафт".
Назначая драконовские проценты и т.д. Забавно все это...


Вообще то, для дебетового счета существуют дебетовые карты (типа Visa Electron). Технология которых подразумевает только on-line авторизации (по правилам платежной Системы), что в какой то мере гарантирует "дебетовость". Про извраты с приемом дебетовых карт в offline лучше печально промолчать.


Само наличие у человека кредитной карты (кредитные карточные продукты) в других странах означает: Банк клиенту доверяет и открыл ему кредитную линию. Gold кому попало не дают. Поэтому для аренды машины, бронирования и пр. карту кредитную (кредитовый карточный продукт Платежной Системы).


А у нас даже бомжу (если почитать на сайтах Российских банков) могут Visa Platinum выдать на дебетовый счет.

Так ли часто бывает, что у держателя карты нет денег на счету, а он этой картой за границей расплачивается? Как же ему визу дали?

Думаю, в основной массе там имело место списание платы за обслуживание. И вот тут как раз вопрос, куда они эти средства зачислили. Если в доходы, то это провал.
Так ли часто бывает, что у держателя карты нет денег на счету, а он этой картой за границей расплачивается?

Да сплошь и рядом. Сам так попадал. Не ту карту сунешь второпях..


Как же ему визу дали

C визой это никакой связи не имеет. Причем здесь деньги на счету. Никто это не контролирует. Всегда только справку о доходах на шанген подавал (и дают его на 3-5 лет обычно).


Типичный способ попасть на теховер:


  1. несколько карт.
  2. Перепутал карту. (воспользовался специально выделенной дебетовой с минимальным остатком для paypal и/или покупок в Азии)
  3. не удосужился проконтролировать остаток перед поездкой
  4. и пр.

Оплачиваешь услуги типа платной дороги, кофе в вендинговой машине, проезд в метро и тп. и тд. (суммы мелкие и как правило в offline).
Запросто можно выйти за пределы остатка и "попасть" на так называемый теховер.


Да еще эти транзакции в банк могут приползти через неделю или больше.


Думаю, в основной массе там имело место списание платы за обслуживание.

Типично банки просто карту блокируют если средств на очередную плату за обслуживание не хватает. А поскольку плата берется каждый месяц (и авансом), то потери банков от "на счете 0, клиент забил на платеж и исчез" минимальны. И никто особо по этому не беспокоится.


Что там проскочило в новостях (без всяких подробностей)… Уровень журналистов знаю и поэтому такие "желтые" новости всегда делю на 10 минимум.

за границей можно и в интернете расплатиться, и если за 1-2 дня курс резко скакнул вверх, то легко попасть в теховер — авторизация (блокировка средств) приходит по курсу даты совершения операции, а списание фактически происходит через 2-3 дня по курсу того дня, когда оно пришло в банк.
день Х — на карте 5600 рублей, курс бакса 56, покупка на 100 баксов прошла успешно
день Х+2 — курс 58 рублей, пришло списание 100 баксов, с карты списалось 5800 баксов и клиент должен банку 200 рублей (тех овер).

Ну и еще миллиард способов попасть в тех овер — типа штрафов за прокат машины, выпитый минибар в отеле и т.д.
Теховер может возникнуть и из-за того, что деньги на карте фиксируются в двух местах — в процессинговом центре карты и в банке на счете. В процессинговом центре суммы обновляются быстрее, чем в банке, хотя банк и обменивается информацией с процессингом, но реально операции по счету совершенные с помощью карты фиксируются позже, после подтверждения с терминала или банкомата. Это и порождает различные ситуации, например пополняешь карту в банкомате, через некоторое время рассчитываешься картой, в банк сперва пришла информация, о том что деньги потрачены, образуется теховер, потом приходит информация о пополнении карты, теховер закрывается. Еще можно снять деньги в кассе а потом если успеть потратить деньги в магазине, пока системы не обменялись информацией, также попасть в технический овердрафт, только гасить его придется самому.
это вообще из эпохи 20 летней давности кейс похоже(пока слип бумажный по почте идет), или просто полет фантазии.
Если в процессинге еще нет информации о зачислении и средств для авторизации не хватает, то он просто отобьет ее, а не загонит клиента в овер.
просто полет фантазии.

Нравятся мне такие без безапелляционные заявления…
Вы вообще в этой обрасти работали когда ни будь?
Я так уже больше 15 лет платежными картами занимаюсь.
Слипы блин…
У меня вот на моей личной EMV карте лимит offline есть. Хотя она к дебетовому счету привязана. Сам лично в служебном ПО ее ковырял и профиль анализировал. Чисто из любопытства.
Зачем банк такие карты заказывал — ХБЗ.

А разве оффлайн лимит небольшой в некоторых случаях не штатная функция?
Просто вспоминаются покупки через Samsung Pay с использованием дебетовой карты, место такое что связи там нет, оплата проходит успешно, ИБ платеж не видел несколько дней хотя обычно сразу видит. Суммы в этих случаях всегда меньше 1000 рублей были.

А разве оффлайн лимит небольшой в некоторых случаях не штатная функция?

Штатная для кредитных карт, привязанных к банковскому счету с кредитной линией.
В иных случаях (дебетовый счет) запросто может возникнуть ситуация "технический овердрафт".
Есть Кредитный карточный продукт Платежной Системы. Он четко описан правилами конкретной платежной системы. Кредитные карточные продукты технологически допускают транзакций без online обращения к эмитенту (к процессингу эмитента). Это означает, что если эквайрер выполнил все требования ПС по авторизации (выполнил без обращения к хосту эмитента), то это не его дело есть деньги на счету клиента или нет.
Это проблема банка/организации эмитента карт.


По простому… (исторически сложилось… слипы..), если карта с "выпуклыми цифрами" — то это кредитный карточный продует. Если с "давленными", то дебетовый.


Кстати, никто не мешает эквайреру обслуживать и, например, Maestro в offline (видел и такое извращение), особенно если эмитента = эквайрер.
Но в общем случае по дебетовым картам технический овердрафт не получить.


Хотя я видел и Maestro и Electron (логотип) эмбоссированных как будто это Кредитный карточный продукт. Уж не знаю, как они апрув получили на эмиссию такого дизайна.


В общем, не надо путать Карточный продукт Платежной Системой с тем, как какой счет в банке. "Снаружи" никто и не знает дебетовый у вас счет или с кредитной линией.

Оффлайн лимит, CVM-лист и тип продукта вообще ни как не связаны.
Про эмбоссинг/индент тоже чушь :) разница только в том, можно ли ее катать в импринтере или нет.
Про эмбоссинг/индент тоже чушь :) разница только в том, можно ли ее катать в импринтере или нет.

Да ну? А Вы не в курсе про требования к продуктовому дизайну карт, рекомендованным профилям персонализации (куда CVM входит) и согласование эмиссии с ПС (Visa|MC|CUP|Мир)?
Что не разу не сталкивалиcь? Ни разу такие документы от ПС не видели и процедуру согласования эмиссии не проходили?


И правила для эквайреров по настройке терминалов/обслуживания разных карточных продуктов (по AID, по логотипам) ни разу не читали?


Что то меня сомнения берут насчет вашего опыта в 12 лет...

да все это согласование это заполнение трех экселек-опросников :)
хорошо если бизнес вообще понимает что туда пишется и сколько денег за это платить :) а так туда можно вообще все что угодно запихать и мпс радостно согласует — любой каприз за ваши деньги.

Ну да. Только в опроснике обычно ссылка идет на типовой профиль.
А если что то не типовое, то переписка с ПС (а можно так?).
Возможно эта переписка перестраховка. Но кому хочется брать на себя ответственность?
Бизнесу все подешевле… бы…
И появляются уродцы типа Maestro|Electron instant карта с эмбоссированием и дизайном косящим под visa infinite. "А чо круто же смотрится".

чего то я начал через чур активно возмущаться; )
Задевают фразы "чушь".
Извините.
Предлагаю прекратить и общаться покорректнее.


А тип продукта и профили и дизайн все же связаны.
Получить от Плат. Системы добро на "нестандартный" дизайн/профиль — практически не реально

делали в мпс на букву М голдовую карту с индентом по бину зарегистрированному как кредитный. без смс и регистрации.

Наверное у вас крупный банк, что MC так пошел навстречу.
Голд с идент это что очень странная комбинация. ATM все равно. Хоть Gold, хоть Cirus.


А смысл Голда, на мой взгляд, помахать им в аэропорту что бы в VIP lounge попасть. Но Gold с идентом…
Народ (сотрудники сервиса) то привык к конкретному внешнему виду карт. Им же "буквари с картинками" показывают..


Впрочем, я и не говорил, что не бывает. Приводя пример Electron c эмбоссированием.
Но как объяснить обычному человеку как на беглый взгляд отличить разные карточные продукты? Логотип и тип печати.
Тип печати нагляднее и понятнее и в 99% верен.
Разве не так?

в наш чудный век от этого всего лучше вообще абстрагироваться :)
договор заключенный с банком — вот что важно, а карта это просто разрисованный кусок пвх :)

Карта это то же весчь бывает.
Валяются образы пластика (рекламные):


  1. Светящиеся (Не помню чьи)
  2. Прозрачные с голограммой бабочки (шлямбурже еще)
  3. С запахом. Уже не пахнут… (Gеmplus)
  4. Бесконтактные в разном форм-факторе (только в зубную щетку не догадались засунуть)

Даже обидно, что рано или поздно будет вытеснены другими средствами платежа.


Даже самые первые Java карты Gemplus (еще с усеченном 3DES) валяются. И VisualBasic OS карта и диск Microsoft SDK для нее (2000год).
RIP карте MS..

И VisualBasic OS карта

Что это за зверь?

Была неудачная попытка Microsoft выйти на рынок смарт карт. В пику Java картам. VB в качестве языка написания апплетов… Вызвала неподдельный (скептический) интерес на конференции в 2000 году.
Нашел у себя диск тех времен и из ностальгических воспоминаний памятнику ему сделал: )


Хм, это они смело. Спасибо.
это кейс когда авторизация в онлайне вообще в процессинг не летит, в банк через системы файлового обмена с платежными системами только финансовое требование на возмещение прилетает за авторизованную в оффлайне сумму
здравствуйте коллега :) 12 лет в отрасли :)
как вы знаете по своему опыту — оффлайн операция прилетит в клиринге, а это лаг минимум в 1-2 дня. Если в банке пополнение карты через АТМ происходит через АБС сперва а синхронизация АБС-Процессинг имеет лаг больше 1-2 дней, то мое утверждение про кейс 20-летней давности и/или полет фантазии справедливо.

Однако если вы 15 лет печатаете пин-конверты, то скорее всего этого не знаете, хотя и работаете в этой отрасли: Р
cинхронизация АБС-Процессинг имеет лаг больше 1-2 дней

Это кейз 20 летной давности. Голубиной почтой…
Не встречал банк, который бы не требовал синхронизации минимум раз в день.
А большинство вообще через on-line интерфейс с процессингом предпочитают.
Впрочем, без конкретных примеров АБС+процессинг — это разговор ни о чем.


Если в банке пополнение карты через АТМ происходит

Если уж корректно говорить, то функция пополнения счета наличными в АТМ с идентификацией (аутентификацией) по карте, это не стандартная операция и вариантов реализации существует много. И процессинг (если это его сетка) может поддерживать это. И в АБС может напрямую (через on-line API) зачисления идти.


Однако если вы 15 лет печатаете пин-конверты, то скорее всего этого не знаете, хотя и работаете в этой отрасли: Р

Начнем бодать и меряться? Так смысла нет. Если вы еще в этой области работаете, то прекрасно понимаете, что конкретными примерами я не могу оперировать (иначе по шапке получу).


А на счет PIN конвертов… А что, вам известны какие то другие способы кроме:


  1. Выдачи карты с конвертом.
  2. Выдачи карты с собственоручным вводм клиентом нового PIN в служебном терминале.
  3. Выдачи PIN через AVRS (телефон)
в процессинге информация то есть, а вот в банке — еще пока нет.
разрешение на снятие средст с карты (авторизацию) как раз процессинг и дает.
Нет средств — в авторизации откажут с 51 кодом.
На карте есть деньги
ПЦ об этом знает (как следствие разрешение на авторизацию ПЦ дает)
Банк — не знает
В Банке открывается теховер, не в процессинге, а в банке.
так не бывает :) просто в банке ни чего не открывается и не происходит.
счета ведутся в АБС, а не где-то в вакууме в банке.
Теховер это задолжность по счету неразрешенной задолженности. Задолжность по счету образуется в результате банковских проводок. Проводки по банковским картам попадают в АБС в результате появления требований с клиентского счета от платежных систем или собственных устройств. Эти требования в первую очередь появляются в процессинге (если свое устройство то от устройства при проведении операции, если другого банка то в результате файлового обмена с платежной системой — виза/мс/мир).

Если о пополнении карты процессинг знает и дает разрешение на авторизацию, то и проводки по пополнению он тоже выгрузит — одновременно с требованием по операции списания.

Хотя конечно нарукожопить можно все что угодно. Например требования выгружать в течении всего дня, а пополнения выгружать только при закрытии дня(или вообще после инкассации кеш-ин банкомата :) ).
В актуальных системах конкретно описанные вами сценарии не пройдут. Процессинг знает, на какую сумму он одобрил вам транзакций, и не даст уйти в минус (отклонит транзакцию). Но, я вполне верю, что такие ситуации возможны в какой-нибудь условной Оклахоме, где используются системы тридцатилетней давности, импринтеры, слипы, вот это всё.

Реальный сценарий, возникающий из-за разделения авторизации и финансового представления: вы купили товар за 100$ = 5867р, и остался у вас на счету 1р. А представление пришло через неделю, и обработалось по курсу 59.00р/$, вот вам и минус на счету.
В актуальных системах конкретно описанные вами сценарии не пройдут.

Проезд на транспорте по банковской бесконтактной карте — практически никто не использует авторизацию в on-line. (Авторизации проводятся позже пакетно. С утерянной выгодой мирятся).
Вендинговые машины в Европе… везде offline.
Оплата платных дорог — везде offline.
Курьез — оплата туалета на ЖД вокзале в Норвегии (точно не помню какой) — картой в off-line.


Ага… системы 30 летней давности… слипы…
Off-line лимит на VSDC|UP|MChip карте — это отнюдь не "off-line остаток". А просто типично сбрасываемый в начальное значение лимит при любой успешной online авторизации.
Сделан под кредитные банковские продукты. А не под дебетовые.


То что Вы никогда не сталкивались с off-line платежами по чиповым картам совсем не означает, что это их не бывает.

Перечитайте пожалуйста оригинальный сценарий, на который отвечали — там кейс с тем что пополнение дольше обрабатывается чем списание.

Ну да. Там сумбурно написано. Сходу понял не так.
Перечитал еще раз.


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

Такого конечно не бывает. Теховер по on-line операциям (снятия нала в АТМ) естественно не возможен.

Как же ему визу дали?
Занимаешь денег у друзей на день. Кладёшь их на счёт в банке. Берёшь справку с этого счёта. Снимаешь деньги. Раздаёшь занятое. Профит.
А потом уходишь в минус и гуляешь без денег по Парижу в поисках, чего бы пожрать…

Эх, как же скучно я живу.
Да и виза делается раз и несколько раньше поездки (я начал делать за три месяца до неё). А после справки три списания ипотеки (я забыл про это и у меня на следующий после справки списали), платежи по кредитке и ещё много чего. Наличная валюта для поездки у меня в руках была, а не на счёте. Так что эта справка вообще ни о чём не говорит.
С Visa Electron тоже может не прокатить — «Зачисление проводится раз в сутки, поэтому денег, которые вы только что положили на карту, на счёте нет, они есть только на карте».
Хорошо написано. Может когда-нибудь и пригодится. Спасибо!
Еще один проблемный вопрос из банковской практики обработки денежных сумм. При суммировании вычисленных денежных величин делать округление к целым копейкам до суммирования или после? Есть вполне реальные условия, при стечении которых это приведет к реально противоположным результатам.
На это в кредитной организации должны быть нормативные документы. Разработчик тут решать не может. В отсутствие четких требований я бы сформулировал следующие правила:

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

Абсолютно согласен
НЛО прилетело и опубликовало эту надпись здесь
О, спасибо. Вспомнил 6-е правило: не экономьте на вычислениях с суммами.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Вообще да, mutable BigDecimal был бы очень полезен. Так как каждое значение в новый объект запихивать может быть очень накладно. Его можно для своих нужд быстренько сделать или готовый скачать.

Я, пожалуй, погорячился насчет экономии, но ради производительности double применять все равно не стоит.
не делать округлений при выполенении операций, делать их только в момент получения из внешних источников и хранить у себя так как есть
К сожалению, не панацея. Например, при начислении процентов по вкладам или кредитам.
Проценты начисляются за полный период с его начала до даты начисления, затем округляются до копеек, затем уменьшаются на количество начисленных на конец прошлого периода. Таким образом достигается минимум ошибки округления, и она не накапливается со временем. Все это — в разрезе каждого договора. Сложностей и чудес нет.
Есть вот такая сложность. Допустим 10000 рублей лежит на депозите 100 дней. По ним за 100 дней получается некоторая сумма процентов. И берем второй случай, когда 10000 положили на депозит, через 50 дней сняли, открыли другой депозит, положили на него те же 10000 рублей еще на 50 дней. В итоге с высокой вероятностью получится, что начисленная сумма процентов за 100 дней на одинаковую сумму будет различаться на копейки. Казалось бы — мелочь. Но в реальности может оказаться головной болью для кредитной организации.
На практике никто не сравнивает суммы процентов за 100 дней по одному договору и за 50+50 по двум другим, у банка нет обязанности поддерживать их одинаковыми до копейки, а следовательно, нет и проблемы.

Более того, сумма процентов по первым 50 дням должна до копейки быть равна сумме по вторым 50. Таким образом, возможность выравнивания ошибки округления на горизонте 100 дней отсутствует также как и необходимость.
Хорошо, если и в самом деле так.
Просто когда я работал разработчиком ПО в банке, то на такой фразе:
> у банка нет обязанности поддерживать их одинаковыми до копейки
у нас люди не успокаивались ))

Фактически, в этом банке расчет начисляемых процентов по вкладам и кредитам идет на остаток на счете за каждый отдельный день, затем округление до копеек и суммирование процентов по дням за заданный период. Тогда проблема с вкладами на 100 и на 50 дней не возникает. Правда, и у этого метода есть недостаток. Так, если на двух депозитах на 1 год под 10% лежат суммы 10000р. и 10000р. 10к., то сумма процентов за год получится по ним одинаковая. Хотя, должна отличаться на 1 копейку.

ИМХО, все это должно быть прописано в правильно составленной учетной политике банка, на которую должен ссылаться любой договор между банком и клиентом. Тогда у банка будет чуть больше прикрытия от желающих доставить ему юридические проблемы по надуманным поводам.
в этом банке расчет начисляемых процентов по вкладам и кредитам идет на остаток на счете за каждый отдельный день, затем округление до копеек и суммирование

При таком расчете копится ошибка округления, причем немаленькая. А мелкие суммы (ниже 30 или 15 рублей, в зависимости от способа округления) могут вообще неопроцентиться никогда.

При изменении суммы вклада (а также процентной ставки или любых других условий, влияющих на расчет процентов, например, количества дней в году) в рамках действия одного договора вводится понятие «периода постоянства условий начисления», период с начала действия договора по дату начисления разбивается на «периоды постоянства...» по ним проходят начисления процентов с округлением до копеек, затем начисленные проценты складываются.

Вот такие ноу-хау. Иногда настолько хау, что большинство внедренцев АБС ни разу ни ноу (печальный опыт)
НЛО прилетело и опубликовало эту надпись здесь
У меня там внутри класса Sum поле amount.
НЛО прилетело и опубликовало эту надпись здесь

Про неотрицательные суммы — не согласен. В приведенном вами примере вы, фактически, предлагаете заменить одну проверку двумя (нужна была проверка на неотрицательность дебетового счета, предлагается ввести неотрицательность любого счета, автоматический кредит при нехватке средств и запрет кредита на дебетового счете). Но ведь проблема-то и заключалась в том, что программисты даже одну проверку — и ту забыли написать!


Чтобы избежать таких вот ошибок — надо не усложнять модель, а вводить внутреннее API, которое просто не даст совершить некорректную проводку.


А отрицательные суммы на уровне модели очень даже полезны. Вот недавно был пост про двойную бухгалтерию, где было показано, что простая смена знака пассивным счетам позволяет объединить в один все типы проводок.

Это значит, что признак дебета-кредита сохранили внутри числа в знаковом бите. При этом в команде должен быть супергуру, который помнит, что на пассивных счетах отрицательные суммы означают дебет, положительные кредит, а на активных наоборот. Остальные же разработчики воспринимают эти понятия как магию и боятся заглядывать в код, который ее реализует.

Мой подход таков: Каждый переход суммы через 0 — это инцидент, требующий анализа. Когда же мы используем знак, то мы просто откладываем расследование этого инцидента со стадии разработки в рантайм.
Почему в рантайм?
Знак числа никоим образом не может быть единственным источником типа счета.
Это даже не вопрос грамотной нормализации и т.п., а просто неизбежный факт — возможность существования нулевых значений у счета делает данный признак бессмысленным.
На самом деле у любого действия с деньгами (будь то проводка или «кварковая проводка» моих оппонентов, не суть) есть информация о том с каким счетом мы работаем. И этот счет «знает» свои правила. В простейшем случае — активные счета не могут быть отрицательными, пассивные — положительными. В реальной жизни таких валидаций может быть много разных. И на суммы транзакции, и на «неснижаемые остатки» и счета у которых всегда ноль, и только ноль и т.п.
Соответственно если у нас стоит ограничения что бывают только активные и пассивные счета, а активно-пассивных нет в принципе (а это достижимо даже в регламентированном учете), то в рантайме как раз переходов через ноль не будет физически, ибо валидатор просто не пропустит такую операцию. Так что разбираться придется еще на этапе разработки или тестирования.
Просто мы с вами используем разный понятийный аппарат. То, что вы называете «отрицательным активным счетом» я называю «кредитовым сальдо по активному счету». Когда вы говорите, что «пассивный счет не может быть положительным», я говорю:
«Образование в конце дня в учете дебетового сальдо по пассивному счету или кредитового по активному счету не допускается».

Поскольку я обхожусь без понятий отрицательной суммы, то могу следить за тем, чтобы сумма была не меньше нуля.
Дело не в терминологии а в контексте.
Я говорю о том, что отрицательные суммы В БАЗЕ очень даже хорошо смотрятся если счет пассивный. Это упрощает целый ряд вычислений. Например это упрощает проводку. Нам не нужно вот это вот «если счет активный, то дебет это плюс....». Дебет у нас всегда плюс, кредит всегда минус. Мы упрощаем контроль целостности — просто сумма баланса всегда равна нулю, иначе «ужас-ужас» и падаем с ошибкой, и т.п. А так, то я сторонник того чтобы счета были либо строго активными либо строго пассивными, а если сильно нужно активно-пассивный, то делать два счета и отдельно сверху логику их взаимного зачета.
если счет активный, то дебет это плюс

Когда все суммы положительные, то не нужен ни плюс, ни минус. Поэтому я считаю, что моя модель проще. Мне не нужно ничего запоминать.

У вас дебет — это плюс? Т. е. вы от кассы считаете? Вам так удобно?

Если бы я использовал плюс-минус, то у меня дебет был бы минус.
Давайте я не буду повторять здесь статью и 250 комментариев к ней а просто дам ссылку.
Если активные счета положительные а пассивные отрицательные, то дебет всегда плюс, а кредит всегда минус.
Надо разделить понятия — тип счета и тип остатка по счету.
Счета могут быть активные, пассивные и активно-пассивные по типу. И у счета любого типа может быть активный (дебетовый) или пассивный (кредитовый) остаток (+ или -). Просто когда у счета «неправильный по знаку (неположенный ему по типу) остаток» — то и называют сие счет с «красным сальдо».
Валидаторы проверки остатка по типу остатка и контролируют может ли быть пассивный (кредитовый) остаток по счету например по типу — активному (при наличии признака например на этом счете «разрешенного красного сальдо»).

Говорить что «нельзя иметь активно-пассивные счета» — достаточно недальновидно. Есть системы бухгалтерского учета где они активно используются и непонятно почему ради этого надо держать два и т.п. счета. Если пользователю нужен такой счет — ну пусть объявит счет с таким типом и валидаторы будут пропускать любые проводки по этому счету без контроля знака остатка. С контролем проводок тоже нет однозначности. Во многих системах бухучета есть возможности за контролем транзакции — когда например один счет по дебету и 2 счета по кредиту (например по дебету проводка на 1000, по кредитам одна на 300, вторая на 700). Просто идет тогда контроль что общая сумма дебетов и кредитов в одной транзакции равна.
Регламентированный учет есть регламентированный учет, и не важно кто его регламентирует — государство или клиент. Но если делать учет самому, то мое мнение: активно-пассивных лучше избегать.
Ну и проводки с количеством счетов отличным от двух я тоже не люблю.
Но все это обсуждено и пережевано в соответствующей статье, и комментариях к ней. В итоге каждая сторона осталась при своем мнении, так что давайте не будем это выносить еще и сюда. Если есть желание подискутировать то велком в комменты к моей статье или в личку.
Счет 70 (взаиморасчеты с сотрудниками) в день аванса — самый частый пример пассивного счета с отрицательным остатком на хозрасчетном плане счетов.
В банках вроде с этим борятся.

Дополню, что в конвертации валют надо обязательно зафиксировать 0.01 единицу валюты для округления и привязать её к конкретной стороне, чтобы А -> Б при округлении в стиле 3.455 — шло как 3.46, а наоборот 3.45. Т.е. принудительно! Обычно это делается в сторону основной валюты. Избежите кучи проблем.
Также обратите особое внимание на обработку нулевых сумм (0.00) и сумм очень маленьких (0.01) — дабы избежать начисления процентов на суммы по типу: 0.00 и 0.0001 (некотоыре системы, например топливные, работают с 3 цифрами после запятой, а банки вообще могут работать и с 5-тыми цифрами после запятой).

Вы совершенно правы. Даем 1000₽, получаем по курсу 58.79₽/$ 17.0096$.

Если округлить в большую сторону, то получится 17.01$ на руки и потеря (хотя, кому-то и выгода) 2 копеек. А на самом деле нужно округлить до 17.00$ и выдать 57 неразменянных копеек на сдачу.
НЛО прилетело и опубликовало эту надпись здесь
Каждый переход суммы через ноль — это инцидент требующий анализа. В данном случае разработчики такой анализ не провели. Они просто показали вам знак минус, а вы сами разберетесь, что это значит, потому что вы помните, что вы с запасом платили.

Теперь представьте, что кто-то вам закинул без вашего ведома 380 евро. Вы помните, что у вас долг около 200, заходите и видите -190.33. Вы быстренько берете и закидываете заготовленные 200. Потом, когда сумма станет -390.33, вы поймете, что это значит, до следующего раза, который произойдет может быть через год.

А надо было вместо знака минус написать словами Переплата или Собственные средства.

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

"Переплата" вместо минуса — это не анализ перехода через ноль, а простое условие при отображении.


Переход через ноль — это когда был плюс, стал минус (или наоборот). А тут надо показывать переплату каждый раз когда знак отрицательный, независимо от того когда он таким стал.

Смотря как реализовать. Если двумя виртуальными счетами с наблюдателем, как у меня, то перехода не будет, и это будет не просто условие отображения.
Соглашусь с mayorovp. Автор анализирует и советует на тему внутреннего представления.
А минус на счете — это представление пользователю.
Мне наоборот при анализе счета удобнее не 2 колонки, а приход "+", трата "-". Так интуитивно понятнее для непосвященного пользователя. Чем пресловутые дебет/кредит.
Хотя идея Nordea (со слов sergey-b — я с этим банком не знаком) мне не нравится.
Логичнее переплату показывать как "+", а вот кредит как "-" — ибо кредит на кредитных картах, это Вы должны банку.
НЛО прилетело и опубликовало эту надпись здесь
все люди вроде вас не способны постичь смысл отрицательного БАЛАНСА

Активно-пассивные счета это зло. Ну не прям совсем конечно, но обычно это или упрощение плана счетов и корреспонденции (ИМХО чрезмерное упрощение) или явно ошибочная корреспонденция.
У топикстартера безусловно каша в терминологии и т.п., но если перевести его позицию на человеческий язык, то он выступает против активно-пассивных счетов, и предлагает отдельно отображать долг банка нам, а отдельно наш долг банку. Ну и соответственно если это предусмотрено договором (обычно подразумевается или явно озвучено) то автоматически делать их взаимозачет.
Тогда «отрицательный баланс» как вы говорите может быть только в случае сторно и прочих специфических ситуаций.
Ситуация номер три — это очень даже вкусный вариант для бизнеса, так как получает деньги заранее, пусть даже и запутывая клиента)
За клиента в этом плане можно не беспокоиться, а вот банк в таком случае имеет на своем счету доходов виртуальные средства. Если он их потратит на хозяйственную деятельность, то в итоге у него в балансе образуется дыра. А все потому, что на счет доходов записали средства, которых у клиента не было.


Это непонимание бухгалтерского учета в принципе. Никакого отношения начисленные доходы не имеют к хоз.деятельности банка. Все расходы банка производятся не со счета доходов, а со счета расходов. И разница образуемая между ними и есть прибыль или убыток.
Ну и далее — реально вывести можно только деньги, которые находятся на корсчетах банка или в кассе. Вы можете нарисовать хоть 1 млрд доходов, но для образования дыры вам доступны только средства на корсчете или в кассе. Упрощенная модель выглядит так — клиент вносит в кассу банка 1 млн во вклад, банк выдает другому клиенту 1 млн как кредит. Далее % по вкладу вы начисляете как расходы банка, % по кредиту — как доходы банка. Но есть разница между начисленными и реализованными расходами и доходами. Например % вы согласно правил начисляете клиенту по вкладу каждый месяц, а выплата % по договору раз в квартал. Т.е. можно спокойно показывать что у вас расходы нарастают, но реально никаких денег вам для выплат в кассе или на корсчете не нужно. Мало того — вы можете % выплачивать на счет клиента и тогда это просто «бумажное» нарастание обязательств, до тех пор пока клиент не захочет вывести деньги из банка. Вот тогда и выяснится что денег на корсчетах и в кассах может быть 0, а все деньги были выведены за счет выдачи кредитов, которые никто возвращать не собирается, или ценных бумаг, которые ничего не стоят и т.п.

Далее — на что попадет банк или другая организация, которая начислит «лишние доходы». А попадет она на «лишние налоги».
Представляете, если бы я вместо слов «хозяйственная деятельность», расписал бы как вы, что можно сделать со счетом доходов. Это был бы неоправданный оффтоп. А в комментариях эти подробности смотрятся очень даже гармонично.
НЛО прилетело и опубликовало эту надпись здесь

Почему регулярный платеж за обслуживание карты вы называете "передачей денег банку на хранение"?

НЛО прилетело и опубликовало эту надпись здесь

Вы их сделали. 4 с половиной часа назад в комментарии, на который я отвечал. Если я его как-то неправильно понял, то поясните что именно было не так.


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


Вы же начали рассказывать про какие-то "средства клиента которые переданы банку на хранение". Какие именно средства тут имелись в виду?

Мне кажется один из самых интересных траблов при работе с суммовым показателем описан в этом этом комментарии. Кстати, подобное касается не только приведенных НДС, но и аналогично в любой ситуации, когда вам нужно фиксировать валютную сумму итого в национальной валюте регламентного учета. Часто бывает, что общий итог <> итог по колонке сумм. И тут возникает вопрос, как от этого избавиться? Мне кажется, это один из топовых по своей интересности вопросов. В свое время много было дров поломано на эту тему. ;)
… Часто бывает, что общий итог <> итог по колонке сумм… И тут возникает вопрос, как от этого избавиться?

Не стоит технарю решать проблему экономиста/бухгалтера?

Опишите прямой алгоритм расчета (когда сумма по ИТОГО не сходится с ПРОИЗВЕДЕНИЕМ) или обратный (когда сумма по ИТОГО с ПРОИЗВЕДЕНИЕМ сходится, но в последней позиции копеечка подправлена). Подпишите порядок расчета у Главного экономиста(бухгалтера). И не ломаете себе голову.

… налоговая это нормально хавает.
Я не знаю, чем руководствовался автор строчки «налоговая это нормально хавает», но обычный короткий гуглинг, показывает что ФНС идет в арбиртаж из-за погрешности подобного округления. (А56-17988/2011 от 19.01.2012г., А32-13441/2010 от 28.06.2012г., А40-69215/08-107-326 11.03.2009г.)

Более того, вот здесь я нагуглил еще более интересную ситуацию, которая показывает насколько глубоко днище этой проблемы.

но в последней позиции копеечка подправлена

Представьте себе список, состоящий из несколько сот позиций, у которых веса (суммы) каждой позиции приблизительно одинаково небольшие. Если сделать так как вы предлагается, то у вас погрешность округления будет больше чем вес самой позиции. Но вы же сделали все по «подписанному», какая разница, да? :) Я именно про это, а вы все сразу редиректите на экономиста/бухгалтера. Типа, а это не техническая задача, мне это по барабану, это чисто бухгалтерская задача, мне как сказали, я так и накодил. Подписано? Подписано! А ко мне какие проблемы? Хотя отчасти вы, конечно же правы. Если заказчик подписал 2+2=5, то это 5 и никак иначе. Но мне кажется, свои 5 копеек в исходную постановку, если вы видите очевидный косяк, вставить можно. Я рассматриваю для себя такую позицию так: я вижу, что у вас есть некоторые проблемы в постановке, я вам их сообщаю, а делать вам что-то с этим или нет — дело ваше. После этого считаю, что моя совесть, чиста.
Другой пример с положительной судебной практикой для налогоплательщика
Показывает лишь то, что вопрос не технический, а экономический и юридический. Программисту остается только затребовать четкое ТЗ, как считать суммы.

PS: Бухгалтерский учет часто не понять логически. Бывают законы, которые противоречат друг другу, или наоборот, не раскрывают важные моменты, как с округлениями. В таком случае судьба предприятия будет зависеть от позиции бухгалтера, его опыта, умения вести разговор с инспекцией. Поэтому как он скажет...(2+2=5 ;-)
Полностью согласен. При этом задача IT — создать такую модель, в которой все хотелки заказчика будут работать. А сомнительные результаты будут достаточно подробно документированы.

У меня в одной системе было требование 1.45 = 2. Сделал так, как просили, но чтобы округление так работало, надо было зайти в настройки и явно включить эту фичу, которая по умолчанию была отключена.
Округление до большего — частое требование в ситуациях, где на производство нужны материалы в неделимой фасовке. Остаток тупо выбрасывается.
Другой пример с положительной судебной практикой для налогоплательщика

Так и все мои «гугли»-примеры, они тоже с положительным решением арбитража, который встал на сторону клиента, а не фнс. Я привел эти решения только на постфактум, что якобы «налоговая это нормально хавает». Налоговая, как раз таки и не хавает. Хавает пока арбитраж, куда фнс любит ходить по этим спорным моментам.
фнс любит ходить по этим спорным моментам

Профессиональный бухгалтер тоже пойдет в арбитраж, если не согласен с фнс.
А вот программисту желательно иметь основания не появляться там по этим спорным вопросам.
В C# вроде double теперь работает с точными значениями
        static void Main()
        {
            var a = 0.000001;
            var b = 0.000003 / 3;
            Console.WriteLine(a == b); //True
        }
double a = 1111111.100001d;
double b = 333333330.0003d / 300d;
Console.WriteLine(a == b); // False
Теперь это стало совсем забавно работать
            double a = 1111111.100001d;
            double b = 333333330.0003d / 300d;
            Console.WriteLine(a == b); // False
            Console.WriteLine(a - b);
            Console.WriteLine(a);
            Console.WriteLine(b);
А почему бы ему случайно не совпасть после округления? Так будет не всегда.
Справедливости ради следлвало бы отметить зачем тогда вообще нужен тип double когда есть decimal: математические операции производятся над decimal очень намного медленнее (сам точно разницу не замерял, только на глазок, но давеча видел где-то коммент, что примерно в 100 раз). Аппаратная поддержка десятичных типов вроде есть только в процессорах IBM начиная с POWER6.
Ну так в компьютерных играх этот тип незаменим. Он просто для финансовых операций не подходит.
справедливости ради следует добавить, что в тех местах где обычно и проводится основная масса финансовых расчетов (а именно в СУБД) накладные расходы CPU на обработку decimal вместо float настолько ничтожны по сравнению с прочими типа I/O, sorting, hash group и т.д. что при них ни один DBA на свете не чешется.
Точно. А в оракле double вообще только в десятке появился.
Классно было бы уметь отслеживать размерности во время компиляции. Получает, например, функция два параметра со совими размерностями и компилятор проверяет, что все арифметические операции в функции так устроены, что результат имеет осмысленную размерность… Наверное это можно сделать в рамках шаблонов C++. Кто-нибудь пробовал?
Я делал размерности на шарпе, ввел класс размерных величин и размерностей, у размерных величин перекрывал оператор умножения. В коде нужно было писать 0.5*liter, получилось достаточно удобно.
Есть boost::units.

В классе ExchangeRate лучше оперировать понятиями из мира трейдинга: fixedSum & floatingSum. fixedSum (фиксированная сумма) — сумма, которая известна заранее. floatingSum (плавающая сумма) — сумма, которую мы вычисляем.


Поясню, почему clientGives/Takes — не совсем точно с точки зрения доменной области.


Например, клиент продает USD за EUR, в скобках — свойства класса ExchangeRate:


fixedSum = X USD (clientGives — верно, потому что у клиента есть фикс. сумма USD, которые он продает)
floatingSum = Y EUR (clientTakes — тоже верно, мы считаем, сколько EUR клиенту отдать согласно курсу)


То же самое будет, если клиент покупает USD за EUR, но описание ситуации в нашем классе уже не соответствует действительности, ведь на самом клиент отдает EUR, а забирает USD:


fixedSum = X USD (clientGives — это неправильно, потому что это фикс. сумма USD, которую клиент забирает)
floatingSum = Y EUR (clientTakes — и это тоже, потому что это сумма EUR, которую мы считаем, а клиент отдает)


В то же время fixed/floatingSum — это хорошие имена свойств, которые в обоих случаях релевантны ситуации.


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

Так ведь нельзя клиенту по этому же курсу продать USD за EUR. Это будет убыток банку. В этом-то то и фишка моей технологии. Нужно создать другой курс с параметрами clientGives: EUR, clientTakes: USD. И там цифры будут другие. Хотя, если вы готовы по тому же курсу в обратную сторону торговать, можно просто переставить значения местами, но это уже будет не бизнес, а благотворительность.

Курс обмена — односторонний, именно поэтому он является вектором.

Я понимаю, что будут 2 курса. Фишка в том gives/takes не всегда соответствуют реальности. Прочитав код, я буду думать, что этот курс только для случая продажи клиентом Н-ой суммы.

fixedSum = X USD (clientGives — это неправильно, потому что это фикс. сумма USD, которую клиент забирает)
floatingSum = Y EUR (clientTakes — и это тоже, потому что это сумма EUR, которую мы считаем, а клиент отдает)

Зачем вы сначала неправильно расставили имена параметров, а потом говорите что это не соответствует реальности?


Чем вас не устраивает вариант clientGives: EUR, clientTakes: USD?

Смотрите. Я хочу купить 1000 USD, у меня есть деньги в EUR. По вашей логике, я должен создать объект ExchangeRate вот так:


new ExchangeRate(1 EUR, 1.2 USD)


Как мне теперь посчитать, сколько EUR мне нужно для сделки? Метод exchange() выдаст мне сумму в USD, а предоставить ему я должен сумму в EUR!


Получается, чтобы осуществить задуманное, мне надо писать примерно так:


rate = new ExchangeRate(1.2 USD, 1 EUR)
rate.exchange(1000 USD)


При этом я получу то, что нужно, но при создании объекта я передаю доллары в поле clientGives, тогда как на самом деле это clientTakes ((

Нет, не надо писать так как вы написали! В данном случае, надо добавить метод обратного преобразования в класс, при сохранении семантики clientGives/clientTakes.

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

public Sum howManyClientShouldGive(Sum clientWants) {
    if (!clientWants.getCurrency().equals(clientTakes.getCurrency())) {
        throw new IllegalArgumentException();
    }
    BigDecimal amount = clinetWants.getAmount().multiply(clientGives.getAmount())
            .divide(clinetTakes.getAmount(), PRECISION, BigDecimal.ROUND_HALF_UP);
    return new Sum(amount, clientGives.getCurrency());
}

Это субъективно, конечно, но мне больше нравится решение с одним методом exchange().

Это ж творчество. Тут каждый художник пишет в своем неповторимом стиле.
1. Суммы округлять через round() перед каждой операцией — далеко не самый плохой приницп работы с ними.
1.1. Как вариант: хранить суммы в полях int в значениях наименьшой возможной единицы (копейки, центы) и делить их на степень кратности к денежной единице только перед выводом на экран/печать. Кстати, такие реализации встречал неоднократно.

2. Проводки, особенно корректирующие, могут быть и с отрицательным знаком, а не только дебетовые и кредитовые. Так что не надо путать работу с суммами и корректность отображения результатов в пользовательском интерфейсе. В конце концов баланс имеет право быть отрицательным, как и сальдо.

3. Покупка/продажа валюты, акций и т.д. во всем мире идет как bid/ask. Оператор рынка (банк в данном случае) объявялет обменные курсы, по которым покупает и продает. Если юзер не понимает таких элементарных вещей, то лучше ему не заниматься такими операциями. Какой велосипед изобретаете?
1. Применимость очень ограничена. Обычно, как было указано в комментариях выше, единственный правильный порядок округления — тот, который указан заказчиком. Если заказчик не указал, имеет смысл явно спросить, иначе рискуете где-нибудь под новый год получить блокер вида «у вас тут на целую копейку расхождение!!!111».

2. В технических целях вы можете у себя в системе иметь и отрицательные денежные суммы, и отрицательные балансы, да хоть мнимые. Только не показывайте эти потроха бухгалтеру, ему плохо станет. Я когда программистам объясняю про бухучёт, так говорю: бухучёт придумали раньше, чем отрицательные числа, и будущие бухгалтера в школе на эту тему не ходят. Отрицательных чисел в бухучёте не бывает, зато бывает дебет и кредит, а также пассивные и активные счета.

3. Это терминология, характерная для бирж (и, возможно, ещё где-то). В банках говорят buy/sell, при этом подразумевая покупку/продажу иностранной валюты банком за национальную валюту.
1. Мы будем искать и находить много новых вариантов. Последнее слово тут не скажет никто. :) Всего лишь поделился своими наблюдениями за имеющимися реализациями.
У меня есть пачка «обезьянок», которым базовую математику исчисления НДС привить не могу, но они очень сильно любят «красивые цифирьки» и крайне недовольны, когда не могут занести в систему подготовленный в Excel прайс-лист, красивость которого обеспечена использованием до 8 знаков после запятой. :(

2. Все зависит от конкретного законодательного поля данной страны. Корректирующие проводки налогового учета, например.
Я же обратил внимание на то, что автор «запрещает» показывать отрицательные цифры/итоги. Но что делать с отрицательными балансом или сальдо — он не сказал. Когда смотришь оборотно-сальдовую ведомость по клиенту, то проще все понять по знаку, чем оперировать дебитом/кредитом. А тем более когда просматриваешь пачку клиентов одной таблицей. Ну за исключением идеального случая, когда сразу ноль, конечно :)

3. buy/sell, bid/ask — суть одного поля термины, где традиции среды, в которой они используются, намного важнее программистского идеального мира.
Я когда программистам объясняю про бухучёт, так говорю: бухучёт придумали раньше, чем отрицательные числа, и будущие бухгалтера в школе на эту тему не ходят. Отрицательных чисел в бухучёте не бывает, зато бывает дебет и кредит, а также пассивные и активные счета.

Бухгалтера знают отрицательные числа. И для них отрицательные числа синоним красного сторно, а значит вызывает сильный попаболь, и эмоции перевешивают любые ваши объяснения, что вы совсем не это имели ввиду.
Ну вот я эту попоболь и имею ввиду, говоря что они не знают отрицательных чисел :) Можно ещё вспомнить какого-то беднягу, утопленного пифагорейцами за открытие иррациональных чисел.
Вопрос в том эмоции и привычка это, или и вправду «не стоит». Присоединяйтесь к обсуждению сторно чуть ниже.
> Суммы округлять через round() перед каждой операцией — далеко не самый плохой приницп работы с ними.

Вопрос: а зачем? Вычисления в целых числах заведомо не имеют подобной проблемы и обходятся обычно дешевле (сумма целых даже в виде Decimal из C# проще, чем два round плюс суммирование и масштабирование).

> Как вариант: хранить суммы в полях int в значениях наименьшой возможной единицы (копейки, центы) и делить их на степень кратности к денежной единице только перед выводом на экран/печать. Кстати, такие реализации встречал неоднократно.

Именно. Фактически, для целей финансов это самый нормальный режим. (С поправкой: не только печать, но и всякая конверсия; иногда и в какой-нибудь float надо переводить — например, проценты взять.)

> Покупка/продажа валюты, акций и т.д. во всем мире идет как bid/ask. Оператор рынка (банк в данном случае) объявялет обменные курсы, по которым покупает и продает. Если юзер не понимает таких элементарных вещей, то лучше ему не заниматься такими операциями.

Практически любой человек, который ездит за границу, вынужден думать о покупке/продаже валюты. Предлагаете вообще не ездить?
Практически любой человек, который ездит за границу, вынужден думать о покупке/продаже валюты. Предлагаете вообще не ездить?

Мне тоже очень нравится творчество А. Макаревича, но применять буквально к реальной жизни его «Не стоит прогибаться под изменчивый мир» как-то не додумался :) Думаете стоит попробовать?
Если вы их перепутаете, это будет в 10 разу хуже, чем сложить метры с миллиметрами, потому что 1 BYN = 10 000 BYR.

Не в 10, а ровно в 10 тысяч раз хуже :)
Складывать метры с миллиметрами — ошибка в 1000 раз, складывать BYR и BYN — ошибка в 10000 раз, что в 10 раз «хуже», видимо именно это и имел ввиду автор.
Метры с миллиметрами складывать не сложно. Сам так много раз делал. Куда сложнее складывать граммы с миллилитрами. Примерно так же сложно, как BYN с BYR или BYR с RUR.
Куда сложнее складывать граммы с миллилитрами.

Когда ГСМ или лаки-краски закупаются по весу, а реализуются по объему? В первом случае свои корректировки вносит температура товара, во втором точность отмеривания (40 кг бочку отпускают большим количеством порций с точностью до 10 мл). Обе эти задачи без регулярных инвентаризаций по нормативам решения практически не имеют.
Есть еще long double, который можно использовать для промежуточных вычислений.
Красное сторно — под запретом?
Тип «Currency» отменили?
> Не используйте double

Первый пункт я бы формулировал немного иначе, хотя по сути близко.

Идти в бухгалтерских расчётах (речь о них — не о финансовых в целом, там может быть и «высокая» математика) надо от некоторых базовых правил. В первую очередь это то, что деньги не могут появляться ниоткуда и исчезать в никуда, любая сумма и разность должны быть точными. Как бы ни были сформулированы правила для округлений в каком-то случае (НДС, проценты, другое), это особые ситуации. Лучше получить исключение переполнения, если точности не хватает, чем потерять доли (копейки) и потом искать источник несведения баланса.

Основной проблемный случай — деление на несколько частей. Например, деля 100.00 поровну на 3, мы должны получить не 33.33+33.33+33.33, а 33.33+33.33+33.34. Кому будет лишняя копейка — вопрос полиси, но она не должна потеряться неизвестно где. Аналогично, деля даже на 2: 2.33 пополам должно дать 1.16+1.17, а не 1.16+1.16, как получилось бы, если бы округляли оба результата деления по умолчанию. Проценты? Пожалуйста — 6.99 при выделении шестой части должно дать 1.16+5.83 или 1.17+5.82, но не 1.16+5.82.

Далее. Чем плоха плавучка? И двоичная, и десятичная тут плохи. Для двоичной — банальный пример: в IEEE double, 0.1*3 — 0.3 равно 5.55...e-17. Часто достаточно того, что это не 0: чтобы сравнить с нулём, надо вначале округлить до 0.01 (или какая там выбрана точность). Вообще, все расчёты, получается, надо вести так, что вместо a+b мы делаем round(round(a,0.01), round(b,0.01), 0.01) (вторым аргументом я показал единицу точности). Но так как round(a,b) обычно выполняется как b*round_int(a/b), то резонный вопрос — нафига вообще держать числа в дробном виде, если на каждую операцию они переводятся в целые, а потом обратно?

Есть десятичная плавучка — даже в IEEE754 (версии 2008 года), IBM её умеет аппаратно, многие остальные — программно (например, GCC даёт для C). Но и с ней проблемы. Decimal32 из IEEE это 7 точных десятичных цифр. Сложив 9999999 и 4, мы получим 10000000, а не 10000003, точности для последней цифры не хватит. И потеря будет молчаливой, за исключением (обычно замаскированного) inexact exception. С такими данными можно работать, но только при условии, что inexact exception всегда включен или регулярно проверяется, кроме отдельных операций, где он вреден (вдруг кому-то таки нужен квадратный корень из суммы). Более того, после операций за пределами обычного сложения/вычитания надо округлять в нужные пределы. Если 19.5% от 123.45, пусть мы даже подсчитаем проценты в float (любом), результат должен быть выдан уже как 23.46, а не 23.4555, и только этот результат имеет право быть назван суммой в выбранной валюте.

Аналогично проблемен и Decimal из C#, и даже больше: если каким-то образом вылезли за его 28 цифр, то там даже inexact exception не будет: он не умеет жаловаться на такое. Поэтому вернуться к сумме после всех вычислений надо через Round(), не доверяя любым предыдущим результатам. Но там сложно найти случай переполнения этих 28 цифр :) и это спасает на практике. Но, боюсь, не все сертификаторы положительно оценят подобный «авось» даже тогда, когда суммы в септиллионы букозоидов не бывают в принципе: контроля переполнения и потери точности нет => не годится.

Поэтому основной вариант, о котором надо думать — это масштабированные целые. Где конкретно кому — целые копейки, 1/100 цента, как-то ещё — это уже по местным требованиям.

Далее, это было только про сложение/вычитание чисел одной размерности. Но есть и другие случаи. А именно:
* одна валюта, но разные размерности (где-то копейки, где-то сотые доли копейки)
* разные валюты и деноминации (автор поста достаточно изложил, не повторяю)
* иные операции, чем сложение и вычитание однородных денег

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

Операции умножения на целое ещё могут проходить в том же логическом «поле» сумм, деление — уже нет (см. примеры выше), аналогично взятие процентов. Более сложная арифметика и алгебра изначально должны выполняться в пределах подходов с плавающей точкой, хотя, возможно, с расширенной точностью (single, он же float в C, очень часто недостаточен; double уже обычно годится). Возврат в финансы — только через приведение к соответствующему типу с указанием выходной точности и контролем особых входных данных типа INF или NaN.

К остальному есть немного комментариев.

> Сумма не может быть отрицательной

Даже пытался возразить по мелочам, но не смог :) на самом деле часто допускают временный уход какого-то счёта в минус; проблема возникает, когда этот минус не закрыт на какой-то момент (как конец рабочего дня). Не могу сказать, что это хорошо, но это явно не всегда плохо.

Показ переплаты как долга с минусом — да, против не готовых к такому может быть диверсионным. Не зря в некоторых традициях вместо минуса используют другую нотацию — например, [в квадратных скобках]. Хотя сейчас эти скобки, наоборот, собьют технически подготовленных.
Вот противоположный стиль — переплата с плюсом, а долг с минусом — меньше сбивает, насколько я видел; особенно если потом таки выписать «к оплате столько-то» и писать ноль в случае переплаты.

> Поэтому лучше всего использовать нейтральные понятия «сделка», «обмен», «дебет», «кредит», «зачисление», «списание» и т. д.

Это ещё менее понятно, just IMHO. Подозреваю, хоть как-то универсально понятным будут варианты типа «сторона A получает...», «сторона B получает...»
Если вызвать round(0.1d) на выходе будет число, в котором все равно останется мусор после 15-го знака.

В C# я проверял — выскакивает ArithmeticOverflowException. А вот Java проглатывает.
> Если вызвать round(0.1d) на выходе будет число, в котором все равно останется мусор после 15-го знака.

Можно полный пример?

> В C# я проверял — выскакивает ArithmeticOverflowException.

Аналогично. О какой операции речь?
round(0.1) продемонстрировать не получилось. Я думаю, это какой-то крайний случай, когда получается точное число. Зато на 0.33 все предсказуемо

C#
double d = Math.Round(0.33d, 2);
d *= 10d;
d -= 3d;
d *= 10d;
d -= 3d;
Console.WriteLine(d); // 2.66453525910038E-15


Исключение при переполнении целой части не выбрасывается, вы правы. Я спутал с decimal.
Да, ошибка тут накапливается, причём в C# ещё и как-то очень странно работает вывод значения. Если сделать так:

double fd1 = 0.33d;
double fd2 = Math.Round(0.33d, 2);
Console.WriteLine("{0:F30} {1:E30}", fd1, fd1);
Console.WriteLine("{0:F30} {1:E30}", fd2, fd2);
fd2 *= 10d;
Console.WriteLine("{0:F30} {1:E30}", fd2, fd2);
fd2 -= 3d;
Console.WriteLine("{0:F30} {1:E30}", fd2, fd2);
fd2 *= 10d;
Console.WriteLine("{0:F30} {1:E30}", fd2, fd2);
fd2 -= 3d;
Console.WriteLine("{0:F30} {1:E30}", fd2, fd2);


то видно, что Math.Round ничего не изменил, а вот дальше возникает отклонение (но рантайм врёт в печати — проверено на Mono 4.6.2 и .NET Core 1.0.0-preview2-003118, результаты идентичны):

0.330000000000000000000000000000 3.300000000000000200000000000000E-001
0.330000000000000000000000000000 3.300000000000000200000000000000E-001
3.300000000000000000000000000000 3.300000000000000300000000000000E+000
0.300000000000000000000000000000 3.000000000000002700000000000000E-001
3.000000000000000000000000000000 3.000000000000002700000000000000E+000
0.000000000000002664535259100380 2.664535259100375700000000000000E-015


Для сравнения то же на Python3 (идеально честный вывод: он для любой заданной точности подбирает десятичную строку такую, что обратно превращается в такую же двоичную) —
0.330000000000000015543122344752 3.300000000000000155431223447522e-01
3.300000000000000266453525910038 3.300000000000000266453525910038e+00
0.300000000000000266453525910038 3.000000000000002664535259100376e-01
3.000000000000002664535259100376 3.000000000000002664535259100376e+00
0.000000000000002664535259100376 2.664535259100375697016716003418e-15


он не зарезает «хвосты» там, где начинается дребезг, и видно, как именно он был заложен изначально.

> round(0.1) продемонстрировать не получилось. Я думаю, это какой-то крайний случай, когда получается точное число.

Да, и таких достаточно много. А вот на знаменитом из всяких stackoverflow примере уже лезет дребезг: тот же Python3:

>>> d = 0.3
>>> d*10-3
0.0
>>> d = 0.1*3
>>> d*10-3
4.440892098500626e-16


> Исключение при переполнении целой части не выбрасывается, вы правы. Я спутал с decimal.

Ну да, переполнений нет, но в варианте типа

decimal d2 = 1.0e28m;
decimal d3 = 1.0e-28m;
Console.WriteLine("{0:F40}", d2 + d3);


мы получаем

10000000000000000000000000000.0000000000000000000000000000000000000000

следы второго слагаемого тупо потерялись :( почему я и говорю — inexact не ловится.
Понятно. Вы имели в виду 28 знак справа от точки, а я слева.

На stackoverflow пишут, что формат {:F} в C# всегда округляет до какой-то захардкоженной позиции. Именно поэтому я в примерах умножал на 10^15, чтобы нужные знаки получить. Я просто не нашел формат, в котором нет неявного округления.
Что касается покупки-продажи. Речь идет чисто о внутренней реализации. На табло можно надписать что угодно, все равно это никто не читает. А внутри, в коде, вместо терминов покупка-продажа должны быть точные нейтральные термины, которые тяжело спутать.

У меня был реальный случай, когда одна половина команды считала, что операция обозначает покупку иностранной валюты у клиента за рубли, а другая половина команды была уверена, что это покупка валюты у компании. Чтобы разобраться подняли все документы. Везде было слово «покупка», но нигде не уточнялось, кто у кого покупает. Причем, в некоторых согласованных всеми участниками документах были разночтения в проводках. Т. е. даже по проводкам однозначно нельзя было смысл восстановить.

Ну и известный инцидент, когда в одном серьезном банке курсы забили наоборот, в результате можно было купить доллары и тут же их продать дороже, тоже связан с неоднозначностью терминологии.
Это вопрос первой фундаментальной проблемы программирования а не финансов :)
Если есть выделенная сторона сделки то можно писать что-то вроде CompanyBuyCurrencyRate а если выделенной стороны нет (биржа), то там курс один, плюс спред или комиссия или еще что-то, но курс у сделки один.
На бирже как раз обычно есть Ask и Bid, а вовсе не «средний» курс и спред или комиссия.
Ask и Bid там есть в статистике. А у каждой конкретной сделки цена вполне определенная.
Не в статистике а в «стакане». </зануда
В статистике оно отображается с точки зрения торговца. Обезличено, сточки зрения биржи цена колеблется от времени, а не от направления ибо нет у сделок направления.
Ну это смотря что понимать под статистикой. Весь стакан на каждый момент времени — тоже статистика, и Ask и Bid в нем есть
Кажется, вы ходите по кругу.
Ага, вспомнил, что я хотел сказать. Я хотел возразить вот на это

Аналогично проблемен и Decimal из C#, и даже больше: если каким-то образом вылезли за его 28 цифр, то там даже inexact exception не будет: он не умеет жаловаться на такое.

Decimal выкидывает Exception.

decimal d0 = 79228162514264337593543950335m;
decimal d1 = d0 + 1; // OverflowException
> Decimal выкидывает Exception.

Я не о переполнении порядка, а о превышении точности представления (и Вы пропустили слово inexact — это не overflow):

decimal d2 = 1.0e28m;
decimal d3 = 0.1m;
decimal d4 = d2 + d3;
Console.WriteLine("{0:F40}", d4);
Console.WriteLine("{0:F40}", d4 - d2);


получаем:

10000000000000000000000000000.0000000000000000000000000000000000000000
0.0000000000000000000000000000000000000000


d3 тупо ушло в никуда.

Эффект сам по себе банальный и общеизвестный, в инструкциях по работе с плавучкой он описывается одним из первых. Речь не о нём в принципе, а о том, что (1) в бухгалтерии он недопустим, и (2) защита в .NET Decimal от него есть только в виде «а вы не допускайте таких больших чисел».
Да уж. Я поражен. Оказывается, если к 79228162514264337593543950335m прибавить 0.1, то слагаемое потеряется. А если к этому же числу прибавить 1, то будет OverflowException. Плавающая точка — зло.
Ну почему же зло? Вполне добро, просто не для бухгалтерии. :)
Для матфизики, наоборот, фиксированная десятичная точка будет бессмысленной.

Вот то, что в C, C++, C# плавучка обязательна, а фиксированная точка — нет, уже показывает ориентацию языков. А в результате имеем сохранение старого кода на всяких коболах ;(
Конечно, она в определенных областях незаменима. Но вот тип decimal в матфизике не нужен. Он предназначен именно для точного представления чисел, которые ведутся в десятичной системе счисления. Зачем в C# его плавающим сделали, для меня загадка. И вот этот эффект, когда в сходных случаях наблюдается разное поведение, меня удивляет. В double такого эффекта нет. Там, что 0.1 прибавить, что 100 — если точности не хватило, то число просто увеличено не будет.
> В double такого эффекта нет. Там, что 0.1 прибавить, что 100 — если точности не хватило, то число просто увеличено не будет.

Хм, не согласен. Поведение decimal в этом смысле ничем не отличается от поведения плавучки IEEE (тут и далее — для дефолтного режима округления, roundToNearestTiesToEven).

Возьмём для примера single, чтобы не было слишком больших степеней. Множество представимых в нём чисел это «сетка» с шагом, который меняется в 2 раза при переходе через степень двойки. Например, самое большое представимое число это 2^128-2^104; на одну позицию ниже 2^128-2*2^104, затем 2^128-3*2^104, и так далее по убыванию до 2^127; от 2^127 вниз до 2^126 шаг 2^103; и так далее. Следующая возможная точка в сторону увеличения была бы 2^128, но она уже непредставима. Результат любой операции округляется в сторону ближайшей представимой точки, а если точно посредине — выбирается та, которая более чётная (voting digit — минимальная цифра из представимых в округлённом результате — равна 0).

Так вот — если Вы к этому MAX_SINGLE = 2^128-2^104 ~= 3.40282347E+38 прибавите любое положительное число меньше чем 2^104, округлённая сумма будет снова равна MAX_SINGLE, потому что точная сумма меньше среднего между MAX_SINGLE и 2^128; но если прибавите любое число не меньшее чем 2^104, точная сумма будет уже не меньше этого среднего, поэтому сработает округление к ближайшему чётному, оно будет равно 2^128, не представимо в single, и результатом будет возбуждение исключения переполнения (а если исключение заглушено, будет выдан результат +INF). (В стандарте это правило сказано другими словами, но мне описание через следующую возможную точку кажется более естественным.)

Та же логика, с поправкой на размеры мантиссы и диапазоны порядка Decimal, применяется и к нему. «The finite set of values of type Decimal are of the form m / 10^e, where m is an integer such that -2^96 < m < 2^96, and e is an integer between 0 and 28 inclusive.» Шаг между значениями около Decimal.MAX_VALUE равен 1 (e == 0), и первое непредставимое — следующее за MAX_VALUE — равно MAX_VALUE+1 == 2^96. Прибавка к MAX_VALUE любого положительного меньше 0.5 игнорируется, а >=0.5 — даёт округление к 2^96, которое уже не влазит => генерируется исключение переполнения. Тесты это подтверждают.
Вот теперь мне все понятно. Спасибо.
> Так вот — если Вы к этому MAX_SINGLE = 2^128-2^104 ~= 3.40282347E+38 прибавите любое положительное число меньше чем 2^104

Поправка — 2^103, ибо как раз половина интервала до следующего; то же самое дальше по абзацу.

Может это повтор, но говоря о валютах, не следует забывать, что у некоторых валют, наименьшая единица не есть одна сотая(цент, копейка).
Что касаемо конверсии, то если говорить о банках, то там будут для некоторых операций использованыофициальные курсы ЦБ, а для нкоторых курсы самого банка. Это тянет за собой доп проводки с участием счетов доходов/расходов и тд.

Кстати коллеги, раз уж столько копий сломали тут об отрицательные числа, то вот такой вот вопрос:
В нерегламентированном учете «для блондинок» (я сейчас начал новый экстрим-проект, хочу автоматизировать управленческий учет микро-бизнесов вроде киосков/батискафов, так что ради простоты готов на большие жертвы, ибо иначе люди просто не станут с этим работать), стоит ли использовать сторно не для исправления ошибок а для текущих реальных операций? Хочется иметь минимальный план счетов. До безобразия минимальный, ибо дюжина счетов это для неподготовленного человека уже адЪ. Ну и вот простейший кейс — киоск работает с кофе-машиной взятой в аренду. Была взята машина с расходниками, аренда ноль, за расходники заплатили. Машина на забалансе по залоговой стоимости (рядом я рисую забалансовый же пассивный счет «залоговая стоимость арендного имущества» с той же суммой, так что забаланс у меня живет по правилам баланса, но не суть), расходники приходуем на материалы, точнее «расходники кофе-машины», чтобы не пугать пользователя сложными названиями. Всё ок, работа кипит, материалы потихоньку списыватся согласно смет, и тут прилетает новый план он поставщика машинок. По новому плану не договорились и делаем возврат. Поскольку изменение условий по инициативе поставщика, то он забирает остатки расходников и возвращает за них денег пропорционально расходу.
Эту операцию можно отразить по разному.
Можно сделать «продажу» материалов поставщику. Вроде норм, но тогда у нас появляется «мусор» по оборотам. Ломается статистика себестоимости, рентабельности, да и просто продажа это доход, а на доход считаем проценты продавцу или манагеру… В общем некрасиво. Остальные варианты довольно монструозные. Больше всего мне нравится вариант — сторнировать эту частичную сумму и частичный возврат (те же проводки что при покупке, с теми же номерами счетов только сумма минусом). Плюс такого подхода в том, что обороты по счетам не будут содержать лишних сумм, при этом по всем счетам на любую дату будут правильные сальдо и т.п. Минусы: на некоторых периодах некоторые обороты будут красным, что не очень понятно, хоть и правильно (да, в этот день поступление расходников отрицательное, и оперативные расходы отрицательные, но это ведь так и есть), ну и у меня есть какое-то внутреннее сопротивление говорящее о том, что сторно только для ошибок.
А вы что думаете?
Сторно — это коррекция ошибок. В данном же случае происходит возврат товара поставщику. Если продукт делается с прицелом на вырост, то лучще сразу все делать по-человечески.
Можно сделать «продажу» материалов поставщику.

Ну разве что для поржать :)
Ну на вырост будет ядро. А продукт будет именно что нишевый. Да и на вырост ИМХО главное первичку правильно вести, чтобы потом можно было другую логику сделать по старой первичке. Но вот на счет сторно да. С одной стороны по логике так правильнее. С другой стороны у меня тоже стойкое предубеждение против сторно в живой работе.
С другой стороны у меня тоже стойкое предубеждение против сторно в живой работе.

Но в реальной жизни без этого никак — отмену ошибочной (поспешной) реализации или ошибочного прихода (например: накладную поставщика по учету провели «по бырому», а живой товар до склада так и не дошел) в учетной системе надо именно сторнировать, так как фактически этих операций не было и в обороте с контрагентом они учитываться не должны. В отличие от кофемашины, которую поставщик поставил год назад, на которую в течение года шли затраты на расходники, электроэнергию, воду и т.д. Да еще и возврат может производиться с учетом амортизации.
Не-не-не, я не о машинке в данном случае а о расходниках (кофе, молоко, стаканчики и т.п.). Машинка взятая в аренду а потом возвращенная это понятно, тут лучше не трогать, но она и так на забалансе идет.
А с кофе/молоком выходит как раз близко к «по ошибке провели». Поставщик дал на условиях покупки, т.е. мы у него купили, не взяли под реализацию и т.п., а именно купили, а потом (спустя пару недель, так что да, другой учетный период, в разрабатываемой системе учета учетный период неделя планируется) вдруг решил разорвать договор. Но поскольку расходники в значительной мере привязаны к конкретному поставщику и конкретной машинке, а разрыв по его вине то он забрал остатки расходников (кофе/молоко/шоколад), посчитав пропорционально расходу остаток и вернув деньги. Фактически тут мы купили, а потом через две недели оказалось что «ой, нет, не купили, отдавай мои какашки, забирай свои бумажки». Тут по смыслу явное сторно, т.е. отмена операции, только на другом уровне абстракции, не ошибка учета а «тот день когда я с тобой познакомился был ошибкой».
В большом, «взрослом» учете когда у меня бывали такие или похожие случаи, то я всегда делал тупо продажу поставщику обратно. Что достаточно близко к тому что требует налоговый кодекс (один фиг налоговую накладную писать), но не нужно заморачиваться со всякими рекламациями, письмами, доп.соглашениями и прочим. Я купил, я продал. Продал в ноль «потому что могу». (главное правильно объемы указать, чтобы не сделать в минус, тогда нужно будет доначислять себе НДС). Теоретически по бухучету это надо было бы проводить по другим счетам с другой корреспонденцией, но итог все равно был бы тот-же, и сальдо бы не поменялось.
В учете для блондинок это «сложно-сложно-блин». Вот честно, я очень хорошо помню какая аудитория ходила ко мне когда я в банке аналитиком работал. Да что далеко ходить, тут у нас прямо на хабре куча людей не готова воспринять двойную запись, а тут куча малопонятных счетов с «более правильной» корреспонденцией. Нафиг, нафиг.
А с кофе/молоком выходит как раз близко к «по ошибке провели». Поставщик дал на условиях покупки, т.е. мы у него купили, не взяли под реализацию и т.п., а именно купили, а потом (спустя пару недель, так что да, другой учетный период, в разрабатываемой системе учета учетный период неделя планируется) вдруг решил разорвать договор. Но поскольку расходники в значительной мере привязаны к конкретному поставщику и конкретной машинке, а разрыв по его вине то он забрал остатки расходников (кофе/молоко/шоколад), посчитав пропорционально расходу остаток и вернув деньги. Фактически тут мы купили, а потом через две недели оказалось что «ой, нет, не купили, отдавай мои какашки, забирай свои бумажки». Тут по смыслу явное сторно, т.е. отмена операции, только на другом уровне абстракции, не ошибка учета а «тот день когда я с тобой познакомился был ошибкой».

Тут нет никакой ошибки — банальный [частичный] возврат товара поставщику. И который должен идти в оборот между вами как контрагентами (в РФ так?).
Так же при подбитии управленческих итогов необходимо понимать, что «тут» продажа готового продукта (стаканчик кофе) и есть такая-то прибыль, а «там» возврат расходников поставщику и прибыли тут нет. А на следующем уровне анализа при подготовке среза «там» мы должны понимать с каким поставщиком и когда и по какой причине происходили возвраты — переоценка под акцию поставщика, возврат некондиции, выбрыки поставщика (описанный Вами случай) и т.д. Ведь как иначе понимать с кем стоит работать. а с кем нет?
Так же при наличии налога с продаж такое проведение возврата приведет к необходимости уплаты этого самого налога. Кому это нужно?

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

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

Не теоретически, а практически так надо делать, иначе «шоколадка» фининспектору будет расти в геометрической прогрессии. Сальдо — это просто показатель текущего среза. Показатели оборота очень важны, а Вы хотите это отрезать.

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

То что они что-то не могут воспринять — это пол-беды, страшнее, что они за это минусят направо и налево и в кармо. Агрессивное невежество ведет любое сообщество вниз и только вниз.

PS
Внимательно посмотрите на Gnucash — или придете к пониманию, что изобретаете велосипед, или сможете почерпнуть оттуда много полезного. В случае «велосипеда» может будет иметь смысл сосредоточиться на изготовлении обертки к Gnucash?
Не теоретически, а практически так надо делать, иначе «шоколадка» фининспектору будет расти в геометрической прогрессии.

Да ну, какая шоколадка инспектору там где вообще не было учета и все было красиво? Это у вас профдеформация взрослым учетом.
СПД/ИП лепят что попало и никого ничего не волнует, лишь бы кассовый аппарат/безнал сошелся с отчетом (утрирую, есть наемные работники и т.п. но правильные счета возвратов это точно ерунда). А в цивилизованных странах у микробизнесов вообще все настолько пофиг что даже иногда шокирует.
Помню дядя мой рассказывал лет десять назад — у него гринкарта. Он художник. Оформлен чем-то вроде наших СПД/ИП. Говорит инспектор его ругала что он не записывает себе в тетрадку сколько топлива у него уходит на поездки на натуры, и ворчит что надо записывать «на глаз». Ну и пару недель назад жена моя спрашивала у одной израилитянки которая занимается схожим с ней бизнесом, как у той с налоговой организованы отношения. Ответ той был еще веселее — «а зачем оно? Я еще не доросла до того объема когда надо что-то писать или платить». Не помню лимит который она назвала, но вроде речь шла о суммах в месяц в размере чуть больше их минимальной з/п.
Сальдо — это просто показатель текущего среза. Показатели оборота очень важны, а Вы хотите это отрезать.

Ну так обороты то как раз становятся более правильными.
Налог с продаж (американский вы имеете ввиду?) да, плясать будет плохо, хотя как я помню там он на бизнес бизнесу не распространяется. А остальное можно учесть и так. Проводки то видны. Не знаю, в сомнениях я. Сторно выглядит красиво, но странно. Наверное буду таки отдельные счета делать, и для оборотов какие-то костыли приделывать красивые.
Да ну, какая шоколадка инспектору там где вообще не было учета и все было красиво? Это у вас профдеформация взрослым учетом.
СПД/ИП лепят что попало и никого ничего не волнует, лишь бы кассовый аппарат/безнал сошелся с отчетом (утрирую, есть наемные работники и т.п. но правильные счета возвратов это точно ерунда). А в цивилизованных странах у микробизнесов вообще все настолько пофиг что даже иногда шокирует.

Ну, по моим наблюдениям, у вас в последнее время делают все что угодно, только не облегчают жизнь микробизнесам. Поэтому необходимо быть готовым к закручиванию гаек.
То, что я за 4+ года налоговую ни разу не посетил и ни одного отчета вне общего графика не сдал не говорит ровно ни о чем — трахнут какого-то моего контрагента и пойдут трусить его контрагентов встречными проверками и штрафовать/сажать/штрафовать. А еще с вашими перекладками ответственности за определение добросовестности контрагентов на плечи бизнеса так вообще все весело становится…

Профдеформация у меня если и есть, то от другого, но в целом Вы правы — если информацию можно структурировать и сохранить, то ее обязательно надо структурировать и сохранить.

Ну так обороты то как раз становятся более правильными.

Примитивно != правильно.
Почему правильное чуть-чуть сложнее, написал ранее. Здесь Вы хотите облегчить свою работу, не особенно думая о завтрашнем дне. Имеете право — это Ваш продукт, — но истина дороже :)
Налог с продаж (американский вы имеете ввиду?) да, плясать будет плохо, хотя как я помню там он на бизнес бизнесу не распространяется.

В РФ его отменили в начале нулевых. В РФ его регулярно обсуждают с конца 2016. В РФ его скорее всего введут в 2019.
По каким правилам это будет работать в РФ — мне не известно. Но вряд ли по американским. :)
И опять же: разные операции подчинять одним и тем же правилам физического документооборота намного проще, чем «свалку» разделять по разным правилам на основании каких-то косвенных признаков.
А остальное можно учесть и так. Проводки то видны.

По своему опыту лоточной торговли в первой половине 90-ых знаю, что более-менее понимать чем выгодней торговать можно только при 2..3 поставщиках и не более 5...10 видов товаров. Дальше уже только нормальный учет или чутье (которое чаще подводит, чем нет).
В РФ его отменили в начале нулевых. В РФ его регулярно обсуждают с конца 2016. В РФ его скорее всего введут в 2019.
По каким правилам это будет работать в РФ — мне не известно. Но вряд ли по американским. :)

Вот как раз на РФ-учет мне больше всего плевать ибо РФ-клиенты у меня идут по остаточному принципу — если подошло что-то под них, то хорошо, если нет то и нет, но США интересует а у них он есть.
И опять же: разные операции подчинять одним и тем же правилам физического документооборота намного проще, чем «свалку» разделять по разным правилам на основании каких-то косвенных признаков.

Так признак то не косвенный а очень даже прямой — сторно или не сторно.
Тут вопрос в основном стоит в том, что сторно ассоциируется с ошибками, и соответственно будет плохим стилем использовать его в другом смысле.
По своему опыту лоточной торговли в первой половине 90-ых знаю, что более-менее понимать чем выгодней торговать можно только при 2..3 поставщиках и не более 5...10 видов товаров. Дальше уже только нормальный учет или чутье (которое чаще подводит, чем нет).

Нет, ну вы прям обижаете. АВС-анализ и все такое, плюс минимальное бюджетирование (группировка субсчетов в привязке к разным направлениям чтобы видеть не только консолидированный баланс/обороты но и по направлениям) и т.д, в общем все стандартные виды анализов я конечно подразумеваю. Без этого никуда. Иначе тупо смысла нет. «Учет на регистрах» каждая бабушка торгующая семечками имеет. Даже с учетом того что версия «Киоск» и ядро пойдут под MIT, все равно — без внятного анализа оно никому не нужно.
Сокрытие реализации да, необходимо, но хочется его уменьшить, чтобы пользователь все-таки хоть немного понимал что перед ним баланс а не магические отчеты из непонятно откуда.

А правильно это как?)
Продажу я точно делать не буду. Одно дело делать такое в большом учете для налоговой, другое — в управленческом для микро. А правильно… Вот как бы Вы сделали правильно?
Если не планируете выходить на баланс, вам не нужна двойная запись, и можно вести учёт интересующих сущностей на регистрах. Накопительный регистр неплохо реализован в 1С, если не видели — гляньте. Аналог пишется +- за месяц, дальнейшая учетная жизнь сильно упрощается.
P.S. Логотип поделки обязательно должен быть квадратным.
Если не планируете выходить на баланс, вам не нужна двойная запись

Я не могу не планировать «выходить на баланс». И даже дело не в том что одна из целей проекта это красивая демонстрация для второй части статьи про двойную запись. Учет без двойной записи — ущербен по своей природе. Даже если это шесть счетов всего.
Аналог пишется +- за месяц

Может мы о разных регистрах говорим и там есть какой-то специфический функционал которого я не знаю (в свое время прошел всю пачку их курсов на специалиста или профессионала как оно там у них, но сертификат получать не стал ибо 1с мне не очень интересен). Но насколько я помню там буквально пару фич у накопительного было. Сальдо на дату, дебетовый оборот за период, кредитовый оборот за период и вроде всё… День работы с отладкой, документацией и расписыванием АПИ. Нет, там еще вкусно то что можно их использовать в языке запросов (и соответственно построителю), но это уже не к регистру а к ядру запросов и построителю вопрос.
Собственно накопительный регистр это тот же регистр сведений (т.е. запрос вытягивания последней записи у которой дата меньше или равна), только с предвычислениями сальдо и оборотов. Ну или я что-то забыл. Книжки на полке стоят, скоро буду перечитывать, может и вправду забыл, но какой-то магии сравнимой с тем что может дать двойная запись я там не помню.
Учет без двойной записи — ущербен по своей природе.

Не каждый хозяйственный учёт — бухгалтерский. Зачастую двойная запись порождает больше проблем, чем решает. Потому вполне приемлемо, например, сложную/специфическую аналитику вести в отдельных системах на регистрах (фактически, журналах с плюшками разной степени автоматичности агрегации), а сводные данные, сгруппированные по видам проводок, сливать в бухгалтерскую систему.

Абстракция и API накопительного регистра весьма просты, некоторые сложности начинаются в оптимизациях структуры БД и методов обработки данных на относительно больших базах (10-20 Гб). Не всё красиво ложится на SQL с оптимальным планом. Очень повезёт, если за оптимистично оцененный мною месяц всплывут основные архитектурные ошибки.

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

Пример можно?
Я в общем и в целом не то чтобы прям против
сложную/специфическую аналитику вести в отдельных системах на регистрах (фактически, журналах с плюшками разной степени автоматичности агрегации), а сводные данные, сгруппированные по видам проводок, сливать в бухгалтерскую систему

Но хотелось бы реальных примеров.
Абстракция и API накопительного регистра весьма просты, некоторые сложности начинаются в оптимизациях структуры БД и методов обработки данных на относительно больших базах (10-20 Гб). Не всё красиво ложится на SQL с оптимальным планом. Очень повезёт, если за оптимистично оцененный мною месяц всплывут основные архитектурные ошибки.

Если речь идет о сугубо названном мною АПИ, то я вот просто не представляю что тут вообще делать, не то что «где могут быть узкие места». От объема базы зависит время проведения операций «задним числом», да и то не от размера базы а от того насколько это число будет задним. все остальное вообще О(1) будет (на самом деле конечно O(log(n)) но сиуации когда индексы влияют на производительность это уже проблемы DBA а не разработчика).
Единственное узкое место для «взрослого» объема на накопительном регистре это переполнение оборотов накапливаемых за всю жизнь. Особенно если взять 32-бит знаковое целое и в него засунуть денежку с шестью знаками после запятой и ничего не делать, а просто ждать когда же к нам придет переполнение. Но даже в таком случае может и не придти.
В реальности берем Unsigned BIGINT, ну и как минимум чтение оборотов организуем через вьюв, запись процедурой (если клиент может оказаться 32-бит) и об этой проблеме тоже забываем.
Я могу ошибаться. Я могу сильно ошибаться. Но очень бы хотелось не просто «вы не понимаете», а хоть каких-то намеков в чем тут можно ошибиться…
Если это чисто «управленческий учёт», не связанный с бухгалтерией и отчётностью перед ЦБ/налоговой/whatever, то делайте как вам удобнее.
Если вы хотите по данным этого учёта непосредственно формировать отчёты органам, то открывайте учебник по бухучёту и делайте как там написано.
Если у вас сейчас нет бухгалтерии, а в будущем планируется, то разрабатывайте систему так, чтобы к вашему учёту рядом сбоку можно было прикрутить параллельно работающий правильный бухучёт. Чтобы можно было в одном окне видеть ваш простой и удобный учёт из трёх счетов, а в соседнем — правильный учёт из 100. Вариант: продумать выгрузку данных из вашей системы во взрослую бухгалтерскую систему, и на уровне интеграции заставить бухгалтерскую систему делать правильный учёт. Но не надо надеяться, что это само получится, надо продумывать состав информации, который у вас хранится, с тем чтобы бухгалтерской системе потом было достаточно информации для правильного учёта.
Но не надо надеяться, что это само получится, надо продумывать состав информации, который у вас хранится, с тем чтобы бухгалтерской системе потом было достаточно информации для правильного учёта.

Я придерживаюсь принципа «первичка наше всё». Ибо запоротая структура первички это гарантированный ад потом, так что всю информацию которую можно прихватить автоматом я прихватываю всегда (без ущерба удобству).
А так то я говорю сугубо о управленческом. ЦА — люди на упрощенных системах учета с оборота или фикс.патенты и т.п. которым все по шарабану. У таких обычно управленческий в полном «ж».
Почему не раскрыта тема таких популярных операций с денежными суммами, как «отнимать» и «делить»? :)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории