src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
@@ -5,7 +5,7 @@ @Getter public enum StockOutQualifiedRecordTypeEnum implements BaseEnum<String> { CUSTOMIZATION_STOCK_OUT("1", "合格自定义出库"); CUSTOMIZATION_STOCK_OUT("0", "合格自定义出库"); // PRODUCTION_REPORT_STOCK_OUT("3", "生产报工-出库"); // SALE_STOCK_OUT("8", "销售-出库"), // SALE_SHIP_STOCK_OUT("13", "销售-发货出库"); src/main/java/com/ruoyi/consumables/execl/ConsumablesInRecordExportData.java
@@ -21,7 +21,7 @@ @Excel(name = "入库来源") private String recordType; @Excel(name = "入库数量") private String ConsumablesInNum; private String stockInNum; @Excel(name = "入库时间") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") src/main/java/com/ruoyi/consumables/execl/ConsumablesInventoryExportData.java
@@ -21,13 +21,16 @@ @Excel(name = "库存数量") private BigDecimal qualitity; @Excel(name = "预警数量") private BigDecimal warnNum; // // @Excel(name = "预警数量") // private BigDecimal warnNum; @Excel(name = "冻结数量") private BigDecimal lockedQuantity; @Excel(name = "净重(吨)") private BigDecimal netWeight; @Excel(name = "备注") private String remark; // src/main/java/com/ruoyi/consumables/service/impl/ConsumablesInventoryServiceImpl.java
@@ -162,7 +162,7 @@ consumablesInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode()); consumablesInventoryDto.setQualitity(dto.getQualitity()); consumablesInventoryDto.setRemark(dto.getRemark()); consumablesInventoryDto.setWarnNum(dto.getWarnNum()); consumablesInventoryDto.setNetWeight(dto.getNetWeight()); if (ObjectUtils.isNotEmpty(dto.getLockedQuantity()) && dto.getLockedQuantity().compareTo(dto.getQualitity()) > 0) { throw new RuntimeException("冻结数量不能超过本次导入的库存数量"); } src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
@@ -110,21 +110,23 @@ @Override public AjaxResult saveDeviceRepair(DeviceRepair deviceRepair) { DeviceLedger byId = deviceLedgerService.getById(deviceRepair.getDeviceLedgerId()); if (CollectionUtils.isNotEmpty(deviceRepair.getFileList())) { List<String> fileIds = deviceRepair.getFileList().stream() .map(TempFile::getTempId) .collect(Collectors.toList()); try { tempFileService.migrateTempFilesToFormal(deviceRepair.getId(), fileIds, FileNameType.DeviceRepair.getValue()); } catch (Exception e) { log.error("设备维修文件迁移错误", e); } } deviceRepair.setDeviceName(byId.getDeviceName()); deviceRepair.setDeviceModel(byId.getDeviceModel()); if (deviceRepair.getRemark() == null) { deviceRepair.setRemark(""); } boolean save = this.save(deviceRepair); if (save) { if (CollectionUtils.isNotEmpty(deviceRepair.getFileList())) { List<String> fileIds = deviceRepair.getFileList().stream() .map(TempFile::getTempId) .collect(Collectors.toList()); try { tempFileService.migrateTempFilesToFormal(deviceRepair.getId(), fileIds, FileNameType.DeviceRepair.getValue()); } catch (Exception e) { log.error("设备维修文件迁移错误", e); } } return AjaxResult.success(); } return AjaxResult.error(); src/main/java/com/ruoyi/home/dto/HomeBusinessDto.java
@@ -4,8 +4,6 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; /** * @author :yys * @date : 2025/7/25 9:25 @@ -29,7 +27,13 @@ @ApiModelProperty("当前库存数量") private String inventoryNum = "0.00"; @ApiModelProperty("当前耗材库存数量") private String consumablesQuantityTotal = "0.00"; @ApiModelProperty("今日库存数量") private String todayInventoryNum = "0.00"; @ApiModelProperty("今日耗材库存数量") private String consumablesTodayNum = "0.00"; } src/main/java/com/ruoyi/home/dto/QualityStatisticsDto.java
@@ -1,5 +1,6 @@ package com.ruoyi.home.dto; import com.ruoyi.quality.pojo.RawMaterial; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -26,4 +27,6 @@ private List<QualityStatisticsItem> item; private List<RawMaterial> rawItem; } src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -19,6 +19,7 @@ import com.ruoyi.common.enums.ApproveTypeEnum; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.consumables.mapper.ConsumablesInventoryMapper; import com.ruoyi.device.mapper.DeviceRepairMapper; import com.ruoyi.device.pojo.DeviceRepair; import com.ruoyi.dto.MapDto; @@ -38,8 +39,10 @@ import com.ruoyi.purchase.pojo.PurchaseLedger; import com.ruoyi.quality.mapper.QualityInspectMapper; import com.ruoyi.quality.mapper.QualityUnqualifiedMapper; import com.ruoyi.quality.mapper.RawMaterialMapper; import com.ruoyi.quality.pojo.QualityInspect; import com.ruoyi.quality.pojo.QualityUnqualified; import com.ruoyi.quality.pojo.RawMaterial; import com.ruoyi.sales.mapper.ReceiptPaymentMapper; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; @@ -85,6 +88,9 @@ private StockInventoryMapper stockInventoryMapper; @Autowired private ConsumablesInventoryMapper consumablesInventoryMapper; @Autowired private QualityInspectMapper qualityStatisticsMapper; @Autowired @@ -128,6 +134,9 @@ @Autowired private QualityInspectMapper qualityInspectMapper; @Autowired private RawMaterialMapper rawMaterialMapper; @Autowired private QualityUnqualifiedMapper qualityUnqualifiedMapper; @@ -202,11 +211,15 @@ } // 统计库存 BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal(); BigDecimal consumablesQuantityTotal = consumablesInventoryMapper.selectTotal(); homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString()); homeBusinessDto.setConsumablesQuantityTotal(consumablesQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString()); // 获取当天入库数量 BigDecimal bigDecimal = stockInventoryMapper.selectTotalByDate(LocalDate.now()); BigDecimal consumablesTodayNum = consumablesInventoryMapper.selectTotalByDate(LocalDate.now()); homeBusinessDto.setTodayInventoryNum(bigDecimal.setScale(2, RoundingMode.HALF_UP).toString()); homeBusinessDto.setConsumablesTodayNum(consumablesTodayNum.setScale(2, RoundingMode.HALF_UP).toString()); return homeBusinessDto; } @@ -2389,87 +2402,88 @@ endDate = today.with(DayOfWeek.SUNDAY); } List<QualityInspect> qualityInspectList = qualityInspectMapper .selectList(new LambdaQueryWrapper<QualityInspect>() .ge(QualityInspect::getCheckTime, startDate.toString()) .le(QualityInspect::getCheckTime, endDate.toString()) .eq(QualityInspect::getInspectState, 1)); List<RawMaterial> rawMaterialList = rawMaterialMapper .selectList(new LambdaQueryWrapper<RawMaterial>() .ge(RawMaterial::getCheckTime, startDate.toString()) .le(RawMaterial::getCheckTime, endDate.toString()) .eq(RawMaterial::getInspectState, 1)); QualityStatisticsDto dto = new QualityStatisticsDto(); dto.setSupplierNum(sumQuantity(qualityInspectList, 0)); // 原材料 dto.setProcessNum(sumQuantity(qualityInspectList, 1)); // 过程 dto.setFactoryNum(sumQuantity(qualityInspectList, 2)); // 出厂 Map<Integer, BigDecimal> countMap = rawMaterialList.stream() .collect(Collectors.groupingBy( RawMaterial::getCheckType, Collectors.collectingAndThen( Collectors.counting(), count -> BigDecimal.valueOf(count) ) )); Map<String, List<QualityInspect>> groupedByCheckResult = qualityInspectList.stream() .collect(Collectors.groupingBy(QualityInspect::getCheckResult)); dto.setSupplierNum(countMap.getOrDefault(0, BigDecimal.ZERO)); // 入厂 dto.setProcessNum(countMap.getOrDefault(1, BigDecimal.ZERO)); // 车间 dto.setFactoryNum(countMap.getOrDefault(2, BigDecimal.ZERO)); // 出厂 List<QualityInspect> qualityInspects = groupedByCheckResult.getOrDefault("不合格", new ArrayList<>()); // 3. 筛选不合格的记录(check_result = 0) List<RawMaterial> unqualifiedList = rawMaterialList.stream() .filter(i -> i.getCheckResult() == 0) // 0表示不合格 .collect(Collectors.toList()); // 4. 处理图表项 (Item) List<QualityStatisticsItem> itemList = new ArrayList<>(); Map<QualityInspect, LocalDate> dateMap = qualityInspects.isEmpty() ? new HashMap<>() : qualityInspectList.stream() .collect(Collectors.toMap( i -> i, i -> i.getCheckTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), (existing, replacement) -> existing)); List<RawMaterial> itemList = new ArrayList<>(); if (type == 3) { // 季度模式:按月分组 Map<String, List<QualityInspect>> groupByMonth = qualityInspects.isEmpty() ? Map<String, List<RawMaterial>> groupByMonth = unqualifiedList.isEmpty() ? new HashMap<>() : qualityInspects.stream() unqualifiedList.stream() .collect(Collectors.groupingBy(i -> { LocalDate ld = dateMap.get(i); LocalDate ld = i.getCheckTime().toInstant() .atZone(ZoneId.systemDefault()).toLocalDate(); return ld.format(DateTimeFormatter.ofPattern("yyyy-MM")); })); for (int i = 0; i < 3; i++) { LocalDate monthDate = startDate.plusMonths(i); String monthStr = monthDate.format(DateTimeFormatter.ofPattern("yyyy-MM")); itemList.add(buildItem(monthStr, groupByMonth.getOrDefault(monthStr, new ArrayList<>()))); List<RawMaterial> monthData = groupByMonth.getOrDefault(monthStr, new ArrayList<>()); itemList.add(buildItem(monthStr, monthData)); } } else { // 周或月模式:按天分组 Map<String, List<QualityInspect>> groupByDay = qualityInspects.isEmpty() ? Map<String, List<RawMaterial>> groupByDay = unqualifiedList.isEmpty() ? new HashMap<>() : qualityInspects.stream() unqualifiedList.stream() .collect(Collectors.groupingBy(i -> { LocalDate ld = dateMap.get(i); LocalDate ld = i.getCheckTime().toInstant() .atZone(ZoneId.systemDefault()).toLocalDate(); return ld.format(DateTimeFormatter.ofPattern("MM/dd")); })); long days = ChronoUnit.DAYS.between(startDate, endDate); for (int i = 0; i <= days; i++) { LocalDate tempDay = startDate.plusDays(i); String dayStr = tempDay.format(DateTimeFormatter.ofPattern("MM/dd")); itemList.add(buildItem(dayStr, groupByDay.getOrDefault(dayStr, new ArrayList<>()))); List<RawMaterial> dayData = groupByDay.getOrDefault(dayStr, new ArrayList<>()); itemList.add(buildItem(dayStr, dayData)); } } dto.setItem(itemList); dto.setRawItem(itemList); return dto; } private BigDecimal sumQuantity(List<QualityInspect> list, Integer type) { return list.stream() .filter(i -> i.getInspectType().equals(type)) .map(QualityInspect::getQuantity) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } private QualityStatisticsItem buildItem(String dateLabel, List<QualityInspect> list) { QualityStatisticsItem item = new QualityStatisticsItem(); private RawMaterial buildItem(String dateLabel, List<RawMaterial> list) { RawMaterial item = new RawMaterial(); item.setDate(dateLabel); item.setSupplierNum(list.stream().filter(i -> i.getInspectType() == 0).map(QualityInspect::getQuantity) .reduce(BigDecimal.ZERO, BigDecimal::add)); item.setProcessNum(list.stream().filter(i -> i.getInspectType() == 1).map(QualityInspect::getQuantity) .reduce(BigDecimal.ZERO, BigDecimal::add)); item.setFactoryNum(list.stream().filter(i -> i.getInspectType() == 2).map(QualityInspect::getQuantity) .reduce(BigDecimal.ZERO, BigDecimal::add)); item.setSupplierNum( BigDecimal.valueOf(list.stream().filter(i -> i.getCheckType() == 0).count()) ); item.setProcessNum( BigDecimal.valueOf(list.stream().filter(i -> i.getCheckType() == 1).count()) ); item.setFactoryNum( BigDecimal.valueOf(list.stream().filter(i -> i.getCheckType() == 2).count()) ); return item; } src/main/java/com/ruoyi/production/controller/ProductBomController.java
@@ -73,7 +73,7 @@ @Log(title = "修改", businessType = BusinessType.UPDATE) @PutMapping("/update") public AjaxResult update(@RequestBody ProductBom productBom) { return AjaxResult.success(productBomService.updateById(productBom)); return AjaxResult.success(productBomService.update(productBom)); } @ApiOperation("删除BOM") src/main/java/com/ruoyi/production/service/ProductBomService.java
@@ -24,6 +24,8 @@ AjaxResult add(ProductBom productBom); AjaxResult update(ProductBom productBom); AjaxResult uploadBom(MultipartFile file); void exportBom(HttpServletResponse response, Integer bomId); src/main/java/com/ruoyi/production/service/impl/ProductBomServiceImpl.java
@@ -13,12 +13,11 @@ import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.production.dto.BomImportDto; import com.ruoyi.production.dto.ProcessRouteDto; import com.ruoyi.production.dto.ProductBomDto; import com.ruoyi.production.dto.ProductStructureDto; import com.ruoyi.production.mapper.ProductBomMapper; import com.ruoyi.production.pojo.ProductBom; import com.ruoyi.production.pojo.ProductProcess; import com.ruoyi.production.pojo.ProductStructure; import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.ProductBomService; import com.ruoyi.production.service.ProductProcessService; import com.ruoyi.production.service.ProductStructureService; @@ -59,6 +58,18 @@ @Autowired private ProductProcessService productProcessService; @Autowired private ProductStructureMapper productStructureMapper; @Autowired private ProductOrderMapper productOrderMapper; @Autowired private ProductProcessRouteMapper productProcessRouteMapper; @Autowired private ProcessRouteMapper processRouteMapper; @Override public IPage<ProductBomDto> listPage(Page page, ProductBomDto productBomDto) { return productBomMapper.listPage(page, productBomDto); @@ -99,6 +110,85 @@ @Override @Transactional(rollbackFor = Exception.class) public AjaxResult update(ProductBom productBom) { // 查询出产品模型信息 if (productBom.getProductModelId() == null) { throw new ServiceException("请选择产品模型"); } ProductBom oldBom = productBomMapper.selectById(productBom.getId()); // 如果规格改变,关联的生产订单如果订单完成数量>0,则不许改bom;否则删除之前关联的产品结构, 修改关联订单的产品信息 if (!oldBom.getProductModelId().equals(productBom.getProductModelId())) { ProductModel productModel = productModelService.getById(productBom.getProductModelId()); if (productModel == null) { throw new ServiceException("选择的产品模型不存在"); } // 关联的生产订单如果订单完成数量>0,则不许改bom // 先查询与该BOM关联的工艺路线 List<ProductProcessRoute> productProcessRoutes = productProcessRouteMapper.selectList(new LambdaQueryWrapper<ProductProcessRoute>() .eq(ProductProcessRoute::getBomId, oldBom.getId())); // 检查是否有关联的工艺路线 if (!productProcessRoutes.isEmpty()) { // 提取工艺路线关联的生产订单ID List<Long> orderIds = productProcessRoutes.stream() .map(ProductProcessRoute::getProductOrderId) .filter(Objects::nonNull) .collect(Collectors.toList()); // 查询关联的生产订单 if (!orderIds.isEmpty()) { List<ProductOrder> productOrders = productOrderMapper.selectList(new LambdaQueryWrapper<ProductOrder>() .in(ProductOrder::getId, orderIds)); // 检查订单完成数量 for (ProductOrder order : productOrders) { if (order.getCompleteQuantity() != null && order.getCompleteQuantity().compareTo(BigDecimal.ZERO) > 0) { throw new ServiceException("该BOM已关联生产订单且有完成数量,无法修改"); } else { // 修改关联订单的产品信息 order.setProductModelId(productBom.getProductModelId()); productOrderMapper.updateById(order); } } } // 修改关联产品工艺路线的产品信息 for (ProductProcessRoute route : productProcessRoutes) { route.setProductModelId(productBom.getProductModelId()); productProcessRouteMapper.updateById(route); } // 查询关联的工艺路线 List<ProcessRoute> processRoutes = processRouteMapper.selectList(new LambdaQueryWrapper<ProcessRoute>() .eq(ProcessRoute::getBomId, oldBom.getId())); if (!processRoutes.isEmpty()) { // 修改关联工艺路线的产品信息 for (ProcessRoute route : processRoutes) { route.setProductModelId(productBom.getProductModelId()); processRouteMapper.updateById(route); } } } // 删除之前关联的产品结构 productStructureMapper.delete(new LambdaQueryWrapper<ProductStructure>().eq(ProductStructure::getBomId, productBom.getId())); // 关联新的产品结构 ProductStructure productStructure = new ProductStructure(); productStructure.setProductModelId(productBom.getProductModelId()); productStructure.setUnit(productModel.getUnit()); productStructure.setUnitQuantity(BigDecimal.valueOf(1)); productStructure.setBomId(productBom.getId()); productStructureService.save(productStructure); } productBomMapper.updateById(productBom); return AjaxResult.success(); } @Override @Transactional(rollbackFor = Exception.class) public AjaxResult uploadBom(MultipartFile file) { ExcelUtil<BomImportDto> util = new ExcelUtil<>(BomImportDto.class); List<BomImportDto> list; src/main/java/com/ruoyi/quality/pojo/RawMaterial.java
@@ -10,6 +10,7 @@ import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Date; @@ -68,4 +69,20 @@ @ApiModelProperty(value = "修改时间") @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; @ApiModelProperty(value = "原材料不合格数") @TableField(exist = false) private BigDecimal supplierNum; @ApiModelProperty(value = "出厂不合格数") @TableField(exist = false) private BigDecimal factoryNum; @TableField(exist = false) @ApiModelProperty(value = "过程不合格数") private BigDecimal processNum; @TableField(exist = false) private String date; } src/main/resources/mapper/consumables/ConsumablesInventoryMapper.xml
@@ -110,6 +110,8 @@ <select id="listConsumablesInventoryExportData" resultType="com.ruoyi.consumables.execl.ConsumablesInventoryExportData"> select si.qualitity, -- 当前净重 = 入库净重 - 出库净重 (COALESCE(sir.total_net_weight,0) - COALESCE(sor.total_net_weight,0)) as net_weight, pm.model, pm.unit, p.product_name, @@ -120,6 +122,21 @@ from consumables_inventory si left join product_model pm on si.product_model_id = pm.id left join product p on pm.product_id = p.id left join ( select product_model_id, sum(net_weight) as total_net_weight from Consumables_in_record group by product_model_id ) sir on si.product_model_id = sir.product_model_id -- 出库净重 left join ( select product_model_id, sum(net_weight) as total_net_weight from Consumables_out_record group by product_model_id ) sor on si.product_model_id = sor.product_model_id where 1 = 1 <if test="ew.productName != null and ew.productName !=''"> and p.product_name like concat('%',#{ew.productName},'%') src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
@@ -30,20 +30,20 @@ LEFT JOIN product_model pm ON qu.model = pm.id where 1=1 <if test="qualityUnqualified.inspectType != null "> AND inspect_type = #{qualityUnqualified.inspectType} <if test="qualityUnqualified.checkType != null "> AND check_type = #{qualityUnqualified.checkType} </if> <if test="qualityUnqualified.inspectState != null "> AND inspect_state = #{qualityUnqualified.inspectState} </if> <if test="qualityUnqualified.productName != null and qualityUnqualified.productName != '' "> AND product_name = #{qualityUnqualified.productName} AND product_name like concat('%',#{qualityUnqualified.productName},'%') </if> <if test="qualityUnqualified.entryDateStart != null and qualityUnqualified.entryDateStart != '' "> AND check_time >= DATE_FORMAT(#{qualityUnqualified.entryDateStart},'%Y-%m-%d') </if> <if test="qualityUnqualified.entryDateEnd != null and qualityUnqualified.entryDateEnd != '' "> AND check_time <= DATE_FORMAT(#{qualityUnqualified.entryDateEnd},'%Y-%m-%d') AND check_time <= DATE_FORMAT(#{qualityUnqualified.entryDateEnd},'%Y-%m-%d') </if> </select> <select id="qualityUnqualifiedExport" resultType="com.ruoyi.quality.pojo.QualityUnqualified"> src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -102,6 +102,8 @@ <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData"> select si.qualitity, -- 当前净重 = 入库净重 - 出库净重 (COALESCE(sir.total_net_weight, 0) - COALESCE(sor.total_net_weight, 0)) AS net_weight,- pm.model, pm.unit, p.product_name, @@ -112,6 +114,16 @@ from stock_inventory si left join product_model pm on si.product_model_id = pm.id left join product p on pm.product_id = p.id LEFT JOIN ( SELECT product_model_id, SUM(net_weight) AS total_net_weight FROM stock_in_record GROUP BY product_model_id ) sir ON si.product_model_id = sir.product_model_id LEFT JOIN ( SELECT product_model_id, SUM(net_weight) AS total_net_weight FROM stock_out_record GROUP BY product_model_id ) sor ON si.product_model_id = sor.product_model_id where 1 = 1 <if test="ew.productName != null and ew.productName !=''"> and p.product_name like concat('%',#{ew.productName},'%') src/main/resources/static/work-order-template.docxBinary files differ