{"id":24737822,"url":"https://github.com/epogrebnyak/abacus-minimal","last_synced_at":"2025-08-03T00:35:15.140Z","repository":{"id":257901967,"uuid":"869738697","full_name":"epogrebnyak/abacus-minimal","owner":"epogrebnyak","description":"A minimal event-based ledger in Python that follows accounting rules","archived":false,"fork":false,"pushed_at":"2025-01-03T16:53:25.000Z","size":409,"stargazers_count":21,"open_issues_count":3,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-03T16:44:23.523Z","etag":null,"topics":["acca","accounting","cpa","gaap","ifrs"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/epogrebnyak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-08T19:57:16.000Z","updated_at":"2025-05-11T05:23:39.000Z","dependencies_parsed_at":"2024-10-23T20:24:31.224Z","dependency_job_id":"234a5ca9-f357-48de-93bb-69aa42646c41","html_url":"https://github.com/epogrebnyak/abacus-minimal","commit_stats":null,"previous_names":["epogrebnyak/abacus-minimal"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epogrebnyak%2Fabacus-minimal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epogrebnyak%2Fabacus-minimal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epogrebnyak%2Fabacus-minimal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epogrebnyak%2Fabacus-minimal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/epogrebnyak","download_url":"https://codeload.github.com/epogrebnyak/abacus-minimal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epogrebnyak%2Fabacus-minimal/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259219391,"owners_count":22823571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acca","accounting","cpa","gaap","ifrs"],"created_at":"2025-01-27T22:09:26.982Z","updated_at":"2025-06-11T07:09:19.566Z","avatar_url":"https://github.com/epogrebnyak.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# abacus-minimal\n\n![PyPI - Version](https://img.shields.io/pypi/v/abacus-minimal?color=blue)\n\nAccounting logic should be possible to express in readable code, right?\n\n`abacus-minimal` aims to be concise, correct and expressive in implementation of double entry book-keeping rules.\n\nProgress and features so far:\n\n- event-based ledger,\n- regular and contra accounts,\n- multiple entries,\n- period end closing,\n- income statement and balance sheet reports,\n- proxy reports before closing,\n- saving and loading data from JSON.\n\n## Install\n\n```bash\npip install abacus-minimal\n```\n\n\u003cdetails\u003e\n    \nLatest:\n\n```bash\npip install git+https://github.com/epogrebnyak/abacus-minimal.git\n```\n\n\u003c/details\u003e\n\n## Ledger as a sequence of events\n\n`abacus-minimal` provides an accounting ledger that is controlled by\nevents:\n\n- chart of account changes,\n- business transactions,\n- adjustment and reconciliation entries,\n- closing entries.\n\nGiven a sequence of events you can always recreate the ledger state.\n\n### Example\n\nIn code below account creation, three double entries and a command to close\naccounts are in the `events` list. This list is accepted by ledger, and from the\nledger you can see the account balances and reports.\n\n```python\nfrom abacus import Asset, Double, Equity, Expense, Income, Ledger, Close\n\ncreate_accounts = [\n    Asset(\"cash\"),\n    Equity(\"equity\"),\n    Equity(\"re\", title=\"Retained earnings\"),\n    Income(\"sales\"),\n    Expense(\"salaries\"),\n]\nbusiness_events = [\n    # Post entries\n    Double(\"cash\", \"equity\", 1000),\n    Double(\"cash\", \"sales\", 250),\n    Double(\"salaries\", \"cash\", 150),\n    # Close period\n    Close(earnings_account=\"re\")\n]\nevents = create_accounts + business_events\nledger = Ledger.from_list(events)\n```\n\nReports reflect the state of ledger:\n\n```python\nprint(ledger.balances)\nprint(ledger.income_statement())\nprint(ledger.balance_sheet())\n```\n\n### Primitive and compound events\n\nThere are six types of basic, or 'primitive', events in `abacus-minimal`:\n\n\u003c!-- prettier-ignore-start --\u003e\n\nPrimitive event   | What is does\n:----------------:|----------------\n`Add`       | Add an empty account of asset, equity, liability, income or expense type\n`Offset`    | Аdd a empty contra account that corresponds to an existing account\n`Drop`      | Deactivate an account\n`Debit`     | Debit an account with a specified amount\n`Credit`    | Credit an account with a specified amount\n`PeriodEnd` | Mark reporting period end\n\n\u003c!-- prettier-ignore-end --\u003e\n\nFrom example above you can extract the primitives as following:\n\n```python\nfor p in ledger.history.primitives:\n   print(p)\n```\n\nThere are also compound events. Every compound event\ncan be represented as a list of primitives.\n\n\u003c!-- prettier-ignore-start --\u003e\n\nCompound event | What it does                                 | Translates to a list of\n:-------------:|----------------------------------------------|-------------\n`Account`      | Specifies an account and its contra accounts | `Add` and `Offset`\n`Double` and `Multiple` | Represent accounting entries        | `Debit` and `Credit`\n`Transfer`     | Moves account balance from one account to another | `Double` and `Drop` \n`Close`        | Closes temporary accounts to aggregation account  | `PeriodEnd` and `Transfer`\n\n\u003c!-- prettier-ignore-end --\u003e\n\nNote that `Transfer` and `Close` are initially translated into other compound events,\nbut ultimately, they are further simplified into the primitives.\n\n## Ledger as class\n\nYou can also work with higher-level `Chart`, `Book` and `Entry` classes\nwithout looking at events level:\n\n- `Chart` holds a chart of accounts and can be saved and loaded from JSON (`chart.json`).\n- `Book` is created from chart, accepts entries to post to ledger and\n  also can be saved and loaded from JSON (`history.json`).\n- `Entry` is a posting to a ledger that can be a double or a multiple entry.\n\n### Example\n\nConsider an example where you need to process the following transactions:\n\n- a company gets €20000 equity investment from shareholders,\n- bills a client €1000 plus 20% [value added tax (VAT)][vat],\n- receives €600 installment payment,\n- makes a €150 refund,\n- pays €450 in salaries to the staff.\n\n[vat]: https://taxation-customs.ec.europa.eu/taxation/vat_en\n\n```python\nfrom abacus import Book, Chart, Entry\n\n# Create chart and ledger\nchart = Chart(\n    assets=[\"cash\", \"ar\"],\n    equity=[\"equity\"],\n    liabilities=[\"tax_due\"],\n    income=[\"services\"],\n    expenses=[\"salaries\"],\n    contra_accounts={\"services\": [\"refunds\"]},\n    retained_earnings=\"retained_earnings\",\n    current_earnings=\"current_earnings\")\nbook = Book.from_chart(chart)\n\n# Post entries\nentries = [\n    Entry(\"Shareholder investment\").double(\"cash\", \"equity\", 20_000),\n    Entry(\"Invoiced services\")\n       .debit(\"ar\", 1200)\n       .credit(\"services\", 1000)\n       .credit(\"tax_due\", 200),\n    Entry(\"Accepted payment\").double(\"cash\", \"ar\", 600),\n    Entry(\"Made refund\").double(\"refunds\", \"cash\", 150),\n    Entry(\"Paid salaries\").double(\"salaries\", \"cash\", 450),\n]\nbook.post_many(entries)\nprint(book.balances)\n\n# Close the period and show reports\nbook.close()\nprint(book.income_statement)\nprint(book.balance_sheet)\n```\n\n## Everything as JSON\n\nAll data structures used are serialisable. You can write code to create a chart of accounts and a ledger, save them to JSONs or pick up data from the JSON files and restore the ledger.\n\n```python\n# Save\nbook.save(\"chart.json\", \"history.json\", allow_overwrite=True)\n\n# Load and re-enter\nbook2 = Book.load_unsafe(\"chart.json\", \"history.json\")\n\nfrom pprint import pprint\npprint(book2) # may not fully identical to `book` yet\n```\n\n## Accounting concepts and workflow\n\n### Key concepts\n\nIn `abacus-minimal`:\n\n- there are regular accounts of five types (asset, liability, equity, income, expense);\n- contra accounts to regular accounts are possible (eg depreciation, discounts);\n- period end closes temporary accounts (income, expense and their associated contra accounts);\n- balance sheet and income statement are available before and after close;\n- post close entries are allowed on permanent accounts;\n\nSee a detailed list of assumptions and limitations [here](prose/assumptions.md).\n\n### Workflow\n\nThe steps for using `abacus-minimal` follow the steps of a typical accounting cycle:\n\n- create a chart of accounts,\n- open ledger for the current reporting period,\n- post account balances from the previous period,\n- post entries that reflect business transactions within the period,\n- post reconciliation and adjustment entries,\n- close accounts at reporting period end,\n- post entries after close,\n- show financial reports,\n- save account balances data for the next reporting period.\n\n### Accounting identity\n\n`abacus-minimal` adheres to the following interpretation of accounting identity.\n\n1. The value of company property, or assets, equals to shareholder and creditor claims on the company:\n\n```\nAssets = Equity + Liabilities\n```\n\nEquity is the residual claim on assets after creditors:\n\n```\nEquity = Assets - Liabilities\n```\n\n2. Company current earnings, or profit, is equal to income less expenses associated with generating this income:\n\n```\nCurrent Earnings = Income - Expenses\n```\n\nCurrent earnings accumulate to retained earnings:\n\n```\nRetained Earnings = Retained Earnings From Previous Period + Current Earnings\n```\n\n3. Equity consists of shareholder equity, other equity accounts and\n   retained earnings:\n\n```\nEquity = Shareholder Equity + Other Equity + Retained Earnings\n```\n\n4. Substituting we get a form of extended accounting equation:\n\n```\nAssets + Expenses =\n   Shareholder Equity + Other Equity + Retained Earnings + Income + Liabilities\n```\n\n5. We also add contra accounts:\n\n```\nAssets + Expenses + Contra Accounts To Equity, Income and Liabilitites =\n   Shareholder Equity + Other Equity + Retained Earnings + Income + Liabilities\n   + Contra Accounts to Assets and Expenses\n```\n\nOur book-keeping goal is to reflect business events as changes to the variables\nin this eqaution while maintaining the balance of it.\n\n## Alternatives\n\n`abacus-minimal` takes a lot of inspiration from the following projects:\n\n- [ledger](https://ledger-cli.org/),\n  [hledger](https://github.com/simonmichael/hledger),\n  [beancount](https://github.com/beancount/beancount)\n  and other [plain text accounting tools](https://plaintextaccounting.org/),\n- [medici](https://github.com/flash-oss/medici), a high performance ledger in JavaScript using Mongo database,\n- [microbooks](https://microbooks.io/) API and [python-accounting](https://github.com/ekmungai/python-accounting), a production-grade project, tightly coupled to a database.\n\n## Accounting knowledge\n\nIf you are totally new to accounting the suggested friendly course is \u003chttps://www.accountingcoach.com/\u003e.\n\nACCA and CPA are the international and the US professional qualifications and IFRS and US GAAP are the standards for accounting recognition, measurement and disclosure.\n\nA great overview of accounting concepts is at [IFRS Conceptual Framework for Financial Reporting][ifrs].\n\nPart B-G in the [ACCA syllabus for the FFA exam][ffa] are useful to review to work with `abacus-minimal`.\n\n[ifrs]: https://www.ifrs.org/content/dam/ifrs/publications/pdf-standards/english/2021/issued/part-a/conceptual-framework-for-financial-reporting.pdf\n[ffa]: https://www.accaglobal.com/content/dam/acca/global/PDF-students/acca/f3/studyguides/fa-ffa-syllabusandstudyguide-sept23-aug24.pdf\n\nTextbooks and articles:\n\n1. [A list of free and open source textbooks](https://library.sacredheart.edu/opentextbooks/accounting).\n2. [Frank Wood \"Business Accounting\"](https://www.google.com/search?q=Frank+Wood+%22Business+Accounting).\n3. [200 Years of Accounting History Dates and Events](https://maaw.info/AccountingHistoryDatesAndEvents.htm).\n\n## Project conventions\n\nI use [`just` command runner](https://github.com/casey/just) to automate code maintenance tasks in this project.\n\n`just test` and `just fix` scripts will run the following tools:\n\n- `pytest`\n- `mypy`\n- `black` and `isort --float-to-top` (probably should replace with `ruff format`)\n- `ruff check`\n- `prettier` for markdown formatting\n- `codedown` to extract Python code from README.md.\n\n`examples/readme.py` is overwritten by the `just readme` command.\n\nI use `poetry` as a package manager, but heard good things about `uv` that I want to try.\n\n## Changelog\n\n- `0.14.2` (2024-11-23) Added `just sql` command to run database examples.\n- `0.14.0` (2024-11-15) Event-based ledger now on `main`. Mixed test suite and `pyright`.\n- `0.13.0` (2024-11-15) Event-based ledger will become next minor version.\n- `0.12.0` (2024-11-13) `events.py` offers events-based ledger modification.\n- `0.11.1` (2024-11-06) `abacus.core` now feature complete.\n- `0.10.7` (2024-11-02) `Posting` type is a list of single entries.\n- `0.10.5` (2024-10-27) Handles income statement and balances sheet before and after close.\n- `0.10.0` (2024-10-24) Separates core, chart, entry and book code and tests.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepogrebnyak%2Fabacus-minimal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fepogrebnyak%2Fabacus-minimal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepogrebnyak%2Fabacus-minimal/lists"}