自动化立体仓库 - WMS系统
pang.jiabao
2024-10-07 0f769b47d8d71bd419ddf1733b0b2f21c82e86b1
跨巷道移库
14个文件已修改
444 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/entity/OrderDetl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/importexcle/ImportOrderDto.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/importexcle/ImportOrderListener.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/OrderDetlMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/OrderMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WorkService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/GhjtScheduler.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/GhjtHandler.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/OrderDetlMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/OrderMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/order/order.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderDetl.java
@@ -417,14 +417,14 @@
    }
    public String getDanger$(){
        if (null == this.danger){ return null; }
        if (null == this.danger){ return "待移库"; }
        switch (this.danger){
            case 1:
                return "是";
            case 0:
                return "否";
                return "移库中";
            case 2:
                return "移库完成";
            default:
                return String.valueOf(this.danger);
                return "待移库";
        }
    }
src/main/java/com/zy/asrs/importexcle/ImportOrderDto.java
@@ -11,10 +11,13 @@
@Data
public class ImportOrderDto {
    @ExcelProperty(value = "包装组号",index = 0)
    @ExcelProperty(value = "包装组号/源库位",index = 0)
    private String column1;
    @ExcelProperty(value = "出库单号/单据类型",index = 1)
    @ExcelProperty(value = "出库单号/单据类型/目标库位",index = 1)
    private String column2;
    @ExcelProperty(value = "目标巷道",index = 2)
    private String column3;
}
src/main/java/com/zy/asrs/importexcle/ImportOrderListener.java
@@ -37,6 +37,8 @@
    private long orderId;
    private long docTypeId;
    List<ImportOrderDto> list = new ArrayList<>();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
@@ -77,6 +79,7 @@
                throw new ExcelAnalysisException("单据类型错误:" + list.get(1).getColumn2());
            }
            orderNo = list.get(0).getColumn2();
            docTypeId = docType.getDocId();
            Order order = new Order();
            order.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
            order.setOrderNo(orderNo);
