liding
2026-04-24 4c8144cc2785cdb8c5529c6c59a8901a33ec06bb
feat:1.生产计划下发 2.订单-工艺路线,bom,工序
已添加2个文件
已修改32个文件
已删除1个文件
650 ■■■■■ 文件已修改
doc/add.sql 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionBomStructureVo.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderRoutingOperationVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionBomStructureController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionBomStructureMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionOrderRoutingOperationMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionOrder.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionOrderRouting.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperation.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionPlan.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionBomStructureService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 102 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java 94 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyBomStructureController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/pojo/TechnologyBomStructure.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/pojo/TechnologyRouting.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/pojo/TechnologyRoutingOperation.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionBomStructureMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOrderMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOrderRoutingOperationMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/technology/TechnologyRoutingMapper.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/add.sql
ÎļþÒÑɾ³ý
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
@@ -38,7 +38,7 @@
    private Long returnManagementId;
    @Schema(description = "退货产品id")
    private Long returnSaleLedgerProductId;
    private Long returnsalesLedgerProductId;
    @Schema(description = "退货产品数量")
    private BigDecimal num;
src/main/java/com/ruoyi/production/bean/vo/ProductionBomStructureVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.production.bean.vo;
import com.ruoyi.production.pojo.ProductionBomStructure;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class ProductionBomStructureVo extends ProductionBomStructure {
    @Schema(description = "工序名称")
    private String operationName;
    @Schema(description = "产品名称")
    private String productName;
    @Schema(description = "产品ID")
    private Long productId;
    @Schema(description = "规格型号")
    private String model;
    private List<ProductionBomStructureVo> children;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderRoutingOperationVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.production.bean.vo;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class ProductionOrderRoutingOperationVo extends ProductionOrderRoutingOperation {
    @Schema(description = "产品名称")
    private String productName;
    @Schema(description = "规格型号")
    private String model;
    @Schema(description = "单位")
    private String unit;
    @Schema(description = "基础工序ID")
    private Long technologyOperationId;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
@@ -4,9 +4,11 @@
import com.ruoyi.production.pojo.ProductionOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(name = "ProductionOrderVo", description = "生产订单返回对象")
public class ProductionOrderVo extends ProductionOrder {
@@ -27,4 +29,7 @@
    @Schema(description = "产品图片")
    private List<StorageBlobVO> productImages;
    @Schema(description = "bom编号")
    private String bomNo;
}
src/main/java/com/ruoyi/production/controller/ProductionBomStructureController.java
@@ -1,7 +1,16 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.service.ProductionBomStructureService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * <p>
@@ -13,6 +22,15 @@
 */
@RestController
@RequestMapping("/productionBomStructure")
@AllArgsConstructor
public class ProductionBomStructureController {
    private ProductionBomStructureService productionBomStructureService;
    @GetMapping("/listByBomId/{bomId}")
    @Operation(summary = "根据BOM查询生产订单BOM结构树")
    public R<List<ProductionBomStructureVo>> listByBomId(@PathVariable Long bomId) {
        return R.ok(productionBomStructureService.listByBomId(bomId));
    }
}
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
@@ -7,19 +7,12 @@
import com.ruoyi.production.bean.vo.ProductionOrderVo;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.service.ProductionOrderService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -45,36 +38,35 @@
    @GetMapping("/{id}")
    @Operation(summary = "生产订单详情")
    public R<ProductionOrderVo> getInfo(@PathVariable("id") Long id) {
    public R<ProductionOrderVo> getInfo(@PathVariable Long id) {
        return R.ok(productionOrderService.getProductionOrderInfo(id));
    }
    @PostMapping
    @Operation(
            summary = "新增生产订单",
            description = "新增下单只支持两种方式:1. ç”Ÿäº§è®¡åˆ’生成,传 productionPlanIds,系统自动汇总计划得到产品规格和数量;"
    @PostMapping("/addOrder")
    @Operation(summary = "新增生产订单", description = "新增下单只支持两种方式:1. ç”Ÿäº§è®¡åˆ’生成,传 productionPlanIds,系统自动汇总计划得到产品规格和数量;"
                    + "2. æ‰‹åŠ¨æ–°å¢žï¼Œå¿…é¡»ä¼  productModelId å’Œ quantity。"
                    + "technologyRoutingId ä¸ºç©ºæ—¶ä¼šè‡ªåŠ¨åŒ¹é…è¯¥äº§å“è§„æ ¼æœ€æ–°å·¥è‰ºè·¯çº¿ï¼Œquantity æœ€ç»ˆå¿…须大于 0。"
    )
    @io.swagger.v3.oas.annotations.parameters.RequestBody(
            required = true,
            description = "前端友好提示:如果是生产计划生成,请传 productionPlanIds;"
                    + "如果是手动新增,请至少填写 productModelId、quantity,且 quantity å¿…须大于 0。",
            content = @Content(schema = @Schema(implementation = ProductionOrder.class))
    )
                    + "technologyRoutingId ä¸ºç©ºæ—¶ä¼šè‡ªåŠ¨åŒ¹é…è¯¥äº§å“è§„æ ¼æœ€æ–°å·¥è‰ºè·¯çº¿ï¼Œquantity æœ€ç»ˆå¿…须大于 0。")
    @io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, description = "前端友好提示:如果是生产计划生成,请传 productionPlanIds;"
                    + "如果是手动新增,请至少填写 productModelId、quantity,且 quantity å¿…须大于 0。", content = @Content(schema = @Schema(implementation = ProductionOrder.class)))
    public R<Boolean> add(@RequestBody ProductionOrder productionOrder) {
        return R.ok(productionOrderService.saveProductionOrder(productionOrder));
    }
    @PutMapping
    @PutMapping("/editOrder")
    @Operation(summary = "修改生产订单")
    public R<Boolean> edit(@RequestBody ProductionOrder productionOrder) {
        return R.ok(productionOrderService.saveProductionOrder(productionOrder));
    }
    @Operation(summary = "绑定工艺路线")
    @PostMapping("/bindingRoute")
    public R bindingRoute(@RequestBody ProductionOrderDto productionOrderDto) {
        return R.ok(productionOrderService.bindingRoute(productionOrderDto));
    }
    @PostMapping("/syncSnapshot/{id}")
    @Operation(summary = "同步生产订单工艺/BOM快照")
    public R<Integer> syncSnapshot(@PathVariable("id") Long id) {
    public R<Integer> syncSnapshot(@PathVariable Long id) {
        return R.ok(productionOrderService.syncProductionOrderSnapshot(id));
    }
src/main/java/com/ruoyi/production/mapper/ProductionBomStructureMapper.java
@@ -1,8 +1,12 @@
package com.ruoyi.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.pojo.ProductionBomStructure;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -15,4 +19,6 @@
@Mapper
public interface ProductionBomStructureMapper extends BaseMapper<ProductionBomStructure> {
    List<ProductionBomStructureVo> listByBomId(@Param("bomId") Long bomId);
}
src/main/java/com/ruoyi/production/mapper/ProductionOrderRoutingOperationMapper.java
@@ -1,8 +1,12 @@
package com.ruoyi.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.production.bean.vo.ProductionOrderRoutingOperationVo;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -15,4 +19,6 @@
@Mapper
public interface ProductionOrderRoutingOperationMapper extends BaseMapper<ProductionOrderRoutingOperation> {
    List<ProductionOrderRoutingOperationVo> selectVoListByOrderId(@Param("orderId") Long orderId);
}
src/main/java/com/ruoyi/production/pojo/ProductionOrder.java
@@ -65,7 +65,7 @@
    private LocalDateTime endTime;
    @Schema(description = "销售产品规格id。生产下单接口一般不作为前端必填项,存在销售来源时由系统内部关联。")
    private Integer saleLedgerProductId;
    private Integer salesLedgerProductId;
    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/production/pojo/ProductionOrderRouting.java
@@ -48,8 +48,11 @@
    @Schema(description = "工艺路线编码")
    private String processRouteCode;
    @Schema(description = "关联bom的id")
    private Integer bomId;
    @Schema(description = "基础bom的id")
    private Long bomId;
    @Schema(description = "订单bom的id")
    private Long orderBomId;
    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperation.java
@@ -3,8 +3,6 @@
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
@@ -53,6 +51,9 @@
    @Schema(description = "是否质检工序")
    private Boolean isQuality;
    @Schema(description = "是否生产")
    private Boolean isProduction;
    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
@@ -60,4 +61,7 @@
    @Schema(description = "部门ID")
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @Schema(description = "工序名称")
    private String operationName;
}
src/main/java/com/ruoyi/production/pojo/ProductionPlan.java
@@ -71,6 +71,9 @@
    @Schema(description = "需求数量")
    private BigDecimal qtyRequired;
    @Schema(description = "已下发数量")
    private BigDecimal quantityIssued;
    @Schema(description = "是否下发制造订单")
    private Boolean issued;
src/main/java/com/ruoyi/production/service/ProductionBomStructureService.java
@@ -1,7 +1,10 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.pojo.ProductionBomStructure;
import java.util.List;
/**
 * <p>
@@ -13,4 +16,6 @@
 */
public interface ProductionBomStructureService extends IService<ProductionBomStructure> {
    List<ProductionBomStructureVo> listByBomId(Long bomId);
}
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingService.java
@@ -1,8 +1,8 @@
package com.ruoyi.production.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.bean.vo.ProductionOrderRoutingOperationVo;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import java.util.List;
@@ -10,5 +10,5 @@
    ProductionOrderRouting listMain(Long orderId);
    List<ProductionOrderRoutingOperation> listItem(Long orderId);
    List<ProductionOrderRoutingOperationVo> listItem(Long orderId);
}
src/main/java/com/ruoyi/production/service/ProductionOrderService.java
@@ -22,4 +22,6 @@
    boolean removeProductionOrder(List<Long> ids);
    int syncProductionOrderSnapshot(Long productionOrderId);
    Object bindingRoute(ProductionOrderDto productionOrderDto);
}
src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -1,10 +1,17 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
import com.ruoyi.production.mapper.ProductionBomStructureMapper;
import com.ruoyi.production.pojo.ProductionBomStructure;
import com.ruoyi.production.service.ProductionBomStructureService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -15,6 +22,36 @@
 * @since 2026-04-21 03:55:52
 */
