Типовая схема биллинга

Я как человек измученный различными АСР и сам принимавший участие в разработке АСР регулярно сталкивался с отсутствием какой-то эталонной схемы, которую можно было бы посмотреть для оценки АСР, а так же перед созданием своей АСР. В сети есть некоторое количество работ по этой теме к примеру я в свое время при написании диплома курил вот этот вот труд Методы моделирования и разработки биллинговых систем. Диплом же и есть диплом и тащить из него схемы странное занятие, так-как реалиям оно отвечает от слова никак.

В итоге сейчас уже имея довольно большой опыт работы с АСР я решил сделать эталонную схему. Но так-как я все же один человек, то для ее улучшения, надо показать другим и подвергнуть критике. Так что я надеюсь их эта тема заинтересует и они мне расскажут где у меня плохо, а где хорошо. В дальнейшем когда схема будет уже как-то внятно сформирована, я выложу ее на github с документацией, ER-диаграммой и готовой схемой для РСУБД (скорее всего это будет PostgreSQL).

Прежде чем переходить к описанию приведу схему в виде ER-диаграммы

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

  • Договор клиента и ведение его баланса
  • Начисление услуг и их стоимость
  • Предоставление скидок
  • Проведение платежей
  • Перевод денежных средств
  • Ввод остатков клиента из другой системы
  • Счета выставленные клиенту и их погашение

Прежде чем переходить к описанию я укажу те соглашения которые используются во всей схеме:

Внешние ключи идут в формате id_<имя таблицы>. В случае если ссылок несколько или идут на саму таблицу допускается или дополнение к названию вида id_<имя таблицы>_<поясняющее дополнение> или же id_<поясняющее дополнение>. В качестве примера id_trx_from, id_trx_to для первого случая и id_revoke, id_revoked для второго случая в таблице bill.transfer.

Поля с деньгами определены как numeric(10,2). Если есть возражения почему так не стоит делать, я желаю их услышать.

Поля с датой имеют префикс dt в обязательном порядке. Поля с датой и временем имеют префикс ts в обязательном порядке.

В случае если имеется временной интервал (dtfrom, dtto или tsfrom, tsto), то первая дата всегда задана и по умолчанию равна now(), вторая дата может быть пустой и в этом случае интервал считается действующим на данный момент.

В части случаев у справочников вместо числового первичного ключа используется текстовый мнемонический ключ. Такие ключи обозначены как sid. Используется исключительно для удобства при работе с данными напрямую через консоль РСУБД.

Ну а теперь переходим к описанию схемы. Для начала перечислю опорные таблицы:

  • Договор (bill.contract) - описание договора в самом его минимальном с моей точки зрения проявлении.
  • Проведенные операции (bill.trx) - проведенная на договоре операция. Просто сумма денег в плюс или минус на счету.
  • Отчетные периоды (bill.period) - отчетный период в бухгалтерском смысле слова. Хотя и содержит дату начала и дату завершения, на самом деле практически всегда месяц.
  • Документы к оплате (bill.invoce) - выставленный клиенту по договору

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

Для начала пройдемся по таблицам которые уточняют операции:

  • Тип операции (bill.trxtype) - собственно указывает на тип операции. К примеру начисление (charge), платеж (payment), скидка (discount), перевод (transfer), остатки(remain). Да названия совпадают не случайно.
  • Остатки (bill.remain) - входящие остатки. Входящие остатки должны быть всегда. Иначе через год концов, что было при миграции не найдете. Да и в любой нормальной системе положено что клиент входит с нулем. Дополнительно это позволяет корректировать этот самый входящий остаток если к примеру в предыдущей системе он был не верный. Остаткам в типах операции trx соотвествует 'remain'
  • Платежи (bill.payment) - поступающие платежи. Дополнительно есть таблица с типами платежей (bill.paymenttype) для разделение безнал, нал и т.п.
  • Переводы (bill.transfer) - перевод денежных средств со счета на счет. Замечу, что переводы больше баланса делать не стоит.
  • Начисления (bill.charge) - начисления за потребленные или предоставляемые (при авансе) услуги.
  • Скидки (bill.discount) - скидки на услуги. Тут остановлюсь по подробнее. Хотя в целом в бух учете нет единого мнения как выражать скидку, я выражаю ее в качестве денежного эквивалента к определенному начислению. Это упрощает ее учет.

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

