34
BUILDING A POWERFUL DOUBLE- ENTRY ACCOUNTING SYSTEM Lucas Cavalcanti

Building a powerful double entry accounting system

Embed Size (px)

Citation preview

Page 1: Building a powerful double entry accounting system

BUILDING A POWERFUL DOUBLE-ENTRY ACCOUNTING SYSTEMLucas Cavalcanti

Page 2: Building a powerful double entry accounting system

DOUBLE-ENTRY ACCOUNTING ANCIENT, UBIQUITOUS TECHNOLOGY

Page 3: Building a powerful double entry accounting system
Page 4: Building a powerful double entry accounting system
Page 5: Building a powerful double entry accounting system

USER BALANCES

Future bills

Open bill

Due bill

Available balance

Page 6: Building a powerful double entry accounting system

OPERATIONS

Purchases

Chargebacks

Payments

Page 7: Building a powerful double entry accounting system

BALANCE SHEET

Page 8: Building a powerful double entry accounting system

DEBIT CREDIT

General Ledger

USEFUL ABSTRACTIONS

Income statement

Cash flow statement

Balance sheet

LIABILITYASSET

EQUITY

Debits Credits

Page 9: Building a powerful double entry accounting system

PROPERTY: on each movement

Image © http://chestofbooks.com/business/reference/Home-Cyclopedia-Of-Business/Bill-Book.html

Page 10: Building a powerful double entry accounting system

Credits (CR): - $97 on liability payables (we will pay the merchant) - $3 Credit on P&L interchange (our profit)

- $100 on off-balance asset current-limit

EXAMPLE: A purchase of $100

Debits (DR): - $100 on asset settled (we will receive it from the customer)

- $100 on off-balance liability current-limit-cp

Page 11: Building a powerful double entry accounting system

EXAMPLE: A payment of $100

Debits (DR): - $100 on asset cash (we received it from the customer)

- $100 on off-balance asset current-limit

Credits (CR): - $100 on asset settled (we paid the purchase)

- $100 on off balance liability current-limit-cp

Page 12: Building a powerful double entry accounting system

DOUBLE ENTRY ACCOUNTING

EVENTS TRIGGERING MOVEMENTS • purchases • payments • bills

IMMUTABLE • append-only • entry log • can fix past by compensating

INVARIANTS • movements sums to zero • a book-account balance is sum of credits

and debits

Page 13: Building a powerful double entry accounting system

1 MOVEMENT => N ENTRIES

(s/defschema Entry {:entry/id s/Uuid :entry/amount PositiveAmount :entry/debit-account BookAccount :entry/credit-account BookAccount :entry/post-date LocalDate :entry/movement Movement})

So by design,

(ns double-entry.models.entry (:require [schema.core :as s]))

Page 14: Building a powerful double entry accounting system

1 BUSINESS EVENT => 1 MOVEMENT + META e.g new-purchase, new-payment, new-bill

(s/defschema Movement {:movement/id s/Uuid :movement/flow-id String :movement/topic Topic :movement/owner-account Account :movement/produced-at LocalDateTime :movement/consumed-at LocalDateTime :movement/user String})

(s/defschema Meta {:meta/id s/Uuid :meta/movement Movement :meta/entity (s/either Payment Purchase Bill ...)})

Page 15: Building a powerful double entry accounting system

DECLARATIVE RULES FOR MOVEMENTS

(ns common-schemata.wire)

(s/defschema Purchase {:purchase {:id s/Uuid :merchant String :amount t-money/PositiveAmount :interchange t-money/PositiveAmount :time LocalDateTime …}})

(s/defschema Payment {:payment {:id s/Uuid :amount t-money/PositiveAmount :post-date LocalDate …}})

Page 16: Building a powerful double entry accounting system

DECLARATIVE RULES FOR MOVEMENTS

