Commits

Joel Mohler  committed 77088df

rename src files to not clash with schema class names

  • Participants
  • Parent commits 242adca

Comments (0)

Files changed (12)

File pyhacc/AccountTypes.py

-# -*- coding: utf-8 -*-
-##############################################################################
-#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
-#
-#  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-##############################################################################
-from qtalchemy import *
-from PySide import QtCore, QtGui
-from qtalchemy.dialogs import *
-from PyHaccSchema import *
-
-class AccountTypeEditor(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a = AccountTypeEditor(None,row=s.query(AccountTypes).filter(AccountTypes.name=="Asset").one())
-    """
-    def __init__(self, parent, row=None, Session=None, row_id=None, flush=True):
-        BoundDialog.__init__(self,parent)
-
-        self.setObjectName("AccountTypesInfo")
-        self.setDataReader(Session, AccountTypes, "id")
-
-        main = QtGui.QVBoxLayout()
-        self.setLayout(main)
-        grid = LayoutLayout(main,QtGui.QFormLayout())
-        self.mm = self.mapClass(AccountTypes)
-        self.mm.addBoundField(grid,"name")
-        
-        tabs = LayoutWidget(main,QtGui.QTabWidget())
-
-        tab1 = QtGui.QWidget()
-        page_layout = QtGui.QVBoxLayout(tab1)
-        self.mm.addBoundForm(page_layout,"sort balance_sheet debit retained_earnings description".split(' '))
-        tabs.addTab(tab1,"Settings")
-
-        self.account_tab = PBTableTab(self, Session, AccountEntity, 
-                        [(AccountTypes.id, lambda dataContext: dataContext.id)], 
-                        Query((Accounts.id,Accounts.name.label("Account Name"),Journals.name.label("Journal"))).outerjoin(Journals).join(AccountTypes), 
-                        extensionId=suffixExtId(self, "Accounts"))
-        tabs.addTab(self.account_tab,"Accounts")
-
-        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
-        buttonbox.accepted.connect(self.accept)
-        buttonbox.rejected.connect(self.reject)
-
-        self.geo = WindowGeometry(self, position=False, tabs=[tabs])
-
-        self.readData(row, row_id)
-        
-    def load(self):
-        self.mm.connect_instance(self.main_row)
-        self.account_tab.refresh(self.main_row)
-        self.setWindowTitle("Account Type {0.name}".format(self.main_row))

File pyhacc/Accounts.py

-# -*- coding: utf-8 -*-
-##############################################################################
-#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
-#
-#  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-##############################################################################
-from qtalchemy import PBTableTab
-from qtalchemy.dialogs import *
-from qtalchemy.widgets import *
-from PySide import QtCore, QtGui
-from PyHaccSchema import *
-from PyHaccLib import *
-
-
-class ReconcileCommands(object):
-    def __init__(self, Session, parent):
-        self.Session = Session
-        self.parent = parent
-
-    commands = CommandMenu("_commands")
-
-    #@commands.itemAction("&Print", iconFile=":/pyhacc/document-print.ico", requireSelection=False, viewRelated=False)
-    #def print_(self, id=None):
-    #    q = self.query(self.Session)
-    #    # TODO:  implement the report!
-
-    @commands.itemAction("&Refresh", iconFile=":/pyhacc/refresh.ico", requireSelection=False, viewRelated=False)
-    def refresh(self, id=None):
-        # do nothing, just be a place-holder for save/load bracketing
-        pass
-
-class AccountEditor(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a = AccountEditor(None,row=s.query(Accounts).filter(Accounts.name=="Cash").one())
-    """
-    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
-        BoundDialog.__init__(self,parent)
-
-        self.setObjectName("AccountsInfo")
-        self.setDataReader(Session, Accounts, "id")
-
-        main = QtGui.QVBoxLayout(self)
-        top_grid = LayoutLayout(main,QtGui.QGridLayout())
-
-        self.mm = self.mapClass(Accounts)
-        self.mm.addBoundFieldGrid(top_grid,"name",0,0)
-        self.mm.addBoundFieldGrid(top_grid,"type",0,2)
-
-        self.tab = LayoutWidget(main,QtGui.QTabWidget())
-
-        self.accounting_tab = QtGui.QWidget()
-        self.mm.addBoundForm(QtGui.QVBoxLayout(self.accounting_tab),["journal_name","retearn_name","description"])
-        self.tab.addTab( self.accounting_tab,"&Accounting" )
-
-        self.institution_tab = QtGui.QWidget()
-        self.mm.addBoundForm(QtGui.QVBoxLayout(self.institution_tab),"instname,instaddr1,instaddr2,instcity".split(','))
-        self.tab.addTab( self.institution_tab,"&Institution" )
-
-        self.rec_tab = QtGui.QWidget()
-        self.mm.addBoundForm(QtGui.QVBoxLayout(self.rec_tab),"rec_note".split(','))
-        self.tab.addTab( self.rec_tab,"&Reconciliation" )
-
-        self.transactions_tab = PBTableTab(self, Session, TransactionEntity, 
-                        [(Splits.account_id, lambda dataContext: dataContext.id)], 
-                        Query((Transactions.tid.label("id"),Transactions.date, Transactions.reference, Transactions.payee, Transactions.memo, Splits.sum)).join(Splits).order_by(Transactions.date.desc()), 
-                        extensionId=suffixExtId(self, "Transactions"))
-        self.tab.addTab(self.transactions_tab,"Tran&sactions")
-
-        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
-        buttonbox.accepted.connect(self.accept)
-        buttonbox.rejected.connect(self.reject)
-
-        self.geo = WindowGeometry(self, position=False, tabs=[self.tab])
-
-        self.readData(row, row_id)
-
-    def load(self):
-        self.mm.connect_instance(self.main_row)
-        self.transactions_tab.refresh(self.main_row)
-        self.setWindowTitle( "Account Information - {0.name}".format(self.main_row) )
-
-class SplitQuery(ModelObject):
-    def __init__(self):
-        self.thisTag = None
-        self.thisAccount = None
-        self.thisType = None
-        self.fiscal_range = "This Year"
-
-    def set(self, account_id, type_id, tag_id):
-        if tag_id is not None:
-            self.thisTag = self.session().query(Tags).filter(Tags.id==tag_id).one()
-        if type_id is not None:
-            self.thisType = self.session().query(AccountTypes).filter(AccountTypes.id==type_id).one()
-        if account_id is not None:
-            self.thisAccount = self.session().query(Accounts).filter(Accounts.id==account_id).one()
-
-    classEvents = ModelObject.Events()
-
-    tag = TagReferral("Tag", "thisTag") 
-    account = AccountReferral("Account", "thisAccount") 
-    type_name = AccountTypeReferral("Account Type","thisType")
-    fiscal_range = FiscalRangeAttr(str, "Range")
-    begin_date = UserAttr(datetime.date, "Beginning Date")
-    end_date = UserAttr(datetime.date, "Ending Date")
-    tagged = UserAttr(bool, "Only show tagged splits")
-
-    fiscalDateRangeEvents(classEvents, "fiscal_range", "begin_date", "end_date")
-
-    def query(self, Session):
-        session = Session()
-        tag = session.query(Tags).filter(Tags.id==self.thisTag.id).one()
-        q = session.query(Splits).join(Transactions)
-        if self.begin_date is not None:
-            q = q.filter(Transactions.date>=self.begin_date)
-        if self.end_date is not None:
-            q = q.filter(Transactions.date<=self.end_date)
-        if self.thisType is not None:
-            q = q.join(Accounts).filter(Accounts.type_id==self.thisType.id)
-        if self.thisAccount is not None:
-            q = q.filter(Splits.account_id==self.thisAccount.id)
-        if self.tagged:
-            q = q.join(Tagsplits).filter(Tagsplits.tag_id==self.thisTag.id)
-        q = q.order_by(Transactions.date, Transactions.payee)
-        return session, tag, q
-
-class TagWorksheet(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a = TagWorksheet(None, account_id=s.query(Accounts).filter(Accounts.name=="Cash").one().id, Session=Session)
-    """
-    def __init__(self, parent, account_id=None, type_id=None, tag_id=None, Session=None):
-        BoundDialog.__init__(self,parent)
-
-        self.setWindowTitle("Split Tagger Worksheet")
-        self.setObjectName("TagWorksheet")
-        self.setDataReader(Session, SplitQuery, None)
-
-        main = QtGui.QHBoxLayout(self)
-        left_controls = LayoutLayout(main,QtGui.QVBoxLayout())
-
-        self.mm = self.mapClass(SplitQuery)
-        self.mm.addBoundForm(left_controls,["tag", "fiscal_range", "begin_date", "end_date", "account", "type_name", "tagged"])
-
-        right_controls = LayoutLayout(main, QtGui.QVBoxLayout())
-        self.toolbar = LayoutWidget(right_controls, QtGui.QToolBar())
-        self.checkTable = LayoutWidget(right_controls, TableView(extensionId=suffixExtId(self, "Table")))
-        self.model = ClassTableModel(SplitTaggerSimple,"tagged,debit,credit,date,reference,payee,memo".split(','), readonly=False, fixed_rows=True)
-        self.checkTable.setModel(self.model)
-        main.setStretch(1,15) # massively favor the table side of things
-
-        self.buttonBox = LayoutWidget(left_controls,QtGui.QDialogButtonBox())
-
-        self.okBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Ok)
-        self.buttonBox.accepted.connect(self.accept)
-        self.cancelBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
-        self.buttonBox.rejected.connect(self.reject)
-        self.refreshBtn = self.buttonBox.addButton("Refresh", QtGui.QDialogButtonBox.ActionRole)
-        self.refreshBtn.clicked.connect(lambda checked=False: self.load())
-
-        self.geo = WindowGeometry(self)
-
-        self.entity = TransactionEntity(Session, self)
-        self.binding = self.entity.itemCommands.withView(self.checkTable, objectConverter=lambda x:x.split.Transaction.tid)
-        self.binding.fillToolbar(self.toolbar)
-        self.binding.preCommand.connect(self.preCommandSave)
-        self.binding.refresh.connect(self.refresh)
-
-        self.toolbar.addSeparator()
-
-        self.entity2 = ReconcileCommands(self.Session, self.parent())
-        self.bindings2 = self.entity2.commands.withView(self.checkTable, bindDefault=False)
-        self.bindings2.fillToolbar(self.toolbar)
-        self.bindings2.preCommand.connect(self.preCommandSave)
-        self.bindings2.refresh.connect(self.refresh)
-
-        self.queryStuff = SplitQuery()
-        self.queryStuffSession = Session()
-        self.queryStuffSession.npadd(self.queryStuff)
-        self.queryStuff.set(account_id=account_id, type_id=type_id, tag_id=tag_id)
-
-        self.entity2.queryStuff = self.queryStuff
-
-        self.main_row = self.queryStuff
-        self.load()
-        #self.readData(self.queryStuff, None)
-
-    def load(self):
-        self.submit()
-        self.checkTable.setEnabled(self.queryStuff.thisTag is not None)
-
-        if self.queryStuff.thisTag is not None:
-            progress = QtGui.QProgressDialog("Loading...", "", 0, 2, self)
-            progress.setCancelButton(None)
-            progress.setMinimumDuration(1)
-            progress.setWindowModality(QtCore.Qt.WindowModal)
-
-            #TODO: it seems I don't know how to make the QProgressDialog show at a convenient point
-            progress.setValue(0)
-
-            self.session, self.tag, q = self.queryStuff.query(self.Session)
-            self.split_list = q.all()
-            progress.setValue(1)
-
-            self.shown_check_split_list = []
-            for i in range(len(self.split_list)):
-                progress.setValue(i)
-                split = self.split_list[i]
-                self.shown_check_split_list.append(SplitTaggerSimple(split, self.tag))
-
-            self.model.reset_content_from_list(self.shown_check_split_list)
-            for x in self.shown_check_split_list:
-                instanceEvent(x, "set", "tagged")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
-
-            progress.setValue(2)
-
-            progress.hide()
-
-        self.mm.connect_instance(self.queryStuff)
-
-        if self.main_row.thisAccount is not None:
-            self.setWindowTitle( "Account Tag Worksheet - {0}".format(self.main_row.thisAccount.name) )
-
-class AccountReconcile(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a = AccountReconcile(None,row=s.query(Accounts).filter(Accounts.name=="Cash").one())
-    """
-    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
-        BoundDialog.__init__(self,parent)
-
-        self.setObjectName("AccountReconciliation")
-        self.setDataReader(Session, Accounts, "id")
-
-        main = QtGui.QHBoxLayout(self)
-        left_controls = LayoutLayout(main,QtGui.QVBoxLayout())
-
-        self.mm = self.mapClass(SplitTagger.ReconciliationStatus)
-        self.mm.addBoundForm(left_controls,["reconciled_balance","pending_balance","outstanding_balance"])
-        self.mm_account = self.mapClass(Accounts)
-        self.mm_account.addBoundForm(left_controls,["rec_note"])
-
-        right_controls = LayoutLayout(main, QtGui.QVBoxLayout())
-        self.toolbar = LayoutWidget(right_controls, QtGui.QToolBar())
-        self.checkTable = LayoutWidget(right_controls, TableView(extensionId=suffixExtId(self, "Table")))
-        self.model = ClassTableModel(SplitTagger,"pending,reconciled,debit,credit,date,reference,payee,memo".split(','), readonly=False, fixed_rows=True)
-        self.checkTable.setModel(self.model)
-        main.setStretch(1,15) # massively favor the table side of things
-
-        self.buttonBox = LayoutWidget(left_controls,QtGui.QDialogButtonBox())
-
-        self.okBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Ok)
-        self.buttonBox.accepted.connect(self.accept)
-        self.reconcileBtn = self.buttonBox.addButton("&Reconcile",QtGui.QDialogButtonBox.ActionRole)
-        self.reconcileBtn.clicked.connect(self.reconcile_now)
-        self.cancelBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
-        self.buttonBox.rejected.connect(self.reject)
-
-        self.geo = WindowGeometry(self)
-
-        self.entity = TransactionEntity(Session, self)
-        self.binding = self.entity.itemCommands.withView(self.checkTable, objectConverter=lambda x:x.split.Transaction.tid)
-        self.binding.fillToolbar(self.toolbar)
-        self.binding.preCommand.connect(self.preCommandSave)
-        self.binding.refresh.connect(self.refresh)
-
-        self.toolbar.addSeparator()
-
-        self.entity2 = ReconcileCommands(self.Session, self.parent())
-        self.bindings2 = self.entity2.commands.withView(self.checkTable, bindDefault=False)
-        self.bindings2.fillToolbar(self.toolbar)
-        self.bindings2.preCommand.connect(self.preCommandSave)
-        self.bindings2.refresh.connect(self.refresh)
-
-        self.readData(row, row_id)
-
-    def load(self):
-        self.recStatus = SplitTagger.ReconciliationStatus()
-
-        self.tagRec = self.session.query(Tags).filter(Tags.name==Tags.Names.BankReconciled).one()
-        self.tagPen = self.session.query(Tags).filter(Tags.name==Tags.Names.BankPending).one()
-        q = self.session.query(Tagsplits, Tags.name.label('tag_name_s')).join(Tags).filter(Tags.name==Tags.Names.BankReconciled).subquery()
-        self.outstanding_split_list = self.session.query(Splits) \
-                            .outerjoin(q) \
-                            .join(Accounts) \
-                            .join(Transactions) \
-                            .filter(Accounts.id==self.main_row.id) \
-                            .filter(q.c.tag_name_s==None) \
-                            .order_by(Transactions.date) \
-                            .all()
-
-        progress = QtGui.QProgressDialog("Loading...", "", 0, len(self.outstanding_split_list), self)
-        progress.setCancelButton(None)
-        progress.setMinimumDuration(1)
-        progress.setWindowModality(QtCore.Qt.WindowModal)
-
-        self.shown_check_split_list = []
-        for i in range(len(self.outstanding_split_list)):
-            progress.setValue(i)
-            split = self.outstanding_split_list[i]
-            self.shown_check_split_list.append(SplitTagger(split,self.tagRec,self.tagPen,self.recStatus))
-
-        self.reconciled_split_list = self.session.query(Splits) \
-                            .outerjoin(q) \
-                            .join(Accounts) \
-                            .filter(Accounts.id==self.main_row.id) \
-                            .filter(q.c.tag_name_s==Tags.Names.BankReconciled) \
-                            .all()
-        self.recStatus.reconciled_balance = sum([split.sum for split in self.reconciled_split_list],decimal.Decimal())
-        self.recStatus.pending_balance = self.recStatus.reconciled_balance + sum([split.amount for split in self.shown_check_split_list if split.pending],decimal.Decimal())
-        self.recStatus.outstanding_balance = self.recStatus.reconciled_balance + sum([split.amount for split in self.shown_check_split_list],decimal.Decimal())
-
-        self.model.reset_content_from_list(self.shown_check_split_list)
-        for x in self.shown_check_split_list:
-            instanceEvent(x, "set", "pending")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
-            instanceEvent(x, "set", "reconciled")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
-
-        progress.setValue(len(self.outstanding_split_list))
-
-        self.mm.connect_instance(self.recStatus)
-        self.mm_account.connect_instance(self.main_row)
-
-        self.setWindowTitle( "Account Reconciliation - {0}".format(self.main_row.name) )
-
-    def reconcile_now(self):
-        for s in self.shown_check_split_list:
-            if s.pending:
-                s.reconciled = True
-                s.pending = False

File pyhacc/Journals.py

-# -*- coding: utf-8 -*-
-##############################################################################
-#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
-#
-#  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-##############################################################################
-from qtalchemy import *
-from PySide import QtCore, QtGui
-from qtalchemy.dialogs import *
-from PyHaccSchema import *
-
-class JournalEditor(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a=JournalEditor(None,row=s.query(Journals).filter(Journals.name=="General").one())
-    """
-    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
-        BoundDialog.__init__(self,parent)
-        self.setObjectName("JournalsInfo")
-        self.setDataReader(Session, Journals, "id")
-
-        self.mm = self.mapClass(AccountTypes)
-
-        main = QtGui.QVBoxLayout(self)
-        grid = LayoutLayout(main,QtGui.QFormLayout())
-        self.mm.addBoundField(grid,"name")
-
-        tabs = LayoutWidget(main,QtGui.QTabWidget())
-
-        tab1 = QtGui.QWidget()
-        page_layout = QtGui.QVBoxLayout(tab1)
-        self.mm.addBoundForm(page_layout,["description"])
-        tabs.addTab(tab1,"Settings")
-
-        self.accounts_tab = PBTableTab(self, Session, AccountEntity, 
-                        [(Journals.id, lambda dataContext: dataContext.id)], 
-                        Query((Accounts.id,Accounts.name.label("Account Name"),AccountTypes.name.label("Type"))).outerjoin(Journals).join(AccountTypes), 
-                        extensionId=suffixExtId(self, "Accounts"))
-        tabs.addTab(self.accounts_tab,"Accounts")
-
-        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
-        buttonbox.accepted.connect(self.accept)
-        buttonbox.rejected.connect(self.reject)
-
-        self.geo = WindowGeometry(self, position=False, tabs=[tabs])
-
-        self.readData(row, row_id)
-        
-    def load(self):
-        self.mm.connect_instance(self.main_row)
-        self.accounts_tab.refresh(self.main_row)
-        self.setWindowTitle("Journals - {0.name}".format(self.main_row))

File pyhacc/PyHaccMainWindow.py

 
 from PyHaccSchema import *
 from PyHaccLib import *
-from Transactions import *
+from transactions import *
 from BalanceDock import *
 from DateDock import *
 

File pyhacc/PyHaccSchema.py

         return (Query(queryCols).order_by(AccountTypes.sort, AccountTypes.name, self.key_column), lambda x: x._hidden_id)
 
     def view_row(self, session, row):
-        import AccountTypes as amod
-        aa=amod.AccountTypeEditor(self.parent,Session=self.Session,row=row)
+        import accounttypes as mod
+        aa=mod.AccountTypeEditor(self.parent,Session=self.Session,row=row)
         aa.show()
         aa.exec_()
         session.close()
     @itemCommands.itemAction("&Tag Transactions...", iconFile=":/pyhacc/money.ico")
     def view(self, id):
         session, a = self.session_item(id)
-        import Accounts as amod
-        aa=amod.TagWorksheet(self.parent, Session=self.Session, type_id=a.id)
+        import accounts as mod
+        aa=mod.TagWorksheet(self.parent, Session=self.Session, type_id=a.id)
         aa.show()
         aa.exec_()
         session.close()
         self.list_search_columns = [Accounts.name, Accounts.description]
 
     def view_row(self, session, row):
-        import Accounts as amod
-        aa=amod.AccountEditor(self.parent,Session=self.Session,row=row)
+        import accounts as mod
+        aa=mod.AccountEditor(self.parent,Session=self.Session,row=row)
         aa.show()
         aa.exec_()
         session.close()
     @itemCommands.itemAction("&Reconcile...", iconFile=":/pyhacc/money.ico")
     def view(self, id):
         session, a = self.session_item(id)
-        import Accounts as amod
-        aa=amod.AccountReconcile(self.parent,Session=self.Session,row=a)
+        import accounts as mod
+        aa=mod.AccountReconcile(self.parent,Session=self.Session,row=a)
         aa.show()
         aa.exec_()
         session.close()
     @itemCommands.itemAction("&Tag Transactions...", iconFile=":/pyhacc/money.ico")
     def view(self, id):
         session, a = self.session_item(id)
-        import Accounts as amod
-        aa=amod.TagWorksheet(self.parent, Session=self.Session, account_id=a.id)
+        import accounts as mod
+        aa=mod.TagWorksheet(self.parent, Session=self.Session, account_id=a.id)
         aa.show()
         aa.exec_()
         session.close()
         self.list_search_columns = [Journals.name, Journals.description]
 
     def view_row(self, session, row):
-        import Journals as mod
+        import journals as mod
         aa=mod.JournalEditor(self.parent,Session=self.Session,row=row)
         aa.show()
         aa.exec_()
     @basicNew.itemNew(iconFile=":/pyhacc/transactions-new.ico")
     @itemCommands.itemNew(iconFile=":/pyhacc/transactions-new.ico")
     def new(self, id=None):
-        import Transactions as amod
+        import transactions as mod
         session = self.Session()
         new = Transactions()
         session.add(new)
         if self.Session.date is not None:
             new.date = self.Session.date
-        aa=amod.TransactionEditor(self.parent,Session=self.Session,row=new)
+        aa=mod.TransactionEditor(self.parent,Session=self.Session,row=new)
         aa.show()
         aa.exec_()
         session.close()
 
     @itemCommands.itemNew(descr="Copy...", requireSelection=True, iconFile=":/pyhacc/transactions-copy.ico")
     def copy(self,id):
-        import Transactions as amod
+        import transactions as mod
         session = self.Session()
         a = session.query(Transactions).filter(Transactions.tid==id).one()
         
             new.date = self.Session.date
 
         session.add(new)
-        aa=amod.TransactionEditor(self.parent,Session=self.Session,row=new)
+        aa=mod.TransactionEditor(self.parent,Session=self.Session,row=new)
         aa.show()
         aa.exec_()
         session.close()
 
     @itemCommands.itemAction("&Edit...", default=True, iconFile=":/pyhacc/transactions-edit.ico")
     def view(self, id):
-        import Transactions as amod
+        import transactions as mod
         session = self.Session()
         a = session.query(Transactions).filter(Transactions.tid==id).one()
-        aa=amod.TransactionEditor(self.parent,Session=self.Session,row=a)
+        aa=mod.TransactionEditor(self.parent,Session=self.Session,row=a)
         aa.show()
         aa.exec_()
         session.close()
         self.list_search_columns = [Tags.name, Tags.description]
 
     def view_row(self, session, row):
-        import Tags as mod
+        import tags as mod
         aa=mod.TagEditor(self.parent,Session=self.Session,row=row)
         aa.show()
         aa.exec_()

File pyhacc/Tags.py

-# -*- coding: utf-8 -*-
-##############################################################################
-#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
-#
-#  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-##############################################################################
-from qtalchemy import *
-from PySide import QtCore, QtGui
-from qtalchemy.dialogs import *
-
-from PyHaccSchema import *
-
-class TagEditor(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a=TagEditor(None,row=s.query(Tags).filter(Tags.name==Tags.Names.BankReconciled).one())
-    """
-    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
-        BoundDialog.__init__(self,parent)
-        self.setWindowTitle("Tags")
-        self.setDataReader(Session, Tags, "id")
-
-        main = QtGui.QVBoxLayout(self)
-        grid = LayoutLayout(main,QtGui.QFormLayout())
-        self.mm = self.mapClass(Tags)
-        self.mm.addBoundField(grid,"name")
-        self.mm.addBoundField(grid,"description")
-
-        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
-        buttonbox.accepted.connect(self.accept)
-        buttonbox.rejected.connect(self.reject)
-
-        self.readData(row, row_id)
-
-    def load(self):
-        self.mm.connect_instance(self.main_row)

File pyhacc/Transactions.py

-# -*- coding: utf-8 -*-
-##############################################################################
-#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
-#
-#  Distributed under the terms of the GNU General Public License (GPL)
-#                  http://www.gnu.org/licenses/
-##############################################################################
-from qtalchemy import *
-from PySide import QtCore, QtGui
-from qtalchemy.dialogs import *
-from qtalchemy.widgets import *
-
-import sqlalchemy.sql.expression as expr
-from PyHaccSchema import *
-from PyHaccUI import *
-
-class TransactionEditor(BoundDialog):
-    """
-    >>> app, Session = qtappsession()
-    >>> s = Session()
-    >>> a = TransactionEditor(None,row=s.query(Transactions).filter(Transactions.memo=="Groceries")[0])
-    """
-    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
-        BoundDialog.__init__(self,parent)
-
-        self.setWindowTitle("Transaction")
-        self.setObjectName("Transaction Editor")
-
-        self.setDataReader(Session, Transactions, "tid")
-
-        main = QtGui.QVBoxLayout(self)
-        top_grid = LayoutLayout(main,QtGui.QGridLayout())
-
-        self.mapping = self.mapClass(Transactions)
-        self.mapping.addBoundFieldGrid(top_grid,"date",0,0)
-        self.mapping.addBoundFieldGrid(top_grid,"reference",0,2)
-        self.mapping.addBoundFieldGrid(top_grid,"payee",1,0,columnSpan=3)
-        self.mapping.addBoundFieldGrid(top_grid,"memo",2,0,columnSpan=3)
-
-        self.tab = LayoutWidget(main, QtGui.QTabWidget())
-
-        # first tab: transactions
-        self.splitme = QtGui.QSplitter()
-        self.tags_table = TableView()
-        self.splits_table = TableView()
-        self.splitme.addWidget(self.splits_table)
-        self.splitme.addWidget(self.tags_table)
-        self.tab.addTab(self.splitme, "&Transactions")
-
-        # second tab:  receipt memo
-        self.receipt = QtGui.QTextEdit(self)
-        self.tab.addTab(self.receipt, "&Receipt")
-        self.mapping.bind(self.receipt, "receipt")
-
-        self.splits_model = ClassTableModel(Splits, ("account", "debit", "credit"), readonly=False)
-        self.splits_table.setModel(self.splits_model, extensionId=suffixExtId(self, "Splits"))
-        self.splits_table.setItemDelegate(AlchemyModelDelegate())
-
-        self.tags_model = ClassTableModel(CheckableTagList, ("checked", "tag_name"), readonly=False, fixed_rows=True)
-        self.tags_table.setModel(self.tags_model, extensionId=suffixExtId(self, "Tags"))
-        self.tags_table.setItemDelegate(AlchemyModelDelegate())
-
-        self.split_model_sel = self.splits_table.selectionModel()
-        self.split_model_sel.selectionChanged.connect(self.setupChecks)
-
-        self.buttonBox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
-        self.buttonBox.accepted.connect(self.accept)
-        self.buttonBox.rejected.connect(self.reject)
-        self.buttonBox.addButton(QtGui.QDialogButtonBox.Apply).clicked.connect(self.commitAndRefresh)
-        self.buttonBox.addButton("To Cli&pboard", QtGui.QDialogButtonBox.ActionRole).clicked.connect(self.copyClipboard)
-        
-        self.actionBalance = QtGui.QAction("&Balance on Current Line", self)
-        self.actionBalance.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_B)
-        self.actionBalance.triggered.connect(self.balance)
-        self.addAction(self.actionBalance)
-
-        self.geo = WindowGeometry(self, position=False, tabs=[self.tab], splitters=[self.splitme])
-
-        self.readData(row, row_id)
-
-    def load(self):
-        self.mapping.connect_instance(self.main_row)
-        self.splits_model.reset_content_from_list(self.main_row.splits, self.main_row.session())
-
-    def balance(self):
-        index = self.splits_table.currentIndex()
-        r = index.internalPointer()
-        b = decimal.Decimal('0')
-        for row in self.main_row.splits:
-            if row is not r:
-                b += AttrNumeric(2)(row.sum)
-        debitIndex = self.splits_model.index(index.row(), 1, None)
-        creditIndex = self.splits_model.index(index.row(), 2, None)
-        for i in [debitIndex, creditIndex]:
-            w = self.splits_table.indexWidget(i)
-            self.splits_table.closeEditor(w, QtGui.QAbstractItemDelegate.NoHint)
-        #self.splits_table.closePersistentEditor(debitIndex)
-        #self.splits_table.closePersistentEditor(creditIndex)
-        if b > 0:
-            self.splits_model.setData(debitIndex, 0)
-            self.splits_model.setData(creditIndex, b)
-        else:
-            self.splits_model.setData(debitIndex, -b)
-            self.splits_model.setData(creditIndex, 0)
-
-    def setupChecks(self):
-        l = self.splits_table.selectedIndexes()
-        if len(l) == 0:
-            split = None
-        else:
-            split = l[0].internalPointer()
-
-        if split:
-            tags = self.main_row.session().query(Tags).order_by(Tags.name)
-            checkable = [CheckableTagList(split, x) for x in tags]
-            self.tags_model.reset_content_from_list(checkable)
-        else:
-            self.tags_table.setEnabled(False)
-
-    def copyClipboard(self):
-        QtGui.QApplication.clipboard().setText(self.main_row.ascii_repr())
-
-from qtviews import *
-from BalanceDock import DockCommands
-
-def TransactionsCalendar(Session):
-    win = QtGui.QMainWindow()
-
-    win.toolbar = QtGui.QToolBar()
-
-    today = Session.demo_end_date if hasattr(Session, 'demo_end_date') else datetime.date.today()
-    prior_sunday = today-datetime.timedelta((today.weekday()+1)%7)
-    day0 = prior_sunday-datetime.timedelta(28)
-    day1 = prior_sunday+datetime.timedelta(14)
-    c = CalendarView()
-    c.Session = Session
-    c.setDateRange(day0, 6, dayHeight=4)
-
-    def load(obj):
-        s = Session()
-        trans = s.query(Transactions) \
-                .filter(expr.and_(Transactions.date >= day0, Transactions.date <= day1)) \
-                .all()
-        c.setEventList(trans,
-                startDate=lambda x: x.date,
-                endDate=lambda x: x.date,
-                text=lambda x: x.payee if x.payee != "" else x.memo,
-                bkColor=lambda x: QtGui.QColor(0, 0, 224))
-        s.close()
-
-    win.setCentralWidget(c)
-    win.title = 'Transaction Calendar'
-    win.factory = 'TransactionsCalendar'
-    win.tranEntity = TransactionEntity(Session, win)
-    win.entity = DockCommands(Session, win)
-    win.bindings = win.entity.commands
-    win.tranBindings = win.tranEntity.basicNew
-    win.tranBindings.fillToolbar(win.toolbar)
-    win.toolbar.addSeparator()
-    win.bindings.fillToolbar(win.toolbar)
-    win.bindings.refresh.connect(load)
-    win.tranBindings.refresh.connect(load)
-    win.addToolBar(win.toolbar)
-
-    def contextMenu(pos, tran):
-        c.thingMenu = QtGui.QMenu(c)
-        c.thingBinding = c.entity.itemCommands.withObject(tran, objectConverter=lambda x:x.tid)
-        c.thingBinding.fillMenu(c.thingMenu)
-        c.thingBinding.refresh.connect(load)
-        c.thingMenu.popup(c.viewport().mapToGlobal(pos))
-
-    c.contextMenuCalendarEvent.connect(contextMenu)
-
-    c.entity = TransactionEntity(Session, c)
-    c.doubleClickCalendarEvent.connect(lambda tran: c.entity.view(tran.tid))
-
-    load(None)
-
-    return win
-
-from qtalchemy.PBSearchDialog import PBSearchableListDialog
-
-class TransactionsByDate(PBMdiTableView):
-    title = 'Transaction List'
-    factory = 'TransactionsByDate'
-
-    def __init__(self,Session):
-        extensionId = "{0}/MDIRecent".format(self.__class__.__name__)
-
-        PBSearchableListDialog.__init__(self, extensionId=extensionId)
-
-        self.Session = Session
-        self.entity = TransactionEntity(Session, self)
-        self.base_query, converter = self.entity.list_query_converter()
-        self.base_query = self.base_query.order_by(expr.desc(Transactions.date))
-        self.table.setModel(QueryTableModel(self.base_query, ssrc=Session,objectConverter=converter), extensionId=suffixExtId(self,"Table"))
-        self.bindings = self.entity.itemCommands.withView(self.table, bindDefault=True)
-
-        self.table.model().reset_content_from_session()
-
-from sqlalchemy.orm import column_property
-
-class TransactionsPopular(PBMdiTableView):
-    title = 'Popular Transactions'
-    factory = 'TransactionsPopular'
-
-    def __init__(self,Session):
-        extensionId = "{0}/MDIPopular".format(self.__class__.__name__)
-
-        PBSearchableListDialog.__init__(self, extensionId=extensionId)
-
-        self.Session = Session
-        self.entity = TransactionEntity(Session, self)
-        q = Query((Transactions.payee,Transactions.memo,func.count().label("count")))\
-                .filter(Transactions.date>datetime.date.today()-datetime.timedelta(366))\
-                .group_by(Transactions.payee,Transactions.memo).order_by(expr.desc("count")).subquery()
-        q2= Query((q.c.count.label("count"),q.c.payee,q.c.memo)).order_by(expr.desc(q.c.count)).subquery()
-        
-        class Thing(object):
-            __tablename__ = "sam"
-
-            @property
-            def tid(self):
-                s = Session()
-                r = s.query(Transactions.tid)\
-                        .filter(Transactions.memo==self.memo)\
-                        .filter(Transactions.payee==self.payee)\
-                        .order_by(expr.desc(Transactions.date)).limit(1).one()[0]
-                s.close()
-                return r
-
-        mapper(Thing, q2, primary_key=[q2.c.memo, q2.c.payee])
-
-        self.base_query, converter = Query(Thing), lambda x: x.tid
-        #self.base_query = self.base_query.order_by(expr.desc(Transactions.date))
-        self.table.setModel(QueryTableModel(self.base_query, ssrc=Session,objectConverter=converter), extensionId=suffixExtId(self,"Table"))
-        self.bindings = self.entity.itemCommands.withView(self.table, bindDefault=True)
-
-        self.table.model().reset_content_from_session()

File pyhacc/accounts.py

+# -*- coding: utf-8 -*-
+##############################################################################
+#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#                  http://www.gnu.org/licenses/
+##############################################################################
+from qtalchemy import PBTableTab
+from qtalchemy.dialogs import *
+from qtalchemy.widgets import *
+from PySide import QtCore, QtGui
+from PyHaccSchema import *
+from PyHaccLib import *
+
+
+class ReconcileCommands(object):
+    def __init__(self, Session, parent):
+        self.Session = Session
+        self.parent = parent
+
+    commands = CommandMenu("_commands")
+
+    #@commands.itemAction("&Print", iconFile=":/pyhacc/document-print.ico", requireSelection=False, viewRelated=False)
+    #def print_(self, id=None):
+    #    q = self.query(self.Session)
+    #    # TODO:  implement the report!
+
+    @commands.itemAction("&Refresh", iconFile=":/pyhacc/refresh.ico", requireSelection=False, viewRelated=False)
+    def refresh(self, id=None):
+        # do nothing, just be a place-holder for save/load bracketing
+        pass
+
+class AccountEditor(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a = AccountEditor(None,row=s.query(Accounts).filter(Accounts.name=="Cash").one())
+    """
+    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
+        BoundDialog.__init__(self,parent)
+
+        self.setObjectName("AccountsInfo")
+        self.setDataReader(Session, Accounts, "id")
+
+        main = QtGui.QVBoxLayout(self)
+        top_grid = LayoutLayout(main,QtGui.QGridLayout())
+
+        self.mm = self.mapClass(Accounts)
+        self.mm.addBoundFieldGrid(top_grid,"name",0,0)
+        self.mm.addBoundFieldGrid(top_grid,"type",0,2)
+
+        self.tab = LayoutWidget(main,QtGui.QTabWidget())
+
+        self.accounting_tab = QtGui.QWidget()
+        self.mm.addBoundForm(QtGui.QVBoxLayout(self.accounting_tab),["journal_name","retearn_name","description"])
+        self.tab.addTab( self.accounting_tab,"&Accounting" )
+
+        self.institution_tab = QtGui.QWidget()
+        self.mm.addBoundForm(QtGui.QVBoxLayout(self.institution_tab),"instname,instaddr1,instaddr2,instcity".split(','))
+        self.tab.addTab( self.institution_tab,"&Institution" )
+
+        self.rec_tab = QtGui.QWidget()
+        self.mm.addBoundForm(QtGui.QVBoxLayout(self.rec_tab),"rec_note".split(','))
+        self.tab.addTab( self.rec_tab,"&Reconciliation" )
+
+        self.transactions_tab = PBTableTab(self, Session, TransactionEntity, 
+                        [(Splits.account_id, lambda dataContext: dataContext.id)], 
+                        Query((Transactions.tid.label("id"),Transactions.date, Transactions.reference, Transactions.payee, Transactions.memo, Splits.sum)).join(Splits).order_by(Transactions.date.desc()), 
+                        extensionId=suffixExtId(self, "Transactions"))
+        self.tab.addTab(self.transactions_tab,"Tran&sactions")
+
+        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
+        buttonbox.accepted.connect(self.accept)
+        buttonbox.rejected.connect(self.reject)
+
+        self.geo = WindowGeometry(self, position=False, tabs=[self.tab])
+
+        self.readData(row, row_id)
+
+    def load(self):
+        self.mm.connect_instance(self.main_row)
+        self.transactions_tab.refresh(self.main_row)
+        self.setWindowTitle( "Account Information - {0.name}".format(self.main_row) )
+
+class SplitQuery(ModelObject):
+    def __init__(self):
+        self.thisTag = None
+        self.thisAccount = None
+        self.thisType = None
+        self.fiscal_range = "This Year"
+
+    def set(self, account_id, type_id, tag_id):
+        if tag_id is not None:
+            self.thisTag = self.session().query(Tags).filter(Tags.id==tag_id).one()
+        if type_id is not None:
+            self.thisType = self.session().query(AccountTypes).filter(AccountTypes.id==type_id).one()
+        if account_id is not None:
+            self.thisAccount = self.session().query(Accounts).filter(Accounts.id==account_id).one()
+
+    classEvents = ModelObject.Events()
+
+    tag = TagReferral("Tag", "thisTag") 
+    account = AccountReferral("Account", "thisAccount") 
+    type_name = AccountTypeReferral("Account Type","thisType")
+    fiscal_range = FiscalRangeAttr(str, "Range")
+    begin_date = UserAttr(datetime.date, "Beginning Date")
+    end_date = UserAttr(datetime.date, "Ending Date")
+    tagged = UserAttr(bool, "Only show tagged splits")
+
+    fiscalDateRangeEvents(classEvents, "fiscal_range", "begin_date", "end_date")
+
+    def query(self, Session):
+        session = Session()
+        tag = session.query(Tags).filter(Tags.id==self.thisTag.id).one()
+        q = session.query(Splits).join(Transactions)
+        if self.begin_date is not None:
+            q = q.filter(Transactions.date>=self.begin_date)
+        if self.end_date is not None:
+            q = q.filter(Transactions.date<=self.end_date)
+        if self.thisType is not None:
+            q = q.join(Accounts).filter(Accounts.type_id==self.thisType.id)
+        if self.thisAccount is not None:
+            q = q.filter(Splits.account_id==self.thisAccount.id)
+        if self.tagged:
+            q = q.join(Tagsplits).filter(Tagsplits.tag_id==self.thisTag.id)
+        q = q.order_by(Transactions.date, Transactions.payee)
+        return session, tag, q
+
+class TagWorksheet(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a = TagWorksheet(None, account_id=s.query(Accounts).filter(Accounts.name=="Cash").one().id, Session=Session)
+    """
+    def __init__(self, parent, account_id=None, type_id=None, tag_id=None, Session=None):
+        BoundDialog.__init__(self,parent)
+
+        self.setWindowTitle("Split Tagger Worksheet")
+        self.setObjectName("TagWorksheet")
+        self.setDataReader(Session, SplitQuery, None)
+
+        main = QtGui.QHBoxLayout(self)
+        left_controls = LayoutLayout(main,QtGui.QVBoxLayout())
+
+        self.mm = self.mapClass(SplitQuery)
+        self.mm.addBoundForm(left_controls,["tag", "fiscal_range", "begin_date", "end_date", "account", "type_name", "tagged"])
+
+        right_controls = LayoutLayout(main, QtGui.QVBoxLayout())
+        self.toolbar = LayoutWidget(right_controls, QtGui.QToolBar())
+        self.checkTable = LayoutWidget(right_controls, TableView(extensionId=suffixExtId(self, "Table")))
+        self.model = ClassTableModel(SplitTaggerSimple,"tagged,debit,credit,date,reference,payee,memo".split(','), readonly=False, fixed_rows=True)
+        self.checkTable.setModel(self.model)
+        main.setStretch(1,15) # massively favor the table side of things
+
+        self.buttonBox = LayoutWidget(left_controls,QtGui.QDialogButtonBox())
+
+        self.okBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Ok)
+        self.buttonBox.accepted.connect(self.accept)
+        self.cancelBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
+        self.buttonBox.rejected.connect(self.reject)
+        self.refreshBtn = self.buttonBox.addButton("Refresh", QtGui.QDialogButtonBox.ActionRole)
+        self.refreshBtn.clicked.connect(lambda checked=False: self.load())
+
+        self.geo = WindowGeometry(self)
+
+        self.entity = TransactionEntity(Session, self)
+        self.binding = self.entity.itemCommands.withView(self.checkTable, objectConverter=lambda x:x.split.Transaction.tid)
+        self.binding.fillToolbar(self.toolbar)
+        self.binding.preCommand.connect(self.preCommandSave)
+        self.binding.refresh.connect(self.refresh)
+
+        self.toolbar.addSeparator()
+
+        self.entity2 = ReconcileCommands(self.Session, self.parent())
+        self.bindings2 = self.entity2.commands.withView(self.checkTable, bindDefault=False)
+        self.bindings2.fillToolbar(self.toolbar)
+        self.bindings2.preCommand.connect(self.preCommandSave)
+        self.bindings2.refresh.connect(self.refresh)
+
+        self.queryStuff = SplitQuery()
+        self.queryStuffSession = Session()
+        self.queryStuffSession.npadd(self.queryStuff)
+        self.queryStuff.set(account_id=account_id, type_id=type_id, tag_id=tag_id)
+
+        self.entity2.queryStuff = self.queryStuff
+
+        self.main_row = self.queryStuff
+        self.load()
+        #self.readData(self.queryStuff, None)
+
+    def load(self):
+        self.submit()
+        self.checkTable.setEnabled(self.queryStuff.thisTag is not None)
+
+        if self.queryStuff.thisTag is not None:
+            progress = QtGui.QProgressDialog("Loading...", "", 0, 2, self)
+            progress.setCancelButton(None)
+            progress.setMinimumDuration(1)
+            progress.setWindowModality(QtCore.Qt.WindowModal)
+
+            #TODO: it seems I don't know how to make the QProgressDialog show at a convenient point
+            progress.setValue(0)
+
+            self.session, self.tag, q = self.queryStuff.query(self.Session)
+            self.split_list = q.all()
+            progress.setValue(1)
+
+            self.shown_check_split_list = []
+            for i in range(len(self.split_list)):
+                progress.setValue(i)
+                split = self.split_list[i]
+                self.shown_check_split_list.append(SplitTaggerSimple(split, self.tag))
+
+            self.model.reset_content_from_list(self.shown_check_split_list)
+            for x in self.shown_check_split_list:
+                instanceEvent(x, "set", "tagged")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
+
+            progress.setValue(2)
+
+            progress.hide()
+
+        self.mm.connect_instance(self.queryStuff)
+
+        if self.main_row.thisAccount is not None:
+            self.setWindowTitle( "Account Tag Worksheet - {0}".format(self.main_row.thisAccount.name) )
+
+class AccountReconcile(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a = AccountReconcile(None,row=s.query(Accounts).filter(Accounts.name=="Cash").one())
+    """
+    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
+        BoundDialog.__init__(self,parent)
+
+        self.setObjectName("AccountReconciliation")
+        self.setDataReader(Session, Accounts, "id")
+
+        main = QtGui.QHBoxLayout(self)
+        left_controls = LayoutLayout(main,QtGui.QVBoxLayout())
+
+        self.mm = self.mapClass(SplitTagger.ReconciliationStatus)
+        self.mm.addBoundForm(left_controls,["reconciled_balance","pending_balance","outstanding_balance"])
+        self.mm_account = self.mapClass(Accounts)
+        self.mm_account.addBoundForm(left_controls,["rec_note"])
+
+        right_controls = LayoutLayout(main, QtGui.QVBoxLayout())
+        self.toolbar = LayoutWidget(right_controls, QtGui.QToolBar())
+        self.checkTable = LayoutWidget(right_controls, TableView(extensionId=suffixExtId(self, "Table")))
+        self.model = ClassTableModel(SplitTagger,"pending,reconciled,debit,credit,date,reference,payee,memo".split(','), readonly=False, fixed_rows=True)
+        self.checkTable.setModel(self.model)
+        main.setStretch(1,15) # massively favor the table side of things
+
+        self.buttonBox = LayoutWidget(left_controls,QtGui.QDialogButtonBox())
+
+        self.okBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Ok)
+        self.buttonBox.accepted.connect(self.accept)
+        self.reconcileBtn = self.buttonBox.addButton("&Reconcile",QtGui.QDialogButtonBox.ActionRole)
+        self.reconcileBtn.clicked.connect(self.reconcile_now)
+        self.cancelBtn = self.buttonBox.addButton(QtGui.QDialogButtonBox.Cancel)
+        self.buttonBox.rejected.connect(self.reject)
+
+        self.geo = WindowGeometry(self)
+
+        self.entity = TransactionEntity(Session, self)
+        self.binding = self.entity.itemCommands.withView(self.checkTable, objectConverter=lambda x:x.split.Transaction.tid)
+        self.binding.fillToolbar(self.toolbar)
+        self.binding.preCommand.connect(self.preCommandSave)
+        self.binding.refresh.connect(self.refresh)
+
+        self.toolbar.addSeparator()
+
+        self.entity2 = ReconcileCommands(self.Session, self.parent())
+        self.bindings2 = self.entity2.commands.withView(self.checkTable, bindDefault=False)
+        self.bindings2.fillToolbar(self.toolbar)
+        self.bindings2.preCommand.connect(self.preCommandSave)
+        self.bindings2.refresh.connect(self.refresh)
+
+        self.readData(row, row_id)
+
+    def load(self):
+        self.recStatus = SplitTagger.ReconciliationStatus()
+
+        self.tagRec = self.session.query(Tags).filter(Tags.name==Tags.Names.BankReconciled).one()
+        self.tagPen = self.session.query(Tags).filter(Tags.name==Tags.Names.BankPending).one()
+        q = self.session.query(Tagsplits, Tags.name.label('tag_name_s')).join(Tags).filter(Tags.name==Tags.Names.BankReconciled).subquery()
+        self.outstanding_split_list = self.session.query(Splits) \
+                            .outerjoin(q) \
+                            .join(Accounts) \
+                            .join(Transactions) \
+                            .filter(Accounts.id==self.main_row.id) \
+                            .filter(q.c.tag_name_s==None) \
+                            .order_by(Transactions.date) \
+                            .all()
+
+        progress = QtGui.QProgressDialog("Loading...", "", 0, len(self.outstanding_split_list), self)
+        progress.setCancelButton(None)
+        progress.setMinimumDuration(1)
+        progress.setWindowModality(QtCore.Qt.WindowModal)
+
+        self.shown_check_split_list = []
+        for i in range(len(self.outstanding_split_list)):
+            progress.setValue(i)
+            split = self.outstanding_split_list[i]
+            self.shown_check_split_list.append(SplitTagger(split,self.tagRec,self.tagPen,self.recStatus))
+
+        self.reconciled_split_list = self.session.query(Splits) \
+                            .outerjoin(q) \
+                            .join(Accounts) \
+                            .filter(Accounts.id==self.main_row.id) \
+                            .filter(q.c.tag_name_s==Tags.Names.BankReconciled) \
+                            .all()
+        self.recStatus.reconciled_balance = sum([split.sum for split in self.reconciled_split_list],decimal.Decimal())
+        self.recStatus.pending_balance = self.recStatus.reconciled_balance + sum([split.amount for split in self.shown_check_split_list if split.pending],decimal.Decimal())
+        self.recStatus.outstanding_balance = self.recStatus.reconciled_balance + sum([split.amount for split in self.shown_check_split_list],decimal.Decimal())
+
+        self.model.reset_content_from_list(self.shown_check_split_list)
+        for x in self.shown_check_split_list:
+            instanceEvent(x, "set", "pending")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
+            instanceEvent(x, "set", "reconciled")(lambda obj, attr, value: self.model.rowEmitChange(obj, "all"))
+
+        progress.setValue(len(self.outstanding_split_list))
+
+        self.mm.connect_instance(self.recStatus)
+        self.mm_account.connect_instance(self.main_row)
+
+        self.setWindowTitle( "Account Reconciliation - {0}".format(self.main_row.name) )
+
+    def reconcile_now(self):
+        for s in self.shown_check_split_list:
+            if s.pending:
+                s.reconciled = True
+                s.pending = False

File pyhacc/accounttypes.py

+# -*- coding: utf-8 -*-
+##############################################################################
+#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#                  http://www.gnu.org/licenses/
+##############################################################################
+from qtalchemy import *
+from PySide import QtCore, QtGui
+from qtalchemy.dialogs import *
+from PyHaccSchema import *
+
+class AccountTypeEditor(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a = AccountTypeEditor(None,row=s.query(AccountTypes).filter(AccountTypes.name=="Asset").one())
+    """
+    def __init__(self, parent, row=None, Session=None, row_id=None, flush=True):
+        BoundDialog.__init__(self,parent)
+
+        self.setObjectName("AccountTypesInfo")
+        self.setDataReader(Session, AccountTypes, "id")
+
+        main = QtGui.QVBoxLayout()
+        self.setLayout(main)
+        grid = LayoutLayout(main,QtGui.QFormLayout())
+        self.mm = self.mapClass(AccountTypes)
+        self.mm.addBoundField(grid,"name")
+        
+        tabs = LayoutWidget(main,QtGui.QTabWidget())
+
+        tab1 = QtGui.QWidget()
+        page_layout = QtGui.QVBoxLayout(tab1)
+        self.mm.addBoundForm(page_layout,"sort balance_sheet debit retained_earnings description".split(' '))
+        tabs.addTab(tab1,"Settings")
+
+        self.account_tab = PBTableTab(self, Session, AccountEntity, 
+                        [(AccountTypes.id, lambda dataContext: dataContext.id)], 
+                        Query((Accounts.id,Accounts.name.label("Account Name"),Journals.name.label("Journal"))).outerjoin(Journals).join(AccountTypes), 
+                        extensionId=suffixExtId(self, "Accounts"))
+        tabs.addTab(self.account_tab,"Accounts")
+
+        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
+        buttonbox.accepted.connect(self.accept)
+        buttonbox.rejected.connect(self.reject)
+
+        self.geo = WindowGeometry(self, position=False, tabs=[tabs])
+
+        self.readData(row, row_id)
+        
+    def load(self):
+        self.mm.connect_instance(self.main_row)
+        self.account_tab.refresh(self.main_row)
+        self.setWindowTitle("Account Type {0.name}".format(self.main_row))

File pyhacc/journals.py

+# -*- coding: utf-8 -*-
+##############################################################################
+#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#                  http://www.gnu.org/licenses/
+##############################################################################
+from qtalchemy import *
+from PySide import QtCore, QtGui
+from qtalchemy.dialogs import *
+from PyHaccSchema import *
+
+class JournalEditor(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a=JournalEditor(None,row=s.query(Journals).filter(Journals.name=="General").one())
+    """
+    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
+        BoundDialog.__init__(self,parent)
+        self.setObjectName("JournalsInfo")
+        self.setDataReader(Session, Journals, "id")
+
+        self.mm = self.mapClass(AccountTypes)
+
+        main = QtGui.QVBoxLayout(self)
+        grid = LayoutLayout(main,QtGui.QFormLayout())
+        self.mm.addBoundField(grid,"name")
+
+        tabs = LayoutWidget(main,QtGui.QTabWidget())
+
+        tab1 = QtGui.QWidget()
+        page_layout = QtGui.QVBoxLayout(tab1)
+        self.mm.addBoundForm(page_layout,["description"])
+        tabs.addTab(tab1,"Settings")
+
+        self.accounts_tab = PBTableTab(self, Session, AccountEntity, 
+                        [(Journals.id, lambda dataContext: dataContext.id)], 
+                        Query((Accounts.id,Accounts.name.label("Account Name"),AccountTypes.name.label("Type"))).outerjoin(Journals).join(AccountTypes), 
+                        extensionId=suffixExtId(self, "Accounts"))
+        tabs.addTab(self.accounts_tab,"Accounts")
+
+        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
+        buttonbox.accepted.connect(self.accept)
+        buttonbox.rejected.connect(self.reject)
+
+        self.geo = WindowGeometry(self, position=False, tabs=[tabs])
+
+        self.readData(row, row_id)
+        
+    def load(self):
+        self.mm.connect_instance(self.main_row)
+        self.accounts_tab.refresh(self.main_row)
+        self.setWindowTitle("Journals - {0.name}".format(self.main_row))

File pyhacc/tags.py

+# -*- coding: utf-8 -*-
+##############################################################################
+#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#                  http://www.gnu.org/licenses/
+##############################################################################
+from qtalchemy import *
+from PySide import QtCore, QtGui
+from qtalchemy.dialogs import *
+
+from PyHaccSchema import *
+
+class TagEditor(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a=TagEditor(None,row=s.query(Tags).filter(Tags.name==Tags.Names.BankReconciled).one())
+    """
+    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
+        BoundDialog.__init__(self,parent)
+        self.setWindowTitle("Tags")
+        self.setDataReader(Session, Tags, "id")
+
+        main = QtGui.QVBoxLayout(self)
+        grid = LayoutLayout(main,QtGui.QFormLayout())
+        self.mm = self.mapClass(Tags)
+        self.mm.addBoundField(grid,"name")
+        self.mm.addBoundField(grid,"description")
+
+        buttonbox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
+        buttonbox.accepted.connect(self.accept)
+        buttonbox.rejected.connect(self.reject)
+
+        self.readData(row, row_id)
+
+    def load(self):
+        self.mm.connect_instance(self.main_row)

File pyhacc/transactions.py

+# -*- coding: utf-8 -*-
+##############################################################################
+#       Copyright (C) 2010, Joel B. Mohler <joel@kiwistrawberry.us>
+#
+#  Distributed under the terms of the GNU General Public License (GPL)
+#                  http://www.gnu.org/licenses/
+##############################################################################
+from qtalchemy import *
+from PySide import QtCore, QtGui
+from qtalchemy.dialogs import *
+from qtalchemy.widgets import *
+
+import sqlalchemy.sql.expression as expr
+from PyHaccSchema import *
+from PyHaccUI import *
+
+class TransactionEditor(BoundDialog):
+    """
+    >>> app, Session = qtappsession()
+    >>> s = Session()
+    >>> a = TransactionEditor(None,row=s.query(Transactions).filter(Transactions.memo=="Groceries")[0])
+    """
+    def __init__(self,parent,row=None,Session=None,row_id=None,flush=True):
+        BoundDialog.__init__(self,parent)
+
+        self.setWindowTitle("Transaction")
+        self.setObjectName("Transaction Editor")
+
+        self.setDataReader(Session, Transactions, "tid")
+
+        main = QtGui.QVBoxLayout(self)
+        top_grid = LayoutLayout(main,QtGui.QGridLayout())
+
+        self.mapping = self.mapClass(Transactions)
+        self.mapping.addBoundFieldGrid(top_grid,"date",0,0)
+        self.mapping.addBoundFieldGrid(top_grid,"reference",0,2)
+        self.mapping.addBoundFieldGrid(top_grid,"payee",1,0,columnSpan=3)
+        self.mapping.addBoundFieldGrid(top_grid,"memo",2,0,columnSpan=3)
+
+        self.tab = LayoutWidget(main, QtGui.QTabWidget())
+
+        # first tab: transactions
+        self.splitme = QtGui.QSplitter()
+        self.tags_table = TableView()
+        self.splits_table = TableView()
+        self.splitme.addWidget(self.splits_table)
+        self.splitme.addWidget(self.tags_table)
+        self.tab.addTab(self.splitme, "&Transactions")
+
+        # second tab:  receipt memo
+        self.receipt = QtGui.QTextEdit(self)
+        self.tab.addTab(self.receipt, "&Receipt")
+        self.mapping.bind(self.receipt, "receipt")
+
+        self.splits_model = ClassTableModel(Splits, ("account", "debit", "credit"), readonly=False)
+        self.splits_table.setModel(self.splits_model, extensionId=suffixExtId(self, "Splits"))
+        self.splits_table.setItemDelegate(AlchemyModelDelegate())
+
+        self.tags_model = ClassTableModel(CheckableTagList, ("checked", "tag_name"), readonly=False, fixed_rows=True)
+        self.tags_table.setModel(self.tags_model, extensionId=suffixExtId(self, "Tags"))
+        self.tags_table.setItemDelegate(AlchemyModelDelegate())
+
+        self.split_model_sel = self.splits_table.selectionModel()
+        self.split_model_sel.selectionChanged.connect(self.setupChecks)
+
+        self.buttonBox = LayoutWidget(main,QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
+        self.buttonBox.accepted.connect(self.accept)
+        self.buttonBox.rejected.connect(self.reject)
+        self.buttonBox.addButton(QtGui.QDialogButtonBox.Apply).clicked.connect(self.commitAndRefresh)
+        self.buttonBox.addButton("To Cli&pboard", QtGui.QDialogButtonBox.ActionRole).clicked.connect(self.copyClipboard)
+        
+        self.actionBalance = QtGui.QAction("&Balance on Current Line", self)
+        self.actionBalance.setShortcut(QtCore.Qt.CTRL + QtCore.Qt.Key_B)
+        self.actionBalance.triggered.connect(self.balance)
+        self.addAction(self.actionBalance)
+
+        self.geo = WindowGeometry(self, position=False, tabs=[self.tab], splitters=[self.splitme])
+
+        self.readData(row, row_id)
+
+    def load(self):
+        self.mapping.connect_instance(self.main_row)
+        self.splits_model.reset_content_from_list(self.main_row.splits, self.main_row.session())
+
+    def balance(self):
+        index = self.splits_table.currentIndex()
+        r = index.internalPointer()
+        b = decimal.Decimal('0')
+        for row in self.main_row.splits:
+            if row is not r:
+                b += AttrNumeric(2)(row.sum)
+        debitIndex = self.splits_model.index(index.row(), 1, None)
+        creditIndex = self.splits_model.index(index.row(), 2, None)
+        for i in [debitIndex, creditIndex]:
+            w = self.splits_table.indexWidget(i)
+            self.splits_table.closeEditor(w, QtGui.QAbstractItemDelegate.NoHint)
+        #self.splits_table.closePersistentEditor(debitIndex)
+        #self.splits_table.closePersistentEditor(creditIndex)
+        if b > 0:
+            self.splits_model.setData(debitIndex, 0)
+            self.splits_model.setData(creditIndex, b)
+        else:
+            self.splits_model.setData(debitIndex, -b)
+            self.splits_model.setData(creditIndex, 0)
+
+    def setupChecks(self):
+        l = self.splits_table.selectedIndexes()
+        if len(l) == 0:
+            split = None
+        else:
+            split = l[0].internalPointer()
+
+        if split:
+            tags = self.main_row.session().query(Tags).order_by(Tags.name)
+            checkable = [CheckableTagList(split, x) for x in tags]
+            self.tags_model.reset_content_from_list(checkable)
+        else:
+            self.tags_table.setEnabled(False)
+
+    def copyClipboard(self):
+        QtGui.QApplication.clipboard().setText(self.main_row.ascii_repr())
+
+from qtviews import *
+from BalanceDock import DockCommands
+
+def TransactionsCalendar(Session):
+    win = QtGui.QMainWindow()
+
+    win.toolbar = QtGui.QToolBar()
+
+    today = Session.demo_end_date if hasattr(Session, 'demo_end_date') else datetime.date.today()
+    prior_sunday = today-datetime.timedelta((today.weekday()+1)%7)
+    day0 = prior_sunday-datetime.timedelta(28)
+    day1 = prior_sunday+datetime.timedelta(14)
+    c = CalendarView()
+    c.Session = Session
+    c.setDateRange(day0, 6, dayHeight=4)
+
+    def load(obj):
+        s = Session()
+        trans = s.query(Transactions) \
+                .filter(expr.and_(Transactions.date >= day0, Transactions.date <= day1)) \
+                .all()
+        c.setEventList(trans,
+                startDate=lambda x: x.date,
+                endDate=lambda x: x.date,
+                text=lambda x: x.payee if x.payee != "" else x.memo,
+                bkColor=lambda x: QtGui.QColor(0, 0, 224))
+        s.close()
+
+    win.setCentralWidget(c)
+    win.title = 'Transaction Calendar'
+    win.factory = 'TransactionsCalendar'
+    win.tranEntity = TransactionEntity(Session, win)
+    win.entity = DockCommands(Session, win)
+    win.bindings = win.entity.commands
+    win.tranBindings = win.tranEntity.basicNew
+    win.tranBindings.fillToolbar(win.toolbar)
+    win.toolbar.addSeparator()
+    win.bindings.fillToolbar(win.toolbar)
+    win.bindings.refresh.connect(load)
+    win.tranBindings.refresh.connect(load)
+    win.addToolBar(win.toolbar)
+
+    def contextMenu(pos, tran):
+        c.thingMenu = QtGui.QMenu(c)
+        c.thingBinding = c.entity.itemCommands.withObject(tran, objectConverter=lambda x:x.tid)
+        c.thingBinding.fillMenu(c.thingMenu)
+        c.thingBinding.refresh.connect(load)
+        c.thingMenu.popup(c.viewport().mapToGlobal(pos))
+
+    c.contextMenuCalendarEvent.connect(contextMenu)
+
+    c.entity = TransactionEntity(Session, c)
+    c.doubleClickCalendarEvent.connect(lambda tran: c.entity.view(tran.tid))
+
+    load(None)
+
+    return win
+
+from qtalchemy.PBSearchDialog import PBSearchableListDialog
+
+class TransactionsByDate(PBMdiTableView):
+    title = 'Transaction List'
+    factory = 'TransactionsByDate'
+
+    def __init__(self,Session):
+        extensionId = "{0}/MDIRecent".format(self.__class__.__name__)
+
+        PBSearchableListDialog.__init__(self, extensionId=extensionId)
+
+        self.Session = Session
+        self.entity = TransactionEntity(Session, self)
+        self.base_query, converter = self.entity.list_query_converter()
+        self.base_query = self.base_query.order_by(expr.desc(Transactions.date))
+        self.table.setModel(QueryTableModel(self.base_query, ssrc=Session,objectConverter=converter), extensionId=suffixExtId(self,"Table"))
+        self.bindings = self.entity.itemCommands.withView(self.table, bindDefault=True)
+
+        self.table.model().reset_content_from_session()
+
+from sqlalchemy.orm import column_property
+
+class TransactionsPopular(PBMdiTableView):
+    title = 'Popular Transactions'
+    factory = 'TransactionsPopular'
+
+    def __init__(self,Session):
+        extensionId = "{0}/MDIPopular".format(self.__class__.__name__)
+
+        PBSearchableListDialog.__init__(self, extensionId=extensionId)
+
+        self.Session = Session
+        self.entity = TransactionEntity(Session, self)
+        q = Query((Transactions.payee,Transactions.memo,func.count().label("count")))\
+                .filter(Transactions.date>datetime.date.today()-datetime.timedelta(366))\
+                .group_by(Transactions.payee,Transactions.memo).order_by(expr.desc("count")).subquery()
+        q2= Query((q.c.count.label("count"),q.c.payee,q.c.memo)).order_by(expr.desc(q.c.count)).subquery()
+        
+        class Thing(object):
+            __tablename__ = "sam"
+
+            @property
+            def tid(self):
+                s = Session()
+                r = s.query(Transactions.tid)\
+                        .filter(Transactions.memo==self.memo)\
+                        .filter(Transactions.payee==self.payee)\
+                        .order_by(expr.desc(Transactions.date)).limit(1).one()[0]
+                s.close()
+                return r
+
+        mapper(Thing, q2, primary_key=[q2.c.memo, q2.c.payee])
+
+        self.base_query, converter = Query(Thing), lambda x: x.tid
+        #self.base_query = self.base_query.order_by(expr.desc(Transactions.date))
+        self.table.setModel(QueryTableModel(self.base_query, ssrc=Session,objectConverter=converter), extensionId=suffixExtId(self,"Table"))
+        self.bindings = self.entity.itemCommands.withView(self.table, bindDefault=True)
+
+        self.table.model().reset_content_from_session()