Теперь давайте обратим внимание следующий момент у всех уточняющих таблиц.

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

Остальные общие поля у этих таблиц я думаю понятны и пояснений не требуют. Если не понятно пишите в комментарии.

Переходим к остальным таблицам:

  • Услуги (bill.service) - услуги предоставляемые клиенту.
  • Единицы измерения (bill.unit) - Единицы измерения. К примеру можно взять из ОКЕИ.
  • Цены (bill.price) - Стоимость услуги. Так-как может изменяться в течении времени, то имеется временной интервал.
  • История балансов (bill.balance) - история балансов с привязкой к проводимым операциям. Чисто справочная таблица для построения отчетов и общего удобства.

И дополнительно пройдемся по таблицам связывающим документ к оплате с проведенными операциями:

Услуги выставляемые в документе для оплаты (bill.invoice_trx) - услуги выставляемые в счете

Платежи покрывающие документ для оплаты (bill.invoice_cover_trx) - платежи покрывающие счет, amount указывает на остаток, после покрытия счета. Если платеж полностью ушел в оплату счета, то тут 0. Если частично, то остаток. Необходим исключительно из-за того что платеж может покрывать два документа по оплате.

Ну и пожалуй на этом пока все.

Теперь про то что в данный момент не учтено в схеме:

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

Мультивалютность - не совсем понятно надо ли вообще и если надо, то позволять смешивать платежи и начисления в различных валютах.

Комментарии

Изображение пользователя Serguei_Tarassov.

Для затравки

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

Куча полей типа ts и tscreated, как я понимаю, технические и соответствуют времени последней модификации и создания записи. Их, в принципе, можно опустить, указав, что они используются в таблицах по списку. Можно использовать для этой цели одну системную таблицу (см. описание в книжке, глава "Реестр объектов и аудит", стр. 143) - реестр, вынеся в прикладные только "время модификации" для производительности.

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

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

Ответы на затравку

[quote=Serguei_Tarassov]Небольшая критика формата: на схеме не вполне очевидны связи, неплохо указывать на линии выражение, т.е. какой ключ соответствуют внешнему.
[/quote]
К сожалению PowerArchitect это не умеет. Но зато умеет это делать в своем редакторе. Можно нажать на связь и она покажет оба ключа в таблицах.

[quote=Serguei_Tarassov]
Куча полей типа ts и tscreated, как я понимаю, технические и соответствуют времени последней модификации и создания записи. Их, в принципе, можно опустить, указав, что они используются в таблицах по списку.
[/quote]
Учту

[quote]
Можно использовать для этой цели одну системную таблицу (см. описание в книжке, глава "Реестр объектов и аудит", стр. 143) - реестр, вынеся в прикладные только "время модификации" для производительности.
[/quote]
Скажем к сильному улучшению производительности это не приведет, а вот к увеличению связей и усложнению запросов приведет.

[quote=Serguei_Tarassov]
Баланс. По моим представлениям (бухгалтерским) расположение хронологических атрибутов должно быть зеркальное. Транзакция - это проводка по-русски. Соответственно, дату и период имеет она, они же суть история платежей и трат (списаний и приходов, т.е. движения денежных средств). А в балансах хранится агрегация (сумма) проводок за период. Это позволяет, например, контролировать неотрицательность или приближение баланса к порогу в реальном времени.
[/quote]
Это бухгалтерское понятие баланса. У меня же таблица балансов это история изменения баланса при проведении проводок. Указанная вами таблица из нее вычисляется весьма легко. Но так-как мне уже про это не первый человек говорит, надо видимо ввести такую таблицу вместо текущей, а balance переименовать balancehistory к примеру.

[quote=Serguei_Tarassov]
В этом варианте таблица остатков получается избыточной, т.к. остаток на любой период можно получить по агрегированному балансу в разрезе клиента (контракта).[/quote]
Я придерживаюсь мысли что баланс должен быть вычисляем из тех проводок что были по клиенту. Я слишком часто сталкивался с тем, что балансу в системе верить нельзя.

Изображение пользователя Serguei_Tarassov.

Продолжим понемногу

Введение реестра не усложнит запросы, зато появится возможность делать ранее труднодоступные, например "выбрать все сущности в системе независимо от их типа, созданные позже даты". Или то же самое, но по заданному множеству типов.

