GithubHelp home page GithubHelp logo

epogrebnyak / abacus Goto Github PK

View Code? Open in Web Editor NEW
56.0 3.0 3.0 2.93 MB

A minimal yet valid double-entry accounting system in Python or command line.

Home Page: https://epogrebnyak.github.io/abacus/

License: GNU General Public License v3.0

Python 97.44% Just 0.91% Shell 0.34% Batchfile 1.31%
accounting finance ledger double-entry-accounting acca cpa gaap ifrs tax plaintext-accounting

abacus's Introduction

Hi I'm Evgeniy Pogrebnyak

Telegram VKontakte Gmail Twitter Follow

Work

Publications

Datasets

I maintain several open datasets and codebooks:

Name Content Years Github
boo Russian firms annual financial statements 2012-2018
weo World Economic Outlook releases as pandas dataframes 2007+
ssg Static site generators popularity on Github 2021

Read more here

Publications

Topics: power markets, economics of automotive industry, industrial and competition policies, exchange rates, sustainable development goals.

Thesis: Policy parameters and regulatory controls for Russian competitive electricity market

Also published: dictionary of Russian business slang.

Other links

  • My bio in Russian here

abacus's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

abacus's Issues

Add unit tests (pytest) for api.py

abacus/experimental/api.py

Lines 155 to 168 in fe716fe

# TODO: create test_api.py and code with asserts to test_api.py as pytest unit tests
# use nsames like test_add_tests(),
# the following command should run as a test: poetry run pytest experimental
# if you install just command runner `just go` should also pass
assert ChartCommand(Chart()).add_assets(["cash"]).chart.assets == ["cash"]
assert ChartCommand(Chart()).add_capital(["equity"]).chart.equity == ["equity"]
assert ChartCommand(Chart()).add_liabilities(["ap"]).chart.liabilities == ["ap"]
assert ChartCommand(Chart()).add_income(["sales"]).chart.income == ["sales"]
assert ChartCommand(Chart()).add_expenses(["cogs"]).chart.expenses == ["cogs"]
with pytest.raises(AbacusError):
ChartCommand(Chart())._add("no such attribute", [])
# end of TODO here

add retained_earnings_account to Chart

  • Chart becomes the following:
chart = Chart(
    assets=["cash"],
    expenses=["overhead"],
    equity=["equity", "retained_earnings"],
    liabilities=["dividend_payable"],
    income=["sales"],
    # new field
    retained_earnings_account = "retained_earnings"
)
  • retained_earnings_account must be in self.equity, raise exception otherwise
  • .close(...) method changes to .close()
  • class RetainedEarnings(Capital) used to match the account

add title to Entry (or other class)

e1 = Entry(dr="cash", cr="equity", amount=1000)  # pay in shareholder equity
e2 = Entry(dr="goods", cr="cash", amount=250)    # acquire goods worth 250
e3 = Entry(cr="goods", dr="cogs", amount=200)    # sell goods worth 200
e4 = Entry(cr="sales", dr="cash", amount=400)    # for 400 in cash
e5 = Entry(cr="cash", dr="sga", amount=50)       # administrative expenses

similar software (for README/docs)

  • medici is a ledger store, allows compound entries and very optimised for high loads (> 30k entries), does not enforce chart of accounts conventions
  • pyluca, actively developed and has practical use in mind, somewhat different structure of classes in mind and no contra accounts I think
  • ledger.py - started with Python 2, once a hledger rival, good docs and explaination, last commit in 2018

Check out a few open source ERPs with accounting functionality under double-entry-accounting tag on Github.

better Ledger