@Service
@RequiredArgsConstructor()
public class ProductionBomStructureServiceImpl extends ServiceImpl<ProductionBomStructureMapper, ProductionBomStructure> implements ProductionBomStructureService {
    private  final ProductionBomStructureMapper productionBomStructureMapper;
    /**
     * æ ¹æ®BOM查询并组装结构树。
     */
    @Override
    public List<ProductionBomStructureVo> listByBomId(Long bomId) {
        List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId);
        Map<Long, ProductionBomStructureVo> map = new HashMap<>();
        for (ProductionBomStructureVo node : list) {
            node.setChildren(new ArrayList<>());
            map.put(node.getId(), node);
        }
        List<ProductionBomStructureVo> tree = new ArrayList<>();
        for (ProductionBomStructureVo node : list) {
            Long parentId = node.getParentId();
            if (parentId == null || parentId == 0L) {
                tree.add(node);
                continue;
            }
            ProductionBomStructureVo parent = map.get(parentId);
            if (parent != null) {
                parent.getChildren().add(node);
            }
        }
        return tree;
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingServiceImpl.java
@@ -2,10 +2,10 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.production.bean.vo.ProductionOrderRoutingOperationVo;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.service.ProductionOrderRoutingService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -29,11 +29,7 @@
    }
    @Override
    public List<ProductionOrderRoutingOperation> listItem(Long orderId) {
        return productionOrderRoutingOperationMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, orderId)
                        .orderByAsc(ProductionOrderRoutingOperation::getDragSort)
                        .orderByAsc(ProductionOrderRoutingOperation::getId));
    public List<ProductionOrderRoutingOperationVo> listItem(Long orderId) {
        return productionOrderRoutingOperationMapper.selectVoListByOrderId(orderId);
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -58,6 +58,7 @@
    private final TechnologyRoutingMapper technologyRoutingMapper;
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
    private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper;
    private final TechnologyOperationMapper technologyOperationMapper;
    private final TechnologyBomMapper technologyBomMapper;
    private final TechnologyBomStructureMapper technologyBomStructureMapper;
    private final FileUtil fileUtil;
@@ -136,20 +137,64 @@
    }
    @Override
    public Integer bindingRoute(ProductionOrderDto productionOrderDto) {
        if (productionOrderDto == null || productionOrderDto.getId() == null) {
            throw new ServiceException("生产订单ID不能为空");
        }
        ProductionOrder productionOrder = this.getById(productionOrderDto.getId());
        if (productionOrder == null) {
            throw new ServiceException("生产订单不存在");
        }
        Long targetRoutingId = productionOrderDto.getTechnologyRoutingId() == null
                ? productionOrder.getTechnologyRoutingId()
                : productionOrderDto.getTechnologyRoutingId();
        if (targetRoutingId == null) {
            throw new ServiceException("工艺路线ID不能为空");
        }
        TechnologyRouting targetRouting = technologyRoutingMapper.selectById(targetRoutingId);
        if (targetRouting == null) {
            throw new ServiceException("工艺路线不存在");
        }
        if (productionOrder.getProductModelId() != null
                && !Objects.equals(productionOrder.getProductModelId(), targetRouting.getProductModelId())) {
            throw new ServiceException("工艺路线与生产订单产品规格不匹配");
        }
        if (ProductOrderStatusEnum.isStarted(productionOrder.getStatus())
                && !Objects.equals(productionOrder.getTechnologyRoutingId(), targetRoutingId)) {
            throw new ServiceException("生产订单已开工,不能修改工艺路线");
        }
        if (!Objects.equals(productionOrder.getTechnologyRoutingId(), targetRoutingId)) {
            ProductionOrder update = new ProductionOrder();
            update.setId(productionOrder.getId());
            update.setTechnologyRoutingId(targetRoutingId);
            if (!this.updateById(update)) {
                throw new ServiceException("绑定工艺路线失败");
            }
        }
        // ç»‘定路线仅重建订单侧快照数据
        return syncProductionOrderSnapshot(productionOrder.getId());
    }
    @Override
    public int syncProductionOrderSnapshot(Long productionOrderId) {
        ProductionOrder productionOrder = this.getById(productionOrderId);
        if (productionOrder == null) {
            throw new ServiceException("Production order not found");
            throw new ServiceException("生产订单不存在");
        }
        if (productionOrder.getTechnologyRoutingId() == null) {
            throw new ServiceException("technologyRoutingId is required");
            throw new ServiceException("工艺路线ID不能为空");
        }
        TechnologyRouting technologyRouting = technologyRoutingMapper.selectById(productionOrder.getTechnologyRoutingId());
        if (technologyRouting == null) {
            throw new ServiceException("Technology routing not found");
            throw new ServiceException("工艺路线不存在");
        }
        // è®¢å•快照按“先清后建”处理,保证工艺路线、工序、参数、BOM å…¨éƒ¨æ¥è‡ªåŒä¸€ç‰ˆæœ¬ã€‚
        clearProductionSnapshot(productionOrderId);
        ProductionOrderBom orderBom = syncProductionOrderBomSnapshot(productionOrder, technologyRouting);
        ProductionOrderRouting orderRouting = new ProductionOrderRouting();
        orderRouting.setProductionOrderId(productionOrder.getId());
@@ -158,6 +203,7 @@
        orderRouting.setProcessRouteCode(technologyRouting.getProcessRouteCode());
        orderRouting.setDescription(technologyRouting.getDescription());
        orderRouting.setBomId(technologyRouting.getBomId());
        orderRouting.setOrderBomId(orderBom == null ? null : orderBom.getId());
        productionOrderRoutingMapper.insert(orderRouting);
        int syncedParamCount = 0;
@@ -166,6 +212,13 @@
                        .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId())
                        .orderByAsc(TechnologyRoutingOperation::getDragSort)
                        .orderByAsc(TechnologyRoutingOperation::getId));
        Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
                        routingOperations.stream()
                                .map(TechnologyRoutingOperation::getTechnologyOperationId)
                                .filter(Objects::nonNull)
                                .collect(Collectors.toSet()))
                .stream()
                .collect(Collectors.toMap(TechnologyOperation::getId, TechnologyOperation::getName, (a, b) -> a));
        for (TechnologyRoutingOperation sourceOperation : routingOperations) {
            // è®¢å•工序保存的是工艺工序快照,后续报工只依赖快照,不再直接引用工艺主数据。
            ProductionOrderRoutingOperation targetOperation = new ProductionOrderRoutingOperation();
@@ -175,6 +228,7 @@
            targetOperation.setProductModelId(sourceOperation.getProductModelId());
            targetOperation.setDragSort(sourceOperation.getDragSort());
            targetOperation.setIsQuality(sourceOperation.getIsQuality());
            targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId()));
            productionOrderRoutingOperationMapper.insert(targetOperation);
            ProductionOperationTask task = new ProductionOperationTask();
