| | |
| | | "silkserver/constvar" |
| | | "silkserver/models" |
| | | "silkserver/pkg/logx" |
| | | "silkserver/utils" |
| | | "silkserver/utils/calculator" |
| | | "strconv" |
| | | "strings" |
| | | "time" |
| | | ) |
| | | |
| | | // WorkingHours 存表:工时计算(日期(年月日)、工种、员工姓名、小组、车台、产量工资、上班超时(小时)、上班超时(天)、加班(单独)、加班(全车间)、出勤(天)、带徒(天)、产量(KG)、其它) |
| | | func WorkingHours(date string) error { |
| | | dateTime, err := time.Parse("2006-01-02", date) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | // 人员信息 |
| | | workers, err := models.NewWorkerSearch().FindNotTotal() |
| | | if err != nil { |
| | |
| | | // 排班信息(功能缺失) |
| | | // 打卡信息(通过人员关联) |
| | | attendances, err := models.NewAttendanceManageSearch().SetDate(date).FindNotTotal() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(attendances) == 0 { |
| | | return errors.New(date + ":打卡信息为空") |
| | | } |
| | | |
| | | // 车台信息(通过人员关联) |
| | | workerPositions, err := models.NewWorkerPositionSearch().SetOverlappingDate(date, date).FindAll() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | |
| | | list := make([]*models.PayrollWorkingHours, 0) |
| | | for _, worker := range workers { |
| | |
| | | } |
| | | workingHours.ShiftClockInTime = attendance.StartWorkTime |
| | | workingHours.ShiftClockOutTime = attendance.EndWorkTime |
| | | workingHours.OvertimeType = constvar.ShiftOvertimeTypeTimeout |
| | | workingHours.OvertimeDuration = attendance.OverTimeDuration |
| | | if dateTime.Weekday() == 0 { |
| | | workingHours.OvertimeType = constvar.ShiftOvertimeTypeOvertime |
| | | workingHours.OvertimeDuration = decimal.NewFromInt32(1) |
| | | } else { |
| | | workingHours.OvertimeType = constvar.ShiftOvertimeTypeTimeout |
| | | workingHours.OvertimeDuration = attendance.OverTimeDuration |
| | | } |
| | | } |
| | | } |
| | | for _, position := range workerPositions { |
| | |
| | | } |
| | | |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | if len(list) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollWorkingHoursSearch().SetOrm(db).SetCycle(date).Delete(); err != nil { |
| | | return err |
| | | } |
| | |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(yieldRegisters) == 0 { |
| | | return errors.New(date + ":产量登记为空") |
| | | } |
| | | |
| | | finenesss, err := models.NewFinenessRegisterSearch().SetFinishDate(date).FindAll() // 纤度登记 |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(finenesss) == 0 { |
| | | return errors.New(date + ":纤度登记为空") |
| | | } |
| | | |
| | | finenessIds := make([]uint, len(finenesss)) |
| | | for i, fineness := range finenesss { |
| | | finenessIds[i] = fineness.ID |
| | |
| | | return err |
| | | } |
| | | |
| | | priceStandards, _ := models.NewRawSilkPriceStandardSearch().FindNotTotal() // 生丝定价 |
| | | priceStandardMap := make(map[string]decimal.Decimal) // map[庄口\标号]定价 |
| | | priceStandards, err := models.NewRawSilkPriceStandardSearch().FindNotTotal() // 生丝定价 |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(priceStandards) == 0 { |
| | | return errors.New(date + ":生丝定价为空") |
| | | } |
| | | |
| | | priceStandardMap := make(map[string]decimal.Decimal) // map[庄口\标号]定价 |
| | | for _, price := range priceStandards { |
| | | key := fmt.Sprintf("%d%s", price.MarketNumber, price.RawSilkGrade) |
| | | priceStandardMap[key] = price.PayStandard |
| | |
| | | workingHours, err := models.NewPayrollWorkingHoursSearch().SetWorkTypeCode(constvar.JobTypeWeavers).SetCycle(date).FindNotTotal() // 员工的工时统计 |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(workingHours) == 0 { |
| | | return errors.New(date + ":挡车工打卡信息为空") |
| | | } |
| | | |
| | | // 车台挡车工重复人员标记 |
| | |
| | | } |
| | | for _, workingHour := range workingHourArr { |
| | | key := fmt.Sprintf("%v%v%v", workingHour.WorkshopNumber, workingHour.GroupNumber, workingHour.StartCarNumber) |
| | | carEmployeeMap[key][workingHour.WorkerID] = true |
| | | if temp, ok := carEmployeeMap[key]; ok { |
| | | temp[workingHour.WorkerID] = true |
| | | carEmployeeMap[key] = temp |
| | | } else { |
| | | carEmployeeMap[key] = map[string]bool{workingHour.WorkerID: true} |
| | | } |
| | | |
| | | } |
| | | |
| | | // 方案2 根据纤度登记来查人数 |
| | |
| | | |
| | | } |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | if len(productionCar) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollProductionCarSearch().SetOrm(db).SetCycle(date).Delete(); err != nil { |
| | | return err |
| | | } |
| | |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(productionCars) == 0 { |
| | | errors.New(date + ":车台每天的产量统计为空") |
| | | } |
| | | |
| | | workingHours, err := models.NewPayrollWorkingHoursSearch().SetWorkTypeCode(constvar.JobTypeWeavers).SetCycle(date).FindNotTotal() // 员工的工时统计 |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(workingHours) == 0 { |
| | | return errors.New(date + ":挡车工打卡信息为空") |
| | | } |
| | | |
| | | groupWorkingHourMap := make(map[string]map[string]bool) |
| | | for _, workingHour := range workingHours { |
| | | key := fmt.Sprintf("%v%v", workingHour.WorkshopNumber, workingHour.GroupNumber) |
| | | groupWorkingHourMap[key][workingHour.WorkerID] = true |
| | | if temp, ok := groupWorkingHourMap[key]; ok { |
| | | temp[workingHour.WorkerID] = true |
| | | groupWorkingHourMap[key] = temp |
| | | } else { |
| | | groupWorkingHourMap[key] = map[string]bool{workingHour.WorkerID: true} |
| | | } |
| | | } |
| | | |
| | | productionGroupList := make([]*models.PayrollProductionGroup, 0) |
| | |
| | | } |
| | | |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | err := models.NewPayrollProductionGroupSearch().SetOrm(db).SetCycle(date).Delete() |
| | | if err != nil { |
| | | if len(productionGroupList) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollProductionGroupSearch().SetOrm(db).SetCycle(date).Delete(); err != nil { |
| | | return err |
| | | } |
| | | err = models.NewPayrollProductionGroupSearch().SetOrm(db).CreateBatch(productionGroupList) |
| | | if err != nil { |
| | | if err = models.NewPayrollProductionGroupSearch().SetOrm(db).CreateBatch(productionGroupList); err != nil { |
| | | return err |
| | | } |
| | | return nil |
| | |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(workingHours) == 0 { |
| | | return errors.New(date + ":挡车工打卡信息为空") |
| | | } |
| | | |
| | | productionCars, err := models.NewPayrollProductionCarSearch().SetCycle(date).FindNotTotal() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(productionCars) == 0 { |
| | | return errors.New(date + ":车台每天产量统计为空") |
| | | } |
| | | |
| | | productionEmployee := make([]*models.PayrollProductionWeavers, 0) |
| | |
| | | info.FinishTotalAmount = info.FinishTotalAmount.Add(car.FinishTotalAvgAmount) |
| | | } |
| | | } |
| | | if info.SilkQuantity.IsZero() { |
| | | continue |
| | | } |
| | | productionEmployee = append(productionEmployee, &info) |
| | | } |
| | | |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | if len(productionEmployee) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollProductionWeaversSearch().SetOrm(db).SetCycle(date).Delete(); err != nil { |
| | | return err |
| | | } |
| | |
| | | |
| | | GroupCarHeadAvgAmount decimal.Decimal // 同组车头工工资 |
| | | GroupWeaversAvgAmount decimal.Decimal // 同组挡车工月平均工资 |
| | | JobDays decimal.Decimal // 出勤天数 |
| | | WorkingDay decimal.Decimal // 一个月工作天数 |
| | | JobDays decimal.Decimal // 一个月出勤天数 |
| | | |
| | | ShiftTimeout decimal.Decimal // 工作日加班时长(小时) |
| | | ShiftOvertime decimal.Decimal // 休息日加班时长(天) |
| | | MentorDays decimal.Decimal // 带徒天数 |
| | | FullAttendanceAward decimal.Decimal // 满勤奖 |
| | | LeaveDays decimal.Decimal // 请假天数 |
| | | Seniority decimal.Decimal // 工龄 |
| | | } |
| | | |
| | | // SalaryPlan 生产工资计算 |
| | |
| | | } else { |
| | | date = date[:7] |
| | | } |
| | | dateTime, err := time.Parse("2006-01", date) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | |
| | | hours, err := models.NewPayrollWorkingHoursSearch().SetOrder("worker_id").SetMonthly(date).FindNotTotal() // 员工的工时统计 |
| | | if err != nil { |
| | | return err |
| | | } |
| | | jobQuantityMap := make(map[string]int) // 员工出勤统计 |
| | | for _, hour := range hours { |
| | | jobQuantityMap[hour.WorkerID] += 1 |
| | | if len(hours) == 0 { |
| | | return errors.New(date + ":员工打卡信息为空") |
| | | } |
| | | |
| | | workers, err := models.NewWorkerSearch().FindNotTotal() |
| | | jobQuantityMap := make(map[string]int) |
| | | shiftTimeoutMap := make(map[string]decimal.Decimal) |
| | | shiftOvertimeMap := make(map[string]decimal.Decimal) |
| | | for _, hour := range hours { |
| | | jobQuantityMap[hour.WorkerID] += 1 |
| | | if hour.OvertimeType == constvar.ShiftOvertimeTypeOvertime { |
| | | shiftOvertimeMap[hour.WorkerID] = hour.OvertimeDuration.Add(shiftOvertimeMap[hour.WorkerID]) |
| | | } else if hour.OvertimeType == constvar.ShiftOvertimeTypeTimeout { |
| | | shiftTimeoutMap[hour.WorkerID] = hour.OvertimeDuration.Add(shiftTimeoutMap[hour.WorkerID]) |
| | | } |
| | | } |
| | | |
| | | workers, _ := models.NewWorkerSearch().FindNotTotal() |
| | | workerMap := make(map[string]*models.Worker) |
| | | for _, worker := range workers { |
| | | workerMap[worker.ID] = worker |
| | | } |
| | | |
| | | mentors, _ := models.NewMentorSearch().SetMonth(date).FindNotTotal() |
| | | mentorMap := make(map[string]int) |
| | | for _, mentor := range mentors { |
| | | mentorMap[mentor.WorkerId] = mentor.Days |
| | | } |
| | | |
| | | // 挡车工工资 |
| | | weaversAmountArr, _ := models.NewPayrollProductionWeaversSearch().SetMonthly(date).FindNotTotal() |
| | | weaversAmountArr, err := models.NewPayrollProductionWeaversSearch().SetMonthly(date).FindNotTotal() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | |
| | | weaversAmountMap := make(map[string]*models.PayrollProductionWeavers, len(weaversAmountArr)) |
| | | for _, weaver := range weaversAmountArr { |
| | | key := fmt.Sprintf("%v%v", weaver.Cycle, weaver.WorkerID) |
| | |
| | | |
| | | // 工种工资方案 map[工种]方案 |
| | | salaryPlans, _ := models.NewWorkTypeManageSearch().FindNotTotal() |
| | | if len(salaryPlans) == 0 { |
| | | return errors.New(date + ":工资方案为空") |
| | | } |
| | | |
| | | salaryPlansMap := make(map[uint]*models.WorkTypeManage, len(salaryPlans)) |
| | | for _, salaryPlan := range salaryPlans { |
| | | salaryPlansMap[salaryPlan.ID] = salaryPlan |
| | | } |
| | | |
| | | groups, err := models.NewPayrollProductionGroupSearch().SetMonthly(date).FindNotTotal() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if len(groups) == 0 { |
| | | return errors.New(date + ":小组每天的产量为空") |
| | | } |
| | | |
| | | groupMap := make(map[string]*models.PayrollProductionGroup, len(groups)) |
| | | groupByMonthMap := make(map[string]*models.PayrollProductionGroup, len(groups)) |
| | | for _, group := range groups { |
| | |
| | | |
| | | // 按天算:日产丝量、生丝单价、桶数、野纤数量、野纤单价 |
| | | groupKey := fmt.Sprintf("%v%v%v", hour.Cycle, hour.WorkshopNumber, hour.GroupNumber) |
| | | group := groupMap[groupKey] |
| | | weaversKey := fmt.Sprintf("%v%v", hour.Cycle, hour.WorkerID) |
| | | weavers := weaversAmountMap[weaversKey] |
| | | parameter := SalaryParameter{ |
| | | SilkQuantity: group.SilkQuantity, |
| | | SilkUnitAmount: decimal.NewFromInt32(1), |
| | | SilkTotalAmount: weavers.FinishTotalAmount, |
| | | FallingSilkBucket: group.FallingSilkBucket, |
| | | BadSilkQuantity: group.BadSilkQuantity, |
| | | BadSilkUnitAmount: decimal.NewFromInt32(1), |
| | | BadSilkTotalAmount: weavers.BadSilkTotalAmount, |
| | | } |
| | | if workType, ok := salaryPlansMap[hour.WorkTypeID]; ok { |
| | | for _, salaryPlan := range workType.SalaryPlans { |
| | | if matched, _ := regexp.MatchString("(日产丝量)|(生丝单价)|(桶数)|(野纤数量)|(野纤单价)", salaryPlan.SalaryFormula); matched { |
| | | temp := production |
| | | formula, s := salaryCalculate(¶meter, salaryPlan) |
| | | if group, ok := groupMap[groupKey]; ok { |
| | | weaversKey := fmt.Sprintf("%v%v", hour.Cycle, hour.WorkerID) |
| | | finishTotalAmount := decimal.NewFromInt32(0) |
| | | badSilkTotalAmount := decimal.NewFromInt32(0) |
| | | if weavers, ok := weaversAmountMap[weaversKey]; ok { |
| | | finishTotalAmount = weavers.FinishTotalAmount |
| | | badSilkTotalAmount = weavers.BadSilkTotalAmount |
| | | } |
| | | |
| | | temp.SalaryFormula = formula |
| | | temp.SalaryPlanId = salaryPlan.ID |
| | | temp.Amount = temp.Amount.Add(s) |
| | | productionByDay = append(productionByDay, &temp) // 每个人的所有方案 |
| | | parameter := SalaryParameter{ |
| | | SilkQuantity: group.SilkQuantity, |
| | | SilkUnitAmount: decimal.NewFromInt32(1), |
| | | SilkTotalAmount: finishTotalAmount, |
| | | FallingSilkBucket: group.FallingSilkBucket, |
| | | BadSilkQuantity: group.BadSilkQuantity, |
| | | BadSilkUnitAmount: decimal.NewFromInt32(1), |
| | | BadSilkTotalAmount: badSilkTotalAmount, |
| | | } |
| | | if workType, ok := salaryPlansMap[hour.WorkTypeID]; ok { |
| | | for _, salaryPlan := range workType.SalaryPlans { |
| | | if matched, _ := regexp.MatchString("(日产丝量)|(生丝单价)|(桶数)|(野纤数量)|(野纤单价)", salaryPlan.SalaryFormula); matched { |
| | | temp := production |
| | | formula, s := salaryCalculate(¶meter, salaryPlan) |
| | | |
| | | temp.SalaryFormula = formula |
| | | temp.SalaryPlanId = salaryPlan.ID |
| | | temp.Amount = temp.Amount.Add(s) |
| | | productionByDay = append(productionByDay, &temp) // 每个人的所有方案 |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | if len(productionByDay) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollOtherSubsidiesSearch().SetOrm(db).SetMonthly(date).Delete(); err != nil { |
| | | return err |
| | | } |
| | |
| | | CreatedBy: "auto", |
| | | } |
| | | |
| | | monthKey := fmt.Sprintf("%v%v", worker.ShopNumber, worker.GroupNumber) |
| | | group := groupByMonthMap[monthKey] |
| | | firstDay, lastDay := utils.GetLastMonthPeriod(dateTime) |
| | | _, days := utils.CalcWorkHour(firstDay, lastDay, []time.Weekday{time.Sunday}, 12.0) |
| | | parameter := SalaryParameter{ |
| | | FallingSilkBucket: group.FallingSilkBucket, |
| | | GroupWeaversAvgAmount: group.FinishTotalAvgAmount, |
| | | GroupCarHeadAvgAmount: group.FinishTotalAvgAmount.Div(ready70), |
| | | JobDays: decimal.NewFromInt32(int32(dayCount)), |
| | | WorkingDay: decimal.NewFromInt32(int32(days)), |
| | | JobDays: decimal.NewFromInt32(int32(dayCount)), |
| | | FullAttendanceAward: decimal.NewFromInt32(0), // 满勤奖 |
| | | LeaveDays: decimal.NewFromInt32(0), // 请假天数 |
| | | Seniority: decimal.NewFromInt32(0), // 工龄 |
| | | } |
| | | monthKey := fmt.Sprintf("%v%v", worker.ShopNumber, worker.GroupNumber) |
| | | if group, ok := groupByMonthMap[monthKey]; ok { |
| | | parameter.FallingSilkBucket = group.FallingSilkBucket |
| | | parameter.GroupWeaversAvgAmount = group.FinishTotalAvgAmount |
| | | parameter.GroupCarHeadAvgAmount = group.FinishTotalAvgAmount.Div(ready70) |
| | | } |
| | | if timeout, ok := shiftTimeoutMap[hourId]; ok { |
| | | parameter.ShiftTimeout = timeout |
| | | } |
| | | if overtime, ok := shiftOvertimeMap[hourId]; ok { |
| | | parameter.ShiftOvertime = overtime |
| | | } |
| | | if mentorDays, ok := mentorMap[hourId]; ok { |
| | | parameter.MentorDays = decimal.NewFromInt32(int32(mentorDays)) |
| | | } |
| | | |
| | | // 按月算:同组挡车工月平均工资、同组车头工工资、出勤天数 |
| | | if workType, ok := salaryPlansMap[worker.WorkTypeId]; ok { |
| | | for _, salaryPlan := range workType.SalaryPlans { |
| | | if matched, _ := regexp.MatchString("(同组挡车工月平均工资)|(同组车头工工资)|(出勤天数)", salaryPlan.SalaryFormula); matched { |
| | | if matched, _ := regexp.MatchString("(同组挡车工月平均工资)|(同组车头工工资)|(出勤天数)|(工作日加班时长)|(休息日加班时长)|(带徒天数)|(满勤奖)|(请假天数)|(工龄)", salaryPlan.SalaryFormula); matched { |
| | | temp := constitute |
| | | formula, s := salaryCalculate(¶meter, salaryPlan) |
| | | temp.SalaryFormula = formula |
| | |
| | | } |
| | | |
| | | err = models.WithTransaction(func(db *gorm.DB) error { |
| | | if len(constituteByMonth) == 0 { |
| | | return nil |
| | | } |
| | | if err := models.NewPayrollConstituteSearch().SetOrm(db).SetCycle(date).SetCreatedBy("auto").Delete(); err != nil { |
| | | return err |
| | | } |
| | |
| | | |
| | | // 根据方案计算各工种薪资 |
| | | func salaryCalculate(parameter *SalaryParameter, salaryPlan *models.SalaryPlan) (string, decimal.Decimal) { |
| | | formula := strings.ReplaceAll(salaryPlan.SalaryFormula, " ", "") |
| | | formula := strings.ReplaceAll(salaryPlan.SalaryFormula, ",", "") |
| | | formula = strings.ReplaceAll(salaryPlan.SalaryFormula, " ", "") |
| | | //var SplitFixedField = []string{"日产丝量", "生丝单价", "桶数", "野纤数量", "野纤单价", "同组挡车工月平均工资", "同组车头工工资", "出勤天数"} |
| | | formula = strings.Replace(formula, "日产丝量*生丝单价", parameter.SilkTotalAmount.String(), -1) |
| | | formula = strings.Replace(formula, "野纤数量*野纤单价", parameter.BadSilkTotalAmount.String(), -1) |
| | |
| | | formula = strings.Replace(formula, "同组车头工工资", parameter.GroupCarHeadAvgAmount.String(), -1) |
| | | formula = strings.Replace(formula, "出勤天数", parameter.JobDays.String(), -1) |
| | | |
| | | formula = strings.Replace(formula, "工作日加班时长", parameter.ShiftTimeout.String(), -1) |
| | | formula = strings.Replace(formula, "休息日加班时长", parameter.ShiftOvertime.String(), -1) |
| | | formula = strings.Replace(formula, "带徒天数", parameter.MentorDays.String(), -1) |
| | | formula = strings.Replace(formula, "满勤奖", parameter.FullAttendanceAward.String(), -1) |
| | | formula = strings.Replace(formula, "请假天数", parameter.LeaveDays.String(), -1) |
| | | formula = strings.Replace(formula, "工龄", parameter.Seniority.String(), -1) |
| | | |
| | | logx.Debugf("salary formula: %v", formula) |
| | | |
| | | result, err := calculator.ParseAndExec(formula) |
| | | if err != nil { |
| | | logx.Errorf("%s : %v", formula, err) |