src/main/java/com/ruoyi/basic/dto/ProductTreeDto.java
@@ -2,6 +2,7 @@ import lombok.Data; import java.math.BigDecimal; import java.util.List; @Data @@ -9,6 +10,7 @@ private Long id; private Long parentId; private String productName; private BigDecimal standardQuantity; private String label; // 用于树形结构的显示名称 private List<ProductTreeDto> children; } src/main/java/com/ruoyi/basic/pojo/Product.java
@@ -26,6 +26,12 @@ */ private String productName; /** * 产品标准数量 */ @Schema(description = "产品标准数量") private java.math.BigDecimal standardQuantity; @Schema(description = "租户ID") @TableField(fill = FieldFill.INSERT) private Long tenantId; src/main/java/com/ruoyi/basic/pojo/ProductModel.java
@@ -31,6 +31,10 @@ @Excel(name = "产品名称") private String productName; @TableField(exist = false) @Excel(name = "产品标准数量") private BigDecimal productStandardQuantity; /** * 规格型号 */ src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java
@@ -81,6 +81,9 @@ @Schema(description = "工序") private String process; @Schema(description = "工序标准数量") private java.math.BigDecimal standardQuantity; @Schema(description = "工资定额") private BigDecimal workHours; src/main/java/com/ruoyi/staff/dto/StaffSalaryCalculateDto.java
@@ -20,11 +20,6 @@ private java.math.BigDecimal standardQuantity; /** * 工序折算系数 */ private java.math.BigDecimal conversionFactor; /** * 当月实际产量 */ private java.math.BigDecimal actualOutput; src/main/java/com/ruoyi/staff/pojo/StaffSalaryDetail.java
@@ -47,11 +47,33 @@ @Schema(description = "部门名称") private String deptName; @Schema(description = "入司日期") @TableField(exist = false) private String entryDate; @Schema(description = "基本工资") private BigDecimal basicSalary; @Schema(description = "全勤奖金") private BigDecimal attendanceBonus; @Schema(description = "出勤天数") private Integer workDays; @Schema(description = "事假") private BigDecimal personalLeave; @Schema(description = "病假") private BigDecimal sickLeave; @Schema(description = "丧假") private BigDecimal bereavementLeave; @Schema(description = "婚假") private BigDecimal marriageLeave; @Schema(description = "津贴") private BigDecimal allowance; @Schema(description = "计件工资") private BigDecimal pieceSalary; @@ -59,20 +81,11 @@ @Schema(description = "计时工资") private BigDecimal hourlySalary; @Schema(description = "月工作日天数") private Integer monthWorkDays; @Schema(description = "月标准工时") private BigDecimal standardHours; @Schema(description = "月时间合计") private BigDecimal monthTimeTotal; @Schema(description = "折合倍数") private BigDecimal conversionRatio; @Schema(description = "工序折算工时") private BigDecimal convertedWorkHours; @Schema(description = "其他收入") private BigDecimal otherIncome; src/main/java/com/ruoyi/staff/pojo/StaffSalaryMain.java
@@ -53,6 +53,9 @@ @Schema(description = "工资月份,格式:yyyy-MM") private String salaryMonth; @Schema(description = "工作日") private Integer monthWorkDays; @Schema(description = "备注") private String remark; src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
@@ -158,13 +158,13 @@ * 通过员工id计算社保方案 * @param id */ public void calculateByEmployeeId(Integer id,Map<String, Object> map,String date) { public void calculateByEmployeeId(Integer id, Map<String, Object> map, String date) { if (id == null || map == null) { return; } Long staffId = id.longValue(); StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId); if(staffOnJobDto == null){ if (staffOnJobDto == null) { return; } BigDecimal basicSalary = resolveBasicSalary(map, staffOnJobDto); @@ -174,11 +174,12 @@ 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); int monthWorkDays = resolveMonthWorkDays(map, date); BigDecimal workDays = resolveWorkDays(map, monthWorkDays); BigDecimal standardHours = resolveStandardHours(map, monthWorkDays); BigDecimal actualOutput = resolveActualOutput(map); BigDecimal standardQuantity = resolveStandardQuantity(map); BigDecimal conversionFactor = resolveConversionFactor(map); BigDecimal dailySalary = calculateDailySalary(basicSalary, workDays); Long sysUserId = getUidByStaffId(staffId); UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto(); @@ -190,7 +191,6 @@ map, actualOutput, standardQuantity, conversionFactor, repairWorkHour, byUserId ); @@ -201,21 +201,24 @@ if (!CollectionUtils.isEmpty(schemeList)) { for (SchemeApplicableStaff scheme : schemeList) { List<SchemeInsuranceDetail> detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(scheme.getId()); if(CollectionUtils.isEmpty(detailList)){ if (CollectionUtils.isEmpty(detailList)) { continue; } for (SchemeInsuranceDetail detail : detailList) { if("公积金".equals(detail.getInsuranceType())){ if ("公积金".equals(detail.getInsuranceType())) { fundPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, basicSalary, staffOnJobDto, detail); }else{ } else { socialPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, basicSalary, staffOnJobDto, detail); } } } } // 工资 = 全勤奖金 + 基本工资 * 折合倍数 BigDecimal grossSalary = attendanceBonus.add(basicSalary.multiply(conversionRatio)).add(otherIncome); // 工资 = 全勤奖金 + 基本工资 * 折合倍数 + 其他收入 + 津贴 BigDecimal grossSalary = attendanceBonus .add(basicSalary.multiply(conversionRatio)) .add(otherIncome) .add(normalizeMoney(getBigDecimal(map, "allowance"))); BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax( grossSalary, socialPersonal.add(socialSupplementAmount), @@ -236,14 +239,16 @@ } map.put("basicSalary", basicSalary); map.put("attendanceBonus", attendanceBonus); map.put("workDays", workDays); map.put("monthWorkDays", monthWorkDays); map.put("dailySalary", dailySalary); 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("benefit", conversionRatio); map.put("pieceSalary", convertedWorkHours); map.put("hourlySalary", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); map.put("otherIncome", otherIncome); @@ -301,6 +306,20 @@ } /** * 解析月标准工时。 * 优先使用工资表传入的工作日计算,其次使用当月默认工作日。 */ private BigDecimal resolveStandardHours(Map<String, Object> map, int monthWorkDays) { if (hasValue(map, "standardHours")) { BigDecimal standardHours = normalizeMoney(getBigDecimal(map, "standardHours")); if (standardHours.compareTo(BigDecimal.ZERO) > 0) { return standardHours; } } return calculateStandardHours(monthWorkDays); } /** * 解析当月实际产量。 */ private BigDecimal resolveActualOutput(Map<String, Object> map) { @@ -316,15 +335,6 @@ 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); } /** @@ -350,14 +360,13 @@ /** * 计算月时间合计。 * 计件场景使用实际产量、标准数量和折算系数换算; * 计件场景使用实际产量和标准数量换算; * 没有计件数据时,回落到生产核算汇总值; * 最后再叠加返工补修理工时。 */ private BigDecimal resolveConvertedWorkHours(Map<String, Object> map, BigDecimal actualOutput, BigDecimal standardQuantity, BigDecimal conversionFactor, BigDecimal repairWorkHour, UserAccountDto byUserId) { if (hasValue(map, "monthTimeTotal")) { @@ -367,12 +376,11 @@ } } BigDecimal convertedWorkHours = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); // 计件折算工时 = 当月实际产量 / 工序标准数量 * 8 * 工序折算系数 // 计件折算工时 = 当月实际产量 / 工序标准数量 * 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())); @@ -386,9 +394,49 @@ */ private BigDecimal calculateConversionRatio(BigDecimal standardHours, BigDecimal convertedWorkHours) { return standardHours.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) ? BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP) : convertedWorkHours.divide(standardHours, 8, RoundingMode.HALF_UP) .setScale(2, RoundingMode.HALF_UP); .setScale(4, RoundingMode.HALF_UP); } /** * 计算日计工资。 * 规则:基本工资 / 工作日天数。 */ private BigDecimal calculateDailySalary(BigDecimal basicSalary, BigDecimal workDays) { if (basicSalary == null || workDays == null || workDays.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO.setScale(3, RoundingMode.HALF_UP); } return basicSalary.divide(workDays, 3, RoundingMode.HALF_UP); } /** * 解析工资表工作日。 * 优先使用页面传入的工作日,未传时才按自然月工作日计算。 */ private int resolveMonthWorkDays(Map<String, Object> map, String date) { if (hasValue(map, "monthWorkDays")) { BigDecimal monthWorkDays = normalizeMoney(getBigDecimal(map, "monthWorkDays")); if (monthWorkDays.compareTo(BigDecimal.ZERO) > 0) { return monthWorkDays.intValue(); } } return calculateWorkDays(date); } /** * 解析工作日。 * 如果工资表里已经填写了工作日,则优先使用人工填写值; * 否则默认使用当月工作日天数。 */ private BigDecimal resolveWorkDays(Map<String, Object> map, int monthWorkDays) { if (hasValue(map, "workDays")) { BigDecimal workDays = normalizeMoney(getBigDecimal(map, "workDays")); if (workDays.compareTo(BigDecimal.ZERO) > 0) { return workDays; } } return BigDecimal.valueOf(monthWorkDays).setScale(2, RoundingMode.HALF_UP); } /** src/main/java/com/ruoyi/staff/service/impl/StaffSalaryMainServiceImpl.java
@@ -10,6 +10,9 @@ import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.mapper.SysUserDeptMapper; import com.ruoyi.production.mapper.ProductionAccountMapper; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.bean.dto.ProductionAccountDto; import com.ruoyi.production.bean.dto.ProductionProductMainDto; import com.ruoyi.production.pojo.ProductionAccount; import com.ruoyi.technology.mapper.TechnologyOperationMapper; import com.ruoyi.technology.pojo.TechnologyOperation; @@ -18,9 +21,11 @@ import com.ruoyi.staff.dto.CalculateSalaryDto; import com.ruoyi.staff.dto.StaffSalaryCalculateDto; import com.ruoyi.staff.mapper.StaffLeaveMapper; import com.ruoyi.staff.mapper.StaffContractMapper; import com.ruoyi.staff.mapper.StaffSalaryDetailMapper; import com.ruoyi.staff.mapper.StaffSalaryMainMapper; import com.ruoyi.staff.pojo.StaffLeave; import com.ruoyi.staff.pojo.StaffContract; import com.ruoyi.staff.pojo.StaffSalaryDetail; import com.ruoyi.staff.pojo.StaffSalaryMain; import com.ruoyi.staff.service.StaffSalaryDetailService; @@ -46,6 +51,7 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.IOException; import java.text.SimpleDateFormat; /** * <p> @@ -65,8 +71,10 @@ private final SchemeApplicableStaffServiceImpl schemeApplicableStaffService; private final SysUserDeptMapper sysUserDeptMapper; private final StaffLeaveMapper staffLeaveMapper; private final StaffContractMapper staffContractMapper; private final SysDeptMapper sysDeptMapper; private final ProductionAccountMapper productionAccountMapper; private final ProductionProductMainMapper productionProductMainMapper; private final TechnologyOperationMapper technologyOperationMapper; @Override @@ -87,6 +95,18 @@ Page<StaffSalaryMain> page1 = staffSalaryMainMapper.selectPage(page, staffSalaryMainLambdaQueryWrapper); page1.getRecords().forEach(main -> { List<StaffSalaryDetail> staffSalaryDetailList = staffSalaryDetailMapper.selectList(new LambdaQueryWrapper<StaffSalaryDetail>().eq(StaffSalaryDetail::getMainId, main.getId())); staffSalaryDetailList.forEach(detail -> { if (detail.getEntryDate() == null && detail.getStaffOnJobId() != null) { StaffContract staffContract = staffContractMapper.selectOne(new LambdaQueryWrapper<StaffContract>() .eq(StaffContract::getStaffOnJobId, detail.getStaffOnJobId()) .orderByAsc(StaffContract::getContractStartTime) .orderByAsc(StaffContract::getId) .last("limit 1")); if (staffContract != null && staffContract.getContractStartTime() != null) { detail.setEntryDate(formatEntryDate(staffContract.getContractStartTime())); } } }); main.setStaffSalaryDetailList(staffSalaryDetailList); }); return AjaxResult.success(page1); @@ -182,33 +202,44 @@ @Override public AjaxResult calculateSalary(CalculateSalaryDto calculateSalaryDto) { if(CollectionUtils.isEmpty(calculateSalaryDto.getIds())){ if (CollectionUtils.isEmpty(calculateSalaryDto.getIds())) { return AjaxResult.error("参数错误"); } java.math.BigDecimal deductAmount = calculateSalaryDto.getDeductAmount() == null ? java.math.BigDecimal.ZERO : calculateSalaryDto.getDeductAmount(); List<Map<String, Object>> longs = setSchemeApplicableStaffUserInfo(calculateSalaryDto.getIds()); // 通过部门ids获取用户信息 if(CollectionUtils.isEmpty(longs)){ if (CollectionUtils.isEmpty(longs)) { return AjaxResult.error("无员工"); } List<Map<String, Object>> mapList = new ArrayList<>(); for (Map<String, Object> id : longs) { Long staffOnJobId = id.get("id") == null ? null : ((Number) id.get("id")).longValue(); // 判断员工是否为当月离职 if((Integer) id.get("staffState") == 0){ if ((Integer) id.get("staffState") == 0) { StaffLeave id1 = staffLeaveMapper.selectOne(new LambdaQueryWrapper<StaffLeave>() .eq(StaffLeave::getStaffOnJobId, staffOnJobId) .like(StaffLeave::getLeaveDate, calculateSalaryDto.getDate())); if(id1 == null){ if (id1 == null) { continue; } } id.put("otherDeduct", deductAmount); if (!id.containsKey("allowance")) { id.put("allowance", java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP)); } if (id.get("entryDate") == null) { StaffContract staffContract = staffContractMapper.selectOne(new LambdaQueryWrapper<StaffContract>() .eq(StaffContract::getStaffOnJobId, staffOnJobId) .orderByDesc(StaffContract::getId) .last("limit 1")); if (staffContract != null && staffContract.getContractStartTime() != null) { id.put("entryDate", formatEntryDate(staffContract.getContractStartTime())); } } Long sysUserId = schemeApplicableStaffService.getUidByStaffId(staffOnJobId); BigDecimal monthTimeTotal = resolveMonthTimeTotal(sysUserId, calculateSalaryDto.getDate()); id.put("monthTimeTotal", monthTimeTotal); id.put("convertedWorkHours", monthTimeTotal); schemeApplicableStaffService.calculateByEmployeeId(staffOnJobId == null ? null : staffOnJobId.intValue(), id, calculateSalaryDto.getDate()); mapList.add(id); } @@ -235,15 +266,12 @@ map.put("attendanceBonus", staffSalaryCalculateDto.getAttendanceBonus()); map.put("pieceSalary", staffSalaryCalculateDto.getPieceSalary()); map.put("hourlySalary", staffSalaryCalculateDto.getHourlySalary()); map.put("monthWorkDays", staffSalaryCalculateDto.getMonthWorkDays()); map.put("standardHours", staffSalaryCalculateDto.getStandardHours()); map.put("monthTimeTotal", staffSalaryCalculateDto.getMonthTimeTotal()); map.put("conversionRatio", staffSalaryCalculateDto.getConversionRatio()); map.put("convertedWorkHours", staffSalaryCalculateDto.getConvertedWorkHours()); map.put("actualOutput", staffSalaryCalculateDto.getActualOutput()); map.put("standardQuantity", staffSalaryCalculateDto.getStandardQuantity()); map.put("conversionFactor", staffSalaryCalculateDto.getConversionFactor()); map.put("otherIncome", staffSalaryCalculateDto.getOtherIncome()); map.put("allowance", staffSalaryCalculateDto.getAllowance()); map.put("socialPersonal", staffSalaryCalculateDto.getSocialPersonal()); map.put("fundPersonal", staffSalaryCalculateDto.getFundPersonal()); map.put("otherDeduct", staffSalaryCalculateDto.getOtherDeduct()); @@ -285,11 +313,8 @@ exportData.setAttendanceBonus(detail.getAttendanceBonus()); exportData.setPieceSalary(detail.getPieceSalary()); exportData.setHourlySalary(detail.getHourlySalary()); exportData.setMonthWorkDays(detail.getMonthWorkDays()); exportData.setStandardHours(detail.getStandardHours()); exportData.setMonthTimeTotal(detail.getMonthTimeTotal()); exportData.setConversionRatio(detail.getConversionRatio()); exportData.setConvertedWorkHours(detail.getConvertedWorkHours()); exportData.setOtherIncome(detail.getOtherIncome()); exportData.setSocialPersonal(detail.getSocialPersonal()); exportData.setFundPersonal(detail.getFundPersonal()); @@ -310,7 +335,10 @@ /** * 汇总员工当月月时间合计。 * 计时工序直接取工时,计件工序按产量折算后再叠加补修理工时。 * 规则: * 1. 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时 * 2. 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时 * 最终结果就是工资表里的月时间合计。 */ private java.math.BigDecimal resolveMonthTimeTotal(Long sysUserId, String date) { if (sysUserId == null || sysUserId <= 0 || StringUtils.isEmpty(date)) { @@ -324,25 +352,108 @@ java.math.BigDecimal workHour = toDecimal(item.get("workHour")); java.math.BigDecimal repairWorkHour = toDecimal(item.get("repairWorkHour")); java.math.BigDecimal standardQuantity = toDecimal(item.get("standardQuantity")); java.math.BigDecimal conversionFactor = toDecimal(item.get("conversionFactor")); java.math.BigDecimal rowTotal; if (Integer.valueOf(0).equals(operationType)) { rowTotal = workHour.add(repairWorkHour); } else { if (standardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) { rowTotal = java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP); } else { rowTotal = finishedNum .divide(standardQuantity, 8, java.math.RoundingMode.HALF_UP) .multiply(new java.math.BigDecimal("8")) .multiply(conversionFactor) .add(repairWorkHour) .setScale(2, java.math.RoundingMode.HALF_UP); } } java.math.BigDecimal productStandardQuantity = toDecimal(item.get("productStandardQuantity")); java.math.BigDecimal violinFactor = toDecimal(item.get("violinFactor")); java.math.BigDecimal violaFactor = toDecimal(item.get("violaFactor")); java.math.BigDecimal bassFactor = toDecimal(item.get("bassFactor")); String productName = item.get("productName") == null ? null : String.valueOf(item.get("productName")); java.math.BigDecimal rowTotal = calculateOperationTime( operationType, productName, finishedNum, workHour, repairWorkHour, standardQuantity, productStandardQuantity, violinFactor, violaFactor, bassFactor ); total = total.add(rowTotal).setScale(2, java.math.RoundingMode.HALF_UP); } return total; } /** * 计算单条工序的折算工时。 * 这里是月时间合计的核心分支: * - 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时; * - 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时。 */ private java.math.BigDecimal calculateOperationTime(Integer operationType, String productName, java.math.BigDecimal finishedNum, java.math.BigDecimal workHour, java.math.BigDecimal repairWorkHour, java.math.BigDecimal standardQuantity, java.math.BigDecimal productStandardQuantity, java.math.BigDecimal violinFactor, java.math.BigDecimal violaFactor, java.math.BigDecimal bassFactor) { if (isInstrumentProduct(productName)) { java.math.BigDecimal foldedQuantity = calculateFoldedQuantity( finishedNum, resolveInstrumentFactor(productName, violinFactor, violaFactor, bassFactor) ); if (standardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) { return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP); } return foldedQuantity .divide(standardQuantity, 8, java.math.RoundingMode.HALF_UP) .multiply(new java.math.BigDecimal("8")) .add(repairWorkHour) .setScale(2, java.math.RoundingMode.HALF_UP); } if (productStandardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) { return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP); } return finishedNum .divide(productStandardQuantity, 8, java.math.RoundingMode.HALF_UP) .multiply(new java.math.BigDecimal("8")) .add(repairWorkHour) .setScale(2, java.math.RoundingMode.HALF_UP); } /** * 按产品名称和配置系数折算产量。 * 只对小提琴、大提琴、贝斯三类产品生效, * 返回的是可用于后续工序标准数量换算的统一折合产量。 */ private java.math.BigDecimal calculateFoldedQuantity(java.math.BigDecimal quantity, java.math.BigDecimal factor) { if (quantity == null || factor == null) { return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP); } return quantity.multiply(factor).setScale(2, java.math.RoundingMode.HALF_UP); } private boolean isInstrumentProduct(String productName) { return "小提琴".equals(productName) || "大提琴".equals(productName) || "贝斯".equals(productName); } private java.math.BigDecimal resolveInstrumentFactor(String productName, java.math.BigDecimal violinFactor, java.math.BigDecimal violaFactor, java.math.BigDecimal bassFactor) { if ("小提琴".equals(productName)) { return violinFactor; } if ("大提琴".equals(productName)) { return violaFactor; } if ("贝斯".equals(productName)) { return bassFactor; } return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP); } private String formatEntryDate(java.util.Date entryDate) { if (entryDate == null) { return null; } return new SimpleDateFormat("yyyy-MM-dd").format(entryDate); } private java.math.BigDecimal toDecimal(Object value) { @@ -364,9 +475,9 @@ @Override public void exportWorkHours(HttpServletResponse response, String salaryMonth) { List<ProductionAccount> accountList = queryProductionAccounts(salaryMonth); List<ProductionProductMainDto> accountList = queryProductionDetails(salaryMonth); List<String> operationNames = accountList.stream() .map(ProductionAccount::getTechnologyOperationName) .map(ProductionProductMainDto::getProcess) .filter(StringUtils::isNotEmpty) .distinct() .sorted() @@ -421,9 +532,10 @@ headRow.getCell(3).setCellStyle(headerStyle); Map<String, java.math.BigDecimal> standardMap = buildStandardQuantityMap(List.of(operationName)); Map<String, java.math.BigDecimal> opQtyMap = buildOperationQuantityMap(accountList, operationName); Map<String, java.math.BigDecimal> opQtyMap = buildOperationQuantityMap(accountList, operationName, operation); Set<String> employeeNames = accountList.stream() .map(ProductionAccount::getSchedulingUserName) .filter(item -> operationName.equals(item.getProcess())) .map(ProductionProductMainDto::getSchedulingUserName) .filter(StringUtils::isNotEmpty) .collect(Collectors.toCollection(LinkedHashSet::new)); for (String staffName : employeeNames) { @@ -434,9 +546,7 @@ createNumberCell(row, 1, qty, numStyle); java.math.BigDecimal standardQty = standardMap.getOrDefault(operationName, java.math.BigDecimal.ONE); createNumberCell(row, 2, standardQty.compareTo(java.math.BigDecimal.ZERO) == 0 ? java.math.BigDecimal.ZERO : qty.divide(standardQty, 2, java.math.RoundingMode.HALF_UP).multiply(new java.math.BigDecimal("8")), numStyle); java.math.BigDecimal wage = operation == null || operation.getSalaryQuota() == null ? java.math.BigDecimal.ZERO : (Integer.valueOf(1).equals(operation.getType()) ? operation.getSalaryQuota().multiply(qty) : operation.getSalaryQuota()); java.math.BigDecimal wage = calculateSheetWage(operation, operationName, qty, standardQty); createNumberCell(row, 3, wage, numStyle); } @@ -589,32 +699,67 @@ return sb.toString(); } private List<ProductionAccount> queryProductionAccounts(String salaryMonth) { private List<ProductionProductMainDto> queryProductionDetails(String salaryMonth) { if (StringUtils.isEmpty(salaryMonth)) { return new ArrayList<>(); } YearMonth ym = YearMonth.parse(salaryMonth); LocalDate start = ym.atDay(1); LocalDate end = ym.atEndOfMonth(); return productionAccountMapper.selectList(new LambdaQueryWrapper<ProductionAccount>() .ge(ProductionAccount::getSchedulingDate, start.atStartOfDay()) .le(ProductionAccount::getSchedulingDate, end.atTime(23, 59, 59))); ProductionAccountDto dto = new ProductionAccountDto(); dto.setEntryDateStart(start); dto.setEntryDateEnd(end); dto.setAuditStatus(1); dto.setReportType(0); Page<ProductionAccountDto> page = new Page<>(1, 100000); return productionProductMainMapper.listProductionDetails(dto, page).getRecords(); } private Map<String, java.math.BigDecimal> buildOperationQuantityMap(List<ProductionAccount> accountList, String operationName) { private Map<String, java.math.BigDecimal> buildOperationQuantityMap(List<ProductionProductMainDto> accountList, String operationName, TechnologyOperation operation) { Map<String, java.math.BigDecimal> map = new LinkedHashMap<>(); if (CollectionUtils.isEmpty(accountList) || StringUtils.isEmpty(operationName)) { return map; } accountList.stream() .filter(item -> item != null && operationName.equals(item.getTechnologyOperationName())) .filter(item -> item != null && operationName.equals(item.getProcess())) .filter(item -> StringUtils.isNotEmpty(item.getSchedulingUserName())) .forEach(item -> map.merge(item.getSchedulingUserName(), item.getFinishedNum() == null ? java.math.BigDecimal.ZERO : item.getFinishedNum(), calculateFoldedQuantity(item, operation), java.math.BigDecimal::add)); return map; } private java.math.BigDecimal calculateSheetWage(TechnologyOperation operation, String operationName, java.math.BigDecimal qty, java.math.BigDecimal standardQty) { if (operation == null || qty == null) { return java.math.BigDecimal.ZERO; } if (standardQty == null || standardQty.compareTo(java.math.BigDecimal.ZERO) <= 0) { return java.math.BigDecimal.ZERO; } return qty.divide(standardQty, 2, java.math.RoundingMode.HALF_UP).multiply(new java.math.BigDecimal("8")); } private java.math.BigDecimal calculateFoldedQuantity(ProductionProductMainDto item, TechnologyOperation operation) { if (item == null || operation == null) { return java.math.BigDecimal.ZERO; } java.math.BigDecimal quantity = item.getQuantity() == null ? java.math.BigDecimal.ZERO : item.getQuantity(); String productName = item.getProductName(); if ("小提琴".equals(productName)) { return operation.getViolinFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolinFactor()); } if ("大提琴".equals(productName)) { return operation.getViolaFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolaFactor()); } if ("贝斯".equals(productName)) { return operation.getBassFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getBassFactor()); } return java.math.BigDecimal.ZERO; } private Map<String, java.math.BigDecimal> buildStandardQuantityMap(List<String> operationNames) { Map<String, java.math.BigDecimal> map = new LinkedHashMap<>(); if (CollectionUtils.isEmpty(operationNames)) { src/main/java/com/ruoyi/technology/pojo/TechnologyOperation.java
@@ -49,8 +49,14 @@ @Schema(description = "工序标准数量") private BigDecimal standardQuantity; @Schema(description = "工序折算系数") private BigDecimal conversionFactor; @Schema(description = "月产小提琴系数") private BigDecimal violinFactor; @Schema(description = "月产大提琴系数") private BigDecimal violaFactor; @Schema(description = "月产贝斯系数") private BigDecimal bassFactor; @Schema(description = "是否质检") private Boolean isQuality; src/main/java/com/ruoyi/technology/service/impl/TechnologyOperationServiceImpl.java
@@ -47,6 +47,9 @@ */ @Override public R add(TechnologyOperationDto technologyOperationDto) { if (technologyOperationDto == null || technologyOperationDto.getType() == null || technologyOperationDto.getType() != 1) { return R.fail("工序仅支持计件"); } TechnologyOperation technologyOperation = new TechnologyOperation(); BeanUtils.copyProperties(technologyOperationDto, technologyOperation); boolean saved = technologyOperationMapper.insert(technologyOperation) > 0; @@ -62,6 +65,9 @@ if (technologyOperation.getId() == null) { return R.fail("工序ID不能为空"); } if (technologyOperation.getType() == null || technologyOperation.getType() != 1) { return R.fail("工序仅支持计件"); } boolean updated = technologyOperationMapper.update( null, Wrappers.<TechnologyOperation>lambdaUpdate() @@ -71,7 +77,9 @@ .set(TechnologyOperation::getRemark, technologyOperation.getRemark()) .set(TechnologyOperation::getSalaryQuota, technologyOperation.getSalaryQuota()) .set(TechnologyOperation::getStandardQuantity, technologyOperation.getStandardQuantity()) .set(TechnologyOperation::getConversionFactor, technologyOperation.getConversionFactor()) .set(TechnologyOperation::getViolinFactor, technologyOperation.getViolinFactor()) .set(TechnologyOperation::getViolaFactor, technologyOperation.getViolaFactor()) .set(TechnologyOperation::getBassFactor, technologyOperation.getBassFactor()) .set(TechnologyOperation::getIsQuality, technologyOperation.getIsQuality()) .set(TechnologyOperation::getIsProduction, technologyOperation.getIsProduction()) .set(TechnologyOperation::getType, technologyOperation.getType()) src/main/resources/mapper/basic/ProductModelMapper.xml
@@ -23,7 +23,7 @@ </resultMap> <select id="listPageProductModel" resultMap="ProductModelVoResultMap"> select pm.*,p.product_name select pm.*,p.product_name, ifnull(p.standard_quantity, 0) as product_standard_quantity from product_model pm left join product p on pm.product_id = p.id <where> src/main/resources/mapper/production/ProductionAccountMapper.xml
@@ -43,6 +43,7 @@ group_concat(distinct p_parent.product_name order by p_parent.product_name separator ',') as productCategory, group_concat(distinct pm.model order by pm.model separator ',') as specificationModel, group_concat(distinct pm.unit order by pm.unit separator ',') as unit, ifnull(p.standard_quantity, 0) as productStandardQuantity, pa.scheduling_user_id as schedulingUserId, pa.scheduling_user_name as schedulingUserName, group_concat(distinct pot.work_order_no order by pot.work_order_no separator ',') as workOrderNo, @@ -50,11 +51,9 @@ case when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0) when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0)) else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * case when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) else 1 else case when ifnull(p.standard_quantity, 0) = 0 then 0 else ifnull(pa.finished_num, 0) / ifnull(p.standard_quantity, 0) * 8 end end ) as decimal(18,4)) as wages, @@ -89,6 +88,7 @@ left join production_operation_task pot on ppm.production_operation_task_id = pot.id left join production_order po on pot.production_order_id = po.id left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id left join technology_operation too on poro.technology_operation_id = too.id left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id) left join product p on pm.product_id = p.id left join product p_parent on p_parent.id = p.parent_id @@ -137,17 +137,15 @@ <select id="selectUserAccount" resultType="com.ruoyi.production.bean.dto.UserAccountDto"> select cast(ifnull(sum( case when pa.adjust_amount is not null then 0 when poro.type = 0 then 0 else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * case when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) else 1 end end ), 0) as decimal(18,2)) as accountBalance, case when pa.adjust_amount is not null then 0 when poro.type = 0 then 0 else case when ifnull(p.standard_quantity, 0) = 0 then 0 else ifnull(pa.finished_num, 0) / ifnull(p.standard_quantity, 0) * 8 end end ), 0) as decimal(18,2)) as accountBalance, cast(ifnull(sum( case when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0) @@ -161,6 +159,7 @@ left join production_order po on pot.production_order_id = po.id left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id) left join product p on pm.product_id = p.id where pa.scheduling_user_id = #{userId} and date_format(pa.scheduling_date, '%Y-%m') = #{date} and ( @@ -179,12 +178,20 @@ ifnull(ppm.work_hour, 0) as workHour, ifnull(ppm.repair_work_hour, 0) as repairWorkHour, ifnull(too.standard_quantity, 0) as standardQuantity, ifnull(too.conversion_factor, 1) as conversionFactor ifnull(p.standard_quantity, 0) as productStandardQuantity, ifnull(too.violin_factor, 0) as violinFactor, ifnull(too.viola_factor, 0) as violaFactor, ifnull(too.bass_factor, 0) as bassFactor, p.product_name as productName from production_account pa left join production_product_main ppm on ppm.id = pa.production_product_main_id left join production_operation_task pot on ppm.production_operation_task_id = pot.id left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id left join technology_operation too on poro.technology_operation_id = too.id left join production_order po on pot.production_order_id = po.id left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id) left join product p on pm.product_id = p.id left join product p_parent on p_parent.id = p.parent_id where pa.scheduling_user_id = #{userId} and date_format(pa.scheduling_date, '%Y-%m') = #{date} and ( src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -45,6 +45,7 @@ ifnull(ppo.quantity, 0) as quantity, ifnull(ppo.scrap_qty, 0) as scrapQty, p.product_name as productName, ifnull(p.standard_quantity, 0) as productStandardQuantity, pm.model as productModelName, pm.unit, po_sales.salesContractNo, @@ -131,9 +132,11 @@ end as auditStatusName, pot.work_order_no as workOrderNo, p.product_name as productName, ifnull(p.standard_quantity, 0) as productStandardQuantity, pm.model as productModelName, pm.unit, pa.technology_operation_name as process, ifnull(too.standard_quantity, 0) as standardQuantity, ifnull(ppo.quantity, 0) as quantity, ifnull(ppo.scrap_qty, 0) as scrapQty, date(pa.scheduling_date) as schedulingDate, @@ -147,11 +150,9 @@ cast( case when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0)) else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * case when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) else 1 else case when ifnull(too.standard_quantity, 0) = 0 then 0 else ifnull(pa.finished_num, 0) / ifnull(too.standard_quantity, 0) * 8 end end as decimal(18,4) @@ -160,11 +161,9 @@ case when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0) when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0)) else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * case when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) else 1 else case when ifnull(too.standard_quantity, 0) = 0 then 0 else ifnull(pa.finished_num, 0) / ifnull(too.standard_quantity, 0) * 8 end end as decimal(18,4) @@ -174,6 +173,7 @@ left join production_operation_task pot on ppm.production_operation_task_id = pot.id left join production_order po on pot.production_order_id = po.id left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id left join technology_operation too on poro.technology_operation_id = too.id left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id) left join product p on pm.product_id = p.id left join product p_parent on p_parent.id = p.parent_id src/main/resources/mapper/staff/StaffSalaryDetailMapper.xml
@@ -11,12 +11,23 @@ <result column="post_name" property="postName" /> <result column="dept_name" property="deptName" /> <result column="basic_salary" property="basicSalary" /> <result column="attendance_bonus" property="attendanceBonus" /> <result column="daily_salary" property="dailySalary" /> <result column="work_days" property="workDays" /> <result column="personal_leave" property="personalLeave" /> <result column="sick_leave" property="sickLeave" /> <result column="bereavement_leave" property="bereavementLeave" /> <result column="marriage_leave" property="marriageLeave" /> <result column="allowance" property="allowance" /> <result column="piece_salary" property="pieceSalary" /> <result column="hourly_salary" property="hourlySalary" /> <result column="month_time_total" property="monthTimeTotal" /> <result column="conversion_ratio" property="conversionRatio" /> <result column="other_income" property="otherIncome" /> <result column="social_personal" property="socialPersonal" /> <result column="fund_personal" property="fundPersonal" /> <result column="other_deduct" property="otherDeduct" /> <result column="social_supplement_amount" property="socialSupplementAmount" /> <result column="salary_tax" property="salaryTax" /> <result column="gross_salary" property="grossSalary" /> <result column="deduct_salary" property="deductSalary" /> src/main/resources/mapper/staff/StaffSalaryMainMapper.xml
@@ -8,6 +8,7 @@ <result column="salary_title" property="salaryTitle" /> <result column="dept_ids" property="deptIds" /> <result column="salary_month" property="salaryMonth" /> <result column="month_work_days" property="monthWorkDays" /> <result column="remark" property="remark" /> <result column="status" property="status" /> <result column="create_time" property="createTime" /> src/main/resources/mapper/technology/TechnologyOperationMapper.xml
@@ -12,6 +12,9 @@ <result column="remark" property="remark" /> <result column="salary_quota" property="salaryQuota" /> <result column="standard_quantity" property="standardQuantity" /> <result column="violin_factor" property="violinFactor" /> <result column="viola_factor" property="violaFactor" /> <result column="bass_factor" property="bassFactor" /> <result column="conversion_factor" property="conversionFactor" /> <result column="is_quality" property="isQuality" /> <result column="type" property="type" />