I've been using GenuTax since 2013 to file my Canadian tax return. It's provided free and does the job nicely, but it misses one feature. Up north we can do pension splitting. That means I can offload some of my pension income onto my spouse, hopefully putting us in a lower tax bracket. Figuring out the optimum split is a chore. I have to type in a value then add up the separate values for me and my spouse to see if the combined number is better or worse than my previous guess. It's tedious and unnecessary so this yoar I automated it.
Using AutoItx and vbScript I threw together some code that does the grunt work for me. I'm making it available to all. Just copy the below code and save it into PensionSplitCalc.vbs
. Remember to set your default script engine to cscript.exe
(you need do this only once) by
cscript //h:cscript //nologo //s
.
''#region Header '
'' '
'' Name: '
'' '
'' PensionSplitCalc.vbs '
'' '
'' Description: '
'' '
'' Automates the pension split calculation in GenuTax Standard. '
'' '
'' Notes: '
'' '
'' If you are using GenuTax to file a joint tax return then you are likely using '
'' pension splitting. GenuTax, unfortunatly (at this date) does not pick a value '
'' that maximizes your return (or minimizes your payment). This script, using '
'' AutoItX, automates that task. This script requires that you install AutoItX '
'' which can be downloaded and used at no cost from '
'' https://www.autoitscript.com/site/autoit/downloads/ '
'' '
'' Contact: '
'' '
'' The easiest way to contact me with feedback is through www.daniweb.com where I '
'' post as "Reverend Jim". If you post a thread in the Programming forum With '
'' GenuTax and/or vbScript in the title I will be sure to see it. '
'' '
'' Usage: '
'' '
'' Make sure you have GenuTax running, and that you are on the Pension Split '
'' page. Run the script from a Windows command shell. It will start with a 50-50 '
'' split and recalculate the amounts owing/refunded for various split amounts in '
'' increasing finer steps until it has determined what split gives you the maximum '
'' benefit. '
'' '
'' Licensing: '
'' '
'' This software is released into the public domain and may be used and modified '
'' as needed by anyone. '
'' '
'' Audit: '
'' '
'' 2018-03-01 rj original code '
'' '
''#endregion '
Set aut = WScript.CreateObject("AutoItX3.Control")
aut.Opt "WinTitleMatchMode", 1
'This string appears at the start of the title bar text for GenuTax Standard
Const GENUTAX = "GenuTax"
'These consts are the ControlID numbers from AutoItSpy for GenuTax controls. Note that '
'it is possible that these numbers may change with future revisions of GenuTax (a new '
'version is released for each new tax year). '
'SPLITV = Pension Split data entry field '
'HEADER1 = Person 1 Refund/Balance owing label'
'PERSON1 = Person 1 Refund/Balance owing value'
'NAME1 = Person 1 name '
'HEADER2 = Person 2 Refund/Balance owing label'
'PERSON2 = Person 2 Refund/Balance owing value'
'NAME2 = Person 2 name '
Const SPLITV = "[Class:WindowsForms10.Window.8.app.0.13965fa_r9_ad1; INSTANCE:4]"
Const HEADER1 = "[NAME:lblResultTextA]"
Const PERSON1 = "[NAME:lblResultA]"
Const NAME1 = "[NAME:lblNameA]"
Const HEADER2 = "[NAME:lblResultTextB]"
Const PERSON2 = "[NAME:lblResultB]"
Const NAME2 = "[NAME:lblNameB]"
'Check if GenuTax is running and on the Pension Split page
If aut.WinExists(GENUTAX) = 0 Then
WScript.Echo GENUTAX,"does not appear to be running"
WScript.Quit
End If
text = aut.WinGetText(GENUTAX)
If Instr(text,"Elected Split-Pension Amount") = 0 Then
WScript.Echo "You are not on the Pension Split page"
WScript.Quit
End If
'Get the names of the joint filers
p1name = lPad(aut.ControlGetText(GENUTAX,"",NAME1),13," ")
p2name = lPad(aut.ControlGetText(GENUTAX,"",NAME2),13," ")
'Optimize. Start with half of the allowable pension split value and optimize
'with smaller increments and range until we get the best balance. The first
'optimize will be over the entire range.
SetSplit(GetMaxSplit() * 0.5)
minsplit = 0
maxsplit = GetMaxSplit()
optimum = GetOptimumSplit(minsplit,maxsplit,1000)
optimum = GetOptimumSplit(optimum-1000,optimum+1000,100)
optimum = GetOptimumSplit(optimum-100,optimum+100,10)
optimum = GetOptimumSplit(optimum-10,optimum+10,1)
results = GetResults()
WScript.Echo ""
WScript.Echo "Optimum Split:",FormatCurrency(GetSplit(),2,True,True,True)
WScript.Echo p1name & ":",FormatCurrency(results(0),2,True,True,True)
WScript.Echo p2name & ":",FormatCurrency(results(1),2,True,True,True)
WScript.Echo " Combined:",FormatCurrency(results(2),2,True,True,True)
''Try the newsplit value for a better return. Return True if improved '
Function Try (newsplit)
Dim currsplit 'Current split value in case we have to restore '
Dim results 'results for calculation with new split value '
Dim currbal 'balance from calculation with new split value '
currsplit = GetSplit() 'save the current split value if no improvement '
results = GetResults() 'get the current results to compare with the new results'
currbal = results(2) 'save the current balance '
'Set the new (test) value and get the results
SetSplit(newsplit)
results = GetResults()
'If the new split results in a better return then leave it, otherwise restore
'the previous split. Return True if we got a better result with the new Split
If results(2) > currbal Then
ShowResults GetSplit(),results
Try = True
Else
SetSplit(currsplit)
results = GetResults()
Try = False
End If
End Function
''Display the results for the current split calculation '
Sub ShowResults(psplit,results)
WScript.StdOut.Write "Split: " & lPad(FormatCurrency(psplit,2,True,True,True),12," ")
WScript.StdOut.Write " " & lPad(FormatCurrency(results(0),True,True),12," ")
WScript.StdOut.Write " " & lPad(FormatCurrency(results(1),2,True,True,True),12," ")
WScript.StdOut.Write " " & lPad(FormatCurrency(results(2),2,True,True,True),12," ")
Wscript.Stdout.WriteLine ""
End Sub
''Try stepsize increments within [minsplit,maxsplit] and look for better results '
Function GetOptimumSplit(minsplit, maxsplit, stepsize)
Do: Loop While Try(GetSplit() + stepsize) And GetSplit() <= maxsplit
Do: Loop While Try(GetSplit() - stepsize) And GetSplit() >= minsplit
End Function
''Get the currently displayed pension split value '
Function GetSplit()
GetSplit = aut.ControlGetText(GENUTAX,"",SPLITV)
GetSplit = GetCurrency(GetSplit)
End Function
''Set the pension split to a new value. Return the new value. '
Function SetSplit(newsplit)
aut.ControlFocus GENUTAX,"",SPLITV
aut.ControlSend GENUTAX,"",SPLITV,cInt(newsplit) & "{ENTER}"
SetSplit = GetSplit()
End Function
''Get the maximum allowable pension split value '
Function GetMaxSplit()
Dim text: text = Split(aut.WinGetText(GENUTAX,""))
Dim i
'If we get the entire text in the window and split it on blanks, the
'vslue we want is the token following the token "exceeding".
For i = 0 To ubound(text)
If text(i) = "exceeding" Then
GetMaxSplit = GetCurrency(text(i+1))
Exit function
End If
Next
GetMaxSplit = 0
End Function
''Convert a currency string to a double numeric (ignore non-numeric chars) '
Function GetCurrency(str)
Dim i, ch
GetCurrency = ""
For i = 1 To Len(str)
ch = Mid(str,i,1)
If IsNumeric(ch) or ch = "." Then
GetCurrency = GetCurrency & ch
End If
Next
On Error Resume Next
GetCurrency = CDbl(GetCurrency)
If Err.Number <> 0 Then
WScript.Echo "Error in GetCurrency:",Err.Number,Err.Description
WScript.Echo " str = '" & str & "'"
WScript.Echo " trying to convert '" & GetCurrency & "' to Double"
Err.Clear
End If
On Error GoTo 0
End Function
''Get the results of the last split - return total owing or refundable '
Function GetResults()
Dim h1: h1 = aut.ControlGetText(GENUTAX,"",HEADER1)
Dim p1: p1 = GetCurrency(aut.ControlGetText(GENUTAX,"",PERSON1))
Dim h2: h2 = aut.ControlGetText(GENUTAX,"",HEADER2)
Dim p2: p2 = GetCurrency(aut.ControlGetText(GENUTAX,"",PERSON2))
If h1 = "Balance owing:" Then p1 = -p1
If h2 = "Balance owing:" Then p2 = -p2
GetResults = Array(p1, p2, p1 + p2)
End Function
''Return <val> as a string padded on the left with <padch> to a max width of <width> '
Function Lpad (val, width, padch)
Lpad = Right(Repeat(padch,width-len(cStr(val))) & val,width)
End Function
''Return <ntimes> repetitions of <str> '
Function Repeat (str, ntimes)
Repeat = ""
Dim i: For i = 1 To ntimes
Repeat = Repeat & str
Next
End Function