(def new-purchase [{:entry/debit-account :book-account.asset/settled-brazil :entry/credit-account :book-account.liability/payable-brazil :entry/amount (comp :amount :purchase) :entry/post-date (comp time->date :time :purchase)}

{:entry/debit-account :book-account.liability/payable-brazil :entry/credit-account :book-account.profit-and-loss/interchange-brazil :entry/amount (comp :interchange :purchase) :entry/post-date (comp time->date :time :purchase)} {:entry/debit-account :book-account.liability/current-limit-counterparty :entry/credit-account :book-account.asset/current-limit :entry/amount (comp :amount :purchase) :entry/post-date (comp time->date :time :purchase)}])

Page 17: Building a powerful double entry accounting system

DECLARATIVE RULES FOR MOVEMENTS

(def new-payment [{:entry/debit-account :book-account.asset/transitory-bank :entry/credit-account :book-account.asset/settled-brazil :entry/amount (comp :amount :payment) :entry/post-date (comp :post-date :payment)} {:entry/debit-account :book-account.asset/current-limit :entry/credit-account :book-account.liability/current-limit-counterparty :entry/amount (comp :amount :payment) :entry/post-date (comp :post-date :purchase)}])

Page 18: Building a powerful double entry accounting system

[{:entry/id (uuid) :entry/amount 100.0M :entry/debit-account :book-account.asset/settled-brazil :entry/credit-account :book-account.liability/payable-brazil :entry/post-date #nu/date "2016-12-01" :entry/movement new-purchase} {:entry/id (uuid) :entry/amount 3.0M :entry/debit-account :book-account.liability/payable-brazil :entry/credit-account :book-account.profit-and-loss/interchange-brazil :entry/post-date #nu/date "2016-12-01" :entry/movement new-purchase}

{:entry/id (uuid) :entry/amount 100.0M :entry/debit-account :book-account.liability/current-limit-counterparty :entry/credit-account :book-account.asset/current-limit :entry/post-date #nu/date "2016-12-01" :entry/movement new-purchase}]

{:purchase {:id (uuid) :amount 100.0M :interchange 3.0M :time #nu/time "2016-12-01T13:37:42Z"}}

Rulebook

Page 19: Building a powerful double entry accounting system

[{:entry/id (uuid) :entry/amount 100.0M :entry/debit-account :book-account.asset/transitory-bank :entry/credit-account :book-account.asset/settled-brazil :entry/post-date #nu/date "2016-12-01" :entry/movement new-payment}

{:entry/id (uuid) :entry/amount 100.0M :entry/debit-account :book-account.asset/current-limit :entry/credit-account :book-account.liability/current-limit-counterparty :entry/post-date #nu/date "2016-12-01" :entry/movement new-payment}]

{:payment {:id (uuid) :amount 100.0M :post-date #nu/date "2016-12-01"}}

Rulebook

Page 20: Building a powerful double entry accounting system

Cumulative cache (balance sheet)

Event (log)

2 LOGS AND A CACHE

ACTUAL TIME audit trail / Datomic log “when did we know”day 0 day 30 day 90

SYSTEM OF RECORD TIME official version of events uses business-relevant “post dates” can correct after the fact

day 90

day 0day 0

day 30

Page 21: Building a powerful double entry accounting system

SANITY CHECKS / BUSINESS INVARIANTS

• Balances must be always positive or always negative

• Cannot have a “late” balance there is a “prepaid” balance

• A purchase should “move” exactly the purchase amount on assets and on current limit

Page 22: Building a powerful double entry accounting system

(def balances-property (prop/for-all [account (g/generator Account) events (gen/vector (gen/one-of [(g/generator Purchase) (g/generator Payment) ...]))] (->> (empty-db) (save-all! account events) :db-after (balances-are-positive!)))

(fact (tc/quick-check 50 balances-property) => (th/embeds {:result true}))

GENERATIVE TESTING(ns double-entry.controllers.rulebook-test (:require [midje.sweet :refer :all] [clojure.test.check.properties :as prop] [clojure.test.check :as tc] [schema-generators.generators :as g] [clojure.test.check.generators :as gen]))

Page 23: Building a powerful double entry accounting system

EVENT STREAM FOR A SINGLE CUSTOMER

Page 24: Building a powerful double entry accounting system

1. Business events generate idempotent Kafka messages

2. For each event, apply functions to convert the event data into a movement with 1+ entries • Movements balance by design • Movements associate provenance metadata

3. Pre-check guarantees invariants against db value

4. Eagerly cache resulting balances

debit-account credit-account amount

a/max-limit a/current-limit

l/max-limit-cpl/current-limit-cp

initial-limit

15000.0015000.00

CARD ISSUED

Page 25: Building a powerful double entry accounting system

debit-account credit-account amount

new-purchase / settle-brazil

100.00100.00

100.00100.00

CARD ISSUED FIRST PURCHASE

l/current-limit-cpa/unsettled

l/unsettled-cpa/settled-brazil

a/current-limitl/unsettled-cp

a/unsettledl/payable-brazil

Page 26: Building a powerful double entry accounting system

debit-account credit-account amount

closed-bill

15.00100.00

CARD ISSUED FIRST PURCHASE

a/minimum-paymenta/closed

l/minimum-payment-cpa/settled-brazil

FIRST BILL CLOSES

Page 27: Building a powerful double entry accounting system

debit-account credit-account amount

new-payment

10.0010.0010.00

CARD ISSUED FIRST PURCHASE

a/current-limitl/minimum-payment-cpa/transitory-bank

l/current-limit-cpa/minimum-paymenta/late

FIRST BILL CLOSES PARTIAL PAYMENT

Page 28: Building a powerful double entry accounting system

debit-account credit-account amount

new-adjustment

7.007.00

CARD ISSUED FIRST PURCHASE

l/current-limit-cpa/settled-financed

a/current-limite/revolving-interest

FIRST BILL CLOSES PARTIAL PAYMENT INTEREST ASSESSED

Page 29: Building a powerful double entry accounting system

debit-account credit-account amount

closed-bill

7.005.0014.557.00

90.005.007.0090.007.007.007.002.557.00

CARD ISSUED FIRST PURCHASE

a/latel/minimum-payment-cpa/minimum-paymenta/closed

a/current-limitl/minimum-payment-cpa/transitory-banka/transitory-bankl/prepaida/current-limitl/minimum-payment-cpl/minimum-payment-cpa/closed

a/closeda/minimum-paymentl/minimum-payment-cpa/settled-financed

l/current-limit-cpa/minimum-paymentl/prepaida/latea/closedl/current-limit-cpa/minimum-paymenta/minimum-paymenta/late

FIRST BILL CLOSES PARTIAL PAYMENT INTEREST ASSESSED PAYMENT IN FULL

Page 30: Building a powerful double entry accounting system

ZOOMING OUT

2015-01 2015-04 2015-07 2015-10 2015-01 2016-03

Page 31: Building a powerful double entry accounting system

DETECTING OPERATIONAL MISTAKES

USE CASES

MANAGEMENT ACCOUNTING • delinquency tables by cohort and aging

• receivables (domestic, foreign, financed)

• revenue per customer (interchange, interest, fx spread)

REPORTING • covenants

• regulatory

FINANCIAL ACCOUNTING • consolidate to ERP

Page 32: Building a powerful double entry accounting system

Declarative rules are extensible for additional financial products (e.g., already extending to rewards, debt financing)

Financial analysis applies at a micro level (negative balances, weird ratios, operational problems)

Business-specific invariants provide safety (declare mutually exclusive and impossible states,alert unexpected situations)

Generative testing finds real bugs

Service is shardable by customer account (no interactions between accounts)

WHAT DO WE LIKE?

Page 33: Building a powerful double entry accounting system

33

IF YOU ARE ENTRUSTED WITH A CUSTOMER’S FINANCIAL RELATIONSHIP, CONSIDER BUILDING A DOUBLE-ENTRY SYSTEM FOR YOUR DOMAIN

Page 34: Building a powerful double entry accounting system

Thank you!

Lucas Cavalcanti @lucascs

IF YOU CHOOSE TO DO SO, USE ALL THE POWER FUNCTIONAL PROGRAMMING GIVES YOU