Поля с деньгами надо определять с 4 знаками после запятой, чтобы уменьшить погрешности вычислений. "Стандартный" тип money это numeric(18,4).

Тип ключей bigint не обоснован. Если планируются массивы с миллиардами записей, то имеет смысл его использовать, но только для ключей этих таблиц. В остальных - обычный целочисленный int (до 2 млрд. с хвостиком) и smallint (до 32768), а то и tinyint (byte, до 255 значений). В частности, ключ типа varchar меняется на tinyint. Опять сошлюсь на главу "Первичный и прочие ключи" из книжки, чтобы не копировать текст оттуда.

И вот что меня беспокоит: я нигде не вижу собственно счета. Что его заменяет, контракт? Но тогда имеет место смешение документооборота и бухгалтерии, что не есть хорошо. Бухгалтерии не в налоговом смысле, а в смысле абстрактной механики учета с использованием регистров и проводок. Вот смотрите, есть старенькая статья, где в разрезы проводок (и баланса) можно вставить по-моему все описанные у вас размерности. Эта схема решит ваши учетные потребности, сверху вы накрутите сколько угодно типов документов, но механика не изменится. В каждый момент времени есть полная история операций и баланс по ним, который можно стереть и восстановить заново из операций (баланс служит для предвычислений, фактически, для кэша в техническом смысле слова).

Уточнения по предметной области

[quote=Serguei_Tarassov]Введение реестра не усложнит запросы, зато появится возможность делать ранее труднодоступные, например "выбрать все сущности в системе независимо от их типа, созданные позже даты". Или то же самое, но по заданному множеству типов.
[/quote]
Для этого есть таблица операции (bill.trx). В других местах это не требуется.

[quote=Serguei_Tarassov]
Поля с деньгами надо определять с 4 знаками после запятой, чтобы уменьшить погрешности вычислений. "Стандартный" тип money это numeric(18,4).
[/quote]
Ну вот да интересный вопрос. Я тоже интересовался этим моментом. Когда спрашивал большая часть склонилась к numeric с точностью 2, а не 4 но для вычислений предлагали использовать 4 и потом приводить.

[quote=Serguei_Tarassov]
Тип ключей bigint не обоснован. Если планируются массивы с миллиардами записей, то имеет смысл его использовать, но только для ключей этих таблиц. В остальных - обычный целочисленный int (до 2 млрд. с хвостиком) и smallint (до 32768), а то и tinyint (byte, до 255 значений). В частности, ключ типа varchar меняется на tinyint. Опять сошлюсь на главу "Первичный и прочие ключи" из книжки, чтобы не копировать текст оттуда.
[/quote]
Сейчас использование вместо bigint int это уже экономия на спичках. В любом случае система где будет работать база будет 64 битной и если это x86_64 ,то там int уже 64 бита, а не 32. Таким образом какого-то значительного выигрыша не получим. А вот когда у нас внезапно где-то произойдет упор в размерность ключа будет несколько грустно. Я в свое время кстати ловил такую проблему.

[quote=Serguei_Tarassov]
И вот что меня беспокоит: я нигде не вижу собственно счета. Что его заменяет, контракт? Но тогда имеет место смешение документооборота и бухгалтерии, что не есть хорошо. Бухгалтерии не в налоговом смысле, а в смысле абстрактной механики учета с использованием регистров и проводок.
[/quote]
Давайте немного тут притормозим. Это не бухгалтерская система, как бы это странно не звучало. Это АСР. Основные ее пользователи это клиенты и обслуживающий персонал. По этой причине она не должна использовать учет по счетам. Учет по счетам возникнет, после выгрузки данных из АСР в бухгалтерию. Выгрузка есть всегда, так-как АСР охватывает только одну часть хозяйственной деятельности. А именно предоставление и учет услуг клиентам. Остальная часть хозяйственной деятельности в любом случае ведется в какой либо ERP системе.

Изображение пользователя Serguei_Tarassov.

Разделение

[quote=norguhtar]Сейчас использование вместо bigint int это уже экономия на спичках. В любом случае система где будет работать база будет 64 битной и если это x86_64 ,то там int уже 64 бита, а не 32. Таким образом какого-то значительного выигрыша не получим. А вот когда у нас внезапно где-то произойдет упор в размерность ключа будет несколько грустно. Я в свое время кстати ловил такую проблему.[/quote]

