Pull to refresh

Comments 54

Дебит — Количество газа, а также воды, нефти или другой жидкости, даваемое источником в определённый промежуток времени. (с) Вики
В статье речь скорее про дебет.
Спасибо, поправил. Интересно, а по-английски именно debit
Однако, насчет «Cash book» задумались, даже посовещались. Ну в данном конкретном случае, если клиент приносит наличные (судя по cash в названии), причем не важно зачем — оплата за товар, либо положить на свой счет в банке — то это будет с точки зрения бухгалтера 50 счет плана счетов — «Касса предприятия». Перевод тут неуместен.
Все правильно «cash book» — это кассовая книга. А счет 50 (точнее его субсчета) используется только в обычных организациях. В банках для учета наличных денежных средств используется счет 202 согласно плану счетов в кредитных организациях.

Кэш, это не только наличные, это все не кредитные средства клюоторыми вы можете распоряжаться. Налиные, это дословный перевод который не отражает реалное значение этого термина.

Спасибо за статью, как раз предстоит писать биллинг в собственном проекте.

Правильно ли я понял работу с расчетными периодами?
Предположим, что после периода A наступил период B. Мы выводим остатки по каждому аккаунту в сash book в периоде A, а потом возвращаем остатки аккаунтов из сash book в периоде B.
Да, именно так.
В итоге каждый период можно рассматривать независимо от всех остальных периодов. Можно выкинуть из БД все периоды кроме текущего, и это никак не повлияет на балансы пользователей и выполнение правил общего нулевого баланса. Зато сильно упростит подсчеты — ибо транзакции копятся, и с миллионами архивных записей в БД подсчеты начинают уже подтормаживать.

Да, картинки были действительно перепутаны, поправил
Как правильно организовывать переход между расчетными периодами на уровне архитектуры/кода?

Блокировать новые транзакции пока не будут перенесены все остатки на новый период? (Но тут много вопросов. Что делать если переход занимает неприемлемое время (SLA)? Как организовать блокировку при микросервисной архитектуре? И т.д)

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

У нас в системе около ста тысяч аккаунтов, операция подсчетов балансов по всем занимает пару минут. Это не так долго. В это время апи просто на все запросы о переводах отдает код ошибки «на сервере ведутся работы».

Нагрузка у нас невелика (где-то один запрос в две-три секунды), а ночью почти совсем сходит на нет, так что это все не проблема.

В супер-пупер хайлоаде, возможно, применяются какие-то другие подходы. Хотя я и у вполне серьезных банков встречал такие клиринговые периоды, когда выполнение операций приостанавливается на час по ночам.
И я так понимаю, что не страшно, если перенос остатков на новый период будет осуществлён не сразу, а после того, как некоторые транзакции в новом периоде уже были совершены? Просто при переносе остатков на текущий период не учитывать транзакции за этот же текущий период, верно? Или есть нюансы?
Если я правильно понимаю вопрос, а также основываясь на своем небольшом опыте:
— транзакиям по определенным правилам назначаются posting period id чтобы заранее определить к какому периоду они относятся. В зависимости от принятых правил, транзакция может быть отнесена к следующему периоду если даже она началась в текущем.
— перенос остатков осуществляется процедурой закрытия периода
Нет, перенос остатков очевидно должен выполняться до любой транзакции. Так как если мы списали в конце прошлого периода все балансы в ноль, то мы не сможем сделать никакой исходящий перевод. Сможем сделать входящий, но это все путаницы добавит. Лучше все-таки это включить в процесс закрытия периода, во время которого система не принимает запросы на переводы.

В моем сервисе это реализовано как-то так:

Сущность Account имеет поле period
Баланс юзера счетается примерно так (пример абстрактный, не используйте в продакшн):
SELECT SUM(amount) as balance FROM Transaction WHERE account_id = account.id AND period <= account.period

Организовать блокировку можно средствами самой СУБД:
SELECT * FROM Account WHERE period < "current period" FOR UPDATE SKIP LOCKED LIMIT 10

Таким образом, условный мускул пикнет вам 10 аккаунтов и залочит их, так что параллельные транзакции не смогут получить доступ к этим записям. И перенос балансов можно повесить на крон, либо придумать что-то более эффективное, исходя из вашей конкретной ситауции. Почитайте про такие фичи как FOR UPDATE, SKIP LOCKED, NOWAIT

