Комментарии 241
Алгоритм округления может отличаться в разных программах. Это приводит к тому что поставщик, выписывающий документы в Аксапте например, и покупатель, приходующий товар в одноэсе получат определенные расхождения в суммах товара.
Если у вас есть табличка товар | цена | кол-во | сумма | ндс, и последней строкой у вас идёт «итого», то сумма по столбцу ндс чаще всего не будет равна итого*18%. И налоговая это нормально хавает.
А если НДС 0%, то зачем его указывать?
Или вы что-то другое имели ввиду?
То есть считаем НДС один раз для ИТОГО.Не получится просто из-за отсутствия единой ставки.
Налоговый кодекс
С т а т ь я 153. Налоговая база
…
При применении налогоплательщиками при реализации товаров
(работ, услуг) различных налоговых ставок налоговая база
определяется отдельно по каждому виду товаров (работ, услуг),
облагаемых по разным ставкам. При применении одинаковых ставок
налога налоговая база определяется суммарно по всем видам операций,
облагаемых по этой ставке.
…
определяется отдельно по каждому виду товаров (работ, услуг),
облагаемых по разным ставкам»
И внимательно читайте второе выделение.
То есть у вас в чеке есть по одной строке для каждой ставки НДС.
Но если вы хотели меня поймать на том, что я сказал «считаем НДС один раз для ИТОГО», то считайте что вы победили, но мы говорили немного о другом.
А как, извините, применить ваш алгоритм для покупки в которой присутствуют товары со ставками НДС в 18%, 10% и 0%?
Тупое минусение извинению не подлежит.
Речь не об алгоритмах, а о требованиях закона. Товары с разными ставками НДС будьте добры отпускать разными расходными накладными.
И сколько «этим» не рассказывай, что при ставке НДС 20% и цене товара 100 грн с НДС (83,[3] грн без НДС) будут копеечки гулять, — они все равно проводят «округления» в 4...6 знаке и потом удивляются, что остальные такие тупые и так не делают.
Не нужно 1С-ников под одну гребенку чесать, иногда ситуации патовые, и что там одобряет Аксапта с 1С — дело десятое. С сайта госзакупок приходят цены с торгов, которые по закону включающих в себя налоги. И будьте добры, чтобы итоговая цена в реализациях совпадала с контрактной. Клиенты выкручиваются тем, что правят циферки в Экселе. И не волнует никого, что правильным расчётом эту цену с учётом НДС не получить. Так и появляются эти дикие дроби. Конечно, исходная проблема в том, что нет проверки правильного расчёта цен.
Вместо double надо использовать представление числа на основе степеней с основанием 10.
Почему бы просто не использовать long и вести все вычисления в монетах наименьшей ценности? Для рубля — в копейках, для доллара — в центах.
В отличие от валют, у весовых товаров нет не только минимальной неделимой единицы, но и вообще какой-то естественной единицы.
Для весовых товаров будут свои подходы, отличные от подходов к представлению валют.
Ну а вам не все ли равно, в каком виде золото пришло — в твердом, жидком или газообразном? =)
Это уже вопрос представления данных.
В случае внутреннего хранения курсов валют как рациональных дробей вопросы вида "сколько центов дадим за одну копейку" становятся именно что вопросом представления — в каком виде показывать курс пользователю.
А суммы везде отражаются с округлением до минимальной единицы. Цента, копейки или сатоши. Взяли курс, взяли сумму в одной валюте, вычислили сумму в другой с учетом наших правил округления, и получили целое число минимальных единиц. Другое дело что могут быть промежуточные этапы вычислений, и могут образоваться нетривиальные ситуации с округлением. Но дело в том, что само округление в любом случае есть.
курсы даются с точностью до сотых долей цента/копейки
Курс в теории не ограничен количеством знаков после запятой.
ЦБ на сайте выдает до 4 знаков после запятой.
Но при при конвертации миллионов $, с таким курсом можно потерять несколько тысяч рублей.
Поэтому иногда используют курс с 6 — 8 знаками после запятой.
Лог лучше или хуже в данном случае? А то у себя для денег как раз лонг выбрал.
Ниже уже написали, все это должно быть прописано в нормативных документах банка.
Стоимость без НДС = Стоимость с НДС / 118 * 100 (если НДС 18%)
НДС = Стоимость с НДС / 118 * 18 (если НДС 18%)
Ну или попроще «3 шарикоподшипника на сумму 100 руб.» -)
Заодно построить поставщиков на предмет кратности -)
p.s. Next Level: счет-фактура и фискальный чек — там несколько взаимоисключающе вычисления -)
p.p.s. Собственно решения для всех этих случаев есть, не всегда бескостыльные, но тут увы…
Точно. Особенно, если контракт с сайта госзакупок пришёл
з.ы. и это, друг, в 3:30 лучше спасть, а не на хабр писать xD
В нормальных системах существует цела иерархия объектов и классов предназначенных для работы с денежными объектами. И она существенно сложнее чем вы описываете.
Есть правила расчетов – то с какой точностью ведутся расчёты для примера для рубля РФ это чаще всего 4 знака после запятой(т.е. с точностью до 0,01 коп.)
Правила округления, правила отображения и печати сумм. Вводится понятие операция: кредитовая или дебетовая. И еще много вроде бы очевидных вещей, но наличие «стандарта» на них обязательно.
А уж если вводился понятие счет (объект для хранения сумм и правил работы с ними) то описания подобных вещей реально взрывает мозг. Там и связанные счета и активные и пассивные. А сверху это цементируется понятием «проводка» чтобы исполнялся закон сохранения энергии — закон сохранения денег.
И все это ноу-хау о которых разработчики не очень любят распространяться.
Не так давно в новостях много рассказывалось о так называемом «техническом овердрафте», который якобы образовался на карточных счетах клиентов в одном российском банке.
Технический овердрафт по карточному счету это немного другое.
Сложилось так, что кредитовый карточные продукты (с точки зрения платежной системы) выдают в банках клиентам, у которых нет кредитного лимита. В результате возникают такие уродцы как "Золотые дебетовые карты Visa и MasterCard".
Кредитная карта (по правилам платежной системы) допускает операции в offline без жесткого контроля лимита, а у клиента кредитного лимита в банке нет.
И такие ситуации (на счете 0, а приходит presentment по операции сделанной ранее в offline), скромно называют "технический овердрафт".
Назначая драконовские проценты и т.д. Забавно все это...
Вообще то, для дебетового счета существуют дебетовые карты (типа Visa Electron). Технология которых подразумевает только on-line авторизации (по правилам платежной Системы), что в какой то мере гарантирует "дебетовость". Про извраты с приемом дебетовых карт в offline лучше печально промолчать.
Само наличие у человека кредитной карты (кредитные карточные продукты) в других странах означает: Банк клиенту доверяет и открыл ему кредитную линию. Gold кому попало не дают. Поэтому для аренды машины, бронирования и пр. карту кредитную (кредитовый карточный продукт Платежной Системы).
А у нас даже бомжу (если почитать на сайтах Российских банков) могут Visa Platinum выдать на дебетовый счет.
Думаю, в основной массе там имело место списание платы за обслуживание. И вот тут как раз вопрос, куда они эти средства зачислили. Если в доходы, то это провал.
Так ли часто бывает, что у держателя карты нет денег на счету, а он этой картой за границей расплачивается?
Да сплошь и рядом. Сам так попадал. Не ту карту сунешь второпях..
Как же ему визу дали
C визой это никакой связи не имеет. Причем здесь деньги на счету. Никто это не контролирует. Всегда только справку о доходах на шанген подавал (и дают его на 3-5 лет обычно).
Типичный способ попасть на теховер:
- несколько карт.
- Перепутал карту. (воспользовался специально выделенной дебетовой с минимальным остатком для paypal и/или покупок в Азии)
- не удосужился проконтролировать остаток перед поездкой
- и пр.
Оплачиваешь услуги типа платной дороги, кофе в вендинговой машине, проезд в метро и тп. и тд. (суммы мелкие и как правило в offline).
Запросто можно выйти за пределы остатка и "попасть" на так называемый теховер.
Да еще эти транзакции в банк могут приползти через неделю или больше.
Думаю, в основной массе там имело место списание платы за обслуживание.
Типично банки просто карту блокируют если средств на очередную плату за обслуживание не хватает. А поскольку плата берется каждый месяц (и авансом), то потери банков от "на счете 0, клиент забил на платеж и исчез" минимальны. И никто особо по этому не беспокоится.
Что там проскочило в новостях (без всяких подробностей)… Уровень журналистов знаю и поэтому такие "желтые" новости всегда делю на 10 минимум.
день Х — на карте 5600 рублей, курс бакса 56, покупка на 100 баксов прошла успешно
день Х+2 — курс 58 рублей, пришло списание 100 баксов, с карты списалось 5800 баксов и клиент должен банку 200 рублей (тех овер).
Ну и еще миллиард способов попасть в тех овер — типа штрафов за прокат машины, выпитый минибар в отеле и т.д.
Если в процессинге еще нет информации о зачислении и средств для авторизации не хватает, то он просто отобьет ее, а не загонит клиента в овер.
просто полет фантазии.
Нравятся мне такие без безапелляционные заявления…
Вы вообще в этой обрасти работали когда ни будь?
Я так уже больше 15 лет платежными картами занимаюсь.
Слипы блин…
У меня вот на моей личной EMV карте лимит offline есть. Хотя она к дебетовому счету привязана. Сам лично в служебном ПО ее ковырял и профиль анализировал. Чисто из любопытства.
Зачем банк такие карты заказывал — ХБЗ.
Просто вспоминаются покупки через Samsung Pay с использованием дебетовой карты, место такое что связи там нет, оплата проходит успешно, ИБ платеж не видел несколько дней хотя обычно сразу видит. Суммы в этих случаях всегда меньше 1000 рублей были.
А разве оффлайн лимит небольшой в некоторых случаях не штатная функция?
Штатная для кредитных карт, привязанных к банковскому счету с кредитной линией.
В иных случаях (дебетовый счет) запросто может возникнуть ситуация "технический овердрафт".
Есть Кредитный карточный продукт Платежной Системы. Он четко описан правилами конкретной платежной системы. Кредитные карточные продукты технологически допускают транзакций без online обращения к эмитенту (к процессингу эмитента). Это означает, что если эквайрер выполнил все требования ПС по авторизации (выполнил без обращения к хосту эмитента), то это не его дело есть деньги на счету клиента или нет.
Это проблема банка/организации эмитента карт.
По простому… (исторически сложилось… слипы..), если карта с "выпуклыми цифрами" — то это кредитный карточный продует. Если с "давленными", то дебетовый.
Кстати, никто не мешает эквайреру обслуживать и, например, Maestro в offline (видел и такое извращение), особенно если эмитента = эквайрер.
Но в общем случае по дебетовым картам технический овердрафт не получить.
Хотя я видел и Maestro и Electron (логотип) эмбоссированных как будто это Кредитный карточный продукт. Уж не знаю, как они апрув получили на эмиссию такого дизайна.
В общем, не надо путать Карточный продукт Платежной Системой с тем, как какой счет в банке. "Снаружи" никто и не знает дебетовый у вас счет или с кредитной линией.
Про эмбоссинг/индент тоже чушь :) разница только в том, можно ли ее катать в импринтере или нет.
Про эмбоссинг/индент тоже чушь :) разница только в том, можно ли ее катать в импринтере или нет.
Да ну? А Вы не в курсе про требования к продуктовому дизайну карт, рекомендованным профилям персонализации (куда CVM входит) и согласование эмиссии с ПС (Visa|MC|CUP|Мир)?
Что не разу не сталкивалиcь? Ни разу такие документы от ПС не видели и процедуру согласования эмиссии не проходили?
И правила для эквайреров по настройке терминалов/обслуживания разных карточных продуктов (по AID, по логотипам) ни разу не читали?
Что то меня сомнения берут насчет вашего опыта в 12 лет...
хорошо если бизнес вообще понимает что туда пишется и сколько денег за это платить :) а так туда можно вообще все что угодно запихать и мпс радостно согласует — любой каприз за ваши деньги.
Ну да. Только в опроснике обычно ссылка идет на типовой профиль.
А если что то не типовое, то переписка с ПС (а можно так?).
Возможно эта переписка перестраховка. Но кому хочется брать на себя ответственность?
Бизнесу все подешевле… бы…
И появляются уродцы типа Maestro|Electron instant карта с эмбоссированием и дизайном косящим под visa infinite. "А чо круто же смотрится".
чего то я начал через чур активно возмущаться; )
Задевают фразы "чушь".
Извините.
Предлагаю прекратить и общаться покорректнее.
А тип продукта и профили и дизайн все же связаны.
Получить от Плат. Системы добро на "нестандартный" дизайн/профиль — практически не реально
Наверное у вас крупный банк, что MC так пошел навстречу.
Голд с идент это что очень странная комбинация. ATM все равно. Хоть Gold, хоть Cirus.
А смысл Голда, на мой взгляд, помахать им в аэропорту что бы в VIP lounge попасть. Но Gold с идентом…
Народ (сотрудники сервиса) то привык к конкретному внешнему виду карт. Им же "буквари с картинками" показывают..
Впрочем, я и не говорил, что не бывает. Приводя пример Electron c эмбоссированием.
Но как объяснить обычному человеку как на беглый взгляд отличить разные карточные продукты? Логотип и тип печати.
Тип печати нагляднее и понятнее и в 99% верен.
Разве не так?
договор заключенный с банком — вот что важно, а карта это просто разрисованный кусок пвх :)
Карта это то же весчь бывает.
Валяются образы пластика (рекламные):
- Светящиеся (Не помню чьи)
- Прозрачные с голограммой бабочки (шлямбурже еще)
- С запахом. Уже не пахнут… (Gеmplus)
- Бесконтактные в разном форм-факторе (только в зубную щетку не догадались засунуть)
Даже обидно, что рано или поздно будет вытеснены другими средствами платежа.
Даже самые первые Java карты Gemplus (еще с усеченном 3DES) валяются. И VisualBasic OS карта и диск Microsoft SDK для нее (2000год).
RIP карте MS..
И VisualBasic OS карта
Что это за зверь?
Была неудачная попытка Microsoft выйти на рынок смарт карт. В пику Java картам. VB в качестве языка написания апплетов… Вызвала неподдельный (скептический) интерес на конференции в 2000 году.
Нашел у себя диск тех времен и из ностальгических воспоминаний памятнику ему сделал: )
как вы знаете по своему опыту — оффлайн операция прилетит в клиринге, а это лаг минимум в 1-2 дня. Если в банке пополнение карты через АТМ происходит через АБС сперва а синхронизация АБС-Процессинг имеет лаг больше 1-2 дней, то мое утверждение про кейс 20-летней давности и/или полет фантазии справедливо.
Однако если вы 15 лет печатаете пин-конверты, то скорее всего этого не знаете, хотя и работаете в этой отрасли: Р
cинхронизация АБС-Процессинг имеет лаг больше 1-2 дней
Это кейз 20 летной давности. Голубиной почтой…
Не встречал банк, который бы не требовал синхронизации минимум раз в день.
А большинство вообще через on-line интерфейс с процессингом предпочитают.
Впрочем, без конкретных примеров АБС+процессинг — это разговор ни о чем.
Если в банке пополнение карты через АТМ происходит
Если уж корректно говорить, то функция пополнения счета наличными в АТМ с идентификацией (аутентификацией) по карте, это не стандартная операция и вариантов реализации существует много. И процессинг (если это его сетка) может поддерживать это. И в АБС может напрямую (через on-line API) зачисления идти.
Однако если вы 15 лет печатаете пин-конверты, то скорее всего этого не знаете, хотя и работаете в этой отрасли: Р
Начнем бодать и меряться? Так смысла нет. Если вы еще в этой области работаете, то прекрасно понимаете, что конкретными примерами я не могу оперировать (иначе по шапке получу).
А на счет PIN конвертов… А что, вам известны какие то другие способы кроме:
- Выдачи карты с конвертом.
- Выдачи карты с собственоручным вводм клиентом нового PIN в служебном терминале.
- Выдачи 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 операциям (снятия нала в АТМ) естественно не возможен.
Как же ему визу дали?Занимаешь денег у друзей на день. Кладёшь их на счёт в банке. Берёшь справку с этого счёта. Снимаешь деньги. Раздаёшь занятое. Профит.
Эх, как же скучно я живу.
- Количество округлений должно быть минимизировано
- Если промежуточное значение фиксируется в документах, то его надо округлить
Абсолютно согласен
Я, пожалуй, погорячился насчет экономии, но ради производительности double применять все равно не стоит.
Более того, сумма процентов по первым 50 дням должна до копейки быть равна сумме по вторым 50. Таким образом, возможность выравнивания ошибки округления на горизонте 100 дней отсутствует также как и необходимость.
Просто когда я работал разработчиком ПО в банке, то на такой фразе:
> у банка нет обязанности поддерживать их одинаковыми до копейки
у нас люди не успокаивались ))
Фактически, в этом банке расчет начисляемых процентов по вкладам и кредитам идет на остаток на счете за каждый отдельный день, затем округление до копеек и суммирование процентов по дням за заданный период. Тогда проблема с вкладами на 100 и на 50 дней не возникает. Правда, и у этого метода есть недостаток. Так, если на двух депозитах на 1 год под 10% лежат суммы 10000р. и 10000р. 10к., то сумма процентов за год получится по ним одинаковая. Хотя, должна отличаться на 1 копейку.
ИМХО, все это должно быть прописано в правильно составленной учетной политике банка, на которую должен ссылаться любой договор между банком и клиентом. Тогда у банка будет чуть больше прикрытия от желающих доставить ему юридические проблемы по надуманным поводам.
в этом банке расчет начисляемых процентов по вкладам и кредитам идет на остаток на счете за каждый отдельный день, затем округление до копеек и суммирование
При таком расчете копится ошибка округления, причем немаленькая. А мелкие суммы (ниже 30 или 15 рублей, в зависимости от способа округления) могут вообще неопроцентиться никогда.
При изменении суммы вклада (а также процентной ставки или любых других условий, влияющих на расчет процентов, например, количества дней в году) в рамках действия одного договора вводится понятие «периода постоянства условий начисления», период с начала действия договора по дату начисления разбивается на «периоды постоянства...» по ним проходят начисления процентов с округлением до копеек, затем начисленные проценты складываются.
Вот такие ноу-хау. Иногда настолько хау, что большинство внедренцев АБС ни разу ни ноу (печальный опыт)
Про неотрицательные суммы — не согласен. В приведенном вами примере вы, фактически, предлагаете заменить одну проверку двумя (нужна была проверка на неотрицательность дебетового счета, предлагается ввести неотрицательность любого счета, автоматический кредит при нехватке средств и запрет кредита на дебетового счете). Но ведь проблема-то и заключалась в том, что программисты даже одну проверку — и ту забыли написать!
Чтобы избежать таких вот ошибок — надо не усложнять модель, а вводить внутреннее API, которое просто не даст совершить некорректную проводку.
А отрицательные суммы на уровне модели очень даже полезны. Вот недавно был пост про двойную бухгалтерию, где было показано, что простая смена знака пассивным счетам позволяет объединить в один все типы проводок.
Мой подход таков: Каждый переход суммы через 0 — это инцидент, требующий анализа. Когда же мы используем знак, то мы просто откладываем расследование этого инцидента со стадии разработки в рантайм.
Знак числа никоим образом не может быть единственным источником типа счета.
Это даже не вопрос грамотной нормализации и т.п., а просто неизбежный факт — возможность существования нулевых значений у счета делает данный признак бессмысленным.
На самом деле у любого действия с деньгами (будь то проводка или «кварковая проводка» моих оппонентов, не суть) есть информация о том с каким счетом мы работаем. И этот счет «знает» свои правила. В простейшем случае — активные счета не могут быть отрицательными, пассивные — положительными. В реальной жизни таких валидаций может быть много разных. И на суммы транзакции, и на «неснижаемые остатки» и счета у которых всегда ноль, и только ноль и т.п.
Соответственно если у нас стоит ограничения что бывают только активные и пассивные счета, а активно-пассивных нет в принципе (а это достижимо даже в регламентированном учете), то в рантайме как раз переходов через ноль не будет физически, ибо валидатор просто не пропустит такую операцию. Так что разбираться придется еще на этапе разработки или тестирования.
«Образование в конце дня в учете дебетового сальдо по пассивному счету или кредитового по активному счету не допускается».
Поскольку я обхожусь без понятий отрицательной суммы, то могу следить за тем, чтобы сумма была не меньше нуля.
Я говорю о том, что отрицательные суммы В БАЗЕ очень даже хорошо смотрятся если счет пассивный. Это упрощает целый ряд вычислений. Например это упрощает проводку. Нам не нужно вот это вот «если счет активный, то дебет это плюс....». Дебет у нас всегда плюс, кредит всегда минус. Мы упрощаем контроль целостности — просто сумма баланса всегда равна нулю, иначе «ужас-ужас» и падаем с ошибкой, и т.п. А так, то я сторонник того чтобы счета были либо строго активными либо строго пассивными, а если сильно нужно активно-пассивный, то делать два счета и отдельно сверху логику их взаимного зачета.
Когда все суммы положительные, то не нужен ни плюс, ни минус. Поэтому я считаю, что моя модель проще. Мне не нужно ничего запоминать.
У вас дебет — это плюс? Т. е. вы от кассы считаете? Вам так удобно?
Если бы я использовал плюс-минус, то у меня дебет был бы минус.
Счета могут быть активные, пассивные и активно-пассивные по типу. И у счета любого типа может быть активный (дебетовый) или пассивный (кредитовый) остаток (+ или -). Просто когда у счета «неправильный по знаку (неположенный ему по типу) остаток» — то и называют сие счет с «красным сальдо».
Валидаторы проверки остатка по типу остатка и контролируют может ли быть пассивный (кредитовый) остаток по счету например по типу — активному (при наличии признака например на этом счете «разрешенного красного сальдо»).
Говорить что «нельзя иметь активно-пассивные счета» — достаточно недальновидно. Есть системы бухгалтерского учета где они активно используются и непонятно почему ради этого надо держать два и т.п. счета. Если пользователю нужен такой счет — ну пусть объявит счет с таким типом и валидаторы будут пропускать любые проводки по этому счету без контроля знака остатка. С контролем проводок тоже нет однозначности. Во многих системах бухучета есть возможности за контролем транзакции — когда например один счет по дебету и 2 счета по кредиту (например по дебету проводка на 1000, по кредитам одна на 300, вторая на 700). Просто идет тогда контроль что общая сумма дебетов и кредитов в одной транзакции равна.
Ну и проводки с количеством счетов отличным от двух я тоже не люблю.
Но все это обсуждено и пережевано в соответствующей статье, и комментариях к ней. В итоге каждая сторона осталась при своем мнении, так что давайте не будем это выносить еще и сюда. Если есть желание подискутировать то велком в комменты к моей статье или в личку.
В банках вроде с этим борятся.
Дополню, что в конвертации валют надо обязательно зафиксировать 0.01 единицу валюты для округления и привязать её к конкретной стороне, чтобы А -> Б при округлении в стиле 3.455 — шло как 3.46, а наоборот 3.45. Т.е. принудительно! Обычно это делается в сторону основной валюты. Избежите кучи проблем.
Также обратите особое внимание на обработку нулевых сумм (0.00) и сумм очень маленьких (0.01) — дабы избежать начисления процентов на суммы по типу: 0.00 и 0.0001 (некотоыре системы, например топливные, работают с 3 цифрами после запятой, а банки вообще могут работать и с 5-тыми цифрами после запятой).
Теперь представьте, что кто-то вам закинул без вашего ведома 380 евро. Вы помните, что у вас долг около 200, заходите и видите -190.33. Вы быстренько берете и закидываете заготовленные 200. Потом, когда сумма станет -390.33, вы поймете, что это значит, до следующего раза, который произойдет может быть через год.
А надо было вместо знака минус написать словами Переплата или Собственные средства.
Таков мой подход. Я, между прочим, его никому не навязываю, о чем написал в самом начале статьи.
"Переплата" вместо минуса — это не анализ перехода через ноль, а простое условие при отображении.
Переход через ноль — это когда был плюс, стал минус (или наоборот). А тут надо показывать переплату каждый раз когда знак отрицательный, независимо от того когда он таким стал.
А минус на счете — это представление пользователю.
Мне наоборот при анализе счета удобнее не 2 колонки, а приход "+", трата "-". Так интуитивно понятнее для непосвященного пользователя. Чем пресловутые дебет/кредит.
Хотя идея Nordea (со слов sergey-b — я с этим банком не знаком) мне не нравится.
Логичнее переплату показывать как "+", а вот кредит как "-" — ибо кредит на кредитных картах, это Вы должны банку.
все люди вроде вас не способны постичь смысл отрицательного БАЛАНСА
Активно-пассивные счета это зло. Ну не прям совсем конечно, но обычно это или упрощение плана счетов и корреспонденции (ИМХО чрезмерное упрощение) или явно ошибочная корреспонденция.
У топикстартера безусловно каша в терминологии и т.п., но если перевести его позицию на человеческий язык, то он выступает против активно-пассивных счетов, и предлагает отдельно отображать долг банка нам, а отдельно наш долг банку. Ну и соответственно если это предусмотрено договором (обычно подразумевается или явно озвучено) то автоматически делать их взаимозачет.
Тогда «отрицательный баланс» как вы говорите может быть только в случае сторно и прочих специфических ситуаций.
За клиента в этом плане можно не беспокоиться, а вот банк в таком случае имеет на своем счету доходов виртуальные средства. Если он их потратит на хозяйственную деятельность, то в итоге у него в балансе образуется дыра. А все потому, что на счет доходов записали средства, которых у клиента не было.
Это непонимание бухгалтерского учета в принципе. Никакого отношения начисленные доходы не имеют к хоз.деятельности банка. Все расходы банка производятся не со счета доходов, а со счета расходов. И разница образуемая между ними и есть прибыль или убыток.
Ну и далее — реально вывести можно только деньги, которые находятся на корсчетах банка или в кассе. Вы можете нарисовать хоть 1 млрд доходов, но для образования дыры вам доступны только средства на корсчете или в кассе. Упрощенная модель выглядит так — клиент вносит в кассу банка 1 млн во вклад, банк выдает другому клиенту 1 млн как кредит. Далее % по вкладу вы начисляете как расходы банка, % по кредиту — как доходы банка. Но есть разница между начисленными и реализованными расходами и доходами. Например % вы согласно правил начисляете клиенту по вкладу каждый месяц, а выплата % по договору раз в квартал. Т.е. можно спокойно показывать что у вас расходы нарастают, но реально никаких денег вам для выплат в кассе или на корсчете не нужно. Мало того — вы можете % выплачивать на счет клиента и тогда это просто «бумажное» нарастание обязательств, до тех пор пока клиент не захочет вывести деньги из банка. Вот тогда и выяснится что денег на корсчетах и в кассах может быть 0, а все деньги были выведены за счет выдачи кредитов, которые никто возвращать не собирается, или ценных бумаг, которые ничего не стоят и т.п.
Далее — на что попадет банк или другая организация, которая начислит «лишние доходы». А попадет она на «лишние налоги».
Почему регулярный платеж за обслуживание карты вы называете "передачей денег банку на хранение"?
Вы их сделали. 4 с половиной часа назад в комментарии, на который я отвечал. Если я его как-то неправильно понял, то поясните что именно было не так.
Напомню, в тексте поста была упомянута ситуация, когда банк снял плату за обслуживание с пустого счета и записал это себе в доходы. Утверждается, что с доходами банк конкретно в этом случае погорячился, даже если не учитывать заведомую некорректность операции.
Вы же начали рассказывать про какие-то "средства клиента которые переданы банку на хранение". Какие именно средства тут имелись в виду?
… Часто бывает, что общий итог <> итог по колонке сумм… И тут возникает вопрос, как от этого избавиться?
Не стоит технарю решать проблему экономиста/бухгалтера?
Опишите прямой алгоритм расчета (когда сумма по ИТОГО не сходится с ПРОИЗВЕДЕНИЕМ) или обратный (когда сумма по ИТОГО с ПРОИЗВЕДЕНИЕМ сходится, но в последней позиции копеечка подправлена). Подпишите порядок расчета у Главного экономиста(бухгалтера). И не ломаете себе голову.
… налоговая это нормально хавает.
Более того, вот здесь я нагуглил еще более интересную ситуацию, которая показывает насколько глубоко днище этой проблемы.
но в последней позиции копеечка подправлена
Представьте себе список, состоящий из несколько сот позиций, у которых веса (суммы) каждой позиции приблизительно одинаково небольшие. Если сделать так как вы предлагается, то у вас погрешность округления будет больше чем вес самой позиции. Но вы же сделали все по «подписанному», какая разница, да? :) Я именно про это, а вы все сразу редиректите на экономиста/бухгалтера. Типа, а это не техническая задача, мне это по барабану, это чисто бухгалтерская задача, мне как сказали, я так и накодил. Подписано? Подписано! А ко мне какие проблемы? Хотя отчасти вы, конечно же правы. Если заказчик подписал 2+2=5, то это 5 и никак иначе. Но мне кажется, свои 5 копеек в исходную постановку, если вы видите очевидный косяк, вставить можно. Я рассматриваю для себя такую позицию так: я вижу, что у вас есть некоторые проблемы в постановке, я вам их сообщаю, а делать вам что-то с этим или нет — дело ваше. После этого считаю, что моя совесть, чиста.
Показывает лишь то, что вопрос не технический, а экономический и юридический. Программисту остается только затребовать четкое ТЗ, как считать суммы.
PS: Бухгалтерский учет часто не понять логически. Бывают законы, которые противоречат друг другу, или наоборот, не раскрывают важные моменты, как с округлениями. В таком случае судьба предприятия будет зависеть от позиции бухгалтера, его опыта, умения вести разговор с инспекцией. Поэтому как он скажет...(2+2=5 ;-)
У меня в одной системе было требование 1.45 = 2. Сделал так, как просили, но чтобы округление так работало, надо было зайти в настройки и явно включить эту фичу, которая по умолчанию была отключена.
Другой пример с положительной судебной практикой для налогоплательщика
Так и все мои «гугли»-примеры, они тоже с положительным решением арбитража, который встал на сторону клиента, а не фнс. Я привел эти решения только на постфактум, что якобы «налоговая это нормально хавает». Налоговая, как раз таки и не хавает. Хавает пока арбитраж, куда фнс любит ходить по этим спорным моментам.
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
В классе 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 — это хорошие имена свойств, которые в обоих случаях релевантны ситуации.
Вся логика, похоже, реализована верно, и такой класс можно спокойно использовать в трейдинге, просто ИМХО имена свойств надо изменить, чтобы они больше соответствовали доменной области.
Курс обмена — односторонний, именно поэтому он является вектором.
Я понимаю, что будут 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.
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());
}
1.1. Как вариант: хранить суммы в полях int в значениях наименьшой возможной единицы (копейки, центы) и делить их на степень кратности к денежной единице только перед выводом на экран/печать. Кстати, такие реализации встречал неоднократно.
2. Проводки, особенно корректирующие, могут быть и с отрицательным знаком, а не только дебетовые и кредитовые. Так что не надо путать работу с суммами и корректность отображения результатов в пользовательском интерфейсе. В конце концов баланс имеет право быть отрицательным, как и сальдо.
3. Покупка/продажа валюты, акций и т.д. во всем мире идет как bid/ask. Оператор рынка (банк в данном случае) объявялет обменные курсы, по которым покупает и продает. Если юзер не понимает таких элементарных вещей, то лучше ему не заниматься такими операциями. Какой велосипед изобретаете?
2. В технических целях вы можете у себя в системе иметь и отрицательные денежные суммы, и отрицательные балансы, да хоть мнимые. Только не показывайте эти потроха бухгалтеру, ему плохо станет. Я когда программистам объясняю про бухучёт, так говорю: бухучёт придумали раньше, чем отрицательные числа, и будущие бухгалтера в школе на эту тему не ходят. Отрицательных чисел в бухучёте не бывает, зато бывает дебет и кредит, а также пассивные и активные счета.
3. Это терминология, характерная для бирж (и, возможно, ещё где-то). В банках говорят buy/sell, при этом подразумевая покупку/продажу иностранной валюты банком за национальную валюту.
У меня есть пачка «обезьянок», которым базовую математику исчисления НДС привить не могу, но они очень сильно любят «красивые цифирьки» и крайне недовольны, когда не могут занести в систему подготовленный в Excel прайс-лист, красивость которого обеспечена использованием до 8 знаков после запятой. :(
2. Все зависит от конкретного законодательного поля данной страны. Корректирующие проводки налогового учета, например.
Я же обратил внимание на то, что автор «запрещает» показывать отрицательные цифры/итоги. Но что делать с отрицательными балансом или сальдо — он не сказал. Когда смотришь оборотно-сальдовую ведомость по клиенту, то проще все понять по знаку, чем оперировать дебитом/кредитом. А тем более когда просматриваешь пачку клиентов одной таблицей. Ну за исключением идеального случая, когда сразу ноль, конечно :)
3. buy/sell, bid/ask — суть одного поля термины, где традиции среды, в которой они используются, намного важнее программистского идеального мира.
Я когда программистам объясняю про бухучёт, так говорю: бухучёт придумали раньше, чем отрицательные числа, и будущие бухгалтера в школе на эту тему не ходят. Отрицательных чисел в бухучёте не бывает, зато бывает дебет и кредит, а также пассивные и активные счета.
Бухгалтера знают отрицательные числа. И для них отрицательные числа синоним красного сторно, а значит вызывает сильный попаболь, и эмоции перевешивают любые ваши объяснения, что вы совсем не это имели ввиду.
Вопрос: а зачем? Вычисления в целых числах заведомо не имеют подобной проблемы и обходятся обычно дешевле (сумма целых даже в виде Decimal из C# проще, чем два round плюс суммирование и масштабирование).
> Как вариант: хранить суммы в полях int в значениях наименьшой возможной единицы (копейки, центы) и делить их на степень кратности к денежной единице только перед выводом на экран/печать. Кстати, такие реализации встречал неоднократно.
Именно. Фактически, для целей финансов это самый нормальный режим. (С поправкой: не только печать, но и всякая конверсия; иногда и в какой-нибудь float надо переводить — например, проценты взять.)
> Покупка/продажа валюты, акций и т.д. во всем мире идет как bid/ask. Оператор рынка (банк в данном случае) объявялет обменные курсы, по которым покупает и продает. Если юзер не понимает таких элементарных вещей, то лучше ему не заниматься такими операциями.
Практически любой человек, который ездит за границу, вынужден думать о покупке/продаже валюты. Предлагаете вообще не ездить?
Практически любой человек, который ездит за границу, вынужден думать о покупке/продаже валюты. Предлагаете вообще не ездить?
Мне тоже очень нравится творчество А. Макаревича, но применять буквально к реальной жизни его «Не стоит прогибаться под изменчивый мир» как-то не додумался :) Думаете стоит попробовать?
Если вы их перепутаете, это будет в 10 разу хуже, чем сложить метры с миллиметрами, потому что 1 BYN = 10 000 BYR.
Не в 10, а ровно в 10 тысяч раз хуже :)
Куда сложнее складывать граммы с миллилитрами.
Когда ГСМ или лаки-краски закупаются по весу, а реализуются по объему? В первом случае свои корректировки вносит температура товара, во втором точность отмеривания (40 кг бочку отпускают большим количеством порций с точностью до 10 мл). Обе эти задачи без регулярных инвентаризаций по нормативам решения практически не имеют.
Тип «Currency» отменили?
Первый пункт я бы формулировал немного иначе, хотя по сути близко.
Идти в бухгалтерских расчётах (речь о них — не о финансовых в целом, там может быть и «высокая» математика) надо от некоторых базовых правил. В первую очередь это то, что деньги не могут появляться ниоткуда и исчезать в никуда, любая сумма и разность должны быть точными. Как бы ни были сформулированы правила для округлений в каком-то случае (НДС, проценты, другое), это особые ситуации. Лучше получить исключение переполнения, если точности не хватает, чем потерять доли (копейки) и потом искать источник несведения баланса.
Основной проблемный случай — деление на несколько частей. Например, деля 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 получает...»
В C# я проверял — выскакивает ArithmeticOverflowException. А вот Java проглатывает.
Можно полный пример?
> В C# я проверял — выскакивает ArithmeticOverflowException.
Аналогично. О какой операции речь?
C#
double d = Math.Round(0.33d, 2);
d *= 10d;
d -= 3d;
d *= 10d;
d -= 3d;
Console.WriteLine(d); // 2.66453525910038E-15
Исключение при переполнении целой части не выбрасывается, вы правы. Я спутал с decimal.
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 не ловится.
У меня был реальный случай, когда одна половина команды считала, что операция обозначает покупку иностранной валюты у клиента за рубли, а другая половина команды была уверена, что это покупка валюты у компании. Чтобы разобраться подняли все документы. Везде было слово «покупка», но нигде не уточнялось, кто у кого покупает. Причем, в некоторых согласованных всеми участниками документах были разночтения в проводках. Т. е. даже по проводкам однозначно нельзя было смысл восстановить.
Ну и известный инцидент, когда в одном серьезном банке курсы забили наоборот, в результате можно было купить доллары и тут же их продать дороже, тоже связан с неоднозначностью терминологии.
Если есть выделенная сторона сделки то можно писать что-то вроде CompanyBuyCurrencyRate а если выделенной стороны нет (биржа), то там курс один, плюс спред или комиссия или еще что-то, но курс у сделки один.
Аналогично проблемен и Decimal из C#, и даже больше: если каким-то образом вылезли за его 28 цифр, то там даже inexact exception не будет: он не умеет жаловаться на такое.
Decimal выкидывает Exception.
decimal d0 = 79228162514264337593543950335m;
decimal d1 = d0 + 1; // OverflowException
Я не о переполнении порядка, а о превышении точности представления (и Вы пропустили слово 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 от него есть только в виде «а вы не допускайте таких больших чисел».
Для матфизики, наоборот, фиксированная десятичная точка будет бессмысленной.
Вот то, что в C, C++, C# плавучка обязательна, а фиксированная точка — нет, уже показывает ориентацию языков. А в результате имеем сохранение старого кода на всяких коболах ;(
Хм, не согласен. Поведение 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, которое уже не влазит => генерируется исключение переполнения. Тесты это подтверждают.
Может это повтор, но говоря о валютах, не следует забывать, что у некоторых валют, наименьшая единица не есть одна сотая(цент, копейка).
Что касаемо конверсии, то если говорить о банках, то там будут для некоторых операций использованыофициальные курсы ЦБ, а для нкоторых курсы самого банка. Это тянет за собой доп проводки с участием счетов доходов/расходов и тд.
В нерегламентированном учете «для блондинок» (я сейчас начал новый экстрим-проект, хочу автоматизировать управленческий учет микро-бизнесов вроде киосков/батискафов, так что ради простоты готов на большие жертвы, ибо иначе люди просто не станут с этим работать), стоит ли использовать сторно не для исправления ошибок а для текущих реальных операций? Хочется иметь минимальный план счетов. До безобразия минимальный, ибо дюжина счетов это для неподготовленного человека уже адЪ. Ну и вот простейший кейс — киоск работает с кофе-машиной взятой в аренду. Была взята машина с расходниками, аренда ноль, за расходники заплатили. Машина на забалансе по залоговой стоимости (рядом я рисую забалансовый же пассивный счет «залоговая стоимость арендного имущества» с той же суммой, так что забаланс у меня живет по правилам баланса, но не суть), расходники приходуем на материалы, точнее «расходники кофе-машины», чтобы не пугать пользователя сложными названиями. Всё ок, работа кипит, материалы потихоньку списыватся согласно смет, и тут прилетает новый план он поставщика машинок. По новому плану не договорились и делаем возврат. Поскольку изменение условий по инициативе поставщика, то он забирает остатки расходников и возвращает за них денег пропорционально расходу.
Эту операцию можно отразить по разному.
Можно сделать «продажу» материалов поставщику. Вроде норм, но тогда у нас появляется «мусор» по оборотам. Ломается статистика себестоимости, рентабельности, да и просто продажа это доход, а на доход считаем проценты продавцу или манагеру… В общем некрасиво. Остальные варианты довольно монструозные. Больше всего мне нравится вариант — сторнировать эту частичную сумму и частичный возврат (те же проводки что при покупке, с теми же номерами счетов только сумма минусом). Плюс такого подхода в том, что обороты по счетам не будут содержать лишних сумм, при этом по всем счетам на любую дату будут правильные сальдо и т.п. Минусы: на некоторых периодах некоторые обороты будут красным, что не очень понятно, хоть и правильно (да, в этот день поступление расходников отрицательное, и оперативные расходы отрицательные, но это ведь так и есть), ну и у меня есть какое-то внутреннее сопротивление говорящее о том, что сторно только для ошибок.
А вы что думаете?
Можно сделать «продажу» материалов поставщику.
Ну разве что для поржать :)
С другой стороны у меня тоже стойкое предубеждение против сторно в живой работе.
Но в реальной жизни без этого никак — отмену ошибочной (поспешной) реализации или ошибочного прихода (например: накладную поставщика по учету провели «по бырому», а живой товар до склада так и не дошел) в учетной системе надо именно сторнировать, так как фактически этих операций не было и в обороте с контрагентом они учитываться не должны. В отличие от кофемашины, которую поставщик поставил год назад, на которую в течение года шли затраты на расходники, электроэнергию, воду и т.д. Да еще и возврат может производиться с учетом амортизации.
А с кофе/молоком выходит как раз близко к «по ошибке провели». Поставщик дал на условиях покупки, т.е. мы у него купили, не взяли под реализацию и т.п., а именно купили, а потом (спустя пару недель, так что да, другой учетный период, в разрабатываемой системе учета учетный период неделя планируется) вдруг решил разорвать договор. Но поскольку расходники в значительной мере привязаны к конкретному поставщику и конкретной машинке, а разрыв по его вине то он забрал остатки расходников (кофе/молоко/шоколад), посчитав пропорционально расходу остаток и вернув деньги. Фактически тут мы купили, а потом через две недели оказалось что «ой, нет, не купили, отдавай мои какашки, забирай свои бумажки». Тут по смыслу явное сторно, т.е. отмена операции, только на другом уровне абстракции, не ошибка учета а «тот день когда я с тобой познакомился был ошибкой».
В большом, «взрослом» учете когда у меня бывали такие или похожие случаи, то я всегда делал тупо продажу поставщику обратно. Что достаточно близко к тому что требует налоговый кодекс (один фиг налоговую накладную писать), но не нужно заморачиваться со всякими рекламациями, письмами, доп.соглашениями и прочим. Я купил, я продал. Продал в ноль «потому что могу». (главное правильно объемы указать, чтобы не сделать в минус, тогда нужно будет доначислять себе НДС). Теоретически по бухучету это надо было бы проводить по другим счетам с другой корреспонденцией, но итог все равно был бы тот-же, и сальдо бы не поменялось.
В учете для блондинок это «сложно-сложно-блин». Вот честно, я очень хорошо помню какая аудитория ходила ко мне когда я в банке аналитиком работал. Да что далеко ходить, тут у нас прямо на хабре куча людей не готова воспринять двойную запись, а тут куча малопонятных счетов с «более правильной» корреспонденцией. Нафиг, нафиг.
А с кофе/молоком выходит как раз близко к «по ошибке провели». Поставщик дал на условиях покупки, т.е. мы у него купили, не взяли под реализацию и т.п., а именно купили, а потом (спустя пару недель, так что да, другой учетный период, в разрабатываемой системе учета учетный период неделя планируется) вдруг решил разорвать договор. Но поскольку расходники в значительной мере привязаны к конкретному поставщику и конкретной машинке, а разрыв по его вине то он забрал остатки расходников (кофе/молоко/шоколад), посчитав пропорционально расходу остаток и вернув деньги. Фактически тут мы купили, а потом через две недели оказалось что «ой, нет, не купили, отдавай мои какашки, забирай свои бумажки». Тут по смыслу явное сторно, т.е. отмена операции, только на другом уровне абстракции, не ошибка учета а «тот день когда я с тобой познакомился был ошибкой».
Тут нет никакой ошибки — банальный [частичный] возврат товара поставщику. И который должен идти в оборот между вами как контрагентами (в РФ так?).
Так же при подбитии управленческих итогов необходимо понимать, что «тут» продажа готового продукта (стаканчик кофе) и есть такая-то прибыль, а «там» возврат расходников поставщику и прибыли тут нет. А на следующем уровне анализа при подготовке среза «там» мы должны понимать с каким поставщиком и когда и по какой причине происходили возвраты — переоценка под акцию поставщика, возврат некондиции, выбрыки поставщика (описанный Вами случай) и т.д. Ведь как иначе понимать с кем стоит работать. а с кем нет?
Так же при наличии налога с продаж такое проведение возврата приведет к необходимости уплаты этого самого налога. Кому это нужно?
Для «учета для блондинок» все это можно спрятать под оберткой с каким-то подобием учета на регистрах, но внутри все должно быть ок.
Теоретически по бухучету это надо было бы проводить по другим счетам с другой корреспонденцией, но итог все равно был бы тот-же, и сальдо бы не поменялось.
Не теоретически, а практически так надо делать, иначе «шоколадка» фининспектору будет расти в геометрической прогрессии. Сальдо — это просто показатель текущего среза. Показатели оборота очень важны, а Вы хотите это отрезать.
Да что далеко ходить, тут у нас прямо на хабре куча людей не готова воспринять двойную запись, а тут куча малопонятных счетов с «более правильной» корреспонденцией. Нафиг, нафиг.
То что они что-то не могут воспринять — это пол-беды, страшнее, что они за это минусят направо и налево и в кармо. Агрессивное невежество ведет любое сообщество вниз и только вниз.
PS
Внимательно посмотрите на Gnucash — или придете к пониманию, что изобретаете велосипед, или сможете почерпнуть оттуда много полезного. В случае «велосипеда» может будет иметь смысл сосредоточиться на изготовлении обертки к Gnucash?
Не теоретически, а практически так надо делать, иначе «шоколадка» фининспектору будет расти в геометрической прогрессии.
Да ну, какая шоколадка инспектору там где вообще не было учета и все было красиво? Это у вас профдеформация взрослым учетом.
СПД/ИП лепят что попало и никого ничего не волнует, лишь бы кассовый аппарат/безнал сошелся с отчетом (утрирую, есть наемные работники и т.п. но правильные счета возвратов это точно ерунда). А в цивилизованных странах у микробизнесов вообще все настолько пофиг что даже иногда шокирует.
Помню дядя мой рассказывал лет десять назад — у него гринкарта. Он художник. Оформлен чем-то вроде наших СПД/ИП. Говорит инспектор его ругала что он не записывает себе в тетрадку сколько топлива у него уходит на поездки на натуры, и ворчит что надо записывать «на глаз». Ну и пару недель назад жена моя спрашивала у одной израилитянки которая занимается схожим с ней бизнесом, как у той с налоговой организованы отношения. Ответ той был еще веселее — «а зачем оно? Я еще не доросла до того объема когда надо что-то писать или платить». Не помню лимит который она назвала, но вроде речь шла о суммах в месяц в размере чуть больше их минимальной з/п.
Сальдо — это просто показатель текущего среза. Показатели оборота очень важны, а Вы хотите это отрезать.
Ну так обороты то как раз становятся более правильными.
Налог с продаж (американский вы имеете ввиду?) да, плясать будет плохо, хотя как я помню там он на бизнес бизнесу не распространяется. А остальное можно учесть и так. Проводки то видны. Не знаю, в сомнениях я. Сторно выглядит красиво, но странно. Наверное буду таки отдельные счета делать, и для оборотов какие-то костыли приделывать красивые.
Да ну, какая шоколадка инспектору там где вообще не было учета и все было красиво? Это у вас профдеформация взрослым учетом.
СПД/ИП лепят что попало и никого ничего не волнует, лишь бы кассовый аппарат/безнал сошелся с отчетом (утрирую, есть наемные работники и т.п. но правильные счета возвратов это точно ерунда). А в цивилизованных странах у микробизнесов вообще все настолько пофиг что даже иногда шокирует.
Ну, по моим наблюдениям, у вас в последнее время делают все что угодно, только не облегчают жизнь микробизнесам. Поэтому необходимо быть готовым к закручиванию гаек.
То, что я за 4+ года налоговую ни разу не посетил и ни одного отчета вне общего графика не сдал не говорит ровно ни о чем — трахнут какого-то моего контрагента и пойдут трусить его контрагентов встречными проверками и штрафовать/сажать/штрафовать. А еще с вашими перекладками ответственности за определение добросовестности контрагентов на плечи бизнеса так вообще все весело становится…
Профдеформация у меня если и есть, то от другого, но в целом Вы правы — если информацию можно структурировать и сохранить, то ее обязательно надо структурировать и сохранить.
Ну так обороты то как раз становятся более правильными.
Примитивно != правильно.
Почему правильное чуть-чуть сложнее, написал ранее. Здесь Вы хотите облегчить свою работу, не особенно думая о завтрашнем дне. Имеете право — это Ваш продукт, — но истина дороже :)
Налог с продаж (американский вы имеете ввиду?) да, плясать будет плохо, хотя как я помню там он на бизнес бизнесу не распространяется.
В РФ его отменили в начале нулевых. В РФ его регулярно обсуждают с конца 2016. В РФ его скорее всего введут в 2019.
По каким правилам это будет работать в РФ — мне не известно. Но вряд ли по американским. :)
И опять же: разные операции подчинять одним и тем же правилам физического документооборота намного проще, чем «свалку» разделять по разным правилам на основании каких-то косвенных признаков.
А остальное можно учесть и так. Проводки то видны.
По своему опыту лоточной торговли в первой половине 90-ых знаю, что более-менее понимать чем выгодней торговать можно только при 2..3 поставщиках и не более 5...10 видов товаров. Дальше уже только нормальный учет или чутье (которое чаще подводит, чем нет).
В РФ его отменили в начале нулевых. В РФ его регулярно обсуждают с конца 2016. В РФ его скорее всего введут в 2019.
По каким правилам это будет работать в РФ — мне не известно. Но вряд ли по американским. :)
Вот как раз на РФ-учет мне больше всего плевать ибо РФ-клиенты у меня идут по остаточному принципу — если подошло что-то под них, то хорошо, если нет то и нет, но США интересует а у них он есть.
И опять же: разные операции подчинять одним и тем же правилам физического документооборота намного проще, чем «свалку» разделять по разным правилам на основании каких-то косвенных признаков.
Так признак то не косвенный а очень даже прямой — сторно или не сторно.
Тут вопрос в основном стоит в том, что сторно ассоциируется с ошибками, и соответственно будет плохим стилем использовать его в другом смысле.
По своему опыту лоточной торговли в первой половине 90-ых знаю, что более-менее понимать чем выгодней торговать можно только при 2..3 поставщиках и не более 5...10 видов товаров. Дальше уже только нормальный учет или чутье (которое чаще подводит, чем нет).
Нет, ну вы прям обижаете. АВС-анализ и все такое, плюс минимальное бюджетирование (группировка субсчетов в привязке к разным направлениям чтобы видеть не только консолидированный баланс/обороты но и по направлениям) и т.д, в общем все стандартные виды анализов я конечно подразумеваю. Без этого никуда. Иначе тупо смысла нет. «Учет на регистрах» каждая бабушка торгующая семечками имеет. Даже с учетом того что версия «Киоск» и ядро пойдут под MIT, все равно — без внятного анализа оно никому не нужно.
А правильно это как?)
Продажу я точно делать не буду. Одно дело делать такое в большом учете для налоговой, другое — в управленческом для микро. А правильно… Вот как бы Вы сделали правильно?
P.S. Логотип поделки обязательно должен быть квадратным.
Если не планируете выходить на баланс, вам не нужна двойная запись
Я не могу не планировать «выходить на баланс». И даже дело не в том что одна из целей проекта это красивая демонстрация для второй части статьи про двойную запись. Учет без двойной записи — ущербен по своей природе. Даже если это шесть счетов всего.
Аналог пишется +- за месяц
Может мы о разных регистрах говорим и там есть какой-то специфический функционал которого я не знаю (в свое время прошел всю пачку их курсов на специалиста или профессионала как оно там у них, но сертификат получать не стал ибо 1с мне не очень интересен). Но насколько я помню там буквально пару фич у накопительного было. Сальдо на дату, дебетовый оборот за период, кредитовый оборот за период и вроде всё… День работы с отладкой, документацией и расписыванием АПИ. Нет, там еще вкусно то что можно их использовать в языке запросов (и соответственно построителю), но это уже не к регистру а к ядру запросов и построителю вопрос.
Собственно накопительный регистр это тот же регистр сведений (т.е. запрос вытягивания последней записи у которой дата меньше или равна), только с предвычислениями сальдо и оборотов. Ну или я что-то забыл. Книжки на полке стоят, скоро буду перечитывать, может и вправду забыл, но какой-то магии сравнимой с тем что может дать двойная запись я там не помню.
Учет без двойной записи — ущербен по своей природе.
Не каждый хозяйственный учёт — бухгалтерский. Зачастую двойная запись порождает больше проблем, чем решает. Потому вполне приемлемо, например, сложную/специфическую аналитику вести в отдельных системах на регистрах (фактически, журналах с плюшками разной степени автоматичности агрегации), а сводные данные, сгруппированные по видам проводок, сливать в бухгалтерскую систему.
Абстракция и API накопительного регистра весьма просты, некоторые сложности начинаются в оптимизациях структуры БД и методов обработки данных на относительно больших базах (10-20 Гб). Не всё красиво ложится на SQL с оптимальным планом. Очень повезёт, если за оптимистично оцененный мною месяц всплывут основные архитектурные ошибки.
В случае мелкого бизнеса, конечно, можно не морочиться и забить им типовые формы типовых операций, формирующих типовые проводки в единственную оборотную таблицу, захардкодить минимальные типовые отчеты, не давать настроек и интеграции, продать бинарь, каждую новую хотелку пилить за рандомную денюжку — показать, короче, звериный оскал автоматизации.
Зачастую двойная запись порождает больше проблем, чем решает.
Пример можно?
Я в общем и в целом не то чтобы прям против
сложную/специфическую аналитику вести в отдельных системах на регистрах (фактически, журналах с плюшками разной степени автоматичности агрегации), а сводные данные, сгруппированные по видам проводок, сливать в бухгалтерскую систему
Но хотелось бы реальных примеров.
Абстракция и API накопительного регистра весьма просты, некоторые сложности начинаются в оптимизациях структуры БД и методов обработки данных на относительно больших базах (10-20 Гб). Не всё красиво ложится на SQL с оптимальным планом. Очень повезёт, если за оптимистично оцененный мною месяц всплывут основные архитектурные ошибки.
Если речь идет о сугубо названном мною АПИ, то я вот просто не представляю что тут вообще делать, не то что «где могут быть узкие места». От объема базы зависит время проведения операций «задним числом», да и то не от размера базы а от того насколько это число будет задним. все остальное вообще О(1) будет (на самом деле конечно O(log(n)) но сиуации когда индексы влияют на производительность это уже проблемы DBA а не разработчика).
Единственное узкое место для «взрослого» объема на накопительном регистре это переполнение оборотов накапливаемых за всю жизнь. Особенно если взять 32-бит знаковое целое и в него засунуть денежку с шестью знаками после запятой и ничего не делать, а просто ждать когда же к нам придет переполнение. Но даже в таком случае может и не придти.
В реальности берем Unsigned BIGINT, ну и как минимум чтение оборотов организуем через вьюв, запись процедурой (если клиент может оказаться 32-бит) и об этой проблеме тоже забываем.
Я могу ошибаться. Я могу сильно ошибаться. Но очень бы хотелось не просто «вы не понимаете», а хоть каких-то намеков в чем тут можно ошибиться…
Если вы хотите по данным этого учёта непосредственно формировать отчёты органам, то открывайте учебник по бухучёту и делайте как там написано.
Если у вас сейчас нет бухгалтерии, а в будущем планируется, то разрабатывайте систему так, чтобы к вашему учёту рядом сбоку можно было прикрутить параллельно работающий правильный бухучёт. Чтобы можно было в одном окне видеть ваш простой и удобный учёт из трёх счетов, а в соседнем — правильный учёт из 100. Вариант: продумать выгрузку данных из вашей системы во взрослую бухгалтерскую систему, и на уровне интеграции заставить бухгалтерскую систему делать правильный учёт. Но не надо надеяться, что это само получится, надо продумывать состав информации, который у вас хранится, с тем чтобы бухгалтерской системе потом было достаточно информации для правильного учёта.
Но не надо надеяться, что это само получится, надо продумывать состав информации, который у вас хранится, с тем чтобы бухгалтерской системе потом было достаточно информации для правильного учёта.
Я придерживаюсь принципа «первичка наше всё». Ибо запоротая структура первички это гарантированный ад потом, так что всю информацию которую можно прихватить автоматом я прихватываю всегда (без ущерба удобству).
А так то я говорю сугубо о управленческом. ЦА — люди на упрощенных системах учета с оборота или фикс.патенты и т.п. которым все по шарабану. У таких обычно управленческий в полном «ж».
5 правил работы с суммами