Архитектура процессора и тип хранения не связаны. Тип int определяет 4 байта в файле на диске и только. Внутреннее представление в СУБД может быть совсем другим. Если по прикидкам получается "экономия на спичках" (т.е. размеры БД и индексов различаются на единицы процентов), то имеет смысл использовать последовательный UUID (который внутри int128). Это снимает все вопросы по синхронизации распределенной БД.

Вопрос по привидению 4 знаков для типа "деньги" решается на уровне отображения, а внутри они хранятся полностью. В выч.технике и в частности на калькуляторах, есть метод "скрытых разрядов", повышающих точность. При этом на индикаторе эти разряды не показываются.

[quote=norguhtar]Давайте немного тут притормозим. Это не бухгалтерская система, как бы это странно не звучало. Это АСР. Основные ее пользователи это клиенты и обслуживающий персонал. По этой причине она не должна использовать учет по счетам. Учет по счетам возникнет, после выгрузки данных из АСР в бухгалтерию. Выгрузка есть всегда, так-как АСР охватывает только одну часть хозяйственной деятельности. А именно предоставление и учет услуг клиентам. Остальная часть хозяйственной деятельности в любом случае ведется в какой либо ERP системе.[/quote]

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

Уточнение по схеме со счетами.

[quote=Serguei_Tarassov]
Архитектура процессора и тип хранения не связаны. Тип int определяет 4 байта в файле на диске и только. Внутреннее представление в СУБД может быть совсем другим. Если по прикидкам получается "экономия на спичках" (т.е. размеры БД и индексов различаются на единицы процентов), то имеет смысл использовать последовательный UUID (который внутри int128). Это снимает все вопросы по синхронизации распределенной БД.
[/quote]
Вообще использовать UUID идея хорошая, но это генераторы ключей в РСУБД пока не умеют.

[quote=Serguei_Tarassov]
Вопрос по привидению 4 знаков для типа "деньги" решается на уровне отображения, а внутри они хранятся полностью. В выч.технике и в частности на калькуляторах, есть метод "скрытых разрядов", повышающих точность. При этом на индикаторе эти разряды не показываются.
[/quote]
Тогда просто сделаю 4 знака и успокоюсь.

[quote=Serguei_Tarassov]
Если вы почитаете дискуссию по ссылке, то там у товарища возникло такое же непонимание в связи с термином "бухгалтерия", ассоциирующегося у большинства с тётками-счетоводами для налоговой отчетности. Можете не называть описанный там движок "бухгалтерией", суть от этого не изменится - это абстрактный учетный механизм для расчетных и учетных систем (оперативный и оперативно-аналитический контур). Поэтому мне кажется, что имеет смысл взять для учетного ядра "велосипед" с более продвинутой функциональностью, упрощающий некоторые моменты (не нужно явное хранение остатков) и четко разделяющий документы-операции от собственно учета.[/quote]
Я посмотрел схему и не вижу реальной пользы от ее применения. Есть какой нибудь пример когда эта схема выгоднее? Вида "к примеру тут вот меньше придется делать".

Изображение пользователя Serguei_Tarassov.

Пример

[quote=norguhtar]
Вообще использовать UUID идея хорошая, но это генераторы ключей в РСУБД пока не умеют.[/quote]

SQL Server умеет, начиная с 2008 версии (см. newsequentialid)

[quote=norguhtar]
Я посмотрел схему и не вижу реальной пользы от ее применения. Есть какой нибудь пример когда эта схема выгоднее? Вида "к примеру тут вот меньше придется делать".[/quote]

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

Операция (Первичный документ)
---
1 - Пополнение счета
    2015-04-15 15:30 
    Иванов 
    Контракт 1 
    300 руб 
2 - Услуга (звонок)  
    2015-04-16 12:45
    Иванов 
    Контракт 1 
    5 минут
    20 руб
3 - Перевод среств
    2015-04-16 12:45
    Иванов
    Контракт 1
    Контракт 2 (Петров)
    100 руб
 
 