@@ -212,18 +266,17 @@
            }
        }
        syncProductionOrderBomSnapshot(productionOrder, technologyRouting);
        upsertOrderPick(productionOrder);
        return syncedParamCount;
    }
    private void syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
    private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) {
        if (technologyRouting.getBomId() == null) {
            return;
            return null;
        }
        TechnologyBom technologyBom = technologyBomMapper.selectById(technologyRouting.getBomId());
        if (technologyBom == null) {
            throw new ServiceException("Technology BOM not found");
            throw new ServiceException("工艺BOM不存在");
        }
        List<TechnologyBomStructure> structureList = technologyBomStructureMapper.selectList(
                Wrappers.<TechnologyBomStructure>lambdaQuery()
@@ -257,6 +310,7 @@
            productionBomStructureMapper.insert(target);
            idMap.put(source.getId(), target.getId());
        }
        return orderBom;
    }
    private void clearProductionSnapshot(Long productionOrderId) {
@@ -265,7 +319,7 @@
                Wrappers.<ProductionOrderPickRecord>lambdaQuery()
                        .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)) > 0;
        if (hasPickRecord) {
            throw new ServiceException("Production order pick records already exist, snapshot cannot be regenerated");
            throw new ServiceException("生产订单已存在领料记录,不能重新生成快照");
        }
        List<Long> taskIds = productionOperationTaskMapper.selectList(
                        Wrappers.<ProductionOperationTask>lambdaQuery()
@@ -277,7 +331,7 @@
                    Wrappers.<ProductionProductMain>lambdaQuery()
                            .in(ProductionProductMain::getProductionOperationTaskId, taskIds)) > 0;
            if (started) {
                throw new ServiceException("Production order already started, snapshot cannot be regenerated");
                throw new ServiceException("生产订单已开工,不能重新生成快照");
            }
            productionOperationTaskMapper.delete(Wrappers.<ProductionOperationTask>lambdaQuery()
                    .eq(ProductionOperationTask::getProductionOrderId, productionOrderId));