Можно сделать saldo(тут account, в АБС видел ledger) по дням(неделям/месяцам) и считать его накопительным итогом при этом для проведения платёжного документа (тут journal, но, вроде как обычно это payment_document) можно считать входящий остаток как сумму закрытого периода по saldo + записи из task (мне чаще попадалось названное как journal) за незакрытые периоды. Это может помочь решить вопросы с производительностью.
p.s. названия не привычны мне, привык к тому что journal — это полупроводки, ledger/saldo — остатки account — справочник счетов, payment_document -платёжный документ (может быть иерархическим, т.е. заменяет и batch из статьи)

В контексте данной статьи account переводится как счет

Еще у такой архитектуры (когда все данные сводятся в одну таблицу Posting) есть такое огромное преимущество как высокая скорость подсчета балансов и простота генерации отчетов. Ну а факт что подсчет нужно вести от начала последнего открытого периода, ускоряет еще больше.
Да, про периоды важная штука которую легко забыть если пытаться сделать все самостоятельно. И огрести потом вычисления баланса по всему архиву записей, занимающие минуты и вешающие весь сервер.

Кажется тут происходит попытка изобретения Event Source'инга. Не надо так (с) Не нужны вам никакие cash book если все движения денег описаны транзакциями (записями), а перекладывание денег со счета на счет это всегда две транзакции ссылающиеся на общий обьект "причина" или типа того.

Ну так вопрос: если мы вносим деньги в систему, то с какого счета мы должны деньги переложить на счет получателя? Вот он cash book и есть.

Ну и главное тут на самом деле — не столько организовать транзакции (их можно было бы и без двойной записи легко сделать), сколько организовать контроль за их корректностью. А правила нулевых суммарных балансов — очень мощная штука, позволяющая легко искать ошибки и проверять валидность состояния системы. Очень, просто супер легко, одним запросом в духе «сложить все значения amount с timestamp в указанном периоде и сравнить с нулем».

У меня в последнее время большие сомнения, что физическая двойная запись оправдана в современных ИС. Таблиц транзакций и операций с ид транзакции, суммой, валютой, счётом дебета и счётом кредита вполне достаточно.
Любая транзакция — 1+N вставок только в две таблицы, где N — количество операций в транзакции, в простых случаях одна только. В целом можно обойтись даже одной вставкой в одну таблицу, если база поддерживает эффективную работу со сложными типами данных типа объектов и их массивов.


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


P. S. Архаичный перевод архаичного cash book — амбарная книга

Неочевидно, как по одной таблице безболезненно собирать балансы и оборотку.
В бухгалтерии контроль обычно на одном из первых мест стоит. И ошибки могут быть всегда — от ошибок ввода данных в систему или багов в ПО до аппаратных сбоев.

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

Поэтому все и перестраховываются. Опять же повторю мнение из комментария выше — двойная запись нужна не для оптимальной организации хранения данных (для этого и простая запись подойдет), а для гарантий надежности и верифицируемости. Причем в такой записи эти задачи (та же проверка нулевого общего баланса) делаются тривиально, быстро и просто одним запросом к БД. Вот в чем основной профит такой архитектуры.
Как не понимал принцип двойной записи бухгалтерии, так и не понимаю после прочтения Вашей статья. Как нахрен понять в каком случае нужно писать в дебет, а в каком случае в кредит? Вот к примеру операция:
(c ) Добавим еще один аккаунт Паттел и переведем 100£ ему от Смита. Для этого нам понадобится создать дебет на эту сумму у Смита и кредит у Паттела.

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

Но поскольку такая вот система предназначена для работы только с пассивными счетами, для них все просто. Кредит — то что дали (плюс), дебет — то что забрали (минус). Слово дебет вообще происходит от слова debt, долг.

Это надо просто запомнить.

Ну а дальше любая операция просто состоит из того, что мы берем деньги в одном месте (и записываем их туда с минусом, дебет), а в другое место их кладем — и туда записываем с плюсом, кредит. Если операция связана с движением денег между границами системы и окружающего мира — в качестве одного из счетов используем специальный cash book.
Блииин, спасибо! Хоть какая то крупица стала понятнее. Я же знал, что debt — долг, но не придавал этому никакого значения, теперь многое стало понятнее. Полностью согласен с Вашим высказыванием:
Если лезть в глубины бухучета — там вообще черт ногу сломит.
Я думал, что я один считаю бух учет адом адским, ведь все говорят — «Это же так просто!». Для меня это всегда мУка, вообще не понимаю что происходит. Кроме того есть ведь не только активные и пассивные счета, в мире 1С к примеру есть еще активно-пассивные и парные счета =0 Я вообще не понимаю зачем было изобретать двойную запись, я просто не могу этого понять…
Как я в комментариях выше писал, главная задача двойной записи — контроль за ошибками. Изобретали ее еще в средние века (точно даже неизвестно когда именно).

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

Активные счета — это то, что у нас есть и можно потрогать. Остатка по кредиту быть не может. Например, 50 счет — касса. Дебет — прибавляем, кредит — убавляем. Минус сколько-то денег в ней лежать не может — это верный признак ошибки бух. учета.

С пассивными счетами все наоборот. Они как бы со знаком минус. Это наши долги кому-то. Дебет увеличивает долг — т.е. минусует нас (плюс на минус дает минус), кредит — уменьшает долг, т.е. плюсует нас (минус на минус дает плюс). Соответственно, остаток может быть только по кредиту — мы либо чего-то должны, либо ничего. Дебетовый остаток — ошибка учета.
Все еще усложняется тем, что есть два вида счетов — активные и пассивные, для которых дебет с кредитом имеют противоположный смысл.

Более того, есть ещё и активно-пассивные счета, где сальдо может быть и дебетовое, и кредитовое. Например, расчеты с контрагентами :)
Может есть у кого ссылки на материал, который позволяет раскрыть тайный смысл двойной записи в бух. учете и понять его мышлением IT-шника со всеми тонкостями и примерами (активные, пассивные, активно-пассивные, парные счета, какой в каких случаях используется и почему и т.д)?
А у вас есть ссылки на материал, который позволяет раскрыть тайный смысл паттернов разработки софта и понять его мышлением бухгалтера со всеми тонкостями и примерами (observer, decorator, DI, какой в каких случаях используется и почему и т.д.)? ;)

Там же два правила, квадрат 2х2 и "дебет слева, кредит справа", но я первый никак запомнить не могу :)
Из жизни, т.к. cr при сортировке идёт раньше db (debit) выгрузил как-то данные с обратным порядком записей, попросили больше так не делать, т.к. очень неудобно :)

"Алексей Виноградов
Азбука бухгалтерского учета. Что надо знать для работы с бухгалтерскими программами" Мне в свое время очень помогла эта книга, сразу говорю — она небольшая, но даже программист поймет основы.

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

Вопрос, а при изучении и создании такой сложной системы были готовые решения? Если были, то расскажите какие и почему отмели, например из-за стоимости или неподходящего функционала? спасибо
Мы делали систему учета балансов для электронного кошелька одного. До того там сперва какой-то один вендор был (уже забыл какой), потом Huawei. Но крупным вендорам было невыгодно работать с небольшой нигерийской компанией, при этом и заморочек было много (черный ящик без возможности допиливания). А мы для них уже много чего другого сделали, собственно почти все — веб портал, шлюзы в другие системы, обработку USSD запросов и т.п., они решили что тогда имеет смысл и ядро сделать нашими руками, благо у нас уже давняя история сотрудничества.
Ну мы за два месяца в ударном темпе его и нафигачили с нуля. Год уже работает, вроде полет нормальный.
Налоги и прочие сложности там не нужны были, чисто баланс кошельков пользователей.
Ясно, очень крутые вещи делаете. Прочитал «Нигерийские истории российского разработчика» с большим интересом! Тот пост от начала 2018 года, за это время ситуация, условия развивать бизнес стали лучше или всё примерно одно и тоже?
Я из этого бизнеса почти вышел сейчас, так как внятного дохода он так и не принес. Так, 500-1000$ долларов в месяц нерегулярно. Иногда делаю что-то за фиксированный прайс там.

