def budget(self): ''' Fund high priority categories by withdrawing from low priority categories ''' important = iter(self.priorities) unimportant = reversed(self.priorities) sink = next(important) source = next(unimportant) # utils.debug() while source is not sink: utils.log_info(source, sink) need = sink.total_need() utils.log_info('need', need) if utils.equalish(need, 0): sink = next(important) utils.log_info('new sink', sink) available = source.total_available() utils.log_info('available', available) if available <= 0: source = next(unimportant) utils.log_info('new source', source) use = min(available, need) utils.log_info('use', use) before = source.total_available() + sink.total_available() utils.log_info('before', before) utils.log_info('source before distribute', source) source.distribute(-use) utils.log_info('source after distribute', source) utils.log_info('sink before distribute', sink) sink.distribute(use) utils.log_info('sink after distribute', sink) after = source.total_available() + sink.total_available() utils.log_info('after', after) assert utils.equalish(after, before, -1)
def get_order(t, orders): ''' Gets an order corresponding to the ynab transaction ''' utils.log_debug('get_order', t, orders) possible_orders = [] for order in orders.values(): # need negative because YNAB outflows have negative amount if utils.equalish(order.total_charged, -afy.ynab.get_amount(t)): possible_orders.append(order) utils.log_debug('possible_orders', possible_orders) if len(possible_orders) == 0: utils.log_debug('No matching order for transaction') return None if len(possible_orders) == 1: order = possible_orders[0] else: if afy.settings.fail_on_ambiguous_transaction: utils.log_error('Skipping ambiguous transaction', t, possible_orders) return None else: utils.log_debug('Skipping ambiguous transaction', t, possible_orders) unused_orders = [ o for o in possible_orders if o.id not in assigned_ids ] if not unused_orders: return None unused_orders.sort( key=lambda o: utils.day_delta(t.date, o.shipment_date)) order = unused_orders[0] assigned_ids.add(order.id) utils.log_info('Matched transaction with order', t, order) return order
def budget2(self): unimportant = list(reversed(self.priorities)) for p1, p2 in zip(unimportant, unimportant[1:]): before = p1.total_available() + p2.total_available() amount = p1.total_available() utils.log_info('moving', amount) utils.log_info(p1, p2) p1.distribute(-amount) p2.distribute(amount) utils.log_info('moved') utils.log_info(p1, p2) after = p1.total_available() + p2.total_available() assert utils.equalish(after, before, -1)
def add_adjustment_subtransaction(t): ''' Ensures that the sum of subtransaction prices equals the transaction amount ''' utils.log_debug('add_adjustment_subtransaction', t) if not t.subtransactions: return amount = calculate_adjustment(t) if not amount: return adjustment = utils.copy(t.subtransactions[0]) adjustment.memo = 'Split transaction adjustment' adjustment.amount = amount adjustment.category_name = afy.settings.default_category # TODO utils.log_info('Warning, adjusting: subtransactions do not add up, by $%s' % -get_amount(adjustment)) t.subtransactions.append(adjustment) assert utils.equalish(t.amount, sum(s.amount for s in t.subtransactions))
def calculate_adjustment(t): subtransaction_total = sum(s.amount for s in t.subtransactions) if utils.equalish(subtransaction_total, t.amount, precision=-1): return return t.amount - subtransaction_total