Проводки
(С1 - счет-регистр "Счета клиентов")
--------
Счет | Период | Сумма | Основание | У1:Клиент | У2:Контракт | У3:Тип док
------------------------------------------------------------------------
С1   | 1      |  +300 | 1         | Иванов    | Контракт 1  | Пополнение 
С1   | 2      |   -20 | 2         | Иванов    | Контракт 1  | Звонок 
С1   | 2      |  -100 | 3         | Иванов    | Контракт 1  | Звонок 
С1   | 2      |  +100 | 3         | Петров    | Контракт 2  | Перевод
 
 
Баланс 
(В настройках агрегации для счета С1 используются только У1 и У2)
------
Счет | Период | Сумма | У1:Клиент | У2:Контракт
-----------------------------------------------
С1   | 1      |   300 | Иванов    | Контракт 1  
С1   | 2      |   180 | Иванов    | Контракт 1 
С1   | 2      |   100 | Петров    | Контракт 2 

Уточнение по счетам

[quote=Serguei_Tarassov]
SQL Server умеет, начиная с 2008 версии (см. newsequentialid)
[/quote]
У меня основной целевой будет все же PostgreSQL он пока не умеет.

[quote=Serguei_Tarassov]
Давайте рассмотрим простой пример (в нем даже нет двойной записи). Имея 2 таблицы (проводки и баланс) с настраиваемой агрегацией по уровням вы умещаете туда любой биллинг с любой детализацией, пригодной для оперативного анализа и генерации выходных документов (например, счетов) и отчетности.

Операция (Первичный документ)
---
1 - Пополнение счета
    2015-04-15 15:30 
    Иванов 
    Контракт 1 
    300 руб 
2 - Услуга (звонок)  
    2015-04-16 12:45
    Иванов 
    Контракт 1 
    5 минут
    20 руб
3 - Перевод среств
    2015-04-16 12:45
    Иванов
    Контракт 1
    Контракт 2 (Петров)
    100 руб
 
 
Проводки
(С1 - счет-регистр "Счета клиентов")
--------
Счет | Период | Сумма | Основание | У1:Клиент | У2:Контракт | У3:Тип док
------------------------------------------------------------------------
С1   | 1      |  +300 | 1         | Иванов    | Контракт 1  | Пополнение 
С1   | 2      |   -20 | 2         | Иванов    | Контракт 1  | Звонок 
С1   | 2      |  -100 | 3         | Иванов    | Контракт 1  | Звонок 
С1   | 2      |  +100 | 3         | Петров    | Контракт 2  | Перевод
 
 
Баланс 
(В настройках агрегации для счета С1 используются только У1 и У2)
------
Счет | Период | Сумма | У1:Клиент | У2:Контракт
-----------------------------------------------
С1   | 1      |   300 | Иванов    | Контракт 1  
С1   | 2      |   180 | Иванов    | Контракт 1 
С1   | 2      |   100 | Петров    | Контракт 2 
[/quote]

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

Двойная запись

Что-то я не догоняю с двойной записью. Если я правильно понимаю, то фактически если взять таблицу trx она представляет из себя как раз таблицу проводок на один счет под названием "Счета клиентов", только его не видно. Если мне требуется двойная запись, то мне надо их два "Счета клиентов" и "Счет компании". И в первом случае идет как указано у вас в C1 во втором наоборот, для поддержания двойной записи. Тут возникает вопрос нафига козе боян,так-как с течением времени эти счета особо меняться не будут. Я думал добавить два счета, на один из которых идут только дебет (только операции "типа платеж"), а на второй кредит (только операции типа " начисления").

Изображение пользователя Serguei_Tarassov.

Зависит от реализации

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

У двойной записи есть как минимум 2 реализации:
- писать проводку целиком (в таблице 2 колонки счетов дебета и кредита, соответственно)
- писать проводку "половинками" (одна колонка для счета + признак, при этом аналитика может быть разной для дебета и кредита, что не требуется во внешнем официальном бухучете, но может быть интересно во внутреннем, управленческом)

Нафига козе баян? Затем же, зачем и в 15 веке, для внутреннего контроля. Если клиент воспользовался услугой, то это событие фиксируется минимум с двух позиций, дающих в сумме с соответствующим знаком ноль:
- расход (услуга оказана)
- будущий приход (клиент должен рассчитаться)

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

Один счет

[quote=Serguei_Tarassov]Я собсно, с того и начал, что транзакции у вас суть бух.проводки, только в минимально реализованном виде. Но суть та же.[/quote]

