├── .gitignore ├── example.pbix ├── screenshots ├── screenshot_001.png ├── screenshot_002.png ├── screenshot_003.png └── screenshot_004.png ├── scripts ├── get_accounts.py └── get_records.py ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | beancount.pbix 2 | -------------------------------------------------------------------------------- /example.pbix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaulan/beancount-pbi/HEAD/example.pbix -------------------------------------------------------------------------------- /screenshots/screenshot_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaulan/beancount-pbi/HEAD/screenshots/screenshot_001.png -------------------------------------------------------------------------------- /screenshots/screenshot_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaulan/beancount-pbi/HEAD/screenshots/screenshot_002.png -------------------------------------------------------------------------------- /screenshots/screenshot_003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaulan/beancount-pbi/HEAD/screenshots/screenshot_003.png -------------------------------------------------------------------------------- /screenshots/screenshot_004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaulan/beancount-pbi/HEAD/screenshots/screenshot_004.png -------------------------------------------------------------------------------- /scripts/get_accounts.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from beancount.query import query 3 | from beancount import loader 4 | 5 | input_file = dataset['data_path'][0] 6 | query_str = """ 7 | SELECT DISTINCT 8 | account, 9 | getitem(open_meta(account), "asset-type") AS asset_type, 10 | getitem(open_meta(account), "mortage-id") AS mortage_id, 11 | getitem(open_meta(account), "interest-rate") AS interest_rate 12 | ORDER BY account 13 | """.strip() 14 | 15 | _entries, _errors, _options_map = loader.load_file(input_file) 16 | output = query.run_query(_entries, _options_map, query_str) 17 | records = pd.DataFrame(output[1]) 18 | 19 | print(records) 20 | -------------------------------------------------------------------------------- /scripts/get_records.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from beancount.query import query 3 | from beancount import loader 4 | 5 | input_file = dataset['data_path'][0] 6 | query_str = """ 7 | SELECT 8 | id, date, type, payee, account, 9 | units(position) AS units_position, 10 | convert(cost(position), '{currency}') AS cost_position, 11 | convert(value(position), '{currency}') AS value_position, 12 | narration, links, tags 13 | """.strip().format(currency=dataset['currency'][0]) 14 | 15 | _entries, _errors, _options_map = loader.load_file(input_file) 16 | output = query.run_query(_entries, _options_map, query_str) 17 | records = pd.DataFrame(output[1]) 18 | records['links'] = records['links'].apply(','.join) 19 | records['tags'] = records['tags'].apply(','.join) 20 | 21 | print(records) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Power BI Desktop dashboard for beancount data. 2 | 3 | ## initial setup 4 | 1. Install [*Power BI Desktop*](https://powerbi.microsoft.com/en-us/desktop/). 5 | 2. Install `python` along with `pandas` and `beancount`. 6 | 3. Open *Power BI* and go to `File > Options and Settings > Options > Python scripting`, either select the detected `python` instance or paste your installation path. 7 | 4. Open `example.pbix`, find `account` table from the `Fields` pandel on the right. Right click on it and select `Edit query`. 8 | ![](screenshots/screenshot_001.png) 9 | 5. From the pop-up `Power Query Editor` window, `Queries` panel on the left side, change `data_path` to your beancount file and `currency` to your major currency. 10 | 6. Close the editor and the data will refresh automatically. 11 | 12 | ## how does it work? 13 | 1. *Net-Worth*: report for net worth based on related accounts. It depands on the account metadata `asset-type`, which defines the account to be considered and its category. Here is an example: 14 | ```text 15 | 2020-01-01 open Assets:US:BofA 16 | asset-type: "SAVING" 17 | ``` 18 | 2. *Income* and *Expenses*: income or expense breaks down by time, account and payee. 19 | ![](screenshots/screenshot_002.png) ![](screenshots/screenshot_003.png) 20 | 3. *QoQ*: quarter over quarter data comparison. 21 | ![](screenshots/screenshot_004.png) 22 | --------------------------------------------------------------------------------