| | |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.production.bean.dto.UserAccountDto; |
| | | import com.ruoyi.production.bean.dto.UserProductionAccountingDto; |
| | | import com.ruoyi.production.mapper.ProductionProductMainMapper; |
| | | import com.ruoyi.production.service.SalesLedgerProductionAccountingService; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.DayOfWeek; |
| | | import java.time.YearMonth; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | import java.time.LocalDate; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | private final SysDeptMapper sysDeptMapper; |
| | | private final StaffOnJobMapper staffOnJobMapper; |
| | | private final SalesLedgerProductionAccountingService salesLedgerProductionAccountingService; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | |
| | | |
| | | @Override |
| | |
| | | if(staffOnJobDto == null){ |
| | | return; |
| | | } |
| | | BigDecimal basicSalary = hasValue(map, "basicSalary") |
| | | ? normalizeMoney(getBigDecimal(map, "basicSalary")) |
| | | : normalizeMoney(staffOnJobDto.getBasicSalary()); |
| | | BigDecimal basicSalary = resolveBasicSalary(map, staffOnJobDto); |
| | | BigDecimal attendanceBonus = resolveAttendanceBonus(staffOnJobDto); |
| | | BigDecimal otherIncome = normalizeMoney(getBigDecimal(map, "otherIncome")); |
| | | BigDecimal otherDeduct = normalizeMoney(getBigDecimal(map, "otherDeduct")); |
| | | BigDecimal socialSupplementAmount = hasValue(map, "socialSupplementAmount") |
| | | ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount")) |
| | | : calculateLatestDeptSocialSupplementAmount(staffOnJobDto); |
| | | BigDecimal pieceSalary = normalizeMoney(getBigDecimal(map, "pieceSalary")); |
| | | BigDecimal hourlySalary = normalizeMoney(getBigDecimal(map, "hourlySalary")); |
| | | BigDecimal socialPersonal = normalizeMoney(getBigDecimal(map, "socialPersonal")); |
| | | BigDecimal fundPersonal = normalizeMoney(getBigDecimal(map, "fundPersonal")); |
| | | BigDecimal socialSupplementAmount = resolveSocialSupplementAmount(map, staffOnJobDto); |
| | | BigDecimal socialPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal fundPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | int monthWorkDays = calculateWorkDays(date); |
| | | BigDecimal standardHours = calculateStandardHours(monthWorkDays); |
| | | BigDecimal actualOutput = resolveActualOutput(map); |
| | | BigDecimal standardQuantity = resolveStandardQuantity(map); |
| | | BigDecimal conversionFactor = resolveConversionFactor(map); |
| | | |
| | | Long sysUserId = getUidByStaffId(staffId); |
| | | UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto(); |
| | | userProductionAccountingDto.setUserId(getUidByStaffId(staffId)); |
| | | userProductionAccountingDto.setUserId(sysUserId); |
| | | userProductionAccountingDto.setDate(date); |
| | | UserAccountDto byUserId = salesLedgerProductionAccountingService.getByUserId(userProductionAccountingDto); |
| | | if(byUserId != null){ |
| | | pieceSalary = normalizeMoney(byUserId.getAccountBalance()); |
| | | hourlySalary = normalizeMoney(byUserId.getAccount()); |
| | | } |
| | | BigDecimal repairWorkHour = resolveRepairWorkHour(sysUserId, date); |
| | | BigDecimal convertedWorkHours = resolveConvertedWorkHours( |
| | | map, |
| | | actualOutput, |
| | | standardQuantity, |
| | | conversionFactor, |
| | | repairWorkHour, |
| | | byUserId |
| | | ); |
| | | BigDecimal conversionRatio = calculateConversionRatio(standardHours, convertedWorkHours); |
| | | |
| | | // 查询该人员对应的社保方案 |
| | | List<SchemeApplicableStaff> schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId); |
| | |
| | | } |
| | | for (SchemeInsuranceDetail detail : detailList) { |
| | | if("公积金".equals(detail.getInsuranceType())){ |
| | | fundPersonal = fundPersonal.add(calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, staffOnJobDto, detail)); |
| | | fundPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, basicSalary, staffOnJobDto, detail); |
| | | }else{ |
| | | socialPersonal = socialPersonal.add(calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, staffOnJobDto, detail)); |
| | | socialPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, basicSalary, staffOnJobDto, detail); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | BigDecimal grossSalary = basicSalary.add(pieceSalary).add(hourlySalary).add(otherIncome); |
| | | // 工资 = 全勤奖金 + 基本工资 * 折合倍数 |
| | | BigDecimal grossSalary = attendanceBonus.add(basicSalary.multiply(conversionRatio)).add(otherIncome); |
| | | BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax( |
| | | grossSalary, |
| | | socialPersonal.add(socialSupplementAmount), |
| | |
| | | map.put("nation", staffOnJobDto.getNation()); |
| | | } |
| | | map.put("basicSalary", basicSalary); |
| | | map.put("pieceSalary", pieceSalary); |
| | | map.put("hourlySalary", hourlySalary); |
| | | map.put("attendanceBonus", attendanceBonus); |
| | | map.put("monthWorkDays", monthWorkDays); |
| | | map.put("standardHours", standardHours); |
| | | map.put("actualOutput", actualOutput); |
| | | map.put("standardQuantity", standardQuantity); |
| | | map.put("conversionFactor", conversionFactor); |
| | | map.put("convertedWorkHours", convertedWorkHours); |
| | | map.put("monthTimeTotal", convertedWorkHours); |
| | | map.put("conversionRatio", conversionRatio); |
| | | map.put("pieceSalary", convertedWorkHours); |
| | | map.put("hourlySalary", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); |
| | | map.put("otherIncome", otherIncome); |
| | | map.put("socialPersonal", socialPersonal); |
| | | map.put("fundPersonal", fundPersonal); |
| | |
| | | map.put("salaryTax", salaryTax); |
| | | map.put("deductSalary", deductSalary); |
| | | map.put("netSalary", netSalary); |
| | | } |
| | | |
| | | /** |
| | | * 解析员工基础工资。 |
| | | * 优先使用工资表里本次传入的值,未传时回落到员工档案中的基础工资。 |
| | | */ |
| | | private BigDecimal resolveBasicSalary(Map<String, Object> map, StaffOnJob staffOnJobDto) { |
| | | // 员工基础工资优先取工资表传入值,没有则回落到员工档案中的基础工资。 |
| | | return hasValue(map, "basicSalary") |
| | | ? normalizeMoney(getBigDecimal(map, "basicSalary")) |
| | | : normalizeMoney(staffOnJobDto.getBasicSalary()); |
| | | } |
| | | |
| | | /** |
| | | * 获取员工最新全勤奖金。 |
| | | * 全勤奖金统一以员工档案配置为准。 |
| | | */ |
| | | private BigDecimal resolveAttendanceBonus(StaffOnJob staffOnJobDto) { |
| | | // 全勤奖金直接取员工档案里的最新配置。 |
| | | return staffOnJobDto.getAttendanceBonus() == null |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : normalizeMoney(staffOnJobDto.getAttendanceBonus()); |
| | | } |
| | | |
| | | /** |
| | | * 解析社保补缴金额。 |
| | | * 如果工资表传入了补缴金额则直接使用,否则按员工所属部门最新方案计算。 |
| | | */ |
| | | private BigDecimal resolveSocialSupplementAmount(Map<String, Object> map, StaffOnJob staffOnJobDto) { |
| | | return hasValue(map, "socialSupplementAmount") |
| | | ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount")) |
| | | : calculateLatestDeptSocialSupplementAmount(staffOnJobDto); |
| | | } |
| | | |
| | | /** |
| | | * 计算月标准工时。 |
| | | * 规则:工作日天数 * 8 小时。 |
| | | */ |
| | | private BigDecimal calculateStandardHours(int monthWorkDays) { |
| | | // 月标准工时 = 工作日天数 * 8 小时。 |
| | | return BigDecimal.valueOf(monthWorkDays) |
| | | .multiply(new BigDecimal("8")) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 解析当月实际产量。 |
| | | */ |
| | | private BigDecimal resolveActualOutput(Map<String, Object> map) { |
| | | return hasValue(map, "actualOutput") |
| | | ? normalizeMoney(getBigDecimal(map, "actualOutput")) |
| | | : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 解析工序标准数量。 |
| | | */ |
| | | private BigDecimal resolveStandardQuantity(Map<String, Object> map) { |
| | | return hasValue(map, "standardQuantity") |
| | | ? normalizeMoney(getBigDecimal(map, "standardQuantity")) |
| | | : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 解析工序折算系数,未配置时默认 1。 |
| | | */ |
| | | private BigDecimal resolveConversionFactor(Map<String, Object> map) { |
| | | return hasValue(map, "conversionFactor") |
| | | ? normalizeMoney(getBigDecimal(map, "conversionFactor")) |
| | | : BigDecimal.ONE.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 汇总轻微返工的补修理工时。 |
| | | * 按系统用户 ID 和工资月份统计,并作为月时间合计的一部分。 |
| | | */ |
| | | private BigDecimal resolveRepairWorkHour(Long sysUserId, String date) { |
| | | if (sysUserId == null || sysUserId <= 0 || StringUtils.isEmpty(date)) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | LocalDate monthStart = YearMonth.parse(date).atDay(1); |
| | | LocalDate monthEnd = YearMonth.parse(date).atEndOfMonth(); |
| | | return productionProductMainMapper.selectList(new LambdaQueryWrapper<com.ruoyi.production.pojo.ProductionProductMain>() |
| | | .eq(com.ruoyi.production.pojo.ProductionProductMain::getUserId, sysUserId) |
| | | .eq(com.ruoyi.production.pojo.ProductionProductMain::getReportType, 1) |
| | | .ge(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthStart.atStartOfDay()) |
| | | .le(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthEnd.atTime(23, 59, 59))) |
| | | .stream() |
| | | .map(item -> normalizeMoney(item.getRepairWorkHour())) |
| | | .reduce(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), BigDecimal::add) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 计算月时间合计。 |
| | | * 计件场景使用实际产量、标准数量和折算系数换算; |
| | | * 没有计件数据时,回落到生产核算汇总值; |
| | | * 最后再叠加返工补修理工时。 |
| | | */ |
| | | private BigDecimal resolveConvertedWorkHours(Map<String, Object> map, |
| | | BigDecimal actualOutput, |
| | | BigDecimal standardQuantity, |
| | | BigDecimal conversionFactor, |
| | | BigDecimal repairWorkHour, |
| | | UserAccountDto byUserId) { |
| | | if (hasValue(map, "monthTimeTotal")) { |
| | | BigDecimal monthTimeTotal = normalizeMoney(getBigDecimal(map, "monthTimeTotal")); |
| | | if (monthTimeTotal.compareTo(BigDecimal.ZERO) > 0) { |
| | | return monthTimeTotal; |
| | | } |
| | | } |
| | | BigDecimal convertedWorkHours = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | // 计件折算工时 = 当月实际产量 / 工序标准数量 * 8 * 工序折算系数 |
| | | if (actualOutput.compareTo(BigDecimal.ZERO) > 0 && standardQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | convertedWorkHours = actualOutput |
| | | .divide(standardQuantity, 8, RoundingMode.HALF_UP) |
| | | .multiply(new BigDecimal("8")) |
| | | .multiply(conversionFactor) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } else if (byUserId != null) { |
| | | convertedWorkHours = normalizeMoney(byUserId.getAccountBalance().add(byUserId.getAccount())); |
| | | } |
| | | return convertedWorkHours.add(repairWorkHour).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 计算折合倍数。 |
| | | * 规则:月时间合计 / 月标准工时。 |
| | | */ |
| | | private BigDecimal calculateConversionRatio(BigDecimal standardHours, BigDecimal convertedWorkHours) { |
| | | return standardHours.compareTo(BigDecimal.ZERO) == 0 |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : convertedWorkHours.divide(standardHours, 8, RoundingMode.HALF_UP) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param detail |
| | | * @return |
| | | */ |
| | | public BigDecimal calculateByEmployeeIdType(String type,BigDecimal bigDecimal, StaffOnJob staffOnJobDto,SchemeInsuranceDetail detail) { |
| | | public BigDecimal calculateByEmployeeIdType(String type,BigDecimal bigDecimal, BigDecimal basicSalary, StaffOnJob staffOnJobDto,SchemeInsuranceDetail detail) { |
| | | // 判断是否调用基本工资 |
| | | if (detail.getUseBasicSalary() == 1) { |
| | | BigDecimal divide = detail.getPaymentBase().multiply(detail.getPersonalRatio()).divide(new BigDecimal("100"), 2); |
| | | bigDecimal = bigDecimal.add(divide); |
| | | }else{ |
| | | // 调用基本工资 |
| | | BigDecimal multiply = staffOnJobDto.getBasicSalary().multiply(detail.getPersonalRatio().divide(new BigDecimal("100"), 2)); |
| | | // 调用当前工资表中的基本工资 |
| | | BigDecimal multiply = basicSalary.multiply(detail.getPersonalRatio().divide(new BigDecimal("100"), 2)); |
| | | bigDecimal = bigDecimal.add(multiply); |
| | | } |
| | | bigDecimal = bigDecimal.add(detail.getPersonalFixed()); |
| | |
| | | BigDecimal socialSupplementAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | for (SchemeInsuranceDetail detail : detailList) { |
| | | if (!"公积金".equals(detail.getInsuranceType())) { |
| | | socialSupplementAmount = socialSupplementAmount.add( |
| | | calculateByEmployeeIdType(detail.getInsuranceType(), socialSupplementAmount, staffOnJobDto, detail) |
| | | socialSupplementAmount = calculateByEmployeeIdType( |
| | | detail.getInsuranceType(), |
| | | socialSupplementAmount, |
| | | normalizeMoney(staffOnJobDto.getBasicSalary()), |
| | | staffOnJobDto, |
| | | detail |
| | | ); |
| | | } |
| | | } |
| | |
| | | private BigDecimal normalizeMoney(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : value.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private int calculateWorkDays(String date) { |
| | | if (StringUtils.isEmpty(date)) { |
| | | return 0; |
| | | } |
| | | YearMonth yearMonth = YearMonth.parse(date); |
| | | int workDays = 0; |
| | | for (int day = 1; day <= yearMonth.lengthOfMonth(); day++) { |
| | | DayOfWeek dayOfWeek = yearMonth.atDay(day).getDayOfWeek(); |
| | | if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { |
| | | workDays++; |
| | | } |
| | | } |
| | | return workDays; |
| | | } |
| | | } |