├── .gitattributes ├── .project ├── .pydevproject ├── .settings └── org.eclipse.core.resources.prefs ├── .vs └── Accounting-System │ └── v15 │ └── .suo ├── AccountDB.py ├── Accounting-System-alpha-release.zip ├── Accounting-System.pyproj ├── Accounting-System.sln ├── AccountingSystemOrganization.png ├── Blank General Journal.png ├── Blank General Ledger.png ├── Example python code printing with win32 .odt ├── FormDialogs.py ├── LICENSE ├── OpenAccounting.db ├── OpenAccounting.py ├── OpenAccounting.sql ├── Printfile.ps ├── README.md ├── ReportPreps.py ├── Tooltips.py ├── __pycache__ ├── AccountDB.cpython-37.pyc ├── FormDialogs.cpython-37.pyc ├── ReportPreps.cpython-37.pyc ├── Tooltips.cpython-37.pyc └── splash.cpython-37.pyc ├── _config.yml ├── cruncherCr3.gif ├── cruncherCr3.jpg ├── cruncherCr3.png ├── images ├── AccountingSystemOrganization.dia ├── AccountingSystemOrganization.png ├── ChartAccts.png ├── Journal1.png ├── Journal1.xcf ├── Journal2.png ├── Journal2.xcf ├── Journal3.png ├── Journal3.xcf ├── Journal4.png ├── Journal4.xcf ├── Journal5.png ├── Journal5.xcf ├── Journal6.png ├── Journal6.xcf ├── Journal7.png ├── Journal7.xcf ├── Ldgeracct1.png ├── Ldgeracct1.xcf ├── Number_cruncherCr3.ico ├── Number_cruncherCr3.png ├── README.md ├── RevnExp1.png ├── ScreenChartTab.png ├── ScreenChartTab2.png ├── ScreenJournalTab.png ├── ScreenLedgerTab.png ├── ScreenMaintenanceTab.png ├── ScreenMemoTab.png ├── ScreenReportTab1.png ├── ScreenReportTab2.png ├── balancesheet1.png ├── cruncherCr3.gif ├── medmatix.ico ├── medmatix.png ├── medmatix_tilt.ico ├── medmatix_tilt.png └── trialbalance1.png ├── medmatix_tilt.png ├── splash.py └── testcode.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Accounting-System 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /${PROJECT_DIR_NAME} 5 | 6 | python interpreter 7 | Default 8 | 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/Tooltips.py=utf-8 3 | -------------------------------------------------------------------------------- /.vs/Accounting-System/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/.vs/Accounting-System/v15/.suo -------------------------------------------------------------------------------- /AccountDB.py: -------------------------------------------------------------------------------- 1 | ''' 2 | AccountDB module 3 | Created on Oct 29, 2018 4 | @summary: This module provides all the database access methods for the accounting system 5 | @author: david york 6 | ''' 7 | import sqlite3 8 | import tkinter as tk 9 | from tkinter import messagebox as mBox 10 | 11 | 12 | class AccountDB(object): 13 | ''' 14 | Class with Account Database manipulation methods for SQLite3 Database 15 | ''' 16 | 17 | 18 | def __init__(self, params): 19 | ''' 20 | Constructor 21 | ''' 22 | 23 | # ################################# 24 | # Database Functions 25 | # ################################# 26 | 27 | 28 | def createAccounts(self): 29 | ''' 30 | Create New Database and Tables if not existing 31 | @summary: Creates a fully new accounting set-up. The System accounts are 32 | fixed by accounting convention and may not be changed of reused for 33 | other purposes. 34 | @warning: Will not allow overwriting so any old data tables should be moved from the directory. 35 | ''' 36 | # Check that a fully new set-up is intended 37 | proceedAnswer = mBox.askyesno("Initial Set-up","This is The New System Set-up.\nThere should be no old data tables in directory,\n Proceed?") 38 | if (not proceedAnswer): 39 | print('aborted new setup') 40 | return 41 | # Some local, set-up related message boxes 42 | def setupError(self): 43 | mBox.showerror(title='Set-up Error', message='Chart of Accounts already populated, you may not overwrite') 44 | def setupInfo(self): 45 | mBox.showinfo('Set-up Information' , 'Adding System Accounts to Chart of Accounts.') 46 | 47 | #If Not Existing, Create batabase, data tables and system ledger accounts 48 | db = sqlite3.connect('OpenAccounting.db') 49 | db.execute('''CREATE TABLE IF NOT EXISTS journal 50 | (Transact INTEGER PRIMARY KEY NOT NULL, 51 | Date DATETIME NOT NULL, 52 | Description STRING(50) NOT NULL, 53 | DebitAccount INT(4) NOT NULL, 54 | CreditAccount INT(4) NOT NULL, 55 | Amount DECIMAL NOT NULL)''') 56 | 57 | db.execute('''CREATE TABLE IF NOT EXISTS ledger 58 | (Account INTEGER NOT NULL, 59 | Transact INTEGER KEY NOT NULL, 60 | Amount REAL NOT NULL, 61 | Balance REAL NOT NULL)''') 62 | 63 | db.execute('''CREATE TABLE IF NOT EXISTS chart 64 | (Account INTEGER PRIMARY KEY NOT NULL, 65 | Name STRING(60) NOT NULL, 66 | Type STRING(10) NOT NULL, 67 | Balance REAL NOT NULL)''') 68 | 69 | db.execute('''CREATE TABLE IF NOT EXISTS accountmemos 70 | (MemoID INTEGER PRIMARY KEY NOT NULL, 71 | Transact INTEGER NOT NULL, 72 | Date DATETIME NOT NULL, 73 | Memo BLOB NOT NULL)''') 74 | 75 | # Check if Chart of Accounts already set-up, may not be overwritten 76 | count = db.execute("SELECT count(Account) FROM chart") 77 | for row in count: 78 | tableFull=bool(row[0]) 79 | if (not tableFull): 80 | setupInfo(self) 81 | db.execute('''INSERT INTO chart VALUES (100, "ASSETS", "DEBIT", 0), 82 | (120, "RECEIVABLES","DEBIT",0), 83 | (200, "LIABILITIES","CREDIT",0), 84 | (220, "PAYABLES","CREDIT",0), 85 | (300, "EQUITY","CREDIT",0), 86 | (399, "RETAINED EARNINGS", "CREDIT",0), 87 | (400, "REVENUE","CREDIT",0), 88 | (500, "EXPENSES","DEBIT",0) 89 | ''') 90 | db.commit() 91 | else: 92 | setupError(self) 93 | 94 | db.close() 95 | 96 | def getChartAccounts(self): 97 | ''' 98 | Return all Chart of Accounts 99 | ''' 100 | chart = list() 101 | db = sqlite3.connect('OpenAccounting.db') 102 | cursor = db.cursor() 103 | cursor.execute("SELECT * FROM chart ORDER BY Account") 104 | for row in cursor: 105 | chart.append(row) 106 | db.close() 107 | return chart 108 | 109 | def getBalSheetAccounts(self, accRange): 110 | ''' 111 | Return a Block of Accounts 112 | ''' 113 | accBlock = list() 114 | db = sqlite3.connect('OpenAccounting.db') 115 | cursor = db.cursor() 116 | cursor.execute("SELECT * FROM chart WHERE Account >= {} AND Account <= {} ORDER BY Account".format(accRange[0], accRange[1])) 117 | for row in cursor: 118 | accBlock.append(row) 119 | db.close() 120 | return accBlock 121 | 122 | def existChartAccount(self, account): 123 | ''' 124 | ''' 125 | pass 126 | 127 | def getLedgerAccount(self,account): 128 | ''' 129 | Return all entries for an account 130 | ''' 131 | ledger = list() 132 | db = sqlite3.connect('OpenAccounting.db') 133 | cursor = db.cursor() 134 | if int(account) == 0: 135 | cursor.execute("SELECT * FROM ledger ORDER BY Account") 136 | else: 137 | cursor.execute("SELECT * FROM ledger WHERE Account = {} ORDER BY Transact".format(account,)) 138 | for row in cursor: 139 | ledger.append(row) 140 | db.close() 141 | return ledger 142 | 143 | def getJournalEntries(self, jDates): 144 | ''' 145 | Return all of a journal date range 146 | ''' 147 | journal = list() 148 | db = sqlite3.connect('OpenAccounting.db') 149 | cursor = db.cursor() 150 | if jDates == (0,0): 151 | cursor.execute("SELECT * FROM journal ORDER BY Date") 152 | else: 153 | cursor.execute("SELECT * FROM journal WHERE Date = {} ORDER BY Transact".format(jDates[0])) 154 | for row in cursor: 155 | journal.append(row) 156 | db.close() 157 | return journal 158 | 159 | def getJournalTransact(self, jTransact): 160 | ''' 161 | Return a journal transaction 162 | ''' 163 | transact = list() 164 | db = sqlite3.connect('OpenAccounting.db') 165 | cursor = db.cursor() 166 | if int(jTransact) == 0: 167 | cursor.execute("SELECT * FROM journal ORDER BY Date") 168 | else: 169 | cursor.execute("SELECT * FROM journal WHERE Transact = {} ORDER BY Date".format(jTransact)) 170 | for row in cursor: 171 | transact.append(row) 172 | db.close() 173 | return transact 174 | 175 | def getTransactMemo(self, jTransact): 176 | ''' 177 | Return a transaction accounting memos 178 | ''' 179 | transact = list() 180 | db = sqlite3.connect('OpenAccounting.db') 181 | cursor = db.cursor() 182 | if int(jTransact) == 0: 183 | cursor.execute("SELECT * FROM accountMemos ORDER BY Date") 184 | else: 185 | cursor.execute("SELECT * FROM accountMemos WHERE Transact = {} ORDER BY MemoDate".format(jTransact)) 186 | for row in cursor: 187 | transact.append(row) 188 | db.close() 189 | return transact 190 | 191 | def insertJournalEntry(self, jrow): 192 | ''' 193 | Insert an entry into Journal 194 | ''' 195 | db = sqlite3.connect('OpenAccounting.db') 196 | db.execute("INSERT INTO journal VALUES {}".format(jrow)) 197 | db.commit() 198 | db.close() 199 | 200 | def insertLedgerEntry(self, lrow): 201 | ''' 202 | Post journal debit or credit to Ledger 203 | ''' 204 | db = sqlite3.connect('OpenAccounting.db') 205 | db.execute("INSERT INTO ledger VALUES {}".format(lrow)) 206 | db.commit() 207 | db.close() 208 | 209 | def insertChartAccount(self, row): 210 | ''' 211 | Update an Balance into Chart Account 212 | ''' 213 | db = sqlite3.connect('OpenAccounting.db') 214 | db.execute("INSERT INTO chart VALUES {}".format(row)) 215 | db.commit() 216 | db.close() 217 | 218 | def insertAccountMemos(self, row): 219 | ''' 220 | Update an Balance into Chart Account 221 | ''' 222 | db = sqlite3.connect('OpenAccounting.db') 223 | db.execute("INSERT INTO accountMemos VALUES {}".format(row)) 224 | db.commit() 225 | db.close() 226 | 227 | def updateChartBalance(self, account, balance): # Account balance in chart of accounts 228 | db = sqlite3.connect('OpenAccounting.db') 229 | cursor = db.cursor() 230 | cursor.execute("UPDATE Chart SET Balance = {} WHERE Account = {}".format(balance, account)) 231 | db.commit() 232 | db.close() 233 | 234 | def getAccountNumbers(self, accrange, acctype): 235 | ''' 236 | Get a list of account numbers from chart 237 | ''' 238 | acctNos = list() 239 | db = sqlite3.connect('OpenAccounting.db') 240 | if accrange == (0,0): 241 | if acctype == 'DEBIT': 242 | cursor = db.cursor() 243 | cursor.execute("SELECT Account FROM chart WHERE ActyType ='DEBIT' ORDER BY Account".format(accrange)) 244 | else: # AccType = CREDIT 245 | cursor = db.cursor() 246 | cursor.execute("SELECT * FROM chart WHERE ActyType = 'CREDIT' ORDER BY Account".format(accrange)) 247 | else: 248 | cursor = db.cursor() 249 | cursor.execute("SELECT * FROM chart WHERE Account > {} and Account < {} ORDER BY Account".format(accrange)) 250 | for row in cursor: 251 | acctNos.append(row) 252 | db.close() 253 | return acctNos 254 | 255 | def getAccountType(self,laccount): 256 | ''' 257 | Get LAST ledger balance for specified account 258 | ''' 259 | atype = list() 260 | db = sqlite3.connect('OpenAccounting.db') 261 | cursor = db.cursor() 262 | cursor.execute("SELECT ActyType FROM Chart WHERE Account = {}".format(laccount,)) 263 | for row in cursor: 264 | atype.append(row) 265 | rtype = atype[0][0] 266 | db.close() 267 | return rtype 268 | 269 | def getAccountBalance(self,laccount): 270 | ''' 271 | Get LAST ledger balance for specified account 272 | ''' 273 | balance = list() 274 | db = sqlite3.connect('OpenAccounting.db') 275 | cursor = db.cursor() 276 | cursor.execute("SELECT Balance FROM Chart WHERE Account = {}".format(laccount,)) 277 | for row in cursor: 278 | balance.append(row) 279 | oldBalance = balance[0][0] 280 | db.close() 281 | return oldBalance 282 | 283 | def getsgnAdjust(self, laccount, txType): 284 | actyType = list() 285 | db = sqlite3.connect('OpenAccounting.db') 286 | cursor = db.cursor() 287 | cursor.execute("SELECT actyType FROM Chart WHERE Account = {}".format(laccount,)) 288 | for row in cursor: 289 | actyType.append(row) 290 | aType = actyType[0][0] 291 | if (txType=='DEBIT' and aType=='DEBIT') or (txType=='CREDIT' and aType=='CREDIT' ): 292 | db.close() 293 | return 1 294 | else: 295 | db.close() 296 | return -1 297 | 298 | # return balance 299 | 300 | def packDatabase(self): 301 | db = sqlite3.connect('OpenAccounting.db') 302 | mBox.showinfo('Maintenance' , 'PACKING Database for Clean-up, Defragmentation and Improved Performance.') 303 | db.execute('VACUUM') 304 | db.close() -------------------------------------------------------------------------------- /Accounting-System-alpha-release.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/Accounting-System-alpha-release.zip -------------------------------------------------------------------------------- /Accounting-System.pyproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | 2.0 6 | {36b71360-ba1b-4c61-be93-2b584588523e} 7 | 8 | OpenAccounting.py 9 | 10 | . 11 | . 12 | {888888a0-9f3d-457c-b088-3a5042f75d52} 13 | Standard Python launcher 14 | 15 | 16 | 17 | 18 | 19 | 10.0 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Accounting-System.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Accounting-System", "Accounting-System.pyproj", "{36B71360-BA1B-4C61-BE93-2B584588523E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {36B71360-BA1B-4C61-BE93-2B584588523E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {36B71360-BA1B-4C61-BE93-2B584588523E}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ExtensibilityGlobals) = postSolution 21 | SolutionGuid = {D3102E44-3EA1-46C6-842A-B6E71D02C677} 22 | EndGlobalSection 23 | EndGlobal 24 | -------------------------------------------------------------------------------- /AccountingSystemOrganization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/AccountingSystemOrganization.png -------------------------------------------------------------------------------- /Blank General Journal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/Blank General Journal.png -------------------------------------------------------------------------------- /Blank General Ledger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/Blank General Ledger.png -------------------------------------------------------------------------------- /Example python code printing with win32 .odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/Example python code printing with win32 .odt -------------------------------------------------------------------------------- /FormDialogs.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Module for view and report forms and input form dialogs 3 | Created on Oct 30, 2018 4 | @license: MIT 5 | @author: David York 6 | @copyright: 2018 D A York 7 | ''' 8 | 9 | # ###################################### 10 | # Imports 11 | # ###################################### 12 | import tkinter as tk 13 | from tkinter import ttk, LabelFrame 14 | from tkinter import scrolledtext 15 | from tkinter import Canvas 16 | from tkinter import font 17 | from PIL import Image, ImageTk 18 | 19 | 20 | import math as mt 21 | import numpy as np 22 | import datetime as dt 23 | import pytz 24 | 25 | from AccountDB import AccountDB 26 | from Tooltips import createToolTip 27 | import os 28 | import pathlib 29 | # ####################################################### 30 | # Classes, Independently constructed Forms, Dialogs etc 31 | # ####################################################### 32 | ''' 33 | Classes, External, independently constructed Forms, Dialogs etc 34 | ''' 35 | 36 | 37 | 38 | 39 | 40 | class insertJournalForm(object): 41 | ''' 42 | A form-dialog class insertion Form to building and display for Journal Access 43 | in it's own pop-up Dialog window 44 | @summary: A form to collect journal item data to add to journal table,. A Part of Accounting System application 45 | @see: refer to main module (AccountingSystem) documentation 46 | created: Oct 17, 2018 47 | ''' 48 | here = pytz.timezone('America/New_York') 49 | currentDT = dt.datetime.now(here) 50 | 51 | def __init__(self, goal): 52 | ''' 53 | Constructor for Add to Journal in it's own pop-up data dialog window 54 | ''' 55 | 56 | # Create instance 57 | self.goal=goal 58 | self.frm = tk.Tk() 59 | # Add a title 60 | self.frm.title("Insert a Journal Entry") 61 | self.journalForm() 62 | self.frm.mainloop() 63 | 64 | 65 | 66 | def journalForm(self): 67 | ''' 68 | The form for journal access for anew transaction entry 69 | ''' 70 | # Whole form label frame--------------------------------------- 71 | lbfr = ttk.LabelFrame(self.frm, text=' Input Form ') 72 | lbfr.grid(column=0, row=0, padx=10, pady=10, sticky='W') 73 | # Transaction Entry field ------------------------------------- 74 | self.lbl0 = tk.Label(lbfr, text="Transaction").grid(column=0, row=0) 75 | self.e0 = tk.Entry(lbfr, width=16) 76 | self.e0.grid(column=1, row=0, padx=5, pady=4, sticky='W') 77 | # Associated tool tip 78 | transEnter = 'Unique transaction ID number. Is all numeric' 79 | createToolTip(self.e0, transEnter) 80 | # ------------------------------------------------------------- 81 | self.lbl1 = tk.Label(lbfr, text="Date").grid(column=0, row=1) 82 | self.e1 = tk.Entry(lbfr,width=10) 83 | self.e1.grid(column=1, row=1, padx=5, pady=4, sticky='W') 84 | rnow = str(self.currentDT.year)+'-'+str(self.currentDT.month)+'-'+str(self.currentDT.day) 85 | self.e1.insert(0, rnow) 86 | # Associated tool tip 87 | dateEnter = 'Date transaction entered, yyyy-mm-dd, defaults to today, ' 88 | createToolTip(self.e1, dateEnter) 89 | # ------------------------------------------------------------- 90 | self.lbl2 = tk.Label(lbfr, text="Time").grid(column=0, row=2) 91 | self.e2 = tk.Entry(lbfr,width=9) 92 | self.e2.grid(column=1, row=2, padx=5, pady=4, sticky='W') 93 | rtime = str(self.currentDT.hour)+':'+str(self.currentDT.minute)+':'+str(self.currentDT.second) 94 | self.e2.insert(0, rtime) 95 | # Associated tool tip 96 | timeEnter = 'Time transaction is entered. [hh:mm:ss] optional, defaults to Now' 97 | createToolTip(self.e2, timeEnter) 98 | # ------------------------------------------------------------- 99 | self.lbl3 = tk.Label(lbfr, text="Description").grid(column=0, row=3) 100 | self.e3 = tk.Entry(lbfr,width=50) 101 | self.e3.grid(column=1, row=3, padx=5, pady=4, sticky='W') 102 | # Associated tool tip 103 | descEnter = 'transaction description.' 104 | createToolTip(self.e3, descEnter) 105 | # ------------------------------------------------------------- 106 | self.lbl4 = tk.Label(lbfr, text="DebitAccount").grid(column=0, row=4) 107 | self.e4 = tk.Entry(lbfr,width=4) 108 | self.e4.grid(column=1, row=4, padx=5, pady=4, sticky='W') 109 | # Associated tool tip 110 | dAcctsEnter = 'Account to Debit' 111 | createToolTip(self.e4, dAcctsEnter) 112 | # ------------------------------------------------------------- 113 | self.lbl5 = tk.Label(lbfr, text="DebitAmount").grid(column=0, row=5) 114 | self.e5 = tk.Entry(lbfr,width=8) 115 | self.e5.grid(column=1, row=5, padx=5, pady=4, sticky='W') 116 | # Associated tool tip 117 | dAmtEnter = 'Amount to debit, dollars and cents, no not include sign, will be added by system' 118 | createToolTip(self.e5, dAmtEnter) 119 | # ------------------------------------------------------------- 120 | self.lbl6 = tk.Label(lbfr, text="CreditAccount").grid(column=0, row=6) 121 | self.e6 = tk.Entry(lbfr,width=4) 122 | self.e6.grid(column=1, row=6, padx=5, pady=4, sticky='W') 123 | # Associated tool tip 124 | cAcctEnter = 'Account to Credit' 125 | createToolTip(self.e6, cAcctEnter) 126 | # ------------------------------------------------------------- 127 | self.lbl7 = tk.Label(lbfr, text="CreditAmount").grid(column=0, row=7) 128 | self.e7 = tk.Entry(lbfr,width=8) 129 | self.e7.grid(column=1, row=7, padx=5, pady=4, sticky='W') 130 | # Associated tool tip 131 | cAmtEnter = 'Amount to credit, dollars and cents, no not include sign, will be added by system' 132 | createToolTip(self.e7, cAmtEnter) 133 | # -------------------------------------------------------------- 134 | self.btn1 = ttk.Button(lbfr, text="Commit", command=lambda: self.on_click()).grid(column=0,row=9,padx=8, pady=4, sticky='W') 135 | self.btn2 = ttk.Button(lbfr, text="Cancel", command=lambda: self.on_cancel()).grid(column=1,row=9,padx=8, pady=4, sticky='W') 136 | 137 | 138 | def on_click(self): 139 | ''' 140 | Save new Journal Entry and update associated General Ledger accounts 141 | ''' 142 | # get debit account balance 143 | daccount = int(self.e4.get()) 144 | txType = 'DEBIT' 145 | debitBalance = AccountDB.getAccountBalance(self,daccount) 146 | debitSgn = AccountDB.getsgnAdjust(self,daccount,txType) 147 | debitAmount = debitSgn * round(float(self.e7.get()),2) 148 | # get credit account balance 149 | caccount = int(self.e6.get()) 150 | txType = 'CREDIT' 151 | creditBalance= AccountDB.getAccountBalance(self, caccount) 152 | creditSgn = AccountDB.getsgnAdjust(self, caccount,txType) 153 | creditAmount = creditSgn * round(float(self.e5.get()),2) 154 | newDBalance=(debitBalance + debitAmount) 155 | newCBalance=(creditBalance + creditAmount) 156 | posted = 1 157 | jrow = (int(self.e0.get()), self.e1.get(), self.e2.get(), self.e3.get(), int(self.e4.get()), debitAmount, int(self.e6.get()),creditAmount, posted) 158 | lDRow = (int(self.e4.get()),int(self.e0.get()), debitAmount, newDBalance) 159 | lCRow = (int(self.e6.get()),int(self.e0.get()), creditAmount, newCBalance) 160 | ''' the activation of the form''' 161 | if (abs(creditAmount) == abs(debitAmount)): 162 | print("commit") 163 | AccountDB.insertJournalEntry(self, jrow) 164 | lrow = lDRow 165 | AccountDB.insertLedgerEntry(self, lrow) 166 | lrow = lCRow 167 | AccountDB.insertLedgerEntry(self, lrow) 168 | AccountDB.updateChartBalance(self, daccount, newDBalance) 169 | AccountDB.updateChartBalance(self, caccount, newCBalance) 170 | print("journalForm closed") 171 | self.frm.destroy() 172 | else: 173 | print("ERROR: Credits and Debits do not balance") 174 | 175 | def on_cancel(self): 176 | print("Cancelled action") 177 | self.frm.destroy() 178 | 179 | class insertChartForm(object): 180 | ''' 181 | A form-dialog class insertion Form for building and display Chart of Account 182 | access in it's own pop-up Dialog window 183 | @summary: A form to collect new account to add to Chart, and send to database. 184 | created: Oct 14, 2018 185 | ''' 186 | def __init__(self, goal): 187 | ''' 188 | Constructor for Add New Account dialog window 189 | ''' 190 | 191 | # Create instance 192 | self.frm = tk.Tk() 193 | # Add a title 194 | self.frm.title("Add Entry into Chart of Accounts") 195 | self.chartForm() 196 | self.frm.mainloop() 197 | 198 | def chartForm(self): 199 | ''' 200 | The form for Chart of Account access 201 | ''' 202 | # Whole form label frame -------------------------------------- 203 | lbfr = ttk.LabelFrame(self.frm, text=' Input Form ') 204 | lbfr.grid(column=0, row=0, padx=10, pady=10, sticky='W') 205 | # ------------------------------------------------------------- 206 | self.lbl0 = tk.Label(lbfr, text="Account").grid(column=0, row=0) 207 | self.e0 = tk.Entry(lbfr, width=16) 208 | self.e0.grid(column=1, row=0, padx=5, pady=4, sticky='W') 209 | acctEnter = 'Number of the New Account being Opened' 210 | createToolTip(self.e0, acctEnter) 211 | # ------------------------------------------------------------- 212 | self.lbl2 = tk.Label(lbfr, text="Name").grid(column=0, row=2) 213 | self.e2 = tk.Entry(lbfr,width=50) 214 | self.e2.grid(column=1, row=2, padx=5, pady=4, sticky='W') 215 | nameEnter = 'Name or Description of the new account' 216 | createToolTip(self.e2, nameEnter) 217 | # ------------------------------------------------------------- 218 | self.lbl3 = tk.Label(lbfr, text="Type of Account").grid(column=0, row=3) 219 | choices = ['DEBIT', 'CREDIT'] 220 | self.vtype = tk.StringVar() 221 | self.e3 = tk.OptionMenu(lbfr, self.vtype, *choices, command=self.func) 222 | self.e3.configure(width = 12) 223 | self.e3.grid(column=1, row=3, padx=5, pady=4, sticky='W') 224 | atypeEnter = 'SELECT the Type of account, Credit vs Debit. Used to determine if transactions add to or subtract from balance' 225 | createToolTip(self.e3, atypeEnter) 226 | # ------------------------------------------------------------- 227 | self.lbl4 = tk.Label(lbfr, text="Starting Balance").grid(column=0, row=4) 228 | self.e4 = tk.Entry(lbfr,width=10) 229 | self.e4.grid(column=1, row=4, padx=5, pady=4, sticky='W') 230 | self.e4.insert(0, 0) 231 | balEnter = 'Starting Account balance when opened' 232 | createToolTip(self.e4, balEnter) 233 | # ------------------------------------------------------------- 234 | self.btn3 = ttk.Button(lbfr, text="Commit", command=lambda: self.on_click()).grid(column=0,row=7,padx=8, pady=4, sticky='W') 235 | self.btn4 = ttk.Button(lbfr, text="Cancel", command=lambda: self.on_cancel()).grid(column=1,row=7,padx=8, pady=4, sticky='W') 236 | 237 | def func(self,value): 238 | pass 239 | 240 | def on_click(self): 241 | 242 | account = self.e0.get() 243 | if(account in (100,200,300,400,500,120,220,320,399)): 244 | print("ERROR, account is system reserved account, do not use.") 245 | self.on_cancel() 246 | # Check if account exits 247 | if (self.e0.get() == ''): 248 | self.on_cancel() 249 | else: 250 | account = int(self.e0.get()) 251 | existAccount = AccountDB.existChartAccount(self,account) 252 | atype = self.vtype.get() 253 | 254 | row = (int(self.e0.get()), self.e2.get(), atype, self.e4.get()) 255 | ''' the activation of the form''' 256 | if (not existAccount): 257 | print("commit") 258 | print(row) 259 | AccountDB.insertChartAccount(self, row) 260 | print("journalForm closed") 261 | self.frm.destroy() 262 | else: 263 | print("ERROR: An Account Already Exists with this Number") 264 | self.on_cancel() 265 | 266 | def on_cancel(self): 267 | print("Cancelled action") 268 | self.frm.destroy() 269 | 270 | class insertMemoForm(object): 271 | ''' 272 | A form-dialog class insertion Form for building and display Memo Composition 273 | in it's own pop-up Dialog window 274 | @summary: A form to collect memo input, 275 | created: Oct 14, 2018 276 | ''' 277 | here = pytz.timezone('America/New_York') 278 | currentDT = dt.datetime.now(here) 279 | 280 | def __init__(self, goal): 281 | ''' 282 | Constructor for Add New Memo dialog window 283 | ''' 284 | 285 | # Create instance 286 | self.frmFD3 = tk.Tk() 287 | # Add a title 288 | self.frmFD3.title("Add a Memorandum") 289 | self.memoForm() 290 | self.frmFD3.mainloop() 291 | 292 | def memoForm(self): 293 | ''' 294 | The form for Memo Composition and adding 295 | ''' 296 | # frame for reference fields for memo-------------------------- 297 | lbfr = ttk.LabelFrame(self.frmFD3, width= 560, height=200) 298 | lbfr.grid(column=0, row=0, padx=10, pady=10) 299 | # ------------------------------------------------------------- 300 | self.lbl0 = tk.Label(lbfr, text="Memo Number").grid(column=0, row=0, sticky="W") 301 | self.e0 = tk.Entry(lbfr, width=11) 302 | self.e0.grid(column=1, row=0, padx=5, pady=4, sticky='W') 303 | # ------------------------------------------------------------- 304 | self.lbl1 = tk.Label(lbfr, text="Transaction").grid(column=2, row=0, sticky='W') 305 | self.e1 = tk.Entry(lbfr, width=16) 306 | self.e1.grid(column=3, row=0, padx=5, pady=4, sticky='W') 307 | # ------------------------------------------------------------- 308 | self.lbl2 = tk.Label(lbfr, text="Date Time").grid(column=4, row=0, sticky='W') 309 | self.e2 = tk.Entry(lbfr,width=18) 310 | self.e2.grid(column=5, row=0, padx=5, pady=4, sticky='W') 311 | self.e2.insert(0, self.currentDT) 312 | # ------------------------------------------------------------- 313 | lbfr2 = ttk.LabelFrame(self.frmFD3, text='Memo Attaching', width= 560, height=200) 314 | lbfr2.grid(column=0, row=1, padx=10, pady=10) 315 | # frame around memo text -------------------------------------- 316 | scrolW1 = 80; scrolH1 = 20 317 | self.scr_memo = scrolledtext.ScrolledText(lbfr2, width=scrolW1, height=scrolH1, wrap=tk.WORD) 318 | self.scr_memo.grid(column=0, row=0, padx=4, pady=4, sticky='WE', columnspan=3) 319 | # ------------------------------------------------------------- 320 | self.btn5 = ttk.Button(lbfr2, text="Commit", command=lambda: self.on_commit3()).grid(column=0,row=27,padx=8, pady=4, sticky='W') 321 | self.btn6 = ttk.Button(lbfr2, text="Cancel", command=lambda: self.on_cancel()).grid(column=1,row=27,padx=8, pady=4, sticky='W') 322 | 323 | def on_commit3(self): 324 | ''' 325 | Save a Memo 326 | ''' 327 | if (self.e0.get() == ''): 328 | self.on_cancel() 329 | else: 330 | row = (self.e0.get(),self.e1.get(),self.e2.get(),self.scr_memo.get(1.0, tk.END)) 331 | ''' the activation of the form''' 332 | print("commit") 333 | AccountDB.insertAccountMemos(self, row) 334 | print("memoForm closed") 335 | self.frmFD3.destroy() 336 | 337 | def on_cancel(self): 338 | print("Cancelled action") 339 | self.frmFD3.destroy() 340 | 341 | class ReportFormats(): 342 | 343 | def do_reptLedger(self, account): 344 | ''' 345 | Report formating and data retrieval for Ledger account reports 346 | ''' 347 | self.win.update() 348 | self.tab4.focus_force() 349 | self.tabControl.update() 350 | self.tab4.lift(aboveThis=None) 351 | self.tabControl.update() 352 | ledgerAcct=AccountDB.getLedgerAccount(self,account) 353 | self.reportWin.delete("all") 354 | self.reportWin.create_text(5,18,anchor=tk.NW, text='Account') 355 | self.reportWin.create_text(56,18,anchor=tk.NW, text='Transaction: ') 356 | # self.reportWin.create_text(150,18,anchor=tk.NW, text='Date') 357 | # self.reportWin.create_text(196,18,anchor=tk.NW, text='Time') 358 | self.reportWin.create_text(150,18,anchor=tk.NW, text='Description') 359 | self.reportWin.create_text(430,18,anchor=tk.NW, text='Amount') 360 | self.reportWin.create_text(495,18,anchor=tk.NW, text='Balance') 361 | cline = 18 362 | cline = cline + 20 363 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 364 | cline = cline + 3 365 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 366 | for row in ledgerAcct: 367 | 368 | cline = cline + 13 369 | maccount = str(row[0]) 370 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 371 | maccount = maccount.ljust(maccountLength) 372 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=maccount) 373 | mTransact = str(row[1]) 374 | transactLength = 16 # 16 x 7 = 112 375 | mTransact = mTransact.ljust(transactLength) 376 | self.reportWin.create_text(62,cline,anchor=tk.NW, text=mTransact) 377 | transact = AccountDB.getJournalTransact(self,mTransact) 378 | for line in transact: 379 | mDescription = line[3] 380 | descLength = len(mDescription)+(30-len(mDescription)) 381 | mDescription = mDescription.ljust(descLength) 382 | self.reportWin.create_text(150,cline,anchor=tk.NW, text=mDescription) 383 | mAmount = str(round(row[2],2)) 384 | mamountLength = len(mAmount)+(8-len(mAmount)) # 8 x 7 = 56 385 | mAmount = mAmount.rjust(mamountLength) 386 | self.reportWin.create_text(430,cline,anchor=tk.NW, text=mAmount) 387 | mBalance = str(round(row[3],2)) 388 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 389 | mBalance = mBalance.rjust(mbalanceLength) 390 | self.reportWin.create_text(495,cline,anchor=tk.NW, text=mBalance) 391 | cline = cline + 20 392 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 393 | cline = cline + 3 394 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 395 | 396 | def do_reptTransact(self, jTransact): 397 | ''' 398 | Show formatted journal transaction 399 | font conversion factor is 7 = 10pt 400 | ''' 401 | self.win.update() 402 | self.tab4.focus_force() 403 | self.tabControl.update() 404 | self.tab4.lift(aboveThis=None) 405 | self.tabControl.update() 406 | transaction = AccountDB.getJournalTransact(self,jTransact) 407 | self.reportWin.delete("all") 408 | self.reportWin.create_text(10,18,anchor=tk.NW, text='Transaction: ') 409 | self.reportWin.create_text(112,18,anchor=tk.NW, text='Date') 410 | self.reportWin.create_text(189,18,anchor=tk.NW, text='Time') 411 | self.reportWin.create_text(252,18,anchor=tk.NW, text='Description') 412 | self.reportWin.create_text(522,18,anchor=tk.NW, text='Debit') 413 | self.reportWin.create_text(606,18,anchor=tk.NW, text='Credit') 414 | cline = 18 415 | cline = cline + 20 416 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 417 | cline = cline + 3 418 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 419 | for row in transaction: 420 | cline = cline + 13 421 | mTransact = str(row[0]) 422 | transactLength = 16 # 16 x 7 = 112 423 | mTransact = mTransact.ljust(transactLength) 424 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=mTransact) 425 | mDate = row[1] 426 | #mDate ="2018-10-24" 427 | dateLength = 11 # 11 x 7 = 77 428 | mDate = mDate.ljust(dateLength) 429 | self.reportWin.create_text(112,cline,anchor=tk.NW, text=mDate) 430 | mTime = row[2] 431 | #mTime = "12:40:00" 432 | timeLength = 9 # 9 x 7 = 63 433 | mTime = mTime.ljust(timeLength) 434 | self.reportWin.create_text(189,cline,anchor=tk.NW, text=mTime) 435 | mDescription = row[3] 436 | #mDescription ="Pay bills" # 30 x 9 = 270 437 | descLength = len(mDescription)+(30-len(mDescription)) 438 | mDescription = mDescription.ljust(descLength) 439 | self.reportWin.create_text(252,cline,anchor=tk.NW, text=mDescription) 440 | mdAccount = str(row[4]) 441 | #mdAccount = "100" # 4 x 7 =28 442 | daccountLength = len(mdAccount)+(4-len(mdAccount)) 443 | mdAccount = mdAccount.ljust(daccountLength) 444 | self.reportWin.create_text(522,cline,anchor=tk.NW, text=mdAccount) 445 | mdAmount = str(round(row[5],2)) 446 | #mdAmount = "-24.91 " # 8 x 7 = 56 447 | damountLength = len(mdAmount)+(8-len(mdAmount)) 448 | mdAmount = mdAmount.rjust(damountLength) 449 | self.reportWin.create_text(550,cline,anchor=tk.NW, text=mdAmount) 450 | mcAccount = str(row[6]) 451 | #mcAccount = "220" 452 | caccountLength = len(mcAccount)+(4-len(mcAccount)) 453 | mcAccount = mcAccount.ljust(caccountLength) 454 | self.reportWin.create_text(606,cline,anchor=tk.NW, text=mcAccount) 455 | mcAmount = str(round(row[7],2)) 456 | #mcAmount = "-24.91 " 457 | camountLength = len(mcAmount)+(8-len(mcAmount)) 458 | mcAmount = mcAmount.rjust(camountLength) 459 | self.reportWin.create_text(634,cline,anchor=tk.NW, text=mcAmount) 460 | 461 | cline = cline + 20 462 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 463 | cline = cline + 3 464 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 465 | if (int(jTransact) != 0): 466 | ''' 467 | If only a single transaction is to be reported, 468 | then include all associated accounting memos with it 469 | ''' 470 | memo = AccountDB.getTransactMemo(self,jTransact) 471 | cline = cline + 26 472 | self.reportWin.create_text(10,cline,anchor=tk.NW, text='Memos') 473 | cline = cline + 13 474 | self.reportWin.create_text(10,cline,anchor=tk.NW, text='Number') 475 | self.reportWin.create_text(112,cline,anchor=tk.NW, text='Date') 476 | self.reportWin.create_text(189,cline,anchor=tk.NW, text='Time') 477 | for mrow in memo: 478 | cline = cline + 13 479 | memoDate = mrow[2] 480 | dateLength = 11 # 11 x 7 = 77 481 | memoDate = memoDate.ljust(dateLength) 482 | self.reportWin.create_text(112,cline,anchor=tk.NW, text=memoDate) 483 | 484 | cline = cline + 13 485 | memoID = mrow[0] 486 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=memoID) 487 | memoText = mrow[3] 488 | self.reportWin.create_text(55,cline,anchor=tk.NW, text=memoText) 489 | 490 | def do_reptChart(self): 491 | ''' 492 | ''' 493 | self.win.update() 494 | self.tab4.focus_force() 495 | self.tabControl.update() 496 | self.tab4.lift(aboveThis=None) 497 | self.tabControl.update() 498 | chartAcct=AccountDB.getChartAccounts(self) 499 | self.reportWin.delete("all") 500 | self.reportWin.create_text(5,18,anchor=tk.NW, text='Account') 501 | self.reportWin.create_text(56,18,anchor=tk.NW, text='Description') 502 | self.reportWin.create_text(420,18,anchor=tk.NW, text='Type') 503 | self.reportWin.create_text(485,18,anchor=tk.NW, text='Balance') 504 | cline = 18 505 | cline = cline + 20 506 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 507 | cline = cline + 3 508 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 509 | for row in chartAcct: 510 | cline = cline + 13 511 | maccount = str(row[0]) 512 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 513 | maccount = maccount.ljust(maccountLength) 514 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=maccount) 515 | mDescription = row[1] 516 | descLength = len(mDescription)+(30-len(mDescription)) 517 | mDescription = mDescription.ljust(descLength) 518 | self.reportWin.create_text(56,cline,anchor=tk.NW, text=mDescription) 519 | mType = row[2] 520 | mTypeLength = len(mType)+(8-len(mType)) # 6 x 7 = 42 521 | mType = mType.rjust(mTypeLength) 522 | self.reportWin.create_text(420,cline,anchor=tk.NW, text=mType) 523 | mBalance = str(round(row[3],2)) 524 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 525 | mBalance = mBalance.rjust(mbalanceLength) 526 | self.reportWin.create_text(485,cline,anchor=tk.NW, text=mBalance) 527 | cline = cline + 20 528 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 529 | cline = cline + 3 530 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 531 | 532 | def do_reptRevandExp(self): 533 | ''' 534 | ''' 535 | # Fetch revenue accounts 536 | #Fetch Expense accounts 537 | 538 | # Get Balance totals for each account 539 | 540 | # Display the Revenue and Expense accounts in a table 541 | self.reportWin.delete("all") 542 | self.reportWin.create_text(335,12, font=font.BOLD, text='THE REVENUE STATEMENT') 543 | self.reportWin.create_text(150,38, anchor=tk.NW, font=font.BOLD, text='Revenue', fill='blue') 544 | self.reportWin.create_text(460,38,anchor=tk.NW, font=font.BOLD, text='Expenses',fill='blue') 545 | self.reportWin.create_text(12,55,anchor=tk.NW, text='Account', fill='blue') 546 | self.reportWin.create_text(63,55,anchor=tk.NW, text='Name', fill='blue') 547 | self.reportWin.create_text(273,55,anchor=tk.NW, text='Balance', fill='blue') 548 | self.reportWin.create_text(344,55,anchor=tk.NW, text='Account', fill='blue') 549 | self.reportWin.create_text(395,55,anchor=tk.NW, text='Name', fill='blue') 550 | self.reportWin.create_text(605,55,anchor=tk.NW, text='Balance', fill='blue') 551 | cline = 55 552 | cline = cline + 20 553 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 554 | cline = cline + 3 555 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 556 | # get assets block 557 | blockAcct=AccountDB.getBalSheetAccounts(self,(400,499)) 558 | revenueTotal = 0.0 559 | for row in blockAcct: 560 | revenueTotal = revenueTotal + float(row[3]) 561 | cline = cline + 13 562 | maccount = str(row[0]) 563 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 564 | maccount = maccount.ljust(maccountLength) 565 | self.reportWin.create_text(12,cline,anchor=tk.NW, text=maccount) 566 | mDescription = row[1] 567 | descLength = len(mDescription)+(30-len(mDescription)) 568 | mDescription = mDescription.ljust(descLength) 569 | self.reportWin.create_text(63,cline,anchor=tk.NW, text=mDescription) 570 | mBalance = str(round(row[3],2)) 571 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 572 | mBalance = mBalance.rjust(mbalanceLength) 573 | self.reportWin.create_text(273,cline,anchor=tk.NW, text=mBalance) 574 | 575 | #get liabilities 576 | # get liability block 577 | blockAcct=AccountDB.getBalSheetAccounts(self,(500,599)) 578 | expenseTotal = 0.0 579 | # reset cline to top right 580 | cline = 78 581 | for row in blockAcct: 582 | expenseTotal = expenseTotal + float(row[3]) 583 | cline = cline + 13 584 | maccount = str(row[0]) 585 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 586 | maccount = maccount.ljust(maccountLength) 587 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 588 | mDescription = row[1] 589 | descLength = len(mDescription)+(30-len(mDescription)) 590 | mDescription = mDescription.ljust(descLength) 591 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 592 | mBalance = str(round(row[3],2)) 593 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 594 | mBalance = mBalance.rjust(mbalanceLength) 595 | self.reportWin.create_text(605,cline,anchor=tk.NW, text=mBalance) 596 | 597 | retainedEarnings = revenueTotal - expenseTotal 598 | 599 | cline = cline + 33 600 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 601 | cline = cline + 3 602 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 603 | cline = cline + 13 604 | # Revenue total 605 | mlTotal = str(round(revenueTotal,2)) 606 | mlTotalLength = len(mlTotal)+(10-len(mlTotal)) # 8 x 7 = 56 607 | mlTotal = mlTotal.rjust(mlTotalLength) 608 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=mlTotal) 609 | # expense total 610 | mrTotal = str(round(expenseTotal,2)) 611 | mrTotalLength = len(mrTotal)+(10-len(mrTotal)) # 8 x 7 = 56 612 | mrTotal = mrTotal.rjust(mrTotalLength) 613 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=mrTotal) 614 | 615 | cline = cline + 39 616 | maccount = str(399) 617 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 618 | maccount = maccount.ljust(maccountLength) 619 | self.reportWin.create_text(12,cline,anchor=tk.NW, text=maccount) 620 | mDescription = 'Retained Earnings' 621 | descLength = len(mDescription)+(30-len(mDescription)) 622 | mDescription = mDescription.ljust(descLength) 623 | self.reportWin.create_text(63,cline,anchor=tk.NW, text=mDescription) 624 | mBalance = str(round(retainedEarnings,2)) 625 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 626 | mBalance = mBalance.rjust(mbalanceLength) 627 | self.reportWin.create_text(273,cline,anchor=tk.NW, text=mBalance) 628 | 629 | 630 | 631 | def do_reptBalSheet(self): 632 | ''' 633 | ''' 634 | # Fetch all Account Balances 635 | 636 | # Do trial Balances of each account, notify if any balances do not 637 | # agree with Chart of Account balances 638 | 639 | 640 | #Make and display the Balance Sheet 641 | self.reportWin.delete("all") 642 | self.reportWin.create_text(335,12, font=font.BOLD, text='THE BALANCE SHEET') 643 | self.reportWin.create_text(150,38, anchor=tk.NW, font=font.BOLD, text='ASSETS', fill='blue') 644 | self.reportWin.create_text(460,38,anchor=tk.NW, font=font.BOLD, text='LIABILITIES',fill='blue') 645 | self.reportWin.create_text(12,55,anchor=tk.NW, text='Account', fill='blue') 646 | self.reportWin.create_text(63,55,anchor=tk.NW, text='Name', fill='blue') 647 | self.reportWin.create_text(273,55,anchor=tk.NW, text='Balance', fill='blue') 648 | self.reportWin.create_text(344,55,anchor=tk.NW, text='Account', fill='blue') 649 | self.reportWin.create_text(395,55,anchor=tk.NW, text='Name', fill='blue') 650 | self.reportWin.create_text(605,55,anchor=tk.NW, text='Balance', fill='blue') 651 | cline = 55 652 | cline = cline + 20 653 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 654 | cline = cline + 3 655 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 656 | # get assets block 657 | blockAcct=AccountDB.getBalSheetAccounts(self,(100,199)) 658 | assetTotal = 0.0 659 | for row in blockAcct: 660 | assetTotal = assetTotal + float(row[3]) 661 | cline = cline + 13 662 | maccount = str(row[0]) 663 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 664 | maccount = maccount.ljust(maccountLength) 665 | self.reportWin.create_text(12,cline,anchor=tk.NW, text=maccount) 666 | mDescription = row[1] 667 | descLength = len(mDescription)+(30-len(mDescription)) 668 | mDescription = mDescription.ljust(descLength) 669 | self.reportWin.create_text(63,cline,anchor=tk.NW, text=mDescription) 670 | mBalance = str(round(row[3],2)) 671 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 672 | mBalance = mBalance.rjust(mbalanceLength) 673 | self.reportWin.create_text(273,cline,anchor=tk.NW, text=mBalance) 674 | 675 | cline = cline + 20 676 | self.reportWin.create_line(265,cline, 325,cline, fill="blue") 677 | cline = cline + 13 678 | mTotal = str(round(assetTotal,2)) 679 | mTotalLength = len(mTotal)+(10-len(mTotal)) # 8 x 7 = 56 680 | mTotal = mTotal.rjust(mTotalLength) 681 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=mTotal) 682 | 683 | #get liabilities 684 | # get liability block 685 | blockAcct=AccountDB.getBalSheetAccounts(self,(200,299)) 686 | liabilityTotal = 0.0 687 | # reset cline to top right 688 | cline = 78 689 | for row in blockAcct: 690 | assetTotal = assetTotal + float(row[3]) 691 | cline = cline + 13 692 | maccount = str(row[0]) 693 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 694 | maccount = maccount.ljust(maccountLength) 695 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 696 | mDescription = row[1] 697 | descLength = len(mDescription)+(30-len(mDescription)) 698 | mDescription = mDescription.ljust(descLength) 699 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 700 | mBalance = str(round(row[3],2)) 701 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 702 | mBalance = mBalance.rjust(mbalanceLength) 703 | self.reportWin.create_text(605,cline,anchor=tk.NW, text=mBalance) 704 | 705 | cline = cline + 20 706 | self.reportWin.create_line(598,cline, 663,cline, fill="blue") 707 | cline = cline + 13 708 | msTotal = str(round(liabilityTotal,2)) 709 | msTotalLength = len(msTotal)+(10-len(msTotal)) # 8 x 7 = 56 710 | msTotal = msTotal.rjust(msTotalLength) 711 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=msTotal) 712 | 713 | cline = cline + 33 714 | self.reportWin.create_text(467,cline,anchor=tk.NW, font=font.BOLD, text='Equity',fill='blue') 715 | 716 | cline = cline + 20 717 | self.reportWin.create_line(335,cline, 670,cline, fill="blue") 718 | cline = cline + 3 719 | self.reportWin.create_line(335,cline, 670,cline, fill="blue") 720 | 721 | # get income block total 722 | blockAcct=AccountDB.getBalSheetAccounts(self,(400,499)) 723 | incomeTotal = 0.0 724 | for row in blockAcct: 725 | incomeTotal = incomeTotal + float(row[3]) 726 | # get expense block total 727 | blockAcct=AccountDB.getBalSheetAccounts(self,(500,599)) 728 | expenseTotal = 0.0 729 | for row in blockAcct: 730 | expenseTotal = expenseTotal + float(row[3]) 731 | retainedEarnings = incomeTotal - expenseTotal 732 | 733 | # get equity block 734 | blockAcct=AccountDB.getBalSheetAccounts(self,(300,399)) 735 | equityTotal = 0.0 736 | # reset cline to top right 737 | for row in blockAcct: 738 | equityTotal = equityTotal + float(row[3]) 739 | cline = cline + 13 740 | maccount = str(row[0]) 741 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 742 | maccount = maccount.ljust(maccountLength) 743 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 744 | mDescription = row[1] 745 | descLength = len(mDescription)+(30-len(mDescription)) 746 | mDescription = mDescription.ljust(descLength) 747 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 748 | mBalance = str(round(row[3],2)) 749 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 750 | mBalance = mBalance.rjust(mbalanceLength) 751 | self.reportWin.create_text(605,cline,anchor=tk.NW, text=mBalance) 752 | 753 | cline = cline + 13 754 | maccount = str(399) 755 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 756 | maccount = maccount.ljust(maccountLength) 757 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 758 | mDescription = 'Retained Earnings' 759 | descLength = len(mDescription)+(30-len(mDescription)) 760 | mDescription = mDescription.ljust(descLength) 761 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 762 | mBalance = str(round(retainedEarnings,2)) 763 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 764 | mBalance = mBalance.rjust(mbalanceLength) 765 | self.reportWin.create_text(612,cline,anchor=tk.NW, text=mBalance) 766 | 767 | cline = cline + 20 768 | self.reportWin.create_line(598,cline, 663,cline, fill="blue") 769 | cline = cline + 13 770 | msTotal = str(round((equityTotal + retainedEarnings),2)) 771 | 772 | msTotalLength = len(msTotal)+(10-len(msTotal)) 773 | msTotal = msTotal.rjust(msTotalLength) 774 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=msTotal) 775 | 776 | cline = cline + 20 777 | self.reportWin.create_line(598,cline, 663,cline, fill="blue") 778 | 779 | cline = cline + 43 780 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 781 | cline = cline + 3 782 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 783 | cline = cline + 13 784 | 785 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=mTotal) 786 | mrTotal = str(round((equityTotal + liabilityTotal),2)) 787 | mrTotalLength = len(mrTotal)+(10-len(mrTotal)) 788 | mrTotal = msTotal.rjust(mrTotalLength) 789 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=mrTotal) 790 | 791 | def do_reptTrialBalance(self): 792 | ''' 793 | ''' 794 | # Fetch revenue accounts 795 | # Fetch Expense accounts 796 | # Get Balance totals for each account 797 | # Display the Revenue and Expense accounts in a table 798 | self.reportWin.delete("all") 799 | self.reportWin.create_text(335,12, font=font.BOLD, text='Trial Balance Display') 800 | self.reportWin.create_text(150,38, anchor=tk.NW, font=font.BOLD, text='DEBITS', fill='blue') 801 | self.reportWin.create_text(460,38,anchor=tk.NW, font=font.BOLD, text='CREDITS',fill='blue') 802 | self.reportWin.create_text(12,55,anchor=tk.NW, text='Account', fill='blue') 803 | self.reportWin.create_text(63,55,anchor=tk.NW, text='Name', fill='blue') 804 | self.reportWin.create_text(273,55,anchor=tk.NW, text='Balance', fill='blue') 805 | self.reportWin.create_text(344,55,anchor=tk.NW, text='Account', fill='blue') 806 | self.reportWin.create_text(395,55,anchor=tk.NW, text='Name', fill='blue') 807 | self.reportWin.create_text(605,55,anchor=tk.NW, text='Balance', fill='blue') 808 | cline = 55 809 | # ------------------------------------------------------------- 810 | cline = cline + 20 811 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 812 | cline = cline + 3 813 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 814 | # ------------------------------------------------------------- 815 | # ------------------------------------------------------------- 816 | # Get Debits 817 | # get assets block 818 | blockAcct=AccountDB.getBalSheetAccounts(self,(100,199)) 819 | assetsTotal = 0.0 820 | for row in blockAcct: 821 | account = row[0] 822 | ldgrAccount = AccountDB.getLedgerAccount(self,account) 823 | acctTotal = 0.0 824 | for mtransact in ldgrAccount: 825 | acctTotal = acctTotal + mtransact[2] 826 | assetsTotal = assetsTotal + float(acctTotal) 827 | cline = cline + 13 828 | maccount = str(row[0]) 829 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 830 | maccount = maccount.ljust(maccountLength) 831 | self.reportWin.create_text(12,cline,anchor=tk.NW, text=maccount) 832 | mDescription = row[1] 833 | descLength = len(mDescription)+(30-len(mDescription)) 834 | mDescription = mDescription.ljust(descLength) 835 | self.reportWin.create_text(63,cline,anchor=tk.NW, text=mDescription) 836 | mBalance = str(round(acctTotal,2)) 837 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 838 | mBalance = mBalance.rjust(mbalanceLength) 839 | self.reportWin.create_text(273,cline,anchor=tk.NW, text=mBalance) 840 | 841 | cline = cline + 20 842 | self.reportWin.create_line(265,cline, 325,cline, fill="blue") 843 | cline = cline + 13 844 | mTotal = str(round(assetsTotal,2)) 845 | mTotalLength = len(mTotal)+(10-len(mTotal)) # 8 x 7 = 56 846 | mTotal = mTotal.rjust(mTotalLength) 847 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=mTotal) 848 | cline = cline + 13 849 | 850 | # ------------------------------------------------------------- 851 | # get Expense block 852 | blockAcct=AccountDB.getBalSheetAccounts(self,(500,599)) 853 | expenseTotal = 0.0 854 | # reset cline to top right 855 | for row in blockAcct: 856 | account = row[0] 857 | ldgrAccount = AccountDB.getLedgerAccount(self,account) 858 | acctTotal = 0.0 859 | for mtransact in ldgrAccount: 860 | acctTotal = acctTotal + mtransact[2] 861 | expenseTotal = expenseTotal + float(acctTotal) 862 | cline = cline + 13 863 | maccount = str(row[0]) 864 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 865 | maccount = maccount.ljust(maccountLength) 866 | self.reportWin.create_text(12,cline,anchor=tk.NW, text=maccount) 867 | mDescription = row[1] 868 | descLength = len(mDescription)+(30-len(mDescription)) 869 | mDescription = mDescription.ljust(descLength) 870 | self.reportWin.create_text(63,cline,anchor=tk.NW, text=mDescription) 871 | mBalance = str(round(acctTotal,2)) 872 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 873 | mBalance = mBalance.rjust(mbalanceLength) 874 | self.reportWin.create_text(273,cline,anchor=tk.NW, text=mBalance) 875 | 876 | cline = cline + 20 877 | self.reportWin.create_line(265,cline, 325,cline, fill="blue") 878 | cline = cline + 13 879 | mlTotal = str(round(expenseTotal,2)) 880 | mlTotalLength = len(mlTotal)+(10-len(mlTotal)) # 8 x 7 = 56 881 | mlTotal = mlTotal.rjust(mlTotalLength) 882 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=mlTotal) 883 | cline = cline + 13 884 | lcline = cline 885 | 886 | # ------------------------------------------------------------- 887 | # ------------------------------------------------------------- 888 | # get Credits 889 | # get Liabilities and Equity block 890 | blockAcct=AccountDB.getBalSheetAccounts(self,(200,399)) 891 | liabilitiesTotal = 0.0 892 | # reset cline to top right 893 | cline = 78 894 | for row in blockAcct: 895 | account = row[0] 896 | ldgrAccount = AccountDB.getLedgerAccount(self,account) 897 | acctTotal = 0.0 898 | for mtransact in ldgrAccount: 899 | acctTotal = acctTotal + mtransact[2] 900 | liabilitiesTotal = liabilitiesTotal + float(acctTotal) 901 | cline = cline + 13 902 | maccount = str(row[0]) 903 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 904 | maccount = maccount.ljust(maccountLength) 905 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 906 | mDescription = row[1] 907 | descLength = len(mDescription)+(30-len(mDescription)) 908 | mDescription = mDescription.ljust(descLength) 909 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 910 | mBalance = str(round(acctTotal,2)) 911 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 912 | mBalance = mBalance.rjust(mbalanceLength) 913 | self.reportWin.create_text(612,cline,anchor=tk.NW, text=mBalance) 914 | 915 | cline = cline + 20 916 | self.reportWin.create_line(598,cline, 663,cline, fill="blue") 917 | cline = cline + 13 918 | mrTotal = str(round(liabilitiesTotal,2)) 919 | mrTotalLength = len(mrTotal)+(10-len(mrTotal)) # 8 x 7 = 56 920 | mrTotal = mrTotal.rjust(mrTotalLength) 921 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=mrTotal) 922 | 923 | # ------------------------------------------------------------- 924 | # get revenue block 925 | cline = cline + 13 926 | blockAcct=AccountDB.getBalSheetAccounts(self,(400,499)) 927 | revenueTotal = 0.0 928 | for row in blockAcct: 929 | account = row[0] 930 | ldgrAccount = AccountDB.getLedgerAccount(self,account) 931 | acctTotal = 0.0 932 | for mtransact in ldgrAccount: 933 | revenueTotal = revenueTotal + mtransact[2] 934 | assetsTotal = assetsTotal + float(acctTotal) 935 | cline = cline + 13 936 | maccount = str(row[0]) 937 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 938 | maccount = maccount.ljust(maccountLength) 939 | self.reportWin.create_text(344,cline,anchor=tk.NW, text=maccount) 940 | mDescription = row[1] 941 | descLength = len(mDescription)+(30-len(mDescription)) 942 | mDescription = mDescription.ljust(descLength) 943 | self.reportWin.create_text(395,cline,anchor=tk.NW, text=mDescription) 944 | mBalance = str(round(acctTotal,2)) 945 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 946 | mBalance = mBalance.rjust(mbalanceLength) 947 | self.reportWin.create_text(612,cline,anchor=tk.NW, text=mBalance) 948 | 949 | cline = cline + 20 950 | self.reportWin.create_line(598,cline, 663,cline, fill="blue") 951 | 952 | cline = cline + 13 953 | mrTotal = str(round(revenueTotal,2)) 954 | mrTotalLength = len(mrTotal)+(10-len(mrTotal)) # 8 x 7 = 56 955 | mrTotal = mrTotal.rjust(mrTotalLength) 956 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=mrTotal) 957 | cline = cline + 13 958 | rcline = cline 959 | # ------------------------------------------------------------- 960 | # check which column is longer and use it 961 | if lcline > rcline: 962 | cline = lcline 963 | else: 964 | cline = rcline 965 | cline = cline + 26 966 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 967 | cline = cline + 3 968 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 969 | cline = cline + 26 970 | debitTotal = assetsTotal + expenseTotal 971 | self.reportWin.create_text(63,cline,anchor=tk.NW, text="Total Debits") 972 | self.reportWin.create_text(265,cline,anchor=tk.NW, text=debitTotal) 973 | creditTotal = liabilitiesTotal + revenueTotal 974 | self.reportWin.create_text(395,cline,anchor=tk.NW, text="Total Credits") 975 | self.reportWin.create_text(598,cline,anchor=tk.NW, text=creditTotal) 976 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 David A York 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /OpenAccounting.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/OpenAccounting.db -------------------------------------------------------------------------------- /OpenAccounting.py: -------------------------------------------------------------------------------- 1 | ''' 2 | OpenAccounting 3 | Created on Oct 17, 2018 4 | @summary: An open source double entry accounting system based on SQLite and written in python 5 | @note: Project pages at: https://medmatix.github.io/Accounting-System/ 6 | @author: David York 7 | @version: 0.21 8 | @copyright: David A York 2018 9 | @license: MIT 10 | @contact: http://crunches-data.appspot.com/contact.html 11 | ''' 12 | 13 | # ##################### 14 | # Imports 15 | # ##################### 16 | # Standard library and third party imports 17 | # tkinter imports 18 | import tkinter as tk 19 | from tkinter import ttk 20 | from tkinter import scrolledtext 21 | from tkinter import Menu 22 | from tkinter import messagebox as mBox 23 | from tkinter import simpledialog 24 | from tkinter import Scrollbar 25 | from tkinter import Canvas 26 | from tkinter import font 27 | from PIL import Image, ImageTk 28 | # other standard and third party imports 29 | # all 3rd party are pip installable 30 | import sqlite3 31 | import datetime as dt 32 | import pytz 33 | import math as mt 34 | import numpy as np 35 | import sys 36 | import os 37 | import pathlib 38 | import time 39 | 40 | # Custom module imports 41 | from AccountDB import AccountDB 42 | import FormDialogs 43 | from FormDialogs import insertJournalForm, insertChartForm, insertMemoForm 44 | from FormDialogs import ReportFormats 45 | from Tooltips import createToolTip, ToolTip 46 | from ReportPreps import TrialBalance 47 | import splash 48 | 49 | 50 | 51 | class AccountingSystem(): 52 | ''' 53 | Class for the Main Application 54 | @summary: Application tkinter GUI interface and accounting function access 55 | @note: From this module all the functionality of the accounting system is accessible 56 | ''' 57 | def __init__(self): 58 | ''' 59 | Constructor Setup the Application 60 | ''' 61 | # Create instance 62 | self.win = tk.Tk() 63 | 64 | # Add a title 65 | self.win.title("Open Accounting") 66 | 67 | 68 | # Add a icon 69 | if not sys.platform.startswith('linux'): 70 | self.win.iconbitmap('./images/medmatix_tilt.ico') 71 | 72 | # Initialize widgets 73 | self.createWidgets() 74 | 75 | # ################################# 76 | # GUI callback functions 77 | # ################################# 78 | # -- Exit GUI cleanly ------------- 79 | def _quit(self): 80 | self.win.quit() 81 | self.win.destroy() 82 | print('run is done, exited normally!') 83 | exit() 84 | 85 | def do_showChart(self): 86 | self.scrolList3.delete(1.0,tk.END) 87 | listAll = AccountDB.getChartAccounts(self) 88 | for row in listAll: 89 | #self.do_formatedList(row) 90 | self.scrolList3.insert(tk.END,row[0]) 91 | self.scrolList3.insert(tk.END,'\t ') 92 | mName = row[1] 93 | nameLength = len(mName)+(40-len(mName)) 94 | mName = mName.ljust(nameLength) 95 | self.scrolList3.insert(tk.END,mName) 96 | self.scrolList3.insert(tk.END,' ') 97 | mType = row[2] 98 | typeLength = len(row[2])+(12-len(row[2])) 99 | mType = mType.ljust(typeLength) 100 | self.scrolList3.insert(tk.END,mType) 101 | self.scrolList3.insert(tk.END,'\t') 102 | mBalance = str(round(row[3],2)) 103 | balanceLength = len(mBalance)+(8-len(mBalance)) 104 | mBalance = mBalance.rjust(balanceLength) 105 | self.scrolList3.insert(tk.END,mBalance) 106 | self.scrolList3.insert(tk.END,'\n') 107 | 108 | def do_showLedger(self, account): 109 | self.scrolList2.delete(1.0,tk.END) 110 | account = int(account) 111 | 112 | listAll = AccountDB.getLedgerAccount(self,account) 113 | for row in listAll: 114 | #self.do_formatedList(row) 115 | self.scrolList2.insert(tk.END,row[0]) 116 | self.scrolList2.insert(tk.END,'\t ') 117 | mTransact = str(row[1]) 118 | transactLength = len(mTransact)+(10-len(mTransact)) 119 | mTransact = mTransact.ljust(transactLength) 120 | self.scrolList2.insert(tk.END,mTransact) 121 | self.scrolList2.insert(tk.END,'\t ') 122 | mAmount = str(row[2]) 123 | amountLength = len(mAmount)+(8-len(mAmount)) 124 | mAmount = mAmount.ljust(amountLength) 125 | self.scrolList2.insert(tk.END,mAmount) 126 | self.scrolList2.insert(tk.END,'\t') 127 | mBalance = str(round(row[3],2)) 128 | balanceLength = len(mBalance)+(8-len(mBalance)) 129 | mBalance = mBalance.rjust(balanceLength) 130 | self.scrolList2.insert(tk.END,mBalance) 131 | self.scrolList2.insert(tk.END,'\n') 132 | 133 | def do_showJournal(self, jDates): 134 | ''' 135 | Show formatted journal 136 | ''' 137 | self.scrolList1.delete(1.0,tk.END) 138 | listAll = AccountDB.getJournalEntries(self,jDates) 139 | for row in listAll: 140 | self.scrolList1.insert(tk.END,row[0]) 141 | self.scrolList1.insert(tk.END,'\t') 142 | mDate = row[1] 143 | dateLength = 10 144 | mDate = mDate.ljust(dateLength) 145 | self.scrolList1.insert(tk.END,mDate) 146 | self.scrolList1.insert(tk.END,' ') 147 | mTime = row[2] 148 | timeLength = 9 149 | mTime = mTime.ljust(timeLength) 150 | self.scrolList1.insert(tk.END,mTime) 151 | self.scrolList1.insert(tk.END,' ') 152 | mDescription = row[3] 153 | descLength = len(row[3])+(30-len(row[3])) 154 | mDescription = mDescription.ljust(descLength) 155 | self.scrolList1.insert(tk.END,mDescription) 156 | self.scrolList1.insert(tk.END,' \t') 157 | mdAccount = str(row[4]) 158 | daccountLength = len(mdAccount)+(4-len(mdAccount)) 159 | mdAccount = mdAccount.ljust(daccountLength) 160 | self.scrolList1.insert(tk.END,mdAccount) 161 | self.scrolList1.insert(tk.END,' ') 162 | mdAmount = str(round(row[5],2)) 163 | damountLength = len(mdAmount)+(8-len(mdAmount)) 164 | mdAmount = mdAmount.rjust(damountLength) 165 | self.scrolList1.insert(tk.END,mdAmount) 166 | self.scrolList1.insert(tk.END,'\t\t ') 167 | mcAccount = str(row[6]) 168 | caccountLength = len(mcAccount)+(4-len(mcAccount)) 169 | mcAccount = mcAccount.ljust(caccountLength) 170 | self.scrolList1.insert(tk.END,mcAccount) 171 | self.scrolList1.insert(tk.END,'\t') 172 | mcAmount = str(round(row[7],2)) 173 | camountLength = len(mcAmount)+(8-len(mcAmount)) 174 | mcAmount = mcAmount.rjust(camountLength) 175 | self.scrolList1.insert(tk.END,mcAmount) 176 | self.scrolList1.insert(tk.END,'\n') 177 | 178 | def do_reptTransact(self, jTransact): 179 | ''' 180 | Show formatted journal transaction 181 | font conversion factor is 7 = 10pt 182 | ''' 183 | self.win.update() 184 | self.tab4.focus_force() 185 | self.tabControl.update() 186 | self.tab4.lift(aboveThis=None) 187 | self.tabControl.update() 188 | transaction = AccountDB.getJournalTransact(self,jTransact) 189 | self.reportWin.delete("all") 190 | self.reportWin.create_text(10,18,anchor=tk.NW, text='Transaction: ') 191 | self.reportWin.create_text(112,18,anchor=tk.NW, text='Date') 192 | self.reportWin.create_text(189,18,anchor=tk.NW, text='Time') 193 | self.reportWin.create_text(252,18,anchor=tk.NW, text='Description') 194 | self.reportWin.create_text(522,18,anchor=tk.NW, text='Debit') 195 | self.reportWin.create_text(606,18,anchor=tk.NW, text='Credit') 196 | cline = 18 197 | cline = cline + 20 198 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 199 | cline = cline + 3 200 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 201 | for row in transaction: 202 | cline = cline + 13 203 | mTransact = str(row[0]) 204 | transactLength = 16 # 16 x 7 = 112 205 | mTransact = mTransact.ljust(transactLength) 206 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=mTransact) 207 | mDate = row[1] 208 | #mDate ="2018-10-24" 209 | dateLength = 11 # 11 x 7 = 77 210 | mDate = mDate.ljust(dateLength) 211 | self.reportWin.create_text(112,cline,anchor=tk.NW, text=mDate) 212 | mTime = row[2] 213 | #mTime = "12:40:00" 214 | timeLength = 9 # 9 x 7 = 63 215 | mTime = mTime.ljust(timeLength) 216 | self.reportWin.create_text(189,cline,anchor=tk.NW, text=mTime) 217 | mDescription = row[3] 218 | #mDescription ="Pay bills" # 30 x 9 = 270 219 | descLength = len(mDescription)+(30-len(mDescription)) 220 | mDescription = mDescription.ljust(descLength) 221 | self.reportWin.create_text(252,cline,anchor=tk.NW, text=mDescription) 222 | mdAccount = str(row[4]) 223 | #mdAccount = "100" # 4 x 7 =28 224 | daccountLength = len(mdAccount)+(4-len(mdAccount)) 225 | mdAccount = mdAccount.ljust(daccountLength) 226 | self.reportWin.create_text(522,cline,anchor=tk.NW, text=mdAccount) 227 | mdAmount = str(round(row[5],2)) 228 | #mdAmount = "-24.91 " # 8 x 7 = 56 229 | damountLength = len(mdAmount)+(8-len(mdAmount)) 230 | mdAmount = mdAmount.rjust(damountLength) 231 | self.reportWin.create_text(550,cline,anchor=tk.NW, text=mdAmount) 232 | mcAccount = str(row[6]) 233 | #mcAccount = "220" 234 | caccountLength = len(mcAccount)+(4-len(mcAccount)) 235 | mcAccount = mcAccount.ljust(caccountLength) 236 | self.reportWin.create_text(606,cline,anchor=tk.NW, text=mcAccount) 237 | mcAmount = str(round(row[7],2)) 238 | #mcAmount = "-24.91 " 239 | camountLength = len(mcAmount)+(8-len(mcAmount)) 240 | mcAmount = mcAmount.rjust(camountLength) 241 | self.reportWin.create_text(634,cline,anchor=tk.NW, text=mcAmount) 242 | 243 | cline = cline + 20 244 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 245 | cline = cline + 3 246 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 247 | if (int(jTransact) != 0): 248 | ''' 249 | If only a single transaction is to be reported, 250 | then include all associated accounting memos with it 251 | ''' 252 | memo = AccountDB.getTransactMemo(self,jTransact) 253 | cline = cline + 26 254 | self.reportWin.create_text(10,cline,anchor=tk.NW, text='Memos') 255 | cline = cline + 13 256 | self.reportWin.create_text(10,cline,anchor=tk.NW, text='Number') 257 | self.reportWin.create_text(112,cline,anchor=tk.NW, text='Date') 258 | self.reportWin.create_text(189,cline,anchor=tk.NW, text='Time') 259 | for mrow in memo: 260 | cline = cline + 13 261 | memoDate = mrow[2] 262 | dateLength = 11 # 11 x 7 = 77 263 | memoDate = memoDate.ljust(dateLength) 264 | self.reportWin.create_text(112,cline,anchor=tk.NW, text=memoDate) 265 | 266 | cline = cline + 13 267 | memoID = mrow[0] 268 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=memoID) 269 | memoText = mrow[3] 270 | self.reportWin.create_text(55,cline,anchor=tk.NW, text=memoText) 271 | 272 | def do_reptChart(self): 273 | ''' 274 | ''' 275 | self.win.update() 276 | self.tab4.focus_force() 277 | self.tabControl.update() 278 | self.tab4.lift(aboveThis=None) 279 | self.tabControl.update() 280 | chartAcct=AccountDB.getChartAccounts(self) 281 | self.reportWin.delete("all") 282 | self.reportWin.create_text(5,18,anchor=tk.NW, text='Account') 283 | self.reportWin.create_text(56,18,anchor=tk.NW, text='Description') 284 | self.reportWin.create_text(420,18,anchor=tk.NW, text='Type') 285 | self.reportWin.create_text(485,18,anchor=tk.NW, text='Balance') 286 | cline = 18 287 | cline = cline + 20 288 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 289 | cline = cline + 3 290 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 291 | for row in chartAcct: 292 | cline = cline + 13 293 | maccount = str(row[0]) 294 | maccountLength = len(maccount)+(4-len(maccount)) # 4 x 7 =28 295 | maccount = maccount.ljust(maccountLength) 296 | self.reportWin.create_text(10,cline,anchor=tk.NW, text=maccount) 297 | mDescription = row[1] 298 | descLength = len(mDescription)+(30-len(mDescription)) 299 | mDescription = mDescription.ljust(descLength) 300 | self.reportWin.create_text(56,cline,anchor=tk.NW, text=mDescription) 301 | mType = row[2] 302 | mTypeLength = len(mType)+(8-len(mType)) # 6 x 7 = 42 303 | mType = mType.rjust(mTypeLength) 304 | self.reportWin.create_text(420,cline,anchor=tk.NW, text=mType) 305 | mBalance = str(round(row[3],2)) 306 | mbalanceLength = len(mBalance)+(8-len(mBalance)) # 8 x 7 = 56 307 | mBalance = mBalance.rjust(mbalanceLength) 308 | self.reportWin.create_text(485,cline,anchor=tk.NW, text=mBalance) 309 | cline = cline + 20 310 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 311 | cline = cline + 3 312 | self.reportWin.create_line(10,cline, 670,cline, fill="blue") 313 | 314 | 315 | 316 | # ################################# 317 | # Other Control Functions etc. 318 | # ################################# 319 | 320 | def click_on_widget(self, widget, button=1): 321 | widget.focus_force() 322 | self.win.update() 323 | widget.event_generate("".format(button)) 324 | self.win.update() 325 | 326 | def newMemo(self): 327 | ''' 328 | Method to start a new accounting memo 329 | ''' 330 | proceedAnswer = mBox.askyesno("New Memo","Save current memo and start a new one?") 331 | if proceedAnswer==True: 332 | ''' 333 | Insert an entry into Journal 334 | ''' 335 | id = int(self.ememoID.get()) 336 | transact = int(self.ememoTransact.get()) 337 | memo=str(self.scr_memo.get(1.0,tk.END)) 338 | row = (id, transact,self.ememoDate.get(),memo) 339 | try: 340 | AccountDB.insertAccountMemos(row) 341 | except: 342 | print("not saved, probable duplicate memo") 343 | # clear the memo 344 | self.scr_memo.delete(1.0,tk.END) 345 | self.scr_memo.see(tk.END) 346 | print('cleared the notes pad') 347 | else: 348 | pass 349 | 350 | def printMemo(self): 351 | ''' 352 | Method to Print a selected accounting memo 353 | ''' 354 | proceedAnswer = mBox.askyesno("print Memo","Send the current memo to the printer?") 355 | if proceedAnswer==True: 356 | pass 357 | else: 358 | pass 359 | 360 | def saveMemo(self): 361 | ''' 362 | Method to save the current accounting memo 363 | ''' 364 | proceedAnswer = mBox.askyesno("Save Memo","Save current memo and continue?") 365 | if (proceedAnswer): 366 | ''' 367 | Save a Memo 368 | ''' 369 | mid = int(self.ememoID.get()) 370 | transact = int(self.ememoTransact.get()) 371 | memo=str(self.scr_memo.get(1.0,tk.END)) 372 | row = (mid, transact,self.ememoDate.get(),memo) 373 | try: 374 | AccountDB.insertAccountMemos(row) 375 | except: 376 | print("not saved, probable duplicate memo") 377 | 378 | 379 | def fetchMemo(self): 380 | ''' 381 | Method to fetch a selected accounting memo 382 | ''' 383 | answer1 = simpledialog.askstring("Input", "How Do you want to retrieve, MemoNo or Date-Transact", parent=self.win) 384 | if answer1=="MemoNo": 385 | answer2 = simpledialog.askstring("Input", "Enter the Memo ID Number", parent=self.win) 386 | elif answer1=="Date-Transact": 387 | answer2 = simpledialog.askstring("Input", "Enter Date and Transaction (separated by a comma", parent=self.win) 388 | else: 389 | pass 390 | 391 | def do_balSheet(self): 392 | ''' 393 | ''' 394 | 395 | proceedAnswer = mBox.askyesno("Balance Sheet Report","This can take a while.\nPrepare report now?") 396 | if (proceedAnswer): 397 | debandcred = TrialBalance.trialBalance(self) 398 | if (debandcred[0]-debandcred[1]==0): 399 | ReportFormats.do_reptBalSheet(self) 400 | else: 401 | mBox.showinfo('Failed trial balance' , 'The Ledger appears to be out of balance. \n A balance sheet may have errors\nPerform a Trial Balance to Investigate') 402 | def do_LedgerAcct(self): 403 | ''' 404 | ''' 405 | proceedAnswer = mBox.askyesno("Ledger Account Report","Prepare report now?") 406 | pass 407 | 408 | 409 | def do_RevandExp(self): 410 | ''' 411 | ''' 412 | 413 | proceedAnswer = mBox.askyesno("Revenue and Expense Report","Prepare report now?") 414 | if (proceedAnswer): 415 | debandcred = TrialBalance.trialBalance(self) 416 | if (debandcred[0]-debandcred[1]==0): 417 | ReportFormats.do_reptRevandExp(self) 418 | else: 419 | mBox.showinfo('Failed trial balance' , 'The Ledger appears to be out of balance. \nAn Income statment may have errors\nPerform a Trial Balance to Investigate') 420 | 421 | 422 | def do_trialBalance(self): 423 | ''' 424 | Calculate net balances for each chart account and compare to Chart of 425 | Account balances. If any do not agree, notify which are out and return results 426 | ''' 427 | proceedAnswer = mBox.askyesno("Trial Balance Report","This can take a while.\nPerform a trail balance now?") 428 | if (proceedAnswer): 429 | ReportFormats.do_reptTrialBalance(self) 430 | 431 | def do_printCurrentView(self): 432 | ''' 433 | ''' 434 | proceedAnswer = mBox.askyesno("Print Current Report","Postcript write to file 'Printfile.ps'?") 435 | if (proceedAnswer): 436 | self.reportWin.postscript(file='Printfile.ps') 437 | 438 | # ##################################### 439 | # Create GUI Functions (Visualization) 440 | # ##################################### 441 | def createWidgets(self): 442 | ''' 443 | Create the GUI interfaces 444 | ''' 445 | # Messages and Dialogs ------------------------------------------- 446 | def info(self): 447 | mBox.showinfo('About OpenAccounting, ' , 'Application to Perform Basic GAAP Accounting functions.\n\n (c) David A York, 2018\n http:crunches-data.appspot.com \nVersion: 0.2alpha, development version 0.21 \nlicense: MIT') 448 | 449 | 450 | def notImplementedInfo(self): 451 | mBox.showinfo('Function Not Implemented Yet, ' , 'Sorry this is not implemented in full yet.\n\n Note For Printing: This is a high priority for me but tkinter is not very amenable to printing widget contents. However, at this time, you can take a screen shot and print from clipboard') 452 | 453 | def notImplementedPayroll(self): 454 | mBox.showinfo('Payroll Not Implemented Yet, ' , 'Sorry this functionality is not implemented yet.\n\n This specialized journal will be developed after all basic accounting \nfunctionality is in place. Your support is appreciated') 455 | 456 | def notImplementedInventory(self): 457 | mBox.showinfo('Inventory Not Implemented Yet, ' , 'Sorry this functionality is not implemented yet.\n\n This specialized journal will be developed after all basic accounting \nfunctionality is in place. Your support is appreciated') 458 | 459 | def notImplementedHelp(self): 460 | mBox.showinfo('Help Not Implemented Yet, ' , 'Sorry the Help is not implemented in full yet.\n\n This is part of the basic application functionality and is next in priority. \nFor now the background in the github repository readme \nmay be helpful. See about, in help.') 461 | 462 | def notImplementedEndofCycle(self): 463 | mBox.showinfo('End of Cycle Not Implemented Yet, ' , 'Sorry the comprehensive End of Cycle is not implemented in full yet.\n\n This is part of the basic application functionality and is a high priority for me.\n\nFor now, all closing activities can be carried out with the \nGeneral Journal if you are familiar with those tasks.') 464 | 465 | def fetchLedgerAccount(self): 466 | answer = simpledialog.askstring("Get Ledger Account", "What Account Number to retrieve?\n Enter '0' for ALL\n", parent=self.win) 467 | if answer is not None: 468 | ReportFormats.do_reptLedger(self,answer) 469 | else: 470 | print("No Value entered") 471 | return 0 472 | 473 | def getJournalDates(self): 474 | answer = simpledialog.askstring("Get Journal Range", "Start and End Date for Journal Retrieval\n, a comma separated pair. \n Enter '0,0' for ALL\n", parent=self.win) 475 | if answer is not None: 476 | jDates = (answer,) 477 | self.do_showJournal((jDates)) 478 | else: 479 | print("No Value entered") 480 | return 0 481 | 482 | def getTransact(self): 483 | answer = simpledialog.askstring("Get Transaction", "Transaction to retrieve?\n\nEnter a Transaction Number\n Enter '0' for ALL \n", parent=self.win) 484 | if answer is not None: 485 | jTransact = (answer) 486 | self.do_reptTransact(jTransact) 487 | else: 488 | print("No Value entered") 489 | return 0 490 | 491 | 492 | # Tab Controls created here -------------------------------------- 493 | self.tabControl = ttk.Notebook(self.win) # Create Tab Controls 494 | 495 | self.tab1 = ttk.Frame(self.tabControl) 496 | self.tabControl.add(self.tab1, text='Journal') 497 | 498 | self.tab2 = ttk.Frame(self.tabControl) 499 | self.tabControl.add(self.tab2, text='Ledger') 500 | 501 | self.tab3 = ttk.Frame(self.tabControl) 502 | self.tabControl.add(self.tab3, text='Chart of Accounts') 503 | 504 | self.tab4 = ttk.Frame(self.tabControl) 505 | self.tabControl.add(self.tab4, text='Views and Reports') 506 | 507 | self.tab5 = ttk.Frame(self.tabControl) 508 | self.tabControl.add(self.tab5, text='Accounting Memo') 509 | 510 | self.tab6 = ttk.Frame(self.tabControl) 511 | self.tabControl.add(self.tab6, text='Maintenance') 512 | 513 | self.tabControl.grid() # Pack to make visible 514 | 515 | frm1 = ttk.Labelframe(self.tab1, text='General Journal', width= 650, height=600) 516 | frm1.grid() 517 | ttk.Label(frm1, text="The General Journal is the book of first entry in the accounting system. It is accessed directly in the course of recording the daily activities of the enterprise.\n All other related books are accessible as appropriate for addition and update from the general journal.").grid(column=0, row=0, padx=4, pady=4,sticky='W') 518 | frm1a = ttk.Labelframe(frm1, width= 550, height=500) 519 | frm1a.grid(column=0,row=1) 520 | jDates = (0,0) 521 | self.updateJournal = ttk.Button(frm1a, text="Update Display", command=lambda: self.do_showJournal(jDates)).grid(column=0,row=1,padx=4, pady=4) 522 | self.printJournal = ttk.Button(frm1a, text="PRINT", command=lambda: notImplementedInfo(self)).grid(column=1,row=1,padx=4, pady=4) 523 | self.newEntry = ttk.Button(frm1a, text="New Entry", command=lambda: insertJournalForm(self)).grid(column=2,row=1,padx=4, pady=4) 524 | ttk.Label(frm1, text="Transact\t Date\t\t Time\t\tDescription\t\t\tDebit: Account Amount\t Credit: Account Amount").grid(column=0, row=2, padx=4, pady=4,sticky='W') 525 | scrolW1 = 100; scrolH1 = 35 526 | self.scrolList1 = scrolledtext.ScrolledText(frm1, width=scrolW1, height=scrolH1, wrap=tk.WORD) 527 | self.scrolList1.grid(column=0, row=3, padx=4, pady=4, sticky='WE', columnspan=3) 528 | self.do_showJournal((0,0)) 529 | 530 | frm2 = ttk.Labelframe(self.tab2, text='General Ledger', width= 650, height=600) 531 | frm2.grid() 532 | ttk.Label(frm2, text="The General Ledger is the main book in the accounting system. It is accessed indirectly by the General Journal in the \ncourse of daily activity. As a result there will be found no menu or button actions that allow direct entry or edit of \nthe Ledger accounts.").grid(column=0, row=0, padx=4, pady=4,sticky='W') 533 | frm2a = ttk.Labelframe(frm2, width= 400, height=500) 534 | frm2a.grid(column=0,row=1) 535 | self.updateLedger = ttk.Button(frm2a, text="Update Display", command=lambda: self.do_showLedger(0)).grid(column=0,row=0,padx=4, pady=4) 536 | self.printLedger = ttk.Button(frm2a, text="PRINT", command=lambda: notImplementedInfo(self)).grid(column=1,row=0,padx=4, pady=4) 537 | self.newAccount = ttk.Button(frm2a, text="Show Another").grid(column=2,row=0,padx=4, pady=4) 538 | ttk.Label(frm2, text="Account \tTransaction\t Amount \tBalance \t\t").grid(column=0, row=2, padx=4, pady=4,sticky='W') 539 | scrolW1 = 80; scrolH1 = 35 540 | self.scrolList2 = scrolledtext.ScrolledText(frm2, width=scrolW1, height=scrolH1, wrap=tk.WORD) 541 | self.scrolList2.grid(column=0, row=3, padx=4, pady=4, sticky='WE', columnspan=3) 542 | self.do_showLedger(0) 543 | 544 | ## Set tab and contents for the Chart of Accounts 545 | frm3 = ttk.Labelframe(self.tab3, text='Chart of Accounts', width= 650, height=600) 546 | frm3.grid() 547 | ttk.Label(frm3, text="The Chart of Accounts is the organizing principle of the accounting system. It is accessed directly in defining the business\n activities for the of the enterprise.\n\n At system initialization the basic default accounts are automatically configured. Subsequently the chart is accessible as\n needed to set up new customer and supplier accounts for ongoing operation of the business").grid(column=0, row=0, padx=4, pady=4,sticky='W') 548 | frm3a = ttk.Labelframe(frm3, width= 400, height=450) 549 | frm3a.grid() 550 | self.updateChart = ttk.Button(frm3a, text="Update Display", command=lambda: self.do_showChart()).grid(column=0,row=0,padx=4, pady=4) 551 | self.printChart = ttk.Button(frm3a, text="PRINT", command=lambda: notImplementedInfo(self)).grid(column=1,row=0,padx=4, pady=4) 552 | self.newAccount = ttk.Button(frm3a, text="New Account", command=lambda: insertChartForm(self)).grid(column=2,row=0,padx=4, pady=4) 553 | ttk.Label(frm3, text="Account \t Name \t\t\t\t\t\t\tType\t\tBalance").grid(column=0, row=2, padx=4, pady=4,sticky='W') 554 | scrolW1 = 80; scrolH1 = 32 555 | self.scrolList3 = scrolledtext.ScrolledText(frm3, width=scrolW1, height=scrolH1, wrap=tk.WORD) 556 | self.scrolList3.grid(column=0, row=3, padx=4, pady=4, sticky='WE', columnspan=3) 557 | self.do_showChart() 558 | 559 | frm4 = ttk.Labelframe(self.tab4, text='Accounting Reports', width= 800, height=590) 560 | frm4.grid(padx=8, pady=4) 561 | self.reportctl = ttk.LabelFrame(frm4, text = "List of Reports") 562 | self.reportctl.grid(column=0, row=0, padx=8, pady=4, sticky='W') 563 | frm4b = ttk.Frame(frm4, width= 700, height=450) 564 | frm4b.grid(column=0, row=3) 565 | self.action_balanceSheet = ttk.Button(self.reportctl, text="Balance Sheet", command=lambda: self.do_balSheet()) 566 | self.action_balanceSheet.grid(column=0, row=0, padx=4, pady=6) 567 | self.action_journalReport = ttk.Button(self.reportctl, text="Journal Report", command=lambda: self.do_reptTransact(0)) 568 | self.action_journalReport.grid(column=1, row=0, padx=4, pady=6) 569 | self.action_journalReport = ttk.Button(self.reportctl, text="Single Transact", command=lambda: getTransact(self)) 570 | self.action_journalReport.grid(column=2, row=0, padx=4, pady=6) 571 | self.action_ledgerAccount = ttk.Button(self.reportctl, text="Ledger Account", command=lambda: fetchLedgerAccount(self)) 572 | self.action_ledgerAccount.grid(column=3, row=0, padx=4, pady=6) 573 | self.action_listAccounts = ttk.Button(self.reportctl, text="List Accounts", command=lambda: self.do_reptChart()) 574 | self.action_listAccounts.grid(column=4, row=0, padx=4, pady=6) 575 | self.action_revenueExpense = ttk.Button(self.reportctl, text="Revenue and Expenses", command=lambda: self.do_RevandExp()) 576 | self.action_revenueExpense.grid(column=5, row=0, padx=4, pady=6) 577 | self.action_viewPrint = ttk.Button(self.reportctl, text="Print", command=lambda: self.do_printCurrentView()) 578 | self.action_viewPrint.grid(column=6, row=0, padx=4, pady=6) 579 | self.reportWin = Canvas(frm4b, width=700, height=550,bg='#FFFFFF',scrollregion=(0,0,1000,2000)) 580 | hbar=Scrollbar(frm4b,orient=tk.HORIZONTAL) 581 | hbar.pack(side=tk.BOTTOM,fill=tk.X) 582 | hbar.config(command=self.reportWin.xview) 583 | vbar=Scrollbar(frm4b,orient=tk.VERTICAL) 584 | vbar.pack(side=tk.RIGHT,fill=tk.Y) 585 | vbar.config(command=self.reportWin.yview) 586 | self.reportWin.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set) 587 | self.reportWin.pack(side=tk.LEFT,expand=True,fill=tk.BOTH) 588 | 589 | self.reportWin.create_text(10,10, anchor=tk.NW, text="Select a view or report, it will display here") 590 | 591 | frm5 = ttk.Labelframe(self.tab5, text='Journal or Ledger Associated Note', width= 650, height=600) 592 | frm5.grid() 593 | self.memofields = ttk.LabelFrame(frm5, width=56) 594 | self.memofields.grid(column=0, row=1, padx=8, pady=4, sticky='W') 595 | self.lblmemoID = tk.Label(self.memofields, text="ID").grid(column=0, row=1) 596 | self.ememoID = tk.Entry(self.memofields, width=10) 597 | self.ememoID.grid(column=1, row=1, padx=5, pady=4, sticky='W') 598 | self.lblmemoTransact = tk.Label(self.memofields, text="Transact").grid(column=2, row=1) 599 | self.ememoTransact = tk.Entry(self.memofields, width=16) 600 | self.ememoTransact.grid(column=3, row=1, padx=5, pady=4, sticky='W') 601 | self.lblmemoDate = tk.Label(self.memofields, text="Date").grid(column=4, row=1) 602 | self.ememoDate = tk.Entry(self.memofields,width=18) 603 | self.ememoDate.grid(column=5, row=1, padx=5, pady=4, sticky='W') 604 | 605 | scrolW1 = 80; scrolH1 = 20 606 | self.scr_memo = scrolledtext.ScrolledText(frm5, width=scrolW1, height=scrolH1, wrap=tk.WORD) 607 | self.scr_memo.grid(column=0, row=7, padx=4, pady=4, sticky='WE', columnspan=3) 608 | 609 | self.memoctl = ttk.LabelFrame(frm5, width=56) 610 | self.memoctl.grid(column=0, row=0, padx=8, pady=4, sticky='W') 611 | self.action_clrmemo = ttk.Button(self.memoctl, text="NEW MEMO", command=lambda: self.newMemo()) 612 | self.action_clrmemo.grid(column=0, row=0, padx=4, pady=6) 613 | self.action_prtmemo = ttk.Button(self.memoctl, text="PRINT MEMO", command=lambda: notImplementedInfo(self)) 614 | self.action_prtmemo.grid(column=1, row=0, padx=4, pady=6) 615 | self.action_savememo = ttk.Button(self.memoctl, text="SAVE MEMO", command=lambda: self.saveMemo()) 616 | self.action_savememo.grid(column=3, row=0, padx=4, pady=6) 617 | self.action_loadmemo = ttk.Button(self.memoctl, text="GET ANOTHER", command=lambda: self.fetchMemo()) 618 | self.action_loadmemo.grid(column=4, row=0, padx=4, pady=6) 619 | 620 | frm6 = ttk.Labelframe(self.tab6, text='System Maintenance', width= 650, height=600) 621 | frm6.grid() 622 | self.action_sysSetup = ttk.Button(frm6, text=" NEW SET-UP ", command=lambda:AccountDB.createAccounts(self)).grid(column=0,row=0, padx=8, pady=4) 623 | self.action_sysPack = ttk.Button(frm6, text=" PACK DB ", command=lambda:AccountDB.packDatabase(self)).grid(column=1,row=0, padx=8, pady=4) 624 | self.action_sysSetup = ttk.Button(frm6, text="BACK-UP FILES", command=lambda:AccountDB.packDatabase(self)).grid(column=2,row=0, padx=8, pady=4) 625 | self.action_sysPack = ttk.Button(frm6, text=" RESTORE FILES", command=lambda:AccountDB.packDatabase(self)).grid(column=3,row=0, padx=8, pady=4) 626 | self.action_sysTrialBal = ttk.Button(frm6, text="COMPANY NAME", command=lambda:notImplementedInfo(self)).grid(column=0,row=1, padx=8, pady=4) 627 | self.action_sysTrialBal = ttk.Button(frm6, text="TRIAL BALANCE").grid(column=1,row=1, padx=8, pady=4) 628 | self.action_sysFiscalClose = ttk.Button(frm6, text="FISCAL CLOSE").grid(column=2,row=1, padx=8, pady=4) 629 | self.action_sysFiscalClose = ttk.Button(frm6, text="Unassigned").grid(column=3,row=1, padx=8, pady=4) 630 | 631 | # meubar created here -------------------------------------------- 632 | menuBar = Menu(self.win) 633 | self.win.config(menu=menuBar) 634 | # Add menu items 635 | # Add System Menu 636 | sysMenu = Menu(menuBar, tearoff=0) 637 | sysMenu.add_command(label="New Set-up", command=lambda: AccountDB.createAccounts()) 638 | sysMenu.add_command(label="Open") 639 | sysMenu.add_command(label="Save") 640 | sysMenu.add_command(label="Print", command=lambda: notImplementedInfo(self)) 641 | sysMenu.add_command(label="Back-up Database") 642 | sysMenu.add_command(label="Restore Database") 643 | sysMenu.add_separator() 644 | sysMenu.add_command(label="Exit", command=self._quit) 645 | menuBar.add_cascade(label="System", menu=sysMenu) 646 | 647 | # Add an Edit Menu 648 | editMenu = Menu(menuBar, tearoff=0) 649 | editMenu.add_command(label="Cut") 650 | editMenu.add_command(label="Copy") 651 | editMenu.add_command(label="Paste") 652 | editMenu.add_command(label="Delete") 653 | editMenu.add_command(label="Clear") 654 | editMenu.add_command(label="Select") 655 | editMenu.add_separator() 656 | editMenu.add_command(label="Options") 657 | menuBar.add_cascade(label="Edit", menu=editMenu) 658 | 659 | # Add an Data entry Menu 660 | entryMenu = Menu(menuBar, tearoff=0) 661 | entryMenu.add_command(label="Journal Entry", command=lambda: insertJournalForm(self)) 662 | entryMenu.add_command(label="New Account", command=lambda: insertChartForm(self)) 663 | entryMenu.add_command(label="Make Memo", command=lambda: insertMemoForm(self)) 664 | entryMenu.add_separator() 665 | entryMenu.add_command(label="Perform Trial Balance", command=lambda: self.do_trialBalance()) 666 | entryMenu.add_command(label="Perform End of Cycle", command=lambda: notImplementedEndofCycle(self)) 667 | menuBar.add_cascade(label="Activity", menu=entryMenu) 668 | 669 | # Add an Data entry Menu 670 | viewMenu = Menu(menuBar, tearoff=0) 671 | viewMenu.add_command(label="View Journal", command=lambda: self.do_reptTransact(0)) 672 | viewMenu.add_command(label="View Transaction", command=lambda: getTransact(self)) 673 | viewMenu.add_command(label="View Accounts", command=lambda: self.do_reptChart()) 674 | viewMenu.add_command(label="View Ledger", command=lambda: fetchLedgerAccount(self)) 675 | viewMenu.add_command(label="View a Memo", command=lambda: getTransact(self)) 676 | viewMenu.add_separator() 677 | viewMenu.add_command(label="Trial Balance", command=lambda: self.do_trialBalance()) 678 | viewMenu.add_command(label="End of Year") 679 | menuBar.add_cascade(label="View", menu=viewMenu) 680 | 681 | # Add an Edit Menu 682 | reportMenu = Menu(menuBar, tearoff=0) 683 | reportMenu.add_command(label="Balance Sheet", command=lambda: self.do_balSheet()) 684 | reportMenu.add_command(label="Ledger Account", command=lambda: fetchLedgerAccount(self)) 685 | reportMenu.add_command(label="Income Report", command=lambda: self.do_RevandExp()) 686 | reportMenu.add_command(label="Expense Report", command=lambda: self.do_RevandExp()) 687 | reportMenu.add_command(label="Payroll Report", command=lambda: notImplementedPayroll(self)) 688 | reportMenu.add_command(label="Inventory Report",command=lambda: notImplementedInventory(self)) 689 | menuBar.add_cascade(label="Reports", menu=reportMenu) 690 | 691 | # Add a Help Menu 692 | helpMenu = Menu(menuBar, tearoff=0) 693 | helpMenu.add_command(label="Context Help",command=lambda: notImplementedHelp(self)) 694 | helpMenu.add_command(label="Documentation",command=lambda: notImplementedHelp(self)) 695 | helpMenu.add_command(label="About", command=lambda: info(self)) 696 | menuBar.add_cascade(label="Help", menu=helpMenu) 697 | 698 | 699 | if __name__ == '__main__': 700 | ''' 701 | Main Method, OpenAccounting application start-up 702 | @summary: Starts the splash screen the when that is closed, the application window opens 703 | @warning: the application starts with what ever data is in the app directory, AccountDB.db 704 | If no such database files exist they can be created as new. 705 | @author: David York 11/7/2018 706 | @version: 0.20 707 | ''' 708 | spl = splash.splashScreen() 709 | asys = AccountingSystem() 710 | w = (2*asys.win.winfo_screenwidth())/3 711 | h = (3*asys.win.winfo_screenheight())/4 712 | asys.win.geometry("%dx%d+0+0" % (w, h)) 713 | ''' 714 | @attention: the following solution to center the app comes from stackoverflow: 715 | @author: idbrii Jan 17 '17 at 19:11, 716 | https://stackoverflow.com/questions/3352918/how-to-center-a-window-on-the-screen-in-tkinter 717 | @accessed: 11/7/2018 12:35PM 718 | ''' 719 | asys.win.eval('tk::PlaceWindow %s center' % asys.win.winfo_toplevel()) 720 | asys.win.mainloop() -------------------------------------------------------------------------------- /OpenAccounting.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE OpenAccounting; 2 | USE OpenAccounting; 3 | 4 | CREATE TABLE chart ( 5 | Account INTEGER PRIMARY KEY 6 | NOT NULL, 7 | Name STRING (50) NOT NULL, 8 | ActyType STRING (6) NOT NULL, 9 | Balance REAL 10 | ); 11 | 12 | CREATE TABLE journal ( 13 | [Transaction] INT PRIMARY KEY 14 | NOT NULL, 15 | Date DATETIME NOT NULL, 16 | Time DATETIME, 17 | Description STRING (40) NOT NULL, 18 | DebitAccount INTEGER (4) NOT NULL, 19 | DebitAmount REAL NOT NULL, 20 | CreditAccount INTEGER (4) NOT NULL, 21 | CreditAmount DECIMAL NOT NULL, 22 | Posted BOOLEAN NOT NULL 23 | DEFAULT (0) 24 | ); 25 | 26 | CREATE TABLE ledger ( 27 | Account INTEGER NOT NULL, 28 | Transact [INTEGER KEY] NOT NULL, 29 | Amount REAL NOT NULL, 30 | Balance REAL NOT NULL 31 | ); 32 | 33 | CREATE TABLE accountmemos ( 34 | MemoID INTEGER PRIMARY KEY 35 | UNIQUE 36 | NOT NULL, 37 | [Transaction] INTEGER REFERENCES journal ([Transaction]) 38 | NOT NULL, 39 | MemoDate DATETIME NOT NULL, 40 | Memo BLOB NOT NULL 41 | ); 42 | 43 | INSERT INTO chart VALUES ((100, "ASSETS", "DEBIT", 0), 44 | (120, "RECEIVABLES","DEBIT",0), 45 | (200, "LIABILITIES","CREDIT",0), 46 | (220, "PAYABLES","CREDIT",0), 47 | (300, "EQUITY","CREDIT",0), 48 | (400, "REVENUE","CREDIT",0), 49 | (500, "EXPENSES","DEBIT",0)); 50 | 51 | -------------------------------------------------------------------------------- /Printfile.ps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: Tk Canvas Widget 3 | %%Title: Window .!notebook.!frame4.!labelframe.!frame.!canvas 4 | %%CreationDate: Sun Nov 18 14:35:05 2018 5 | %%BoundingBox: 41 188 571 604 6 | %%Pages: 1 7 | %%DocumentData: Clean7Bit 8 | %%Orientation: Portrait 9 | %%DocumentNeededResources: font SegoeUi 10 | %%+ font Helvetica 11 | %%EndComments 12 | 13 | %%BeginProlog 14 | % This is a standard prolog for Postscript generated by Tk's canvas 15 | % widget. 16 | /CurrentEncoding [ 17 | /space/space/space/space/space/space/space/space 18 | /space/space/space/space/space/space/space/space 19 | /space/space/space/space/space/space/space/space 20 | /space/space/space/space/space/space/space/space 21 | /space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesingle 22 | /parenleft/parenright/asterisk/plus/comma/hyphen/period/slash 23 | /zero/one/two/three/four/five/six/seven 24 | /eight/nine/colon/semicolon/less/equal/greater/question 25 | /at/A/B/C/D/E/F/G 26 | /H/I/J/K/L/M/N/O 27 | /P/Q/R/S/T/U/V/W 28 | /X/Y/Z/bracketleft/backslash/bracketright/asciicircum/underscore 29 | /grave/a/b/c/d/e/f/g 30 | /h/i/j/k/l/m/n/o 31 | /p/q/r/s/t/u/v/w 32 | /x/y/z/braceleft/bar/braceright/asciitilde/space 33 | /space/space/space/space/space/space/space/space 34 | /space/space/space/space/space/space/space/space 35 | /space/space/space/space/space/space/space/space 36 | /space/space/space/space/space/space/space/space 37 | /space/exclamdown/cent/sterling/currency/yen/brokenbar/section 38 | /dieresis/copyright/ordfeminine/guillemotleft/logicalnot/hyphen/registered/macron 39 | /degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered 40 | /cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown 41 | /Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla 42 | /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis 43 | /Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply 44 | /Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls 45 | /agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla 46 | /egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis 47 | /eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide 48 | /oslash/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis 49 | ] def 50 | 50 dict begin 51 | /baseline 0 def 52 | /stipimage 0 def 53 | /height 0 def 54 | /justify 0 def 55 | /lineLength 0 def 56 | /spacing 0 def 57 | /stipple 0 def 58 | /strings 0 def 59 | /xoffset 0 def 60 | /yoffset 0 def 61 | /tmpstip null def 62 | /baselineSampler ( TXygqPZ) def 63 | baselineSampler 0 196 put 64 | /cstringshow {{ dup type /stringtype eq { show } { glyphshow } ifelse } forall } bind def 65 | /cstringwidth {0 exch 0 exch { dup type /stringtype eq { stringwidth } { currentfont /Encoding get exch 1 exch put (\001) stringwidth } ifelse exch 3 1 roll add 3 1 roll add exch } forall } bind def 66 | /ISOEncode {dup length dict begin {1 index /FID ne {def} {pop pop} ifelse} forall /Encoding CurrentEncoding def currentdict end /Temporary exch definefont } bind def 67 | /StrokeClip {{strokepath} stopped { (This Postscript printer gets limitcheck overflows when) = (stippling dashed lines; lines will be printed solid instead.) = [] 0 setdash strokepath} if clip } bind def 68 | /EvenPixels {dup 0 matrix currentmatrix dtransform dup mul exch dup mul add sqrt dup round dup 1 lt {pop 1} if exch div mul } bind def 69 | /StippleFill {/tmpstip 1 index def 1 EvenPixels dup scale pathbbox 4 2 roll 5 index div dup 0 lt {1 sub} if cvi 5 index mul 4 1 roll 6 index div dup 0 lt {1 sub} if cvi 6 index mul 3 2 roll 6 index exch { 2 index 5 index 3 index { gsave 1 index exch translate 5 index 5 index true matrix tmpstip imagemask grestore } for pop } for pop pop pop pop pop } bind def 70 | /AdjustColor {CL 2 lt { currentgray CL 0 eq { .5 lt {0} {1} ifelse } if setgray } if } bind def 71 | /DrawText {/stipple exch def /justify exch def /yoffset exch def /xoffset exch def /spacing exch def /strings exch def /lineLength 0 def strings { cstringwidth pop dup lineLength gt {/lineLength exch def} {pop} ifelse newpath } forall 0 0 moveto baselineSampler false charpath pathbbox dup /baseline exch def exch pop exch sub /height exch def pop newpath translate rotate lineLength xoffset mul strings length 1 sub spacing mul height add yoffset mul translate justify lineLength mul baseline neg translate strings { dup cstringwidth pop justify neg mul 0 moveto stipple { gsave /char (X) def { dup type /stringtype eq { { char 0 3 -1 roll put currentpoint gsave char true charpath clip StippleText grestore char stringwidth translate moveto } forall } { currentfont /Encoding get exch 1 exch put currentpoint gsave (\001) true charpath clip StippleText grestore (\001) stringwidth translate moveto } ifelse } forall grestore } {cstringshow} ifelse 0 spacing neg translate } forall } bind def 72 | /TkPhotoColor {gsave 32 dict begin /tinteger exch def /transparent 1 string def transparent 0 tinteger put /olddict exch def olddict /DataSource get dup type /filetype ne { olddict /DataSource 3 -1 roll 0 () /SubFileDecode filter put } { pop } ifelse /newdict olddict maxlength dict def olddict newdict copy pop /w newdict /Width get def /crpp newdict /Decode get length 2 idiv def /str w string def /pix w crpp mul string def /substrlen 2 w log 2 log div floor exp cvi def /substrs [ { substrlen string 0 1 substrlen 1 sub { 1 index exch tinteger put } for /substrlen substrlen 2 idiv def substrlen 0 eq {exit} if } loop ] def /h newdict /Height get def 1 w div 1 h div matrix scale olddict /ImageMatrix get exch matrix concatmatrix matrix invertmatrix concat newdict /Height 1 put newdict /DataSource pix put /mat [w 0 0 h 0 0] def newdict /ImageMatrix mat put 0 1 h 1 sub { mat 5 3 -1 roll neg put olddict /DataSource get str readstring pop pop /tail str def /x 0 def olddict /DataSource get pix readstring pop pop { tail transparent search dup /done exch not def {exch pop exch pop} if /w1 exch length def w1 0 ne { newdict /DataSource pix x crpp mul w1 crpp mul getinterval put newdict /Width w1 put mat 4 x neg put /x x w1 add def newdict image /tail tail w1 tail length w1 sub getinterval def } if done {exit} if tail substrs { anchorsearch {pop} if } forall /tail exch def tail length 0 eq {exit} if /x w tail length sub def } loop } for end grestore } bind def 73 | /TkPhotoMono {gsave 32 dict begin /dummyInteger exch def /olddict exch def olddict /DataSource get dup type /filetype ne { olddict /DataSource 3 -1 roll 0 () /SubFileDecode filter put } { pop } ifelse /newdict olddict maxlength dict def olddict newdict copy pop /w newdict /Width get def /pix w 7 add 8 idiv string def /h newdict /Height get def 1 w div 1 h div matrix scale olddict /ImageMatrix get exch matrix concatmatrix matrix invertmatrix concat newdict /Height 1 put newdict /DataSource pix put /mat [w 0 0 h 0 0] def newdict /ImageMatrix mat put 0 1 h 1 sub { mat 5 3 -1 roll neg put 0.000 0.000 0.000 setrgbcolor olddict /DataSource get pix readstring pop pop newdict /DataSource pix put newdict imagemask 1.000 1.000 1.000 setrgbcolor olddict /DataSource get pix readstring pop pop newdict /DataSource pix put newdict imagemask } for end grestore } bind def 74 | %%EndProlog 75 | %%BeginSetup 76 | /CL 2 def 77 | %%IncludeResource: font SegoeUi 78 | %%IncludeResource: font Helvetica 79 | %%EndSetup 80 | 81 | %%Page: 1 1 82 | save 83 | 306.0 396.0 translate 84 | 0.7508 0.7508 scale 85 | -350 -277 translate 86 | -2 554 moveto 702 554 lineto 702 0 lineto -2 0 lineto closepath clip newpath 87 | gsave 88 | /Helvetica findfont 12 scalefont ISOEncode setfont 89 | 0.000 0.000 0.000 setrgbcolor AdjustColor 90 | 0 335 542 [ 91 | [(THE BALANCE SHEET)] 92 | ] 18 -0.5 0.5 0 false DrawText 93 | grestore 94 | gsave 95 | /Helvetica findfont 12 scalefont ISOEncode setfont 96 | 0.000 0.000 1.000 setrgbcolor AdjustColor 97 | 0 150 516 [ 98 | [(ASSETS)] 99 | ] 18 -0 0 0 false DrawText 100 | grestore 101 | gsave 102 | /Helvetica findfont 12 scalefont ISOEncode setfont 103 | 0.000 0.000 1.000 setrgbcolor AdjustColor 104 | 0 460 516 [ 105 | [(LIABILITIES)] 106 | ] 18 -0 0 0 false DrawText 107 | grestore 108 | gsave 109 | /SegoeUi findfont 9 scalefont ISOEncode setfont 110 | 0.000 0.000 1.000 setrgbcolor AdjustColor 111 | 0 12 499 [ 112 | [(Account)] 113 | ] 15 -0 0 0 false DrawText 114 | grestore 115 | gsave 116 | /SegoeUi findfont 9 scalefont ISOEncode setfont 117 | 0.000 0.000 1.000 setrgbcolor AdjustColor 118 | 0 63 499 [ 119 | [(Name)] 120 | ] 15 -0 0 0 false DrawText 121 | grestore 122 | gsave 123 | /SegoeUi findfont 9 scalefont ISOEncode setfont 124 | 0.000 0.000 1.000 setrgbcolor AdjustColor 125 | 0 273 499 [ 126 | [(Balance)] 127 | ] 15 -0 0 0 false DrawText 128 | grestore 129 | gsave 130 | /SegoeUi findfont 9 scalefont ISOEncode setfont 131 | 0.000 0.000 1.000 setrgbcolor AdjustColor 132 | 0 344 499 [ 133 | [(Account)] 134 | ] 15 -0 0 0 false DrawText 135 | grestore 136 | gsave 137 | /SegoeUi findfont 9 scalefont ISOEncode setfont 138 | 0.000 0.000 1.000 setrgbcolor AdjustColor 139 | 0 395 499 [ 140 | [(Name)] 141 | ] 15 -0 0 0 false DrawText 142 | grestore 143 | gsave 144 | /SegoeUi findfont 9 scalefont ISOEncode setfont 145 | 0.000 0.000 1.000 setrgbcolor AdjustColor 146 | 0 605 499 [ 147 | [(Balance)] 148 | ] 15 -0 0 0 false DrawText 149 | grestore 150 | gsave 151 | 10 479 moveto 152 | 670 479 lineto 153 | 0 setlinecap 154 | 1 setlinejoin 155 | 1 setlinewidth 156 | [] 0 setdash 157 | 0.000 0.000 1.000 setrgbcolor AdjustColor 158 | stroke 159 | grestore 160 | gsave 161 | 10 476 moveto 162 | 670 476 lineto 163 | 0 setlinecap 164 | 1 setlinejoin 165 | 1 setlinewidth 166 | [] 0 setdash 167 | 0.000 0.000 1.000 setrgbcolor AdjustColor 168 | stroke 169 | grestore 170 | gsave 171 | /SegoeUi findfont 9 scalefont ISOEncode setfont 172 | 0.000 0.000 0.000 setrgbcolor AdjustColor 173 | 0 12 463 [ 174 | [(100 )] 175 | ] 15 -0 0 0 false DrawText 176 | grestore 177 | gsave 178 | /SegoeUi findfont 9 scalefont ISOEncode setfont 179 | 0.000 0.000 0.000 setrgbcolor AdjustColor 180 | 0 63 463 [ 181 | [(ASSETS )] 182 | ] 15 -0 0 0 false DrawText 183 | grestore 184 | gsave 185 | /SegoeUi findfont 9 scalefont ISOEncode setfont 186 | 0.000 0.000 0.000 setrgbcolor AdjustColor 187 | 0 273 463 [ 188 | [( 0.0)] 189 | ] 15 -0 0 0 false DrawText 190 | grestore 191 | gsave 192 | /SegoeUi findfont 9 scalefont ISOEncode setfont 193 | 0.000 0.000 0.000 setrgbcolor AdjustColor 194 | 0 12 450 [ 195 | [(110 )] 196 | ] 15 -0 0 0 false DrawText 197 | grestore 198 | gsave 199 | /SegoeUi findfont 9 scalefont ISOEncode setfont 200 | 0.000 0.000 0.000 setrgbcolor AdjustColor 201 | 0 63 450 [ 202 | [(BANK )] 203 | ] 15 -0 0 0 false DrawText 204 | grestore 205 | gsave 206 | /SegoeUi findfont 9 scalefont ISOEncode setfont 207 | 0.000 0.000 0.000 setrgbcolor AdjustColor 208 | 0 273 450 [ 209 | [( 903.19)] 210 | ] 15 -0 0 0 false DrawText 211 | grestore 212 | gsave 213 | /SegoeUi findfont 9 scalefont ISOEncode setfont 214 | 0.000 0.000 0.000 setrgbcolor AdjustColor 215 | 0 12 437 [ 216 | [(120 )] 217 | ] 15 -0 0 0 false DrawText 218 | grestore 219 | gsave 220 | /SegoeUi findfont 9 scalefont ISOEncode setfont 221 | 0.000 0.000 0.000 setrgbcolor AdjustColor 222 | 0 63 437 [ 223 | [(RECEIVABLES )] 224 | ] 15 -0 0 0 false DrawText 225 | grestore 226 | gsave 227 | /SegoeUi findfont 9 scalefont ISOEncode setfont 228 | 0.000 0.000 0.000 setrgbcolor AdjustColor 229 | 0 273 437 [ 230 | [( 100.0)] 231 | ] 15 -0 0 0 false DrawText 232 | grestore 233 | gsave 234 | 265 417 moveto 235 | 325 417 lineto 236 | 0 setlinecap 237 | 1 setlinejoin 238 | 1 setlinewidth 239 | [] 0 setdash 240 | 0.000 0.000 1.000 setrgbcolor AdjustColor 241 | stroke 242 | grestore 243 | gsave 244 | /SegoeUi findfont 9 scalefont ISOEncode setfont 245 | 0.000 0.000 0.000 setrgbcolor AdjustColor 246 | 0 265 404 [ 247 | [( 1003.19)] 248 | ] 15 -0 0 0 false DrawText 249 | grestore 250 | gsave 251 | /SegoeUi findfont 9 scalefont ISOEncode setfont 252 | 0.000 0.000 0.000 setrgbcolor AdjustColor 253 | 0 344 463 [ 254 | [(200 )] 255 | ] 15 -0 0 0 false DrawText 256 | grestore 257 | gsave 258 | /SegoeUi findfont 9 scalefont ISOEncode setfont 259 | 0.000 0.000 0.000 setrgbcolor AdjustColor 260 | 0 395 463 [ 261 | [(LIABILITIES )] 262 | ] 15 -0 0 0 false DrawText 263 | grestore 264 | gsave 265 | /SegoeUi findfont 9 scalefont ISOEncode setfont 266 | 0.000 0.000 0.000 setrgbcolor AdjustColor 267 | 0 605 463 [ 268 | [( 0.0)] 269 | ] 15 -0 0 0 false DrawText 270 | grestore 271 | gsave 272 | /SegoeUi findfont 9 scalefont ISOEncode setfont 273 | 0.000 0.000 0.000 setrgbcolor AdjustColor 274 | 0 344 450 [ 275 | [(220 )] 276 | ] 15 -0 0 0 false DrawText 277 | grestore 278 | gsave 279 | /SegoeUi findfont 9 scalefont ISOEncode setfont 280 | 0.000 0.000 0.000 setrgbcolor AdjustColor 281 | 0 395 450 [ 282 | [(PAYABLES )] 283 | ] 15 -0 0 0 false DrawText 284 | grestore 285 | gsave 286 | /SegoeUi findfont 9 scalefont ISOEncode setfont 287 | 0.000 0.000 0.000 setrgbcolor AdjustColor 288 | 0 605 450 [ 289 | [( 0.0)] 290 | ] 15 -0 0 0 false DrawText 291 | grestore 292 | gsave 293 | 598 430 moveto 294 | 663 430 lineto 295 | 0 setlinecap 296 | 1 setlinejoin 297 | 1 setlinewidth 298 | [] 0 setdash 299 | 0.000 0.000 1.000 setrgbcolor AdjustColor 300 | stroke 301 | grestore 302 | gsave 303 | /SegoeUi findfont 9 scalefont ISOEncode setfont 304 | 0.000 0.000 0.000 setrgbcolor AdjustColor 305 | 0 598 417 [ 306 | [( 0.0)] 307 | ] 15 -0 0 0 false DrawText 308 | grestore 309 | gsave 310 | /Helvetica findfont 12 scalefont ISOEncode setfont 311 | 0.000 0.000 1.000 setrgbcolor AdjustColor 312 | 0 467 384 [ 313 | [(Equity)] 314 | ] 18 -0 0 0 false DrawText 315 | grestore 316 | gsave 317 | 335 364 moveto 318 | 670 364 lineto 319 | 0 setlinecap 320 | 1 setlinejoin 321 | 1 setlinewidth 322 | [] 0 setdash 323 | 0.000 0.000 1.000 setrgbcolor AdjustColor 324 | stroke 325 | grestore 326 | gsave 327 | 335 361 moveto 328 | 670 361 lineto 329 | 0 setlinecap 330 | 1 setlinejoin 331 | 1 setlinewidth 332 | [] 0 setdash 333 | 0.000 0.000 1.000 setrgbcolor AdjustColor 334 | stroke 335 | grestore 336 | gsave 337 | /SegoeUi findfont 9 scalefont ISOEncode setfont 338 | 0.000 0.000 0.000 setrgbcolor AdjustColor 339 | 0 344 348 [ 340 | [(300 )] 341 | ] 15 -0 0 0 false DrawText 342 | grestore 343 | gsave 344 | /SegoeUi findfont 9 scalefont ISOEncode setfont 345 | 0.000 0.000 0.000 setrgbcolor AdjustColor 346 | 0 395 348 [ 347 | [(EQUITY )] 348 | ] 15 -0 0 0 false DrawText 349 | grestore 350 | gsave 351 | /SegoeUi findfont 9 scalefont ISOEncode setfont 352 | 0.000 0.000 0.000 setrgbcolor AdjustColor 353 | 0 605 348 [ 354 | [( 1000.0)] 355 | ] 15 -0 0 0 false DrawText 356 | grestore 357 | gsave 358 | /SegoeUi findfont 9 scalefont ISOEncode setfont 359 | 0.000 0.000 0.000 setrgbcolor AdjustColor 360 | 0 344 335 [ 361 | [(399 )] 362 | ] 15 -0 0 0 false DrawText 363 | grestore 364 | gsave 365 | /SegoeUi findfont 9 scalefont ISOEncode setfont 366 | 0.000 0.000 0.000 setrgbcolor AdjustColor 367 | 0 395 335 [ 368 | [(RETAINED EARNINGS )] 369 | ] 15 -0 0 0 false DrawText 370 | grestore 371 | gsave 372 | /SegoeUi findfont 9 scalefont ISOEncode setfont 373 | 0.000 0.000 0.000 setrgbcolor AdjustColor 374 | 0 605 335 [ 375 | [( 0.0)] 376 | ] 15 -0 0 0 false DrawText 377 | grestore 378 | gsave 379 | /SegoeUi findfont 9 scalefont ISOEncode setfont 380 | 0.000 0.000 0.000 setrgbcolor AdjustColor 381 | 0 344 322 [ 382 | [(399 )] 383 | ] 15 -0 0 0 false DrawText 384 | grestore 385 | gsave 386 | /SegoeUi findfont 9 scalefont ISOEncode setfont 387 | 0.000 0.000 0.000 setrgbcolor AdjustColor 388 | 0 395 322 [ 389 | [(Retained Earnings )] 390 | ] 15 -0 0 0 false DrawText 391 | grestore 392 | gsave 393 | /SegoeUi findfont 9 scalefont ISOEncode setfont 394 | 0.000 0.000 0.000 setrgbcolor AdjustColor 395 | 0 612 322 [ 396 | [( 3.19)] 397 | ] 15 -0 0 0 false DrawText 398 | grestore 399 | gsave 400 | 598 302 moveto 401 | 663 302 lineto 402 | 0 setlinecap 403 | 1 setlinejoin 404 | 1 setlinewidth 405 | [] 0 setdash 406 | 0.000 0.000 1.000 setrgbcolor AdjustColor 407 | stroke 408 | grestore 409 | gsave 410 | /SegoeUi findfont 9 scalefont ISOEncode setfont 411 | 0.000 0.000 0.000 setrgbcolor AdjustColor 412 | 0 598 289 [ 413 | [( 1003.19)] 414 | ] 15 -0 0 0 false DrawText 415 | grestore 416 | gsave 417 | 598 269 moveto 418 | 663 269 lineto 419 | 0 setlinecap 420 | 1 setlinejoin 421 | 1 setlinewidth 422 | [] 0 setdash 423 | 0.000 0.000 1.000 setrgbcolor AdjustColor 424 | stroke 425 | grestore 426 | gsave 427 | 10 226 moveto 428 | 670 226 lineto 429 | 0 setlinecap 430 | 1 setlinejoin 431 | 1 setlinewidth 432 | [] 0 setdash 433 | 0.000 0.000 1.000 setrgbcolor AdjustColor 434 | stroke 435 | grestore 436 | gsave 437 | 10 223 moveto 438 | 670 223 lineto 439 | 0 setlinecap 440 | 1 setlinejoin 441 | 1 setlinewidth 442 | [] 0 setdash 443 | 0.000 0.000 1.000 setrgbcolor AdjustColor 444 | stroke 445 | grestore 446 | gsave 447 | /SegoeUi findfont 9 scalefont ISOEncode setfont 448 | 0.000 0.000 0.000 setrgbcolor AdjustColor 449 | 0 265 210 [ 450 | [( 1003.19)] 451 | ] 15 -0 0 0 false DrawText 452 | grestore 453 | gsave 454 | /SegoeUi findfont 9 scalefont ISOEncode setfont 455 | 0.000 0.000 0.000 setrgbcolor AdjustColor 456 | 0 598 210 [ 457 | [( 1003.19)] 458 | ] 15 -0 0 0 false DrawText 459 | grestore 460 | restore showpage 461 | 462 | %%Trailer 463 | end 464 | %%EOF 465 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Accounting System](/images/Number_cruncherCr3.png) 2 | 3 | # An Accounting System 4 | 5 | A basic accounting system compliant with Generally Accepted Accounting Practice (or Principles) (GAAP). It is written in python 3.7 and SQLite3. 6 | 7 | ## Introduction 8 | 9 | There are lots of accounting information systems both proprietary and free and open source. I needed a project to exercise my python, sql and tkinter coding. The fact that it is a necessary review of my MBA curriculum doesn't hurt either after a long absence of my attention there. Finally, I think another open source offering is good and this is intened be a simple, straight forward, and transparent development offering of a accounting application. 10 | 11 | ## Principle 12 | The central core of accounting is considered to be the General Ledger. The instrument of first entry however is the General Journal. All other aspects of the system are either suecondary entry or subsidieary journals and reoprts. The well known Balance Sheet, the consolidated picture of an enterprise, is a report based on the General Ledger contents. 13 | 14 | The entry of business activities is first into the Journal, or day book. The items of the Journal are posted then to the Ledger. The double entry technique of accounting ensures that the credit accounts and the debit accounts remain in balance. If the credits always equal the the debits from the outset, and there is no other confounding entries allowed balance is retained. This is a kind of drudge task for which computer programs is eminently suited. 15 | 16 | It should be impossible to enter any accounting data except through the general journal. Accounts should be easily added but not removable unless never referenced in transactions (i.e There must never have been any entries for the account in the current fiscal cycle (year), meaning since the books were last closed out). 17 | 18 | ## Manual 19 | This readme is intended to serve as my development notes and discussion for the system. It helps me organize my thoughts and is the source document upon which (I hope. . .) helpful comments can be made. 20 | 21 | I have made a start on a user a Manual of sorts in the attaching wiki here at the [Manual Wiki](https://github.com/medmatix/Accounting-System/wiki) 22 | 23 | ## Approach 24 | Thus the Journal is where data enters the system and is the date-time record of activity. Data is then reorganized and transferred to the Ledger. The ledger is the categorized permanent record of the enterprise's functioning - taking in revenue and paying expenses to produce some value. The business operation is accountable to internal and external stakeholders. This accounting to stakeholders requires a data trail is maintained with integrity and be demonstable on demand. The audit trailas it is called must be immutable right to the reports needed to account to stakeholders. 25 | 26 | ![Accounting Relationships](AccountingSystemOrganization.png) 27 | 28 | The underlying categorization is defined by a set of accounts to which activities are attributated. This is kept in the Chart of Accounts. In modern American GAAP there are five essential accounts aways present as prinary accounts with variable kinds of subsidiary accounts providing a detailed breakdown of these five. These in the US, Canada and the UK are 1) Assets Account, 2) Liabilities Account, 3) Equity or Capital Accounts, 4) Revenue or Income Account and 5) Expense of Costs Account. There is usually some way to remove bad data or expired value from the system. This sixth account or Disposables (6) is how depreciated value, bad debts and perhaps erroneous data, can be removed from the system in a clearly demonstrable and auditable way. 29 | 30 | Often the write-offs are kept as an account in expenses as the result of their access is to expense a bad dept of similar business losses. It is more difficult to retrieve expensed write-offs in a future accounting cycle however so there are advantages to retained write-offs in their own ledger. 31 | 32 | DEBIT ACCOUNTS | NUMBERING | CREDIT ACCOUNTS | EXAMPLES 33 | ---------------|-----------|-----------------|----------------------------- 34 | Assets | 1xx | |101 Bank, 120 Receivables 35 | | | 2xx | Liabilities | 220 Payables 36 | | | 3xx | Equity | 301 Retained Earnings 37 | | | 4xx | Revenue | 420 Sales 38 | Expenses | 5xx | | 520 Mortgage 39 | | | 6xx | Disposables | 620 Write-offs (Bad Debts) 40 | 41 | To debit a Debit Account is to increase it, similarly for crediting an Credit Account. Crediting a Debit Account reduces it and vis-a-versa. Addition and subtraction of figures are implied and automatic. That is what the program must do in the database table. Thus to balance accounts all debits have a credit somewhere and all credits have and offsetting debit - double entry. 42 | 43 | 44 | ## Implementation: 45 | 46 | The Context of the development can be best represented by the list of imports. All non-standard libraries are pip installable. 47 | 48 | 49 | #### Imports 50 | 51 | - Standard library and third party imports 52 | - tkinter imports 53 | 54 | ``` 55 | import tkinter as tk 56 | from tkinter import ttk 57 | from tkinter import scrolledtext 58 | from tkinter import Menu 59 | from tkinter import messagebox as mBox 60 | from tkinter import simpledialog 61 | from tkinter import Scrollbar 62 | from tkinter import Canvas 63 | from tkinter import font 64 | ``` 65 | 66 | - other standard and third party imports 67 | - all 3rd party are pip installable 68 | 69 | ``` 70 | 71 | import sqlite3 72 | import datetime as dt 73 | import pytz 74 | import math as mt 75 | import numpy as np 76 | import sys 77 | ``` 78 | 79 | # Custom module imports 80 | from AccountDB import AccountDB 81 | from FormDialogs import insertJournalForm, insertChartForm, insertMemoForm 82 | from FormDialogs import ReportFormats 83 | from Tooltips import createToolTip, ToolTip 84 | from ReportPreps import TrialBalance 85 | Once the structure is decided on, the creation of the database, and it's tables behind the interface then the needed enabling code guided the development. The stored data must be clearly separated in development from the derived data. The latter are the reports and summaries also central to accounting, the balance sheet, revenue and expense reports and special or subsidiary journals which may play important parts in therereporting for some enterprises. The stored data which are SQLite Database and tables with keys and indexes are as follows. 86 | 87 | ### Tables of OpenAccounting.db: 88 | 89 | Ref | Accounting Reference | Table 90 | -----|----------------------|------------ 91 | 1|The Chart of Accounts | chart 92 | 2|The General Journal | journal 93 | 3|The General Ledger | ledger 94 | 4|The Accounting Annotation| accountmemos 95 | 96 | Eash table structure is shown by the SQL DDL statement that created it. 97 | 98 | #### Structure of chart Table 99 | 100 | ``` 101 | CREATE TABLE chart ( 102 | Account INTEGER PRIMARY KEY 103 | NOT NULL, 104 | Name STRING (50) NOT NULL, 105 | ActyType STRING (6) NOT NULL, 106 | Balance REAL 107 | ); 108 | ``` 109 | 110 | #### Structure of journal Table 111 | 112 | ``` 113 | 114 | CREATE TABLE journal ( 115 | [Transaction] INT PRIMARY KEY 116 | NOT NULL, 117 | Date DATETIME NOT NULL, 118 | Time DATETIME, 119 | Description STRING (40) NOT NULL, 120 | DebitAccount INTEGER (4) NOT NULL, 121 | DebitAmount REAL NOT NULL, 122 | CreditAccount INTEGER (4) NOT NULL, 123 | CreditAmount DECIMAL NOT NULL, 124 | Posted BOOLEAN NOT NULL 125 | DEFAULT (0) 126 | ); 127 | 128 | ``` 129 | 130 | #### Structure of ledger Table 131 | 132 | ``` 133 | 134 | CREATE TABLE ledger ( 135 | Account INTEGER NOT NULL, 136 | Transact [INTEGER KEY] NOT NULL, 137 | Amount REAL NOT NULL, 138 | Balance REAL NOT NULL 139 | ); 140 | 141 | ``` 142 | 143 | #### Structure of accountmemos Table 144 | 145 | ``` 146 | 147 | CREATE TABLE accountmemos ( 148 | MemoID INTEGER PRIMARY KEY 149 | UNIQUE 150 | NOT NULL, 151 | [Transaction] INTEGER REFERENCES journal ([Transaction]) 152 | NOT NULL, 153 | MemoDate DATETIME NOT NULL, 154 | Memo BLOB NOT NULL 155 | ); 156 | 157 | ``` 158 | In spite of the predominantly top-down approach evident, the functionality is determined by the data tables at the system's core. It is reasonable that a data heavy application be at it's heart data driven (here, bottom-up). The manipulation of the data is guided by the the needs of accounting, and the interface is key to that. 159 | 160 | Coding was M-V-C modular and this can be seen from the organization of the source. Starting with the interface, we present the screenshots that lead the bookkeeper to the accounting activities. 161 | 162 | ![Accounting System](/images/ScreenJournalTab.png) 163 | 164 | Discussing the 'Accounting Functions' specifically in light of the interface presented, consider the entry point to the system - the General Journal. The other accessible information is this accounting set-up reflected in the Chart of Accounts. 165 | 166 | ![Accounting System](/images/ScreenChartTab.png) 167 | 168 | The Ledger accounts set up via the Chart of Account are the records of where moneys are flowing from and to. The permanent account are those of the balance sheet, - in general terms the Assets and Liabilities. When the enterprise starts operating funds provided are the owner's equity, Accounts 300 and so on. The Core assets, 100 series accounts, are the Bank holding the capital itself and the incoming cash. Moneys owed to but not yet held by the compant are Accounts recevable (120). Obligations the company aquires are it's liabilities, 200 series accounts, principal among which is the Accounts Payable (220) as bills received but not yet paid. Other 100, 200 (and less so 300) series accounts may be used BUT the ones already mentioned are immutable and present in all accounting set-ups. At the start of use, the System provides these with the database tables created. 169 | 170 | Passing mention, for now, of the General Ledger is sufficient as this is not directly accessible, ever, but populated as Transactions in the Daily Journal entries. 171 | 172 | ![Accounting System](/images/ScreenLedgerTab.png) 173 | 174 | Later the General ledger is the source of the required and useful accounting reporting. 175 | 176 | ![Accounting System](/images/ScreenReportTab1.png) 177 | 178 | ![Accounting System](/images/ScreenReportTab2.png) 179 | 180 | The ability to annoted journal entries in more detail than the Transaction Descriptiong allows is desirable but not a canonical requirement per se in accounting systems. For our system the accounting memo interface expediteds this. 181 | 182 | ![Accounting System](/images/ScreenMemoTab.png) 183 | 184 | Lastly we consider the more general control required for any database systems and the consideration too of the face that accounting is a cyclical process focused on (rather artificial) fiscal periods of business activity. We need to be able to manage the system so a maintenance interface is provided. 185 | 186 | ![Accounting System](/images/ScreenMaintenanceTab.png) 187 | 188 | ==== 189 | 190 | Project Repository maintained by David York. 191 | 192 | Copyright Medmatix, David York 2018. 193 | 194 | License under: MIT license. 195 | -------------------------------------------------------------------------------- /ReportPreps.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 3, 2018 3 | 4 | @author: david 5 | ''' 6 | import tkinter as tk 7 | from tkinter import messagebox as mBox 8 | import math as mt 9 | import numpy as np 10 | import sqlite3 11 | from AccountDB import AccountDB 12 | 13 | class TrialBalance(object): 14 | ''' 15 | Class for Trial Balance Calculations 16 | ''' 17 | 18 | 19 | def __init__(self, params): 20 | ''' 21 | Constructor set up the trial balance 22 | ''' 23 | 24 | 25 | 26 | def getLedgerData(self): 27 | ''' 28 | ''' 29 | # Call database get ledger(all) 30 | # Get a current list of account numbers from chart 31 | accList = [] 32 | db = sqlite3.connect('OpenAccounting.db') 33 | cursor = db.cursor() 34 | cursor.execute("SELECT Account FROM chart ORDER BY Account") 35 | for row in cursor: 36 | accList.append(row[0]) 37 | db.close() 38 | balDict = {0:0.0} 39 | for account in accList: 40 | accTotal = 0 41 | ldgrAccount = AccountDB.getLedgerAccount(self, account) 42 | # sum transactions by account 43 | for row in ldgrAccount: 44 | accTotal = accTotal + row[2] 45 | #record balances and account numbers in balList 46 | balDict[account] = accTotal 47 | # display the balances in a dialog 48 | return(balDict) 49 | # mBox._show(title="Partial Trial Balance", message="Compare to Balances in Chart of Accounts \n\n"+tbal+"\n If they do not match the account is out of balance", _icon="", _type="") 50 | 51 | 52 | 53 | 54 | 55 | 56 | def trialBalance(self): 57 | ''' 58 | ''' 59 | # Get account transactions totals 60 | acctBalances = TrialBalance.getLedgerData(self) 61 | debitBalances = 0.0 62 | for x, y in acctBalances.items(): 63 | if (x >= 100 and x<= 199): 64 | debitBalances = debitBalances + y 65 | if (x >= 500 and x<= 599): 66 | debitBalances = debitBalances + y 67 | creditBalances = 0.0 68 | for x, y in acctBalances.items(): 69 | if (x >= 200 and x<= 499): 70 | creditBalances = creditBalances + y 71 | return (debitBalances, creditBalances) 72 | 73 | # Compare with Chart of Accounts balances for Chart integrity 74 | 75 | # Report Discrepancies 76 | 77 | -------------------------------------------------------------------------------- /Tooltips.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Data Science Calculator Tool tips 4 | @acknowledgement: Adapted on Wed Sep 29, 2018 based on a template from, 5 | Python GUI Programming Solutions, by Burkhardt Meyer Packt Publishing 6 | @author: David York 7 | @copyright: David York 2018 8 | @license: MIT license 9 | """ 10 | 11 | #====================== 12 | # imports 13 | #====================== 14 | import tkinter as tk 15 | 16 | class ToolTip(object): 17 | def __init__(self, widget): 18 | self.widget = widget 19 | self.tipwindow = None 20 | self.id = None 21 | self.col = self.row = 0 # col = x value, row = y value 22 | 23 | def showtip(self, text): 24 | "Display text in tooltip window" 25 | self.text = text 26 | if self.tipwindow or not self.text: 27 | return 28 | col, row, _ccol, crow = self.widget.bbox("insert") 29 | col = col + self.widget.winfo_rootx() + 27 30 | row = row + crow + self.widget.winfo_rooty() +27 31 | self.tipwindow = tw = tk.Toplevel(self.widget) 32 | tw.wm_overrideredirect(1) 33 | tw.wm_geometry("+%d+%d" % (col, row)) 34 | 35 | label = tk.Label(tw, text=self.text, justify=tk.LEFT, 36 | background="#ffffe0", relief=tk.SOLID, borderwidth=1, 37 | font=("tahoma", "8", "normal")) 38 | label.pack(ipadx=1) 39 | 40 | def hidetip(self): 41 | tw = self.tipwindow 42 | self.tipwindow = None 43 | if tw: 44 | tw.destroy() 45 | 46 | #=================================================================== 47 | def createToolTip(widget, text): 48 | toolTip = ToolTip(widget) 49 | def enter(event): 50 | toolTip.showtip(text) 51 | def leave(event): 52 | toolTip.hidetip() 53 | widget.bind('', enter) 54 | widget.bind('', leave) 55 | -------------------------------------------------------------------------------- /__pycache__/AccountDB.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/__pycache__/AccountDB.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/FormDialogs.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/__pycache__/FormDialogs.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/ReportPreps.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/__pycache__/ReportPreps.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/Tooltips.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/__pycache__/Tooltips.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/splash.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/__pycache__/splash.cpython-37.pyc -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /cruncherCr3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/cruncherCr3.gif -------------------------------------------------------------------------------- /cruncherCr3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/cruncherCr3.jpg -------------------------------------------------------------------------------- /cruncherCr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/cruncherCr3.png -------------------------------------------------------------------------------- /images/AccountingSystemOrganization.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/AccountingSystemOrganization.dia -------------------------------------------------------------------------------- /images/AccountingSystemOrganization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/AccountingSystemOrganization.png -------------------------------------------------------------------------------- /images/ChartAccts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ChartAccts.png -------------------------------------------------------------------------------- /images/Journal1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal1.png -------------------------------------------------------------------------------- /images/Journal1.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal1.xcf -------------------------------------------------------------------------------- /images/Journal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal2.png -------------------------------------------------------------------------------- /images/Journal2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal2.xcf -------------------------------------------------------------------------------- /images/Journal3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal3.png -------------------------------------------------------------------------------- /images/Journal3.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal3.xcf -------------------------------------------------------------------------------- /images/Journal4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal4.png -------------------------------------------------------------------------------- /images/Journal4.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal4.xcf -------------------------------------------------------------------------------- /images/Journal5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal5.png -------------------------------------------------------------------------------- /images/Journal5.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal5.xcf -------------------------------------------------------------------------------- /images/Journal6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal6.png -------------------------------------------------------------------------------- /images/Journal6.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal6.xcf -------------------------------------------------------------------------------- /images/Journal7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal7.png -------------------------------------------------------------------------------- /images/Journal7.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Journal7.xcf -------------------------------------------------------------------------------- /images/Ldgeracct1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Ldgeracct1.png -------------------------------------------------------------------------------- /images/Ldgeracct1.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Ldgeracct1.xcf -------------------------------------------------------------------------------- /images/Number_cruncherCr3.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Number_cruncherCr3.ico -------------------------------------------------------------------------------- /images/Number_cruncherCr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/Number_cruncherCr3.png -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | # Images and Graphics 2 | 3 | All images are copyright Medmatix Analytics and David York unless explicitly credited here to another source. Licensing is under the MIT License unless otherwise stated. 4 | -------------------------------------------------------------------------------- /images/RevnExp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/RevnExp1.png -------------------------------------------------------------------------------- /images/ScreenChartTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenChartTab.png -------------------------------------------------------------------------------- /images/ScreenChartTab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenChartTab2.png -------------------------------------------------------------------------------- /images/ScreenJournalTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenJournalTab.png -------------------------------------------------------------------------------- /images/ScreenLedgerTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenLedgerTab.png -------------------------------------------------------------------------------- /images/ScreenMaintenanceTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenMaintenanceTab.png -------------------------------------------------------------------------------- /images/ScreenMemoTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenMemoTab.png -------------------------------------------------------------------------------- /images/ScreenReportTab1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenReportTab1.png -------------------------------------------------------------------------------- /images/ScreenReportTab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/ScreenReportTab2.png -------------------------------------------------------------------------------- /images/balancesheet1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/balancesheet1.png -------------------------------------------------------------------------------- /images/cruncherCr3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/cruncherCr3.gif -------------------------------------------------------------------------------- /images/medmatix.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/medmatix.ico -------------------------------------------------------------------------------- /images/medmatix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/medmatix.png -------------------------------------------------------------------------------- /images/medmatix_tilt.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/medmatix_tilt.ico -------------------------------------------------------------------------------- /images/medmatix_tilt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/medmatix_tilt.png -------------------------------------------------------------------------------- /images/trialbalance1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/images/trialbalance1.png -------------------------------------------------------------------------------- /medmatix_tilt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medmatix/Accounting-System/2ab3dbafebe5818f55fc36f172d5938a68cf1d0c/medmatix_tilt.png -------------------------------------------------------------------------------- /splash.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 6, 2018 3 | @summary: The module contains the code needed to build and present the splash screen at start-up 4 | @author: david york 5 | ''' 6 | 7 | # ############################################# 8 | # imports for the splash code functionality 9 | # ############################################# 10 | import os 11 | import pathlib 12 | import tkinter as tk 13 | from tkinter import ttk 14 | from tkinter import Canvas 15 | from tkinter import font 16 | import PIL 17 | from PIL import Image, ImageTk 18 | import time 19 | 20 | class splashScreen(): 21 | ''' 22 | The splashScreen class 23 | ''' 24 | 25 | def __init__(self): 26 | ''' 27 | splashScreen constructor 28 | ''' 29 | 30 | self.splash() 31 | 32 | 33 | 34 | 35 | #self.splashfrm.mainloop() 36 | 37 | def splash(self): 38 | ''' 39 | The splash interface code 40 | ''' 41 | self.root = tk.Tk() 42 | 43 | #root.title('Splash') 44 | #root.wm_attributes('-fullscreen','true') 45 | #eliminate the titlebar 46 | self.root.overrideredirect(1) 47 | 48 | image = Image.open("medmatix_tilt.png") 49 | f = tk.Frame(self.root, highlightthickness=0 ).grid() 50 | canvas=Canvas(f, height=500, width=500, highlightthickness=0, background='SteelBlue3') 51 | basewidth = 220 52 | wpercent = (basewidth / float(image.size[0])) 53 | hsize = int((float(image.size[1]) * float(wpercent))) 54 | image = image.resize((basewidth, hsize), PIL.Image.ANTIALIAS) 55 | photo = ImageTk.PhotoImage(image) 56 | canvas.create_image(250,250, image=photo) 57 | canvas.create_text(250,161, text='Welcome to', font=('Helvetica', '18', 'italic')) 58 | canvas.create_text(250,200, text='Open Accounting', font=('Helvetica', '32', 'bold italic' )) 59 | canvas.create_text(250,239, text='by Medmatix Analytics', font=('Helvetica', '18', 'italic')) 60 | canvas.create_text(250,470, text='(c)copyright: D A York, Medmatix 2018', font=('Times', '9', 'italic'), fill='white') 61 | canvas.grid() #.pack(side = tk.TOP, expand=True, fill=tk.BOTH) 62 | f2 = f = tk.Frame(self.root, highlightthickness=0, width=500) 63 | f2.configure(bg='SteelBlue3') 64 | f2.grid() 65 | self.btn1 = ttk.Button(f2, text="OK", command=lambda: self.on_OKkill()).grid(column=0,row=2,padx=212, pady=4) 66 | # Acknowledgement of source: 67 | # Note the functionality centering the splash window was found in a discussion on stackoverflow blog: 68 | # https://stackoverflow.com/questions/3352918/how-to-center-a-window-on-the-screen-in-tkinter 69 | self.root.eval('tk::PlaceWindow %s center' % self.root.winfo_toplevel()) 70 | self.root.mainloop() 71 | 72 | def on_OKkill(self): 73 | self.root.quit() # stops mainloop 74 | self.root.destroy() 75 | 76 | 77 | 78 | if __name__ == '__main__': 79 | spl = splashScreen() -------------------------------------------------------------------------------- /testcode.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 6, 2018 3 | 4 | @author: david 5 | ''' 6 | ''' 7 | Created on Sep 30, 2018 8 | 9 | @author: david 10 | ''' 11 | import os 12 | import pathlib 13 | import tkinter as tk 14 | from tkinter import ttk 15 | from tkinter import Canvas 16 | from tkinter import font 17 | import PIL 18 | from PIL import Image, ImageTk 19 | 20 | class splashScreen(): 21 | 22 | def __init__(self): 23 | ''' 24 | Splash constructor 25 | ''' 26 | 27 | self.splash() 28 | 29 | 30 | 31 | 32 | #self.splashfrm.mainloop() 33 | 34 | def splash(self): 35 | ''' 36 | ''' 37 | root = tk.Tk() 38 | #root.title('Splash') 39 | #root.wm_attributes('-fullscreen','true') 40 | #eliminate the titlebar 41 | root.overrideredirect(1) 42 | 43 | image = Image.open("medmatix_tilt.png") 44 | canvas=Canvas(root, height=500, width=500, background='SteelBlue3') 45 | basewidth = 220 46 | wpercent = (basewidth / float(image.size[0])) 47 | hsize = int((float(image.size[1]) * float(wpercent))) 48 | image = image.resize((basewidth, hsize), PIL.Image.ANTIALIAS) 49 | photo = ImageTk.PhotoImage(image) 50 | canvas.create_image(250,250, image=photo) 51 | canvas.create_text(250,161, text='Welcome to', font=('Helvetica', '18', 'italic')) 52 | canvas.create_text(250,200, text='Open Accounting', font=('Helvetica', '32', 'bold italic' )) 53 | canvas.create_text(250,239, text='by Medmatix Analytics', font=('Helvetica', '18', 'italic')) 54 | 55 | canvas.grid() #.pack(side = tk.TOP, expand=True, fill=tk.BOTH) 56 | 57 | root.mainloop() 58 | 59 | 60 | if __name__ == '__main__': 61 | spl = splashScreen() 62 | 63 | 64 | 65 | #=============================================================================== 66 | # root=tk.Tk() 67 | # image = Image.open("medmatix_tilt.png") 68 | # canvas=Canvas(root, height=200, width=200) 69 | # basewidth = 150 70 | # wpercent = (basewidth / float(image.size[0])) 71 | # hsize = int((float(image.size[1]) * float(wpercent))) 72 | # image = image.resize((basewidth, hsize), PIL.Image.ANTIALIAS) 73 | # photo = ImageTk.PhotoImage(image) 74 | # item4 = canvas.create_image(100, 80, image=photo) 75 | # 76 | # canvas.pack(side = tk.TOP, expand=True, fill=tk.BOTH) 77 | # root.mainloop() 78 | #=============================================================================== --------------------------------------------------------------------------------