@@ -122,7 +125,17 @@
        List<OrderDetl> orderDetlList = new ArrayList<>();
        list.forEach(importOrderDto -> {
            OrderDetl orderDetl = new OrderDetl();
            orderDetl.setBrand(importOrderDto.getColumn1());
            if (docTypeId == 24) {
                orderDetl.setSpecs(String.format("%07d",Integer.parseInt(importOrderDto.getColumn1()))); // 源库位
                if (importOrderDto.getColumn2() != null) {
                    orderDetl.setModel(String.format("%07d",Integer.parseInt(importOrderDto.getColumn2()))); // 目标库位
                }
                if (importOrderDto.getColumn3() != null) {
                    orderDetl.setBeBatch(Integer.parseInt(importOrderDto.getColumn3())); //巷道
                }
            } else {
                orderDetl.setBrand(importOrderDto.getColumn1());
            }
            orderDetl.setBatch("");
            orderDetl.setOrderId(orderId);
            orderDetl.setOrderNo(orderNo);
src/main/java/com/zy/asrs/mapper/OrderDetlMapper.java
@@ -53,4 +53,10 @@
     * 按包装组号更新订单明细状态
     */
    void updateOrderDetlStatusByPackageNo(@Param("brands") List<String> brands,@Param("status") int status);
    /**
     * 查询跨巷道移库的未执行的库位
     */
    List<OrderDetl> selectMoveLocDetl();
}
src/main/java/com/zy/asrs/mapper/OrderMapper.java
@@ -23,6 +23,11 @@
    void updateSettleByOrderNo(@Param("orderNo") String orderNo, @Param("status") long status, @Param("userId") Long userId);
    /**
     * 更新单据状态,只有待处理才更新
     */
    void updatePendingSettleByOrderNo(@Param("orderNo") String orderNo, @Param("status") long status);
    /**
     * 查询出库申请单中没有备货的订单明细的包装组号
     */
    List<String> selectStockUpOrderDetl();
src/main/java/com/zy/asrs/service/WorkService.java
@@ -71,6 +71,11 @@
    void locMove(String sourceLocNo, String locNo, Long userId);
    /**
     * 跨巷道库位移转
     */
    void autoLocMove(String orderNo,String sourceLocNo, String locNo, Long userId);
    /**
     * 手动完成工作档
     */
    void completeWrkMast(String workNo, Long userId);
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -1341,6 +1341,8 @@
        wrkMast.setWrkSts(2L);
        if(wrkMast.getIoType() == 10){
            wrkMast.setBarcode(param.getBarcode());
        } else if(wrkMast.getIoType() == 12) { // 跨巷道转移入库
        }
        wrkMastService.updateById(wrkMast);
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -43,6 +43,19 @@
    // 库位排号分配默认类别
    private static final int DEFAULT_ROW_NO_TYPE = 1;
    // 堆垛机映射一楼的入库出库站点
    private static final Map<Integer,List<Integer>> crnMapNo;
    static {
        crnMapNo = new HashMap<>();
        crnMapNo.put(1,new ArrayList<Integer>(){{add(3001);add(3002);}});
        crnMapNo.put(2,new ArrayList<Integer>(){{add(3004);add(3003);}});
        crnMapNo.put(3,new ArrayList<Integer>(){{add(3005);add(3006);}});
        crnMapNo.put(4,new ArrayList<Integer>(){{add(3007);add(3008);}});
        crnMapNo.put(5,new ArrayList<Integer>(){{add(3010);add(3009);}});
        crnMapNo.put(6,new ArrayList<Integer>(){{add(3011);add(3012);}});
    }
    @Autowired
    private MatService matService;
    @Autowired
@@ -831,6 +844,95 @@
    @Override
    @Transactional
    public void autoLocMove(String orderNo,String sourceLocNo, String locNo, Long userId) {
        LocMast sourceLoc = locMastService.selectById(sourceLocNo);
        List<LocDetl> locDetls = locDetlService.selectList(new EntityWrapper<LocDetl>().eq("loc_no", sourceLocNo));
        if (Cools.isEmpty(sourceLoc)){
            throw new CoolException("未找到库位");
        }
        LocMast loc = locMastService.selectById(locNo);
        if (Cools.isEmpty(loc)){
            throw new CoolException("未找到库位");
        }
        if (!loc.getLocSts().equals("O") || (!sourceLoc.getLocSts().equals("F") && !sourceLoc.getLocSts().equals("D"))){
            throw new CoolException("库位状态已改变");
        }
//        if (!sourceLoc.getCrnNo().equals(loc.getCrnNo())) {
//            throw new CoolException("移转库位属于不同堆垛机");
//        }
        Date now = new Date();
        // 获取工作号
        int workNo = commonService.getWorkNo(WorkNoType.PICK.type);
        // 保存工作档
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(workNo);
        wrkMast.setIoTime(now);
        wrkMast.setWrkSts(11L); // 工作状态:11.生成出库ID
        wrkMast.setIoType(12); // 入出库状态: 11.库格移载
        wrkMast.setIoPri(10D);
        wrkMast.setCrnNo(sourceLoc.getCrnNo());
        wrkMast.setSourceLocNo(sourceLocNo); // 源库位
        wrkMast.setLocNo(locNo); // 目标库位
        wrkMast.setSourceStaNo(crnMapNo.get(sourceLoc.getCrnNo()).get(1)); // 出库源站点
        wrkMast.setStaNo(crnMapNo.get(loc.getCrnNo()).get(0)); // 入库目标站点
        wrkMast.setFullPlt(Cools.isEmpty(locDetls)?"N":"Y"); // 满板:Y
        wrkMast.setPicking("N"); // 拣料
        wrkMast.setExitMk("N"); // 退出
        wrkMast.setEmptyMk(sourceLoc.getLocSts().equals("D")?"Y":"N"); // 空板
        wrkMast.setBarcode(sourceLoc.getBarcode()); // 托盘码
        wrkMast.setLinkMis("N");
        wrkMast.setAppeUser(userId);
        wrkMast.setAppeTime(now);
        wrkMast.setModiUser(userId);
        wrkMast.setModiTime(now);
        boolean res = wrkMastService.insert(wrkMast);
        if (!res) {
            throw new CoolException("保存工作档失败");
        }
        // 工作档明细保存
        for (LocDetl locDetl : locDetls) {
            WrkDetl wrkDetl = new WrkDetl();
            wrkDetl.sync(locDetl);
            wrkDetl.setOrderNo(orderNo);
            wrkDetl.setWrkNo(workNo);
            wrkDetl.setIoTime(now);
            wrkDetl.setAnfme(locDetl.getAnfme());
            wrkDetl.setAppeTime(now);
            wrkDetl.setAppeUser(userId);
            wrkDetl.setModiTime(now);
            wrkDetl.setModiUser(userId);
            if (!wrkDetlService.insert(wrkDetl)) {
                throw new CoolException("保存工作档明细失败");
            }
        }
        // 修改源库位状态
        if (sourceLoc.getLocSts().equals("D") || sourceLoc.getLocSts().equals("F")) {
            sourceLoc.setLocSts("R"); // R.出库预约
            sourceLoc.setModiUser(userId);
            sourceLoc.setModiTime(now);
            if (!locMastService.updateById(sourceLoc)){
                throw new CoolException("更新源库位状态失败");
            }
        } else {
            throw new CoolException("源库位出库失败,状态:"+sourceLoc.getLocSts$());
        }
        // 修改目标库位状态
        if (loc.getLocSts().equals("O")) {
            loc.setLocSts("S"); // S.入库预约
            loc.setModiTime(now);
            loc.setModiUser(userId);
            if (!locMastService.updateById(loc)) {
                throw new CoolException("更新目标库位状态失败");
            }
        } else {
            throw new CoolException("移转失败,目标库位状态:"+loc.getLocSts$());
        }
    }
    @Override
    @Transactional
    public void completeWrkMast(String workNo, Long userId) {
        WrkMast wrkMast = wrkMastService.selectById(workNo);
        if (Cools.isEmpty(wrkMast)){
@@ -1019,7 +1121,7 @@
            } else if (wrkMast.getIoType() == 110) {
                locSts = "D";
            // 库位转移 ===>> D.空桶/空栈板
            } else if (wrkMast.getIoType() == 11) {
            } else if (wrkMast.getIoType() == 11 || wrkMast.getIoType() == 12) {
                locSts = wrkMast.getFullPlt().equalsIgnoreCase("N")?"D":"F";
                // 库位转移:目标库位
                LocMast locMast = locMastService.selectById(wrkMast.getLocNo());
@@ -1052,6 +1154,32 @@
            }
        }
        // 取消跨巷道移库任务时,回滚在执行的任务
        if (wrkMast.getIoType() == 12) {
            List<Order> orderList = orderMapper.selectList(new EntityWrapper<Order>().eq("doc_type", 24).lt("settle", 3));
            if (orderList.size() == 1) {
                Order order = orderList.get(0);
                // 回滚单据明细的移库状态
                List<OrderDetl> orderDetlList1 = orderDetlMapper.selectList(new EntityWrapper<OrderDetl>().eq("order_no", order.getOrderNo()).eq("specs", wrkMast.getSourceLocNo()));
                for (OrderDetl orderDetl:orderDetlList1) {
                    if (orderDetl.getBeBatch() != null) {
                        orderDetl.setModel("");
                    }
                    orderDetl.setUpdateTime(now);
                    orderDetl.setDanger(0);
                    orderDetlMapper.updateById(orderDetl);
                }
                // 回滚单据状态,有大于0的订单明细则是作业中,没有则更新为待处理
                Integer count = orderDetlMapper.selectCount(new EntityWrapper<OrderDetl>().eq("order_no", order.getOrderNo()).gt("danger", 0));
                if (count == 0) {
                    order.setSettle(1L);
                    orderMapper.updateById(order);
                }
            } else {
                log.error("回滚移库单据异常,无源库位数据:{}",wrkMast.getSourceLocNo());
            }
        }
        //取消出库工作档时,查询单据管理表,回滚作业中数量
        if(wrkMast.getIoType() == 101 || wrkMast.getIoType() == 103 || wrkMast.getIoType() == 107) {
            List<WrkDetl> wrkDetls = wrkDetlService.selectByWrkNo(wrkMast.getWrkNo());
src/main/java/com/zy/asrs/task/GhjtScheduler.java
@@ -1,7 +1,9 @@
package com.zy.asrs.task;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.zy.asrs.entity.OrderDetl;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.mapper.OrderDetlMapper;
import com.zy.asrs.mapper.OrderMapper;
import com.zy.asrs.mapper.WrkMastMapper;
import com.zy.asrs.task.handler.GhjtHandler;
@@ -35,6 +37,9 @@
    @Resource
    private ConfigMapper configMapper;
    @Resource
    private OrderDetlMapper orderDetlMapper;
    // 自动备货(根据出库单,把要出的货提前放到靠近出库口位置)
    // 定时任务获取待备货订单明细->获取堆垛机对应的源库位,获取备货区库位->生成移库任务11->
    // 执行移库任务12->入库完成4->更新工作档定时任务中更新订单备货状态和订单明细备货状态5->转储历史
@@ -59,7 +64,7 @@
    @Scheduled(cron = "0/2 * * * * ? ")
    public void ckrwPushGwcs() {
        // 查询状态为13的工作档
        List<WrkMast> wrkMasts = wrkMastMapper.selectList(new EntityWrapper<WrkMast>().in("io_type", 101,103,107,110,3).eq("wrk_sts", 13));
        List<WrkMast> wrkMasts = wrkMastMapper.selectList(new EntityWrapper<WrkMast>().in("io_type", 101,103,107,110,3,12).eq("wrk_sts", 13));
        for (WrkMast wrkMast : wrkMasts) {
            try {
                ghjtHandler.startCkrwPushGwcs(wrkMast);
@@ -69,6 +74,26 @@
        }
    }
    // 自动跨巷道移库
    // 配置开启->获取单据明细->获取源库位目标库位->生成移库任务->取消任务回滚单据->
    // wcs出库到堆垛机出库口->gwms给gwcs推送目标站1->gwcs到达堆垛机入库口请求入库->堆垛机执行入库->入库完成->更新单据状态
    // 11->12->13->1->2->3->4->5
    @Scheduled(cron = "0/10 * * * * ?")
    public synchronized void autoMoveLoc() {
        // 查询跨巷道移库配置
        Config config = configMapper.selectConfigByCode("auto_move_loc");
        if (config == null || config.getStatus() == 0) {
            return;
        }
        // 查询待移库的单据明细
        List<OrderDetl> orderDetlList = orderDetlMapper.selectMoveLocDetl();
        if (orderDetlList.isEmpty()) {
            return;
        }
        ghjtHandler.autoMoveLoc(orderDetlList);
    }
    // 空闲理货
    @Scheduled(cron = "0/5 * * * * ? ")
    public void autoTallyGoods() {
src/main/java/com/zy/asrs/task/handler/GhjtHandler.java
@@ -4,16 +4,17 @@
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.exception.CoolException;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.WrkDetl;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.*;
import com.zy.asrs.service.ApiLogService;
import com.zy.asrs.service.WorkService;
import com.zy.asrs.service.WrkDetlService;
import com.zy.asrs.utils.Utils;
import com.zy.common.constant.MesConstant;
import com.zy.common.properties.SlaveProperties;
import com.zy.common.utils.HttpHandler;
import com.zy.system.entity.Config;
import com.zy.system.mapper.ConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -56,6 +57,12 @@
    @Resource
    private WorkService workService;
    @Resource
    private ConfigMapper configMapper;
    @Autowired
    private SlaveProperties slaveProperties;
    @Transactional
    public void startCkrwPushGwcs(WrkMast wrkMast) {
@@ -92,6 +99,9 @@
                } else if (wrkMast.getIoType() == 3) {
                    // 修改工作主档状态
                    wrkMast.setWrkSts(15L);
                    wrkMast.setModiTime(new Date());
                } else if(wrkMast.getIoType() == 12) { // 跨巷道转移
                    wrkMast.setWrkSts(1L); // 状态改为1.生成入库id
                    wrkMast.setModiTime(new Date());
                }
                wrkMastMapper.updateById(wrkMast);
@@ -168,6 +178,7 @@
    /**
     * 自动备货处理
     */
    @Transactional
    public void autoStockUpHandler(List<String> list,int columnNum) {
        // 根据包装组号获取所在库位
@@ -257,4 +268,109 @@
        }
    }
    @Transactional
    public void autoMoveLoc(List<OrderDetl> orderDetlList) {
        // 判断是否已经有执行的移库任务
        Integer count = wrkMastMapper.selectCount(new EntityWrapper<WrkMast>().in("io_type", 11, 12));
        if (count > 0) {
            return;
        }
        OrderDetl detl = null; // 要移库的明细
        String staLoc = null; // 移库目标库位
        // 从待移库位列表中先浅后深获取一个待移库位
        Optional<OrderDetl> any = orderDetlList.stream().filter(orderDetl -> Utils.isShallowLoc(slaveProperties,orderDetl.getSpecs())).findAny();
        if (any.isPresent()){
            detl = any.get();
        }
        // 剩下的应该都是深库位,获取第一个
        if (detl == null) {
            detl = orderDetlList.get(0);
            // 对应浅库位有货,在堆垛机出库的时候会检测到,在那里生成移库任务
        }
        // 获取备货区配置
        Config config = configMapper.selectConfigByCode("auto_stock_up");
        if (config == null) {
            return;
        }
        // 备货取是前几列
        int bay1 = Integer.parseInt(config.getValue());
        // 指定的目标库位
        String model = detl.getModel();
        // 指定目标库位
        if (!Cools.isEmpty(model)) {
            // 目标库位是深库位
            if (Utils.isDeepLoc(slaveProperties,model)) {
                // 目标库位
                LocMast locMast2 = locMastMapper.selectById(model);
                if (locMast2 == null || !locMast2.getLocSts().equals("O")) {
                    log.error("指定的目标库位【{}】状不为空", model);
                    return;
                }
                // 获取到对应浅库位
                String shallowLoc = Utils.getShallowLoc(slaveProperties, model);
                LocMast locMast = locMastMapper.selectById(shallowLoc);
                // 浅库位有货
                if (locMast.getLocSts().equals("F") || locMast.getLocSts().equals("D")) {
                    log.error("选择的深库位【{}】,但是浅库位【{}】有货", model, locMast.getLocNo());
                    return;
                }
                // 避开备货区
                if (Utils.getBay(model) <= bay1) {
                    log.error("指定的目标库位【{}】在备货区,不能转移", model);
                    return;
                }
            }
            // 深库位检测通过,或者是浅库位
            staLoc = model;
        } else if ( detl.getBeBatch() != null) { // 指定目标巷道
            // 目标巷道
            Integer beBatch = detl.getBeBatch();
            // 从巷道里面先深后浅取一个空库位,不要占用备货区
            List<LocMast> locMasts = locMastMapper.selectList(new EntityWrapper<LocMast>().eq("crn_no", beBatch).eq("loc_sts", "O").gt("bay1", bay1));
            if (locMasts.isEmpty()) {
                log.error("指定巷道【{}】没有能移库的空库位了",beBatch);
                return;
            }
            // 先过滤出深库位
            Optional<LocMast> a = locMasts.stream().filter(locMast -> Utils.isDeepLoc(slaveProperties,locMast.getLocNo())).findAny();
            if (a.isPresent()){
                staLoc = a.get().getLocNo();
                // 获取到对应浅库位
                String shallowLoc = Utils.getShallowLoc(slaveProperties, staLoc);
                LocMast locMast = locMastMapper.selectById(shallowLoc);
                // 浅库位有货
                if (locMast.getLocSts().equals("F") || locMast.getLocSts().equals("D")) {
                    log.error("指定的目标巷道深库位【{}】,但是浅库位【{}】有货",staLoc,locMast.getLocNo());
                    return;
                }
            }
            // 深库位没有则取浅库位
            if (staLoc == null) {
                staLoc = locMasts.get(0).getLocNo();
            }
        } else {
            log.error("目标库位或目标巷道有误:{}",detl);
            return;
        }
        // 生成跨巷道移库任务
        workService.autoLocMove(detl.getOrderNo(),detl.getSpecs(),staLoc,29L);
        // 更新单据明细移库状态为移库中
        detl.setDanger(1);
        if (detl.getBeBatch() != null) {
            detl.setModel(staLoc); // 补充目标库位
        }
        orderDetlMapper.updateById(detl);
        // 更新单据状态为2处理中
        orderMapper.updatePendingSettleByOrderNo(detl.getOrderNo(),2L);
    }
}
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -17,6 +17,7 @@
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -364,6 +365,94 @@
                        return FAIL.setMsg("库位移转 ===>> 修改目标库位状态失败; [workNo=" + wrkMast.getWrkNo() + "],[locNo=" + wrkMast.getLocNo() + "]");
                    }
                    break;
                // 跨巷道库位移转完成
                case 12:
                    // 默认目标库位是空板
                    String locSts2 = "D";
                    // 库位移转判断是否为空板移转
                    if (wrkMast.getEmptyMk().equals("N")) {
                        locSts2 = "F";
                        // 转移库存明细数据: 库存号 由工作档源库位变为目标库位
                        if (!locDetlService.updateLocNo(wrkMast.getLocNo(), wrkMast.getSourceLocNo())) {
//                            exceptionHandle("库位移转 ===>> 转移库存明细数据失败;[源库位={0}],[目标库位={1}]", wrkMast.getSourceLocNo(), wrkMast.getLocNo());
                            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                            return FAIL.setMsg("库位移转 ===>> 转移库存明细数据失败; [workNo=" + wrkMast.getWrkNo() + "],[locNo=" + wrkMast.getLocNo() + "]");
                        }
                    }
                    // 跨巷道移库,更新订单明细及订单状态
                    List<WrkDetl> wrkDetlList2 = wrkDetlService.selectList(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()));
                    if (!wrkDetlList2.isEmpty()) {
                        // 判断有无单据编号
                        Optional<WrkDetl> any = wrkDetlList2.stream().filter(wrkDetl -> wrkDetl.getOrderNo() != null).findAny();
                        if (any.isPresent()) {
                            String orderNo = any.get().getOrderNo();
                            // 根据单据编号和库位号查询单据明细
                            List<OrderDetl> orderDetlList = orderDetlMapper.selectList(new EntityWrapper<OrderDetl>().in("order_no",orderNo).eq("specs",wrkMast.getSourceLocNo()));
                            // 更新单据明细的移库状态
                            for(OrderDetl orderDetl: orderDetlList) {
                                orderDetl.setDanger(2);
                                orderDetl.setUpdateTime(now);
                                orderDetlMapper.updateById(orderDetl);
                            }
                            // 判断单据明细是不是全部移库完成
                            Integer count = orderDetlMapper.selectCount(new EntityWrapper<OrderDetl>().lt("danger", 2).eq("order_no", orderNo));
                            if (count == 0) {
                                // 更新单据为已完成
                                orderMapper.updateSettleByOrderNo(orderNo,4,null);
                            }
                        }
                    } else { // 空板转移没有工作明细,但是要更新单据明细状态
                        // 只有一条作业中的移库单据
                        List<Order> orderList = orderMapper.selectList(new EntityWrapper<Order>().eq("doc_type", 24).le("settle", 3));
                        if(orderList.isEmpty()) {
                            log.error("没有获取到正在执行中的移库单据");
                        } else {
                            Order order = orderList.get(0);
                            // 根据单据编号和库位号查询单据明细
                            List<OrderDetl> orderDetlList = orderDetlMapper.selectList(new EntityWrapper<OrderDetl>().in("order_no",order.getOrderNo()).eq("specs",wrkMast.getSourceLocNo()));
                            // 更新单据明细的移库状态
                            for(OrderDetl orderDetl: orderDetlList) {
                                orderDetl.setDanger(2);
                                orderDetl.setUpdateTime(now);
                                orderDetlMapper.updateById(orderDetl);
                            }
                            // 判断单据明细是不是全部移库完成
                            Integer count = orderDetlMapper.selectCount(new EntityWrapper<OrderDetl>().lt("danger", 2).eq("order_no", order.getOrderNo()));
                            if (count == 0) {
                                // 更新单据为已完成
                                orderMapper.updateSettleByOrderNo(order.getOrderNo(),4,null);
                            }
                        }
                    }
                    // 修改源库位状态 ==> O
                    LocMast sourceLoc2 = locMastService.selectById(wrkMast.getSourceLocNo());
                    if (null != sourceLoc2) {
                        sourceLoc2.setBarcode("");
                        sourceLoc2.setLocSts("O");
                        sourceLoc2.setSheetNo("0");
                        sourceLoc2.setModiTime(now);
                        sourceLoc2.setIoTime(now);
                        if (!locMastService.updateById(sourceLoc2)) {
//                            exceptionHandle("库位移转 ===>> 修改源库位状态失败;[workNo={0}],[sourceLoc={1}]", wrkMast.getWrkNo(), wrkMast.getSourceLocNo());
                            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                            return FAIL.setMsg("库位移转 ===>> 修改源库位状态失败; [workNo=" + wrkMast.getWrkNo() + "],[locNo=" + wrkMast.getLocNo() + "]");
                        }
                    }
                    // 修改目标库位状态 ==> .locSts
                    locMast.setLocSts(locSts2);
                    locMast.setBarcode(wrkMast.getBarcode());
                    locMast.setSheetNo("0");
                    locMast.setIoTime(now);
                    locMast.setModiTime(now);
                    if (!locMastService.updateById(locMast)) {
//                        exceptionHandle("库位移转 ===>> 修改目标库位状态失败;[workNo={0}],[locNo={1}]", wrkMast.getWrkNo(), wrkMast.getLocNo());
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                        return FAIL.setMsg("库位移转 ===>> 修改目标库位状态失败; [workNo=" + wrkMast.getWrkNo() + "],[locNo=" + wrkMast.getLocNo() + "]");
                    }
                    break;
                default:
                    break;
            }
