Coverage for hledger_lots/cli.py: 47%
118 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-05 13:34 -0300
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-05 13:34 -0300
1from typing import Tuple, TypedDict
2import os
3import rich_click as click
5from .avg_info import AllAvgInfo, AvgInfo
6from .fifo_info import AllFifoInfo, FifoInfo
7from .info import AllInfo
8from .lib import get_files_comm, get_default_file, get_file_from_stdin
9from .options import Options, get_options
10from .prices_yahoo import YahooPrices
11from .prompt import get_append_file
12from .prompt_buy import PromptBuy
13from .prompt_sell import PromptSell
16class Obj(TypedDict):
17 file: Tuple[str, ...]
18 opt: Options
21CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
23click.rich_click.USE_MARKDOWN = True
24click.rich_click.SHOW_ARGUMENTS = True
25click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
26click.rich_click.MAX_WIDTH = 80
27click.rich_click.SHOW_METAVARS_COLUMN = False
28click.rich_click.APPEND_METAVARS_HELP = True
29click.rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
30click.rich_click.ERRORS_SUGGESTION = (
31 "Try running the '--help' flag for more information."
32)
33click.rich_click.ERRORS_EPILOGUE = "To find out more, visit [link=https://github.com/edkedk99/hledger-lots]https://github.com/edkedk99/hledger-lots[/link]"
34click.rich_click.STYLE_OPTIONS_TABLE_LEADING = 1
35click.rich_click.STYLE_OPTIONS_TABLE_BOX = "SIMPLE"
36click.rich_click.STYLE_OPTIONS_PANEL_BORDER = "dim" # Possibly conceal
39@click.group(context_settings=CONTEXT_SETTINGS)
40@click.option(
41 "-f",
42 "--file",
43 required=True,
44 default=lambda: get_default_file(),
45 multiple=True,
46 help="Inform the journal file path. If \"-\", read from stdin. Without this flag read from $LEDGER_FILE or ~/.hledger.journal in this order or '-f-'.",
47)
48@click.pass_obj
49@click.version_option()
50def cli(obj, file: Tuple[str, ...]):
51 """
52 Commands to apply FIFO(first-in-first-out) or AVERAGE COST accounting principles without manual management of lots. Useful for transactions involving buying and selling foreign currencies or stocks.
54 To find out more, visit [https://github.com/edkedk99/hledger-lots](https://github.com/edkedk99/hledger-lots)
55 """
59 if file[0] == "-":
60 stdin_file = (get_file_from_stdin(),)
61 opt = get_options(stdin_file)
62 obj["file"] = stdin_file
63 else:
64 opt = get_options(file)
65 obj["file"] = file
67 obj["opt"] = opt
70@click.command()
71@click.pass_obj
72def buy(obj: Obj):
73 """
74 Create a purchase transaction for a commodity by answering some prompts that tries to avoid errors with validation and using current journal data to filter possible answers and give informations that guides the user thru the process.\r
76 ### Transaction postings
78 - First posting: Negative amount on the cash account where the money was used to pay for the commodity
80 - Second Posting: Positive amount of the commodity being bought with its cost using \"@\" symbol
81 """
83 file = obj["file"]
84 opt = obj["opt"]
85 prompt_buy = PromptBuy(file, opt.avg_cost, opt.check, opt.no_desc)
86 txn_print = prompt_buy.get_hl_txn()
87 click.echo("\n" + txn_print)
89 append_file = get_append_file(file[0])
90 if append_file:
91 with open(append_file, "a") as f:
92 f.write("\n" + txn_print)
93 else:
94 click.echo("\n" + "Transaction not saved.")
96 commodity = prompt_buy.info["comm"]
97 if opt.avg_cost:
98 info = AvgInfo(file, commodity, opt.check)
99 else:
100 info = FifoInfo(file, commodity, opt.check)
102 click.echo(info.table)
103 click.echo(info.info_txt)
106@click.command()
107@click.pass_obj
108def sell(obj: Obj):
109 """
110 Create a transaction with automatic FIFO or AVERAGE COST for a commodity by answering some prompts that tries to avoid errors with validation and using current journal data to filter possible answers give informations that guides the user thru the process.\r
112 > This command also add transaction's comment with some indicators. See an example on "Output examples" page of the docs.
114 ### Transaction postings
116 - First posting: Positive amount on the 'base-currency' in the account that receives the product of the sale.
118 - Multiple lot postings: Each posting represents a lot you are selling for the cost price on purchasing date, according to FIFO accounting principles or one postings in case of AVERAGE COST method.
120 - Revenue posting: posting that represent Capital Gain or Loss as the difference between the total cost and the amount received on the base-currency.
121 """
122 file = obj["file"]
123 opt = obj["opt"]
124 prompt_sell = PromptSell(file, opt.avg_cost, opt.check, opt.no_desc)
126 txn_print = prompt_sell.get_hl_txn()
127 click.echo("\n" + txn_print)
129 append_file = get_append_file(file[0])
130 if append_file:
131 with open(append_file, "a") as f:
132 f.write("\n" + txn_print)
133 else:
134 click.echo("\n" + "Transaction not saved.")
136 commodity = prompt_sell.info["comm"]
137 if opt.avg_cost:
138 info = AvgInfo(file, commodity, opt.check)
139 else:
140 info = FifoInfo(file, commodity, opt.check)
142 click.echo(info.table)
143 click.echo(info.info_txt)
145 if commodity.startswith("y."):
146 files_comm_str = " ".join(get_files_comm(file))
147 click.echo(
148 f"To update prices from Yahoo finance, run:\n\n hledger-lots {files_comm_str} view -c {commodity} -p {file[0]}"
149 )
152@click.command()
153@click.argument("commodity", type=click.STRING, required=True)
154@click.pass_obj
155def view(obj: Obj, commodity: str):
156 """
157 Report lots for a commodity.\r
159 Show a report with lots for a commodity considering eventual past sale using FIFO or AVERAGE COST accounting principles.
161 Also show some indicators about the lots and performance if there is prices in the journal after the last purchase. See the docs for details
162 """
163 file = obj["file"]
164 opt = obj["opt"]
166 if opt.avg_cost:
167 info = AvgInfo(file, commodity, opt.check, opt.no_desc)
168 else:
169 info = FifoInfo(file, commodity, opt.check, opt.no_desc)
171 click.echo(info.table)
172 click.echo(info.info_txt)
175@click.command(name="list")
176@click.option(
177 "-o",
178 "--output-format",
179 type=click.Choice(["plain", "pretty", "csv"]),
180 default="plain",
181 help="Format to output the report",
182)
183@click.pass_obj
184def list_commodities(obj: Obj, output_format: str):
185 """
186 List indicators for all your commodities in a tabular format sorted from higher to lower **XIRR**. It is advised to use full-screen of the terminal. See the docs for a list of indicators and output examples.
188 It can output in three formats: *plain, pretty and csv*.
189 """
191 file = obj["file"]
192 opt = obj["opt"]
193 lots_info = AllInfo(file, opt.no_desc)
195 lots_info = (
196 AllAvgInfo(file, opt.no_desc, opt.check)
197 if opt.avg_cost
198 else AllFifoInfo(file, opt.no_desc, opt.check)
199 )
201 if output_format == "pretty":
202 table = lots_info.infos_table("mixed_grid")
203 elif output_format == "csv":
204 infos_io = lots_info.infos_csv()
205 table = infos_io.read()
206 else:
207 table = lots_info.infos_table("plain")
209 click.echo(table)
212@click.command()
213@click.pass_obj
214def prices(obj: Obj):
215 """
216 Download market prices from Yahoo Finance and print as **price directives**. Use *BASH* redirection to append to the journal or copy/paste the data.
218 ### Setup
219 Add a \"yahoo_ticker\" tag to the *commodity directive* with the value of the ticker in Yahoo Finance to download prices
221 ### Example
223 ```text
224 commodity AAPL ; yahoo_ticker:AAPL
225 commodity \"PETR4\" ; yahoo_ticker:PETR4.SA
226 commodity BTC ; yahoo_ticker:BTC-USD
227 ```
228 """
230 file = obj["file"]
232 yahoo_prices = YahooPrices(file)
233 yahoo_prices.print_prices()
236cli.add_command(buy)
237cli.add_command(sell)
238cli.add_command(view)
239cli.add_command(list_commodities)
240cli.add_command(prices)