def test_insert_entry_align(tmpdir): file_content = dedent(""" 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD """) samplefile = tmpdir.mkdir('fava_util_file3').join('example.beancount') samplefile.write(file_content) postings = [ data.Posting('Liabilities:US:Chase:Slate', amount.Amount(D('-10.00'), 'USD'), None, None, None, None), data.Posting('Expenses:Food', amount.Amount(D('10.00'), 'USD'), None, None, None, None), ] transaction = data.Transaction(None, datetime.date(2016, 1, 1), '*', 'new payee', 'narr', None, None, postings) fava_options = { 'currency-column': 50, } insert_entry(transaction, [str(samplefile)], fava_options) assert samplefile.read() == dedent(""" 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD 2016-01-01 * "new payee" "narr" Liabilities:US:Chase:Slate -10.00 USD Expenses:Food 10.00 USD """)
def extract(self, file): entries = [] with open(file.name) as file: for index, row in enumerate(csv.DictReader(file)): trans_date = parse(row['date']).date() trans_desc = titlecase(row['name'].rstrip()) trans_amt = row['amount'] meta = data.new_metadata(file.name, index) txn = data.Transaction(meta=meta, date=trans_date, flag=flags.FLAG_OKAY, payee=trans_desc, narration="", tags=set(), links=set(), postings=[]) if D(trans_amt) > 0: txn.postings.append( data.Posting('Assets:VSCU:Savings', amount.Amount(D(trans_amt), 'USD'), None, None, None, None)) txn.postings.append( data.Posting('FIXME', None, None, None, None, None)) else: txn.postings.append( data.Posting('FIXME', amount.Amount(D(trans_amt), 'USD'), None, None, None, None)) txn.postings.append( data.Posting('Assets:VSCU:Savings', None, None, None, None, None)) entries.append(txn) return entries
def get_position_market_value(position_, date, price_map): """Compute the market value of the position at a particular date. If the price map does not contain price information, we avoid converting the position and return itself, unchanged. Args: position: An instance of Position date: A datetime.date instance, the date at which to market the instruments. If the date provided is None, the inventory is valued at the latest market prices. price_map: A price map, as created by beancount.ops.prices. Returns: An inventory of market values per currency. """ lot = position_.lot cost_currency = lot.cost.currency if lot.cost else None if cost_currency: base_quote = (lot.currency, cost_currency) price_date, price_number = get_price(price_map, base_quote, date) if price_number is None: return position_ else: new_amount = amount.Amount(position_.number * price_number, cost_currency) else: new_amount = amount.Amount(position_.number, lot.currency) return position.from_amounts(new_amount)
def get_pholding_market_value(pholding): """Convert a Posting to its market value. This function assumes that the Posting instance already has a non-null 'price' attribute. Args: holding: An instance of beancount.core.data.Posting. Returns: An instance of Amount. """ price = pholding.price position_ = pholding.position if price is None: cost = position_.lot.cost if cost: return amount.Amount(position_.number * cost.number, cost.currency) else: return amount.Amount(position_.number, position_.lot.currency) else: assert price.currency != position_.lot.currency, ( "Invalid currency: '{}'".format(pholding)) cost = position_.lot.cost if cost: assert price.currency == cost.currency, ( "Invalid currency vs. cost: '{}'".format(pholding)) return amount.Amount(position_.number * price.number, price.currency)
def createSingle(self, payout, withholding, quantity, assetAccount, asset, currency, date, priceLookup, description): narration = "Dividend for " + str(quantity) + " : " + description liquidityAccount = self.getLiquidityAccount(assetAccount, asset, currency) incomeAccount = self.getIncomeAccount(assetAccount, asset) price = priceLookup.fetchPrice(currency, date) postings = [ data.Posting(assetAccount, amount.Amount(D(0), asset), None, None, None, None), data.Posting(liquidityAccount, amount.Amount(payout, currency), None, price, None, None), ] if withholding > 0: receivableAccount = self.getReceivableAccount(assetAccount, asset) postings.append( data.Posting(receivableAccount, amount.Amount(withholding, currency), None, None, None, None) ) postings.append( data.Posting(incomeAccount, None, None, None, None, None) ) meta = data.new_metadata('dividend', 0) return data.Transaction( meta, date, '*', '', narration, data.EMPTY_SET, data.EMPTY_SET, postings )
def extract(self, file, existing_entries=None): entries = [] meta = data.new_metadata(file.name, 0) txn = data.Transaction( meta, parse('2017-11-20').date(), '*', None, 'Two Postings', data.EMPTY_SET, data.EMPTY_SET, [ data.Posting('Assets:Patrick:CHF', amount.Amount(D('12'), 'CHF'), None, None, None, None), data.Posting('Assets:Patrick:USD', amount.Amount(D('12'), 'CHF'), None, None, None, None), ] ) entries.append(txn) txn = data.Transaction( meta, parse('2017-11-20').date(), '*', None, 'Single Posting', data.EMPTY_SET, data.EMPTY_SET, [ data.Posting('Assets:Patrick:CHF', amount.Amount(D('12'), 'CHF'), None, None, None, None), ] ) entries.append(txn) return entries
def extractRow(self, row, meta): trans_date = self.getDate(row) trans_desc = self.getDesc(row) trans_amt = self.getAmt(row) account = self.getAccount(row) txn = data.Transaction( meta=meta, date=trans_date, flag=flags.FLAG_OKAY, payee=trans_desc, narration=self.getMemo(row), tags=set(), links=set(), postings=[ data.Posting( self.account, amount.Amount(-1*D(trans_amt), 'USD'), None, None, None, None ), ], ) if account is not None: txn.postings.append(data.Posting( account, amount.Amount(D(trans_amt), 'USD'), None, None, None, None )) return txn
def test__render_transaction(): postings = [ data.Posting('Liabilities:US:Chase:Slate', amount.Amount(D('-10.00'), 'USD'), None, None, None, None), data.Posting('Expenses:Food', amount.Amount(None, None), None, None, None, None), ] transaction = data.Transaction(None, datetime.date(2016, 1, 1), '*', 'new payee', 'narr', None, None, postings) assert '\n' + _render_transaction(transaction) == dedent(""" 2016-01-01 * "new payee" "narr" Liabilities:US:Chase:Slate -10.00 USD Expenses:Food""") postings = [ data.Posting('Liabilities:US:Chase:Slate', amount.Amount(D('-10.00'), 'USD'), None, None, None, None), data.Posting('Expenses:Food', amount.Amount(D('10.00'), 'USD'), None, None, None, None), ] transaction = data.Transaction(None, datetime.date(2016, 1, 1), '*', 'new payee', 'narr', None, None, postings) print(_render_transaction(transaction)) assert '\n' + _render_transaction(transaction) == dedent(""" 2016-01-01 * "new payee" "narr" Liabilities:US:Chase:Slate -10.00 USD Expenses:Food 10.00 USD""")
def balayageJSONtable(self, jsondata, afficherCost: bool = False): """Une procƩdure qui balaye toutes les lignes du JSON""" self.postings = [] self.total = 0 for ligne in jsondata["table"]: # Si debogage, affichage de l'extraction if self.debug: print(ligne) print(parse_datetime(ligne["date"]).date) if ligne["valeurpart"] == "": ligne["valeurpart"] = "1.00" ligne["nbpart"] = ligne["montant"] if afficherCost and re.match("-", ligne["nbpart"]) is None: cost = position.Cost( Decimal( float(ligne["montant"].replace(",", ".").replace( " ", "").replace("\xa0", "").replace(r"\u00a", "")) / float(ligne["nbpart"].replace(",", ".").replace( " ", "").replace("\xa0", "").replace( r"\u00a", ""))).quantize(Decimal(".0001")), "EUR", None, None, ) else: cost = None self.postings.append( data.Posting( account=self.accountList[jsondata["compte"]] + ":" + ligne["isin"].replace(" ", "").upper(), units=amount.Amount( Decimal(ligne["nbpart"].replace(",", ".").replace( " ", "").replace("\xa0", "").replace(r"\u00a", "")), ligne["isin"].replace(" ", "").upper(), ), cost=cost, flag=None, meta=None, price=amount.Amount( Decimal( abs( float(ligne["montant"].replace( ",", ".").replace(" ", "").replace( "\xa0", "").replace(r"\u00a", "")) / float( ligne["nbpart"].replace(",", ".").replace( " ", "").replace("\xa0", "").replace( r"\u00a", "")))).quantize( Decimal(".0001")), "EUR", ), )) self.total = self.total + Decimal(ligne["montant"].replace( ",", ".").replace(" ", "").replace("\xa0", "").replace( r"\u00a", ""))
def test_insert_entry_align(tmpdir): file_content = dedent( """ 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD """ ) samplefile = tmpdir.mkdir("fava_util_file3").join("example.beancount") samplefile.write(file_content) postings = [ data.Posting( "Liabilities:US:Chase:Slate", amount.Amount(D("-10.00"), "USD"), None, None, None, None, ), data.Posting( "Expenses:Food", amount.Amount(D("10.00"), "USD"), None, None, None, None, ), ] transaction = data.Transaction( {}, datetime.date(2016, 1, 1), "*", "new payee", "narr", None, None, postings, ) fava_options = {"currency-column": 50} insert_entry(transaction, [str(samplefile)], fava_options) assert samplefile.read() == dedent( """ 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD 2016-01-01 * "new payee" "narr" Liabilities:US:Chase:Slate -10.00 USD Expenses:Food 10.00 USD """ )
def extract(self, file, existing_entries): entries = [] has_balance = False with StringIO(file.contents()) as csvfile: reader = csv.DictReader(csvfile, [ 'Date', 'Reference', 'PaidOut', 'PaidIn', 'ExchangeOut', 'ExchangeIn', 'Balance', 'Category' ], delimiter=';', skipinitialspace=True) next(reader) for row in reader: metakv = { 'category': row['Category'].strip(), } exchangeIn = row['ExchangeIn'].strip() exchangeOut = row['ExchangeOut'].strip() if exchangeIn and exchangeOut: metakv['originalIn'] = exchangeIn metakv['originalOut'] = exchangeOut elif exchangeIn: metakv['original'] = exchangeIn elif exchangeOut: metakv['original'] = exchangeOut book_date = parse(row['Date'].strip()).date() try: credit = D(row['PaidIn'].replace('\'', '').strip()) debit = D(row['PaidOut'].replace('\'', '').strip()) bal = D(row['Balance'].replace('\'', '').strip()) amt = amount.Amount(credit - debit, self.currency) balance = amount.Amount(bal, self.currency) except Exception as e: logging.warning(e) continue meta = data.new_metadata(file.name, 0, metakv) entry = data.Transaction( meta, book_date, '*', '', row['Reference'].strip(), data.EMPTY_SET, data.EMPTY_SET, [ data.Posting(self.account, amt, None, None, None, None), ]) entries.append(entry) # only add balance after the top (newest) transaction if not has_balance: book_date = book_date + timedelta(days=1) entry = data.Balance(meta, book_date, self.account, balance, None, None) entries.append(entry) has_balance = True return entries
def extract(self, file, existing_entries=None) -> list: # TODO: check for duplicates with open(file.name, "rb") as _file: transactions = pickle.load(_file) entries = [] transactions = sorted(transactions, key=lambda tx: (tx["time"], tx["tx_id"])) for tx_id, transfers in groupby(transactions, lambda tx: tx["tx_id"]): tx_date = None metadata = {"txid": tx_id} postings = [] for transfer in transfers: if tx_date is None: tx_date = transfer["time"].date() if transfer["value"] == 0: continue account_from = self._find_account( transfer["from"], -transfer["value"], transfer["currency"], ) posting_from = data.Posting( account_from, amount.Amount(D(-transfer["value"]), transfer["currency"]), None, None, None, None, ) postings.append(posting_from) account_to = self._find_account( transfer["to"], transfer["value"], transfer["currency"], ) posting_to = data.Posting( account_to, amount.Amount(D(transfer["value"]), transfer["currency"]), None, None, None, None, ) postings.append(posting_to) entry = data.Transaction( data.new_metadata("", 0, metadata), tx_date, "*", "", "", data.EMPTY_SET, data.EMPTY_SET, postings, ) entries.append(entry) return entries
def Dividends(self, match): # this function crates Dividend transactions from IBKR data # make dividend & WHT transactions divTransactions = [] for idx, row in match.iterrows(): currency = row['currency_x'] currency_wht = row['currency_y'] if currency != currency_wht: warnings.warn('Warnging: Dividend currency {} ' + 'mismatches WHT currency {}. Skipping this' + 'Transaction'.format(currency, currency_wht)) continue symbol = row['symbol'] amount_div = amount.Amount(row['amount_x'], currency) amount_wht = amount.Amount(row['amount_y'], currency) text = row['description_x'] # Find ISIN in description in parentheses isin = re.findall('\(([a-zA-Z]{2}[a-zA-Z0-9]{9}\d)\)', text)[0] pershare_match = re.search('(\d*[.]\d*)(\D*)(PER SHARE)', text, re.IGNORECASE) # payment in lieu of a dividend does not have a PER SHARE in description pershare = pershare_match.group(1) if pershare_match else '' # make the postings, three for dividend/ wht transactions postings = [ data.Posting(self.getDivIncomeAcconut(currency, symbol), -amount_div, None, None, None, None), data.Posting(self.getWHTAccount(symbol), -amount_wht, None, None, None, None), data.Posting(self.getLiquidityAccount(currency), AmountAdd(amount_div, amount_wht), None, None, None, None) ] meta = data.new_metadata('dividend', 0, { 'isin': isin, 'per_share': pershare }) divTransactions.append( data.Transaction( meta, # could add div per share, ISIN,.... row['reportDate'], self.flag, symbol, # payee 'Dividend ' + symbol, data.EMPTY_SET, data.EMPTY_SET, postings)) return divTransactions
def Shopping(self, buy): # let's go shopping!! Shoppingbag = [] for idx, row in buy.iterrows(): # continue # debugging currency = row['currency'] currency_IBcommision = row['ibCommissionCurrency'] symbol = row['symbol'] proceeds = amount.Amount(row['proceeds'].__round__(2), currency) commission = amount.Amount((row['ibCommission'].__round__(2)), currency_IBcommision) quantity = amount.Amount(row['quantity'], symbol) price = amount.Amount(row['tradePrice'], currency) text = row['description'] number_per = D(row['tradePrice']) currency_cost = currency cost = position.CostSpec(number_per=price.number, number_total=None, currency=currency, date=row['tradeDate'], label=None, merge=False) postings = [ data.Posting(self.getAssetAccount(symbol), quantity, cost, None, None, None), data.Posting(self.getLiquidityAccount(currency), proceeds, None, None, None, None), data.Posting(self.getLiquidityAccount(currency_IBcommision), commission, None, None, None, None), data.Posting(self.getFeesAccount(currency_IBcommision), minus(commission), None, None, None, None) ] Shoppingbag.append( data.Transaction( data.new_metadata('Buy', 0), row['dateTime'].date(), self.flag, symbol, # payee ' '.join( ['BUY', quantity.to_string(), '@', price.to_string()]), data.EMPTY_SET, data.EMPTY_SET, postings)) return Shoppingbag
def guess_postings(self, payee, total_transaction_value): '''Guess postings based on the previous transactions with the same payee. The guess is simply the most recent transaction with the same payee. If the transaction consists of multiple postings, the total_transaction_value is distributed to the postings in the same ratios as in the previous posting. If there is no previous transaction with the same payee, the target account is the default_adjacent_account. Parameters ---------- payee: string total_transaction_value: float ''' new_postings = [] if payee in self.posting_dict: previous_postings = self.posting_dict[payee][-1] accounts = [] units = [] for prev_posting in previous_postings: accounts.append(prev_posting.account) units.append(prev_posting.units) if prev_posting.account == self.account: prev_posting_had_reversed_signs = 0 > float( prev_posting.units.number) * total_transaction_value s = sum([float(u.number) for u in units if u.number > 0]) for account, unit in zip(accounts, units): share = float(unit.number) / s if prev_posting_had_reversed_signs: share = -share p = data.Posting( account, amount.Amount( D(str(round(share * abs(total_transaction_value), 2))), self.currency), None, None, None, None) new_postings.append(p) #move importing_account to the end of the list i = 0 for j, posting in enumerate(new_postings): if posting.account == self.account: i = j new_postings.append(new_postings.pop(i)) else: new_postings.append( data.Posting( self.default_adjacent_account, amount.Amount(D(str(-total_transaction_value)), self.currency), None, None, None, None)) new_postings.append( data.Posting( self.account, amount.Amount(D(str(total_transaction_value)), self.currency), None, None, None, None)) return new_postings
def extract_balances_and_prices(self, file, counter): new_entries = [] date = self.get_max_transaction_date() if date: # balance assertions are evaluated at the beginning of the date, so move it to the following day date += datetime.timedelta(days=1) else: print( "Warning: no transactions, using statement date for balance assertions." ) settlement_fund_balance = 0 for pos in self.get_balance_positions(): ticker, ticker_long_name = self.get_ticker_info(pos.security) meta = data.new_metadata(file.name, next(counter)) # if there are no transactions, use the date in the source file for the balance. This gives us the # bonus of an updated, recent balance assertion bal_date = date if date else pos.date.date() balance_entry = data.Balance( meta, bal_date, self.commodity_leaf(self.config['main_account'], ticker), amount.Amount(pos.units, ticker), None, None) new_entries.append(balance_entry) if ticker in self.money_market_funds: settlement_fund_balance = pos.units # extract price info if available if hasattr(pos, 'unit_price') and hasattr(pos, 'date'): meta = data.new_metadata(file.name, next(counter)) price_entry = data.Price( meta, pos.date.date(), ticker, amount.Amount(pos.unit_price, self.currency)) new_entries.append(price_entry) # ----------------- available cash available_cash = self.get_available_cash() if available_cash is not False: try: balance = self.get_available_cash() - settlement_fund_balance meta = data.new_metadata(file.name, next(counter)) bal_date = date if date else self.file_date(file).date() balance_entry = data.Balance( meta, bal_date, self.cash_account, amount.Amount(balance, self.currency), None, None) new_entries.append(balance_entry) except AttributeError: # self.get_available_cash() pass return new_entries
def Forex(self, fx): # returns beancount transactions for IBKR forex transactions fxTransactions = [] for idx, row in fx.iterrows(): symbol = row['symbol'] curr_prim, curr_sec = getForexCurrencies(symbol) currency_IBcommision = row['ibCommissionCurrency'] proceeds = amount.Amount(row['proceeds'], curr_sec) quantity = amount.Amount(row['quantity'], curr_prim) price = amount.Amount(row['tradePrice'], curr_sec) commission = amount.Amount(row['ibCommission'], currency_IBcommision) buysell = row['buySell'].name cost = position.CostSpec(number_per=None, number_total=None, currency=None, date=None, label=None, merge=False) postings = [ data.Posting(self.getLiquidityAccount(curr_prim), quantity, None, price, None, None), data.Posting(self.getLiquidityAccount(curr_sec), proceeds, None, None, None, None), data.Posting(self.getLiquidityAccount(currency_IBcommision), commission, None, None, None, None), data.Posting(self.getFeesAccount(currency_IBcommision), minus(commission), None, None, None, None) ] fxTransactions.append( data.Transaction( data.new_metadata('FX Transaction', 0), row['tradeDate'], self.flag, symbol, # payee ' '.join([ buysell, quantity.to_string(), '@', price.to_string() ]), data.EMPTY_SET, data.EMPTY_SET, postings)) return fxTransactions
def Interest(self,int_): # calculates interest payments from IBKR data intTransactions=[] for idx, row in int_.iterrows(): currency=row['currency'] amount_=amount.Amount(row['amount'],currency) text=row['description'] month=re.findall('\w{3}-\d{4}',text)[0] # make the postings, two for interest payments # received and paid interests are booked on the same account postings=[data.Posting(self.getInterestIncomeAcconut(currency), -amount_, None, None, None, None), data.Posting(self.getLiquidityAccount(currency), amount_,None, None, None, None) ] meta=data.new_metadata('Interest',0) intTransactions.append( data.Transaction(meta, # could add div per share, ISIN,.... row['reportDate'], self.flag, 'IB', # payee ' '.join(['Interest ', currency , month]), data.EMPTY_SET, data.EMPTY_SET, postings )) return intTransactions
def Posting(self, posting, entry, oss): flag = '{} '.format(posting.flag) if posting.flag else '' assert posting.account is not None flag_posting = '{:}{:62}'.format(flag, posting.account) pos_str = (position.to_string(posting, self.dformat, detail=False) if isinstance(posting.units, Amount) else '') if posting.price is not None: price_str = '@ {}'.format(posting.price.to_string( self.dformat_max)) else: # Figure out if we need to insert a price on a posting held at cost. # See https://groups.google.com/d/msg/ledger-cli/35hA0Dvhom0/WX8gY_5kHy0J (postings_simple, postings_at_price, postings_at_cost) = postings_by_type(entry) cost = posting.cost if postings_at_price and postings_at_cost and cost: price_str = '@ {}'.format( amount.Amount(cost.number, cost.currency).to_string(self.dformat)) else: price_str = '' posting_str = ' {:64} {} {}'.format(flag_posting, quote_currency(pos_str), quote_currency(price_str)) oss.write(posting_str.rstrip()) oss.write('\n')
def extract(self, file, existing_entries): entries = [] with StringIO(file.contents()) as csvfile: reader = csv.DictReader(csvfile, ['Date', 'Reference', 'PaidOut', 'PaidIn', 'ExchangeOut', 'ExchangeIn', 'Balance', 'Category', 'Notes'], delimiter=';', skipinitialspace=True) next(reader) for row in reader: metakv = { 'category': row['Category'].strip(), } exchangeIn = row['ExchangeIn'].strip() exchangeOut = row['ExchangeOut'].strip() if exchangeIn and exchangeOut: metakv['originalIn'] = exchangeIn metakv['originalOut'] = exchangeOut elif exchangeIn: metakv['original'] = exchangeIn elif exchangeOut: metakv['original'] = exchangeOut meta = data.new_metadata(file.name, 0, metakv) entry = data.Transaction( meta, parse(row['Date'].strip()).date(), '*', '', (row['Reference'].strip() + ' ' + row['Notes'].strip()).strip(), data.EMPTY_SET, data.EMPTY_SET, [ data.Posting(self.account, amount.Amount(D(row['PaidIn'].strip()) - D(row['PaidOut'].strip()), self.currency), None, None, None, None), ] ) entries.append(entry) return entries
def generate_transaction( meta, trans_date, trans_payee, trans_description, trans_account, trans_amount, trans_second_posting_account, ): txn = data.Transaction( meta=meta, date=trans_date, flag=flags.FLAG_OKAY, payee=trans_payee, narration=trans_description, tags=set(), links=set(), postings=[], ) txn.postings.append( data.Posting(trans_account, amount.Amount(round(D(trans_amount), 2), 'EUR'), None, None, None, None)) txn.postings.append( data.Posting(trans_second_posting_account, None, None, None, None, None)) return txn
def extract(self, file): entries = [] with open(file.name) as f: for index, row in enumerate(csv.DictReader(islice(f, 6, None))): if index == 0: if 'Beginning balance' in row['Description']: continue else: logging.error( "Missing 'Beginning balance' in '{}'".format( row['Description'])) meta = data.new_metadata(file.name, index) trans_date = parse(row['Date']).date() trans_desc = titlecase(row['Description']) trans_amt = row['Amount'] units = amount.Amount(D(row['Amount']), self.currency) txn = data.Transaction( meta, trans_date, self.FLAG, trans_desc, None, data.EMPTY_SET, data.EMPTY_SET, [ data.Posting(self.account_root, units, None, None, None, None), data.Posting(self.account2, -units, None, None, None, None), ]) entries.append(txn) return entries
def Balances(self,cr): # generate Balance statements from IBKR Cash reports # balances crTransactions = [] for idx, row in cr.iterrows(): currency=row['currency'] if currency == 'BASE_SUMMARY': continue # this is a summary balance that is not needed for beancount amount_=amount.Amount(row['endingCash'].__round__(2),currency) # make the postings. two for deposits postings=[data.Posting(self.depositAccount, -amount_, None, None, None, None), data.Posting(self.getLiquidityAccount(currency), amount_,None, None, None, None) ] meta=data.new_metadata('balance',0) crTransactions.append(data.Balance( meta, row['toDate'] + timedelta(days=1), # see tariochtools EC imp. self.getLiquidityAccount(currency), amount_, None, None)) return crTransactions
def check_closing(entries, options_map): """Expand 'closing' metadata to a zero balance check. Args: entries: A list of directives. unused_options_map: An options map. Returns: A list of new errors, if any were found. """ new_entries = [] for entry in entries: if isinstance(entry, data.Transaction): for i, posting in enumerate(entry.postings): if posting.meta and posting.meta.get('closing', False): # Remove the metadata. meta = posting.meta.copy() del meta['closing'] posting = posting._replace(meta=meta) entry.postings[i] = posting # Insert a balance. date = entry.date + datetime.timedelta(days=1) balance = data.Balance(data.new_metadata("<check_closing>", 0), date, posting.account, amount.Amount(ZERO, posting.units.currency), None, None) new_entries.append(balance) new_entries.append(entry) return new_entries, []
def Fee(self,fee): # calculates fees from IBKR data feeTransactions=[] for idx, row in fee.iterrows(): currency=row['currency'] amount_=amount.Amount(row['amount'],currency) text=row['description'] month=re.findall('\w{3} \d{4}',text)[0] # make the postings, two for fees postings=[data.Posting(self.getFeesAccount(currency), -amount_, None, None, None, None), data.Posting(self.getLiquidityAccount(currency), amount_,None, None, None, None)] meta=data.new_metadata(__file__,0, {}) # actually no metadata feeTransactions.append( data.Transaction(meta, row['reportDate'], self.flag, 'IB', # payee ' '.join(['Fee', currency , month]), data.EMPTY_SET, data.EMPTY_SET, postings)) return feeTransactions
def Deposits(self, dep): # creates deposit transactions from IBKR Data depTransactions=[] # assumes you figured out how to deposit/ withdrawal without fees if len(self.depositAccount) == 0: # control this from the config file return [] for idx, row in dep.iterrows(): currency=row['currency'] amount_=amount.Amount(row['amount'],currency) # make the postings. two for deposits postings=[data.Posting(self.depositAccount, -amount_, None, None, None, None), data.Posting(self.getLiquidityAccount(currency), amount_,None, None, None, None) ] meta=data.new_metadata('deposit/withdrawel',0) depTransactions.append( data.Transaction(meta, # could add div per share, ISIN,.... row['reportDate'], self.flag, 'self', # payee "deposit / withdrawal", data.EMPTY_SET, data.EMPTY_SET, postings )) return depTransactions
def addPaymentFor(self, txn, trans_date, trans_amt): months_to_add = self.paymentMonthOffset + ( 1 if trans_date.day > self.statementCloseDay else 0) payment_date = set_days(add_months(trans_date, months_to_add), self.paymentDay) paymentTxn = self.payments[payment_date.isoformat( )] if payment_date.isoformat() in self.payments else data.Transaction( meta=txn.meta, date=payment_date, flag=flags.FLAG_OKAY, payee=self.account + " Payment", narration="", tags=set(), links=set(), postings=[ data.Posting(self.account, None, None, None, None, None) ]) paymentTxn.postings.extend([ data.Posting(self.autoPayAccount, amount.Amount(-1 * D(trans_amt), 'USD'), None, None, None, None), ]) self.payments[payment_date.isoformat()] = paymentTxn
def process_entries(entries): new_entries = [] errors = [] for eindex, entry in enumerate(entries): if isinstance(entry, data.Transaction) and entry.flag == 'C': augmenting, reducing = [], [] new_postings = [] for pindex, posting in enumerate(entry.postings): if posting.units.number < ZERO: reducing.append(posting) new_postings.append(posting) else: augmenting.append(posting) # if len(augmenting) != 1: # errors.append(CostTransferError(posting.meta, "Augmenting posts need to be 1", None)) # else: if len(augmenting) > 1: errors.append(CostTransferError(posting.meta, "Augmenting posts cannot to be more than 1", None)) else: if len(augmenting) == 0: account = entry.meta['account'] else: account = augmenting[0].account for reducing_posting in reducing: augmenting_posting = reducing_posting._replace( account=account, units=amount.Amount(-reducing_posting.units.number, reducing_posting.units.currency), ) new_postings.append(augmenting_posting) entry = entry._replace(postings=new_postings) new_entries.append(entry) return new_entries, errors
def extract(self, file): #parse csv file if self.file_format_version == 1: buchungstag, auftraggeber_empfaenger, buchungstext, verwendungszweck, betrag, kontostand, indices, endsaldo = parse_csv_file_v1( file.name) elif self.file_format_version == 2: buchungstag, auftraggeber_empfaenger, buchungstext, verwendungszweck, betrag, kontostand, indices, endsaldo = parse_csv_file_v2( file.name) else: raise IOError("Unknown file format.") #create transactions entries = [] for i in range(len(buchungstag)): postings = self.guess_postings(auftraggeber_empfaenger[i], float(betrag[i])) meta = data.new_metadata(file.name, indices[i]) txn = data.Transaction(meta, buchungstag[i], self.flag, auftraggeber_empfaenger[i], verwendungszweck[i], data.EMPTY_SET, data.EMPTY_SET, postings) entries.append(txn) #create balance meta = data.new_metadata(file.name, endsaldo[2]) entries.append( data.Balance(meta, endsaldo[0] + datetime.timedelta(days=1), self.account, amount.Amount(D(endsaldo[1]), self.currency), None, None)) return entries
def test_insert_transaction(tmpdir): file_content = dedent(""" 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD ; FAVA-INSERT-MARKER """) samplefile = tmpdir.mkdir('fava_util_file3').join('example.beancount') samplefile.write(file_content) transaction = data.Transaction(None, datetime.date(2016, 1, 1), '*', 'payee', 'narr', None, None, []) insert_transaction(transaction, [str(samplefile)]) assert samplefile.read() == dedent(""" 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD 2016-01-01 * "payee" "narr" ; FAVA-INSERT-MARKER """) postings = [ data.Posting('Liabilities:US:Chase:Slate', amount.Amount(D('-10.00'), 'USD'), None, None, None, None), data.Posting('Expenses:Food', amount.Amount(D('10.00'), 'USD'), None, None, None, None), ] transaction = data.Transaction(None, datetime.date(2016, 1, 1), '*', 'new payee', 'narr', None, None, postings) insert_transaction(transaction, [str(samplefile)]) assert samplefile.read() == dedent(""" 2016-02-26 * "Uncle Boons" "Eating out alone" Liabilities:US:Chase:Slate -24.84 USD Expenses:Food:Restaurant 24.84 USD 2016-01-01 * "payee" "narr" 2016-01-01 * "new payee" "narr" Liabilities:US:Chase:Slate -10.00 USD Expenses:Food 10.00 USD ; FAVA-INSERT-MARKER """)