@dataclass
class Book:
   retained_earnings_account: str 
   start_ledger: Ledger
   regular_entries: List[Entry]
   adjustment_enties: List[Entry]
   dividend_enties: List[Entry]
   post_close_entries: List[Entry]
   reverse_entries: List[Entry]
   
    # at any point for any ledger ledger.trail_balance() works 
    # ledger.trail_balance() 
    # can also check ledger.trail_balance_credit_accounts().total()  == ledger.trail_balance_debit_accounts().total() 

   def ledger_after_regular_entries(self) -> Ledger:
       #  start_ledger + regular_entries
       pass

   def create_ledger_before_adjustment_entries(self) -> Ledger:
      # start_ledger + regular_entries + close contra accounts to income/expense

   def create_ledger_after_adjustment_entries(self) -> Ledger:
      # create_ledger_before_adjustment_entries + adjustment_enties
      pass

   def create_ledger_for_income_statement(self)
     # same as create_ledger_after_adjustment_entries()
     # nothing should affect income or expense at this point 
     # income and expense account balances will be zero after this step
     pass

   def  create_ledger_after_income_and_expense_accounts_close(self) -> Ledger:
       # create_ledger_after_adjustment_entries + close income and expense
       # here we know profit, at income summary account
       pass

   def create_ledger_after_income_summary_account_close(self) -> Ledger:
       # create_ledger_after_adjustment_entries + close income and expense + close income summary to retained_earnings
       pass 

   def create_ledger_after_dividend(self) -> Ledger:
      # create_ledger_after_income_summary_account_close + dividend_entries
       pass 
      
   def create_ledger_after_closing_entries(self) -> Ledger:
      # create_ledger_after_dividend+ any closing entries
     pass

   def create_ledger_for_balance_sheet(self) -> Ledger:
     # create_ledger_after_closing_entries + close perm contra accounts (for assets, equity and liabilities)
     pass

   def create_ledger_after_reverse_entries():
     # create_ledger_for_balance_sheet() +reverse_entries, if any 


 

transaction based interface

We currently post entries on a double entry level, but on a business event level there can be several transactions and entries per event, sharing same reference or title.

event("Sales contract #2#).post("sales, "cash",200).post(cogs).post(payment).post(cashback)

RAS there?

Hey, what a cool job, thanks much.
do you plan to make an make_ledger method to make a possibility for RAS accounting?
I'm asking because accounting_types.py looks like a great iunterface for any type of accounting.

Textbook example: Yazici advertising

Intermediate Accounting: IFRS Edition Fourth Edition DONALD E. KIESO PhD, CPA Northern Illinois University DeKalb, Illinois JERRY J. WEYGANDT PhD, CPA University of Wisconsin—Madison Madison, Wisconsin TERRY D. WARFIELD, PhD University of Wisconsin—Madison Madison, Wisconsin

add Pipeline class

methods change pipeline.entries:

pipeline.add_entries()
pipeline.close_contra_accounts(related_to=Income)
pipeline.close_contra_accounts(related_to=Expense)
pipeline.close_temporary_contra_accounts() # equivalent to two above
# --- at this point income statement can be calcultaed
# income and expense will need calculation of ledger
pipeline.close_income()   # raise error if balance of contraccount for income not zero
pipeline.close_expense()  # raise error if balance of contraccount for expenses not zero
pipeline.close_income_and_expense() # raise error if isa balance not zero 
pipeline.close_income_summary_account()
pipeline.close_contra_accounts(related_to=Asset)
pipeline.close_contra_accounts(related_to=Liability)
pipeline.close_contra_accounts(related_to=Capital)
pipeline.close_permanent_contra_accounts()  # equivalent to three above
ledger.is_closed() # all permanent contra accounts are zero, income, expense and isa are zero
ledger.is_neeted() # is closed and permanent contraccounts are closed
# --- at this point balance sheet statement can be calcultaed

@dataclass
class Pipeline:
  """Construct a list of postings to a ledger using add_entries() method and methods for closing temporarily accounts or netting contraccounts."""
  start_ledger: Ledger
  entries: List[Entry] #= ...

  def run(self):
    return process_entries(self.start_ledger, self.entries)

add contra accounts

Contra accounts should:

  • be of debit and credit types
  • indicate where they are netted to
  • apply after trail balance before income and balance sheet report

cli: new syntax for account definition, `bx operation` command, `[ledger] do` and `report --all`

bx chart set --asset cash 
bx chart set --asset ar --title "Accounts receivable" --offset bad_debt
bx chart name bad_debt --title "Allowance for bad debts"
bx chart set --asset inventory
bx chart set --expense cogs --title "Cost of goods sold" 
bx chart set --expense sga --title "Selling expenses"
bx chart set --capital equity 
bx chart set --retained-earnings re
bx chart set --income sales --offset discounts voids
bx chart show