Вопрос. Я сейчас склоняюсь к тому что бы в качестве счета просто использовать таблицу bill.trx, а в тип платежа операции добавить указание дебет или кредит.
С моей точки зрения будет вполне достаточно.

Изображение пользователя Serguei_Tarassov.

В частном случае

[quote=norguhtar]Вопрос. Я сейчас склоняюсь к тому что бы в качестве счета просто использовать таблицу bill.trx, а в тип платежа операции добавить указание дебет или кредит.
С моей точки зрения будет вполне достаточно.[/quote]

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

В АСР других операций быть и не может

[quote=Serguei_Tarassov]
В частном случае может быть и достаточно, но в общем тогда выходит, что вы реализуете регистрацию операций в основном на уровне первичных документов.
[/quote]
Ну вообще говоря в АСР ничего кроме первичных документов то и нет. Что начисления, что платежи являются первичными.

[quote=Serguei_Tarassov]
Соответственно, все последующие отчеты придется генерировать не по стройной системе из двух таблиц (проводки и баланс), а по куче таблиц, отражающих эту "первичку".[/quote]
По какой куче таблиц? Имеете ввиду по уточняющим документам? Можно пример когда это это проще делается по таблице проводки и баланса. И да в каком виде должна быть таблица баланса. Просто возможно я не понимаю что тут под таблицей баланса понимается.

Изображение пользователя Serguei_Tarassov.

Расчеты

[quote=norguhtar]По какой куче таблиц? Имеете ввиду по уточняющим документам? Можно пример когда это это проще делается по таблице проводки и баланса. И да в каком виде должна быть таблица баланса. Просто возможно я не понимаю что тут под таблицей баланса понимается.[/quote]

Таблицы проводок и баланса кратко описаны в комментарии выше, более подробная реализация в статье.

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

SELECT SUM(Сумма) 
FROM Проводки 
WHERE Клиент = 'Иванов' 
      AND Счет = 'С1' 
      AND Тип_док IN ('Звонок', 'СМС', 'Переадресация')

Добавив в таблицы "Проводки" и "Баланс" колонку "Количество" вы сможете делать то же самое чтобы, например, узнать количество минут голосовой связи, только еще проще:

SELECT Количество -- хранится нарастающим итогом
FROM Баланс
WHERE Клиент = 'Иванов' 
      AND Период = <текущий>
      AND Счет = 'С2' 
      AND Тип_док IN ('Звонок')

Попробуйте посчитать это по первичке.

По проводке и балансу.

[quote=Serguei_Tarassov]

Таблицы проводок и баланса кратко описаны в комментарии выше, более подробная реализация в статье.

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

SELECT SUM(Сумма) 
FROM Проводки 
WHERE Клиент = 'Иванов' 
      AND Счет = 'С1' 
      AND Тип_док IN ('Звонок', 'СМС', 'Переадресация')

[/quote]
select sum(amount) from bill.trx
where id_contract=200 and sid_trxtype in ('charge', 'discount');

bill.trx как раз отвечает за проводки. Счет я думаю добавлять не требуется, так-как проще считать что в счет в биллинге один и выражается через bill.trx

[quote=Serguei_Tarassov]

Добавив в таблицы "Проводки" и "Баланс" колонку "Количество" вы сможете делать то же самое чтобы, например, узнать количество минут голосовой связи, только еще проще:
[/quote]
А для платежа в количество единицу класть?

[quote=Serguei_Tarassov]

SELECT Количество -- хранится нарастающим итогом
FROM Баланс
WHERE Клиент = 'Иванов' 
      AND Период = <текущий>
      AND Счет = 'С2' 
      AND Тип_док IN ('Звонок')

Попробуйте посчитать это по первичке.[/quote]
По bill.trx будет тоже самое что выше, только с группировкой. В целом полезно так-как уменьшает нагрузку на биллинг.

Изображение пользователя Serguei_Tarassov.

Аналог

[quote=norguhtar]
select sum(amount) from bill.trx
where id_contract=200 and sid_trxtype in ('charge', 'discount');

bill.trx как раз отвечает за проводки. Счет я думаю добавлять не требуется, так-как проще считать что в счет в биллинге один и выражается через bill.trx[/quote]

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

