package service import ( butil "enterprise/base/util" "enterprise/common/config" "enterprise/common/dao" "enterprise/common/model" log "github.com/sirupsen/logrus" "github.com/smbrave/goutil" "github.com/spf13/cast" "math" "strings" "time" ) type Staff struct { info *model.StaffInfo config *model.StaffConfig } func NewStaff(info *model.StaffInfo, config *model.StaffConfig) *Staff { return &Staff{ info: info, config: config, } } func (s *Staff) CalcSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) { staff := s.info if salary == nil { salary = new(model.StaffSalary) salary.Month = month salary.Username = s.info.Username } entryTime, _ := time.ParseInLocation("2006-01-02", staff.EntryDate, 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) isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false) // 试用期折扣 discount := s.getDiscount(month) //社保和公积金 socialInsurence := goutil.If(s.config.Exist(model.StaffSalarySocialInsurence), cast.ToFloat64(s.config.Get(model.StaffSalarySocialInsurence)), config.SalarySocialInsurence) //houseFund := goutil.If(s.config.Exist(model.StaffSalaryHouseFund), cast.ToFloat64(s.config.Get(model.StaffSalaryHouseFund)), config.SalaryHouseFund) houseFund := cast.ToFloat64(s.config.Get(model.StaffSalaryHouseFund)) if isEntryMonth { //入职月不买社保 socialInsurence = 0 houseFund = 0 } holiday, surplusHoliday := s.getRealVacationDay(staff.Username, month) totalDays := s.getTotalWorkDay(staff.Username, month) realWorkDays := s.getRealWorkDay(staff.EntryDate, staff.LeaveDate, staff.Username, month) approvalCheckinDay := s.getApprovalCheckinDay(staff.Username, month) // 入职月或离职月以大多数人的出勤天数为准 if isEntryMonth || isLeaveMonth { totalDays = s.getTotalWorkDayMax(month) } salary.BaseSalary = cast.ToFloat64(staff.BaseSalary) realWorkDays = realWorkDays + surplusHoliday atttendRate := float64(realWorkDays) / float64(totalDays) attendSalary := salary.BaseSalary * atttendRate * discount staffSalaryPerDay := cast.ToFloat64(s.config.Get(model.StaffSalaryPerDay)) if staffSalaryPerDay > 0 { attendSalary = staffSalaryPerDay * realWorkDays } extra := make(map[string]interface{}) extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖 extra["entryDate"] = staff.EntryDate //展示排序依赖 awardSalay, perfSalary := s.getExtraSalary(month) salary.Holiday = holiday salary.PerfSalary = perfSalary salary.AwardSalary = awardSalay salary.AttendSalary = butil.FloatCut(attendSalary) salary.RealSalary = salary.GetRealSalary() salary.SocialInsurence = socialInsurence salary.HouseFund = houseFund salary.Holiday = holiday salary.TotalDay = float64(totalDays) salary.RealDay = realWorkDays salary.Extra = goutil.EncodeJSONIndent(extra) return salary, nil } func (s *Staff) getExtraSalary(month string) (float64, float64) { monthTime, _ := time.ParseInLocation("200601", month, time.Local) startDay := monthTime.Format("2006-01-02") endDay := monthTime.AddDate(0, 1, -1).Format("2006-01-02") datas, err := dao.NewUnifyAdData().QueryOwnerData(s.info.Username, startDay, endDay) if err != nil { log.Errorf("db error :%s", err.Error()) return 0, 0 } sumProfitAmount := int64(0) for _, data := range datas { sumProfitAmount += data.PayAmount - data.Cost } totalProfit := cast.ToFloat64(goutil.FormatMoney(sumProfitAmount)) //1算提成 awardSalary := float64(0) if s.config.Exist(model.StaffSalaryProfitDeduct) { deductRate := cast.ToFloat64(s.config.Get(model.StaffSalaryProfitDeduct)) awardSalary = totalProfit * deductRate if awardSalary < 0 { awardSalary = 0 } } //2算绩效 perfSalary := cast.ToFloat64(s.info.PerfSalary) if s.config.Exist(model.StaffSalaryProfitTarget) { profitTarget := cast.ToFloat64(s.config.Get(model.StaffSalaryProfitTarget)) if totalProfit < 0 { perfSalary = 0 awardSalary = 0 } else if totalProfit < profitTarget { perfSalary = perfSalary * (totalProfit / profitTarget) awardSalary = 0 } } return awardSalary, perfSalary } func (s *Staff) getDiscount(month string) float64 { officalTime, _ := time.ParseInLocation("2006-01-02", s.info.OfficialDate, time.Local) monthTime, _ := time.ParseInLocation("200601", month, time.Local) startDate := cast.ToInt(monthTime.Format("20060102")) endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102")) officialDate := cast.ToInt(strings.ReplaceAll(s.info.OfficialDate, "-", "")) isOfficialMonth := goutil.If(cast.ToInt(officalTime.Format("200601")) == cast.ToInt(month), true, false) // 试用期折扣 discount := cast.ToFloat64(s.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 } if discount < 1.0 && isOfficialMonth { totalMonthDay := float64(endDate - startDate + 1) discount = discount*float64(officialDate-startDate)/totalMonthDay + 1*float64(endDate-officialDate+1)/totalMonthDay } return discount } func (s *Staff) getRealVacationDay(username, month string) (float64, float64) { // 休假申请 approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(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 *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 entryTime.Format("2006-01-02") == checkin.Day || leaveTime.Format("2006-01-02") == checkin.Day { continue } if checkin.Exception == "" { realWorkdays += 1 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 }