package service import ( butil "enterprise/base/util" "enterprise/common/dao" "enterprise/common/model" "enterprise/common/registry" "github.com/robertkrimen/otto" log "github.com/sirupsen/logrus" "github.com/smbrave/goutil" "github.com/spf13/cast" "math" "time" ) type StaffSalary struct { user *model.StaffUser } func NewStaffSalary(user *model.StaffUser) *StaffSalary { return &StaffSalary{ user: user, } } func (s *StaffSalary) CalcSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) { corp, err := dao.NewCorpDao().Get(s.user.CorpId) if err != nil { return nil, err } userSalary := s.user.GetSalary() userConfig := s.user.GetConfig() corpConfig := corp.GetConfig() if cast.ToInt(userSalary.Base) == 0 && cast.ToInt(userSalary.Target) == 0 { return nil, nil } entryTime, _ := time.ParseInLocation("2006-01-02", s.user.EntryDate, time.Local) leaveTime, _ := time.ParseInLocation("2006-01-02", s.user.LeaveDate, time.Local) isEntryMonth := goutil.If(cast.ToInt(entryTime.Format("200601")) == cast.ToInt(month), true, false) isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false) //入职之前或离职之后的工资不计算 if cast.ToInt(month) < cast.ToInt(entryTime.Format("200601")) || cast.ToInt(month) > cast.ToInt(leaveTime.Format("200601")) { return nil, nil } if salary == nil { salary = new(model.StaffSalary) salary.Month = month salary.CorpId = s.user.CorpId salary.UserId = s.user.Id salary.Username = s.user.Username } //社保和公积金 salary.SocialDeduct = 0 salary.HouseDeduct = 0 if isEntryMonth { //入职月不买社保 salary.SocialDeduct = 0 salary.HouseDeduct = 0 } salary.ShouldDay = int(s.getTotalWorkDay(s.user.CorpId, s.user.Username, month)) // 入职月或离职月以大多数人的出勤天数为准 if isEntryMonth || isLeaveMonth { salary.ShouldDay = int(s.getTotalWorkDayMax(s.user.CorpId, month)) } holiday, surplusHoliday := s.getRealVacationDay(month) realWorkDays := s.getRealWorkDay(month) //approvalCheckinDay := s.getApprovalCheckinDay(s.user.CorpId, s.user.Username, month) //鱼办特殊月份 if salary.Month == "202502" && corp.Id == 1002 { salary.ShouldDay = 21 } salary.HolidayDay = holiday salary.AttendDay = realWorkDays + surplusHoliday salary.Salary = cast.ToFloat64(userSalary.Base) + cast.ToFloat64(userSalary.Target) salary.SocialDeduct = goutil.If(userConfig.SocialDeduct != "", cast.ToFloat64(userConfig.SocialDeduct), cast.ToFloat64(corpConfig.SocialDeduct)) salary.HouseDeduct = goutil.If(userConfig.HouseDeduct != "", cast.ToFloat64(userConfig.HouseDeduct), cast.ToFloat64(corpConfig.HouseDeduct)) //计算工资 s.calculate(corp, salary) s.formatFloat(salary) return salary, nil } func (s *StaffSalary) calculate(corp *model.Corp, salary *model.StaffSalary) { //获取基础数据 data := s.getCalcData(salary) //获取业务数据 dataFactory := registry.NewSalaryCalculator(corp, s.user) if dataFactory != nil { data["biz"] = dataFactory.Calculate(salary) } salary.Extra = goutil.EncodeJSONIndent(data) salary.SetExtra("calculate", goutil.GetNowTime()) //获取计算器表达式 calculator, _ := dao.NewSalaryCalculatorDao().Get(cast.ToInt64(s.user.GetSalary().Calculator)) if calculator == nil { log.Errorf("calculator[%s] is nil", s.user.GetSalary().Calculator) return } //执行表达式 jsrun := otto.New() jsrun.Set("data", data) _, err := jsrun.Run(calculator.Expression) if calculator == nil { log.Errorf("Run[%s] error :%s", calculator.Expression, err.Error()) return } //出勤工资 attendSalary, err := jsrun.Get("attend_salary") if err != nil { log.Errorf("attend_salary error :%s", err.Error()) } else { salary.AttendSalary, err = attendSalary.ToFloat() if err != nil { log.Errorf("attendSalary foloat is error:%s", err.Error()) } } //绩效工资 targetSalary, err := jsrun.Get("target_salary") if err != nil { log.Errorf("attend_salary error :%s", err.Error()) } else { salary.TargetSalary, err = targetSalary.ToFloat() if err != nil { log.Errorf("targetSalary foloat is error:%s", err.Error()) } } //奖金 awardSalary, err := jsrun.Get("award_salary") if err != nil { log.Errorf("attend_salary error :%s", err.Error()) } else { salary.AwardSalary, err = awardSalary.ToFloat() if err != nil { log.Errorf("awardSalary foloat is error:%s", err.Error()) } } } func (s *StaffSalary) getCalcData(mSalary *model.StaffSalary) map[string]interface{} { userSalary := s.user.GetSalary() userConfig := s.user.GetConfig() data := make(map[string]interface{}) user := make(map[string]interface{}) salary := make(map[string]interface{}) user["username"] = s.user.Username //账户名称 user["status"] = s.user.Status //状态 user["entryDate"] = s.user.EntryDate //入职时间 user["officalDate"] = s.user.OfficialDate //转正时间 user["leaveDate"] = s.user.LeaveDate //离职时间 user["baseSalary"] = cast.ToInt(userSalary.Base) //基本工资 user["targetSalary"] = cast.ToInt(userSalary.Target) //绩效工资 user["target"] = userConfig.PerftTarget //绩效目标 salary["month"] = mSalary.Month //工资月份 salary["shouldDay"] = mSalary.ShouldDay //应出勤天数 salary["attendDay"] = mSalary.AttendDay //实际出勤天数 salary["holidayDay"] = mSalary.HolidayDay //休假天数 data["user"] = user data["salary"] = salary return data } func (s *StaffSalary) formatFloat(salary *model.StaffSalary) { salary.AwardSalary = butil.FloatCut(salary.AwardSalary) salary.AttendSalary = butil.FloatCut(salary.AttendSalary) salary.TargetSalary = butil.FloatCut(salary.TargetSalary) salary.OtherSalary = butil.FloatCut(salary.OtherSalary) salary.AttendDay = butil.FloatCut(salary.AttendDay) salary.HolidayDay = butil.FloatCut(salary.HolidayDay) } func (s *StaffSalary) getRealVacationDay(month string) (float64, float64) { // 休假申请 approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(s.user.CorpId, s.user.Username, month, "") if err != nil { log.Errorf("db error :%s", err.Error()) return 0, 0 } 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) } } return holiday, surplusHoliday } func (s *StaffSalary) getApprovalCheckinDay(corpId int64, username, month string) int { approvalDay := int(0) userCheckins, err := dao.NewCheckinDao().Query(corpId, username, month, false) if err != nil { log.Errorf("db error :%s", err.Error()) return approvalDay } for _, checkin := range userCheckins { approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(corpId, username, checkin.Day) if approvalCheckin != nil { approvalDay += 1 } } return approvalDay } func (s *StaffSalary) getRealWorkDay(month string) float64 { realWorkdays := float64(0) userCheckins, err := dao.NewCheckinDao().Query(s.user.CorpId, s.user.Username, month, false) if err != nil { log.Errorf("db error :%s", err.Error()) return realWorkdays } approvalCheckinDay := 0 //入职当天算工作日 entryTime, _ := time.ParseInLocation("2006-01-02", s.user.EntryDate, time.Local) if entryTime.Format("200601") == month { realWorkdays += 1 } //离职当天算工作日 leaveTime, _ := time.ParseInLocation("2006-01-02", s.user.LeaveDate, time.Local) if leaveTime.Format("200601") == month { realWorkdays += 1 } corp, _ := dao.NewCorpDao().Get(s.user.CorpId) for _, checkin := range userCheckins { //入职离职当天已经算过了 if entryTime.Format("2006-01-02") == checkin.Day || leaveTime.Format("2006-01-02") == checkin.Day { continue } //有请假申请的不算出勤 approvalVacation, _ := dao.NewApprovalVacationDao().GetByUsernameDay(s.user.CorpId, s.user.Username, checkin.Day) if approvalVacation != nil { continue } if checkin.Exception == "" { realWorkdays += 1 continue } //有补卡申请就直接算出勤 approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(s.user.CorpId, s.user.Username, checkin.Day) if approvalCheckin != nil { realWorkdays += 1 approvalCheckinDay += 1 continue } //其他按工作时长结算 if checkin.EndTime > 0 && checkin.StartTime > 0 { duration := float64(checkin.EndTime-checkin.StartTime) / cast.ToFloat64(corp.GetConfig().WorkerHouer) realWorkdays += goutil.If(duration > 1, 1, duration) } } return realWorkdays } func (s *StaffSalary) getTotalWorkDay(corpId int64, username, month string) int64 { checkins, _ := dao.NewCheckinDao().Query(corpId, username, month, false) return int64(len(checkins)) } func (s *StaffSalary) getTotalWorkDayMax(corpId int64, month string) int64 { // 最多人数的应出勤天数 为真正的出勤天数 userCounts, err := dao.NewCheckinDao().CountUsername(corpId, 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 }