Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save IvanRainbolt/8f69cf26d512353013da54934a3d5523 to your computer and use it in GitHub Desktop.
Save IvanRainbolt/8f69cf26d512353013da54934a3d5523 to your computer and use it in GitHub Desktop.
The first General Journal attempt | Using F# to model Financial Accounting
// related to blog post:
// The first General Journal attempt
// Using F# to model Financial Accounting
// https://crazyivan.blog/the-first-general-journal-attempt
open System
type Transaction = exn
type AccountData = { AccountName : string }
type Account =
| Assets of AccountData
| Liabilities of AccountData
| Equity of AccountData
| Revenue of AccountData
| Expenses of AccountData
type ChartOfAccounts = { Accounts : Account list }
type Debit =
{ Account : Account
Amount : decimal }
type Credit =
{ Account : Account
Amount : decimal }
type JournalEntry =
{ Date : DateOnly
Debits : Debit list
Credits : Credit list }
type Journal = { JournalEntries : JournalEntry list }
let Cash : Account = Assets {AccountName = "Cash"}
let Loan : Account = Liabilities {AccountName = "Loan"}
let IssuedEquity : Account = Equity {AccountName = "Issued Equity"}
let cashDebit :Debit = {Account=Cash;Amount=30000.00m}
let loanCredit :Credit = {Account=Loan;Amount=30000.00m}
let jeLoan = {Date=DateOnly(2022,01,01);Debits=[cashDebit];Credits=[loanCredit]}
let generalJournal = {JournalEntries=[jeLoan]}
printfn $"%A{generalJournal}"
let equityInjectionDebit :Debit = {Account=Cash;Amount=20000.00m}
let equityInjectionCredit :Credit = {Account=IssuedEquity;Amount=20000.00m}
let jeEquityInjection =
{ Date=DateOnly(2022,01,02)
Debits=[equityInjectionDebit]
Credits=[equityInjectionCredit]}
let generalJournal2 = {JournalEntries=(generalJournal.JournalEntries @ [jeEquityInjection])}
printfn $"%A{generalJournal2}"
@kulshekhar
Copy link

It might be a good idea to structure this such that a journal entry can be added only if the debits and credits for a transaction are equal.

The best way to do that might be to create a type that can only be instantiated from within its module and a creator function that validates the input and if everything is okay, creates the type. In concrete terms,

type Journal = { JournalEntries : JournalEntry list }

would turn to something like

type Journal = { JournalEntries : ValidatedJournalEntry list }

and ValidatedJournalEntry will be in its module

module ValidatedJournalEntry =
    type ValidatedJournalEntry = private ValidatedJournalEntry of ....

    let create date debits credits =
        // validate
        if valid then
            Some ValidatedJournalEntry ...
        else
            None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment