{"id":25109900,"url":"https://github.com/fiveladdercon/textbooks","last_synced_at":"2026-02-06T14:33:31.437Z","repository":{"id":167388698,"uuid":"642971133","full_name":"fiveladdercon/textbooks","owner":"fiveladdercon","description":"A text based command line accounting program","archived":false,"fork":false,"pushed_at":"2024-10-03T19:25:17.000Z","size":206,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-18T06:24:45.347Z","etag":null,"topics":["accounting"],"latest_commit_sha":null,"homepage":"","language":"JetBrains MPS","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/fiveladdercon.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,"zenodo":null}},"created_at":"2023-05-19T19:16:32.000Z","updated_at":"2024-10-03T19:25:22.000Z","dependencies_parsed_at":"2025-07-18T04:49:12.094Z","dependency_job_id":"d7c44911-13e0-4bc1-abd6-6a542dc4f441","html_url":"https://github.com/fiveladdercon/textbooks","commit_stats":null,"previous_names":["fiveladdercon/textbooks"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fiveladdercon/textbooks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiveladdercon%2Ftextbooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiveladdercon%2Ftextbooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiveladdercon%2Ftextbooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiveladdercon%2Ftextbooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fiveladdercon","download_url":"https://codeload.github.com/fiveladdercon/textbooks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiveladdercon%2Ftextbooks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29164864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T12:44:37.655Z","status":"ssl_error","status_checked_at":"2026-02-06T12:44:13.991Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["accounting"],"created_at":"2025-02-08T00:32:47.465Z","updated_at":"2026-02-06T14:33:31.430Z","avatar_url":"https://github.com/fiveladdercon.png","language":"JetBrains MPS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Textbooks #\r\n\r\n**Textbooks** is a text based command line accounting program for producing \r\nincome \u0026 expense statements from CSV bank statements by using patterns to \r\nreconstruct a general ledger (GL).  Patterns that recur year over year can be \r\nsaved to quickly auto-allocate most records on subsequent years, which means \r\nthat textbooks effectively learns your spending patterns over time.\r\n\r\nTextbooks has commands for the managing the following steps in report \r\nproduction:\r\n\r\n1.  Setting up a Chart of Accounts\r\n2.  Importing data from CSV bank statements\r\n3.  Using patterns to reconstruct transactions (allocations \u0026 transfers)\r\n4.  Creating, reading, updating or deleting entries to refine details\r\n5.  Reporting the Balance Sheet and Income \u0026 Expense Statement\r\n\r\nTestbooks stores all data in a human readable text file.  Though there are \r\ncommands that modify the text file, they are generally intended for querying or \r\nbulk editing. The file can and should be edited manually, because manual editing\r\nis the best means to acheive certain basic outcomes. (For example: renaming or \r\nrestructuring the Chart of Accounts; or updating or deleting import rules.)\r\n\r\n\r\n\r\n## Textbooks Session ##\r\n\r\n```\r\n$ textbooks GL [-h | --help] COMMAND [OPTIONS] \r\n   [-M | --mute | -V | --verbose | -P | --profuse]\r\n   [-N | --no-color]\r\n   [-C | --commit]\r\n```\r\n\r\nEach invocation of Textbooks is a **session** that reads the data in the \r\nspecified general ledger (`GL`) file and executes the given `COMMAND`.  \r\n\r\nThe `GL` file is not written back to disk unless the `--commit` option is \r\nspecified.  This allows for visual confirmation of changes on STDOUT before the \r\ndata is changed on disk.\r\n\r\nSession messages such as warnings are output to STDERR. The verbosity of session \r\nmessages is set using the `--mute`, `--verbose` or `--profuse` options.\r\n\r\nSession output is generally colored, often to highlight the input that was \r\nsupplied.  Add the `--no-color` option to remove the color when piping the \r\noutput to a file.\r\n\r\nNote that session options can be specified anywhere on the command line, though \r\nare typically added at the end (the `--commit` option in particular).  Note also\r\nthat session short form options are uppercase, while command options are lower \r\ncase.\r\n\r\nCommands make use of **Names**, **Patterns**, **Dates** and **Periods**.\r\n\r\n\r\n\r\n### Names ###\r\n\r\nA **Name** is a shortcut string used to uniquely identify an account in the \r\nchart of accounts.  \r\n\r\nThe full hiearchical name of the account could be typed, but the intent is to \r\nuniquely identify an account with as few key strokes as possible.\r\n\r\nNames are prefixed with the `@` symbol on the command line to signify that the \r\nremaining string is a Name, but the `@` symbol is not part of the Name itself.\r\n\r\nHierarchy in the chart of accounts can be identified by separating levels with \r\ncolon (`:`) characters:\r\n\r\n`@exp:meals`  identifies  `**Exp**ense:Discretionary:Food \u0026 Drink:**Meals**`\r\n\r\nNames are case insenstive.\r\n\r\n\r\n\r\n### Patterns ###\r\n\r\nA **Pattern** is a string used to identify transactions in bank records from \r\ntheir descriptions.  \r\n\r\nOften may different transactions are allocated to the same account. For example, \r\ntransactions at PETROCAN, ESSO, SHELL and MACEWAN'S are gas stations and would \r\nall be allocated to the @Vehicle:Fuel expense.\r\n\r\nThe Pattern:\r\n\r\n```\r\nPETROCAN | ESSO | SHELL | MACEWAN'S\r\n```\r\n\r\nidentifies transactions at any of these gas stations.\r\n\r\nA Pattern is specified at the command line during allocation, but recurring \r\npatterns can be saved in the GL file to aid with the automatic allocation of\r\ncommon transactions in future periods.\r\n\r\nPatterns use the following symbols to specify logical operations:\r\n\r\n```\r\nCommand Line   GL file    Description\r\n^              |          Terms are ORed together, i.e. A or B\r\n+              \u0026          Terms are ANDed together, i.e. C and D\r\n-              !          Terms are negated, i.e. not E\r\n```\r\n\r\nA Pattern is strictly organized as a disjuction (ORs) of conjunctions (ANDs),\r\nso that bracketting is not required.\r\n\r\n```\r\nTIM \u0026 HORTON | LOCAL \u0026 COFFEE \u0026 HOUSE\r\n```\r\nmeans \r\n```\r\n(TIM \u0026 HORTON) | (LOCAL \u0026 COFFEE \u0026 HOUSE)\r\n```\r\nnot\r\n```\r\nTIM \u0026 (HORTON | LOCAL) \u0026 COFFEE \u0026 HOUSE\r\n```\r\n\r\nAny of the special characters can be enclosed in square brackets (`[]`) to be a \r\nliteral in the pattern:\r\n\r\n```\r\nTRANSFER[-]FROM\r\n```\r\n\r\nPatterns are case insensitive.\r\n\r\n\r\n\r\n### Dates ###\r\n\r\nA **Date** is a specific day of the year specified in `YYYY-MM-DD` format.\r\n\r\nAll dates are specified, reported and stored in this format.\r\n\r\n\r\n\r\n### Periods ###\r\n\r\nA **Period** is shortcut that specifies an interval of time, for example a year, \r\na month, a day or two weeks etc.\r\n\r\nA Period can be specified by specifying two dates separated by a colon (`:`):\r\n\r\n```\r\n2022-01-01:2022-12-31\r\n```\r\n\r\nThe first date is the start date and the second date is the end date.\r\n\r\nThe start \u0026 end dates are inclusive, so that specifying the start and end\r\nas the same day will result a period of that one day:\r\n\r\n```\r\n2022-03-17:2022-03-17\r\n```\r\n\r\nIf you omit the day, the first day of the month is implied for the start date\r\nand the last day of the month is implied for the end date:\r\n\r\n```\r\n2022-01:2022-03   #  == 2022-01-01:2022-03-31  (i.e. Q1)\r\n```\r\n\r\nIf you omit the month, the first month of the year is implied for the start\r\ndate and the last day of the month is implied for the end date:\r\n\r\n```\r\n2021:2022   #  == 2021-01-01:2022-12-31  (i.e. two years)\r\n```\r\n\r\nIf you omit the end date, it is implied by the start date, with inferred\r\ndays and months appropriate to the end date:\r\n\r\n```\r\n2022-01-01  #  == 2022-01-01:2022-01-01  (i.e. one day)\r\n2022-01     #  == 2022-01-01:2022-01-31  (i.e. one month)\r\n2022        #  == 2022-01-01:2022-12-31  (i.e. one year)\r\n```\r\n\r\n\r\n## Textbooks Commands ##\r\n\r\n\r\n\r\n### Chart of Accounts Management ###\r\n\r\n```\r\n$ textbooks GL chart \r\n```\r\n\r\nShows all accounts with explicit hierarchical names.\r\n\r\n\r\n```\r\n$ textbooks GL chart @NAME\r\n```\r\n\r\nShows only accounts with a matching NAME.  Used to uniquely identify an account \r\nusing as few keystrokes as possible.\r\n\r\n\r\n```\r\n$ textbooks GL chart --asset     --parent PARENT NUMBER NAME+\r\n$ textbooks GL chart --liability --parent PARENT NUMBER NAME+\r\n$ textbooks GL chart --income    --parent PARENT NUMBER NAME+\r\n$ textbooks GL chart --expense   --parent PARENT NUMBER NAME+\r\n```\r\n\r\nCreates a new account of the given type.\r\n- PARENT is the unique account number of the parent account.\r\n- NUMBER must be unique number for the new account.\r\n- NAME is a string.  Space separated NAMES on the command line are joined into \r\n  a single NAME to save quoting.  This is a rare case where the `@` symbol is\r\n  not used (i.e. because you are creating an Account Name rather than trying\r\n  to find one that exists).\r\n\r\nAccount numbers are only used to uniquely identify an account for the purpose\r\nof constructing the hierarchy of accounts in the chart.  Once the hierarchy\r\nhas been captured the in GL with account numbers, Names will be used to\r\nuniquely identify accounts for the purposes of allocation and reporting.\r\n\r\nFor example, the following chart defines a Fuel account that sits next to the \r\nInsurance account in the hierarchy - they both have a unique number (4240 \u0026 4220\r\nrespecively) but share a common parent number (4200), the Vehicle account, which\r\nin turn has the Expense parent (4000).\r\n\r\n```\r\nASSET     1000       Assets\r\nASSET     1100:1000  Chequing Account\r\nLIABILITY 2000       Liabilities\r\nLIABILITY 2100:2000  Line of Credit\r\nINCOME    3000       Income\r\nINCOME    3100:3000  Employment\r\nEXPENSE   4000       Expense\r\nEXPENSE   4100:4000  House\r\nEXPENSE   4200:4000  Vehicle\r\nEXPENSE   4210:4200  Payments\r\nEXPENSE   4220:4200  Insurance\r\nEXPENSE   4230:4200  Licensing\r\nEXPENSE   4240:4200  Fuel\r\nEXPENSE   4250:4200  Maintenance\r\n```\r\n\r\nAlthough there are commands to create accounts in the chart, it is in fact\r\nfar easier to just work on the chart by hand once you understand the pattern.  \r\nUse the commands to get you started, but move to hand editing to more easily get \r\nthe bulk of the work done.  Use the chart command (without arguments) to check \r\nyour hierarchy.\r\n\r\nAlso, the order of accounts in the GL file is retained for reporting purposes.\r\nSo to reorder the accounts, hand modify the GL file to sort the accounts to your\r\nliking (and you can renumber if you want, but it's not strictly necessary).\r\n\r\n\r\n\r\n### Bank Statement Management ###\r\n\r\n\r\n```\r\n$ textbooks GL import --rules [--account NAME] [PATTERN+]\r\n```\r\n\r\nShows import rules.  Shows all import rules or otherwise filters\r\naccording to the account NAME or PATTERN provided.\r\n\r\n\r\n```\r\n$ textbooks GL import --files [--account NAME] [PATTERN+]\r\n```\r\n\r\nShows all import files.  Shows all files that match the import\r\nrules or otherwise filters according to the account NAME or \r\nPATTERN provided.\r\n\r\n\r\n```\r\n$ textbooks GL import --account NAME --source GLOB\r\n```\r\n\r\nAdds an import rule to the given account NAME\r\n\r\n\r\n```\r\n$ textbooks GL import [--account NAME] [PATTERN+] [-p PERIOD]\r\n```\r\n\r\nImport transactions.  Imports all transactions or otherwise\r\nfilters according to the Account NAME or PATTERN provided.\r\n\r\n\r\n```\r\n$textbooks GL reconcile\r\n```\r\n\r\nChecks the integrity of the balance of the imported transactions, \r\nverifying that the imported balance is the same as the computed\r\nbalance.  This catches \"balance discontinuities\" that may signify\r\nan import issue.\r\n\r\n\r\n```\r\n$textbooks CL deport\r\n```\r\n\r\n\r\n#### Import Scenarios ####\r\n\r\n1. Import out of order. i.e. work backwards in time since last time.\r\n   e.g. Did it in 2020, it's now 2022, so do 2022 first, then 2021\r\n   rather than require 2021 before 2022.  It means 2021 will be inserted\r\n   between 2020 and 2022 data.  Very common.\r\n\r\n2. Raw data files have duplicate transactions.  Downloading one period sometimes\r\n   includes data from the previous or next period (i.e. month/year).  \r\n   Surprisingly common.\r\n\r\n3. Raw data files have different transaction descriptions.  This happened\r\n   when I downloaded the same period of transactions a couple of months apart\r\n   and the bank had changed the decriptions from hiding transfer identifiers\r\n   (e.g. AU****) to showing transfer identifiers (e.g. AU817X).  Rare.\r\n\r\n4. Raw data files have non-sequential dates.  The last transaction on VISA \r\n   statement in month 1 \"clears\" after the first transaction on the VISA\r\n   statement in month 2.  This happens if one vendor is quick to post the\r\n   transaction while another vendor is slow.  The quick vendor posts on the \r\n   statement date and it \"makes\" the statement, while the slow vendor that \r\n   did the transaction the day before the statement date posts after \r\n   the statement date and it shows up on the next statement with a date \r\n   that is before the statement date.\r\n\r\n5. Using a date/debit/credit/balance \"signature\" does not uniquely identify\r\n   actions.  A scenario where an amount is transferred from one account to\r\n   another, then back again, then to another account (i.e. because the first\r\n   transfer was a mistake) results in the first and third action having the\r\n   same signature.  I believe the signature scheme was added to address item \r\n   (4).  Adding the item to the signature fails under scenario 3.\r\n\r\nI think the import has bounced back and forth between doing a merge based on\r\ndate (which can't handle scenario 4) and doing a signature based merge (which\r\nfails under scenario 5)\r\n\r\n\r\n\r\n### Selections ###\r\n\r\n```\r\n$ textbooks GL select [@NAME]\r\n$ textbooks GL select [@NAME] PATTERN\r\n$ textbooks GL select [@NAME] PATTERN -- @NAME\r\n$ textbooks GL select [@NAME] PATTERN -- @NAME PATTERN\r\n```\r\n\r\n\r\n#### Allocation Scenarios ####\r\n\r\n1.  A pattern applies accross multiple bank accounts and credit cards\r\n    that allocates the amounts as expense or income.  The most common.\r\n    e.g. MCDONALDS allocated to exp:meals\r\n\r\n2.  The same pattern needs to be allocated to two different expense\r\n    accounts.  e.g. Half of \"TD Ins\" actions are auto insurance, half\r\n    are home insurance.  The difference is in the amount, so Limits\r\n    were added.  Limits should probably not be storable, since they\r\n    probably change from year to year.  An alternative is to allocate\r\n    them all to one account, then add a \"correction\" transfer of half \r\n    the amount to the other account.\r\n\r\n3.  Amounts include two different types of expenses.  e.g Condo Fees\r\n    \u0026 water.  Condo fees happen every month, water every quarter but\r\n    there is only one debit from the bank account.  Some work was done \r\n    on split patterns, but I'm not sure how they are recorded in the\r\n    file. Perhaps they shouldn't be.  Splits could be a \"fix\" to the\r\n    existing Entries.  A correction entry would fix this too.\r\n\r\n4.  The same pattern needs to be allocated to two different accounts\r\n    AND the amounts are the same (so Limits won't work).  e.g. RESP \r\n    investments alternate weeks between children.  Perhaps could be\r\n    selected using a step size in the date.\r\n\r\n### Fine Tuning with Entries ###\r\n\r\n```\r\n$ textbooks GL entry RANGE [RANGE ...] [-d | --delete]\r\n```\r\n\r\n```\r\n$ textbooks GL enter @NAME DATE ITEM+     [DEBIT, | ,CREDIT] \r\n                     @NAME [DATE] [ITEM+] [DEBIT, | ,CREDIT]\r\n                     [...]\r\n```\r\n\r\n### Reporting ###\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiveladdercon%2Ftextbooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiveladdercon%2Ftextbooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiveladdercon%2Ftextbooks/lists"}