bx operation set capitalize --debit cash --credit equity --title "shareholder funds"
bx operation set acquire-goods --debit inventory --credit cash --title "acquired goods for cash"
bx operation set invoice --debit ar --credit sales --title "invoice" --requires cost
bx operation set cost --debit cogs --credit inventory --title "cost of sales"
bx operation set discount --debit discounts --credit ar --title "provided discount"
bx operation set accept-payment --debit cash --credit ar --title "accepted cash payment"
bx operation set salary --debit sga --credit cash --title "paid salary in cash"
bx operation show

bx start
bx post capitalize 1000 --title "Initial investment"
bx post acquire-goods 500 --title "Purchased goods for resale"
bx post invoice 440 discount 40 cost 250 --title "Sales contract #2023-1"
bx post accept-payment 200 --title "Cash payment on sales contract #2023-1"
bx post invoice 300 cost 120 accept-payment 300 --title "Sales contract #2023-2"
bx post salary 150 --title "Sales manager remuneration"
bx post --debit bad_debt --credit ar --amount 100 --title "Unrecoverable debt on contract #2023-1"
bx close
bx list

bx report --all
  • --reference or --refer
  • adjustment example (with trial balance)
  • post-close example
  • add operation title to entry title
  • add small hash for entry
  • --cash asset type
  • --current-asset, --non-current-asset asset type
  • --current-liability, --long-term-liability

chart for manufacturing company and assumptions

  • no payroll
  • no amortisation account
  • no trail balance
  • no subgroups in accounts
  • no maturity (short-long-term debt)
  • no accrued/deferred expenses
  • no multiple entries (many-to-many)
  • check account identifiers are unique
  • not a tree of accounts
  • no terminal
  • no closing entries and divident
  • no actual firms (Apple, Tesla)
  • can rename
  • use pandas for tables
chart = Chart(
    assets=["cash", "ar", "raw", "wip", "fg", "ppe"],
    equity=["eq", "re"],
    liabilities=["debt", "ap"],
    income=["sales"],
    expenses=["cogs", "sga", "rd", "interest"],
)

seriliazation of Ledger and Chart

We can use pydatnic to save ledger and load lender, as well as chart and list of entries. This will make abacus closer to working software.

Possible cli (for target release 0.5.0)

Operations:

  • accept chart of accoutns and produce an empty ledger
  • accept a ledger and list of regular entries to make trial balance
  • enter adjustment entries
  • close at period end, produce reports
  • post-close entries and reports again

Tentative CLI:

abacus create ledger chart.json > ledger.json
abacus create store --chart chart.json --ledger ledger.json --entries entries.json > 2023Q2.json
abacus read store 2023Q2.json [--ledger | --entries ] > ...
abacus create entry --dr <account> --cr <account> --amount 100 [--postclose | --adjustment | --reverse]
abacus post 2023Q2.json --dry-run
abacus mark | unmark store 2023Q2.json --business-end
abacus trial balance 2023Q2.json [--business-end | --after-adjustment | --after-close | --after-everything |  --after-reversal ] 
abacus income statement 2023Q2.json  [--business-end | --after-adjustment | --after-close | --after-everything | --after-reversal ] 
abacus balance sheet 2023Q2.json  [--business-end | --after-adjustment | --after-close | --after-everything | after-reversal ] 
...
--net-contra-accounts=[all,temp,perm,income,expense,assets,liab,capital,equity]
--close-temp-accounts=[all,income,expense,retained_earninings] | abacus post ledger.json
abacus report balance sheet
... | abacus post ledger.json

mermaid workflow chart

flowchart LR
  A["Chart"] --> B
  S["AccountBalances\n(start of period)"] --> B("Ledger")
  B --> C
  subgraph "Accounting Period"
    C["List[BusinessEntry]"]
  end 
  C --> F
  subgraph "Period Closing"
     F["List[AdjustmentEntry]"] 
     D["List[ClosingEntry]"]
  end
  F --> D
  D --> E(Ledger)
  E --> R1
  E --> R2
  E --> R3
  subgraph Reports
    R1(BalanceSheet)
    R2(IncomeStatement)
    R3("AccountBalances\n(end of period)")
  end