Не могу понять пример с переводом 100 евро от Сёмы к Пете. Если Сёма вывел из системы 50 евро и они отразились в кассовой книге, то откуда у него взялось ещё 100 евро в дебете мимо кассовой книги? И как утверждение "-190£, отрицательная сумма балансов всех остальных аккаунтов" соотносится с требованием "Сумма всех значений во всей системе в любой момент времени должна давать ноль"?

Если Сёма вывел из системы 50 евро и они отразились в кассовой книге, то откуда у него взялось ещё 100 евро в дебете мимо кассовой книги?

Тут «вывел из системы» означает «обналичил», т.е. сначала положил на счёт 300, потом забрал 50, осталось 250. Из них перевёл 100, осталось 150, у Пети стало 100, Петя вывел 60, осталось 40.
И как утверждение "-190£, отрицательная сумма балансов всех остальных аккаунтов"

Мне кажется, это ошибка перевода, автор оригинала скорее всего там не минус, а тире написал. Баланс по кассе не -190, а 190. Активы по кассе 190, пассивы Сеня 150, Петя 40, итого тоже 190, баланс бьётся, главбух в экстазе.

Не, всё норм, я ниже отписался в ошибках своей логики. Спасибо :)

Разобрались с коллегой :) В общем, меня запутало что:
Я представлял кассовую книгу как историю транзакций, кредит как карту с деньгами, а дебет как наличку. Чтобы перевести от Сёмы к Пете 100 евро надо было бы их вывести с карты Сёмы и внести на карту Пети (через наличку или прямым переводом). И соответственно я ожидал, что сумма всех операций в кассовой книге должна быть равна нулю.


По факту же правильно рассматривать кассовую книгу как денежное хранилище в банке, а Сёму и Петю как его клиентов, переводящих средства без процентов. С точки зрения денежного хранилища операция перевода сотки от Сёмы к Пете ни на что не повлияла. А вот трата Петей средств Сёмы уже отразилось на системе. Таким образом итоговый баланс Пети и Сёмы равен 190 евро, а в кассовой книге -190 евро, что даёт 0 при сложении.


Скрытый текст

image

А что по поводу процентов, которые себе забирает платёжный агрегатор при вводе и выводе средств из системы (cash book)? Как их правильно учитывать? Или если я хочу удерживать процент с транзакций, которые происходят в пределах системы?

Просто заводите отдельный счет, на который переводите проценты и комиссии обычным образом.

Т.е у нас будет одна запись в journal, которая связывает несколько записей в posting:


cash book: -300
payment provider X: +10
system fees: +10
vasya: +280

Верно?

Отличная статья и теория, и практика, и даже примерный проект реализации - много полезного. Согласен с автором, во всем том, что написано во введении, что в сети сложно найти информацию по вопросу. Либо ее мало, либо нужны особые поисковые запросы, чтобы найти. А если находишь, то она бывает весьма специфической и сложной для понимания. Здесь, например, в ответе есть фундаментальный подход вопросу https://stackoverflow.com/questions/59432964/relational-data-model-for-double-entry-accounting/59465148#59465148 но если не работаешь в бухгалтерской сфере или банковской, понять его сложно. По поводу вашей статьи, остался не раскрытым вопрос о том как должна на практике выглядеть batch таблица? Понятно, что каждый ее может сам организовать, но все-таки остальные компоненты полностью разложены, в том числе по таблицам базы данных, а batch осталось несколько неясным, что в нее писать на примере вашей статьи.

Да, не очень понятно зачем там эта batch таблица, мы в итоге без нее обходились. Так как записи и так группируются в бизнес-транзакции в таблице journal. Возможно это имеет смысл для упрощения ручного ввода большого объема данных в систему, когда в одной транзакции очень много записей, и лучше вводить их кусочками.

В posting таблице есть journal id, хотя на схеме posting идет первой. Posting -> Journal -> Batch. Journal как доп проверка для posting. Получается journal запись первой создается, из этой записи берется id и используется в posting?

Ну да, создаем операцию/транзакцию, делаем запись в journal, дальше накидываем в нее записи о самих переводах в posting.

Sign up to leave a comment.

Articles