Вопрос же был о первичке. Поскольку разница видна и понятна, то вопрос отпадает.

[quote]А для платежа в количество единицу класть?[/quote]
Есть три основных подхода:
- записывать количество в некоторых базовых единицах, единых для всех количеств данного набора аналитик
- записывать реальное количество, единицы хранить разрезом аналитики, пересчет делать по ходу
- смешанный, например, хранить все перечисленное

[quote]По bill.trx будет тоже самое что выше, только с группировкой. В целом полезно так-как уменьшает нагрузку на биллинг.[/quote]

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

Уточнения по saldo

[quote=Serguei_Tarassov]
Потому что, как уже было сказано, trx у вас - очень упрощенный аналог проводок. Воспринимайте счет, как разрез аналитики, один из многих, но наиболее важный (он всегда участвует в выборках).

Вопрос же был о первичке. Поскольку разница видна и понятна, то вопрос отпадает.
[/quote]
Ну полный бухучет все же в АСР не требуется делать.

[quote=Serguei_Tarassov]
Есть три основных подхода:
- записывать количество в некоторых базовых единицах, единых для всех количеств данного набора аналитик
- записывать реальное количество, единицы хранить разрезом аналитики, пересчет делать по ходу
- смешанный, например, хранить все перечисленное
[/quote]
Ну тогда буду писать в штуках это проще.

[quote=Serguei_Tarassov]
Считать сумму вместо выборки строки - это увеличение, а не уменьшение нагрузки, для этого и введена таблица балансов (сальдо), кэширующая предвычисленные по разрезам величины. См. пример выше.[/quote]
Я имею ввиду ведение таблицы балансов. Да кстати если как она называется важно? Я вот думаю ее как bill.saldo определить и успокоиться.

Тип "деньги"

[quote=Serguei_Tarassov]Вопрос по привидению 4 знаков для типа "деньги" решается на уровне отображения, а внутри они хранятся полностью. В выч.технике и в частности на калькуляторах, есть метод "скрытых разрядов", повышающих точность. При этом на индикаторе эти разряды не показываются.[/quote]

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

Изображение пользователя Serguei_Tarassov.

Точность

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

Пример для PostgreSQL.

CREATE TABLE t1 (
  id int NOT NULL PRIMARY KEY,
  amt1 numeric(18,2),
  nds1 numeric(18,2),
  amt1_nds numeric(18,2),
  amt2 numeric(18,4),
  nds2 numeric(18,4),
  amt2_nds numeric(18,4)
);
 
INSERT INTO t1 (id, amt1_nds) VALUES
(1, 1.),
(2, 2.),
(3, 3.),
(4, 4.),
(5, 5.),
(6, 6.);
 
UPDATE t1 SET amt2_nds = amt1_nds;
 
UPDATE t1
SET nds1 = amt1_nds - (amt1_nds / 1.18),
    amt1 = amt1_nds / 1.18,
    nds2 = amt2_nds - (amt2_nds / 1.18),
    amt2 = amt2_nds / 1.18;
 
SELECT round(SUM(amt1) * 0.18, 2)      AS nds1_1,
       round(SUM(nds1), 2)             AS nds1_2,
       round(SUM(amt1) * 1.18, 2)      AS amt1_nds1,
       round(SUM(amt1) + SUM(nds1), 2) AS amt1_nds2,
       round(SUM(amt1_nds), 2)         AS amt1_nds3,
       round(SUM(amt2) * 0.18, 2)      AS nds2_1,
       round(SUM(nds2), 2)             AS nds2_2,
       round(SUM(amt2) * 1.18, 2)      AS amt2_nds1,
       round(SUM(amt2) + SUM(nds2), 2) AS amt2_nds2,
       round(SUM(amt2_nds), 2)         AS amt2_nds3
FROM t1;

Результат:

nds1_1 nds1_2 amt1_nds1 amt1_nds2 amt1_nds3 nds2_1 nds2_2 amt2_nds1 amt2_nds2 amt2_nds3
---------------------------------------------------------------------------------------
  3.20   3.21     20.99     21.00     21.00   3.20   3.20     21.00     21.00     21.00

Масштаб?

В тексте ссылки на диплом, 1998 года и телекоммуникации? Тогда две вещи:
1. Как я понимаю, речь не идет о "Big Data" биллинге пройвайдеров. Считать суммы за несколько миллиардов SMS в день в этой схеме явно не предусматривается.
2. Аналогично, здесь нет географической распределенности сервиса.