@@ -350,15 +404,15 @@
    private void validateAndFillOrder(ProductionOrder productionOrder, ProductionOrder oldOrder) {
        if (productionOrder == null) {
            throw new ServiceException("Production order is required");
            throw new ServiceException("生产订单不能为空");
        }
        fillFromSalesLedgerProduct(productionOrder);
        fillFromProductionPlans(productionOrder);
        if (productionOrder.getProductModelId() == null) {
            throw new ServiceException("productModelId is required");
            throw new ServiceException("产品规格ID不能为空");
        }
        if (defaultDecimal(productionOrder.getQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("quantity must be greater than 0");
            throw new ServiceException("下单数量必须大于0");
        }
//        if (productionOrder.getTechnologyRoutingId() == null) {
//            // æœªæ˜¾å¼æŒ‡å®šå·¥è‰ºè·¯çº¿æ—¶ï¼ŒæŒ‰äº§å“è§„格选最新一条工艺作为默认路线。
@@ -368,7 +422,7 @@
//                            .orderByDesc(TechnologyRouting::getId)
//                            .last("limit 1"));
//            if (technologyRouting == null) {
//                throw new ServiceException("No technology routing found for the product model");
//                throw new ServiceException("未找到该产品规格对应的工艺路线");
//            }
//            productionOrder.setTechnologyRoutingId(technologyRouting.getId());
//        }
@@ -377,29 +431,29 @@
            if (!Objects.equals(oldOrder.getProductModelId(), productionOrder.getProductModelId())
                    || !Objects.equals(oldOrder.getTechnologyRoutingId(), productionOrder.getTechnologyRoutingId())
                    || compareDecimal(oldOrder.getQuantity(), productionOrder.getQuantity()) != 0) {
                throw new ServiceException("Started production orders cannot modify product, routing or quantity");
                throw new ServiceException("生产订单已开工,不能修改产品、工艺路线或数量");
            }
        }
    }
    private void fillFromSalesLedgerProduct(ProductionOrder productionOrder) {
        if (productionOrder.getSaleLedgerProductId() == null) {
        if (productionOrder.getSalesLedgerProductId() == null) {
            return;
        }
        // é”€å”®æ˜Žç»†æ˜¯è®¢å•来源时,以销售明细为准回填销售台账、产品规格和默认数量。
        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(productionOrder.getSaleLedgerProductId().longValue());
        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(productionOrder.getSalesLedgerProductId().longValue());
        if (salesLedgerProduct == null) {
            throw new ServiceException("Sales ledger product not found");
            throw new ServiceException("销售台账产品不存在");
        }
        if (productionOrder.getSalesLedgerId() == null) {
            productionOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        } else if (!Objects.equals(productionOrder.getSalesLedgerId(), salesLedgerProduct.getSalesLedgerId())) {
            throw new ServiceException("salesLedgerId does not match the sales ledger product");
            throw new ServiceException("销售台账ID与销售台账产品不一致");
        }
        if (productionOrder.getProductModelId() == null) {
            productionOrder.setProductModelId(salesLedgerProduct.getProductModelId());
        } else if (!Objects.equals(productionOrder.getProductModelId(), salesLedgerProduct.getProductModelId())) {
            throw new ServiceException("productModelId does not match the sales ledger product");
            throw new ServiceException("产品规格ID与销售台账产品不一致");
        }
        if (productionOrder.getQuantity() == null || productionOrder.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            productionOrder.setQuantity(salesLedgerProduct.getQuantity());
@@ -420,22 +474,22 @@
        // å¤šè®¡åˆ’合并转单时,所有计划必须属于同一规格,且只能下发一次。
        List<ProductionPlan> productionPlans = productionPlanMapper.selectBatchIds(planIds);
        if (productionPlans.size() != planIds.size()) {
            throw new ServiceException("Some production plans do not exist");
            throw new ServiceException("部分生产计划不存在");
        }
        Set<Long> productModelIds = productionPlans.stream()
                .map(ProductionPlan::getProductModelId)
                .collect(Collectors.toSet());
        if (productModelIds.size() > 1) {
            throw new ServiceException("Selected production plans must belong to the same product model");
            throw new ServiceException("所选生产计划必须属于同一产品规格");
        }
        if (Boolean.TRUE.equals(productionPlans.stream().anyMatch(item -> Boolean.TRUE.equals(item.getIssued())))) {
            throw new ServiceException("Selected production plans already issued");
        if (productionPlans.stream().anyMatch(item -> item.getStatus() != null && item.getStatus() == 2)) {
            throw new ServiceException("所选生产计划已下发");
        }
        ProductionPlan firstPlan = productionPlans.get(0);
        if (productionOrder.getProductModelId() == null) {
            productionOrder.setProductModelId(firstPlan.getProductModelId());
        } else if (!Objects.equals(productionOrder.getProductModelId(), firstPlan.getProductModelId())) {
            throw new ServiceException("productModelId does not match the production plans");
            throw new ServiceException("产品规格ID与生产计划不一致");
        }
        if (productionOrder.getQuantity() == null || productionOrder.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            productionOrder.setQuantity(productionPlans.stream()
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -73,31 +73,30 @@
            throw new ServiceException("下发失败,未选择生产计划");
        }
        List<ProductionPlanDto> plans = productionPlanMapper.selectWithMaterialByIds(planIds);
        if (plans == null || plans.isEmpty() || plans.size() != planIds.size()) {
        List<ProductionPlanDto> planLists = productionPlanMapper.selectWithMaterialByIds(planIds);
        if (planLists == null || planLists.isEmpty() || planLists.size() != planIds.size()) {
            throw new ServiceException("下发失败,生产计划不存在或已被删除");
        }
        ProductionPlanDto firstPlan = plans.getFirst();
        ProductionPlanDto firstPlan = planLists.getFirst();
        if (firstPlan.getProductModelId() == null) {
            throw new ServiceException("下发失败,生产计划缺少产品型号");
        }
        boolean hasDifferentModel = plans.stream()
        boolean hasDifferentModel = planLists.stream()
                .anyMatch(item -> !Objects.equals(item.getProductModelId(), firstPlan.getProductModelId()));
        if (hasDifferentModel) {
            throw new BaseException("合并失败,所选生产计划的产品型号不一致");
        }
        boolean hasIssuedPlan = plans.stream()
                .anyMatch(item -> item.getStatus() != null && item.getStatus() != PLAN_STATUS_WAIT);
        boolean hasIssuedPlan = planLists.stream()
                .anyMatch(item -> item.getStatus() != null && item.getStatus() == PLAN_STATUS_ISSUED);
        if (hasIssuedPlan) {
            throw new BaseException("合并失败,所选生产计划存在已下发或部分下发数据");
        }
        BigDecimal totalRequiredQuantity = plans.stream()
                .map(ProductionPlan::getQtyRequired)
                .filter(Objects::nonNull)
        BigDecimal totalRequiredQuantity = planLists.stream()
                .map(this::resolveRemainingQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (totalRequiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("下发失败,所选生产计划需求总量必须大于0");
@@ -111,8 +110,25 @@
            throw new ServiceException("下发失败,下发数量不能大于计划需求总量");
        }
        BigDecimal remainingForOrderBind = assignedQuantity;
        List<Long> issuedPlanIds = new ArrayList<>();
        for (ProductionPlanDto plan : planLists) {
            BigDecimal remainingQuantity = resolveRemainingQuantity(plan);
            if (remainingForOrderBind.compareTo(BigDecimal.ZERO) <= 0 || remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            BigDecimal issueForThisPlan = remainingForOrderBind.min(remainingQuantity);
            remainingForOrderBind = remainingForOrderBind.subtract(issueForThisPlan);
            if (issueForThisPlan.compareTo(BigDecimal.ZERO) > 0) {
                issuedPlanIds.add(plan.getId());
            }
        }
        if (issuedPlanIds.isEmpty()) {
            throw new ServiceException("Issue failed, no quantity available for dispatch");
        }
        ProductionOrder productionOrder = new ProductionOrder();
        productionOrder.setProductionPlanIds(formatPlanIds(planIds));
        productionOrder.setProductionPlanIds(formatPlanIds(issuedPlanIds));
        productionOrder.setProductModelId(firstPlan.getProductModelId());
        productionOrder.setQuantity(assignedQuantity);
        productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
@@ -122,13 +138,31 @@
            throw new ServiceException("下发失败,生产订单保存失败");
        }
        int targetStatus = assignedQuantity.compareTo(totalRequiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED;
        List<ProductionPlan> updates = planIds.stream().map(id -> {
        //已下发数量
        BigDecimal remainingAssignedQuantity = assignedQuantity;
        List<ProductionPlan> updates = new ArrayList<>();
        for (ProductionPlanDto plan : planLists) {
            BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO);
            if (requiredQuantity.compareTo(BigDecimal.ZERO) < 0) {
                requiredQuantity = BigDecimal.ZERO;
            }
            BigDecimal remainingQuantity = resolveRemainingQuantity(plan);
            BigDecimal historicalIssuedQuantity = requiredQuantity.subtract(remainingQuantity);
            BigDecimal issuedQuantity = BigDecimal.ZERO;
            if (remainingAssignedQuantity.compareTo(BigDecimal.ZERO) > 0 && remainingQuantity.compareTo(BigDecimal.ZERO) > 0) {
                issuedQuantity = remainingAssignedQuantity.min(remainingQuantity);
                remainingAssignedQuantity = remainingAssignedQuantity.subtract(issuedQuantity);
            }
            BigDecimal totalIssuedQuantity = historicalIssuedQuantity.add(issuedQuantity);
            int planStatus = resolvePlanStatus(requiredQuantity, totalIssuedQuantity);
            ProductionPlan update = new ProductionPlan();
            update.setId(id);
            update.setStatus(targetStatus);
            return update;
        }).collect(Collectors.toList());
            update.setId(plan.getId());
            update.setStatus(planStatus);
            update.setQuantityIssued(totalIssuedQuantity);
            update.setIssued(planStatus == PLAN_STATUS_ISSUED);
            updates.add(update);
        }
        if (!updates.isEmpty()) {
            this.updateBatchById(updates);
        }
@@ -261,6 +295,34 @@
        util.exportExcel(response, exportList, "主生产计划");
    }
    private BigDecimal resolveRemainingQuantity(ProductionPlan plan) {
        if (plan == null) {
            return BigDecimal.ZERO;
        }
        BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO);
        if (requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal issuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO);
        if (issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            return requiredQuantity;
        }
        if (issuedQuantity.compareTo(requiredQuantity) >= 0) {
            return BigDecimal.ZERO;
        }
        return requiredQuantity.subtract(issuedQuantity);
    }
    private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) {
        if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            return PLAN_STATUS_WAIT;
        }
        if (issuedQuantity == null || issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) {
            return PLAN_STATUS_WAIT;
        }
        return issuedQuantity.compareTo(requiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED;
    }
    private String formatPlanIds(List<Long> planIds) {
        return planIds.stream()
                .filter(Objects::nonNull)
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -246,7 +246,7 @@
            ProductionAccount productionAccount = new ProductionAccount();
            productionAccount.setProductionProductMainId(productionProductMain.getId());
            productionAccount.setSalesLedgerId(productionOrder.getSalesLedgerId());
            productionAccount.setSalesLedgerProductId(productionOrder.getSaleLedgerProductId() == null ? null : productionOrder.getSaleLedgerProductId().longValue());
            productionAccount.setSalesLedgerProductId(productionOrder.getSalesLedgerProductId() == null ? null : productionOrder.getSalesLedgerProductId().longValue());
            productionAccount.setSchedulingUserId(user == null ? null : user.getUserId());
            productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName());
            productionAccount.setFinishedNum(productQty);
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -211,7 +211,6 @@
            result = salesLedgerProductMapper.updateById(salesLedgerProduct);
            /*删除对应的生产数据并重新新增*/
            deleteProductionData(Arrays.asList(salesLedgerProduct.getId()));
            // åˆ é™¤ç”Ÿäº§æ ¸ç®—数据
            addProductionData(salesLedgerProduct);
        }
@@ -273,7 +272,7 @@
        List<ProductionPlan> productionPlans = productionPlanMapper.selectList(
                new LambdaQueryWrapper<ProductionPlan>()
                        .in(ProductionPlan::getSalesLedgerProductId, productIds.stream().map(Long::intValue).collect(Collectors.toList())));
        if (org.springframework.util.CollectionUtils.isEmpty(productionPlans)) {
        if (CollectionUtils.isEmpty(productionPlans)) {
            return;
        }
        //如果生产计划已下发则不能删除
src/main/java/com/ruoyi/technology/controller/TechnologyBomStructureController.java
@@ -27,7 +27,7 @@
    @GetMapping("/listByBomId/{bomId}")
    @Operation(summary = "根据BOM查询结构树")
    public R<List<TechnologyBomStructureVo>> listByBomId(@PathVariable("bomId") Long bomId) {
    public R<List<TechnologyBomStructureVo>> listByBomId(@PathVariable Long bomId) {
        return R.ok(technologyBomStructureService.listByBomId(bomId));
    }
}
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java
@@ -43,7 +43,7 @@
    /**
     * æ–°å¢žå·¥è‰ºè·¯çº¿ã€‚
     */
    @PostMapping
    @PostMapping("/addTechRoute")
    @Operation(summary = "新增工艺路线")
    public R add(@RequestBody TechnologyRouting technologyRouting) {
        return R.ok(technologyRoutingService.saveTechnologyRouting(technologyRouting));
@@ -52,7 +52,7 @@
    /**
     * ä¿®æ”¹å·¥è‰ºè·¯çº¿ã€‚
     */
    @PutMapping
    @PutMapping("editTechRoute")
    @Operation(summary = "修改工艺路线")
    public R edit(@RequestBody TechnologyRouting technologyRouting) {
        return R.ok(technologyRoutingService.updateTechnologyRouting(technologyRouting));
src/main/java/com/ruoyi/technology/pojo/TechnologyBomStructure.java
@@ -17,7 +17,7 @@
 */
@Data
@TableName("technology_bom_structure")
@Schema(name = "TechnologyBomStructure对象", description = "BOM产品结构表")
@Schema(name = "TechnologyStructure对象", description = "BOM产品结构表")
public class TechnologyBomStructure implements Serializable {
    private static final long serialVersionUID = 1L;
src/main/java/com/ruoyi/technology/pojo/TechnologyRouting.java
@@ -43,7 +43,7 @@
    private String processRouteCode;
    @Schema(description = "关联bom的id")
    private Integer bomId;
    private Long bomId;
    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/technology/pojo/TechnologyRoutingOperation.java
@@ -48,6 +48,9 @@
    @Schema(description = "是否质检工序")
    private Boolean isQuality;
    @Schema(description = "是否生产")
    private Boolean isProduction;
    @Schema(description = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml
@@ -27,7 +27,7 @@
        from sales_refund_amount_order srao
        left join return_management rm on srao.return_management_id = rm.id
        left join return_sale_product rs on rm.id = rs.return_management_id
        left join sales_ledger_product slp on rs.return_sale_ledger_product_id = slp.id
        left join sales_ledger_product slp on rs.return_sales_ledger_product_id = slp.id
        left join sales_ledger sl on slp.sales_ledger_id = sl.id
        <where>
            <if test="ew.salesContractNo != null and ew.salesContractNo !=''">
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
@@ -6,7 +6,7 @@
    <resultMap id="BaseResultMap" type="com.ruoyi.procurementrecord.pojo.ReturnSaleProduct">
        <id column="id" property="id" />
        <result column="return_management_id" property="returnManagementId" />
        <result column="return_sale_ledger_product_id" property="returnSaleLedgerProductId" />
        <result column="return_sales_ledger_product_id" property="returnsalesLedgerProductId" />
        <result column="num" property="num" />
        <result column="status" property="status" />
    </resultMap>
@@ -21,18 +21,18 @@
                 LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
                 LEFT JOIN shipping_info si ON si.id = rm.shipping_id
                 LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
                 LEFT JOIN (SELECT return_sale_ledger_product_id,
                 LEFT JOIN (SELECT return_sales_ledger_product_id,
                                   SUM(num) AS total_return_num
                            FROM return_sale_product
                            WHERE 1 = 1 and return_management_id != #{returnManagementId}
                            GROUP BY return_sale_ledger_product_id) rs ON rs.return_sale_ledger_product_id = slp.id
                            GROUP BY return_sales_ledger_product_id) rs ON rs.return_sales_ledger_product_id = slp.id
        where rm.id =#{returnManagementId}
    </select>
    <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.dto.ReturnSaleProductDto">
        select rsp.*,slp.tax_inclusive_unit_price ,slp.tax_inclusive_total_price*rsp.num as price
        from return_sale_product rsp
                 left join sales_ledger_product slp on slp.id = rsp.return_sale_ledger_product_id
                 left join sales_ledger_product slp on slp.id = rsp.return_sales_ledger_product_id
        where rsp.return_management_id = #{returnManagementId}
    </select>
src/main/resources/mapper/production/ProductionBomStructureMapper.xml
@@ -17,4 +17,18 @@
        <result column="dept_id" property="deptId" />
    </resultMap>
    <select id="listByBomId" resultType="com.ruoyi.production.bean.vo.ProductionBomStructureVo">
        select pbs.*,
               p.product_name as productName,
               pm.product_id as productId,
               pm.model,
               top1.name as operationName
        from production_bom_structure pbs
                 left join product_model pm on pbs.product_model_id = pm.id
                 left join product p on pm.product_id = p.id
                 left join technology_operation top1 on pbs.technology_operation_id = top1.id
        where pbs.production_order_bom_id = #{bomId}
        order by pbs.id
    </select>
</mapper>
src/main/resources/mapper/production/ProductionOrderMapper.xml
@@ -16,7 +16,7 @@
        <result column="complete_quantity" property="completeQuantity" />
        <result column="start_time" property="startTime" />
        <result column="end_time" property="endTime" />
        <result column="sale_ledger_product_id" property="saleLedgerProductId" />
        <result column="sales_ledger_product_id" property="salesLedgerProductId" />
        <result column="create_user" property="createUser" />
        <result column="dept_id" property="deptId" />
        <result column="plan_complete_time" property="planCompleteTime" />
@@ -44,7 +44,7 @@
        po.complete_quantity,
        po.start_time,
        po.end_time,
        po.sale_ledger_product_id,
        po.sales_ledger_product_id,
        po.create_user,
        po.dept_id,
        po.plan_complete_time,
@@ -53,7 +53,8 @@
        sl.customer_name as customerName,
        p.product_name as productName,
        pm.model as model,
        tr.process_route_code as processRouteCode
        tr.process_route_code as processRouteCode,
        tb.bom_no as bomNo
    </sql>
    <sql id="ProductionOrderVoFrom">
@@ -62,6 +63,7 @@
                 left join product_model pm on po.product_model_id = pm.id
                 left join product p on pm.product_id = p.id
                 left join technology_routing tr on po.technology_routing_id = tr.id
                 left join technology_bom tb on tb.id = tr.bom_id
    </sql>
    <sql id="ProductionOrderWhere">
@@ -79,8 +81,8 @@
                <if test="c.technologyRoutingId != null">
                    and po.technology_routing_id = #{c.technologyRoutingId}
                </if>
                <if test="c.saleLedgerProductId != null">
                    and po.sale_ledger_product_id = #{c.saleLedgerProductId}
                <if test="c.salesLedgerProductId != null">
                    and po.sales_ledger_product_id = #{c.salesLedgerProductId}
                </if>
                <if test="c.status != null">
                    and po.status = #{c.status}
src/main/resources/mapper/production/ProductionOrderRoutingOperationMapper.xml
@@ -17,4 +17,26 @@
        <result column="dept_id" property="deptId" />
    </resultMap>
    <resultMap id="OperationVoResultMap" type="com.ruoyi.production.bean.vo.ProductionOrderRoutingOperationVo" extends="BaseResultMap">
        <result column="technologyOperationId" property="technologyOperationId" />
        <result column="productName" property="productName" />
        <result column="model" property="model" />
        <result column="unit" property="unit" />
    </resultMap>
    <select id="selectVoListByOrderId" resultMap="OperationVoResultMap">
        SELECT
        poro.*,
        tro.technology_operation_id AS technologyOperationId,
        p.product_name AS productName,
        pm.model AS model,
        pm.unit AS unit
        FROM production_order_routing_operation poro
        LEFT JOIN technology_routing_operation tro ON poro.technology_routing_operation_id = tro.id
        LEFT JOIN product_model pm ON poro.product_model_id = pm.id
        LEFT JOIN product p ON pm.product_id = p.id
        WHERE poro.production_order_id = #{orderId}
        ORDER BY poro.drag_sort ASC, poro.id ASC
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -31,7 +31,7 @@
                 left join production_product_main ppm on qi.product_main_id = ppm.id
                 left join product_work_order pwo on ppm.work_order_id = pwo.id
                 left join product_order po on pwo.product_order_id = po.id
                 left join sales_ledger_product slp on po.sale_ledger_product_id = slp.id and slp.type = 1
                 left join sales_ledger_product slp on po.sales_ledger_product_id = slp.id and slp.type = 1
        where qi.product_main_id = #{productMainId}
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -67,14 +67,14 @@
        LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
        LEFT JOIN (
        SELECT
        return_sale_ledger_product_id,
        return_sales_ledger_product_id,
        SUM(num) AS total_return_num
        FROM return_sale_product rsp
        left join return_management rm on rm.id = rsp.return_management_id
        left join shipping_info si on si.id = rm.shipping_id
        WHERE 1=1
        GROUP BY return_sale_ledger_product_id
        ) rs ON rs.return_sale_ledger_product_id = slp.id
        GROUP BY return_sales_ledger_product_id
        ) rs ON rs.return_sales_ledger_product_id = slp.id
        <where>
            <if test="shippingId != null">
                si.id = #{shippingId}
@@ -86,4 +86,4 @@
        left join sales_ledger sl on si.sales_ledger_id = sl.id
        where si.status = '已发货' and sl.customer_name = #{customerName}
    </select>
</mapper>
</mapper>
src/main/resources/mapper/technology/TechnologyRoutingMapper.xml
@@ -16,17 +16,17 @@
    <select id="pageTechnologyRouting" resultType="com.ruoyi.technology.bean.vo.TechnologyRoutingVo">
        select tr.id,
               tr.product_model_id as productModelId,
               tr.description,
               tr.create_time as createTime,
               tr.update_time as updateTime,
               tr.process_route_code as processRouteCode,
               tr.bom_id as bomId,
               tr.create_user as createUser,
               tr.dept_id as deptId,
               p.product_name as productName,
               pm.model,
               tb.bom_no as bomNo
        tr.product_model_id as productModelId,
        tr.description,
        tr.create_time as createTime,
        tr.update_time as updateTime,
        tr.process_route_code as processRouteCode,
        tr.bom_id as bomId,
        tr.create_user as createUser,
        tr.dept_id as deptId,
        p.product_name as productName,
        pm.model,
        tb.bom_no as bomNo
        from technology_routing tr
        left join product_model pm on tr.product_model_id = pm.id
        left join product p on pm.product_id = p.id
@@ -47,15 +47,6 @@
                </if>
                <if test="c.description != null and c.description != ''">
                    and tr.description like concat('%', #{c.description}, '%')
                </if>
                <if test="c.model != null and c.model != ''">
                    and pm.model like concat('%', #{c.model}, '%')
                </if>
                <if test="c.productName != null and c.productName != ''">
                    and p.product_name like concat('%', #{c.productName}, '%')
                </if>
                <if test="c.bomNo != null and c.bomNo != ''">
                    and tb.bom_no like concat('%', #{c.bomNo}, '%')
                </if>
            </if>
        </where>