Exemple #1
0
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
Exemple #2
0
    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()
Exemple #3
0
    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])
Exemple #4
0
 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()
Exemple #6
0
 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")
Exemple #7
0
    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()
Exemple #9
0
    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)
Exemple #10
0
    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)
Exemple #11
0
 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)
Exemple #12
0
 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)
Exemple #13
0
    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")
Exemple #14
0
    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")
Exemple #15
0
    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()
Exemple #16
0
 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
Exemple #17
0
    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)
Exemple #18
0
    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()
Exemple #20
0
 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
Exemple #21
0
    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')
Exemple #22
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
Exemple #23
0
 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)
Exemple #24
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
Exemple #25
0
    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)
Exemple #26
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()
Exemple #27
0
    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")
Exemple #28
0
    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)
Exemple #29
0
    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)
Exemple #30
0
    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")
Exemple #31
0
    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
Exemple #32
0
    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()
Exemple #34
0
    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
Exemple #35
0
    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")
Exemple #36
0
    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
Exemple #37
0
    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
Exemple #38
0
    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)
Exemple #39
0
    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")
Exemple #40
0
    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
Exemple #41
0
    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
Exemple #42
0
    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
Exemple #43
0
    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
Exemple #44
0
 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]))
Exemple #45
0
    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]))
Exemple #46
0
 def onExit(self, message):
     self.syncBalances()
     if self.Dirty:
         Publisher.sendMessage("warning.dirty exit", message.data)
Exemple #47
0
 def SetShowZero(self, val):
     self._ShowZeroBalanceAccounts = val
     wx.Config.Get().WriteBool("HIDE_ZERO_BALANCE_ACCOUNTS", not val)
     Publisher.sendMessage("controller.showzero_toggled", val)
Exemple #48
0
 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)
Exemple #49
0
 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)
Exemple #50
0
 def MoveTransactions(self, transactions, destAccount):
     Publisher.sendMessage("batch.start")
     sources = self.RemoveTransactions(transactions)
     destAccount.AddTransactions(transactions, sources)
     Publisher.sendMessage("batch.end")
Exemple #51
0
 def onTagSearch(self, tag):
     Publisher.sendMessage("SEARCH.EXTERNAL", str(tag))
Exemple #52
0
 def SetShowCurrencyNick(self, val):
     self._ShowCurrencyNick = val
     wx.Config.Get().WriteBool("SHOW_CURRENCY_NICK", val)
     Publisher.sendMessage("controller.show_currency_nick_toggled", val)
Exemple #53
0
 def SetShowZero(self, val):
     self._ShowZeroBalanceAccounts = val
     wx.Config.Get().WriteBool("HIDE_ZERO_BALANCE_ACCOUNTS", not val)
     Publisher.sendMessage("controller.showzero_toggled", val)
Exemple #54
0
 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)
Exemple #55
0
 def RemoveTransactions(self, transactions):
     for t in transactions:
         self.Transactions.remove(t)
     Publisher.sendMessage("transactions.removed", (self, transactions))
Exemple #56
0
    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)
Exemple #57
0
 def onExit(self, message):
     self.syncBalances()
     if self.Dirty:
         Publisher.sendMessage("warning.dirty exit", message.data)
Exemple #58
0
 def SetShowCurrencyNick(self, val):
     self._ShowCurrencyNick = val
     wx.Config.Get().WriteBool("SHOW_CURRENCY_NICK", val)
     Publisher.sendMessage("controller.show_currency_nick_toggled", val)