yinbentan
2024-08-30 cc7c2094568ea8f9d1697da5ed0a2c759ca81abd
service/salary_plan.go
@@ -9,13 +9,19 @@
   "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 {
@@ -25,9 +31,18 @@
   // 排班信息(功能缺失)
   // 打卡信息(通过人员关联)
   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 {
@@ -50,8 +65,13 @@
            }
            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 {
@@ -70,6 +90,9 @@
   }
   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
      }
@@ -88,10 +111,18 @@
   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
@@ -101,8 +132,15 @@
      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
@@ -111,6 +149,9 @@
   workingHours, err := models.NewPayrollWorkingHoursSearch().SetWorkTypeCode(constvar.JobTypeWeavers).SetCycle(date).FindNotTotal() // 员工的工时统计
   if err != nil {
      return err
   }
   if len(workingHours) == 0 {
      return errors.New(date + ":挡车工打卡信息为空")
   }
   // 车台挡车工重复人员标记
@@ -133,7 +174,13 @@
   }
   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 根据纤度登记来查人数
@@ -241,6 +288,9 @@
   }
   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
      }
@@ -263,16 +313,27 @@
   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)
@@ -316,12 +377,13 @@
   }
   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
@@ -339,10 +401,16 @@
   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)
@@ -366,10 +434,16 @@
            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
      }
@@ -393,7 +467,15 @@
   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 生产工资计算
@@ -403,24 +485,49 @@
   } 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)
@@ -429,12 +536,23 @@
   // 工种工资方案 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 {
@@ -475,36 +593,46 @@
      // 按天算:日产丝量、生丝单价、桶数、野纤数量、野纤单价
      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(&parameter, 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(&parameter, 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
      }
@@ -556,19 +684,35 @@
         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(&parameter, salaryPlan)
               temp.SalaryFormula = formula
@@ -582,6 +726,9 @@
   }
   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
      }
@@ -596,7 +743,8 @@
// 根据方案计算各工种薪资
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)
@@ -609,6 +757,15 @@
   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)