From 8bfcf43c18196b83fdefd4c75a88888c00eb8833 Mon Sep 17 00:00:00 2001 From: ananthakumaran Date: Sat, 13 Jan 2024 18:12:50 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20ananthak?= =?UTF-8?q?umaran/paisa@67d7658856676f19510df0e2dedbb545907c22be=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lexer/__pycache__/hook.cpython-311.pyc | Bin 1776 -> 1776 bytes manifesto/index.html | 10 +++++----- search/search_index.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lexer/__pycache__/hook.cpython-311.pyc b/lexer/__pycache__/hook.cpython-311.pyc index 55d98637aa26d443677205e802308473e6920736..10e5ca5283178db4daa01761c50b26e2babd6c2e 100644 GIT binary patch delta 19 Zcmeys`+=8hIWI340}!NN+Q{{c4FEbY1+V}B delta 19 Zcmeys`+=8hIWI340}!}g*vR#a4FEY>1&jay diff --git a/manifesto/index.html b/manifesto/index.html index c47dd4ea..2142b824 100644 --- a/manifesto/index.html +++ b/manifesto/index.html @@ -1627,7 +1627,7 @@ 1. Data Ownership Ledger text format is used to store all the transaction data
Ledger text format is used to store all the transaction data.
User's data is private. Paisa will not collect or send any data to any @@ -1643,10 +1643,10 @@
2022/01/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 100,000 INR\n\n2022/01/03 Rent\n Assets:Checking -20,000 INR\n Expenses:Rent\n\n2022/01/07 Investment\n Assets:Checking -20,000 INR\n Assets:Equity:NIFTY 168.690 NIFTY @ 118.56 INR\n
2022/01/01 Salary\n Income:Salary:Acme $-5,000\n Assets:Checking $5,000\n\n2022/01/03 Rent\n Assets:Checking $-2,000\n Expenses:Rent\n\n2022/01/07 Investment\n Assets:Checking $-1,000\n Assets:Equity:AAPL 6.452 AAPL @ $154.97\n
commodity \u20ac\n format \u20ac1.000,00\n\ncommodity AAPL\n format 1.000,00 AAPL\n\n2022/01/01 Salary\n Income:Salary:Acme \u20ac-5.000\n Assets:Checking \u20ac5.000\n\n2022/01/03 Rent\n Assets:Checking \u20ac-2.000\n Expenses:Rent\n\n2022/01/07 Investment\n Assets:Checking \u20ac-1.000,02\n Assets:Equity:AAPL 6,453 AAPL @ \u20ac154,97\n
Install Demo
I already use ledger/hledger/beancount. How do I get started?
Go through the installation docs and get the app working. Go to configuration page and update journal_path, ledger_cli, default_currency and locale. At this point you should be able to view your journal data. Read the accounts docs to understand the account naming conventions followed by paisa.
journal_path
ledger_cli
default_currency
locale
How do I get started?
You have installed paisa and gone through the demo and you like it, but you don't know what to do next. There is no way you would sit and type all the transactions you have made in the last decade or so.
Paisa is an accounting application and you can focus on things that matters to you the most. For example, if you are not interested in tracking expenses, you can just have a single expense transaction per month. You decide the granularity at which you want to record the transactions. Just recording a few transactions per month like your salary, monthly expense and investments will go a long way and will give you pretty good picture of your finances.
Mac Desktop app fails with executable file not found in $PATH error
Mac Desktop apps are usually started with different PATH than what you would normally get in a Terminal app. Paisa does a fallback search in the following folders before throwing the error. Check if your executable binary is in any of the folder listed below, if not, create a symbolic link from one of the folders to your executable binary.
/bin\n/usr/bin\n/usr/local/bin\n/sbin\n/usr/sbin\n/opt/homebrew/bin\n
User owns the data. It should be possible to migrate all the data to another app or service. Paisa will strive to make this as easy as possible by choosing open standards and formats.
Ledger text format is used to store all the transaction data
User's data is private. Paisa will not collect or send any data to any server1. Paisa will not use any third party analytics or tracking on the app2.
Paisa's source code is open source and could be audited by anyone.
The app should be available for a long time. It takes a lot of effort to collect and maintain the transaction data. The app should not just disappear one day. Paisa will strive to avoid unnecessary dependencies and build a self contained app.
Paisa is licensed under AGPL v3, which helps with this issue to some extent. I have yet to figure out a way to make the app development and maintenance process sustainable in the long term. But rest assured, any decision made related to this will not override the first two points.
Paisa fetches commodity price information from third party servers. Since Paisa will send the commodity identifier to the server, the third party server might be able to connect the commodity list with the user's IP address. This is an opt-in feature, as you have to explicitly configure Paisa to fetch price. VPN could be used if you want to avoid this.\u00a0\u21a9
This doesn't include the website paisa.fyi, which is hosted on third party servers and has consent based analytics.\u00a0\u21a9
The way numbers are formatted varies across various countries. In this post, I am going to talk about how to configure localization in Paisa so you can use the number formatting you are familiar with.
The User Interface localization is controlled by configuration, and the Journal file localization is controlled by the commodity directive.
The way the numbers are formatted by the User Interface is controlled by the configuration key called locale. By default, this value is set to en-IN. It can be edited via the configuration page. The value is made of two parts: the first part is the language name, and the second part is the region name. Refer to Wikipedia for the full list of supported language and country codes.
en-IN
Paisa supports multiple currencies, but it needs you to set one of the currencies as the default currency. The User Interface shows all the numbers in the default currency.
display_precision
Paisa also allows you to control the number of digits shown after the decimal point. By default, it's set to zero.
In most cases, you don't have to worry about the journal file since you can enter the value in the correct format and the ledger will parse it fine. But if you want to follow European number formatting, then you would have to use commodity directive to declare the number formatting. In the example below, we instruct the ledger to treat , as decimal separator and . as thousand separator.
,
.
commodity EUR\n format 1.000,00 EUR\n\n2024/01/01 Salary\n Income:Salary:Acme -11.000,00 EUR\n Assets:Checking 11.000,00 EUR\n
Paisa is available in two formats: a Desktop Application and a CLI (Command Line Interface). Both provide the same list of features, with the primary difference being how the user interface is launched.
paisa-app-linux-amd64.deb
# cd ~/Downloads\n# sudo dpkg -i paisa-app-linux-amd64.deb\n
paisa-app-macos-amd64.dmg
paisa
Documents
paisa-app-windows-amd64.exe
Keep anyway
More info
Run anyway
PikaPods lets you run Paisa on a server with a few clicks. It handles app upgrades, backup and hosting for you. Make sure to setup a user account post installation.
paisa-cli-linux-amd64
# cd ~/Downloads\n# mv paisa-cli-linux-amd64 paisa\n# chmod u+x paisa\n# mv paisa /usr/local/bin\n
paisa-cli-macos-amd64
# cd ~/Downloads\n# mv paisa-cli-macos-amd64 paisa\n# chmod u+x paisa\n# xattr -dr com.apple.quarantine paisa\n# mv paisa /usr/local/bin\n
paisa-cli-windows-amd64.exe
keep anyway
PS C:\\Users\\yourname>
PS C:\\Users\\john> mv .\\Downloads\\paisa-cli-windows-amd64.exe .\\paisa.exe\n
paisa.exe
.\\paisa.exe serve
Tip
Paisa depends on the ledger binary. The prebuilt paisa binaries come with an embedded ledger binary and will use it if it's not already installed on your system. If you prefer to install the ledger yourself, follow the installation instructions on ledger site.
Paisa will store all your journals, configuration files, and other related files in a folder named paisa which will be located in your Documents folder.
# paisa serve\n
PS C:\\Users\\john> .\\paisa.exe serve\n
Go to http://localhost:7500. Read the tutorial to learn more.
Paisa CLI is available on dockerhub.
# mkdir -p /home/john/Documents/paisa/\n# docker run -p 7500:7500 -v /home/john/Documents/paisa/:/root/Documents/paisa/ ananthakumaran/paisa:latest\n
# mkdir -p /Users/john/Documents/paisa/\n# docker run -p 7500:7500 -v /Users/john/Documents/paisa/:/root/Documents/paisa/ ananthakumaran/paisa:latest\n
Paisa CLI is available as a nix flake.
# nix profile install github:ananthakumaran/paisa\n
I offer Paisa as a free app, and I don't generate any revenue from it. Code signing would require me to pay $99 for Mac and approximately $300 for Windows each and every year to get the necessary certificates. I can't justify spending that much for an app that doesn't generate any income. Unfortunately, as a result, you would have to jump through hoops to get it working.\u00a0\u21a9\u21a9\u21a9
As the name implies, these are third party hosting solutions operated by independent companies. I may receive affiliate compensation for linking to their websites.\u00a0\u21a9
This tutorial will introduce all the concepts necessary to get started. Paisa builds on top of the ledger1, a command line tool that follows the principles of plain text accounting. ledger primarily focuses on command line users and doesn't provide any graphical user interface. Paisa aims to create a low-friction graphical user interface on top of ledger, thereby making it accessible to a wider range of users.
As an end user, you should be familiar with the terms and concepts used by ledger, which we will cover below. Paisa comes with an embedded ledger and you are not required to use ledger via command line unless you want to.
Even though the tutorial focuses on Indian users, Paisa is capable of handling any currency. You can change the default currency, locale and financial year starting month etc. Check the configuration reference for more details.
A journal file captures all your financial transactions. A transaction may represent a mutual fund purchase, retirement contribution, grocery purchase and so on. Paisa creates a journal named main.ledger, Let's add our first transaction there. To open the editor, go to Ledger Editor
main.ledger
Ledger
Editor
2022/01/01/*(1)!*/ Salary/*(2)!*/\n Income:Salary:Acme/*(3)!*/ -100,000 INR/*(6)!*/\n Assets:Checking/*(4)!*/ /*(5)!*/100,000 INR\n
Date
Payee
Account
Amount
Currency
ledger follows the double-entry accounting system. In simple terms, it tracks the movement of money from debit account to credit account. Here Income:Salary:Acme is the debit account and Assets:Checking is the credit account. The date at which the transaction took place and a description of the transaction is written in the first line followed by the list of credit or debit entry. Account naming conventions are explained later. The : in the account name represents hierarchy.
Income:Salary:Acme
Assets:Checking
:
2022/01/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 100,000 INR\n\n2022/02/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 100,000 INR\n\n2022/03/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 100,000 INR\n
Let's add few more transactions. As you edit your journal file, the balance of the journal will be shown on the right hand side.
300,000 INR Assets:Checking\n -300,000 INR Income:Salary:Acme\n--------------------\n 0\n
You would notice zero balance and a checking account with 3 lakhs and an income account with -3 lakhs. Double-entry accounting will always results in 0 balance since you have to always enter both the credit and debit side.
Let's say your company deducts 12,000 INR and contributes it to EPF, we could represent it as follows
12,000 INR
2022/01/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 88,000 INR\n Assets:Debt:EPF 12,000 INR\n\n2022/02/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 88,000 INR\n Assets:Debt:EPF 12,000 INR\n\n2022/03/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 88,000 INR\n Assets:Debt:EPF 12,000 INR\n
You can now see the use of : hierarchy in the account name.
300,000 INR Assets\n 264,000 INR Checking\n 36,000 INR Debt:EPF\n -300,000 INR Income:Salary:Acme\n--------------------\n 0\n
So far we have only dealt with INR. ledger can handle commodity as well. Let's say you are also investing 10,000 INR in UTI Nifty Index Fund and 10,000 INR in ICICI Nifty Next 50 Index Fund every month.
10,000 INR
2018/01/01 Investment\n Assets:Checking -20,000 INR\n Assets:Equity:NIFTY 148.0865 NIFTY @ 67.5281 INR\n Assets:Equity:NIFTY_JR 358.6659 NIFTY_JR @ 27.8811 INR\n\n2018/02/01 Investment\n Assets:Checking -20,000 INR\n Assets:Equity:NIFTY 140.2870 NIFTY @ 71.2824 INR\n Assets:Equity:NIFTY_JR 363.2242 NIFTY_JR @ 27.5312 INR\n\n2018/03/01 Withdrawal\n Assets:Checking 6775.49 INR\n Income:CapitalGains:Equity:NIFTY -22.68 INR\n Assets:Equity:NIFTY -100 NIFTY {67.5281 INR} [2018/01/01] @ 67.7549 INR\n
Let's consider 148.0865 NIFTY @ 67.5281 INR. Here NIFTY is the name of the commodity and we have bought 148.0865 units at 67.5281 INR per unit.
148.0865 NIFTY @ 67.5281 INR
NIFTY
148.0865
67.5281 INR
The withdrawal transaction is bit more involved. When you buy a commodity, you buy them at a specific price on a specific date called lot. When you sell, you usually need to record which lot you are selling for taxation purpose, usually FIFO. -100 NIFTY {67.5281 INR} [2018/01/01] @ 67.7549 INR means you are selling NIFTY at price 67.7549 INR that was bought on 2018/01/01 at 67.5281 INR. The gain or loss amount comes from the capital gains account Income:CapitalGains:Equity:NIFTY
-100 NIFTY {67.5281 INR} [2018/01/01] @ 67.7549 INR
67.7549 INR
2018/01/01
Income:CapitalGains:Equity:NIFTY
Paisa has support for fetching commodity price history from few providers. Go to Configuration page and expand the Commodities section. You can click the icon to add a new one. Edit the name to NIFTY. Click the icon near Price section and select the price provider details. Once done, save the configuration and click the Update Prices from the top right hand side menu. If you had done everything correctly, you would see the latest price of the commodity under Assets Balance
Configuration
Commodities
Update Prices
Assets
Balance
There are many instruments like EPF, FD, etc that pay interest at regular intervals. We can treat it as just another transaction. Any income account that has a prefix Income:Interest: can be used as the debit account. It's not mandatory to specify the amount at bot side. If you leave one side, ledger will deduct it.
Income:Interest:
2022/03/31 EPF Interest\n Income:Interest:EPF -5,000 INR\n Assets:Debt:EPF\n
5,000 INR Assets:Debt:EPF\n -5,000 INR Income:Interest:EPF\n--------------------\n 0\n
All the configuration related to paisa is stored in a yaml file named paisa.yaml. The configuration can be edited via the web interface. The sequence in which it looks for the file is described below
paisa.yaml
PAISA_CONFIG
--config
paisa/paisa.yaml
If it can't find the configuration file, it will create a default configuration file named paisa/paisa.yaml inside User Documents folder. The default configuration is tuned for Indians, users from other countries would have to change the default_currency and locale. Check the configuration reference for details.
Paisa fetches the latest price of the commodities only when update command is used. Make sure to run paisa update command after you make any changes to your journal file or you want to fetch the latest value of the commodities. The update can be performed from the UI as well via the dropdown in the top right hand side corner.
paisa update
hledger and beancount are also supported, refer Ledger CLI for more information.\u00a0\u21a9
If you take a typical transaction, money moves between two parties. In ledger, you call them Account. At least two accounts are involved in any transaction. Accounts are also hierarchical. This helps with the organization. For example you can treat EPF and PPF as two accounts namely Assets:Debt:EPF and Assets:Debt:PPF. The hierarchy helps you ask questions like what is the balance of Assets:Debt, which will include both EPF and PPF.
Assets:Debt:EPF
Assets:Debt:PPF
Assets:Debt
Even though ledger doesn't have any strict Account naming convention, Paisa expects you to follow the standard naming convention.
There are five types of account namely
All the accounts you create should be under one of these accounts. This naming convention is a necessity, because without which, it's not possible to tell whether you are spending money or investing money. A transaction from Assets account to Expenses account implies that you are spending money.
Expenses
Money typically flows from Income to Assets, from Assets to either Expenses, Liabilities or other Assets, from Liabilities to Expenses.
Income
Liabilities
graph LR\n I[Income] --> A[Assets];\n A --> E[Expenses];\n A --> A;\n A --> L[Liabilities];\n L --> E[Expenses];
As a general principle, try not to create too many accounts at second level. The UI works best when you create less than or equal to 12 second level accounts under each type. For example, you can have 12 accounts under Expenses. But if you want more, try to add them under 3rd level, example Expenses:Food:Subway.
Expenses:Food:Subway
All your assets should go under Assets:. The level of granularity is up to you. The recommended convention is to use Assets:{instrument_type}:{instrument_name}. The instrument type may be Cash, Equity, Debt, etc. The instrument name may be the name of the fund, stock, etc
Assets:
Assets:{instrument_type}:{instrument_name}
Cash
Equity
Debt
Assets:Checking is a special account where you keep your money for daily use. This will be included in your net worth, but will not be treated as an investment. So gain page for example, will exclude this account and won't show the returns. If you have multiple checking accounts, you can use sub accounts as well, for example Assets:Checking:HDFC and Assets:Checking:AXIS.
Assets:Checking:HDFC
Assets:Checking:AXIS
The distinction is necessary because Checking account is an asset, but not an investment. So you ideally you want to disregard them when you calculate your absolute returns etc.
All your income should come from Income:. The typical way is to treat each employer as a separate account like Income:Salary:{company}
Income:
Income:Salary:{company}
Income:Interest is a special type of account from the perspective of returns calculation. Let's assume you have bought APPLE stock. You might be buying them at regular intervals. To calculate your returns, we can compute the difference between purchase price and current price.
Income:Interest
APPLE
Now in case of FD, you will get your interest credited to your account. The returns is the difference between the amount you deposited and the final balance. It's essential we need to know which transactions are deposits and which are interest credits.
Any money that comes from the sub account of Income:Interest will be treated as interest. This convention allows paisa to calculate the returns of any debt instrument without explicitly specifying anything else.
Income:Interest:{name}
2018/03/01 Withdrawal\n Assets:Checking 6775.49 INR\n Income:CapitalGains:Equity:NIFTY -22.68 INR\n Assets:Equity:NIFTY -100 NIFTY {67.5281 INR} [2018/01/01] @ 67.7549 INR\n
Let's say you have an asset account named Assets:{name} and you make some profit when you sell the asset, this profit should come from capital gains account named Income:CapitalGains:{name}.
Assets:{name}
Income:CapitalGains:{name}
All your expenses should go to Expenses:{category} accounts. You can also have more than 2 levels as well. The expense page will roll it up to 2 level wherever necessary.
Expenses:{category}
Income tax paid to government should be credited to Expenses:Tax account. This is used to calculate your Net Income and your Savings Rate. If you want to track different types of taxes, you can use sub accounts as well, for example Expenses:Tax:Income and Expenses:Tax:GST.
Expenses:Tax
Expenses:Tax:Income
Expenses:Tax:GST
Equity is used in rare cases where you want to balance the transaction, but none of the other accounts are suitable for the purpose. Let's say you have 1000 INR in your real bank account and you want your checking account in Paisa to have same balance, you can use Equity account to balance the transaction.
1000 INR
2022/01/01 Opening Balance\n Assets:Checking:SBI 1000 INR\n Equity:OpeningBalance\n
Each account is associated with an icon and is shown along with the account name in most places. Paisa assigns icon based on few simple rules and it usually falls back to a generic icon. To modify the icon, go to the configuration page, add a new Account and specify the name of your account and then select icon of your choice. Paisa ships with four icon sets.
Experimental
There is a wide range of free icons available, but adding each icon set to Paisa would increase the app binary size. So a balance has to be struck between the number of icons and the app size. Feel free to start a discussion if you feel strongly about any icon set. The current icon sets are not final, they may be replaced if a better alternative is found.
Paisa allows you to set a allocation target for a group of accounts which might represent your assets like mutual funds, stocks etc. The allocation page shows how far your current allocation is from the allocation target. For example, to keep a 40:60 split between debt and equity, use the following configuration. The account name can have * which matches any characters
*
allocation_targets:\n - name: Debt\n target: 40\n accounts:\n - Assets:Debt:*\n - name: Equity\n target: 60\n accounts:\n - Assets:Equity:*\n
Ledger represents everything except currency as commodities. A commodity could represent physical object like Gold or certificates like Stock, Bond etc. Some commodities like Mutual Fund is a container for Securities. When you own a mutual fund unit, you indirectly own these securities.
The Analysis page unwraps the container and shows you what securities you own along with their amount and percentage. It also categorizes the securities by their industry and rating.
The data that powers this page comes from various sources and might not be 100% accurate. Before you make any decision based on this information, double check via different source.
Paisa supports a simple budgeting system. Let's say you get 50000 INR at the beginning of the month. You want to budget this amount and figure out how much you can spend on each category.
Let's add a salary transaction to the ledger:
2023/08/01 Salary\n Income:Salary:Acme -50,000 INR\n Assets:Checking\n
Now you have 50k in your checking account. Let's budget this amount:
~ Monthly in 2023/08/01\n Expenses:Rent 15,000 INR\n Expenses:Food 10,000 INR\n Expenses:Clothing 5,000 INR\n Expenses:Entertainment 5,000 INR\n Expenses:Transport 5,000 INR\n Expenses:Personal 5,000 INR\n Assets:Checking\n
The ~ character indicates that this is a periodic transaction. This is not a real transaction, but used only for forecasting purposes. You can read more about periodic expressions and periodic transactions.
~
Bug
Even though the interval part is optional as per the doc, there is a bug in the ledger-cli, so you can't use ~ in 2023/08/01, instead you always have to specify some interval like ~ Monthly in 2023/08/01.
~ in 2023/08/01
~ Monthly in 2023/08/01
Now you can see that you will have 5k left in your checking account at the end of the month, if you spend as per your budget. Before you spend, you can check your budget and verify if you have money available under that category.
Let's add some real transactions.
2023/08/02 Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n\n2023/08/03 Transport\n Expenses:Transport 1,000 INR\n Assets:Checking\n\n2023/08/03 Food\n Expenses:Food 8,500 INR\n Assets:Checking\n\n2023/08/05 Transport\n Expenses:Transport 2,000 INR\n Assets:Checking\n\n2023/08/07 Transport\n Expenses:Transport 3,000 INR\n Assets:Checking\n\n2023/08/10 Personal\n Expenses:Personal 4,000 INR\n Assets:Checking\n\n2023/08/15 Insurance\n Expenses:Insurance 10000 INR\n Assets:Checking\n
As the month progresses, you can see how much you have spent and how much you have left. You notice that you have overspent on transport and you have missed the insurance payment. You have a budget deficit now. That means, you can't actually spend as per your budget. You have to first bring the deficit back to 0. Let's cut down the entertainment and clothing budget to 0
~ Monthly in 2023/08/01\n Expenses:Rent 15,000 INR\n Expenses:Food 10,000 INR\n Expenses:Clothing 0 INR\n Expenses:Entertainment 0 INR\n Expenses:Transport 5,000 INR\n Expenses:Personal 5,000 INR\n Assets:Checking\n
You can go back and adjust your budget anytime. Let's move on to the next month, assuming you haven't made any further transaction.
2023/09/01 Salary\n Income:Salary:Acme -50,000 INR\n Assets:Checking\n\n~ Monthly in 2023/09/01\n Expenses:Rent 15,000 INR\n Expenses:Food 10,000 INR\n Expenses:Clothing 5,000 INR\n Expenses:Entertainment 5,000 INR\n Expenses:Transport 5,000 INR\n Expenses:Personal 5,000 INR\n Assets:Checking\n
You can see a new element in the UI called Rollover1. This is basically the amount you have budgeted last month, but haven't spent. This will automatically rollover to the next month. That's pretty much it.
To recap, there are just two things you need to do.
1) Create a periodic transaction at the beginning of the month when you get your salary.
2) Adjust your budget as you spend and make sure there is no deficit.
If you prefer to not have rollover feature, it can be disabled in the configuration page.\u00a0\u21a9
Paisa provides bulk transaction editor to search and modify multiple transactions at once. The interface is made of two parts:
1) Search input box allows you to narrow down the transactions you are interested in making changes
2) Bulk Edit form allows you to make changes to the narrowed down set of transactions.
Paisa provides a powerful search query interface. Let's start with a few example queries.
Expenses:Utilities:Electricity\n
This will search for all transactions that have a posting with account named Expenses:Utilities:Electricity. By default, the search is case insensitive and will do a substring match. So, Expenses:Utilities will match Expenses:Utilities:Electricity account as well. If you want to search an account name which has special characters like space in it, you can use double quotes to enclose it like \"Expenses:Utilities:Hair Cut\".
Expenses:Utilities
\"Expenses:Utilities:Hair Cut\"
You can also search on transaction date. For example, if you want to show all the transactions made on 1st Jan 2023, just type [2023-01-01]. If you want to see all made on that month, just leave out the day part [2023-01]. You can do the same with year, [2023] will show all the transactions made in 2023.
[2023-01-01]
[2023-01]
[2023]
There is experimental support for natural language date. You can do queries like [last month], [last year], [this month], [lastweek], [jan 2023], etc.
[last month]
[last year]
[this month]
[lastweek]
[jan 2023]
Let's say you want to search by amount. You can do that by typing 42, it will show all the transactions that have a posting with that amount.
42
if you want to search a exact Account, you can do that using Regular Expression. Just type /^Assets:Equity:APPLE$/, you can also do case insensitive search by using the modifier i like /^Assets:Equity:APPLE$/i.
/^Assets:Equity:APPLE$/
i
/^Assets:Equity:APPLE$/i
You can also search based on properties like account, commodity, amount, total, filename, note, payee and date.
payee =~ /uber/i\npayee = \"Advance Tax\"\ncommodity = GOLD\ntotal > 5000\nnote = \"rebate\"\naccount = \"Expenses:Utilities:Electricity\"\ndate >= [2023-01-01]\nfilename = creditcard/2023/jan.ledger\n
The general format is property operator value. The property can be any of the following:
property operator value
The operator can be any of the following:
Not all the combinations of property, operator and value would work, if in doubt, just try it out, the UI will show you an error if the query is not valid.
In fact, in the previous format we saw, if the property and operator is not specified and a default set is chosen based on the value type. For example, 42 will be treated as amount = 42, Expenses:Utilities will be treated as account = Expenses:Utilities, /Expenses:Utilities/i will be treated as account =~ /Expenses:Utilities/i, [2023-01] will be treated as date = [2023-01].
amount = 42
account = Expenses:Utilities
/Expenses:Utilities/i
account =~ /Expenses:Utilities/i
date = [2023-01]
You can combine multiple property based queries using AND and OR, you can negate them using NOT
AND
OR
NOT
account = Expenses:Utilities AND payee =~ /uber/i\ncommodity = GOLD OR total > 5000\ndate >= [2023-01-01] AND date < [2023-04-01]\naccount = Expenses:Utilities AND payee =~ /uber/i AND (total > 5000 OR total < 1000)\n[last year] AND (payee =~ /swiggy/i OR payee =~ /phonepe/i)\ntotal < 5000 AND NOT account = Expenses:Utilities\n
If you leave out the conditional operator, it will be treated as AND. Both of the below queries are the same
account = Expenses:Utilities payee =~ /uber/i\naccount = Expenses:Utilities AND payee =~ /uber/i\n
Currently bulk edit form supports only account rename feature. More will be added later. The preview button allows you to see the changes before you save them. It will show a side by side diff of the changes.
User Accounts
Retirement page has been moved under goals. If you have used retirement, you need to setup a new retirement goal
User's custom import templates used to be stored in Database, which is a bad idea in hindsight. It's being moved to the configuration file. With this change, all the data in paisa.db would be transient and can be deleted and re created from the journal and configuration files without any data loss.
If you have custom template, take a backup before you upgrade and add it again via new version. If you have already upgraded, you can still get the data directly from the db file using the following query sqlite3 paisa.db \"select * from templates\";
sqlite3 paisa.db \"select * from templates\";
type: mutualfund\n- code: 122639\n+ price:\n+ provider: in-mfapi\n+ code: 122639\n harvest: 365\n
There are no restrictions on the type of commodities that can be used in Paisa. Anything like gold, mutual fund, NPS, etc can be tracked as a commodity. Few example transactions can be found below.
2019/02/18 NPS\n Assets:Equity:NPS:SBI:E/*(1)!*/ /*(2)!*/15.9378 NPS_SBI_E/*(3)!*/ @ /*(4)!*/23.5289 INR/*(5)!*/\n Assets:Checking\n\n2019/02/21 NPS\n Assets:Equity:NPS:SBI:E 1557.2175 NPS_SBI_E @ 23.8406 INR\n Assets:Checking\n\n2020/06/25 Gold\n Assets:Gold 40 GOLD @ 4650 INR\n Assets:Checking\n
Paisa comes with inbuilt support for fetching the latest price of some commodities like mutual fund, NPS, stocks, etc from few providers. For others, it will try to use the latest purchase price specified in the journal. For example, when you enter the second NPS transaction on 2019/02/21, the valuation of your existing holdings will be adjusted based on the new purchase price.
2019/02/21
To link a commodity with a commodity price provider, Go to Configuration page and expand the Commodities section. You can click the icon to add a new one. Edit the name to commodity name. Click the icon near Price section and select the price provider details. Once done, save the configuration and click the Update Prices from the top right hand side menu. If you had done everything correctly, you would see the latest price of the commodity under Assets Balance. You can also view the full price history on Ledger Price
Price
To automatically track the latest value of your mutual funds holdings, you need to link the commodity and the fund scheme code.
commodities:\n - name: NIFTY # (1)!\n type: mutualfund # (2)!\n price:\n provider: in-mfapi # (3)!\n code: 120716 # (4)!\n
The example configuration above links nifty commodity with the respective mutual fund scheme code.
To automatically track the latest value of your stock holdings, you need to link the commodity and the stock ticker name.
commodities:\n - name: APPLE # (1)!\n type: stock # (2)!\n price:\n provider: com-yahoo # (3)!\n code: AAPL # (4)!\n
Stock prices are fetched from yahoo finance website. The ticker code should match the code used in yahoo. If the fetched price currency is not the default currency, it will be converted to default currency using the forex rate apis.
Supports 100,000+ stocks, ETFs, mutual funds etc. It also provides exchange rates, so the price can be converted to default currency if required.
commodities:\n - name: RELIANCE # (1)!\n type: stock # (2)!\n price:\n provider: co-alphavantage # (3)!\n code: 7GAURDT55LU7OQKK:RELIANCE.BSE:INR # (4)!\n
Code is made of three parts, apiKey, symbol and currency. For example in 7GAURDT55LU7OQKK:RELIANCE.BSE:INR, 7GAURDT55LU7OQKK is the api key, RELIANCE.BSE is the symbol and INR is the currency. Alpha Vantage provides free api key with 25 requests per day limit.
apiKey
symbol
currency
7GAURDT55LU7OQKK:RELIANCE.BSE:INR
7GAURDT55LU7OQKK
RELIANCE.BSE
INR
To automatically track the latest value of your nps funds holdings, you need to link the commodity and the fund scheme code.
commodities:\n - name: NPS_HDFC_E # (1)!\n type: nps # (2)!\n price:\n provider: com-purifiedbytes-nps # (3)!\n code: SM008002 # (4)!\n
The example configuration above links NPS fund commodity with their respective NPS fund scheme code.
To automatically track the latest price of gold or silver at various level of purity, you need to link the commodity and the metal code. The price is for 1 gram of the metal.
commodities:\n - name: GOLD # (1)!\n type: metal # (2)!\n price:\n provider: com-purifiedbytes-metal # (3)!\n code: gold-999 # (4)!\n
The following metals and purity combinations are supported.
Some commodities like real estate are bought once and the price changes over time. Ledger allows you to set the price as on date.
2014/01/01 Home purchase\n Assets:House 1 APT @ 4000000 INR\n Liabilities:Homeloan\n\nP 2016/01/01 00:00:00 APT 5000000 INR\nP 2018/01/01 00:00:00 APT 6500000 INR\nP 2020/01/01 00:00:00 APT 6700000 INR\nP 2021/01/01 00:00:00 APT 6300000 INR\nP 2022/01/01 00:00:00 APT 8000000 INR\n
If you need to deal with multiple currencies, just treat them as you would treat any commodity. Since paisa is a reporting tool, it will always try to convert other currencies to the default_currency. As long as the exchange rate from a currency to default_currency is available, paisa would work without issue
P 2023/05/01 00:00:00 USD 81.75 INR\n\n2023/05/01 Freelance Income\n ;; conversion rate will be picked up\n ;; from the price directive above\n Income:Freelance -100 USD\n Assets:Checking\n\n2023/06/01 Freelance Income\n ;; conversion rate is specified inline\n Income:Freelance -200 USD @ 82.75 INR\n Assets:Checking\n\n2023/07/01 Netflix\n ;; if not available for a date,\n ;; will use previous known conversion rate (82.75)\n Expenses:Entertainment 10 USD\n Assets:Checking\n
Paisa fetches the latest price of the commodities only when you update the prices. This can be done via UI using the dropdown in the top right hand side corner or via paisa update command. Make sure to update the prices after you make any changes to your journal file or you want to fetch the latest value of the commodities.
All the configuration related to paisa is stored in a yaml file named paisa.yaml. The configuration can be edited via the web interface. The sequence in which paisa looks for the file is described below
If it can't find the configuration file, it will create a default configuration file named paisa/paisa.yaml inside User Documents folder. The default configuration is tuned for Indians, users from other countries would have to change the default_currency and locale.
In many places, paisa expects you to specify a list of accounts. You can type the full account name like Account:Equity:APPL. Paisa also supports wildcard *, you can use Account:Equity:* to represent all accounts under Equity. It's also possible to use negation. !Expenses:Tax will match all accounts except Tax. If you use negation, then all the accounts should be negation. Don't mix negation with others, if done the behavior will be undefined.
Account:Equity:APPL
Account:Equity:*
!Expenses:Tax
# Path to your journal file. It can be absolute or relative to the\n# configuration file. The main journal file can refer other files using\n# `include` as long as all the files are in the same or sub directory\n# REQUIRED\njournal_path: /home/john/Documents/paisa/main.ledger\n\n# Path to your database file. It can be absolute or relative to the\n# configuration file. The database file will be created if it does not exist.\n# REQUIRED\ndb_path: /home/john/Documents/paisa/paisa.db\n\n# The ledger client to use\n# OPTIONAL, DEFAULT: ledger, ENUM: ledger, hledger, beancount\nledger_cli: ledger\n\n# The default currency to use. NOTE: Paisa tries to convert other\n# currencies to default currency, so make sure it's possible to\n# convert to default currency by specifying the exchange rate.\n#\n# OPTIONAL, DEFAULT: INR\ndefault_currency: INR\n\n# The locale used to format numbers. The list of locales supported\n# depends on your browser. It's known to work well with en-US and en-IN.\n#\n# OPTIONAL, DEFAULT: en-IN\nlocale: en-IN\n\n# First month of the financial year. This can be set to 1 to follow\n# January to December.\n#\n# OPTIONAL, DEFAULT: 4\nfinancial_year_starting_month: 4\n\n# First day of the week. This can be set to 1 to follow Monday to\n# Sunday. 0 represents Sunday, 1 represents Monday and so on.\n#\n# OPTIONAL, DEFAULT: 0\nweek_starting_day: 0\n\n# When strict mode is enabled, all the accounts and commodities should\n# be defined before use. This is same as --pedantic flag in ledger and\n# --strict flag in hledger. Doesn't apply to beancount.\n#\n# OPTIONAL, ENUM: yes, no DEFAULT: no\nstrict: \"no\"\n\n## Budget\nbudget:\n # Rollover unspent money to next month\n # OPTIONAL, ENUM: yes, no DEFAULT: yes\n rollover: \"yes\"\n\n## Goals\ngoals:\n # Retirement goals\n retirement:\n # Goal name\n # REQUIRED\n - name: Retirement\n # Goal icon\n # REQUIRED\n icon: mdi:palm-tree\n # Safe Withdrawal Rate\n # OPTIONAL, DEFAULT: 4\n swr: 2\n # List of expense accounts\n # OPTIONAL, DEFAULT: Expenses:*\n expenses:\n - Expenses:Clothing\n - Expenses:Education\n - Expenses:Entertainment\n - Expenses:Food\n - Expenses:Gift\n - Expenses:Insurance\n - Expenses:Misc\n - Expenses:Restaurant\n - Expenses:Shopping\n - Expenses:Utilities\n # List of accounts where you keep retirement savings\n # OPTIONAL, DEFAULT: Assets:*\n savings:\n - Assets:Equity:*\n - Assets:Debt:*\n # By default, average of last 3 year expenses will be used to\n # calculate your yearly expenses. This can be overridden by setting\n # this configuration to positive value\n # OPTIONAL, DEFAULT: 0\n yearly_expenses: 0\n savings:\n # Goal name\n # REQUIRED\n - name: House\n # Goal icon\n # REQUIRED\n icon: fluent-emoji-high-contrast:house-with-garden\n # Goal target amount\n # REQUIRED\n target: 100000\n # Goal target date\n # OPTIONAL (either target_date or payment_per_period can be specified)\n target_date: \"2030-01-01\"\n # Expected rate of returns\n # OPTIONAL\n payment_per_period: 0\n # Expected rate of returns\n # OPTIONAL, REQUIRED if target_date or payment_per_period is set\n rate: 5\n # List of accounts where you keep the goal's savings\n # REQUIRED\n accounts:\n - Assets:Equity:**\n## Schedule AL\n# OPTIONAL, DEFAULT: []\nschedule_al:\n # Code\n # REQUIRED, ENUM: immovable, metal, art, vehicle, bank, share,\n # insurance, loan, cash, liability\n - code: metal\n accounts:\n - Assets:Gold\n - code: bank\n accounts:\n - Assets:Checking\n - Assets:Debt:Cash:FD\n - code: share\n accounts:\n - Assets:Equity:*\n - code: insurance\n accounts:\n - Assets:Debt:Insurance\n\n## Allocation Target\n# OPTIONAL, DEFAULT: []\nallocation_targets:\n - name: Debt\n target: 30\n accounts:\n - Assets:Debt:*\n - Assets:Checking\n - name: Equity\n target: 60\n accounts:\n - Assets:Equity:*\n - name: Equity Foreign\n target: 20\n accounts:\n - Assets:Equity:NASDAQ\n - name: Equity Index\n target: 20\n accounts:\n - Assets:Equity:NIFTY\n - name: Equity Active\n target: 20\n accounts:\n - Assets:Equity:PPFAS\n - name: Others\n target: 10\n accounts:\n - Assets:Gold\n - Assets:RealEstate\n\n## Commodities\n# OPTIONAL, DEFAULT: []\ncommodities:\n - name: NASDAQ\n # Required, ENUM: mutualfund, stock, nps, unknown\n type: mutualfund\n price:\n # Required, ENUM: in-mfapi, com-yahoo, com-purifiedbytes-nps, co-alphavantage\n provider: in-mfapi\n # differs based on provider\n code: 145552\n harvest: 1095\n # Optional, ENUM: equity65, equity35, debt, unlisted_equity\n tax_category: debt\n - name: NIFTY\n type: mutualfund\n price:\n provider: in-mfapi\n code: 120716\n harvest: 365\n tax_category: equity65\n - name: APPLE\n type: stock\n price:\n provider: com-yahoo\n code: AAPL\n harvest: 1095\n tax_category: equity65\n\n## Import Templates\n# OPTIONAL, DEFAULT: []\nimport_templates:\n - name: SBI Account Statement\n # Required\n content: |\n {{#if (isDate ROW.A \"D MMM YYYY\")}}\n {{date ROW.A \"D MMM YYYY\"}} {{ROW.C}}\n {{#if (isBlank ROW.F)}}\n {{predictAccount prefix=\"Expenses\"}} {{amount ROW.E}} INR\n Assets:Checking:SBI\n {{else}}\n Assets:Checking:SBI {{amount ROW.F}} INR\n {{predictAccount prefix=\"Income\"}}\n {{/if}}\n {{/if}}\n # Should be a valid handlebar template\n\n## Accounts: account customization\n# OPTIONAL, DEFAULT: []\naccounts:\n - name: Liabilities:CreditCard:IDFC\n # Required, name of the account\n icon: arcticons:idfc-first-bank\n # Optional, use the UI to select the icon.\n\n## List of user accounts.\n# If the list is empty, then no authentication will be performed\n#\n# OPTIONAL, DEFAULT: []\nuser_accounts:\n - username: john.doe\n # Required\n password: sha256:a96dc73edd639b1c711b006e714bd2ff5bf5c1aecd77d0b3c3370403c66d58e5\n # Required, password hashed twice with sha256, then prefixed sha256:\n # echo -n 'secret' | sha256sum | head -c 64 | sha256sum | head -c 64\n
Paisa uses codemirror that comes with a standard set of keyboard shortcuts. In addition to that, Paisa has few more shortcuts, which are listed below.
Paisa provides ability to convert CSV, TXT, XLS, XLSX or PDF files to Ledger file format. The import page is made of three components.
PDF support is in an experimental stage and may not accurately detect rows.
1) File Preview - You can drag and drop files here to preview the contents.
2) Ledger Preview - This is where the converted ledger file will be shown.
3) Template Editor - This is where you can edit the template.
Each row in the CSV file is converted to a transaction in the ledger. This conversion is controlled by the template.
{{#if (and (isDate ROW.A \"DD/MM/YYYY\") (isBlank ROW.G))}}\n {{date ROW.A \"DD/MM/YYYY\"}} {{ROW.C}}\n {{predictAccount prefix=\"Expenses\"}} {{ROW.F}} INR\n Assets:Checking\n{{/if}}\n
Let's break this down. The first line is a conditional statement. It checks if the row has a date in the first column. You can refer any column using their alphabets. The second line constructs the transaction header. The third line constructs the first posting. The fourth line constructs the second posting.
Effectively a single row in the CSV file is converted to a single transaction like this.
2023/03/28 AMAZON HTTP://WWW.AM IN\n Expenses:Shopping 249.00 INR\n Assets:Checking\n
The template is written in Handlebars. Paisa provides a few helper functions to make it easier to write the template.
Paisa ships with a few built-in templates. You can also create your own. To create a new template, edit the template and click on the Save As button. User defined custom templates are stored in the configuration file.
Save As
The import system is designed to be extensible and might not be intuitive if you are not accustomed to coding. If you are unable to create a template suitable for your file, please open an issue with a sample file, and we will provide assistance, possibly adding it to the built-in templates.
ROW.A
ROW.B
ROW.index
{\n \"A\": \"28/03/2023\",\n \"B\": \"7357680821\",\n \"C\": \"AMAZON HTTP://WWW.AM IN\",\n \"D\": \"12\",\n \"E\": \"0\",\n \"F\": \"249.00\",\n \"G\": \"\",\n \"index\": 6\n}\n
SHEET.5.A
[\n {\n \"A\": \"Accountno:\",\n \"B\": \"49493xxx003030\",\n \"index\": 0\n },\n {\n \"A\": \"Customer Name:\",\n \"B\": \"MR John Doe\",\n \"index\": 1\n },\n {\n \"A\": \"Address:\",\n \"B\": \"1234, ABC Street, XYZ City, 123456\",\n \"index\": 2\n },\n {\n \"A\": \"Transaction Details:\",\n \"index\": 3\n },\n {\n \"A\": \"Date\",\n \"B\": \"Sr.No.\",\n \"C\": \"Transaction Details\",\n \"D\": \"Reward Point Header\",\n \"E\": \"Intl.Amount\",\n \"F\": \"Amount(in Rs)\",\n \"G\": \"BillingAmountSign\",\n \"index\": 4\n },\n {\n \"A\": \"49493xxx003030\",\n \"index\": 5\n },\n {\n \"A\": \"28/03/2023\",\n \"B\": \"7357680821\",\n \"C\": \"AMAZON HTTP://WWW.AM IN\",\n \"D\": \"12\",\n \"E\": \"0\",\n \"F\": \"249.00\",\n \"G\": \"\",\n \"index\": 6\n },\n {\n \"A\": \"28/03/2023\",\n \"B\": \"7357821997\",\n \"C\": \"AMAZON HTTP://WWW.AM IN\",\n \"D\": \"28\",\n \"E\": \"0\",\n \"F\": \"575.00\",\n \"G\": \"\",\n \"index\": 7\n }\n]\n
eq(a: any, b: any): boolean
Checks if the two values are equal.
not(value: any): boolean
Negates the given value.
and(...args: any[]): boolean
Returns true if all the arguments are true.
or(...args: any[]): boolean
Returns true if any of the arguments are true.
gte(a: string | number, b: string | number): boolean
Checks if a is greater than or equal to b.
gt(a: string | number, b: string | number): boolean
Checks if a is greater than b.
lte(a: string | number, b: string | number): boolean
Checks if a is less than or equal to b.
lt(a: string | number, b: string | number): boolean
Checks if a is less than b.
negate(value: string | number): number
Negates the given value. For example, negate(\"123.45\") will return -123.45
negate(\"123.45\")
-123.45
amount(str: string, {default?: string}): string
Converts the given string to a valid amount. If the string is blank, the default value is used. Examples (0.9534) to -0.9534, \u20b9 1,234.56 to 1234.56
(0.9534)
-0.9534
\u20b9 1,234.56
1234.56
round(str: string, {precision?: number}): number
Rounds the given value to the given precision. If precision is not set, defaults to 0
0
isDate(str: string, format: string): boolean
Checks if the given string is a valid date in the given format. Refer Day.js for the full list of supported formats.
predictAccount(...terms: string[], {prefix?: string}): string
Helps with prediction of account name to be used in a transaction. Let's say you import your Credit Card bill, you would prefer the system to automatically assign the account name for you. predictAccount acts like a search engine. It will try to find a transaction that is similar to the current transaction. The search requires input (referred as terms) and it will match transactions with similar description and will pick the accounts from the top match.
predictAccount
If terms are not provided, the entire ROW will be used.
terms
ROW
The prefix is optional and will be used to filter out matching accounts. If no match is found, Unknown will be returned.
prefix
Unknown
{{predictAccount prefix=\"Income\"}}\n{{predictAccount ROW.C ROW.F prefix=\"Income\"}}\n
Prediction will only work if you have similar transactions in ledger file. It usually means, first time you have to manually fix the Unknown account and then subsequent imports will work
isBlank(str: string): boolean
Checks if the given string is blank.
date(str: string, format: string): string
Parses the given string as a date in the given format and returns the date in the format YYYY/MM/DD. Refer Day.js for the full list of supported formats.
YYYY/MM/DD
findAbove(column: string, {regexp?: string}): string
Finds the first cell above the current row in the given column. If regexp is provided, the search will continue till a match is found
regexp
{{findAbove B regexp=\"LIMITED\"}}\n
findBelow(column: string, {regexp?: string}): string
Finds the first cell below the current row in the given column. If regexp is provided, the search will continue till a match is found
{{findBelow B regexp=\"LIMITED\"}}\n
acronym(str: string): string
Returns the acronym of the given string that is suitable to be used as a commodity symbol. For example, UTI Nifty Next 50 Index Growth Direct Plan will be converted to UNNI
UTI Nifty Next 50 Index Growth Direct Plan
UNNI
replace(str: string, search: string, replace: string): string
Trims the given string.
regexpTest(str: string, regexp: string): boolean
Tests the given string against the given regular expression.
regexpMatch(str: string, regexp: string, {group?: number}): string
Extract part of a string. Let's say you have Axis Long Term Equity Growth Direct Plan and you want to extract Axis Long Term Equity, you can use {{regexpMatch ROW.C \"(.*) Growth Direct Plan\" group=1}} assuming the string is in the column C. group is optional and defaults to 0.
Axis Long Term Equity Growth Direct Plan
Axis Long Term Equity
{{regexpMatch ROW.C \"(.*) Growth Direct Plan\" group=1}}
C
group
textRange(fromColumn: string, toColumn: string, {separator?: number}): string
Extracts text from the given range of columns. The separator is optional and defaults to \" \".
separator
\" \"
{{textRange A C separator=\" \"}}\n
toLowerCase(str: string): string
Converts the given string to lower case.
toUpperCase(str: string): string
Converts the given string to upper case.
match(str: string, {[string]: string}): string
Let's say you are trying to import your Credit Card bill and you want map the transaction to a specific expense account based on the description. You can use the match helper to do that. The helper takes a string and a map of key value pairs. The key is the account to be returned if the string matches the value. The value must be a valid regular expression.
match
{{match ROW.C Expenses:Shopping=\"Amazon|Flipkart\" Expenses:Groceries=\"BigBasket\"}}\n
In case of no match null will be returned. You can combine this with or helper to return a default account.
null
or
{{or (match ROW.C Expenses:Shopping=\"Amazon|Flipkart\" Expenses:Groceries=\"BigBasket\")\n \"Expenses:Unknown\"}}\n
All your transactions are stored in plain text files called journal. You can organize your transactions into multiple journal files. The journal_path configuration refers your main journal file. The main journal file can refer other journal files using include directive. The include directive supports wildcards * as well. Transactions are sourced only from the main journal file and other journal files included from the main journal file.
include
include investments.ledger\ninclude expenses/*.ledger\n
Paisa comes with a journal editor. It allows you to edit all the files with the same file extension as your main journal and in the same or sub directories as your main journal.
Paisa tries its best to keep your journal safe. It creates a backup of your journal file every time you save it. The backup file is created with the same name as your journal file with a .backup.{timestamp} extension. You can revert back to old versions of your journal file via the editor. You can also delete the backup files from the editor.
.backup.{timestamp}
Warning
It is recommended to keep your journal files and paisa.yaml under version control or other backup mechanism. Paisa's backup mechanism is not a replacement for a proper backup. You can ignore paisa.db file from your version control system, the data in db file can be recreated from your journal files.
paisa.db
The journal syntax of the features you use normally along with paisa is documented here. Refer ledger documentation for more details.
2022/01/01 Salary\n Income:Salary:Acme -100,000 INR\n Assets:Checking 100,000 INR\n
A transaction should start with a date followed by description. Following that you can have 2 or more postings. The posting line should have at least 2 leading spaces. The account name and the amount should be separated by at least 2 spaces.
transaction
date
description
postings
account
amount
2022/01/07 Investment\n Assets:Checking -20,000 INR\n Assets:Equity:NIFTY 168.690 NIFTY @ 118.56 INR\n
commodity cost can be specified using the @ syntax. Here 118.56 is the per unit cost and 168.690 is the quantity you have bought.
commodity
@
118.56
168.690
2023/07/01 Rent\n ; This is a transaction comment\n Expenses:Rent 15,000 INR\n Assets:Checking ; This is a posting comment\n
Any text after ; is treated as a comment. Comment can be at whole transaction level or individual posting level. Comment is also referred as note in many places, both are same.
;
2023/07/01 Rent\n ; Recurring: Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n
Transactions can be tagged with extra metadata called tags. Tag has two parts: tag name and value. In the above example, Recurring is the name and Rent is the value. Tag should be inside comment.
Recurring
Rent
Include directive can be used to include other journal files. It supports wildcards *.
Paisa is compatible with ledger, hledger and beancount. By default paisa will try to use ledger, if you prefer to use hledger or beancount instead, change the ledger_cli value in paisa.yaml
# OPTIONAL, DEFAULT: ledger, ENUM: ledger, hledger, beancount\nledger_cli: hledger\n
Paisa ships with ledger binary. If you use hledger or beancount, make sure that the binaries are installed.
Some of the features that are available in Paisa are not supported by hledger and beancount
Budget - budget is based on periodic transactions that is no supported by beancount.
Recurring (partial) - beancount doesn't support automated transactions. It's possible to add metadata to transactions manually. Use lowercase for tag name.
Search filter in Transactions and Postings pages doesn't support note property.
Some of the transactions recur on a regular interval and it might be useful to know the next due date for such transactions. Recurring page shows the upcoming or recently missed transactions.
Paisa depends on the posting metadata to identify which transactions are recurring. This metadata can be added in couple of ways. Let's say you pay rent every month and you want to mark it as recurring, a typical journal would like below
2023/07/01 Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n\n2023/08/01 Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n
You can manually tag a posting by adding ; Recurring: Rent.
; Recurring: Rent
2023/07/01 Rent\n ; Recurring: Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n\n2023/08/01 Rent\n ; Recurring: Rent\n Expenses:Rent 15,000 INR\n Assets:Checking\n
The first part of the metadata before the colon is called tag name. It should be Recurring. The second part is the tag value. This value is used to group transactions.
Tagging each and every posting can be tiresome. Ledger has a feature called Automated Transaction which can make this process simpler.
= Expenses:Rent\n ; Recurring: Rent\n
The first line is the predicate and the line below it will get added to any matching posting. By default, it will match the posting account name. But you can target other attributes like payee, amount etc. You can find more examples below, more info about predicate is available on Ledger docs
= expr payee=~/^PPF$/\n ; Recurring: PPF\n\n= expr payee=~/Mutual Fund/\n ; Recurring: Mutual Fund\n\n= expr 'account=~/Expenses:Insurance/ and (payee=~/HDFC/)'\n ; Recurring: Life Insurance\n\n= expr 'account=~/Expenses:Insurance/ and !(payee=~/HDFC/)'\n ; Recurring: Bike Insurance\n\n= expr payee=~/Savings Interest/\n ; Recurring: Savings Interest\n
Include the automated transactions at the top of the main ledger file. Ledger will apply the rules only to transactions that follow the automated transactions.
Paisa will try to infer the recurring period of the transactions automatically, but this might not be perfect. Recurring period can also be explicitly specified via metadata.
= expr payee=~/Savings Interest/\n ; Recurring: Savings Interest\n ; Period: L MAR,JUN,SEP,DEC ?\n
Let's say your bank deposits the interest on the last day of the last month of the quarter, we can specify like the example above. Paisa editor recognizes period syntax and shows the upcoming 3 schedules right next to period metadata.
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 day of the month 1-31\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 month 1-12 or JAN-DEC\n\u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 day of the week 0-6 (Sunday to Saturday)\n\u2502 \u2502 \u2502\n1 * ?\n
The syntax of the period is similar to cron, with the omission of seconds and hours.
1\u201331
* , - ? L W
1-12
JAN-DEC
* , -
0-6
SUN-SAT
* , - ? L
* also known as wildcard represents all valid values. ? means you want to omit the field, usually you use it on the day of month or day of week. L means last day of the month or week. , can be used to specify multiple entries. - can be used to specify range. W means the closest business day to given day of month
?
L
-
W
Multiple cron expressions can be specified by joining them using |. Refer the wikipedia for more information. If you are not sure, just type it out and the editor will show you whether it is valid and the next 3 schedules if valid.
|
; Period: L * ?
; Period: 5 * ?
; Period: ? * 0
; Period: 1 JAN ? | 7 FEB ?
; Period: 15W * ?
Recurring page will only display a transaction as recurring if there is more than one transaction with the same tag name. If you have only one transaction, wait untill the next transaction is added to see it on the recurring page.
Sheet is a notepad calculator that computes the answer as you type. It has full access to your ledger and can be used to calculate the answers for a wide variety of financial questions.
Sheet is an experimental feature. Give it a try and let me know how it goes, specially what is missing and what can be improved. The syntax and semantics of sheet may change in future releases.
Sheet can act as a normal calculator. The example below shows how to calculate the monthly EMI for a home loan.
# Home Loan\nprice = 40,00,000\ndown_payment = 20% * price\nfinance_amount = price - down_payment\ninterest_rate = 8.6%\nterm = 30\n\nn = term * 12\nr = interest_rate / 12\n# EMI\nmonthly_payment = r / (1 - (1 + r) ^ (-n)) * finance_amount\n
# Home Loan\n40,00,000\n8,00,000\n32,00,000\n0\n30\n\n360\n0\n# EMI\n24832\n
Sheets comes with a set of built-in functions. You can also define your own functions. The example below shows how to define a function
# Years to Double\nyears_to_double(rate) = 72 / rate\n\nyears_to_double(2)\nyears_to_double(4)\nyears_to_double(6)\nyears_to_double(8)\nyears_to_double(10)\nyears_to_double(12)\nyears_to_double(14)\n
# Years to Double\n\n\n36\n18\n12\n9\n7\n6\n5\n
Query is what makes sheets powerful. It allows you to query your ledger postings and do calculations on them. The example below shows how to calculate cost basis of your assets so you can report them to Income Tax department.
# Schedule AL\ndate_query = {date <= [2023-03-31]}\ncost_basis(x) = cost(fifo(x AND date_query))\ncost_basis_negative(x) = cost(fifo(negate(x AND date_query)))\n\n# Immovable\nimmovable = cost_basis({account = Assets:House})\n\n# Movable\nmetal = 0\nart = 0\nvehicle = 0\nbank = cost_basis({account = /^Assets:Checking/})\nshare = cost_basis({account =~ /^Assets:Equity:.*/ OR\n account =~ /^Assets:Debt:.*/})\ninsurance = 0\nloan = 0\ncash = 0\n\n# Liability\nliability = cost_basis_negative({account =~ /^Liabilities:Homeloan/})\n\n# Total\ntotal = immovable + metal + art + vehicle + bank + share + insurance + loan + cash - liability\n
# Schedule AL\n\n\n\n\n# Immovable\n25,00,000\n\n# Movable\n0\n0\n0\n1,21,402\n66,98,880\n\n0\n0\n0\n\n# Liability\n6,21,600\n\n# Total\n86,98,682\n
Sheet allows comma as a separator. % is a syntax sugar for dividing the number by 100. So 8% is same as 0.08.
%
8%
0.08
100,00\n100.00\n-100\n8%\n
Sheet supports the following operators. ^ is the exponentiation operator.
^
1 + 1\n1 - 1\n1 * 1\n1 / 1\n1 ^ 2\n
You can define variables using = operator. Variables have to be defined before they are used.
=
x = 1\ny = 2\n\nz = x + y\n
Sheet comes with a set of built-in functions. You can also define your own functions. Function definition starts with the function name and then a list of arguments. The body of the function is a single expression. To call a function, use the function name followed by arguments.
sum(a, b) = a + b\n\nsum(1, 2)\n
Query is a first class citizen in sheet. You can think of a query as a list of postings that match the conditions. The query syntax is same as the search syntax used in postings and transactions pages. It has to be enclosed inside a {}. Query can be treated as any other value, can be passed as an argument, can be assigned to a variable. In fact you can combine two queries using AND and OR operator as well.
{}
query = {account = Expenses:Utilities AND payee =~ /uber/i}\n\nupto_this_fy = {date < [2024-04-01]}\nassets = { account =~ /^Assets:.*/ }\nliabilities = { account =~ /^Liabilities:.*/ }\n\nassets_upto_this_fy = upto_this_fy AND assets\nliabilities_upto_this_fy = upto_this_fy AND liabilities\n
cost({account = Assets:Checking})
Sheet supports single line comments starting with ; or //.
//
// set x\nx = 1\n; set y\ny = 2\n\nz = x + y // add x and y\n
You can find the full list of built-in functions here. This list is very slim as of now, please start a discussion if you want some functions to be added here.
cost(q: Posting[] | Query): number
Returns the sum of the cost of all the postings.
balance(q: Posting[] | Query): number
Returns the sum of the current market value of all the postings.
fifo(q: Posting[] | Query): Posting[]
Returns the list of postings after performing a FIFO adjustment. For example, assume you have 3 postings
2020/01/01 Buy\n Assets:Bank:Checking 100 USD\n2020/02/01 Buy\n Assets:Bank:Checking 100 USD\n2020/03/01 Sell\n Assets:Bank:Checking -50 USD\n
after FIFO adjustment, you will get the following postings
2020/01/01 Buy\n Assets:Bank:Checking 50 USD\n2020/02/01 Buy\n Assets:Bank:Checking 100 USD\n
All your sell postings will be adjusted against the oldest buy postings and you will have the remaining buy postings.
negate(q: Posting[] | Query): Posting[]
Negates the amount and quantity of all the postings. This would be useful when you want to do some calculation on liabilities or income which are negative in nature.
You can setup username and password to make sure only you can access the application. Go to Configuration page and expand the User Accounts section. You can click the icon to add a new username and password. Once you save the configuration, you will get logged out if you had not logged in earlier via another account.
It is important to understand that authentication only protects the application user interface. If someone can access your computer and they can access the folder where Paisa stores the ledger and database files, they will be able to view your data.
Paisa uses cryptographic hash function sha-256 to convert your password to digest before storing it in the configuration file. This has few implications. No one can look at the configuration file and get the password, this includes you as well. If you forget the password, you can remove the user accounts from configuration file to get back access.
If you run paisa on a server and access it over public internet, make sure to use it over https and use a strong password. If you run it over plain http, man in the middle attack can be performed to obtain the data including your password.
Goal helps you track your financial objective and progress. You can create a goal for any financial objective you have, such as saving for a vacation, or building an emergency fund or planning for retirement.
Paisa currently supports two types of goals:
More goal types will be added in the future. Feel free to create a discussion if you have any suggestions.
To add a new goal, go to the Configuration page and expand the goals section. You would see a list of goal types. Expand the type you want to add and click on the icon to add a new goal. The configuration details are available in the respective goal type pages.
Under Development
Goals is currently under development and you might see changes with every new release. If you want to share feedback, this is a good time to do so.
Paisa will help you plan your retirement and track your progress. The first part is figuring out what should be your retirement corpus. This will be your target. Instead of specifying the amount explicitly, you can specify your expected yearly expenses and the safe withdrawal rate.
goals:\n retirement:\n - name: Retirement\n icon: mdi:palm-tree\n swr: 3.3\n yearly_expenses: 1100000\n
If you use paisa to track expenses, instead of specifying the yearly_expenses, you can specify the list of accounts. Paisa will take the average of the last 3 year expenses
yearly_expenses
goals:\n retirement:\n - name: Retirement\n icon: mdi:palm-tree\n swr: 2\n expenses:\n - Expenses:Entertainment\n - Expenses:Gift\n - Expenses:Insurance\n - Expenses:Misc\n - Expenses:Shopping\n - Expenses:Utilities\n
Now that the target is specified, you need to specify the list of accounts where you keep your retirement savings.
goals:\n retirement:\n - name: Retirement\n icon: mdi:palm-tree\n swr: 2\n expenses:\n - Expenses:Entertainment\n - Expenses:Gift\n - Expenses:Insurance\n - Expenses:Misc\n - Expenses:Shopping\n - Expenses:Utilities\n savings:\n - Assets:Equity:*\n - Assets:Debt:*\n
Savings represents a general financial objective that you want to achieve, like buying a car, or a house, or a vacation.
goals:\n savings:\n - name: House\n target: 5000000\n icon: fluent-emoji-high-contrast:house-with-garden\n accounts:\n - Assets:Equity:*\n - Assets:Debt:*\n
Specify the target amount, and the accounts that you keep the money in.
When you save towards an objective, you will have a target date in mind. In the below config, you are specifying the target date and rate. Paisa will calculate the monthly contribution required to achieve the goal.
goals:\n savings:\n - name: House\n target: 5000000\n target_date: 2030-05-01\n rate: 10\n icon: fluent-emoji-high-contrast:house-with-garden\n accounts:\n - Assets:Equity:*\n - Assets:Debt:*\n
If on the other hand, you know how much you can afford to save every month, but want to know when the goal will be achieved, you can specify the payment per period and the rate. Paisa will calculate the target date.
goals:\n savings:\n - name: House\n target: 5000000\n payment_per_period: 50000\n rate: 10\n icon: fluent-emoji-high-contrast:house-with-garden\n accounts:\n - Assets:Equity:*\n - Assets:Debt:*\n
This section will give some rough idea of how the calculations are done. You can skip this section if you are not interested in the math.
Where
target
payment_per_period
rate
target_date
Out of the 5 variables, if you know any 4, the 5th can be calculated by solving the equation. You can refer the blog post for more details.
Paisa provides few features to help with tax filing and tax optimization.
The author of this tool is not an accountant, the calculations are made based on their interpretation of rules and regulation. This tool is distributed free hoping it would be useful. The users are advised to consult a tax accountant before making any decision based on the information provided here. Check the LICENSE of paisa for more details.
Mutual Funds are considered as capital asset and capital gains tax will be charged on any gains. Capital Gains page shows the financial year wise capital gains tax based on the withdrawal transaction. The sum total, account wise breakdown, and per buy/sell match information are available.
Only commodities with tax_category are considered. Please check Tax Harvesting page for configuration details.
tax_category
The difference between the purchase and sell price.
The gain after applying Indexation or Grandfathering provision.
Based on the duration between the purchase and sell date, an asset might be classified as Short Term or Long Term.
This would be a percentage of the Taxable Gain. The percentage varies based on the asset class.
There are cases where the gain would be added to taxable income and charged Slab rate. Since the tax rate would depend on the person, the whole taxable amount is shown instead of the tax. You can multiply this with your slab rate to get the tax amount.
As per the Indian Income tax law, citizens are obligated to report their entire Assets and Liabilities if the total income exceeds \u20b950 lakh. Paisa helps with the computation of the amount.
All you need to do is to specify the accounts that belong to each section. Paisa will compute the total as on the last day of the previous financial year.
schedule_al:\n - code: bank\n accounts:\n - Assets:Checking\n - code: share\n accounts:\n - Assets:Equity:*\n - Assets:Debt:*\n
Gains from asset types like Mutual Fund, Stock are subject to capital gains tax. The taxation rate itself differs based on how long you hold your investment, whether it's debt or equity, whether you have more than 1 Lakh gains in a year etc. To further complicate things, there might be exit load applicable.
Paisa takes a generic approach and let the user configure when a commodity is safe to harvest. There are two fields related to tax harvesting.
harvest - specifies the number of days after which the commodity is eligible for tax harvesting. This should be set based on fund exit load, fund category etc
tax_category - This defines how the taxes are calculated as the government usually tweaks the tax code regularly with various rules like grandfathering, cost inflation index adjustment, etc.
equity65 - This is for 65% or more investment in Indian equity.
equity65
equity35 - This if for 35% or more investment in India equity but less than 65%
equity35
debt - This is for debt funds.
debt
unlisted_equity - This is for unlisted foreign on Indian equity.
unlisted_equity
commodities:\n - name: NIFTY\n type: mutualfund\n code: 120716\n harvest: 365\n tax_category: equity65\n - name: ABSTF\n type: mutualfund\n code: 119533\n harvest: 1095\n tax_category: debt\n
The tax harvest page will show the list of accounts eligible for harvesting.
Paisa uses FIFO method within an Account. If you have multiple folios, this might result in incorrect values. The issue can be solved by using a different Account for each folio.
Paisa is licensed under AGPL v3, which helps with this issue to some extent. I have yet to figure out a solution to make the app development and maintenance process sustainable in the long term. But rest assured, any decision that would be made related to this will not override the first two points.