2024-11-04 23:00:55 +08:00
|
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-05 12:44:09 +08:00
|
|
|
|
func (s *Staff) GetSlaveSalary(month string) float64 {
|
|
|
|
|
slaveSalarys, err := dao.NewStaffSalaryDao().Query(month, "", &s.info.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalSumSalary := float64(0)
|
|
|
|
|
if len(slaveSalarys) != 0 {
|
|
|
|
|
for _, slaveSalary := range slaveSalarys {
|
|
|
|
|
totalSumSalary += slaveSalary.BaseSalary
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return totalSumSalary
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Staff) CalcSlaveSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) {
|
|
|
|
|
if salary == nil {
|
|
|
|
|
salary = new(model.StaffSalary)
|
|
|
|
|
salary.Month = month
|
|
|
|
|
salary.Username = s.info.Username
|
|
|
|
|
}
|
|
|
|
|
salary.BaseSalary = cast.ToFloat64(s.info.BaseSalary)
|
|
|
|
|
salary.Master = s.info.Master
|
2024-11-05 14:12:48 +08:00
|
|
|
|
extra := make(map[string]interface{})
|
|
|
|
|
extra["entryDate"] = s.info.EntryDate //展示排序依赖
|
|
|
|
|
salary.Extra = goutil.EncodeJSON(extra)
|
2024-11-05 12:44:09 +08:00
|
|
|
|
|
|
|
|
|
return salary, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 23:00:55 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
}
|