-
Notifications
You must be signed in to change notification settings - Fork 0
/
withgui.go
302 lines (261 loc) · 10 KB
/
withgui.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/leekchan/accounting"
"github.com/shopspring/decimal"
)
/* Define a structure for all variables needed
to hold computational results and inputs */
type TaxInputs struct {
MonthlyIncome decimal.Decimal
TaxableIncome decimal.Decimal
Tax decimal.Decimal
NetPayAfterTax decimal.Decimal
SSSContributions decimal.Decimal
PhilHealthContributions decimal.Decimal
PagIbigContributions decimal.Decimal
TotalContributions decimal.Decimal
TotalDeductions decimal.Decimal
NetPayAfterDeductions decimal.Decimal
}
func main() {
/* Create a new application along
with its output and input widgets */
myApp := app.New()
// Input Widgets
incomeEntry := widget.NewEntry()
// Output Widgets
taxLabel := widget.NewLabel("")
taxableIncomeLabel := widget.NewLabel("")
sssContributionsLabel := widget.NewLabel("")
pagibigContributionsLabel := widget.NewLabel("")
philhealthContributionsLabel := widget.NewLabel("")
totalContributionsLabel := widget.NewLabel("")
totalDeductionsLabel := widget.NewLabel("")
netPayAfterDeductionsLabel := widget.NewLabel("")
// Create input row for user input
incomeEntry.SetPlaceHolder("Enter your monthly income")
// Create the calculate button
calculateBtn := widget.NewButton("Calculate", func() {
// Parse user input
incomeStr := incomeEntry.Text
// Convert input to decimal format
monthlyIncome, err := decimal.NewFromString(incomeStr)
// Function to cross check for invalid inputs
if err != nil || monthlyIncome.LessThan(decimal.Zero) {
widget.NewLabel("Invalid monthly income input")
return
}
// Calling functions to calculate for monthly contributions
sssContributions := calculateSSSContributions(monthlyIncome)
philhealthContributions := calculatePhilHealthContributions(monthlyIncome)
pagibigContributions := calculatePagIbigContributions(monthlyIncome)
totalContributions := decimal.Sum(sssContributions,
philhealthContributions,
pagibigContributions)
// Calling functions to calculate for tax deductions
taxableIncome := monthlyIncome.Sub(totalContributions)
tax := calculateTax(taxableIncome)
totalDeductions := totalContributions.Add(tax)
netPayAfterDeductions := monthlyIncome.Sub(totalDeductions)
// Create a display struct to hold the user input and calculated values
inputs := TaxInputs{
MonthlyIncome: monthlyIncome,
TaxableIncome: taxableIncome,
Tax: tax,
SSSContributions: sssContributions,
PhilHealthContributions: philhealthContributions,
PagIbigContributions: pagibigContributions,
TotalContributions: totalContributions,
TotalDeductions: totalDeductions,
NetPayAfterDeductions: netPayAfterDeductions,
}
/* Display the results of computation in Peso format
with 2 digit precision for decimal points */
ac := accounting.Accounting{Symbol: "₱ ", Precision: 2}
taxLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.Tax)))
taxableIncomeLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.TaxableIncome)))
sssContributionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.SSSContributions)))
philhealthContributionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.PhilHealthContributions)))
pagibigContributionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.PagIbigContributions)))
totalContributionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.TotalContributions)))
totalDeductionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.TotalDeductions)))
netPayAfterDeductionsLabel.SetText(fmt.Sprintf(ac.FormatMoney(inputs.NetPayAfterDeductions)))
})
/* Container for tax computations (i.e., taxable income and income tax) */
taxContainer := container.NewVBox(
widget.NewLabelWithStyle("Tax Computation",
fyne.TextAlignLeading,
fyne.TextStyle{Bold: true}),
container.NewHBox(
widget.NewLabel("Taxable Income\t\t"),
taxableIncomeLabel,
),
container.NewHBox(
widget.NewLabel("Income Tax\t\t\t"),
taxLabel,
),
layout.NewSpacer())
/* Container for monthly contributions computations (i.e., SSS, PagIbig and Philheath) */
contribContainer := container.NewVBox(
widget.NewLabelWithStyle("Monthly Contributions",
fyne.TextAlignLeading,
fyne.TextStyle{Bold: true}),
container.NewHBox(
widget.NewLabel("SSS Contribution\t\t"),
sssContributionsLabel,
),
container.NewHBox(
widget.NewLabel("Philheath Contribution\t"),
philhealthContributionsLabel,
),
container.NewHBox(
widget.NewLabel("PagIbig Contribution\t"),
pagibigContributionsLabel,
),
container.NewHBox(
widget.NewLabel("Total Contribution\t\t"),
totalContributionsLabel,
))
/* Container for display of final computations on net pay and total deductions */
finalComputations := container.NewVBox(
widget.NewLabelWithStyle("Total Deductions",
fyne.TextAlignLeading,
fyne.TextStyle{Bold: true}),
totalDeductionsLabel,
widget.NewLabelWithStyle("Net Pay After Deductions",
fyne.TextAlignLeading,
fyne.TextStyle{Bold: true}),
netPayAfterDeductionsLabel,
)
/* Container for display of prompt box for user's input on monthly contribution */
content := container.New(layout.NewVBoxLayout(),
container.NewVBox(
widget.NewLabelWithStyle("Monthly Income",
fyne.TextAlignLeading,
fyne.TextStyle{Bold: true}),
incomeEntry,
calculateBtn,
layout.NewSpacer(),
),
/* Resizing tax and contributions container through Grid Wrap Layout Manager */
container.New(layout.NewGridWrapLayout(fyne.NewSize(300, 200)),
contribContainer,
taxContainer),
container.New(layout.NewGridWrapLayout(fyne.NewSize(150, 150)),
finalComputations),
)
// Create a new window for the desktop application
myWindow := myApp.NewWindow("Tax Calculator")
myApp.Settings().SetTheme(theme.LightTheme())
myWindow.SetContent(content)
myWindow.Resize(fyne.NewSize(600, 600))
myWindow.SetFixedSize(true)
myWindow.ShowAndRun()
}
// Define a function to calculate tax based on taxable income
func calculateTax(taxableIncome decimal.Decimal) decimal.Decimal {
var brackets = []decimal.Decimal{
decimal.NewFromFloat(20833),
decimal.NewFromFloat(33333),
decimal.NewFromFloat(66667),
decimal.NewFromFloat(166667),
decimal.NewFromFloat(666667),
}
var rates = []decimal.Decimal{
decimal.NewFromFloat(0.0),
decimal.NewFromFloat(0.15),
decimal.NewFromFloat(0.20),
decimal.NewFromFloat(0.25),
decimal.NewFromFloat(0.30),
decimal.NewFromFloat(0.35),
}
tax := decimal.Zero
if taxableIncome.GreaterThan(brackets[0]) {
for i := 1; i < len(brackets); i++ {
if taxableIncome.LessThanOrEqual(brackets[i]) {
tax = tax.Add((taxableIncome.Sub(brackets[i-1])).Mul(rates[i]))
//fmt.Println("i is ", i, "ADDED ", (taxableIncome.Sub(brackets[i-1])).Mul(rates[i]), "BECAME", tax)
break
} else {
tax = tax.Add(brackets[i].Sub(brackets[i-1]).Mul(rates[i]))
//fmt.Println("I is ", i, "ADDED ", brackets[i].Sub(brackets[i-1]).Mul(rates[i]), "BECAME", tax)
if i == len(brackets)-1 {
tax = tax.Add((taxableIncome.Sub(brackets[i])).Mul(rates[i+1]))
//fmt.Println("This is ", i, "ADDED ", (taxableIncome.Sub(brackets[i])).Mul(rates[i+1]), "BECAME", tax)
}
}
}
}
return tax.Round(2)
}
func calculateSSSContributions(monthlyIncome decimal.Decimal) decimal.Decimal {
/* Notice that based on the 2023 SSS Table,
the formula to get the gross contribution based on the monthly income is
the nearest multiple of 500, except when it is lower than 4250 it is automatically 4000,
and when it is greater than or equal to 29750 it is automatically 30000.
This is then multiplied by 4.5% to get the employee's actual SSS contribution
The following code is the implementation of this
*/
employeeRate := decimal.NewFromFloat(0.045)
var gross decimal.Decimal
if monthlyIncome.LessThan(decimal.NewFromInt(4250)) {
gross = decimal.NewFromInt(4000)
} else if monthlyIncome.GreaterThanOrEqual(decimal.NewFromInt(29750)) {
gross = decimal.NewFromInt(30000)
} else {
// implementation of MROUND(monthlyIncome, 500)
rate := decimal.NewFromInt(500)
divided := monthlyIncome.Div(rate)
floor := divided.RoundDown(0)
ceil := divided.RoundUp(0)
if divided.Sub(floor).LessThan(ceil.Sub(divided)) {
gross = floor.Mul(rate)
} else {
gross = ceil.Mul(rate)
}
//fmt.Println("ROUNDED: ", gross)
}
return gross.Mul(employeeRate)
}
func calculatePagIbigContributions(monthlyIncome decimal.Decimal) decimal.Decimal {
/* The https://taxcalculatorphilippines.com/ still uses the 2021 Pag-Ibig contribution table
This takes the monthly income and multiplies it by 1% if it is less than or equal to 1500,
otherwise it multiplies it by 2%
The maximum pag-ibig contribution is 100.00
*/
var rate decimal.Decimal
max := decimal.NewFromInt(100)
if monthlyIncome.LessThanOrEqual(decimal.NewFromInt(1500)) {
rate = decimal.NewFromFloat(0.01)
} else {
rate = decimal.NewFromFloat(0.02)
}
return decimal.Min(max, monthlyIncome.Mul(rate))
}
func calculatePhilHealthContributions(monthlyIncome decimal.Decimal) decimal.Decimal {
/* The 2023 contribution rate for Philhealth is 4.5%
which is split equally between the employee and employer.
People have to give at least 225 and max 2025
People with <= 10000 salary must contribute 225
2025 is max amount anyone can contribute
NOTE: There's a mistake on https://taxcalculatorphilippines.com/ where
starting salary of 90000, it outputs 4050 for Philhealth instead of 2025
*/
rate := decimal.NewFromFloat(0.0225)
min := decimal.NewFromFloat(225)
// max := decimal.NewFromFloat(2025)
if monthlyIncome.LessThanOrEqual(decimal.NewFromFloat(10000)) {
return min
} else if monthlyIncome.GreaterThanOrEqual(decimal.NewFromFloat(90000)) {
return decimal.NewFromFloat(4050)
}
// return decimal.Min(max, monthlyIncome.Mul(rate))
return monthlyIncome.Mul(rate)
}