2023-08-31 22:20:40 +08:00
|
|
|
|
package worker
|
|
|
|
|
|
|
|
|
|
import (
|
2023-09-01 20:01:30 +08:00
|
|
|
|
butil "enterprise/base/util"
|
2023-08-31 22:20:40 +08:00
|
|
|
|
"enterprise/common/config"
|
|
|
|
|
"enterprise/common/dao"
|
2023-10-08 21:22:49 +08:00
|
|
|
|
"enterprise/common/global"
|
2023-08-31 22:20:40 +08:00
|
|
|
|
"enterprise/common/model"
|
2023-10-08 21:22:49 +08:00
|
|
|
|
"fmt"
|
2024-08-09 13:57:03 +08:00
|
|
|
|
"git.u8t.cn/open/gosdk/qyweixin"
|
2023-08-31 22:20:40 +08:00
|
|
|
|
log "github.com/sirupsen/logrus"
|
2023-09-01 20:01:30 +08:00
|
|
|
|
"github.com/smbrave/goutil"
|
2023-08-31 22:20:40 +08:00
|
|
|
|
"github.com/spf13/cast"
|
2023-09-01 20:01:30 +08:00
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
2023-08-31 22:20:40 +08:00
|
|
|
|
)
|
|
|
|
|
|
2024-01-07 17:17:33 +08:00
|
|
|
|
type Staff struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Staff) SendStaffSalaryBill(month string) {
|
2023-10-08 21:22:49 +08:00
|
|
|
|
staffSalarys, err := dao.NewStaffSalaryDao().Query(month)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, staffSalary := range staffSalarys {
|
|
|
|
|
if staffSalary.BaseSalary < 0.1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-04-07 17:08:39 +08:00
|
|
|
|
userConfig, err := dao.NewUserConfigDao().GetByUsername(staffSalary.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
2023-10-08 21:22:49 +08:00
|
|
|
|
message := make([]string, 0)
|
|
|
|
|
message = append(message, fmt.Sprintf("【工资单】[%s][%s]", staffSalary.Username, month))
|
|
|
|
|
message = append(message, fmt.Sprintf("基本工资:%.2f", staffSalary.BaseSalary))
|
2024-04-02 12:14:23 +08:00
|
|
|
|
message = append(message, fmt.Sprintf("出勤工资:%.2f", staffSalary.AttendSalary))
|
2023-10-08 21:22:49 +08:00
|
|
|
|
if staffSalary.AwardSalary >= 0.1 {
|
|
|
|
|
message = append(message, fmt.Sprintf("额外工资:%.2f", staffSalary.AwardSalary))
|
|
|
|
|
}
|
|
|
|
|
message = append(message, fmt.Sprintf("社保扣除:%.2f", staffSalary.SocialInsurence))
|
|
|
|
|
message = append(message, fmt.Sprintf("个税扣除:%.2f", staffSalary.PersonalTax))
|
2024-04-02 13:54:39 +08:00
|
|
|
|
message = append(message, fmt.Sprintf("实发工资:%.2f", staffSalary.GetRealSalary()))
|
2023-10-08 21:22:49 +08:00
|
|
|
|
|
|
|
|
|
if err := global.SendMessage([]string{"jiangyong"}, strings.Join(message, "\n")); err != nil {
|
|
|
|
|
log.Errorf("send message error :%s", err.Error())
|
|
|
|
|
}
|
2024-04-07 17:08:39 +08:00
|
|
|
|
if userConfig != nil && cast.ToBool(userConfig.Get(model.StaffSalaryNotify)) == true {
|
2024-05-06 10:35:03 +08:00
|
|
|
|
if err := global.SendMessage([]string{userConfig.Username}, strings.Join(message, "\n")); err != nil {
|
2024-04-07 17:08:39 +08:00
|
|
|
|
log.Errorf("send message error :%s", err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-08 21:22:49 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 17:17:33 +08:00
|
|
|
|
func (s *Staff) SyncStaffInfo() {
|
2023-08-31 22:20:40 +08:00
|
|
|
|
cfg := config.GetConfig()
|
2024-01-22 23:21:37 +08:00
|
|
|
|
hrAssiant := qyweixin.NewAppHr(&qyweixin.AppConfig{
|
|
|
|
|
Corpid: cfg.QyWeixin.Corpid,
|
2024-01-24 16:48:04 +08:00
|
|
|
|
Secret: cfg.QyWeixin.EnterpriseSecret,
|
|
|
|
|
Agent: cfg.QyWeixin.EnterpriseAgent,
|
2024-01-22 23:21:37 +08:00
|
|
|
|
})
|
2023-08-31 22:20:40 +08:00
|
|
|
|
|
|
|
|
|
userConfigs, err := dao.NewUserConfigDao().Query(model.UserConfigStatusNormal)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("query staff db error :%s", err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, user := range userConfigs {
|
|
|
|
|
staffInfo, err := hrAssiant.GetStaffInfo(user.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("getstaff info username[%s] error :%s", user.Username, err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
staff, err := dao.NewStaffInfoDao().GetByUsername(user.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if staff == nil {
|
|
|
|
|
staff = new(model.StaffInfo)
|
|
|
|
|
}
|
|
|
|
|
staff.Username = staffInfo.UserName
|
|
|
|
|
staff.Realname = staffInfo.RealName
|
|
|
|
|
staff.Idno = staffInfo.Idno
|
2023-09-01 20:01:30 +08:00
|
|
|
|
staff.StaffType = staffInfo.StaffType
|
2023-08-31 22:20:40 +08:00
|
|
|
|
staff.Salary = cast.ToString(staffInfo.Salary)
|
|
|
|
|
staff.EntryDate = staffInfo.EntryDate
|
|
|
|
|
staff.OfficialDate = staffInfo.OfficialDate
|
|
|
|
|
staff.BirthDate = staffInfo.BirthDate
|
|
|
|
|
staff.BankName = staffInfo.BankName
|
|
|
|
|
staff.BankCard = staffInfo.BankCard
|
|
|
|
|
|
|
|
|
|
if staff.Id == 0 {
|
|
|
|
|
_, err = dao.NewStaffInfoDao().Create(staff)
|
|
|
|
|
} else {
|
|
|
|
|
err = dao.NewStaffInfoDao().Update(staff)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-01 20:01:30 +08:00
|
|
|
|
|
2024-01-07 17:17:33 +08:00
|
|
|
|
func (s *Staff) SyncStaffSalary(month string) {
|
2023-09-04 19:14:02 +08:00
|
|
|
|
if month == "" {
|
|
|
|
|
month = time.Now().AddDate(0, -1, 0).Format("2006-01")
|
|
|
|
|
}
|
|
|
|
|
month = strings.ReplaceAll(month, "-", "")
|
2024-07-03 13:24:41 +08:00
|
|
|
|
maxTotalDays := s.getTotalWorkDayMax(month)
|
2023-09-01 20:01:30 +08:00
|
|
|
|
monthTime, _ := time.ParseInLocation("200601", month, time.Local)
|
|
|
|
|
startDate := cast.ToInt(monthTime.Format("20060102"))
|
|
|
|
|
endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102"))
|
|
|
|
|
salaryDao := dao.NewStaffSalaryDao()
|
2024-01-31 20:49:43 +08:00
|
|
|
|
|
2024-01-31 22:54:06 +08:00
|
|
|
|
staffs, err := dao.NewStaffInfoDao().Query(model.StaffInfoStatusNormal)
|
2024-01-31 20:49:43 +08:00
|
|
|
|
if err != nil {
|
2024-01-31 22:54:06 +08:00
|
|
|
|
log.Errorf("query staff db error :%s", err.Error())
|
2024-01-31 20:49:43 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2023-09-01 20:01:30 +08:00
|
|
|
|
for _, staff := range staffs {
|
2024-07-04 21:04:50 +08:00
|
|
|
|
|
|
|
|
|
entryTime, _ := time.ParseInLocation("2006-01-02", staff.EntryDate, time.Local)
|
|
|
|
|
officalTime, _ := time.ParseInLocation("2006-01-02", staff.OfficialDate, 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)
|
|
|
|
|
isOfficialMonth := goutil.If(cast.ToInt(officalTime.Format("200601")) == cast.ToInt(month), true, false)
|
|
|
|
|
isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false)
|
|
|
|
|
|
|
|
|
|
// 已离职的员工不处理
|
|
|
|
|
if staff.LeaveDate != "" && !isLeaveMonth {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 20:01:30 +08:00
|
|
|
|
salary, err := salaryDao.GetBy(staff.Username, month)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if salary == nil {
|
|
|
|
|
salary = new(model.StaffSalary)
|
|
|
|
|
salary.Month = month
|
|
|
|
|
salary.Username = staff.Username
|
|
|
|
|
}
|
2024-01-31 22:54:06 +08:00
|
|
|
|
|
2023-09-01 20:01:30 +08:00
|
|
|
|
config, err := dao.NewUserConfigDao().GetByUsername(staff.Username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-05-06 10:41:33 +08:00
|
|
|
|
if config == nil {
|
|
|
|
|
log.Errorf("username[%s] not config,skip!!", staff.Username)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-01-31 20:49:43 +08:00
|
|
|
|
|
|
|
|
|
// 试用期折扣
|
2023-09-01 20:01:30 +08:00
|
|
|
|
discount := cast.ToFloat64(config.Get(model.StaffSalaryExpDiscount))
|
|
|
|
|
if discount == 0.0 {
|
|
|
|
|
discount = 0.8
|
|
|
|
|
}
|
2024-01-31 20:49:43 +08:00
|
|
|
|
if cast.ToInt(monthTime.Format("200601")) > cast.ToInt(officalTime.Format("200601")) {
|
|
|
|
|
discount = 1.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//社保
|
2023-09-01 20:01:30 +08:00
|
|
|
|
socialInsurence := cast.ToFloat64(config.Get(model.StaffSalarySocialInsurence))
|
2024-07-03 13:24:41 +08:00
|
|
|
|
//if isEntryMonth && entryTime.Day() >= 15 {
|
|
|
|
|
if isEntryMonth { //入职月不买社保
|
2024-01-31 20:49:43 +08:00
|
|
|
|
socialInsurence = 0
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-31 22:54:06 +08:00
|
|
|
|
// 休假申请
|
2024-04-25 12:23:19 +08:00
|
|
|
|
approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(staff.Username, month, "")
|
2024-01-31 20:49:43 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
holiday := float64(0)
|
|
|
|
|
surplusHoliday := float64(0)
|
|
|
|
|
for _, vac := range approveVacations {
|
|
|
|
|
holiday += vac.VacationDuration
|
2024-02-02 16:19:30 +08:00
|
|
|
|
surplusHoliday += goutil.If(vac.VacationDuration < 1, 1-vac.VacationDuration, 0)
|
2024-01-31 20:49:43 +08:00
|
|
|
|
}
|
2023-09-04 19:14:02 +08:00
|
|
|
|
|
2024-01-31 22:54:06 +08:00
|
|
|
|
// 打卡正常天数+补卡天数 为正常出勤天数
|
2024-06-02 01:21:45 +08:00
|
|
|
|
totalDays := s.getTotalWorkDay(staff.Username, month)
|
2024-03-01 18:04:58 +08:00
|
|
|
|
realWorkDays := s.getRealWorkDay(staff.Username, month)
|
|
|
|
|
approvalCheckinDay := s.getApprovalCheckinDay(staff.Username, month)
|
2024-01-31 22:54:06 +08:00
|
|
|
|
extra := make(map[string]interface{})
|
2023-09-01 20:01:30 +08:00
|
|
|
|
salary.BaseSalary = cast.ToFloat64(staff.Salary)
|
2024-01-31 20:49:43 +08:00
|
|
|
|
salary.Holiday = holiday
|
2023-09-01 20:01:30 +08:00
|
|
|
|
entryDate := cast.ToInt(strings.ReplaceAll(staff.EntryDate, "-", ""))
|
|
|
|
|
officialDate := cast.ToInt(strings.ReplaceAll(staff.OfficialDate, "-", ""))
|
|
|
|
|
|
2024-01-31 20:49:43 +08:00
|
|
|
|
if discount < 1.0 && isOfficialMonth {
|
|
|
|
|
totalMonthDay := float64(endDate - startDate + 1)
|
|
|
|
|
discount = discount*float64(officialDate-startDate)/totalMonthDay + 1*float64(endDate-officialDate+1)/totalMonthDay
|
2023-09-01 20:01:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 13:24:41 +08:00
|
|
|
|
// 入职月或离职月以大多数人的出勤天数为准
|
|
|
|
|
if isEntryMonth || isLeaveMonth {
|
|
|
|
|
totalDays = maxTotalDays
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 16:19:30 +08:00
|
|
|
|
realWorkDays = realWorkDays + surplusHoliday
|
2024-04-02 12:14:23 +08:00
|
|
|
|
attendSalary := salary.BaseSalary * (realWorkDays / float64(totalDays)) * discount
|
2024-01-31 22:54:06 +08:00
|
|
|
|
staffSalaryPerDay := cast.ToFloat64(config.Get(model.StaffSalaryPerDay))
|
|
|
|
|
if staffSalaryPerDay > 0 {
|
2024-04-02 12:14:23 +08:00
|
|
|
|
attendSalary = staffSalaryPerDay * realWorkDays
|
2024-01-31 22:54:06 +08:00
|
|
|
|
}
|
2023-09-01 20:01:30 +08:00
|
|
|
|
|
|
|
|
|
extra["discount"] = discount
|
2024-03-01 18:04:58 +08:00
|
|
|
|
extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖
|
2023-09-01 20:01:30 +08:00
|
|
|
|
extra["socialInsurence"] = socialInsurence
|
|
|
|
|
extra["days"] = endDate - startDate + 1
|
2024-01-31 20:49:43 +08:00
|
|
|
|
extra["startDate"] = startDate
|
|
|
|
|
extra["totalDays"] = totalDays
|
|
|
|
|
extra["realDays"] = realWorkDays
|
2023-09-01 20:01:30 +08:00
|
|
|
|
extra["startDate"] = startDate
|
|
|
|
|
extra["endDate"] = endDate
|
|
|
|
|
extra["officialDate"] = officialDate
|
|
|
|
|
extra["entryDate"] = entryDate
|
|
|
|
|
extra["isEntryMonth"] = isEntryMonth
|
|
|
|
|
extra["isOfficialMonth"] = isOfficialMonth
|
2024-01-31 20:49:43 +08:00
|
|
|
|
extra["surplusHoliday"] = surplusHoliday
|
2023-09-01 20:01:30 +08:00
|
|
|
|
|
2024-04-02 12:14:23 +08:00
|
|
|
|
salary.AttendSalary = butil.FloatCut(attendSalary)
|
2024-04-02 13:54:39 +08:00
|
|
|
|
salary.RealSalary = salary.GetRealSalary()
|
2023-09-01 20:01:30 +08:00
|
|
|
|
salary.SocialInsurence = socialInsurence
|
2023-09-04 19:14:02 +08:00
|
|
|
|
salary.Holiday = holiday
|
2024-01-31 20:49:43 +08:00
|
|
|
|
salary.TotalDay = float64(totalDays)
|
|
|
|
|
salary.RealDay = realWorkDays
|
|
|
|
|
|
2023-09-01 20:01:30 +08:00
|
|
|
|
salary.Extra = goutil.EncodeJSONIndent(extra)
|
|
|
|
|
if salary.Id == 0 {
|
|
|
|
|
_, err = dao.NewStaffSalaryDao().Create(salary)
|
|
|
|
|
} else {
|
|
|
|
|
err = dao.NewStaffSalaryDao().Update(salary)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2024-01-31 22:54:06 +08:00
|
|
|
|
|
2024-03-01 18:04:58 +08:00
|
|
|
|
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(username, month string) float64 {
|
2024-02-02 16:19:30 +08:00
|
|
|
|
realWorkdays := float64(0)
|
|
|
|
|
userCheckins, err := dao.NewCheckinDao().Query(username, month, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("db error :%s", err.Error())
|
2024-03-01 18:04:58 +08:00
|
|
|
|
return realWorkdays
|
2024-02-02 16:19:30 +08:00
|
|
|
|
}
|
2024-03-01 17:52:24 +08:00
|
|
|
|
approvalCheckinDay := 0
|
2024-02-02 16:19:30 +08:00
|
|
|
|
for _, checkin := range userCheckins {
|
|
|
|
|
if checkin.Exception == "" {
|
|
|
|
|
realWorkdays += 1
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(username, checkin.Day)
|
|
|
|
|
if approvalCheckin != nil {
|
|
|
|
|
realWorkdays += 1
|
2024-03-01 17:52:24 +08:00
|
|
|
|
approvalCheckinDay += 1
|
2024-02-02 16:19:30 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 迟到的按时间折算
|
|
|
|
|
if strings.Contains(checkin.Exception, "上班打卡:时间异常") {
|
|
|
|
|
stTime := time.Unix(checkin.StartTime, 0)
|
|
|
|
|
later := float64(stTime.Hour() - 8) //迟到小时数,从9点算
|
2024-06-02 00:37:12 +08:00
|
|
|
|
if later > 8 {
|
|
|
|
|
later = 8
|
|
|
|
|
}
|
2024-02-02 16:19:30 +08:00
|
|
|
|
realWorkdays += (8 - later) / 8
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-01 18:04:58 +08:00
|
|
|
|
return realWorkdays
|
2024-02-02 16:19:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 01:21:45 +08:00
|
|
|
|
func (s *Staff) getTotalWorkDay(username, month string) int64 {
|
|
|
|
|
checkins, _ := dao.NewCheckinDao().Query(username, month, false)
|
|
|
|
|
return int64(len(checkins))
|
|
|
|
|
}
|
2024-07-03 13:24:41 +08:00
|
|
|
|
|
|
|
|
|
func (s *Staff) getTotalWorkDayMax(month string) int64 {
|
2024-01-31 22:54:06 +08:00
|
|
|
|
// 最多人数的应出勤天数 为真正的出勤天数
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
}
|