-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
340 lines (263 loc) · 12 KB
/
app.js
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
import { Validation } from "./components/validity.js"
import { appData } from "./withholding-info/tax-data.js"
import * as withhold from "./withholding-info/tax-withholding-data.js"
import { stateWH, standard } from "./state-income-tax-calculation.js"
import * as classes from "./classes.js"
// On load, create initial row for Earning Table
document.addEventListener("DOMContentLoaded", () => {
const mainRate = document.querySelector("#main-earning")
const name = mainRate.name
const week = mainRate.dataset.week
const earning = new classes.Earning(name, week)
classes.UI.createEarningList(earning)
})
//=========================================//
// On Submit for Table Update #earning-frm //
//=========================================//
// Submit new values to the Earning table
document.querySelector("#earning-frm").addEventListener("submit", e => {
e.preventDefault()
/*
Add "is-invalid" class if a state is not selected
or if main input (main-earning, main hours) is left empty or no integer is typed
*/
Validation("state", "add", "")
Validation("main-earning", "add", "", /[^0-9.]+/gi)
Validation("week-hours", "add", "", /[^0-9.]+/gi)
// If class contains 'is-invalid', return
let mainEarning = document.querySelector("#main-earning")
const weekHours = document.querySelector("#Week-hours")
const state = document.querySelector("#state")
if (
mainEarning.classList.contains("is-invalid")
|| weekHours.classList.contains("is-invalid")
|| state.classList.contains("is-invalid")
) {return}
const wageForm = document.querySelector("#earning-frm")
const inputs = [...wageForm.querySelectorAll('input[type="text"]')]
inputs.forEach(input => {
// Set values to zero if value is empty or value is not numerical
(!input.value || /[^0-9.]+/gi.test(input.value)) && (input.value = 0)
})
// Set Hours
const hour1 = new classes.Hours(weekHours.name)
hour1.setHours(parseFloat(weekHours.value))
let weekendHours = undefined
let hour2 = new classes.Hours("weekend-hours")
try {
weekendHours = document.querySelector("#weekend-hours")
hour2 = new classes.Hours(weekendHours.name)
hour2.setHours(parseFloat(weekendHours.value))
} catch (err) {
// ignore error
}
// Update values for the Earnings Table
const rateGroup = [...document.querySelectorAll(".rate-group")]
rateGroup.forEach(pay => {
const name = pay.name
const week = pay.dataset.week
const rate = parseFloat(pay.value)
classes.UI.deleteEarning(name)
const earning = new classes.Earning(name, week)
earning.setRate(rate)
const hours = earning.setHours(classes.Calc.getHours(week, hour1.hours, hour2.hours))
classes.Calc.getAmount(rate, hours, earning)
classes.UI.createEarningList(earning)
})
const {table, taxInfo, withholding} = appData
const {threshold, percent} = withhold.FICA.Medicare
//=================================//
// Overtime Sub-Section for weekly //
//=================================//
// Check if hours exceed 40 hours and set or delete overtime
if (hour1.hours > 40 && taxInfo.freq === "weekly") {
classes.UI.deleteEarning('Overtime')
let preAmount = classes.Calc.totalAmount(".earning-row-amount")
const overtime = new classes.Earning('Overtime', 'ot-days')
const rate = classes.Calc.overtimeRate(preAmount, hour1.hours)
const hours = overtime.setHours(classes.Calc.getHours('Overtime', hour1.hours, hour2.hours))
overtime.setRate(rate)
classes.Calc.getAmount(rate, hours, overtime)
classes.UI.createOvertimeList(overtime)
} else {
classes.UI.deleteEarning('Overtime')
}
/*
Get final total amount for the earning table
Set total amount in tax-data.js and the Total in the earning table
*/
table.earning_total = classes.Calc.totalAmount(".earning-row-amount")
document.querySelector("#earning-tbl-total").nextElementSibling.textContent = `$ ${table.earning_total.toFixed(2)}`
//=======================//
// Tax Table Sub-Section //
//=======================//
// Update FICA Social Security table
withholding.social = classes.Calc.social(table.earning_total)
document.querySelector("#fica-social").nextElementSibling.textContent = `$ ${withholding.social.toFixed(2)}`
// Update FICA Medicare table
// TODO: Medicare: Move code to Calc.medicare function
if (withholding.medicare.addSurtax) {
// Useless code for now. Add dates for end of year pay period total
const addlAmount = table.earning_total - threshold[taxInfo.status]
// finish later
}
withholding.medicare.amount = table.earning_total * percent.regular
document.querySelector("#fica-medicare").nextElementSibling.textContent = `$ ${withholding.medicare.amount.toFixed(2)}`
// Update State Withholding
withholding.stateWH = stateWH(taxInfo.state)
document.querySelector("#state-withhold").nextElementSibling.textContent = `$ ${withholding.stateWH.toFixed(2)}`
//Update Federal Withholding
withholding.federalWH = standard({
frequency: taxInfo.freq,
totalWage: table.earning_total,
status: taxInfo.status,
dataLocation: withhold.Federal
})
document.querySelector("#fed-withhold").nextElementSibling.textContent = `$ ${withholding.federalWH.toFixed(2)}`
/*
Get final total amount for the tax table
Set total amount in tax-data.js and the Total in the tax table
*/
table.tax_total = classes.Calc.totalAmount(".tax-row-amount")
document.querySelector("#tax-tbl-total").nextElementSibling.textContent = `$ ${table.tax_total.toFixed(2)}`
//===========================//
// Summary Table Sub-Section //
//===========================//
let gross = document.querySelector("#gross")
let taxable = document.querySelector("#taxable")
let taxes = document.querySelector("#taxes")
let net = document.querySelector("#net")
gross.textContent = `$ ${table.earning_total.toFixed(2)}`
taxable.textContent = `$ ${table.earning_total.toFixed(2)}`
taxes.textContent = `$ ${table.tax_total.toFixed(2)}`
table.net_total = table.earning_total - table.tax_total
net.textContent = `$ ${table.net_total.toFixed(2)}`
classes.UI.clearFields()
})
// Remove "is-invalid" class after correct value is typed
document.querySelector("#earning-frm").addEventListener("input", () => {
Validation("main-earning", "remove", /^[0-9]+\.?[0-9]?[0-9]?/g)
Validation("week-hours", "remove", /^[0-9]+\.?[0-9]?[0-9]?/g)
})
//==========================================//
// On Change for Table Update #tax-info-frm //
//==========================================//
document.querySelector("#tax-info-frm").addEventListener("change", () => {
const getValue = (elementID) => {
return document.getElementById(elementID).value
}
Validation("state", "remove", /\S/)
const {taxInfo, table} = appData
taxInfo.state = getValue('state');
taxInfo.status = getValue('status');
taxInfo.freq = getValue('frequency')
taxInfo.fed_allowance = parseInt(getValue('fed-allowance'))
taxInfo.state_allowance = parseInt(getValue('state-allowance'))
// Taxes table: Change state withholding name and amount
document.querySelector("#state-withhold-select").textContent = taxInfo.state
const regularRate = document.querySelector(".earning-row-title").nextElementSibling.textContent
if (regularRate != 0) {
let totalFedWH = standard({
frequency: taxInfo.freq,
totalWage: table.earning_total,
status: taxInfo.status,
dataLocation: withhold.Federal
})
document.querySelector("#state-withhold").nextElementSibling.textContent = `$ ${stateWH(taxInfo.state).toFixed(2)}`
document.querySelector("#fed-withhold").nextElementSibling.textContent = `$ ${totalFedWH.toFixed(2)}`
/*
Get final total amount for the tax table
Set total amount in tax-data.js and the Total in the tax table
*/
table.tax_total = classes.Calc.totalAmount(".tax-row-amount")
document.querySelector("#tax-tbl-total").nextElementSibling.textContent = `$ ${table.tax_total.toFixed(2)}`
let taxes = document.querySelector("#taxes")
let net = document.querySelector("#net")
taxes.textContent = `$ ${table.tax_total.toFixed(2)}`
net.textContent = `$ ${(table.earning_total - table.tax_total).toFixed(2)}`
}
//=================================//
// State Configuration Sub-Section //
//=================================//
//=============================//
// Manual Overtime Sub-Section //
//=============================//
// Include overtime hours input for pay periods that are not weekly
if (
taxInfo.freq !== "weekly"
|| taxInfo.freq !== "daily"
) {
// TODO: Set a manual overtime input for other pay paeriods besids weekly
}
})
//===============//
// Init New Rate //
//===============//
// Begin creating a new rate
document.querySelector("#add-pay").addEventListener("click", () => {
classes.CreateInput.createOptionsRate()
document.querySelector("#add-pay").disabled = true
document.querySelector("#calculate").disabled = true
})
//==========//
// 1st Step //
//==========//
document.querySelector("#rate-input").addEventListener("change", () => {
const option = document.querySelector("#rate-input").firstElementChild
let {create_input} = appData.class
// Initialize CreateInput class
switch (option.value) {
case "everyday":
case "weekday":
create_input.setWeek(option.value)
break;
case "weekend":
create_input.setWeek(option.value)
let {weekend_hours} = appData.class
// Check if weekend hours does not exist and create weekend hours
if (!weekend_hours.week) {
weekend_hours.setWeek("weekend-hours")
.setName("hours")
.setPlaceholder("Weekend Hours", "")
.createWeekendHours()
}
break;
}
// remove elements within the div #rate-input and switch to div #name-input
option.remove()
document.querySelector("#rate-input").classList.remove("input-group", "me-2")
classes.CreateInput.createNameInput()
})
//==========//
// 2nd Step //
//==========//
document.querySelector("#name-input").addEventListener("click", e => {
if (e.target.id !== "enter-name") {return}
// Check for entered value. Return if value is invalid
const newName = document.querySelector("#new-name")
newName.addEventListener("input", () => Validation("new-name", "remove", /\w/))
Validation("new-name", "add", "")
if (!newName.value) {return}
let letterCase = newName.value.charAt(0).toUpperCase() + newName.value.slice(1).toLowerCase();
// Prevent named duplicates
if (classes.UI.preventDuplicate(letterCase)) {return}
let {create_input} = appData.class
// Value entered
create_input.setName(letterCase)
.setPlaceholder(letterCase)
// remove elements within the div #name-input after clicking enter
const nameInput = document.querySelector("#name-input")
Array.from(nameInput.children).forEach(child => child.remove())
nameInput.classList.remove("input-group", "me-2")
// Create the new rate and add it to the table
create_input.finalizeNewRate(create_input.week)
const earning = new classes.Earning(create_input.name, create_input.week)
classes.UI.createEarningList(earning)
document.querySelector("#add-pay").disabled = false
document.querySelector("#calculate").disabled = false
})
// Delete Rate
document.querySelector("#earning-frm").addEventListener("click", e => {
classes.CreateInput.deleteInput(e)
// TODO: update total after input is deleted
})