enterprise/worker/staff.go

402 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package worker
import (
butil "enterprise/base/util"
"enterprise/common/config"
"enterprise/common/dao"
"enterprise/common/global"
"enterprise/common/model"
"fmt"
"git.u8t.cn/open/gosdk/qyweixin"
log "github.com/sirupsen/logrus"
"github.com/smbrave/goutil"
"github.com/spf13/cast"
"math"
"strings"
"time"
)
type Staff struct {
}
func (s *Staff) MontorWorkAge() {
staffs, err := dao.NewStaffInfoDao().Query(model.StaffInfoStatusNormal)
if err != nil {
log.Errorf("db error :%s", err.Error())
return
}
nowDate := time.Now()
for _, staff := range staffs {
//离职的忽略
if staff.LeaveDate != "" {
continue
}
entryDate, _ := time.ParseInLocation("2006-01-02", staff.EntryDate, time.Local)
spanMonth := (nowDate.Year()-entryDate.Year())*12 + int(nowDate.Month()) - int(entryDate.Month())
log.Infof("staff[%s] entryDate[%s] spanMonth[%.1f]", staff.Username, staff.EntryDate, float64(spanMonth)/12.0)
if nowDate.Day() != entryDate.Day() || spanMonth%6 != 0 {
continue
}
message := make([]string, 0)
message = append(message, fmt.Sprintf("【员工半年提醒】[%s]", staff.Realname))
message = append(message, fmt.Sprintf("入职时间:%s", staff.EntryDate))
message = append(message, fmt.Sprintf("入职年限:%.1f", float64(spanMonth)/12))
message = append(message, fmt.Sprintf("基本工资:%s", staff.Salary))
message = append(message, fmt.Sprintf("身份证号:%s", staff.Idno))
if err := global.SendMessage([]string{"jiangyong"}, strings.Join(message, "\n")); err != nil {
log.Errorf("send message error :%s", err.Error())
}
}
}
func (s *Staff) SendStaffSalaryBill(month string) {
staffSalarys, err := dao.NewStaffSalaryDao().Query(month)
if err != nil {
log.Errorf("db error :%s", err.Error())
return
}
for _, staffSalary := range staffSalarys {
if staffSalary.BaseSalary < 0.1 {
continue
}
userConfig, err := dao.NewUserConfigDao().GetByUsername(staffSalary.Username)
if err != nil {
log.Errorf("db error :%s", err.Error())
continue
}
message := make([]string, 0)
message = append(message, fmt.Sprintf("【工资单】[%s][%s]", staffSalary.Username, month))
message = append(message, fmt.Sprintf("基本工资:%.2f", staffSalary.BaseSalary))
message = append(message, fmt.Sprintf("出勤工资:%.2f", staffSalary.AttendSalary))
if staffSalary.AwardSalary >= 0.1 {
message = append(message, fmt.Sprintf("额外工资:%.2f", staffSalary.AwardSalary))
}
message = append(message, fmt.Sprintf("社保扣除:%.2f", staffSalary.SocialInsurence))
message = append(message, fmt.Sprintf("个税扣除:%.2f", staffSalary.PersonalTax))
message = append(message, fmt.Sprintf("实发工资:%.2f", staffSalary.GetRealSalary()))
if err := global.SendMessage([]string{"jiangyong"}, strings.Join(message, "\n")); err != nil {
log.Errorf("send message error :%s", err.Error())
}
if userConfig != nil && cast.ToBool(userConfig.Get(model.StaffSalaryNotify)) == true {
if err := global.SendMessage([]string{userConfig.Username}, strings.Join(message, "\n")); err != nil {
log.Errorf("send message error :%s", err.Error())
}
}
}
}
func (s *Staff) SyncStaffInfo() {
cfg := config.GetConfig()
hrAssiant := qyweixin.NewAppHr(&qyweixin.AppConfig{
Corpid: cfg.QyWeixin.Corpid,
Secret: cfg.QyWeixin.EnterpriseSecret,
Agent: cfg.QyWeixin.EnterpriseAgent,
})
userConfigs, err := dao.NewUserConfigDao().Query(model.UserConfigStatusNormal)
if err != nil {
log.Errorf("query staff db error :%s", err.Error())
return
}
for _, user := range userConfigs {
staffInfo, err := hrAssiant.GetStaffInfo(user.Username)
if err != nil {
log.Errorf("getstaff info username[%s] error :%s", user.Username, err.Error())
continue
}
staff, err := dao.NewStaffInfoDao().GetByUsername(user.Username)
if err != nil {
log.Errorf("db error :%s", err.Error())
continue
}
if staff == nil {
staff = new(model.StaffInfo)
}
staff.Username = staffInfo.UserName
staff.Realname = staffInfo.RealName
staff.Idno = staffInfo.Idno
staff.StaffType = staffInfo.StaffType
staff.Salary = cast.ToString(staffInfo.Salary)
staff.EntryDate = staffInfo.EntryDate
staff.OfficialDate = staffInfo.OfficialDate
staff.BirthDate = staffInfo.BirthDate
staff.BankName = staffInfo.BankName
staff.BankCard = staffInfo.BankCard
staff.AlipayUid = staffInfo.AlipayUid
if staff.Id == 0 {
_, err = dao.NewStaffInfoDao().Create(staff)
} else {
err = dao.NewStaffInfoDao().Update(staff)
}
if err != nil {
log.Errorf("db error :%s", err.Error())
}
}
}
func (s *Staff) SyncStaffSalary(month string) {
if month == "" {
month = time.Now().AddDate(0, -1, 0).Format("2006-01")
}
month = strings.ReplaceAll(month, "-", "")
maxTotalDays := s.getTotalWorkDayMax(month)
monthTime, _ := time.ParseInLocation("200601", month, time.Local)
startDate := cast.ToInt(monthTime.Format("20060102"))
endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102"))
salaryDao := dao.NewStaffSalaryDao()
staffs, err := dao.NewStaffInfoDao().Query(model.StaffInfoStatusNormal)
if err != nil {
log.Errorf("query staff db error :%s", err.Error())
return
}
for _, staff := range staffs {
entryTime, _ := time.ParseInLocation("2006-01-02", staff.EntryDate, time.Local)
officalTime, _ := time.ParseInLocation("2006-01-02", staff.OfficialDate, time.Local)
leaveTime, _ := time.ParseInLocation("2006-01-02", staff.LeaveDate, time.Local)
isEntryMonth := goutil.If(cast.ToInt(entryTime.Format("200601")) == cast.ToInt(month), true, false)
isOfficialMonth := goutil.If(cast.ToInt(officalTime.Format("200601")) == cast.ToInt(month), true, false)
isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false)
// 已离职的员工不处理
if staff.LeaveDate != "" && !isLeaveMonth {
continue
}
salary, err := salaryDao.GetBy(staff.Username, month)
if err != nil {
log.Errorf("db error :%s", err.Error())
continue
}
if salary == nil {
salary = new(model.StaffSalary)
salary.Month = month
salary.Username = staff.Username
}
config, err := dao.NewUserConfigDao().GetByUsername(staff.Username)
if err != nil {
log.Errorf("db error :%s", err.Error())
continue
}
if config == nil {
log.Errorf("username[%s] not config,skip!!", staff.Username)
continue
}
// 试用期折扣
discount := cast.ToFloat64(config.Get(model.StaffSalaryExpDiscount))
if discount == 0.0 {
discount = 0.8
}
if cast.ToInt(monthTime.Format("200601")) > cast.ToInt(officalTime.Format("200601")) {
discount = 1.0
}
//社保
socialInsurence := cast.ToFloat64(config.Get(model.StaffSalarySocialInsurence))
//if isEntryMonth && entryTime.Day() >= 15 {
if isEntryMonth { //入职月不买社保
socialInsurence = 0
}
// 休假申请
approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(staff.Username, month, "")
if err != nil {
log.Errorf("db error :%s", err.Error())
continue
}
holiday := float64(0)
surplusHoliday := float64(0)
for _, vac := range approveVacations {
startTime, _ := time.ParseInLocation("2006-01-02 15:04:05", vac.VacationStartTime, time.Local)
endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", vac.VacationEndTime, time.Local)
//同一天请假时长大于8小时算一天
if startTime.Format("2006-01-02") == endTime.Format("2006-01-02") && vac.VacationDuration > 1 {
holiday += 1
} else {
holiday += vac.VacationDuration
//不是整天数,把剩余的算上
span := vac.VacationDuration - math.Floor(vac.VacationDuration)
surplusHoliday += goutil.If(math.Abs(span) < 0.000001, 0, 1-span)
}
}
// 打卡正常天数+补卡天数 为正常出勤天数
totalDays := s.getTotalWorkDay(staff.Username, month)
realWorkDays := s.getRealWorkDay(staff.EntryDate, staff.LeaveDate, staff.Username, month)
approvalCheckinDay := s.getApprovalCheckinDay(staff.Username, month)
extra := make(map[string]interface{})
salary.BaseSalary = cast.ToFloat64(staff.Salary)
salary.Holiday = holiday
entryDate := cast.ToInt(strings.ReplaceAll(staff.EntryDate, "-", ""))
officialDate := cast.ToInt(strings.ReplaceAll(staff.OfficialDate, "-", ""))
if discount < 1.0 && isOfficialMonth {
totalMonthDay := float64(endDate - startDate + 1)
discount = discount*float64(officialDate-startDate)/totalMonthDay + 1*float64(endDate-officialDate+1)/totalMonthDay
}
// 入职月或离职月以大多数人的出勤天数为准
if isEntryMonth || isLeaveMonth {
totalDays = maxTotalDays
}
realWorkDays = realWorkDays + surplusHoliday
attendSalary := salary.BaseSalary * (realWorkDays / float64(totalDays)) * discount
staffSalaryPerDay := cast.ToFloat64(config.Get(model.StaffSalaryPerDay))
if staffSalaryPerDay > 0 {
attendSalary = staffSalaryPerDay * realWorkDays
}
extra["discount"] = discount
extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖
extra["socialInsurence"] = socialInsurence
extra["days"] = endDate - startDate + 1
extra["startDate"] = startDate
extra["totalDays"] = totalDays
extra["realDays"] = realWorkDays
extra["startDate"] = startDate
extra["endDate"] = endDate
extra["officialDate"] = officialDate
extra["entryDate"] = entryDate
extra["isEntryMonth"] = isEntryMonth
extra["isOfficialMonth"] = isOfficialMonth
extra["surplusHoliday"] = surplusHoliday
salary.AttendSalary = butil.FloatCut(attendSalary)
salary.RealSalary = salary.GetRealSalary()
salary.SocialInsurence = socialInsurence
salary.Holiday = holiday
salary.TotalDay = float64(totalDays)
salary.RealDay = realWorkDays
salary.Extra = goutil.EncodeJSONIndent(extra)
if salary.Id == 0 {
_, err = dao.NewStaffSalaryDao().Create(salary)
} else {
err = dao.NewStaffSalaryDao().Update(salary)
}
if err != nil {
log.Errorf("db error :%s", err.Error())
}
}
}
func (s *Staff) getApprovalCheckinDay(username, month string) int {
approvalDay := int(0)
userCheckins, err := dao.NewCheckinDao().Query(username, month, false)
if err != nil {
log.Errorf("db error :%s", err.Error())
return approvalDay
}
for _, checkin := range userCheckins {
approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(username, checkin.Day)
if approvalCheckin != nil {
approvalDay += 1
}
}
return approvalDay
}
func (s *Staff) getRealWorkDay(entryDate, leaveDate, username, month string) float64 {
realWorkdays := float64(0)
userCheckins, err := dao.NewCheckinDao().Query(username, month, false)
if err != nil {
log.Errorf("db error :%s", err.Error())
return realWorkdays
}
approvalCheckinDay := 0
//入职当天算工作日
entryTime, _ := time.ParseInLocation("2006-01-02", entryDate, time.Local)
if entryTime.Format("200601") == month {
realWorkdays += 1
}
//离职当天算工作日
leaveTime, _ := time.ParseInLocation("2006-01-02", leaveDate, time.Local)
if leaveTime.Format("200601") == month {
realWorkdays += 1
}
for _, checkin := range userCheckins {
if checkin.Exception == "" {
realWorkdays += 1
continue
}
//入职离职当天已经算过了
if entryTime.Format("2006-01-02") == checkin.Day || leaveTime.Format("2006-01-02") == checkin.Day {
continue
}
//有补卡申请就直接算出勤
approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(username, checkin.Day)
if approvalCheckin != nil {
realWorkdays += 1
approvalCheckinDay += 1
continue
}
//有请假申请的不算出勤
approvalVacation, _ := dao.NewApprovalVacationDao().GetByUsernameDay(username, checkin.Day)
if approvalVacation != nil {
continue
}
// 迟到的按时间折算
if strings.Contains(checkin.Exception, "上班打卡:时间异常") {
stTime := time.Unix(checkin.StartTime, 0)
later := float64(stTime.Hour() - 8) //迟到小时数从9点算
if later > 8 {
later = 8
}
realWorkdays += (8 - later) / 8
continue
}
}
return realWorkdays
}
func (s *Staff) getTotalWorkDay(username, month string) int64 {
checkins, _ := dao.NewCheckinDao().Query(username, month, false)
return int64(len(checkins))
}
func (s *Staff) getTotalWorkDayMax(month string) int64 {
// 最多人数的应出勤天数 为真正的出勤天数
userCounts, err := dao.NewCheckinDao().CountUsername(month)
if err != nil {
log.Errorf("db error :%s", err.Error())
return 0
}
mp := make(map[int64]int)
totalDays := int64(0)
for _, uc := range userCounts {
mp[uc.Count] += 1
}
minCount := 0
for k, v := range mp {
if v > minCount {
minCount = v
totalDays = k
}
}
return totalDays
}