add .print() and .rich_print(width=80) methods to reports

Should encapsulate the following:

income_statement = book.income_statement()
balance_sheet = book.balance_sheet()
tv = PlainTextViewer(rename_dict=chart.names)
tv.print(balance_sheet)
tv.print(income_statement)

rv = RichViewer(rename_dict=chart.names, width=80)
rv.print(balance_sheet)
rv.print(income_statement)

Resuting code:

income_statement.print()
income_statement.rich_print(width=85)
balance_sheet.print()
balance_sheet.rich_print()

Change Netting class and ContraAccount class using `name_after_netting` and `counterpart` fields

@dataclass
class RegularAccount(Account):
"""Account with information which contra accounts must be netted with this account."""
netting: Optional[Netting] = None
def safe_copy(self):
netting = self.netting.safe_copy() if self.netting else None
return self.__class__(self.debits.copy(), self.credits.copy(), netting)

class NettableAccount(Account):
    """`NettableAccount` is an account that may have associated contra accounts.
    If an account has associated contra accounts, the account name will 
    need to change to *resulting_name* after netting of associated contra accounts is complete.
    For exasmple `sales` account name may change to comething like `net_sales`.
    If *resulting_name* is `None`, then there are no contra accounts to be netted. 
    """
    resulting_name: Optional[AccountName] 

class ContraAccount(Account):
    counterpart_name: AccountName

make demo/set.sh or demo/set.bat

We are creating a new version on the command line interface and will need some tests to invoke new commands.
Currently there is demo/chart.bat that should work. Your task is to make a demo demo/set.sh or demo/set.bat (preferred)
based onthis script. Add this script to justfile test command. Run the test and in comment here write out on what line does the test script fail (I think 2nd line).

You can also write out next steps on how you would proceed next with implementing new set command.

bx chart set --asset cash 
bx chart set --asset ar --title "Accounts receivable" --offset bad_debt
bx chart name bad_debt --title "Allowance for bad debts"
bx chart set --asset inventory
bx chart set --expense cogs --title "Cost of goods sold" 
bx chart set --expense sga --title "Selling expenses"
bx chart set --capital equity 
bx chart set --retained-earnings re
bx chart set --income sales --offset discounts voids
bx chart show

Vote about what next to add

  • --explain flag
  • faster csv backend instaed of json for postings
  • export to and import from hledger format
  • quiz command
  • web version with streamlit
  • export to and import from Excel
  • application with text user interface (TUI)
  • interactive prompts
  • better docs
  • more textbook examples
  • keep as is - you cannot keep up with other packages or websites

review and simplify CLI

bx init .   
cat ./abacus.json # {"chart": "chart.json", "entries": "entries.json"}
bx chart add --cash cash 
bx chart add --asset ar prepaid_rent goods
bx chart add --equity equity
bx chart set --retained-earnings re
bx chart add --liability ap dividend_due
bx chart add --income sales
bx chart offset sales refunds
bx chart add --expense cogs sga
bx chart name ar "Accounts recievable"
bx chart name ap "Accounts payable"
bx chart name cogs "Cost of goods sold"
bx chart name sga "Selling, general and adm. expenses"
bx chart show [--json] [--validate]
echo {} > start_balances.json
bx ledger start start_balances.json 
bx ledger post cash equity 500 --title "Initial investment"
bx ledger post prepaid_rent cash 120 "--title Prepay storage facility for 12 months"
bx ledger post goods cash 380 --title "Acquire goods for resale"
bx ledger post cogs goods 250 --title "Register cost of sales on contract 2023-1"
bx ledger post ar sales 360 --title "Register sales on contract 2023-1" 
bx ledger post cash ar 300 --title "Accept cash"
bx ledger post sga cash 60 --title "Pay sales team"
bx ledger show (--all | --last | --recent n | --date range | --id hash) [--csv]
bx trial-balance [--json]
bx ledger adjust sga prepaid_rent 80 --title "Expense 8 months of storage rent"
bx ledger close
bx ledger post-close re dividend_due 20
bx account re --assert-balance 0
bx report --balance-sheet
bx report --income-statement
bx ledger balances --json > end_balances.json   

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.