Тогда для кого это? Для мелкого и среднего бизнеса? Тогда "только биллинга" мало.

Если для провайдеров (типа интернет провайдеров), то таких уже всех съели "киты". У нас был Telix, потом его кипл Мультинекс, а его купила МТС Поэтому когда звонишь в саппорт могут ответить из Новосибирска :)

Масштаб до среднего.

[quote=DTsuranoff]В тексте ссылки на диплом, 1998 года и телекоммуникации? Тогда две вещи:
1. Как я понимаю, речь не идет о "Big Data" биллинге пройвайдеров. Считать суммы за несколько миллиардов SMS в день в этой схеме явно не предусматривается.
[/quote]
Там как минимум другие совсем схемы хранения данных будут.

[quote=DTsuranoff]
2. Аналогично, здесь нет географической распределенности сервиса.
[/quote]
Вопрос. А что по вашему тут должно быть для этого? С моей точки зрения в этой части ничего :)

[quote=DTsuranoff]
Тогда для кого это? Для мелкого и среднего бизнеса? Тогда "только биллинга" мало.
[/quote]
Весьма странные выводы. Особенно если учесть что это только расчетная часть.

[quote=DTsuranoff]
Если для провайдеров (типа интернет провайдеров), то таких уже всех съели "киты". У нас был Telix, потом его кипл Мультинекс, а его купила МТС Поэтому когда звонишь в саппорт могут ответить из Новосибирска :)[/quote]
Ну я тут случайно работаю в МТС. Далее вот на это обратите внимание http://www.comnews.ru/node/55042 В итоге сейчас обычно в каждом городе свой небольшой биллинг.

Резюмируя. Критикуя предлагай. Пока я вижу, вопросы "а кому это надо?". Могу ответить кратко "много кому" и использование подобной схемы возможно не только в рамках телекоммуникаций.

Изображение пользователя ipanshin.

Из недавнего

В рамках 1С при написании модуля аренды имел такой опыт. Понятно, что в 1С почти все уже работает: справочники номенклатуры и контрагентов, документы прихода платежей и пр. Однако как такового биллинга не присутствует. Что делать, если хочется делать начисления на клиента? Берем документ счет на оплату, который как оказалось присутствует, но никакого движения по регистрам учета не делает (что довольно странно). Да, и присоединяем к счету свои собственные регистры учета, по которым добавляем еще движения приходных кассового ордера и платежного поручения. Добавляю отчеты по созданным регистрам в разрезе клиентов и их договоров. Утверждаю, что в целом биллинг готов.

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

Посмотрел диаграмму. Я бы потянул эту диагамму за​​​ invoice (счет) вверх, а все остальное встало бы за счетами.

Не совсем так

[quote=ipanshin]Берем документ счет на оплату, который как оказалось присутствует, но никакого движения по регистрам учета не делает (что довольно странно). Да, и присоединяем к счету свои собственные регистры учета, по которым добавляем еще движения приходных кассового ордера и платежного поручения. Добавляю отчеты по созданным регистрам в разрезе клиентов и их договоров. Утверждаю, что в целом биллинг готов.
[/quote]
Дядя это ты с кем сейчас разговаривал? (c)
Это вот типичная проблема кстати АСР. Люди работающие с ним за исключением бухгалтеров все это весьма плохо понимают :)

[quote=ipanshin]
Самое смешное в этой задаче, что учет начислений на клиентов - это и есть выставленные счета и ничего более. Тарификация - это ничего более чем частные договора контрагентов, увязанные с документами условий поставок по договорам контрагентов. Вот собственно и весь биллинг, который успешно был реализован мной для отдела аренды.
[/quote]
Для отдела аренды да. А вот когда у вас внезапно появится авансовый метод расчета, то там та еще свистопляска будет. Т.е. схема учет начислений это выставленные счета работает только для кредитной схемы. Для аванса это не работает от слова никак :)

[quote=ipanshin]
Посмотрел диаграмму. Я бы потянул эту диагамму за​​​ invoice (счет) вверх, а все остальное встало бы за счетами.[/quote]
Будет возможность покрутить, я выложу новую схему в github в скором времени.