You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
7.5 KiB
Go
343 lines
7.5 KiB
Go
package hr
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"mercury/src/db"
|
|
"mercury/src/mercuryUtil"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
PaycorHoursLegacy struct {
|
|
Paygroup string
|
|
LastName string
|
|
FirstName string
|
|
HomeDept string
|
|
ManagerName string
|
|
WorkedDept string
|
|
OT float64
|
|
Reg float64
|
|
Sick float64
|
|
Vacation float64
|
|
Total float64
|
|
}
|
|
|
|
PaycorHours struct {
|
|
EEid int
|
|
Bereavement float64
|
|
Holiday float64
|
|
OT float64
|
|
Regular float64
|
|
Service float64
|
|
Sick float64
|
|
Vacation float64
|
|
Total float64
|
|
WeekOf string
|
|
}
|
|
|
|
PaycorDirectoryEntry struct {
|
|
Paygroup string
|
|
LastName string
|
|
FirstName string
|
|
EEid int
|
|
DepartmentName string
|
|
Manager string
|
|
}
|
|
)
|
|
|
|
var (
|
|
OldHeaders = []string{"Paygroup", "Last Name", "First Name", "Home Department", "Manager Name", "Worked DeptName", "OT", "Reg", "Sick", "Vac", "Total"}
|
|
NewHeaders = []string{"Badge #", "Brv", "Hol", "OT", "Reg", "Service", "Sick", "Vac", "Total"}
|
|
)
|
|
|
|
const (
|
|
defaultReportSize = 200
|
|
)
|
|
|
|
func UpdatePaycorReports(pathlike string) error {
|
|
updateDirectory()
|
|
err := updatePaycorHours(pathlike)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func updateDirectory() {
|
|
runner := db.NewRunner("create-mercury-hrPaycorDir-table.sql", db.MercuryDatabaseName)
|
|
cx := &db.ConnectorGeneric{}
|
|
cx.ExecuteSqlScript(runner)
|
|
|
|
items := loadDirectory()
|
|
db.BlockUpdate[PaycorDirectoryEntry](cx, db.MercuryDatabaseName, "update-mercury-hrPaycorDirectory.sql", items)
|
|
}
|
|
|
|
func updatePaycorHours(pathlike string) error {
|
|
files, err := os.ReadDir(pathlike)
|
|
cx := &db.ConnectorGeneric{}
|
|
tableRunner := db.NewRunner("create-mercury-hrPaycorHours-table.sql", db.MercuryDatabaseName)
|
|
cx.ExecuteSqlScript(tableRunner)
|
|
itemAccumulator := make([]*PaycorHours, 0, len(files)*defaultReportSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, v := range files {
|
|
if isTempFile(v.Name()) {
|
|
continue
|
|
}
|
|
filePath := path.Join(pathlike, v.Name())
|
|
worker := mercuryUtil.NewCsvWorker(filePath, func() *PaycorHours { return &PaycorHours{} }, true)
|
|
items := make([]*PaycorHours, 0, defaultReportSize)
|
|
err := worker.Process(&items)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, i := range items {
|
|
i.WeekOf = dateFromFileName(v.Name())
|
|
}
|
|
itemAccumulator = append(itemAccumulator, items...)
|
|
}
|
|
db.BlockUpdate[PaycorHours](cx, db.MercuryDatabaseName, "update-mercury-hrPaycorHours.sql", &itemAccumulator)
|
|
return nil
|
|
}
|
|
|
|
func loadDirectory() *[]*PaycorDirectoryEntry {
|
|
items := make([]*PaycorDirectoryEntry, 0, 200)
|
|
//todo hint: I don't like how you implemented this, David.
|
|
hardcodedDirectoryFileLocation := "/home/dtookey/work/clarity-reporting/paycor_dir/20220914_Paycor_Employee Roster.csv"
|
|
worker := mercuryUtil.NewCsvWorker(
|
|
hardcodedDirectoryFileLocation,
|
|
func() *PaycorDirectoryEntry { return &PaycorDirectoryEntry{} },
|
|
true,
|
|
)
|
|
err := worker.Process(&items)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &items
|
|
}
|
|
|
|
func (hl *PaycorHoursLegacy) Set(header string, content string) error {
|
|
content = strings.ReplaceAll(content, "\u00a0", "")
|
|
|
|
switch header {
|
|
case "Paygroup":
|
|
hl.Paygroup = content
|
|
case "Last Name":
|
|
hl.LastName = content
|
|
case "First Name":
|
|
hl.FirstName = content
|
|
case "Home Department":
|
|
hl.HomeDept = content
|
|
case "Manager Name":
|
|
hl.ManagerName = content
|
|
case "Worked DeptName":
|
|
hl.WorkedDept = content
|
|
case "OT":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
hl.OT = f
|
|
case "Reg":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
hl.Reg = f
|
|
case "Sick":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
hl.Sick = f
|
|
case "Vac":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
hl.Vacation = f
|
|
case "Total":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
hl.Total = f
|
|
default:
|
|
return errors.New("Could not find header for '" + header + "'")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *PaycorHours) Set(header string, content string) error {
|
|
switch header {
|
|
case "Badge #":
|
|
i, err := parseInt(content)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.EEid = i
|
|
case "Brv":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Bereavement = f
|
|
case "Hol":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Holiday = f
|
|
case "OT":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.OT = f
|
|
case "Reg":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Regular = f
|
|
case "Service":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Service = f
|
|
case "Sick":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Sick = f
|
|
case "Vac":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Vacation = f
|
|
case "Total":
|
|
f, err := parseFloat(content)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
h.Total = f
|
|
default:
|
|
return errors.New("Could not find mapping for '" + header + "'")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h PaycorHours) ToQueryBlock() string {
|
|
return fmt.Sprintf("(%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,'%s')", h.EEid, h.Bereavement, h.Holiday, h.OT, h.Regular, h.Service, h.Sick, h.Vacation, h.Total, h.WeekOf)
|
|
}
|
|
|
|
func (d PaycorDirectoryEntry) ToQueryBlock() string {
|
|
return fmt.Sprintf("(%d,'%s','%s','%s','%s','%s')", d.EEid, d.Paygroup, d.LastName, d.FirstName, d.DepartmentName, d.Manager)
|
|
}
|
|
|
|
func (d *PaycorDirectoryEntry) Set(header string, content string) error {
|
|
switch header {
|
|
case "Paygroup":
|
|
d.Paygroup = content
|
|
case "Last Name":
|
|
d.LastName = content
|
|
case "First Name":
|
|
d.FirstName = content
|
|
case "Badge #":
|
|
v, err := strconv.Atoi(content)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.EEid = v
|
|
case "Department Name":
|
|
d.DepartmentName = content
|
|
case "Manager":
|
|
d.Manager = content
|
|
default:
|
|
return errors.New("Could not find mapping for '" + header + "'")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (hl *PaycorHoursLegacy) ToPaycorHours(directory *map[string]*PaycorDirectoryEntry) (*PaycorHours, error) {
|
|
h := PaycorHours{
|
|
Bereavement: 0.0,
|
|
Holiday: 0.0,
|
|
OT: hl.OT,
|
|
Regular: hl.Reg,
|
|
Service: 0.0,
|
|
Sick: hl.Sick,
|
|
Vacation: hl.Vacation,
|
|
Total: hl.Total,
|
|
}
|
|
|
|
ee, okay := (*directory)[hl.FirstName+hl.LastName]
|
|
if !okay {
|
|
return nil, errors.New("could not find map entry for [" + hl.FirstName + hl.LastName + "]")
|
|
}
|
|
h.EEid = ee.EEid
|
|
return &h, nil
|
|
}
|
|
|
|
func (h *PaycorHours) ToRow() []string {
|
|
|
|
return []string{
|
|
strconv.Itoa(h.EEid),
|
|
strconv.FormatFloat(h.Bereavement, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Holiday, 'f', 2, 64),
|
|
strconv.FormatFloat(h.OT, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Regular, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Service, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Sick, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Vacation, 'f', 2, 64),
|
|
strconv.FormatFloat(h.Total, 'f', 2, 64),
|
|
}
|
|
}
|
|
|
|
func parseFloat(content string) (float64, error) {
|
|
if len(content) == 0 {
|
|
return 0.0, nil
|
|
} else {
|
|
f, err := strconv.ParseFloat(content, 64)
|
|
if err != nil {
|
|
return 0.0, err
|
|
}
|
|
return f, nil
|
|
}
|
|
}
|
|
|
|
func parseInt(content string) (int, error) {
|
|
if len(content) == 0 {
|
|
return -1, nil
|
|
} else {
|
|
i, err := strconv.Atoi(content)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return i, nil
|
|
}
|
|
}
|
|
|
|
func dateFromFileName(filename string) string {
|
|
parts := strings.Split(filename, "_")
|
|
block := parts[0]
|
|
year := block[:4]
|
|
month := block[4:6]
|
|
date := block[6:]
|
|
return fmt.Sprintf("%s-%s-%s", year, month, date)
|
|
}
|
|
|
|
func isTempFile(filepath string) bool {
|
|
return filepath[len(filepath)-1:] == "#"
|
|
}
|