def init(path=None, welcome=True): import wx, sys from wxbanker import fileservice from wxbanker.controller import Controller bankController = Controller(path) # We can initialize the wx locale now that the wx.App is initialized. localization.initWxLocale() # Push our custom art provider. import wx.lib.art.img2pyartprov as img2pyartprov from wxbanker.art import silk, transparent for provider in (silk, transparent): wx.ArtProvider.Push(img2pyartprov.Img2PyArtProvider(provider)) # Initialize the wxBanker frame! frame = BankerFrame(bankController, welcome) # Greet the user if it appears this is their first time using wxBanker. config = wx.Config.Get() firstTime = not config.ReadBool("RUN_BEFORE") if firstTime: Publisher.sendMessage("first run") config.WriteBool("RUN_BEFORE", True) return bankController.wxApp
def onButton(self, event): """If the save button was clicked save, and close the dialog in any case (Close/Cancel/Save).""" assert event.Id in (wx.ID_CLOSE, wx.ID_SAVE, wx.ID_DELETE) if event.Id == wx.ID_SAVE: self.transactionCtrl.ToRecurring() originalTransaction = self.transactions[ self.transactionChoice.Selection] modifiedTransaction = self.transactionCtrl.recurringObj originalTransaction.UpdateFrom(modifiedTransaction) # Let everyone know about this, someone might want to check if there are new pending transactions. Publisher.sendMessage("recurringtransaction.updated") elif event.Id == wx.ID_DELETE: recurring = self.GetCurrentRecurringTransaction() warningMsg = _( "This will permanently remove this recurring transaction. Continue?" ) dlg = wx.MessageDialog(self, warningMsg, _("Warning"), style=wx.YES_NO | wx.ICON_EXCLAMATION) result = dlg.ShowModal() if result == wx.ID_YES: recurring.Parent.RemoveRecurringTransaction(recurring) self.GrandParent.Destroy()
def OnPaneChanged(self, evt=None): """Redo the text and layout when the widget state is toggled.""" self.Layout() # Change the labels expanded = int(self.IsExpanded()) self.Label = self.Labels[expanded] Publisher.sendMessage("CALCULATOR.TOGGLED", ("SHOW", "HIDE")[expanded])
def OnPaneChanged(self, evt=None): """Redo the text and layout when the widget state is toggled.""" self.Layout() # Change the labels expanded = int(self.IsExpanded()) self.Label = self.Labels[expanded] Publisher.sendMessage("CALCULATOR.TOGGLED", ("SHOW", "HIDE")[expanded])
def onNewTransaction(self, event=None): # First, ensure an account is selected. destAccount = self.CurrentAccount if destAccount is None: dlg = wx.MessageDialog( self.Parent, _("Please select an account and then try again."), _("No account selected"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() return # Grab the transaction values from the control. result = self.getValues() if result is None: # Validation failed, user was informed. return amount, desc, date = result # If a search is active, we have to ask the user what they want to do. if self.Parent.Parent.searchActive: msg = _("A search is currently active.") + " " + _( 'Would you like to clear the current search and make this transaction in "%s"?' ) % (destAccount.Name) dlg = wx.MessageDialog(self.Parent, msg, _("Clear search?"), style=wx.YES_NO | wx.ICON_WARNING) result = dlg.ShowModal() if result == wx.ID_YES: Publisher.sendMessage("SEARCH.CANCELLED") else: return sourceAccount = None # If the transfer box is checked, this is a transfer! if self.transferCheck.Value: result = self.Parent.transferRow.GetAccounts(destAccount) if result is None: dlg = wx.MessageDialog( self.Parent, _("This transaction is marked as a transfer. Please select the transfer account." ), _("No account selected"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() return sourceAccount, destAccount = result # Now let's see if this is a recurring transaction if self.recursCheck.GetValue(): settings = self.Parent.GetSettings() args = [amount, desc, date] + list(settings) + [sourceAccount] destAccount.AddRecurringTransaction(*args) else: destAccount.AddTransaction(amount, desc, date, sourceAccount) # A transaction was added, we can stop flashing if we were. self.newButton.StopFlashing() # Reset the controls and focus to their default values. self.clear()
def AddTransactions(self, transactions, sources=None): Publisher.sendMessage("batch.start") # If we don't have any sources, we want None for each transaction. if sources is None: sources = [None for i in range(len(transactions))] for t, source in zip(transactions, sources): self.AddTransaction(transaction=t, source=source) Publisher.sendMessage("batch.end")
def AddTransactions(self, transactions, sources=None): Publisher.sendMessage("batch.start") # If we don't have any sources, we want None for each transaction. if sources is None: sources = [None for i in range(len(transactions))] for t, source in zip(transactions, sources): self.AddTransaction(transaction=t, source=source) Publisher.sendMessage("batch.end")
def onButton(self, event): """If the save button was clicked save, and close the dialog in any case (Close/Cancel/Save).""" assert event.Id in (wx.ID_CLOSE, wx.ID_SAVE) if event.Id == wx.ID_SAVE: #we have to substract 1 from combo_box selection because we added the "base currency" entry selectedCurrency = self.currencyCombo.GetSelection() - 1 Publisher.sendMessage("user.account_currency_changed", (self.Account, selectedCurrency)) self.GrandParent.Destroy()
def Remove(self, accountName): index = self.AccountIndex(accountName) if index == -1: raise bankexceptions.InvalidAccountException(accountName) account = self.pop(index) # Remove all the transactions associated with this account. account.Purge() Publisher.sendMessage("account.removed.%s"%accountName, account)
def Remove(self, accountName): index = self.AccountIndex(accountName) if index == -1: raise bankexceptions.InvalidAccountException(accountName) account = self.pop(index) # Remove all the transactions associated with this account. account.Purge() Publisher.sendMessage("account.removed.%s" % accountName, account)
def onAccountClick(self, event): """ This method is called when the current account has been changed by clicking on an account name. """ radio = event.EventObject if radio is self.allAccountsRadio: account = None else: account = self.accountObjects[radio.AccountIndex] Publisher.sendMessage("user.account changed", account)
def onAccountClick(self, event): """ This method is called when the current account has been changed by clicking on an account name. """ radio = event.EventObject if radio is self.allAccountsRadio: account = None else: account = self.accountObjects[radio.AccountIndex] Publisher.sendMessage("user.account changed", account)
def SetAutoSave(self, val): self._AutoSave = val wx.Config.Get().WriteBool("AUTO-SAVE", val) Publisher.sendMessage("controller.autosave_toggled", val) for model in self.Models: debug.debug("Setting auto-save to: %s" % val) model.Store.AutoSave = val # If the user enables auto-save, we want to also save. if self.AutoSave: Publisher.sendMessage("user.saved")
def SetAutoSave(self, val): self._AutoSave = val wx.Config.Get().WriteBool("AUTO-SAVE", val) Publisher.sendMessage("controller.autosave_toggled", val) for model in self.Models: debug.debug("Setting auto-save to: %s" % val) model.Store.AutoSave = val # If the user enables auto-save, we want to also save. if self.AutoSave: Publisher.sendMessage("user.saved")
def onButton(self, event): """If the save button was clicked save, and close the dialog in any case (Close/Cancel/Save).""" assert event.Id in (wx.ID_CLOSE, wx.ID_SAVE) if event.Id == wx.ID_SAVE: #we have to substract 1 from combo_box selection because we added the "base currency" entry selectedCurrency = self.currencyCombo.GetSelection() - 1 Publisher.sendMessage("user.account_currency_changed", (self.Account, selectedCurrency)) self.GrandParent.Destroy()
def AddRecurringTransaction(self, amount, description, date, repeatType, repeatEvery=1, repeatOn=None, endDate=None, source=None): # Create the recurring transaction object. recurring = RecurringTransaction(None, self, amount, description, date, repeatType, repeatEvery, repeatOn, endDate, source) # Store it. self.Store.MakeRecurringTransaction(recurring) # Add it to our internal list. self.RecurringTransactions.append(recurring) Publisher.sendMessage("recurringtransaction.created", (self, recurring)) return recurring
def onSearch(self, event=None): # Stop any timer that may be active, in the case of a manual search. self.SearchTimer.Stop() searchString = self.searchCtrl.Value # For a date, should be YYYY-MM-DD. matchType = self.matchChoices.index(self.matchBox.Value) searchInfo = (searchString, matchType) # Consider a blank search as a search cancellation. if searchString == "": self.onCancel() else: Publisher.sendMessage("SEARCH.INITIATED", searchInfo)
def Login(cls, username, password, notify=True): if cls.IsLoggedIn(): return accounts = {} for account in mintapi.get_accounts(username, password): account['balance'] = account['value'] # convert to wxBanker speak accounts[account['accountId']] = account cls._CachedAccounts = accounts if notify: Publisher.sendMessage("mint.updated")
def onNewTransaction(self, event=None): # First, ensure an account is selected. destAccount = self.CurrentAccount if destAccount is None: dlg = wx.MessageDialog(self.Parent, _("Please select an account and then try again."), _("No account selected"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() return # Grab the transaction values from the control. result = self.getValues() if result is None: # Validation failed, user was informed. return amount, desc, date = result # If a search is active, we have to ask the user what they want to do. if self.Parent.Parent.searchActive: msg = _("A search is currently active.") + " " + _('Would you like to clear the current search and make this transaction in "%s"?') % (destAccount.Name) dlg = wx.MessageDialog(self.Parent, msg, _("Clear search?"), style=wx.YES_NO|wx.ICON_WARNING) result = dlg.ShowModal() if result == wx.ID_YES: Publisher.sendMessage("SEARCH.CANCELLED") else: return sourceAccount = None # If the transfer box is checked, this is a transfer! if self.transferCheck.Value: result = self.Parent.transferRow.GetAccounts(destAccount) if result is None: dlg = wx.MessageDialog(self.Parent, _("This transaction is marked as a transfer. Please select the transfer account."), _("No account selected"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() return sourceAccount, destAccount = result # Now let's see if this is a recurring transaction if self.recursCheck.GetValue(): settings = self.Parent.GetSettings() args = [amount, desc, date] + list(settings) + [sourceAccount] destAccount.AddRecurringTransaction(*args) else: destAccount.AddTransaction(amount, desc, date, sourceAccount) # A transaction was added, we can stop flashing if we were. self.newButton.StopFlashing() # Reset the controls and focus to their default values. self.clear()
def getTransactionsFrom(self, account): transactions = TransactionList() # Generate a map of recurring transaction IDs to the objects for fast look-up. recurringCache = {} for recurring in account.Parent.GetRecurringTransactions(): recurringCache[recurring.ID] = recurring Publisher.sendMessage("batch.start") for result in self.dbconn.cursor().execute('SELECT * FROM transactions WHERE accountId=?', (account.ID,)).fetchall(): t = self.result2transaction(result, account, recurringCache=recurringCache) transactions.append(t) Publisher.sendMessage("batch.end") return transactions
def onSearch(self, event=None): # Stop any timer that may be active, in the case of a manual search. self.SearchTimer.Stop() searchString = self.searchCtrl.Value # For a date, should be YYYY-MM-DD. matchType = self.matchChoices.index(self.matchBox.Value) searchInfo = (searchString, matchType) # Consider a blank search as a search cancellation. if searchString == "": self.onCancel() else: Publisher.sendMessage("SEARCH.INITIATED", searchInfo) self.searchCtrl.SetForegroundColour('BLACK')
def Create(self, accountName): self.ThrowExceptionOnInvalidName(accountName) currency = 0 if len(self): # If the list contains items, the currency needs to be consistent. currency = self[-1].Currency account = self.Store.CreateAccount(accountName, currency) # Make sure this account knows its parent. account.Parent = self self.append(account) self.sort() Publisher.sendMessage("account.created.%s" % accountName, account) return account
def testToggleShowZero(self): # Create two accounts, make sure they are visible. a = self.Model.CreateAccount("A") b = self.Model.CreateAccount("B") b.AddTransaction(1) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 2) # Disable showing zero balance accounts, make sure the menu item is unchecked and one account is hidden. Publisher.sendMessage("user.showzero_toggled", False) self.assertFalse( self.Frame.MenuBar.showZeroMenuItem.IsChecked() ) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 1) # Make sure that a balance going to / coming from zero results in a visibility toggle. b.AddTransaction(-1) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 0)
def Create(self, accountName): self.ThrowExceptionOnInvalidName(accountName) currency = 0 if len(self): # If the list contains items, the currency needs to be consistent. currency = self[-1].Currency account = self.Store.CreateAccount(accountName, currency) # Make sure this account knows its parent. account.Parent = self self.append(account) self.sort() Publisher.sendMessage("account.created.%s" % accountName, account) return account
def testToggleShowZero(self): # Create two accounts, make sure they are visible. a = self.Model.CreateAccount("A") b = self.Model.CreateAccount("B") b.AddTransaction(1) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 2) # Disable showing zero balance accounts, make sure the menu item is unchecked and one account is hidden. Publisher.sendMessage("user.showzero_toggled", False) self.assertFalse(self.Frame.MenuBar.showZeroMenuItem.IsChecked()) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 1) # Make sure that a balance going to / coming from zero results in a visibility toggle. b.AddTransaction(-1) self.assertEqual(self.AccountListCtrl.GetVisibleCount(), 0)
def onWarning(self, message): warning = message.topic[1] if warning == "dirty exit": event = message.data title = _("Save changes?") msg = _("You have made changes since the last save. Would you like to save before exiting?") msg += "\n\n" + _("Note that enabling auto-save from the File menu will eliminate the need for manual saving.") dlg = wx.MessageDialog(self, msg, title, style=wx.CANCEL|wx.YES_NO|wx.ICON_WARNING) result = dlg.ShowModal() if result == wx.ID_YES: Publisher.sendMessage("user.saved") elif result == wx.ID_CANCEL: # The user cancelled the close, so cancel the event skip. event.Skip(False) dlg.Destroy()
def onToggleMore(self, event=None): # Show or hide the advanced search options. showLess = self.Sizer.IsShown(self.moreSizer) self.Sizer.Show(self.moreSizer, not showLess) # Update appropriate strings, and make them fully translatable. self.moreButton.State = showLess if showLess: tipActionStr = _("Show advanced search options") else: tipActionStr = _("Hide advanced search options") self.moreButton.SetToolTipString(tipActionStr) # Give or take the appropriate amount of space. self.Parent.Layout() Publisher.sendMessage("SEARCH.MORETOGGLED")
def onCalculatorAction(self, transactions, col, i): """ Given an action to perform on the calculator, and the row and col, generate the string of characters necessary to perform that action in the calculator, and push them. """ if col == self.COL_TOTAL: # Use the last total if multiple are selected. amount = transactions[-1]._Total else: amount = sum((t.Amount for t in transactions)) pushStr = ('C%s', '+%s=', '-%s=')[i] # Send, Add, Subtract commands pushStr %= amount Publisher.sendMessage("CALCULATOR.PUSH_CHARS", pushStr)
def onCalculatorAction(self, transactions, col, i): """ Given an action to perform on the calculator, and the row and col, generate the string of characters necessary to perform that action in the calculator, and push them. """ if col == self.COL_TOTAL: # Use the last total if multiple are selected. amount = transactions[-1]._Total else: amount = sum((t.Amount for t in transactions)) pushStr = ('C%s', '+%s=', '-%s=')[i] # Send, Add, Subtract commands pushStr %= amount Publisher.sendMessage("CALCULATOR.PUSH_CHARS", pushStr)
def onToggleMore(self, event=None): # Show or hide the advanced search options. showLess = self.Sizer.IsShown(self.moreSizer) self.Sizer.Show(self.moreSizer, not showLess) # Update appropriate strings, and make them fully translatable. self.moreButton.State = showLess if showLess: tipActionStr = _("Show advanced search options") else: tipActionStr = _("Hide advanced search options") self.moreButton.SetToolTipString(tipActionStr) # Give or take the appropriate amount of space. self.Parent.Layout() Publisher.sendMessage("SEARCH.MORETOGGLED")
def getTransactionsFrom(self, account): transactions = TransactionList() # Generate a map of recurring transaction IDs to the objects for fast look-up. recurringCache = {} for recurring in account.Parent.GetRecurringTransactions(): recurringCache[recurring.ID] = recurring Publisher.sendMessage("batch.start") for result in self.dbconn.cursor().execute( 'SELECT * FROM transactions WHERE accountId=?', (account.ID, )).fetchall(): t = self.result2transaction(result, account, recurringCache=recurringCache) transactions.append(t) Publisher.sendMessage("batch.end") return transactions
def testDirtyExitWarns(self): """ This test is kind of hilarious. We want to make sure we are warned of exiting with a dirty model, so we create an account, register a callback which will change its name when the dirty warning goes out, then trigger a dirty exit and make sure the account name has changed. """ self.Controller.AutoSave = False a = self.Model.CreateAccount("Unwarned!") # Create and register our callback to test for the warning message. def cb(message): a.Name = "Warned" Publisher.subscribe(cb, "warning.dirty exit") # Now send the exiting message, which should cause our callback to fire if everything is well. Publisher.sendMessage("exiting") self.assertEqual(a.Name, "Warned")
def onButton(self, event): """If the save button was clicked save, and close the dialog in any case (Close/Cancel/Save).""" assert event.Id in (wx.ID_CLOSE, wx.ID_SAVE, wx.ID_DELETE) if event.Id == wx.ID_SAVE: self.transactionCtrl.ToRecurring() originalTransaction = self.transactions[self.transactionChoice.Selection] modifiedTransaction = self.transactionCtrl.recurringObj originalTransaction.UpdateFrom(modifiedTransaction) # Let everyone know about this, someone might want to check if there are new pending transactions. Publisher.sendMessage("recurringtransaction.updated") elif event.Id == wx.ID_DELETE: recurring = self.GetCurrentRecurringTransaction() warningMsg = _("This will permanently remove this recurring transaction. Continue?") dlg = wx.MessageDialog(self, warningMsg, _("Warning"), style=wx.YES_NO|wx.ICON_EXCLAMATION) result = dlg.ShowModal() if result == wx.ID_YES: recurring.Parent.RemoveRecurringTransaction(recurring) self.GrandParent.Destroy()
def SelectItem(self, index): """Given an index (zero-based), select the appropriate account.""" if index is None: account = None self.allAccountsRadio.Value = True else: account = self.accountObjects[index] # Set the value in case it wasn't a click that triggered this. self.radioButtons[index].Value = True self.currentIndex = index # Update the remove/edit buttons. self.removeButton.Enabled = index is not None self.editButton.Enabled = index is not None self.configureButton.Enabled = index is not None # Inform everyone that we've changed. This is different from the 'user.account changed' event, # as account changes are also triggered by account removals and additions. Publisher.sendMessage("view.account changed", account) return account
def testDirtyExitWarns(self): """ This test is kind of hilarious. We want to make sure we are warned of exiting with a dirty model, so we create an account, register a callback which will change its name when the dirty warning goes out, then trigger a dirty exit and make sure the account name has changed. """ self.Controller.AutoSave = False a = self.Model.CreateAccount("Unwarned!") # Create and register our callback to test for the warning message. def cb(message): a.Name = "Warned" Publisher.subscribe(cb, "warning.dirty exit") # Now send the exiting message, which should cause our callback to fire if everything is well. Publisher.sendMessage("exiting") self.assertEqual(a.Name, "Warned")
def SelectItem(self, index): """Given an index (zero-based), select the appropriate account.""" if index is None: account = None self.allAccountsRadio.Value = True else: account = self.accountObjects[index] # Set the value in case it wasn't a click that triggered this. self.radioButtons[index].Value = True self.currentIndex = index # Update the remove/edit buttons. self.removeButton.Enabled = index is not None self.editButton.Enabled = index is not None self.configureButton.Enabled = index is not None # Inform everyone that we've changed. This is different from the 'user.account changed' event, # as account changes are also triggered by account removals and additions. Publisher.sendMessage("view.account changed", account) return account
def AddRecurringTransaction(self, amount, description, date, repeatType, repeatEvery=1, repeatOn=None, endDate=None, source=None): # Create the recurring transaction object. recurring = RecurringTransaction(None, self, amount, description, date, repeatType, repeatEvery, repeatOn, endDate, source) # Store it. self.Store.MakeRecurringTransaction(recurring) # Add it to our internal list. self.RecurringTransactions.append(recurring) Publisher.sendMessage("recurringtransaction.created", (self, recurring)) return recurring
def testSaveEventSaves(self): self.Controller.AutoSave = False model1 = self.Controller.Model # Create an account, don't save. self.assertEqual(len(model1.Accounts), 0) model1.CreateAccount("Hello!") self.assertEqual(len(model1.Accounts), 1) # Make sure that account doesn't exist on a new model model2 = self.Controller.LoadPath("test.db") self.assertEqual(len(model2.Accounts), 0) self.assertNotEqual(model1, model2) # Save Publisher.sendMessage("user.saved") # Make sure it DOES exist after saving. model3 = self.Controller.LoadPath("test.db") self.assertEqual(len(model3.Accounts), 1) self.assertEqual(model1, model3) self.assertNotEqual(model2, model3)
def __init__(self, store): ORMKeyValueObject.__init__(self, store) self.Store = store self.Accounts = AccountList(self, store) self._Tags = {} # Handle Mint integration, but send the message in the main thread, otherwise, dead. if self.MintEnabled: delayedresult.startWorker(lambda result: Publisher.sendMessage("mint.updated"), Mint.LoginFromKeyring, wkwargs={"notify": False}) Publisher.subscribe(self.onGlobalCurrencyChanged, "user.global_currency_changed") Publisher.subscribe(self.onAccountCurrencyChanged, "user.account_currency_changed") Publisher.subscribe(self.onMintToggled, "user.mint.toggled") Publisher.subscribe(self.onAccountChanged, "view.account changed") Publisher.subscribe(self.onTransactionTagged, "transaction.tagged") Publisher.subscribe(self.onTransactionUntagged, "transaction.untagged")
def AddTransaction(self, amount=None, description="", date=None, source=None, transaction=None): """ Enter a transaction in this account, optionally making the opposite transaction in the source account first. """ Publisher.sendMessage("batch.start") if transaction: # It is "partial" because its ID and parent aren't necessarily correct. partialTrans = transaction partialTrans.Parent = self elif amount is not None: # No transaction object was given, we need to make one. partialTrans = Transaction(None, self, amount, description, date) else: raise Exception( "AddTransaction: Must provide either transaction arguments or a transaction object." ) if source: otherTrans = source.AddTransaction(-1 * partialTrans.Amount, partialTrans._Description, partialTrans.Date) transaction = self.Store.MakeTransaction(self, partialTrans) # If it was a transfer, link them together if source: transaction.LinkedTransaction = otherTrans otherTrans.LinkedTransaction = transaction # Don't append if there aren't transactions loaded yet, it is already in the model and will appear on a load. (LP: 347385). if self._Transactions is not None: self.Transactions.append(transaction) else: # We will need to do some magic with these later when transactions are loaded. self._preTransactions.append(transaction) Publisher.sendMessage("transaction.created", (self, transaction)) # Update the balance. self.Balance += transaction.Amount Publisher.sendMessage("batch.end") if source: return transaction, otherTrans else: return transaction
def RemoveTransactions(self, transactions, removeLinkedTransactions=True): """ By default removing a transaction removes the linked transaction from its account too. However when removeLinkedTransactions is False, only transactions in this account will be removed, such as from Account.Purge(). """ Publisher.sendMessage("batch.start") # Return the sources, if any, of the removed transactions, in case we are moving for example. sources = [] # Accumulate the difference and update the balance just once. Cuts 33% time of removals. difference = 0 # Make a copy of the list, to protect against getting passed self.Transactions itself. # Otherwise when we remove from self.Transactions, we'd end up iterating over every other transaction! (LP: #605591) transactions = transactions[:] for transaction in transactions: if transaction not in self.Transactions: raise bankexceptions.InvalidTransactionException( "Transaction does not exist in account '%s'" % self.Name) # If this transaction was a transfer, delete the other transaction as well. if transaction.LinkedTransaction: link = transaction.LinkedTransaction sources.append(link.Parent) # Kill the other transaction's link to this one, otherwise this is quite recursive. link.LinkedTransaction = None # Delete the linked transaction from its account as well, if we should. if removeLinkedTransactions: link.Remove() else: sources.append(None) # Now remove this transaction. self.Store.RemoveTransaction(transaction) transaction.Parent = None self.Transactions.remove(transaction) difference += transaction.Amount # Update the balance. self.Balance -= difference # Send the message for all transactions at once, cuts _97%_ of time! OLV is slow here I guess. Publisher.sendMessage("transactions.removed", (self, transactions)) Publisher.sendMessage("batch.end") return sources
def RemoveTransactions(self, transactions, removeLinkedTransactions=True): """ By default removing a transaction removes the linked transaction from its account too. However when removeLinkedTransactions is False, only transactions in this account will be removed, such as from Account.Purge(). """ Publisher.sendMessage("batch.start") # Return the sources, if any, of the removed transactions, in case we are moving for example. sources = [] # Accumulate the difference and update the balance just once. Cuts 33% time of removals. difference = 0 # Make a copy of the list, to protect against getting passed self.Transactions itself. # Otherwise when we remove from self.Transactions, we'd end up iterating over every other transaction! (LP: #605591) transactions = transactions[:] for transaction in transactions: if transaction not in self.Transactions: raise bankexceptions.InvalidTransactionException("Transaction does not exist in account '%s'" % self.Name) # If this transaction was a transfer, delete the other transaction as well. if transaction.LinkedTransaction: link = transaction.LinkedTransaction sources.append(link.Parent) # Kill the other transaction's link to this one, otherwise this is quite recursive. link.LinkedTransaction = None # Delete the linked transaction from its account as well, if we should. if removeLinkedTransactions: link.Remove() else: sources.append(None) # Now remove this transaction. self.Store.RemoveTransaction(transaction) transaction.Parent = None self.Transactions.remove(transaction) difference += transaction.Amount # Update the balance. self.Balance -= difference # Send the message for all transactions at once, cuts _97%_ of time! OLV is slow here I guess. Publisher.sendMessage("transactions.removed", (self, transactions)) Publisher.sendMessage("batch.end") return sources
def AddTransaction(self, amount=None, description="", date=None, source=None, transaction=None): """ Enter a transaction in this account, optionally making the opposite transaction in the source account first. """ Publisher.sendMessage("batch.start") if transaction: # It is "partial" because its ID and parent aren't necessarily correct. partialTrans = transaction partialTrans.Parent = self elif amount is not None: # No transaction object was given, we need to make one. partialTrans = Transaction(None, self, amount, description, date) else: raise Exception("AddTransaction: Must provide either transaction arguments or a transaction object.") if source: otherTrans = source.AddTransaction(-1 * partialTrans.Amount, partialTrans._Description, partialTrans.Date) transaction = self.Store.MakeTransaction(self, partialTrans) # If it was a transfer, link them together if source: transaction.LinkedTransaction = otherTrans otherTrans.LinkedTransaction = transaction # Don't append if there aren't transactions loaded yet, it is already in the model and will appear on a load. (LP: 347385). if self._Transactions is not None: self.Transactions.append(transaction) else: # We will need to do some magic with these later when transactions are loaded. self._preTransactions.append(transaction) Publisher.sendMessage("transaction.created", (self, transaction)) # Update the balance. self.Balance += transaction.Amount Publisher.sendMessage("batch.end") if source: return transaction, otherTrans else: return transaction
def testSearch(self): a = self.Model.CreateAccount("A") b = self.Model.CreateAccount("B") t1 = a.AddTransaction(1, "Cat") t2 = a.AddTransaction(1, "Dog") t3 = b.AddTransaction(1, "Pig") t4 = b.AddTransaction(1, "Dog") # Ensure b is selected self.assertEqual(set(self.OLV.GetObjects()), set([t3, t4])) # Search for dog, make sure b's matching transaction is shown. Publisher.sendMessage("SEARCH.INITIATED", ("Dog", 1)) self.assertEqual(self.OLV.GetObjects(), [t4]) # Change to a, make sure we see a's match. Publisher.sendMessage("user.account changed", a) self.assertEqual(self.OLV.GetObjects(), [t2]) # Switch to all accounts, make sure we see both matches. Publisher.sendMessage("user.account changed", None) self.assertEqual(set(self.OLV.GetObjects()), set([t2, t4]))
def testSearch(self): a = self.Model.CreateAccount("A") b = self.Model.CreateAccount("B") t1 = a.AddTransaction(1, "Cat") t2 = a.AddTransaction(1, "Dog") t3 = b.AddTransaction(1, "Pig") t4 = b.AddTransaction(1, "Dog") # Ensure b is selected self.assertEqual(set(self.OLV.GetObjects()), set([t3, t4])) # Search for dog, make sure b's matching transaction is shown. Publisher.sendMessage("SEARCH.INITIATED", ("Dog", 1)) self.assertEqual(self.OLV.GetObjects(), [t4]) # Change to a, make sure we see a's match. Publisher.sendMessage("user.account changed", a) self.assertEqual(self.OLV.GetObjects(), [t2]) # Switch to all accounts, make sure we see both matches. Publisher.sendMessage("user.account changed", None) self.assertEqual(set(self.OLV.GetObjects()), set([t2, t4]))
def onExit(self, message): self.syncBalances() if self.Dirty: Publisher.sendMessage("warning.dirty exit", message.data)
def SetShowZero(self, val): self._ShowZeroBalanceAccounts = val wx.Config.Get().WriteBool("HIDE_ZERO_BALANCE_ACCOUNTS", not val) Publisher.sendMessage("controller.showzero_toggled", val)
def importTransactions(self): account = self.accountsDict[self.targetAccountCtrl.StringSelection] account.AddTransactions(self.transactionContainer.Transactions) # Trigger an account change so the user is now looking at the imported transactions. Publisher.sendMessage("user.account changed", account)
def publishIfAppropriate(self, attrname, val): if attrname in self.ORM_ATTRIBUTES: classname = self.__class__.__name__ Publisher.sendMessage("ormobject.updated.%s.%s" % (classname, attrname.strip("_")), self)
def MoveTransactions(self, transactions, destAccount): Publisher.sendMessage("batch.start") sources = self.RemoveTransactions(transactions) destAccount.AddTransactions(transactions, sources) Publisher.sendMessage("batch.end")
def onTagSearch(self, tag): Publisher.sendMessage("SEARCH.EXTERNAL", str(tag))
def SetShowCurrencyNick(self, val): self._ShowCurrencyNick = val wx.Config.Get().WriteBool("SHOW_CURRENCY_NICK", val) Publisher.sendMessage("controller.show_currency_nick_toggled", val)
def SetShowZero(self, val): self._ShowZeroBalanceAccounts = val wx.Config.Get().WriteBool("HIDE_ZERO_BALANCE_ACCOUNTS", not val) Publisher.sendMessage("controller.showzero_toggled", val)
def publishIfAppropriate(self, attrname, val): if attrname in self.ORM_ATTRIBUTES: classname = self.__class__.__name__ Publisher.sendMessage( "ormobject.updated.%s.%s" % (classname, attrname.strip("_")), self)
def RemoveTransactions(self, transactions): for t in transactions: self.Transactions.remove(t) Publisher.sendMessage("transactions.removed", (self, transactions))
def testKeyboardAccountShortcuts(self): model = self.Model accountList = self.AccountListCtrl a = model.CreateAccount("A") a.AddTransaction(1) b = model.CreateAccount("B") b.AddTransaction(2) c = model.CreateAccount("C") c.AddTransaction(3) self.assertCurrentAccount(c) Publisher.sendMessage("user.previous account") self.assertCurrentAccount(b) Publisher.sendMessage("user.previous account") self.assertCurrentAccount(a) Publisher.sendMessage("user.previous account") self.assertCurrentAccount(a) Publisher.sendMessage("user.next account") self.assertCurrentAccount(b) Publisher.sendMessage("user.next account") self.assertCurrentAccount(c) Publisher.sendMessage("user.account changed", None) self.assertCurrentAccount(None) Publisher.sendMessage("user.next account") self.assertCurrentAccount(None) Publisher.sendMessage("user.previous account") self.assertCurrentAccount(c) # Bring "B" to zero, and test that it gets skipped over when not viewing zero balances. b.AddTransaction(-2) Publisher.sendMessage("user.showzero_toggled", False) try: Publisher.sendMessage("user.previous account") self.assertCurrentAccount(a) Publisher.sendMessage("user.next account") self.assertCurrentAccount(c) finally: Publisher.sendMessage("user.showzero_toggled", True)
def onExit(self, message): self.syncBalances() if self.Dirty: Publisher.sendMessage("warning.dirty exit", message.data)
def SetShowCurrencyNick(self, val): self._ShowCurrencyNick = val wx.Config.Get().WriteBool("SHOW_CURRENCY_NICK", val) Publisher.sendMessage("controller.show_currency_nick_toggled", val)