diff --git a/common/config/config.go b/common/config/config.go index d4655ea..016c341 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -55,11 +55,12 @@ type WxPay struct { } type Config struct { - Server *Server `toml:"server"` - Mysql *Mysql `toml:"mysql"` - Redis *Redis `toml:"redis"` - QyWeixin *QyWeixin `toml:"qyweixin"` - WxPay *WxPay `toml:"wxpay"` + Server *Server `toml:"server"` + Mysql *Mysql `toml:"mysql"` + UnifyMysql *Mysql `toml:"unify_mysql"` + Redis *Redis `toml:"redis"` + QyWeixin *QyWeixin `toml:"qyweixin"` + WxPay *WxPay `toml:"wxpay"` } func GetEnv() string { diff --git a/common/config/const.go b/common/config/const.go index 07fcd90..cf77fd6 100644 --- a/common/config/const.go +++ b/common/config/const.go @@ -6,5 +6,7 @@ var ( ) var ( - ContextParam = "context_params" + ContextParam = "context_params" + SalarySocialInsurence float64 = 462.7 + SalaryHouseFund float64 = 105 ) diff --git a/common/dao/dao.go b/common/dao/dao.go index 7ef5c83..b49ce75 100644 --- a/common/dao/dao.go +++ b/common/dao/dao.go @@ -3,13 +3,18 @@ package dao import "gorm.io/gorm" var ( - db *gorm.DB + db *gorm.DB + unifyDB *gorm.DB ) func SetDB(d *gorm.DB) { db = d } +func SetUnifyDB(d *gorm.DB) { + unifyDB = d +} + func GetDB() *gorm.DB { return db } diff --git a/common/dao/user_config.go b/common/dao/staff_config.go similarity index 69% rename from common/dao/user_config.go rename to common/dao/staff_config.go index 2587b61..95a99db 100644 --- a/common/dao/user_config.go +++ b/common/dao/staff_config.go @@ -14,16 +14,16 @@ func NewUserConfigDao() *UserConfigDao { } func (d *UserConfigDao) TableName() string { - return "user_config" + return "staff_config" } -func (d *UserConfigDao) Create(o *model.UserConfig) (int64, error) { +func (d *UserConfigDao) Create(o *model.StaffConfig) (int64, error) { o.CreateTime = time.Now().Unix() res := GetDB().Table(d.TableName()).Create(o) return o.Id, res.Error } -func (d *UserConfigDao) Update(o *model.UserConfig) error { +func (d *UserConfigDao) Update(o *model.StaffConfig) error { o.UpdateTime = time.Now().Unix() tx := GetDB().Table(d.TableName()) res := tx.Save(o) @@ -31,12 +31,12 @@ func (d *UserConfigDao) Update(o *model.UserConfig) error { } func (d *UserConfigDao) Delete(id int64) error { - res := GetDB().Table(d.TableName()).Delete(&model.UserConfig{}, id) + res := GetDB().Table(d.TableName()).Delete(&model.StaffConfig{}, id) return res.Error } -func (d *UserConfigDao) Get(id int64) (*model.UserConfig, error) { - var u model.UserConfig +func (d *UserConfigDao) Get(id int64) (*model.StaffConfig, error) { + var u model.StaffConfig tx := GetDB().Table(d.TableName()) tx = tx.Where("id = ?", id) res := tx.First(&u) @@ -50,8 +50,8 @@ func (d *UserConfigDao) Get(id int64) (*model.UserConfig, error) { return &u, nil } -func (d *UserConfigDao) GetByUsername(username string) (*model.UserConfig, error) { - var u model.UserConfig +func (d *UserConfigDao) GetByUsername(username string) (*model.StaffConfig, error) { + var u model.StaffConfig tx := GetDB().Table(d.TableName()) tx = tx.Where("username = ?", username) res := tx.First(&u) @@ -65,8 +65,8 @@ func (d *UserConfigDao) GetByUsername(username string) (*model.UserConfig, error return &u, nil } -func (d *UserConfigDao) Query(status int) ([]*model.UserConfig, error) { - var u []*model.UserConfig +func (d *UserConfigDao) Query(status int) ([]*model.StaffConfig, error) { + var u []*model.StaffConfig tx := GetDB().Table(d.TableName()) if status != 0 { diff --git a/common/dao/unify_ad_data.go b/common/dao/unify_ad_data.go new file mode 100644 index 0000000..98d07a1 --- /dev/null +++ b/common/dao/unify_ad_data.go @@ -0,0 +1,43 @@ +package dao + +import ( + "enterprise/common/model" + "gorm.io/gorm" +) + +type UnifyAdData struct { +} + +func NewUnifyAdData() *UnifyAdData { + return &UnifyAdData{} +} + +func (d *UnifyAdData) TableName() string { + return "unify_ad_data" +} + +func (d *UnifyAdData) QueryOwnerData(owner, startDay, endDay string) ([]*model.UnifyAdData, error) { + tx := unifyDB.Table(d.TableName()) + + var o []*model.UnifyAdData + + tx = tx.Where("data_type = ?", "account_report") + tx = tx.Where("owner = ?", owner) + if startDay != "" { + tx = tx.Where("day >= ?", startDay) + } + if endDay != "" { + tx = tx.Where("day <= ?", endDay) + } + tx.Order("day DESC") + + tx = tx.Find(&o) + if tx.Error == gorm.ErrRecordNotFound { + return o, nil + } + + if tx.Error != nil { + return nil, tx.Error + } + return o, nil +} diff --git a/common/global/db.go b/common/global/db.go index 64972b9..e86e2b9 100644 --- a/common/global/db.go +++ b/common/global/db.go @@ -69,6 +69,16 @@ func initDB() error { } db.Logger = &DBLogger{threshold: int64(2000)} dao.SetDB(db) + + unifyDsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", cfg.UnifyMysql.User, + cfg.UnifyMysql.Pass, cfg.UnifyMysql.Host, cfg.UnifyMysql.Port, cfg.UnifyMysql.Db) + db, err = gorm.Open(mysql.Open(unifyDsn), &gorm.Config{}) + if err != nil { + log.Errorf("open dsn[%s] error[%s]", dsn, err) + return err + } + db.Logger = &DBLogger{threshold: int64(2000)} + dao.SetUnifyDB(db) return nil } diff --git a/common/model/staff_info.go b/common/model/staff_info.go index 9c7b6a0..505f396 100644 --- a/common/model/staff_info.go +++ b/common/model/staff_info.go @@ -13,7 +13,8 @@ type StaffInfo struct { StaffType string Phone string Idno string - Salary string + BaseSalary string + PerfSalary string EntryDate string OfficialDate string LeaveDate string diff --git a/common/model/staff_salary.go b/common/model/staff_salary.go index 0e4dc1f..8d7c54f 100644 --- a/common/model/staff_salary.go +++ b/common/model/staff_salary.go @@ -1,6 +1,11 @@ package model -import butil "enterprise/base/util" +import ( + "encoding/json" + butil "enterprise/base/util" + "github.com/spf13/cast" + "strings" +) var ( StaffSalaryStatusNormal = 1 @@ -12,10 +17,12 @@ type StaffSalary struct { Username string Month string BaseSalary float64 + PerfSalary float64 AttendSalary float64 RealSalary float64 AwardSalary float64 SocialInsurence float64 + HouseFund float64 PersonalTax float64 TotalDay float64 RealDay float64 @@ -26,5 +33,28 @@ type StaffSalary struct { } func (s *StaffSalary) GetRealSalary() float64 { - return butil.FloatCut(s.AttendSalary + s.AwardSalary - s.SocialInsurence - s.PersonalTax) + return butil.FloatCut(s.AttendSalary + s.PerfSalary + s.AwardSalary - s.SocialInsurence - s.PersonalTax - s.HouseFund) +} + +func (s *StaffSalary) GetShouldSalary() float64 { + return butil.FloatCut(s.AttendSalary + s.PerfSalary + s.AwardSalary) +} + +type StaffSalarySort []*StaffSalary + +func (s StaffSalarySort) Len() int { + return len(s) +} + +func (s StaffSalarySort) Less(i, j int) bool { + var iExtra map[string]string + var jExtra map[string]string + json.Unmarshal([]byte(s[i].Extra), &iExtra) + json.Unmarshal([]byte(s[j].Extra), &jExtra) + + return cast.ToInt64(strings.ReplaceAll(iExtra["entryDate"], "-", "")) < cast.ToInt64(strings.ReplaceAll(jExtra["entryDate"], "-", "")) + +} +func (s StaffSalarySort) Swap(i, j int) { + s[i], s[j] = s[j], s[i] } diff --git a/common/model/unify_ad_data.go b/common/model/unify_ad_data.go new file mode 100644 index 0000000..7d9ccb6 --- /dev/null +++ b/common/model/unify_ad_data.go @@ -0,0 +1,27 @@ +package model + +type UnifyAdData struct { + Id int64 `json:"id"` + AppId int64 `json:"appId"` + Day string `json:"day"` + AccountType string `json:"accountType"` + AccountName string `json:"accountName"` + AccountId string `json:"accountId"` + Owner string `json:"owner"` + Balance int64 `json:"balance"` + DataType string `json:"dataType"` + DataName string `json:"dataName"` + DataId string `json:"dataId"` + Cost int64 `json:"cost"` + ReturnCost int64 `json:"return_cost"` + PayAmount int64 `json:"payAmount"` + Impression int64 `json:"impression"` + Click int64 `json:"click"` + Download int64 `json:"download"` + Active int64 `json:"active"` + Register int64 `json:"register"` + Pay int64 `json:"pay"` + CreateTime int64 `json:"createTime"` + UpdateTime int64 `json:"updateTime"` + Raw string `json:"raw"` +} diff --git a/common/model/user_config.go b/common/model/user_config.go index 9d720fb..18348d7 100644 --- a/common/model/user_config.go +++ b/common/model/user_config.go @@ -1,23 +1,25 @@ package model -import "encoding/json" +import ( + "encoding/json" + "github.com/smbrave/goutil" +) var ( - CheckinOndutyMoneyEnable = "checkin.onduty.money.enable" //上班打卡红包 - CheckinOffdutyMoneyEnable = "checkin.offduty.money.enable" //下班打卡红包 - CheckinOndutyMoney = "checkin.onduty.money" //上班打卡金额 - CheckinOffdutyMoney = "checkin.offduty.money" //下班打卡金额 StaffSalaryExpDiscount = "staff.salary.exp.discount" //试用期薪资折扣 StaffSalarySocialInsurence = "staff.salary.social.insurence" //社保扣除金额 + StaffSalaryHouseFund = "staff.salary.house.fund" //住房公积金 StaffSalaryPerDay = "staff.salary.per.day" //按天计算的工资 StaffSalaryNotify = "staff.salary.notify" //是否发送工资单 + StaffSalaryProfitDeduct = "staff.salary.profit.deduct" //广告收益提成 + StaffSalaryProfitTarget = "staff.salary.profit.target" //广告收益目标 ) var ( - UserConfigStatusNormal = 1 - UserConfigStatusDisable = 2 + StaffConfigStatusNormal = 1 + StaffConfigStatusDisable = 2 ) -type UserConfig struct { +type StaffConfig struct { Id int64 Username string Config string @@ -26,8 +28,14 @@ type UserConfig struct { UpdateTime int64 } -func (u *UserConfig) Get(key string) interface{} { +func (u *StaffConfig) Get(key string) interface{} { config := make(map[string]interface{}) json.Unmarshal([]byte(u.Config), &config) return config[key] } + +func (u *StaffConfig) Exist(key string) bool { + config := make(map[string]interface{}) + json.Unmarshal([]byte(u.Config), &config) + return goutil.If(config[key] == nil, false, true) +} diff --git a/conf/server.conf.dev b/conf/server.conf.dev index f6d1483..2058886 100644 --- a/conf/server.conf.dev +++ b/conf/server.conf.dev @@ -10,6 +10,14 @@ user = "root" pass = "ZW5aaGVuMIIBIj" db = "enterprise" +[unify_mysql] +host = "192.168.2.50" +port = 13307 +user = "unify" +pass = "MDE2LCJIYXNoSWQiOjY" +db = "unify" +threshold = 5000 + [redis] addr="192.168.2.50:6379" db=0 diff --git a/conf/server.conf.prod b/conf/server.conf.prod index ae2d38f..c85cca4 100644 --- a/conf/server.conf.prod +++ b/conf/server.conf.prod @@ -10,6 +10,14 @@ user = "root" pass = "ZW5aaGVuMIIBIj" db = "enterprise" +[unify_mysql] +host = "10.0.2.157" +port = 3307 +user = "unify" +pass = "MDE2LCJIYXNoSWQiOjY" +db = "unify" +threshold = 5000 + [redis] addr="127.0.0.1:6379" db=0 diff --git a/go.mod b/go.mod index 47329a7..de79ef7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module enterprise go 1.21.6 require ( - git.u8t.cn/open/gosdk v0.0.0-20240927033033-8d021096f778 + git.u8t.cn/open/gosdk v0.0.0-20241104101317-10492bab9fd7 github.com/ArtisanCloud/PowerWeChat/v3 v3.2.27 github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 github.com/gin-gonic/gin v1.10.0 diff --git a/server/service/staff_salary.go b/server/service/staff_salary.go index bff6320..c0cbab3 100644 --- a/server/service/staff_salary.go +++ b/server/service/staff_salary.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/cast" excelize "github.com/xuri/excelize/v2" "os" + "sort" "time" "net/http" @@ -33,7 +34,7 @@ func (s *StaffSalary) Agent(month string, ctx *gin.Context) { panic(config.ErrDb.New().Append(err)) } - header := []string{"姓名", "身份证号", "电话", "基本工资", "出勤工资", "奖金", "社保扣除", "个税扣除", "应发工资"} + header := []string{"姓名", "身份证号", "电话", "应发工资", "社保扣除", "公积金扣除", "个税扣除", "实发工资"} datas := make([][]string, 0) for _, staff := range staffSalarys { baseInfo, err := dao.NewStaffInfoDao().GetByUsername(staff.Username) @@ -51,10 +52,9 @@ func (s *StaffSalary) Agent(month string, ctx *gin.Context) { item = append(item, baseInfo.Realname) item = append(item, cast.ToString(baseInfo.Idno)) item = append(item, cast.ToString(baseInfo.Phone)) - item = append(item, cast.ToString(staff.BaseSalary)) - item = append(item, cast.ToString(staff.AttendSalary)) - item = append(item, cast.ToString(staff.AwardSalary)) + item = append(item, cast.ToString(staff.GetShouldSalary())) item = append(item, cast.ToString(staff.SocialInsurence)) + item = append(item, cast.ToString(staff.HouseFund)) item = append(item, cast.ToString(staff.PersonalTax)) item = append(item, cast.ToString(staff.GetRealSalary())) @@ -152,8 +152,9 @@ func (s *StaffSalary) Summary(month string, ctx *gin.Context) { if err != nil { panic(config.ErrDb.New().Append(err)) } + sort.Sort(model.StaffSalarySort(staffSalarys)) - header := []string{"姓名", "身份证号", "入职日期", "转正日期", "基本工资", "出勤工资", "奖金", "社保扣除", "个税扣除", "应出勤天数", "实际出勤天数", "补卡天数", "请假天数", "实发工资", "状态"} + header := []string{"姓名", "身份证号", "入职日期", "转正日期", "基本工资", "绩效工资", "出勤工资", "奖金", "社保扣除", "公积金扣除", "个税扣除", "应出勤天数", "实际出勤天数", "出勤率", "补卡天数", "请假天数", "实发工资", "状态"} datas := make([][]string, 0) summary := new(model.StaffSalary) for _, staff := range staffSalarys { @@ -173,12 +174,15 @@ func (s *StaffSalary) Summary(month string, ctx *gin.Context) { item = append(item, cast.ToString(baseInfo.EntryDate)) item = append(item, cast.ToString(baseInfo.OfficialDate)) item = append(item, cast.ToString(staff.BaseSalary)) - item = append(item, cast.ToString(staff.AttendSalary)) - item = append(item, cast.ToString(staff.AwardSalary)) + item = append(item, cast.ToString(butil.FloatCut(staff.PerfSalary))) + item = append(item, cast.ToString(butil.FloatCut(staff.AttendSalary))) + item = append(item, cast.ToString(butil.FloatCut(staff.AwardSalary))) item = append(item, cast.ToString(staff.SocialInsurence)) + item = append(item, cast.ToString(staff.HouseFund)) item = append(item, cast.ToString(staff.PersonalTax)) item = append(item, cast.ToString(staff.TotalDay)) item = append(item, cast.ToString(staff.RealDay)) + item = append(item, fmt.Sprintf("%d%%", int(100*staff.RealDay/staff.TotalDay))) item = append(item, cast.ToString(extra["approvalCheckinDay"])) item = append(item, cast.ToString(staff.Holiday)) item = append(item, cast.ToString(staff.GetRealSalary())) @@ -190,21 +194,26 @@ func (s *StaffSalary) Summary(month string, ctx *gin.Context) { datas = append(datas, item) summary.BaseSalary += staff.BaseSalary + summary.PerfSalary += staff.PerfSalary summary.AttendSalary += staff.AttendSalary summary.AwardSalary += staff.AwardSalary summary.SocialInsurence += staff.SocialInsurence summary.PersonalTax += staff.PersonalTax summary.Holiday += staff.Holiday + summary.HouseFund += staff.HouseFund } summary.Holiday = butil.FloatCut(summary.Holiday) summary.BaseSalary = butil.FloatCut(summary.BaseSalary) + summary.PerfSalary = butil.FloatCut(summary.PerfSalary) summary.AttendSalary = butil.FloatCut(summary.AttendSalary) summary.AwardSalary = butil.FloatCut(summary.AwardSalary) summary.SocialInsurence = butil.FloatCut(summary.SocialInsurence) + summary.HouseFund = butil.FloatCut(summary.HouseFund) summary.PersonalTax = butil.FloatCut(summary.PersonalTax) datas = append(datas, []string{"合计", "-", "-", "-", - cast.ToString(summary.BaseSalary), cast.ToString(summary.AttendSalary), cast.ToString(summary.AwardSalary), cast.ToString(summary.SocialInsurence), - cast.ToString(summary.PersonalTax), "-", "-", "-", cast.ToString(summary.Holiday), + cast.ToString(summary.BaseSalary), cast.ToString(summary.PerfSalary), cast.ToString(summary.AttendSalary), cast.ToString(summary.AwardSalary), cast.ToString(summary.SocialInsurence), + cast.ToString(summary.HouseFund), + cast.ToString(summary.PersonalTax), "-", "-", "-", "-", cast.ToString(summary.Holiday), cast.ToString(butil.FloatCut(summary.GetRealSalary())), "-"}) if xls != "" { diff --git a/service/staff.go b/service/staff.go new file mode 100644 index 0000000..7787349 --- /dev/null +++ b/service/staff.go @@ -0,0 +1,289 @@ +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 + +} diff --git a/worker/staff.go b/worker/staff.go index 7bfc31c..51ee105 100644 --- a/worker/staff.go +++ b/worker/staff.go @@ -1,17 +1,16 @@ package worker import ( - butil "enterprise/base/util" "enterprise/common/config" "enterprise/common/dao" "enterprise/common/global" "enterprise/common/model" + "enterprise/service" "fmt" "git.u8t.cn/open/gosdk/qyweixin" log "github.com/sirupsen/logrus" "github.com/smbrave/goutil" "github.com/spf13/cast" - "math" "strings" "time" ) @@ -44,7 +43,8 @@ func (s *Staff) MontorWorkAge() { message = append(message, fmt.Sprintf("【员工半年提醒】[%s]", staff.Realname)) message = append(message, fmt.Sprintf("入职时间:%s", staff.EntryDate)) message = append(message, fmt.Sprintf("入职年限:%.1f", float64(spanMonth)/12)) - message = append(message, fmt.Sprintf("基本工资:%s", staff.Salary)) + message = append(message, fmt.Sprintf("基本工资:%s", staff.BaseSalary)) + message = append(message, fmt.Sprintf("绩效工资:%s", staff.PerfSalary)) message = append(message, fmt.Sprintf("身份证号:%s", staff.Idno)) if err := global.SendMessage([]string{"jiangyong"}, strings.Join(message, "\n")); err != nil { @@ -99,7 +99,7 @@ func (s *Staff) SyncStaffInfo() { Agent: cfg.QyWeixin.EnterpriseAgent, }) - userConfigs, err := dao.NewUserConfigDao().Query(model.UserConfigStatusNormal) + userConfigs, err := dao.NewUserConfigDao().Query(model.StaffConfigStatusNormal) if err != nil { log.Errorf("query staff db error :%s", err.Error()) return @@ -120,15 +120,16 @@ func (s *Staff) SyncStaffInfo() { } staff.Username = staffInfo.UserName staff.Realname = staffInfo.RealName - staff.Idno = staffInfo.Idno - staff.StaffType = staffInfo.StaffType - 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 - staff.AlipayUid = staffInfo.AlipayUid + staff.Idno = staffInfo.Extra["11015"] + staff.StaffType = staffInfo.Extra["12003"] + staff.BaseSalary = staffInfo.Extra["20001"] + staff.PerfSalary = staffInfo.Extra["20005"] + staff.EntryDate = time.Unix(cast.ToInt64(staffInfo.Extra["12018"]), 0).Format("2006-01-02") + staff.OfficialDate = time.Unix(cast.ToInt64(staffInfo.Extra["12023"]), 0).Format("2006-01-02") + staff.BirthDate = time.Unix(cast.ToInt64(staffInfo.Extra["11005"]), 0).Format("2006-01-02") + staff.BankName = staffInfo.Extra["13001"] + staff.BankCard = staffInfo.Extra["13002"] + staff.AlipayUid = staffInfo.Extra["20004"] if staff.Id == 0 { _, err = dao.NewStaffInfoDao().Create(staff) @@ -147,11 +148,6 @@ func (s *Staff) SyncStaffSalary(month string) { month = time.Now().AddDate(0, -1, 0).Format("2006-01") } month = strings.ReplaceAll(month, "-", "") - maxTotalDays := s.getTotalWorkDayMax(month) - 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() staffs, err := dao.NewStaffInfoDao().Query(model.StaffInfoStatusNormal) if err != nil { @@ -160,128 +156,33 @@ func (s *Staff) SyncStaffSalary(month string) { } for _, staff := range staffs { - 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 - } - - 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 - } - - config, err := dao.NewUserConfigDao().GetByUsername(staff.Username) - if err != nil { - log.Errorf("db error :%s", err.Error()) - continue - } - if config == nil { - log.Errorf("username[%s] not config,skip!!", staff.Username) - continue - } - - // 试用期折扣 - discount := cast.ToFloat64(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 - } - - //社保 - socialInsurence := cast.ToFloat64(config.Get(model.StaffSalarySocialInsurence)) - //if isEntryMonth && entryTime.Day() >= 15 { - if isEntryMonth { //入职月不买社保 - socialInsurence = 0 - } - - // 休假申请 - approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(staff.Username, month, "") - if err != nil { - log.Errorf("db error :%s", err.Error()) - continue - } - 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) + if staff.LeaveDate != "" { + leaveTime, _ := time.ParseInLocation("2006-01-02", staff.LeaveDate, time.Local) + isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false) + if !isLeaveMonth { + continue } } - // 打卡正常天数+补卡天数 为正常出勤天数 - totalDays := s.getTotalWorkDay(staff.Username, month) - realWorkDays := s.getRealWorkDay(staff.EntryDate, staff.LeaveDate, staff.Username, month) - approvalCheckinDay := s.getApprovalCheckinDay(staff.Username, month) - extra := make(map[string]interface{}) - salary.BaseSalary = cast.ToFloat64(staff.Salary) - salary.Holiday = holiday - entryDate := cast.ToInt(strings.ReplaceAll(staff.EntryDate, "-", "")) - officialDate := cast.ToInt(strings.ReplaceAll(staff.OfficialDate, "-", "")) - - if discount < 1.0 && isOfficialMonth { - totalMonthDay := float64(endDate - startDate + 1) - discount = discount*float64(officialDate-startDate)/totalMonthDay + 1*float64(endDate-officialDate+1)/totalMonthDay + config, err := dao.NewUserConfigDao().GetByUsername(staff.Username) + if err != nil || config == nil { + log.Errorf("username[%s] not config,skip!!", staff.Username) + continue + } + salary, err := dao.NewStaffSalaryDao().GetBy(staff.Username, month) + if err != nil { + log.Errorf("db error :%s", err.Error()) + continue } - // 入职月或离职月以大多数人的出勤天数为准 - if isEntryMonth || isLeaveMonth { - totalDays = maxTotalDays + staffService := service.NewStaff(staff, config) + salary, err = staffService.CalcSalary(salary, month) + if err != nil { + log.Errorf("username[%s] staffService.CalcSalary error :%s", staff.Username, err.Error()) + continue } - realWorkDays = realWorkDays + surplusHoliday - attendSalary := salary.BaseSalary * (realWorkDays / float64(totalDays)) * discount - staffSalaryPerDay := cast.ToFloat64(config.Get(model.StaffSalaryPerDay)) - if staffSalaryPerDay > 0 { - attendSalary = staffSalaryPerDay * realWorkDays - } - - extra["discount"] = discount - extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖 - extra["socialInsurence"] = socialInsurence - extra["days"] = endDate - startDate + 1 - extra["startDate"] = startDate - extra["totalDays"] = totalDays - extra["realDays"] = realWorkDays - extra["startDate"] = startDate - extra["endDate"] = endDate - extra["officialDate"] = officialDate - extra["entryDate"] = entryDate - extra["isEntryMonth"] = isEntryMonth - extra["isOfficialMonth"] = isOfficialMonth - extra["surplusHoliday"] = surplusHoliday - - salary.AttendSalary = butil.FloatCut(attendSalary) - salary.RealSalary = salary.GetRealSalary() - salary.SocialInsurence = socialInsurence - salary.Holiday = holiday - salary.TotalDay = float64(totalDays) - salary.RealDay = realWorkDays - - salary.Extra = goutil.EncodeJSONIndent(extra) if salary.Id == 0 { _, err = dao.NewStaffSalaryDao().Create(salary) } else { @@ -293,109 +194,3 @@ func (s *Staff) SyncStaffSalary(month string) { } } - -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 checkin.Exception == "" { - realWorkdays += 1 - continue - } - - //入职离职当天已经算过了 - if entryTime.Format("2006-01-02") == checkin.Day || leaveTime.Format("2006-01-02") == checkin.Day { - 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 - -}