enterprise/service/staff_salary.go

311 lines
9.5 KiB
Go

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 userSalary.Base == "" && userSalary.Target == "" {
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
}
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)
//社保和公积金
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
}