Coverage for hledger_lots/prompt_buy.py: 25%
71 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 22:41 -0300
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 22:41 -0300
1import subprocess
2from dataclasses import dataclass
3from typing import Optional, Tuple
5import questionary
7from . import prompt
8from .info import LotsInfo
11@dataclass
12class BuyInfo(prompt.Tradeinfo):
13 base_cur: str
16def val_buy_qtty(answer: str):
17 try:
18 answer_float = float(answer)
19 except ValueError:
20 return "Invalid number"
22 if answer_float <= 0:
23 return "Quantity should be positive"
25 return True
28class PromptBuy(prompt.Prompt):
29 def __init__(
30 self,
31 file: Tuple[str, ...],
32 avg_cost: bool,
33 check: bool,
34 no_desc: Optional[str] = None,
35 ) -> None:
36 super().__init__(file, avg_cost, check, no_desc)
37 all_commodities_txt = self.run_hledger_no_query_desc("commodities")
38 self.all_commodities = [
39 com for com in all_commodities_txt.split("\n") if com != ""
40 ]
42 print(
43 """
44 Append a new purchase of a commodity.
45 Use format format "y.ticker" to automatically download prices.
46 Search on yahoo finance website the ticker for the commodity.\n"""
47 )
48 print(self.initial_info)
49 self.info = self.get_info()
50 self.last_purchase = self.get_last_purchase(self.info)
52 def get_info(self):
53 commodity = prompt.ask_commodities_text(self.all_commodities)
54 info_not_found = LotsInfo(
55 comm=commodity,
56 cur="",
57 qtty="0",
58 amount="0",
59 avg_cost="0",
60 mkt_price=None,
61 mkt_date=None,
62 mkt_amount=None,
63 mkt_profit=None,
64 xirr=None,
65 )
66 info = next(
67 (info for info in self.infos if info["comm"] == commodity), info_not_found
68 )
69 return info
71 def ask_base_cur_text(self):
72 if self.info["cur"] == "":
73 answer: str = prompt.custom_autocomplete(
74 "Base Currency", self.all_commodities
75 ).ask()
76 else:
77 answer = self.info["cur"]
78 return answer
80 def ask_buy_qtty(self, info: LotsInfo):
81 available = float(info["qtty"])
83 answer_str: str = questionary.text(
84 f"Quantity (available {available})",
85 validate=val_buy_qtty,
86 instruction="",
87 ).ask()
88 return answer_str
90 def ask_commodity_account(self):
91 accts_txt = self.run_hledger("accounts")
92 accts = [acct for acct in accts_txt.split("\n") if acct != ""]
93 answer: str = prompt.custom_autocomplete("Commodity Account", accts).ask()
94 return answer
96 def prompt(self):
97 commodity = self.info["comm"]
98 base_cur = self.ask_base_cur_text()
99 sell_date = self.ask_date(self.last_purchase)
100 qtty = float(self.ask_buy_qtty(self.info))
101 price_str = self.ask_price(self.info)
103 if price_str == "":
104 value_str = self.ask_total(qtty, self.info)
105 value = float(value_str)
106 price = value / qtty
107 else:
108 price = float(price_str)
109 value = qtty * price
111 cash_acct = self.ask_cash_account()
113 commodity_acct = self.ask_commodity_account()
115 result = BuyInfo(
116 date=sell_date,
117 quantity=qtty,
118 commodity=commodity,
119 base_cur=base_cur,
120 cash_account=cash_acct,
121 commodity_account=commodity_acct,
122 price=price,
123 value=value,
124 )
125 return result
127 def get_hl_txn(self):
128 buy = self.prompt()
130 txn_raw = f"""
131{buy.date} Buy {buy.commodity}
132 {buy.commodity_account} {buy.quantity} \"{buy.commodity}\" @ {buy.price} \"{buy.base_cur}\"
133 {buy.cash_account}
134"""
136 comm = ["hledger", "-f-", "print", "--explicit"]
137 txn_proc = subprocess.run(
138 comm,
139 input=txn_raw.encode(),
140 capture_output=True,
141 )
142 if txn_proc.returncode != 0:
143 err = txn_proc.stderr.decode("utf8")
144 raise prompt.PromptError(err)
146 txn_print: str = txn_proc.stdout.decode("utf8")
147 return txn_print