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 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 test_get_pholding_market_value(self): posting = data.Posting('Account', position.from_string('100 MSFT {54.34 USD}'), A('60.00 USD'), None, None) self.assertEqual(A('6000.00 USD'), holdings.get_pholding_market_value(posting)) posting = data.Posting('Account', position.from_string('100 MSFT {54.34 USD}'), None, None, None) self.assertEqual(A('5434.00 USD'), holdings.get_pholding_market_value(posting)) posting = data.Posting('Account', position.from_string('1000.00 USD'), None, None, None) self.assertEqual(A('1000.00 USD'), holdings.get_pholding_market_value(posting)) with self.assertRaises(AssertionError): posting = data.Posting('Account', position.from_string('1000.00 USD'), A('60.00 USD'), None, None) holdings.get_pholding_market_value(posting) with self.assertRaises(AssertionError): posting = data.Posting('Account', position.from_string('1000.00 USD {1.25 CAD}'), A('60.00 USD'), None, None) holdings.get_pholding_market_value(posting)
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 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 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 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 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 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 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, 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 __txn_common(self, meta, date, acc_in, acc_out, units_common, payee="", desc=""): """Return a transaction object for simple transactions.""" self.logger.debug("Entering Function") self.logger.debug("Receiving account: %s", acc_in) self.logger.debug("Sending account: %s", acc_out) txn = data.Transaction( meta, date, self.FLAG, payee, desc, data.EMPTY_SET, data.EMPTY_SET, [ data.Posting(acc_in, units_common, None, None, None, None), data.Posting(acc_out, -units_common, None, None, None, None) ]) self.logger.debug('Transaction to be recorded: %s', str(txn)) self.logger.debug("Leaving Function") return txn
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 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 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 get_holdings_entries(entries, options_map): """Summarizes the entries to list of entries representing the final holdings.. This list includes the latest prices entries as well. This can be used to load a full snapshot of holdings without including the entire history. This is a way of summarizing a balance sheet in a way that filters away history. Args: entries: A list of directives. options_map: A dict of parsed options. Returns: A string, the entries to print out. """ # The entries will be created at the latest date, against an equity account. latest_date = entries[-1].date _, equity_account, _ = options.get_previous_accounts(options_map) # Get all the assets. holdings_list, _ = holdings.get_assets_holdings(entries, options_map) # Create synthetic entries for them. holdings_entries = [] for index, holding in enumerate(holdings_list): meta = data.new_metadata('report_holdings_print', index) entry = data.Transaction(meta, latest_date, flags.FLAG_SUMMARIZE, None, "", None, None, []) # Convert the holding to a position. pos = holdings.holding_to_position(holding) entry.postings.append( data.Posting(holding.account, pos.units, pos.cost, None, None, None)) cost = -convert.get_cost(pos) entry.postings.append( data.Posting(equity_account, cost, None, None, None, None)) holdings_entries.append(entry) # Get opening directives for all the accounts. used_accounts = {holding.account for holding in holdings_list} open_entries = summarize.get_open_entries(entries, latest_date) used_open_entries = [ open_entry for open_entry in open_entries if open_entry.account in used_accounts ] # Add an entry for the equity account we're using. meta = data.new_metadata('report_holdings_print', -1) used_open_entries.insert( 0, data.Open(meta, latest_date, equity_account, None, None)) # Get the latest price entries. price_entries = prices.get_last_price_entries(entries, None) return used_open_entries + holdings_entries + price_entries
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 create_entries_from_balances(balances, date, source_account, direction, meta, flag, narration_template): """"Create a list of entries from a dict of balances. This method creates a list of new entries to transfer the amounts in the 'balances' dict to/from another account specified in 'source_account'. The balancing posting is created with the equivalent at cost. In other words, if you attempt to balance 10 HOOL {500 USD}, this will synthesize a posting with this position on one leg, and with 5000 USD on the 'source_account' leg. Args: balances: A dict of account name strings to Inventory instances. date: A datetime.date object, the date at which to create the transaction. source_account: A string, the name of the account to pull the balances from. This is the magician's hat to pull the rabbit from. direction: If 'direction' is True, the new entries transfer TO the balances account from the source account; otherwise the new entries transfer FROM the balances into the source account. meta: A dict to use as metadata for the transactions. flag: A string, the flag to use for the transactinos. narration_template: A format string for creating the narration. It is formatted with 'account' and 'date' replacement variables. Returns: A list of newly synthesizes Transaction entries. """ new_entries = [] for account, account_balance in sorted(balances.items()): # Don't create new entries where there is no balance. if account_balance.is_empty(): continue narration = narration_template.format(account=account, date=date) if not direction: account_balance = -account_balance postings = [] new_entry = Transaction(meta, date, flag, None, narration, data.EMPTY_SET, data.EMPTY_SET, postings) for position in account_balance.get_positions(): postings.append( data.Posting(account, position.units, position.cost, None, None, None)) cost = -convert.get_cost(position) postings.append( data.Posting(source_account, cost, None, None, None, None)) new_entries.append(new_entry) return new_entries
def remove_commissions(entries, unused_options_map, config): """Remove the commissions from the P/L of closing/sales transactions.""" try: commission_regexp, income_regexp, outgoing_account = parse_config( config) except ValueError: return [], [ Error(None, "Invalid configuration for {} plugin".format(__file__), None) ] new_entries = [] for entry in entries: # Match the transaction. if (isinstance(entry, data.Transaction) and any( income_regexp.match(posting.account) for posting in entry.postings) and any( commission_regexp.match(posting.account) for posting in entry.postings)): # Find the commissions amounts. commissions = inventory.Inventory() for posting in entry.postings: if commission_regexp.match(posting.account): commissions.add_amount(posting.units) # Find the first income account. for posting in entry.postings: if income_regexp.match(posting.account): income_account = posting.account break assert income_account, "Income account not found." # Insert the new legs. new_postings = [] for cposition in commissions: new_postings.extend([ data.Posting(income_account, cposition.units, None, None, None, None), data.Posting(outgoing_account, -cposition.units, None, None, None, None), ]) # Distribute the commission. distribute_commission_on_metadata(cposition.units, entry.postings) entry = entry._replace(postings=entry.postings + new_postings) new_entries.append(entry) return new_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 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(self, file, existing_entries=None): # Open the CSV file and create directives. entries = [] with open(file.name, encoding="utf-8") as f: for _ in range(16): next(f) for index, row in enumerate(reversed(list(csv.DictReader(f)))): flag = flags.FLAG_WARNING dt = parse(row["交易时间"]) meta = data.new_metadata( file.name, index, kvlist={"time": str(dt.time())} ) amount = Amount(D(row["金额(元)"].lstrip("¥")), self.currency) if row["收/支"] in {"支出", "/"}: # 支出 amount = -amount payee = row["交易对方"] narration: str = row["商品"] if narration.startswith(_COMMENTS_STR): narration = narration.replace(_COMMENTS_STR, "") if narration == "/": narration = "" account_1_text = row["支付方式"] account_1 = "Assets:FIXME" for asset_k, asset_v in self.accountDict.items(): if asset_k in account_1_text: account_1 = asset_v flag = flags.FLAG_OKAY postings = [data.Posting(account_1, amount, None, None, None, None)] if row["当前状态"] == "充值完成": postings.insert( 0, data.Posting(self.account, -amount, None, None, None, None), ) narration = "微信零钱充值" payee = None txn = data.Transaction( meta, dt.date(), flag, payee, narration, self.default_set, data.EMPTY_SET, postings, ) entries.append(txn) return entries
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 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 extract(self, f): entries = [] with open(f.name, newline="") as f: for index, row in enumerate(csv.DictReader(f, delimiter=",")): tx_date = parse(row["date"]).date() tx_desc = row["memo"].strip() tx_amt = row["amount"].strip() tx_payee = row["from"].strip() meta = data.new_metadata(f.name, index) tx = data.Transaction( meta=meta, date=tx_date, flag=flags.FLAG_OKAY, payee=tx_payee, narration=tx_desc, tags=set(), links=set(), postings=[], ) if row["type"] == "expense": to = row["to"] tx_amt = -D(tx_amt) else: to = abbr.abbr[row["to"]] tx.postings.append( data.Posting( abbr.abbr[row["from"]], amount.Amount(-D(tx_amt), "TWD"), None, None, None, None, )) tx.postings.append( data.Posting( to, None, None, None, None, None, )) entries.append(tx) return entries
def test_render_posting_price(self): str_posting = journal_text.render_posting( data.Posting('Assets:Something', position.from_string('10 VHT'), A('45.32 USD'), None, None), self.number_format) self.assertEqual( ' Assets:Something 10 VHT @ 45.32 USD', str_posting)
def complete_entry(self, entry): """Complete the given entry. This method attempts to complete the entry only if it is a transaction with a single posting to the account bound to the completer. The entry will be completed only if a suitable model transaction can be found. If multiple model transactions are found that balance the transaction against different account, the missing posting will be flagged for review. Args: entry: The entry to be completed. Returns: True is the entry was completed; False, otherwise. """ if (isinstance(entry, data.Transaction) and len(entry.postings) == 1 and entry.postings[0].account == self.account): model_txn, model_accounts = self.find_best_model(entry) if model_txn: # If past transactions similar to this one were posted against # different accounts, flag the posting in the new entry. flag = flags.FLAG_WARNING if len(model_accounts) > 1 else None # Add the missing posting to balance the transaction for posting in model_txn.postings: if posting.account != self.account: units = -entry.postings[ 0].units if self.interpolated else None missing_posting = data.Posting(posting.account, units, None, None, flag, None) entry.postings.append(missing_posting) return True return False
def get_postings(self, row: Row): if "fee" in row.narration.casefold(): elif "contribution" in row.narration.casefold(): postings = [ data.Posting( account=account, units=-self.get_amount(row), cost=None, price=None, flag=flag, meta={}, ) ] return postings def get_expense_posting(self, row: Row): return data.Posting( account=self.expense_account, units=self.get_amount(row), cost=None, price=None, flag=None, meta={}, ) def get_amount(self, row: Row): total = row.item_total.strip(''.join(self.currency_symbols)) return data.Amount(number.D(total), row.currency)
def __txn_vacation(self, meta, date, desc, units_vac, units_ovt, price): """Return a holiday transaction object.""" self.logger.debug("Entering Function") txn = data.Transaction( meta, date, "!", self.employer, desc, data.EMPTY_SET, data.EMPTY_SET, [ data.Posting(self.account_vacation, units_vac, None, None, None, None), data.Posting(self.account_employer_vacation, -units_vac, None, None, None, None), data.Posting(self.account_vacation, units_vac, None, price, "!", None), data.Posting(self.account_employer_overtime, -units_ovt, None, None, "!", None) ]) self.logger.debug('Transaction to be recorded: %s', str(txn)) self.logger.debug("Leaving Function") return txn