src/main/resources/mapper/OrderDetlMapper.xml
@@ -126,6 +126,18 @@
        and mdt.pakout = 1
        <include refid="pakOutPageCondition"></include>
    </select>
    <select id="selectMoveLocDetl" resultMap="BaseResultMap">
        select
        md.*
        from
        man_order mo
        left join man_order_detl md on
        mo.order_no = md.order_no
        where
        mo.doc_type = 24
        and mo.settle &lt;= 2
        and md.danger = 0
    </select>
    <update id="increase">
        update man_order_detl
@@ -171,7 +183,7 @@
    </insert>
    <insert id="batchDetls">
        INSERT INTO
        man_order_detl(brand,batch,order_id,order_no,create_by,create_time,status,qty,anfme)
        man_order_detl(brand,batch,order_id,order_no,specs,model,be_batch,create_by,create_time,status,qty,anfme)
        VALUES
        <foreach collection="list" item="item" index="index" separator=",">
            (
@@ -179,6 +191,9 @@
            #{item.batch},
            #{item.orderId},
            #{item.orderNo},
             #{item.specs},
             #{item.model},
             #{item.beBatch},
            #{item.createBy},
            #{item.createTime},
            #{item.status},
src/main/resources/mapper/OrderMapper.xml
@@ -66,6 +66,9 @@
    <update id="updateStatusByOrderNo">
        update man_order set account_day = #{status},update_time = getdate(),update_by = #{userId} where order_no = #{orderNo}
    </update>
    <update id="updatePendingSettleByOrderNo">
        update man_order set settle = #{status},update_time = getdate() where order_no = #{orderNo} and settle = 1
    </update>
    <select id="selectComplete" resultMap="BaseResultMap">
        select
src/main/webapp/static/js/order/order.js
@@ -261,6 +261,10 @@
                            // {field: 'color', title: '颜色'},
                            // {field: 'specs', title: '接头'},
                            {field: 'qty$', title: '出库状态', style: 'font-weight: bold'},
                            {field: 'specs', title: '源库位', style: 'font-weight: bold'},
                            {field: 'model', title: '目标库位', style: 'font-weight: bold'},
                            {field: 'beBatch', title: '跨巷道', style: 'font-weight: bold'},
                            {field: 'danger$', title: '移库状态', style: 'font-weight: bold'},
                            {field: 'inspect$', title: '备货状态', style: 'font-weight: bold'}
                        ]],
                        request: {