class ResultTable(ResultTableBase): COLUMNS = [ Column('marked', ''), Column('name', 'Filename'), Column('folder_path', 'Directory'), Column('size', 'Size (KB)'), Column('extension', 'Kind'), ] DELTA_COLUMNS = {'size', }
class ResultTable(ResultTableBase): COLUMNS = [ Column("marked", ""), Column("name", "Filename"), Column("folder_path", "Directory"), Column("size", "Size (KB)"), Column("extension", "Kind"), ] DELTA_COLUMNS = { "size", }
class SplitTable(GUITable): COLUMNS = [ Column('account', display=trcol("Account")), Column('memo', display=trcol("Memo")), Column('debit', display=trcol("Debit")), Column('credit', display=trcol("Credit")), ] def __init__(self, transaction_panel): GUITable.__init__(self, document=transaction_panel.mainwindow.document) self.panel = transaction_panel self.completable_edit = self.panel.completable_edit # --- Override def _do_add(self): split = self.panel.new_split() row = SplitTableRow(self, split) return row, len(self) def _do_delete(self): self.panel.delete_split(self.selected_row.split) self.refresh() def _is_edited_new(self): split = self.edited.split return split not in split.transaction.splits def _fill(self): transaction = self.panel.transaction splits = transaction.splits for split in splits: self.append(SplitTableRow(self, split)) def _update_selection(self): self.panel.select_splits([row.split for row in self.selected_rows]) # --- Public def move_split(self, from_index, to_index): row = self[from_index] row.split.move_to_index(to_index) self.refresh(refresh_view=False) self.select([to_index]) self.view.refresh() def refresh_splits(self): self._item_changed() def refresh_initial(self): # the refresh just after a panel loading is a bit different self.refresh(refresh_view=False) self.select([0]) self.view.refresh()
class PluginListTable(GUITable): SAVENAME = 'PluginListTable' COLUMNS = [ Column('enabled', display=trcol("Enabled")), Column('name', display=trcol("Name")), Column('type', display=trcol("Type")), Column('author', display=trcol("Author")), ] # --- Override def _fill(self): for plugin in self.document.app.plugins: self.append(PluginListRow(self, plugin))
class ResultTable(ResultTableBase): COLUMNS = [ Column('marked', ''), Column('name', coltr("Filename")), Column('folder_path', coltr("Folder"), optional=True), Column('size', coltr("Size (KB)"), optional=True), Column('extension', coltr("Kind"), visible=False, optional=True), Column('dimensions', coltr("Dimensions"), optional=True), Column('exif_timestamp', coltr("EXIF Timestamp"), visible=False, optional=True), Column('mtime', coltr("Modification"), visible=False, optional=True), Column('percentage', coltr("Match %"), optional=True), Column('dupe_count', coltr("Dupe Count"), visible=False, optional=True), ] DELTA_COLUMNS = {'size', 'dimensions', 'mtime'}
class ExportAccountTable(GUITable): COLUMNS = [ Column('name', display=trcol("Account")), Column('export', display=trcol("Export")), ] def __init__(self, export_panel): self.panel = export_panel GUITable.__init__(self, document=export_panel.document) # --- Override def _fill(self): accounts = sorted(self.panel.accounts, key=ACCOUNT_SORT_KEY) for account in accounts: self.append(ExportAccountTableRow(self, account))
class GeneralLedgerTable(EntryTableBase): SAVENAME = 'GeneralLedgerTable' COLUMNS = [ Column('status', display=''), Column('date', display=trcol("Date")), Column('reconciliation_date', display=trcol("Reconciliation Date"), visible=False, optional=True), Column('checkno', display=trcol("Check #"), visible=False, optional=True), Column('description', display=trcol("Description"), optional=True), Column('payee', display=trcol("Payee"), visible=False, optional=True), Column('transfer', display=trcol("Transfer")), Column('debit', display=trcol("Debit")), Column('credit', display=trcol("Credit")), Column('balance', display=trcol("Balance")), ] ENTRY_ROWCLASS = GeneralLedgerRow # --- Override def _fill(self): accounts = self.document.accounts sort_accounts(accounts) for account in accounts: rows = self._get_account_rows(account) if not rows: continue self.append(AccountRow(self, account)) for row in rows: self.append(row) def _get_current_account(self): row = self.selected_row return row.account if row is not None else None def _get_totals_currency(self): return self.document.default_currency # --- Public def is_account_row(self, row): return isinstance(row, AccountRow) def is_bold_row(self, row): return isinstance(row, (TotalRow, PreviousBalanceRow)) # --- Event Handlers def date_range_changed(self): self.refresh(refresh_view=False) self._update_selection() self.view.refresh() self.view.show_selected_row()
class ExcludeListTable(GUITable, DupeGuruGUIObject): COLUMNS = [ Column("marked", ""), Column("regex", tr("Regular Expressions")) ] def __init__(self, exclude_list_dialog, app): GUITable.__init__(self) DupeGuruGUIObject.__init__(self, app) self.columns = Columns(self) self.dialog = exclude_list_dialog def rename_selected(self, newname): row = self.selected_row if row is None: return False row._data = None return self.dialog.rename_selected(newname) # --- Virtual def _do_add(self, regex): """(Virtual) Creates a new row, adds it in the table. Returns ``(row, insert_index)``.""" # Return index 0 to insert at the top return ExcludeListRow(self, self.dialog.exclude_list.is_marked(regex), regex), 0 def _do_delete(self): self.dalog.exclude_list.remove(self.selected_row.regex) # --- Override def add(self, regex): row, insert_index = self._do_add(regex) self.insert(insert_index, row) self.view.refresh() def _fill(self): for enabled, regex in self.dialog.exclude_list: self.append(ExcludeListRow(self, enabled, regex)) def refresh(self, refresh_view=True): """Override to avoid keeping previous selection in case of multiple rows selected previously.""" self.cancel_edits() del self[:] self._fill() if refresh_view: self.view.refresh()
class ResultTable(ResultTableBase): COLUMNS = [ Column("marked", ""), Column("name", coltr("Filename")), Column("folder_path", coltr("Folder"), optional=True), Column("size", coltr("Size (KB)"), optional=True), Column("extension", coltr("Kind"), visible=False, optional=True), Column("mtime", coltr("Modification"), visible=False, optional=True), Column("percentage", coltr("Match %"), optional=True), Column("words", coltr("Words Used"), visible=False, optional=True), Column("dupe_count", coltr("Dupe Count"), visible=False, optional=True), ] DELTA_COLUMNS = {"size", "mtime"}
class IgnoreListTable(GUITable): COLUMNS = [ # the str concat below saves us needless localization. Column('path1', coltr("File Path") + " 1"), Column('path2', coltr("File Path") + " 2"), ] def __init__(self, ignore_list_dialog): GUITable.__init__(self) self.columns = Columns(self) self.view = None self.dialog = ignore_list_dialog #--- Override def _fill(self): for path1, path2 in self.dialog.ignore_list: self.append(IgnoreListRow(self, path1, path2))
class ScheduleTable(GUITable, TableWithAmountMixin): SAVENAME = 'ScheduleTable' COLUMNS = [ Column('start_date', display=trcol('Start Date')), Column('stop_date', display=trcol('Stop Date')), Column('repeat_type', display=trcol('Repeat Type')), Column('interval', display=trcol('Interval')), Column('checkno', display=trcol('Check #'), visible=False, optional=True), Column('description', display=trcol('Description'), optional=True), Column('payee', display=trcol('Payee'), visible=False, optional=True), Column('from', display=trcol('From')), Column('to', display=trcol('To')), Column('amount', display=trcol('Amount')), ] def __init__(self, schedule_view): GUITable.__init__(self, document=schedule_view.document) self.mainwindow = schedule_view.mainwindow # --- Override def _update_selection(self): self.mainwindow.selected_schedules = self.selected_schedules def _fill(self): self._all_amounts_are_native = True for schedule in self.document.schedules: row = ScheduleTableRow(self, schedule) if not row.is_amount_native: self._all_amounts_are_native = False self.append(row) # --- Public def delete(self): self.document.delete_schedules(self.selected_schedules) def edit(self): self.mainwindow.edit_item() # --- Properties @property def selected_schedules(self): return [row.schedule for row in self.selected_rows]
class ElementTable(GUIObject, GUITable): #--- model -> view calls: # refresh() # COLUMNS = [ Column('page', "Page"), Column('order', "Order"), Column('x', "X"), Column('y', "Y"), Column('fontsize', "Font Size"), Column('text_length', "Text Length"), Column('state', "State"), Column('text', "Text"), ] def __init__(self, app): GUIObject.__init__(self, app) GUITable.__init__(self) self.columns = Columns(self) #--- Override def _fill(self): elements = self.app.elements if self.app.hide_ignored: elements = [e for e in elements if e.state != ElementState.Ignored] for element in elements: self.append(ElementRow(self, element)) def _update_selection(self): # Takes the table's selection and does appropriates updates on the Document's side. elements = {row.element for row in self.selected_rows} self.app.select_elements(elements) #--- Public def press_key(self, key): key = key.upper() if key not in SHORTCUTKEY2FLAG: return state = SHORTCUTKEY2FLAG[key] self.app.change_state_of_selected(state) #--- Event Handlers def elements_changed(self): self.refresh() def elements_selected(self): selected_elements = self.app.selected_elements selected_indexes = [] for index, row in enumerate(self): if row.element in selected_elements: selected_indexes.append(index) if selected_indexes != self.selected_indexes: self.selected_indexes = selected_indexes self.view.refresh()
class ProblemTable(GUITable): COLUMNS = [ Column('path', coltr("File Path")), Column('msg', coltr("Error Message")), ] def __init__(self, problem_dialog): GUITable.__init__(self) self.columns = Columns(self) self.dialog = problem_dialog #--- Override def _update_selection(self): row = self.selected_row dupe = row.dupe if row is not None else None self.dialog.select_dupe(dupe) def _fill(self): problems = self.dialog.app.results.problems for dupe, msg in problems: self.append(ProblemRow(self, dupe, msg))
class BudgetTable(GUITable, TableWithAmountMixin): SAVENAME = 'BudgetTable' COLUMNS = [ Column('start_date', display=trcol("Start Date")), Column('stop_date', display=trcol("Stop Date")), Column('repeat_type', display=trcol("Repeat Type")), Column('interval', display=trcol("Interval")), Column('account', display=trcol("Account")), Column('target', display=trcol("Target")), Column('amount', display=trcol("Amount")), ] def __init__(self, budget_view): GUITable.__init__(self, document=budget_view.document) self.mainwindow = budget_view.mainwindow # --- Override def _update_selection(self): self.mainwindow.selected_budgets = self.selected_budgets def _fill(self): self._all_amounts_are_native = True for budget in self.document.budgets: if not self.mainwindow.document.is_amount_native(budget.amount): self._all_amounts_are_native = False self.append(BudgetTableRow(self, budget)) # --- Public def delete(self): self.document.delete_budgets(self.selected_budgets) def edit(self): self.mainwindow.edit_item() # --- Properties @property def selected_budgets(self): return [row.budget for row in self.selected_rows]
class ResultTable(ResultTableBase): COLUMNS = [ Column("marked", ""), Column("name", coltr("Filename")), Column("folder_path", coltr("Folder"), visible=False, optional=True), Column("size", coltr("Size (MB)"), optional=True), Column("duration", coltr("Time"), optional=True), Column("bitrate", coltr("Bitrate"), optional=True), Column("samplerate", coltr("Sample Rate"), visible=False, optional=True), Column("extension", coltr("Kind"), optional=True), Column("mtime", coltr("Modification"), visible=False, optional=True), Column("title", coltr("Title"), visible=False, optional=True), Column("artist", coltr("Artist"), visible=False, optional=True), Column("album", coltr("Album"), visible=False, optional=True), Column("genre", coltr("Genre"), visible=False, optional=True), Column("year", coltr("Year"), visible=False, optional=True), Column("track", coltr("Track Number"), visible=False, optional=True), Column("comment", coltr("Comment"), visible=False, optional=True), Column("percentage", coltr("Match %"), optional=True), Column("words", coltr("Words Used"), visible=False, optional=True), Column("dupe_count", coltr("Dupe Count"), visible=False, optional=True), ] DELTA_COLUMNS = {"size", "duration", "bitrate", "samplerate", "mtime"}
class ResultTable(ResultTableBase): COLUMNS = [ Column('marked', ''), Column('name', coltr("Filename")), Column('folder_path', coltr("Folder"), visible=False, optional=True), Column('size', coltr("Size (MB)"), optional=True), Column('duration', coltr("Time"), optional=True), Column('bitrate', coltr("Bitrate"), optional=True), Column('samplerate', coltr("Sample Rate"), visible=False, optional=True), Column('extension', coltr("Kind"), optional=True), Column('mtime', coltr("Modification"), visible=False, optional=True), Column('title', coltr("Title"), visible=False, optional=True), Column('artist', coltr("Artist"), visible=False, optional=True), Column('album', coltr("Album"), visible=False, optional=True), Column('genre', coltr("Genre"), visible=False, optional=True), Column('year', coltr("Year"), visible=False, optional=True), Column('track', coltr("Track Number"), visible=False, optional=True), Column('comment', coltr("Comment"), visible=False, optional=True), Column('percentage', coltr("Match %"), optional=True), Column('words', coltr("Words Used"), visible=False, optional=True), Column('dupe_count', coltr("Dupe Count"), visible=False, optional=True), ] DELTA_COLUMNS = {'size', 'duration', 'bitrate', 'samplerate', 'mtime'}
class TransactionTable(TransactionTableBase): SAVENAME = 'TransactionTable' COLUMNS = [ Column('status', display=''), Column('date', display=trcol('Date')), Column('checkno', display=trcol('Check #'), visible=False, optional=True), Column('description', display=trcol('Description'), optional=True), Column('payee', display=trcol('Payee'), visible=False, optional=True), Column('from', display=trcol('From')), Column('to', display=trcol('To')), Column('amount', display=trcol('Amount')), Column('mtime', display=trcol('Modification Time'), visible=False, optional=True), ] # --- Override def _do_add(self): transactions = self.mainwindow.selected_transactions date = transactions[0].date if transactions else datetime.date.today() transaction = Transaction(date, amount=0) rows = self[:-1] # ignore total row for index, row in enumerate(rows): if row._date > transaction.date: insert_index = index break else: insert_index = len(rows) row = TransactionTableRow(self, transaction) return row, insert_index def _do_delete(self): transactions = self.selected_transactions if transactions: self.document.delete_transactions(transactions) def _fill(self): self._all_amounts_are_native = True total_amount = 0 for transaction in self.parent_view.visible_transactions: self.append(TransactionTableRow(self, transaction)) convert = lambda a: convert_amount( a, self.document.default_currency, transaction.date) total_amount += convert(transaction.amount) if not self.document.is_amount_native(transaction.amount): self._all_amounts_are_native = False self.footer = TotalRow(self, self.document.date_range.end, total_amount) self._restore_from_explicit_selection(refresh_view=False) # --- Private def _show_account(self, row_index=None, use_to_column=False): # if `use_to_column` is True, use the To column, else, use the From column if row_index is None: if not self.selected_transactions: return row_index = self.selected_index txn = self[row_index].transaction froms, tos = txn.splitted_splits() splits = tos if use_to_column else froms account_to_show = splits[0].account self.mainwindow.open_account(account_to_show) # --- Public def select_transactions(self, transactions): TransactionTableBase.select_transactions(self, transactions) if self and not self.selected_indexes: self.selected_indexes = [len(self) - 1] def show_from_account(self, row_index=None): self._show_account(row_index, use_to_column=False) def show_to_account(self, row_index=None): self._show_account(row_index, use_to_column=True) # --- Properties @property def selected_transactions(self): return [ row.transaction for row in self.selected_rows if hasattr(row, 'transaction') ] # --- Event handlers def date_range_changed(self): self.refresh(refresh_view=False) self._update_selection() self.view.refresh() self.view.show_selected_row() def transactions_imported(self): self.refresh(refresh_view=False) self._update_selection() self.view.refresh()
class EntryTable(EntryTableBase): SAVENAME = 'EntryTable' COLUMNS = [ Column('status', display=''), Column('date', display=trcol("Date")), Column('reconciliation_date', display=trcol("Reconciliation Date"), visible=False, optional=True), Column('checkno', display=trcol("Check #"), visible=False, optional=True), Column('description', display=trcol("Description"), optional=True), Column('payee', display=trcol("Payee"), visible=False, optional=True), Column('transfer', display=trcol("Transfer")), Column('increase', display=trcol("Increase")), Column('decrease', display=trcol("Decrease")), Column('debit', display=trcol("Debit"), visible=False), Column('credit', display=trcol("Credit"), visible=False), Column('balance', display=trcol("Balance")), ] def __init__(self, account_view): EntryTableBase.__init__(self, account_view) self.columns = EntryTableColumns(self, prefaccess=account_view.document, savename=self.SAVENAME) self.account = account_view.account self.completable_edit.account = self.account self._reconciliation_mode = False # --- Override def _fill(self): account = self.account if account is None: return self.account = account rows = self._get_account_rows(account) is_native = lambda row: self.document.is_amount_native(row._debit)\ and self.document.is_amount_native(row._credit) self._all_amounts_are_native = all(is_native(row) for row in rows) if not rows: # We still show a total row rows.append( TotalRow(self, account, self.document.date_range.end, 0, 0)) if isinstance(rows[0], PreviousBalanceRow): self.header = rows[0] del rows[0] for row in rows[:-1]: self.append(row) self.footer = rows[-1] balance_visible = account.is_balance_sheet_account() self.columns.set_column_visible('balance', balance_visible) self._restore_from_explicit_selection(refresh_view=False) def _get_current_account(self): return self.account def _get_totals_currency(self): return self._get_current_account().currency # --- Public def show_transfer_account(self, row_index=None): if row_index is None: if not self.selected_entries: return row_index = self.selected_index entry = self[row_index].entry splits = entry.transaction.splits accounts = dedupe(split.account for split in splits if split.account is not None) if len(accounts) < 2: return # no transfer index = accounts.index(entry.account) if index < len(accounts) - 1: account_to_show = accounts[index + 1] else: account_to_show = accounts[0] self.mainwindow.open_account(account_to_show) def toggle_reconciled(self): """Toggle the reconcile flag of selected entries""" entries = [ row.entry for row in self.selected_rows if row.can_reconcile() ] self.document.toggle_entries_reconciled(entries) # --- Properties @property def reconciliation_mode(self): return self._reconciliation_mode @reconciliation_mode.setter def reconciliation_mode(self, value): if value == self._reconciliation_mode: return self._reconciliation_mode = value self.refresh() # --- Event Handlers def date_range_changed(self): date_range = self.document.date_range self.refresh(refresh_view=False) self.select_transactions(self.mainwindow.selected_transactions) if not self.selected_indexes: self._select_nearest_date(date_range.start + self._delta_before_change) self.view.refresh() self.view.show_selected_row() self.mainwindow.selected_transactions = self.selected_transactions def date_range_will_change(self): date_range = self.document.date_range transactions = self.selected_transactions date = transactions[0].date if transactions else date_range.end delta = date - date_range.start self._delta_before_change = delta def transaction_changed(self): EntryTableBase.transaction_changed(self) # It's possible that because of the change, the selected txn has been removed, so we have # to update document selection. self._update_selection() def transactions_imported(self): self.refresh(refresh_view=False) self.mainwindow.selected_transactions = self.selected_transactions self.view.refresh() self.view.show_selected_row()
class ImportTable(GUITable, TransactionSelectionMixin): SAVENAME = 'ImportTable' COLUMNS = [ Column('will_import', display=''), Column('date', display=trcol("Date")), Column('description', display=trcol("Description")), Column('amount', display=trcol("Amount")), Column('bound', display=''), Column('date_import', display=trcol("Date")), Column('description_import', display=trcol("Description")), Column('payee_import', display=trcol("Payee")), Column('checkno_import', display=trcol("Check #")), Column('transfer_import', display=trcol("Transfer")), Column('amount_import', display=trcol("Amount")), ] def __init__(self, import_window): GUITable.__init__(self, document=import_window.document) self.window = import_window self._is_two_sided = False self.__last_explicitly_selected = [] # --- Override def select_transactions(self, transactions): selected_indexes = [] for index, row in enumerate(self): indexed_row = row.imported.transaction if row.imported else row.entry.transaction if indexed_row in transactions: selected_indexes.append(index) self.selected_indexes = selected_indexes @property def selected_transactions(self): return [ row.imported.transaction if row.imported else row.entry.transaction for row in self.selected_rows ] @property def _explicitly_selected_transactions(self): return self.__last_explicitly_selected def _update_selection(self): self.__last_explicitly_selected = self.selected_transactions def _fill(self): self._is_two_sided = False self.pane = self.window.selected_pane if not self.pane: return for existing, imported in self.pane.matches: if existing is not None: self._is_two_sided = True self.append(ImportTableRow(self, existing, imported)) # --- Public def bind(self, source_index, dest_index): source = self[source_index] dest = self[dest_index] source.bind(dest) self.refresh() def can_bind(self, source_index, dest_index): source = self[source_index] dest = self[dest_index] return source.can_bind(dest) def delete(self): rows = self.selected_rows for row in rows: row.will_import = False self.view.refresh() def toggle_import_status(self): for row in self.selected_rows: row.will_import = not row.will_import self.view.refresh() def unbind(self, index): row = self[index] if not row.bound: return row.unbind() self.refresh() # --- Properties @property def is_two_sided(self): """Returns whether the table should show columns to display matches from the target account. """ return self._is_two_sided
class IncomeStatement(Report): SAVENAME = 'IncomeStatement' COLUMNS = [ Column('name', display=trcol("Account")), Column('account_number', display=trcol("Account #"), visible=False, optional=True), Column('cash_flow', display=trcol("Current")), Column('last_cash_flow', display=trcol("Last"), optional=True), Column('delta', display=trcol("Change"), visible=False, optional=True), Column('delta_perc', display=trcol("Change %"), visible=False, optional=True), Column('budgeted', display=trcol("Budgeted"), optional=True), ] # --- Override def _compute_account_node(self, node): account = node.account date_range = self.document.date_range currency = self.document.default_currency cash_flow = account.entries.normal_cash_flow(date_range) cash_flow_native = account.entries.normal_cash_flow( date_range, currency) last_cash_flow = account.entries.normal_cash_flow(date_range.prev()) last_cash_flow_native = account.entries.normal_cash_flow( date_range.prev(), currency) remaining = self.document.budgets.normal_amount_for_account( account, date_range) remaining_native = self.document.budgets.normal_amount_for_account( account, date_range, currency) delta = cash_flow - last_cash_flow # Amounts for totals are converted in the document's currency node.cash_flow_amount = cash_flow_native node.last_cash_flow_amount = last_cash_flow_native node.budgeted_amount = remaining_native # Amounts for display are kept in the account's currency node.cash_flow = self.document.format_amount(cash_flow) node.last_cash_flow = self.document.format_amount(last_cash_flow) node.budgeted = self.document.format_amount(remaining) node.delta = self.document.format_amount(delta) node.delta_perc = get_delta_perc(delta, last_cash_flow) def _make_node(self, name): node = Report._make_node(self, name) node.cash_flow = '' node.last_cash_flow = '' node.budget = '' node.budgeted = '' node.delta = '' node.delta_perc = '' node.cash_flow_amount = 0 node.last_cash_flow_amount = 0 node.budgeted_amount = 0 return node def _refresh(self): self.clear() self.has_multiple_currencies = self.document.accounts.has_multiple_currencies( ) self.income = self.make_type_node(tr('INCOME'), AccountType.Income) self.expenses = self.make_type_node(tr('EXPENSES'), AccountType.Expense) self.net_income = self._make_node(tr('NET INCOME')) net_income = self.income.cash_flow_amount - self.expenses.cash_flow_amount last_net_income = self.income.last_cash_flow_amount - self.expenses.last_cash_flow_amount net_budgeted = self.income.budgeted_amount - self.expenses.budgeted_amount delta = net_income - last_net_income force_explicit_currency = self.has_multiple_currencies self.net_income.cash_flow = self.document.format_amount( net_income, force_explicit_currency=force_explicit_currency) self.net_income.last_cash_flow = self.document.format_amount( last_net_income, force_explicit_currency=force_explicit_currency) self.net_income.budgeted = self.document.format_amount( net_budgeted, force_explicit_currency=force_explicit_currency) self.net_income.delta = self.document.format_amount( delta, force_explicit_currency=force_explicit_currency) self.net_income.delta_perc = get_delta_perc(delta, last_net_income) self.net_income.is_total = True self.append(self.income) self.append(self.expenses) self.append(self.net_income) # --- Public def make_total_node(self, parent, name): node = Report.make_total_node(self, name) parent.cash_flow_amount = sum(child.cash_flow_amount for child in parent) parent.last_cash_flow_amount = sum(child.last_cash_flow_amount for child in parent) parent.budgeted_amount = sum(child.budgeted_amount for child in parent) delta = parent.cash_flow_amount - parent.last_cash_flow_amount force_explicit_currency = self.has_multiple_currencies node.cash_flow = parent.cash_flow = self.document.format_amount( parent.cash_flow_amount, force_explicit_currency=force_explicit_currency) node.last_cash_flow = parent.last_cash_flow = self.document.format_amount( parent.last_cash_flow_amount, force_explicit_currency=force_explicit_currency) node.budgeted = parent.budgeted = self.document.format_amount( parent.budgeted_amount, force_explicit_currency=force_explicit_currency) node.delta = parent.delta = self.document.format_amount( delta, force_explicit_currency=force_explicit_currency) node.delta_perc = parent.delta_perc = get_delta_perc( delta, parent.last_cash_flow_amount) return node
class BalanceSheet(Report): SAVENAME = 'BalanceSheet' COLUMNS = [ Column('name', display=trcol("Account")), Column('account_number', display=trcol("Account #"), visible=False, optional=True), Column('end', display=trcol("End")), Column('start', display=trcol("Start"), optional=True), Column('delta', display=trcol("Change"), visible=False, optional=True), Column('delta_perc', display=trcol("Change %"), visible=False, optional=True), Column('budgeted', display=trcol("Budgeted"), optional=True), ] # --- Override def _compute_account_node(self, node): account = node.account date_range = self.document.date_range start_date = date_range.start end_date = date_range.end currency = self.document.default_currency start_amount = account.entries.normal_balance(start_date - timedelta(1)) start_amount_native = account.entries.normal_balance(start_date - timedelta(1), currency=currency) end_amount = account.entries.normal_balance(end_date) end_amount_native = account.entries.normal_balance(end_date, currency=currency) budget_date_range = DateRange(date.today(), end_date) budgeted_amount = self.document.budgeted_amount_for_target( account, budget_date_range) budgeted_amount_native = convert_amount(budgeted_amount, currency, date_range.end) delta = end_amount - start_amount # Amounts for totals are converted in the document's currency node.start_amount = start_amount_native node.end_amount = end_amount_native node.budgeted_amount = budgeted_amount_native # Amounts for display are kept in the account's currency node.start = self.document.format_amount(start_amount) node.end = self.document.format_amount(end_amount) node.budgeted = self.document.format_amount(budgeted_amount) node.delta = self.document.format_amount(delta) node.delta_perc = get_delta_perc(delta, start_amount) def _make_node(self, name): node = Report._make_node(self, name) node.start = '' node.end = '' node.budgeted = '' node.delta = '' node.delta_perc = '' node.start_amount = 0 node.end_amount = 0 node.budgeted_amount = 0 return node def _refresh(self): self.clear() self.has_multiple_currencies = self.document.accounts.has_multiple_currencies( ) self.assets = self.make_type_node(tr('ASSETS'), AccountType.Asset) self.liabilities = self.make_type_node(tr('LIABILITIES'), AccountType.Liability) self.net_worth = self._make_node(tr('NET WORTH')) net_worth_start = self.assets.start_amount - self.liabilities.start_amount net_worth_end = self.assets.end_amount - self.liabilities.end_amount budget_date_range = DateRange(date.today(), self.document.date_range.end) # The net worth's budget is not a simple subtraction, it must count the target-less budgets net_worth_budgeted = self.document.budgeted_amount_for_target( None, budget_date_range) net_worth_delta = net_worth_end - net_worth_start force_explicit_currency = self.has_multiple_currencies self.net_worth.start = self.document.format_amount( net_worth_start, force_explicit_currency=force_explicit_currency) self.net_worth.end = self.document.format_amount( net_worth_end, force_explicit_currency=force_explicit_currency) self.net_worth.budgeted = self.document.format_amount( net_worth_budgeted, force_explicit_currency=force_explicit_currency) self.net_worth.delta = self.document.format_amount( net_worth_delta, force_explicit_currency=force_explicit_currency) self.net_worth.delta_perc = get_delta_perc(net_worth_delta, net_worth_start) self.net_worth.is_total = True self.append(self.assets) self.append(self.liabilities) self.append(self.net_worth) # --- Public def make_total_node(self, parent, name): node = Report.make_total_node(self, name) parent.start_amount = sum(child.start_amount for child in parent) parent.end_amount = sum(child.end_amount for child in parent) parent.budgeted_amount = sum(child.budgeted_amount for child in parent) delta_amount = parent.end_amount - parent.start_amount force_explicit_currency = self.has_multiple_currencies node.start = parent.start = self.document.format_amount( parent.start_amount, force_explicit_currency=force_explicit_currency) node.end = parent.end = self.document.format_amount( parent.end_amount, force_explicit_currency=force_explicit_currency) node.budgeted = parent.budgeted = self.document.format_amount( parent.budgeted_amount, force_explicit_currency=force_explicit_currency) node.delta = parent.delta = self.document.format_amount( delta_amount, force_explicit_currency=force_explicit_currency) node.delta_perc = parent.delta_perc = get_delta_perc( delta_amount, parent.start_amount) return node