enterprise/service/staff.go

290 lines
8.8 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 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
}