自动化立体仓库 - WMS系统
zyx
2023-08-01 8359572be2164aec32219fafd5e72f4035067dff
agv 订单出库功能
12个文件已修改
660 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/OutController.java 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/LocDetl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/AgvLocDetlService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/AgvWorkService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/LocDetlService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/AgvLocDetlServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/AgvWorkServiceImpl.java 216 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/LocDetlServiceImpl.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/LocDto.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/TaskDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/order/out.js 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/mat/mat.html 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OutController.java
@@ -3,9 +3,9 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.annotations.ManagerAuth;
import com.core.common.BaseRes;
import com.core.common.Cools;
import com.core.common.R;
import com.core.exception.CoolException;
import com.zy.asrs.entity.*;
import com.zy.asrs.service.*;
import com.zy.common.model.LocDto;
@@ -17,7 +17,9 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -43,6 +45,15 @@
    private BasDevpService basDevpService;
    @Autowired
    private MatService matService;
    @Autowired
    private AgvLocMastService agvLocMastService;
    @Autowired
    private AgvBasDevpService agvBasDevpService;
    @Autowired
    private AgvWorkService agvWorkService;
    @Autowired
    private AgvLocDetlService agvLocDetlService;
    @PostMapping("/out/pakout/orderDetlIds/auth")
    @ManagerAuth
@@ -51,9 +62,34 @@
        return R.ok().add(orderDetlService.selectByOrderId(orderId).stream().map(OrderDetl::getId).distinct().collect(Collectors.toList()));
    }
    //出库
    @PostMapping("/out/pakout/preview/auth")
    @ManagerAuth
    public R pakoutPreview(@RequestBody List<Long> ids) {
        List<OrderDetl> orderDetlList = orderDetlService.selectBatchIds(ids);
        //返回给前端的库位以及站点信息
        List<LocDto> locDtoList = new ArrayList<>();
        for (OrderDetl orderDetl : orderDetlList) {
            double issued = Optional.of(orderDetl.getAnfme() - orderDetl.getQty()).orElse(0.0D);
            if (issued <= 0.0D) { continue; }
            //先找AGV的库存,如果返回的issued大于0,则去四项库找
            issued = agvLocDetlService.queryStockAndSetLocDto(orderDetl.getMatnr(),orderDetl.getBatch(),orderDetl.getOrderNo(),locDtoList,issued);
            //从四项库的库存里面找,
            issued = locDetlService.queryStockAndSetLocDto(orderDetl.getMatnr(),orderDetl.getBatch(),orderDetl.getOrderNo(),locDtoList,issued);
            if (issued > 0) {
                LocDto locDto = new LocDto(null, orderDetl.getMatnr(), orderDetl.getMaktx(), orderDetl.getBatch(), orderDetl.getOrderNo(), issued);
                locDto.setLack(Boolean.TRUE);
                locDtoList.add(locDto);
            }
        }
        return R.ok().add(locDtoList);
        /*
        if (Cools.isEmpty(ids)) {
            return R.parse(BaseRes.PARAM);
        }
@@ -90,13 +126,54 @@
                locDtos.add(locDto);
            }
        }
        return R.ok().add(locDtos);
        return R.ok().add(locDtos); */
    }
    @PostMapping("/out/pakout/auth")
    @ManagerAuth(memo = "订单出库")
    @Transactional
    public synchronized R pakout(@RequestBody List<LocDto> locDtos) throws InterruptedException {
        //判断是否可以生成出库工作档
        boolean lack = true;
        for (LocDto locDto : locDtos) {
            //如果AGV出库站为空 和 四项库出库站为空 并且 有库存的情况下 则返回
            if (Cools.isEmpty(locDto.getStaNo()) && Cools.isEmpty(locDto.getAgvStaNo()) &&!locDto.isLack()) {
                return R.error(locDto.getLocNo()+"库位请选择出库站");
            }
        }
        //如果所有库都没有库存,则返回
        for (LocDto locDto : locDtos) {
            if (!locDto.isLack()) {
                lack = false;
                break;
            }
        }
        if (lack) {
            return R.error("库存不足");
        }
        Thread.sleep(500L);
        // 订单预校验  ===>> 1.订单状态; 2.订单带出数量
        List<OrderDto> orderDtos = orderPreVerification(locDtos);
        List<TaskDto> taskDtos = new ArrayList<>();
        List<TaskDto> agvTaskDtos = new ArrayList<>();
        generateTaskDto(locDtos,taskDtos,agvTaskDtos);
        //生成AGV出库任务
        agvWorkService.stockOutWrkMast(agvTaskDtos, getUserId());
        // 生成出库任务
        List<String> excludeLocNos = taskDtos.stream().map(TaskDto::getLocNo).distinct().collect(Collectors.toList());
        for (TaskDto taskDto : taskDtos) {
            BasDevp staNo = basDevpService.checkSiteStatus(taskDto.getStaNo());
            workService.stockOut(staNo, taskDto, getUserId());
            locMastService.breakUp(taskDto.getLocNo(), excludeLocNos);
        }
        return R.ok();
        /*
        if (Cools.isEmpty(locDtos)) {
            return R.parse(BaseRes.PARAM);
        }
@@ -184,7 +261,7 @@
            workService.stockOut(staNo, taskDto, getUserId());
            locMastService.breakUp(taskDto.getLocNo(), excludeLocNos);
        }
        return R.ok();
        return R.ok();*/
    }
@@ -200,6 +277,31 @@
    @PostMapping("/out/pakout/preview/merge/auth")
    @ManagerAuth
    public R pakoutPreviewMerge(@RequestBody List<OrderMergeVo> list) {
        //返回给前端的库位以及站点信息
        List<LocDto> locDtoList = new ArrayList<>();
        for (OrderMergeVo vo : list) {
            double issued = Optional.of(vo.getAnfme()).orElse(0.0D);
            if (issued <= 0.0D) {
                continue;
            }
            //先找AGV的库存,如果返回的issued大于0,则去四项库找
            issued = agvLocDetlService.queryStockAndSetLocDto(vo.getMatnr(),vo.getBatch(),null,locDtoList,issued);
            //从四项库的库存里面找,
            issued = locDetlService.queryStockAndSetLocDto(vo.getMatnr(),vo.getBatch(),null,locDtoList,issued);
            if (issued > 0) {
                LocDto locDto = new LocDto(null, vo.getMatnr(), vo.getMaktx(), vo.getBatch(), null, issued);
                locDto.setLack(Boolean.TRUE);
                locDtoList.add(locDto);
            }
        }
        return R.ok().add(locDtoList);
        /*
        if (Cools.isEmpty(list)) {
            return R.parse(BaseRes.PARAM);
        }
@@ -236,7 +338,111 @@
            assert mat != null;
            locDto.setSpecs(mat.getSpecs());
        }
        return R.ok().add(locDtos);
        return R.ok().add(locDtos);*/
    }
    /*
    生成出库任务dto
     */
    private void generateTaskDto(List<LocDto> locDtos, List<TaskDto> taskDtos, List<TaskDto> agvTaskDtos){
        // 根据 (库位 & 出库站) 分组; 理想状态:一组为一次出库任务
        for (LocDto locDto : locDtos) {
            if (locDto.isLack()) { continue; }
            //AGV库
            if(!Cools.isEmpty(locDto.getAgvStaNos())){
                generateTaskDtoForAgv(locDto,agvTaskDtos);
                //四项库
            }else{
                generateTaskDtoForBase(locDto,taskDtos);
            }
        }
    }
    /*
    生成AGV的任务dto
     */
    private void generateTaskDtoForAgv(LocDto locDto, List<TaskDto> agvTaskDtos){
        // 防止前端页面提取库位信息后,在其他地方对该库位生成了出库任务(库位状态非F状态)
        AgvLocMast agvLocMast = agvLocMastService.selectById(locDto.getLocNo());
        if(!Cools.isEmpty(agvLocMast) && !agvLocMast.getLocSts().equals("F")){
            throw new CoolException("库位号非在库状态,请重新选择出库库位===>>" + locDto.getLocNo());
        }
        AgvBasDevp agvBasDevp = agvBasDevpService.selectOne(new EntityWrapper<AgvBasDevp>()
                .eq("station_code", locDto.getAgvStaNo())
                .eq("loc_sts", "O"));
        if(Cools.isEmpty(agvBasDevp)){
            //TODO 假如出库站点不足,记录未出库的库位;
            return;
        }
        TaskDto taskDto = new TaskDto(locDto.getLocNo(), agvBasDevp.getDevNo(), locDto);
        //TODO 暂不考虑库位混载状态(后续看情况是否需要判断)
        agvTaskDtos.add(taskDto);
    }
    /*
    生成四项库的任务dto
     */
    private void generateTaskDtoForBase(LocDto locDto, List<TaskDto> taskDtos){
        // 防止前端页面提取库位信息后,在其他地方对该库位生成了出库任务(库位状态非F状态)
        LocMast locMast = locMastService.selectById(locDto.getLocNo());
        if(!Cools.isEmpty(locMast) && !locMast.getLocSts().equals("F")){
            throw new CoolException("库位号非在库状态,请重新选择出库库位===>>" + locDto.getLocNo());
        }
        TaskDto taskDto = new TaskDto(locDto.getLocNo(), locDto.getStaNo(), locDto);
        //如果库位存在混载
        if (TaskDto.has(taskDtos, taskDto)) {
            TaskDto dto = TaskDto.find(taskDtos, taskDto);
            assert dto != null;
            dto.getLocDtos().addAll(taskDto.getLocDtos());
        } else {
            taskDtos.add(taskDto);
        }
    }
    /*
    订单预校验  ===>> 1.订单状态; 2.订单带出数量
     */
    private List<OrderDto> orderPreVerification(List<LocDto> locDtos){
        List<OrderDto> orderDtos = new ArrayList<>();
        for (LocDto locDto : locDtos) {
            if (!isJSON(locDto.getOrderNo())) {
            //if (!Cools.isEmpty(locDto.getOrderNo())) {
                if (Cools.isEmpty(locDto.getOrderNo())) { continue; }
                OrderDto orderDto = new OrderDto(locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch(), locDto.getAnfme());
                if (OrderDto.has(orderDtos, orderDto)) {
                    OrderDto dto = OrderDto.find(orderDtos, orderDto);
                    assert dto != null;
                    dto.setAnfme(dto.getAnfme() + orderDto.getAnfme());
                } else {
                    orderDtos.add(orderDto);
                }
            } else {
                // 订单合并出库
                List<OrderDto> orderDtoList = JSON.parseArray(locDto.getOrderNo(), OrderDto.class);
                for (OrderDto one : orderDtoList) {
                    OrderDto orderDto = new OrderDto(one.getOrderNo(), locDto.getMatnr(), locDto.getBatch(), one.getAnfme());
                    if (OrderDto.has(orderDtos, orderDto)) {
                        OrderDto dto = OrderDto.find(orderDtos, orderDto);
                        assert dto != null;
                        dto.setAnfme(dto.getAnfme() + orderDto.getAnfme());
                    } else {
                        orderDtos.add(orderDto);
                    }
                }
            }
        }
        for (OrderDto orderDto : orderDtos) {
            Order order = orderService.selectByNo(orderDto.getOrderNo());
            if (order.getSettle() > 2) {
                throw new CoolException(orderDto.getOrderNo() + "订单已失效,请及时刷新页面");
                //return R.error(orderDto.getOrderNo() + "订单已失效,请及时刷新页面");
            }
        }
        return orderDtos;
    }
}
src/main/java/com/zy/asrs/entity/LocDetl.java
@@ -39,8 +39,8 @@
    @ExcelProperty("物料号")
    private String matnr;
    @ApiModelProperty(value= "物料号")
    @ExcelProperty("物料号")
    @ApiModelProperty(value= "物料名称")
    @ExcelProperty("物料名称")
    private String maktx;
    @ApiModelProperty(value= "序列码")
src/main/java/com/zy/asrs/service/AgvLocDetlService.java
@@ -2,9 +2,14 @@
import com.baomidou.mybatisplus.service.IService;
import com.zy.asrs.entity.AgvLocDetl;
import com.zy.common.model.LocDto;
import java.util.List;
public interface AgvLocDetlService extends IService<AgvLocDetl> {
    public void addLocDetlInfo(String locNo, int taskCode);
    public double queryStockAndSetLocDto(String matnr, String batch, String orderNo, List<LocDto> locDtoList, double issued);
}
src/main/java/com/zy/asrs/service/AgvWorkService.java
@@ -2,16 +2,21 @@
import com.zy.asrs.entity.AgvBasDevp;
import com.zy.common.model.StartupDto;
import com.zy.common.model.TaskDto;
import java.util.List;
public interface AgvWorkService {
    /**
     * 通知档手动生成任务
    /*
     通知档手动生成任务
     */
    StartupDto createWaitPainWrkMastStart(List<AgvBasDevp> agvBasDevpList, Long userId);
    /*
    生成出库任务
     */
    void stockOutWrkMast(List<TaskDto> agvTaskDtos, Long userId);
}
src/main/java/com/zy/asrs/service/LocDetlService.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.service.IService;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.result.StockVo;
import com.zy.common.model.LocDto;
import java.util.List;
import java.util.Set;
@@ -51,6 +52,8 @@
    List<LocDetl> queryStock(String matnr, String batch, String orderNo, Set<String> locNos);
    double queryStockAndSetLocDto(String matnr, String batch, String orderNo, List<LocDto> locDtoList, double issued);
    Double queryStockAnfme(String matnr, String batch);
    List<StockVo> queryStockTotal();
src/main/java/com/zy/asrs/service/impl/AgvLocDetlServiceImpl.java
@@ -1,16 +1,23 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.core.common.Cools;
import com.zy.asrs.entity.AgvLocDetl;
import com.zy.asrs.entity.AgvLocMast;
import com.zy.asrs.entity.AgvWrkDetl;
import com.zy.asrs.mapper.AgvLocDetlMapper;
import com.zy.asrs.service.AgvBasDevpService;
import com.zy.asrs.service.AgvLocDetlService;
import com.zy.asrs.service.AgvLocMastService;
import com.zy.asrs.service.AgvWrkDetlService;
import com.zy.common.model.LocDto;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -19,6 +26,10 @@
    @Autowired
    AgvWrkDetlService agvWrkDetlService;
    @Autowired
    AgvBasDevpService agvBasDevpService;
    @Autowired
    AgvLocMastService agvLocMastService;
    public void addLocDetlInfo(String locNo, int taskCode) {
        List<AgvWrkDetl> agvWrkDetls = agvWrkDetlService.selectList(new EntityWrapper<AgvWrkDetl>().eq("wrk_no", taskCode));
@@ -31,4 +42,55 @@
        }).collect(Collectors.toList());
    }
    public double queryStockAndSetLocDto(String matnr, String batch, String orderNo, List<LocDto> locDtoList, double issued) {
        //根据物料号和批次找到对应的库存,并且按照修改时间排序
        Wrapper<AgvLocDetl> wrapper = new EntityWrapper<AgvLocDetl>().eq("matnr", matnr).orderBy("modi_time");
        if(Cools.isEmpty(batch)){
            wrapper.isNull("batch");
        }else {
            wrapper.eq("batch",batch);
        }
        List<AgvLocDetl> agvLocDetls = this.selectList(wrapper);
        for (AgvLocDetl agvLocDetl: agvLocDetls) {
            //判断当前库位货物是否F在库
            AgvLocMast agvLocMast = agvLocMastService.selectById(agvLocDetl.getLocNo());
            if(!"F".equals(agvLocMast.getLocSts())){
                continue;
            }
            //如果订单剩余出库量大于0
            if(issued > 0) {
                //当前库位的库存量
                double anfme = agvLocDetl.getAnfme();
                //int ioType = anfme > issued ? 101 : 103;
                anfme = anfme > issued ? issued : anfme;
                LocDto locDto = new LocDto(agvLocDetl.getLocNo(), agvLocDetl.getMatnr(), agvLocDetl.getMaktx(), agvLocDetl.getBatch(), orderNo, anfme);
                //当前库位所处楼层
                int floor = Integer.parseInt(agvLocDetl.getLocNo().split("@")[1]);
                locDto.setAgvStaNos(queryAgvStaNosByFloor(floor));
                locDtoList.add(locDto);
                issued -= anfme;
            }
        }
        return issued;
    }
    private List<String> queryAgvStaNosByFloor(int floor){
        List<String> agvStaNos = new ArrayList<>();
        if(floor == 1){
            agvStaNos.add("CS-101");
            agvStaNos.add("CS-102");
        }else if(floor ==3){
            agvStaNos.add("CS-305");
            agvStaNos.add("CS-306");
            agvStaNos.add("CS-307");
        }
        return agvStaNos;
    }
}
src/main/java/com/zy/asrs/service/impl/AgvWorkServiceImpl.java
@@ -1,12 +1,18 @@
package com.zy.asrs.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.exception.CoolException;
import com.zy.asrs.entity.*;
import com.zy.asrs.service.*;
import com.zy.common.model.LocDto;
import com.zy.common.model.OrderDto;
import com.zy.common.model.StartupDto;
import com.zy.common.model.TaskDto;
import com.zy.common.model.enums.WorkNoType;
import com.zy.common.service.AgvCommonService;
import com.zy.common.web.BaseController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -34,6 +40,8 @@
    @Autowired
    private AgvLocMastService agvLockMastService;
    @Autowired
    private AgvLocDetlService agvLocDetlService;
    @Autowired
    private AgvCommonService agvCommonService;
    @Autowired
    private AgvWrkMastService agvWrkMastService;
@@ -41,8 +49,16 @@
    private AgvWrkDetlService agvWrkDetlService;
    @Autowired
    private MatService matService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderDetlService orderDetlService;
    @Override
    /*
    入库
     */
    @Transactional
    public StartupDto createWaitPainWrkMastStart(List<AgvBasDevp> agvBasDevpList, Long userId) {
        Date now = new Date();
@@ -61,55 +77,107 @@
            //检索库位,选择合适的库位
            AgvLocMast agvLocMast = agvCommonService.getLocNo(agvWaitPakinList, agvBasDevp.getFloor());
            //生成工作档
            AgvWrkMast wrkMast = createWrkMast(agvBasDevp, agvLocMast, now, userId);
            //AgvWrkMast wrkMast = createWrkMast(agvBasDevp, agvLocMast, now, userId);
            AgvWrkMast wrkMast = createWrkMast(1,201L,agvBasDevp.getDevNo(),agvLocMast.getLocNo(),agvBasDevp.getBarcode(),now,userId);
            //生成工作档明细
            createWrkDetlReWrite(agvWaitPakinList,wrkMast,userId);
            //createWrkDetlReWrite(agvWaitPakinList,wrkMast,userId);
            agvWaitPakinList.forEach(wp -> {
                createWrkDetlReWrite(wp.getMatnr(),wrkMast.getWrkNo(),wp.getOrderNo(),wp.getBatch(),wp.getAnfme(),wp.getZpallet(),now,userId);
            });
            //更新源站点信息
            updateAgvBasDevp(agvBasDevp);
            updateAgvBasDevp(agvBasDevp,"R");
            //更新目标库位状态
            updateAgvLocMast(agvLocMast);
            updateAgvLocMast(agvLocMast,"S");
        });
        //TODO
        return null;
    }
    /*
    订单出库
     */
    @Transactional
    public void stockOutWrkMast(List<TaskDto> agvTaskDtos, Long userId) {
        Date now = new Date();
        agvTaskDtos.forEach(taskDto -> {
            AgvLocMast agvLocMast = agvLockMastService.selectById(taskDto.getLocNo());
            //工作档所需参数
            double anfme = taskDto.getLocDtos().get(0).getAnfme();
            long wrkSts = 21L;
            String sourceLocNo = taskDto.getLocNo();
            String targetLocNo = taskDto.getAgvStaNo();
            String barcode = agvLocMast.getBarcode();
            //明细档所需参数
            String mantr = taskDto.getLocDtos().get(0).getMatnr();
            String orderNo = taskDto.getLocDtos().get(0).getOrderNo();
            String batch = taskDto.getLocDtos().get(0).getBatch();
            //判断是否全板出库
            int ioType = isPakOut(sourceLocNo,anfme) ?  101 : 103;
            //生成工作档
            AgvWrkMast wrkMast = createWrkMast(ioType,wrkSts,sourceLocNo,targetLocNo,barcode,now,userId);
            //生成工作档明细
            createWrkDetlReWrite(mantr,wrkMast.getWrkNo(),orderNo,batch,anfme,barcode,now,userId);
            //修改订单信息
            modifyOrderDetl(taskDto.getLocDtos().get(0), userId);
            //更新源站点信息
            updateAgvLocMast(agvLockMastService.selectById(sourceLocNo),"R");
            //更新目标站点状态
            updateAgvBasDevp(agvBasDevpService.selectById(targetLocNo),"S");
        });
    }
    /*
    更新目标库位信息
     */
    private void updateAgvLocMast(AgvLocMast locMast){
        locMast.setLocSts("S");
    private void updateAgvLocMast(AgvLocMast locMast, String locSts){
        locMast.setLocSts(locSts);
        agvLockMastService.updateById(locMast);
    }
    /*
    更新源站点信息
     */
    private void updateAgvBasDevp(AgvBasDevp agvBasDevp){
        agvBasDevp.setLocSts("R");
    private void updateAgvBasDevp(AgvBasDevp agvBasDevp, String locSts){
        agvBasDevp.setLocSts(locSts);
        agvBasDevpService.updateById(agvBasDevp);
    }
    /*
    生成工作档明细
     */
  /*  private void createWrkDetl(List<AgvWaitPakin> agvWaitPakinList, AgvWrkMast wrkMast, Long userId){
        List<DetlDto> detlDtos = new ArrayList<>();
        agvWaitPakinList.forEach(agvWaitPakin -> {
            DetlDto detlDto = new DetlDto(agvWaitPakin.getMatnr(), agvWaitPakin.getBatch(), agvWaitPakin.getAnfme());
            if (DetlDto.has(detlDtos, detlDto)) {
                DetlDto detlDto1 = DetlDto.find(detlDtos, detlDto.getMatnr(), detlDto.getBatch());
                assert detlDto1 != null;
                detlDto1.setAnfme(detlDto1.getAnfme() + detlDto.getAnfme());
            } else {
                detlDtos.add(detlDto);
            }
        });
        agvWrkDetlService.createWorkDetail(wrkMast.getWrkNo(), detlDtos, wrkMast.getBarcode(), userId);
    }*/
    private void createWrkDetlReWrite(String matnr, int wrkNo, String orderNo,String batch, double anfme, String zpallet, Date now, Long userId){
        Mat mat = matService.selectByMatnr(matnr);
        if (Cools.isEmpty(mat)) {
            throw new CoolException(matnr + "商品维护失败");
        }
        AgvWrkDetl wrkDetl = new AgvWrkDetl();
        wrkDetl.sync(mat);
        wrkDetl.setWrkNo(wrkNo);
        wrkDetl.setOrderNo(orderNo);
        wrkDetl.setIoTime(now);
        wrkDetl.setBatch(batch);
        wrkDetl.setAnfme(anfme); // 数量
        wrkDetl.setZpallet(zpallet); // 托盘条码
        wrkDetl.setAppeUser(userId);
        wrkDetl.setAppeTime(now);
        wrkDetl.setModiUser(userId);
        wrkDetl.setModiTime(now);
        if (!agvWrkDetlService.insert(wrkDetl)) {
            throw new CoolException("保存工作明细失败");
        }
    }
    /*
    生成工作档明细
     */
    @Deprecated
    private void createWrkDetlReWrite(List<AgvWaitPakin> agvWaitPakinList, AgvWrkMast wrkMast, Long userId){
        Date now = new Date();
        agvWaitPakinList.stream().forEach(agvWaitPakin -> {
@@ -138,23 +206,71 @@
    /*
    生成工作档
     */
    private AgvWrkMast createWrkMast(int ioType, long wrkSts, String sourceLocNo, String locNo, String barcode,  Date now, Long userId){
        AgvWrkMast wrkMast = new AgvWrkMast();
        //生成工作号
        int workNo = agvCommonService.getWorkNo(WorkNoType.getWorkNoType(ioType));
        wrkMast.setWrkNo(workNo);
        //工作状态
        wrkMast.setWrkSts(wrkSts);
        //入出库类型
        wrkMast.setIoType(ioType);
        wrkMast.setIoTime(now);
        //优先级
        wrkMast.setIoPri(300.0);
        //源站点
        wrkMast.setSourceLocNo(sourceLocNo);
        //目标站点
        wrkMast.setLocNo(locNo);
        //容器编码
        wrkMast.setBarcode(barcode);
        // 满板:Y
        //wrkMast.setFullPlt("Y");
        // 拣料
        //wrkMast.setPicking("N");
        // 退出
        //wrkMast.setExitMk("N");
        // 空板
        //wrkMast.setEmptyMk("N");
        //wrkMast.setLinkMis("N");
        wrkMast.setAppeUser(userId);
        wrkMast.setAppeTime(now);
        wrkMast.setModiUser(userId);
        wrkMast.setModiTime(now);
        if (!agvWrkMastService.insert(wrkMast)) {
            throw new CoolException("保存工作档失败");
        }
        return wrkMast;
    }
    /*
    生成工作档
     */
    @Deprecated
    private AgvWrkMast createWrkMast(AgvBasDevp agvBasDevp,AgvLocMast agvLocMast, Date now, Long userId){
        AgvWrkMast wrkMast = new AgvWrkMast();
        //生成工作号
        int workNo = agvCommonService.getWorkNo(0);
        wrkMast.setWrkNo(workNo);
        wrkMast.setIoTime(new Date());
        wrkMast.setWrkSts(201L); // 工作状态:201.生成入库任务ID
        wrkMast.setIoType(1); // 入出库状态:1.入库
        // 工作状态:201.生成入库任务ID
        wrkMast.setWrkSts(201L);
        // 入出库状态:1.入库
        wrkMast.setIoType(1);
        //生成优先级
        wrkMast.setIoPri(300.0);
        wrkMast.setSourceLocNo(agvBasDevp.getDevNo());
        wrkMast.setLocNo(agvLocMast.getLocNo());
        wrkMast.setBarcode(agvBasDevp.getBarcode());
        wrkMast.setFullPlt("Y"); // 满板:Y
        wrkMast.setPicking("N"); // 拣料
        wrkMast.setExitMk("N"); // 退出
        wrkMast.setEmptyMk("N"); // 空板
        // 满板:Y
        wrkMast.setFullPlt("Y");
        // 拣料
        wrkMast.setPicking("N");
        // 退出
        wrkMast.setExitMk("N");
        // 空板
        wrkMast.setEmptyMk("N");
        wrkMast.setLinkMis("N");
        wrkMast.setAppeUser(userId);
        wrkMast.setAppeTime(now);
@@ -164,6 +280,48 @@
            throw new CoolException("保存工作档失败");
        }
        return wrkMast;
    }
    private boolean isPakOut(String locNo, double anfme){
        AgvLocDetl agvLocDetl = agvLocDetlService.selectOne(new EntityWrapper<AgvLocDetl>().eq("loc_no", locNo));
        if(agvLocDetl.getAnfme() > anfme){
            return false;
        }
        return true;
    }
    /*
    AGV生成出库工作档后修改订单信息
    TODO 与四项库生成出库工作档后修改订单信息整合到一起
     */
    private void modifyOrderDetl(LocDto locDto, Long userId){
        if (!BaseController.isJSON(locDto.getOrderNo())) {
            //非合并出库
            OrderDetl orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch());
            if (orderDetl == null) {
                orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), null);
            }
            if (!orderDetlService.increase(orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), locDto.getAnfme())) {
                throw new CoolException("修改订单明细数量失败");
            }
            orderService.updateSettle(orderDetl.getOrderId(), 2L, userId);
        }else {
            //合并出库
            List<OrderDto> orderDtoList = JSON.parseArray(locDto.getOrderNo(), OrderDto.class);
            for (OrderDto orderDto : orderDtoList) {
                OrderDetl orderDetl = orderDetlService.selectItem(orderDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch());
                if (orderDetl == null) {
                    orderDetl = orderDetlService.selectItem(orderDto.getOrderNo(), locDto.getMatnr(), null);
                }
                if (!orderDetlService.increase(orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), orderDto.getAnfme())) {
                    throw new CoolException("修改订单明细数量失败");
                }
                orderService.updateSettle(orderDetl.getOrderId(), 2L, userId);
            }
        }
    }
}
src/main/java/com/zy/asrs/service/impl/LocDetlServiceImpl.java
@@ -1,13 +1,20 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.core.common.Cools;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.result.StockVo;
import com.zy.asrs.mapper.LocDetlMapper;
import com.zy.asrs.service.LocDetlService;
import com.zy.asrs.service.LocMastService;
import com.zy.asrs.service.StaDescService;
import com.zy.common.model.LocDto;
import com.zy.common.model.QueryStockPreDo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -16,6 +23,11 @@
@Service("locDetlService")
public class LocDetlServiceImpl extends ServiceImpl<LocDetlMapper, LocDetl> implements LocDetlService {
    @Autowired
    private StaDescService staDescService;
    @Autowired
    private LocMastService locMastService;
    @Override
    public Page<LocDetl> getStockOut(Page<LocDetl> page) {
@@ -87,6 +99,37 @@
        return result;
    }
    //出库
    public double queryStockAndSetLocDto(String matnr, String batch, String orderNo, List<LocDto> locDtoList, double issued) {
        Wrapper<LocDetl> wrapper = new EntityWrapper<LocDetl>().eq("matnr", matnr).orderBy("modi_time");
        if(Cools.isEmpty(batch)){
            wrapper.isNull("batch");
        }else {
            wrapper.eq("batch",batch);
        }
        List<LocDetl> locDetlList = this.selectList(wrapper);
        for (LocDetl locDetl : locDetlList) {
            //判断当前库位货物是否F在库
            LocMast locMast = locMastService.selectById(locDetl.getLocNo());
            if(!"F".equals(locMast.getLocSts())){
                continue;
            }
            if (issued > 0) {
                double anfme = locDetl.getAnfme();
                int ioType = anfme > issued ? 103 : 101;
                anfme = anfme > issued ? issued : anfme;
                LocDto locDto = new LocDto(locDetl.getLocNo(), locDetl.getMatnr(), locDetl.getMaktx(), locDetl.getBatch(), orderNo, anfme);
                List<Integer> staNos = staDescService.queryOutStaNosByLocNo(locDetl.getLocNo(), ioType);
                locDto.setStaNos(staNos);
                locDtoList.add(locDto);
                // 剩余待出数量递减
                issued = issued - locDetl.getAnfme();
            }
        }
        return issued;
    }
    @Override
    public Double queryStockAnfme(String matnr, String batch) {
        return this.baseMapper.queryStockAnfme(matnr, batch);
src/main/java/com/zy/common/model/LocDto.java
@@ -27,7 +27,11 @@
    private List<Integer> staNos;
    private List<String> agvStaNos;
    private Integer staNo;
    private String agvStaNo;
    private String specs;
@@ -69,4 +73,11 @@
        }
    }
    public void setAgvStaNos(List<String> agvStaNos){
        this.agvStaNos = agvStaNos;
        if(!Cools.isEmpty(agvStaNos)){
            this.agvStaNo = agvStaNos.get(0);
        }
    }
}
src/main/java/com/zy/common/model/TaskDto.java
@@ -20,6 +20,8 @@
    private Integer staNo;
    private String agvStaNo;
    private List<LocDto> locDtos;
    {
@@ -37,6 +39,12 @@
        this.locDtos.add(locDto);
    }
    public TaskDto(String locNo, String agvStaNo, LocDto locDto) {
        this.locNo = locNo;
        this.agvStaNo = agvStaNo;
        this.locDtos.add(locDto);
    }
    public TaskDto(String locNo, Integer staNo, List<LocDto> locDtos) {
        this.locNo = locNo;
        this.staNo = staNo;
src/main/webapp/static/js/order/out.js
@@ -143,6 +143,12 @@
    }
    function pakoutPreviewDialog(data) {
        for(var i=0; i<data.length; i++){
            if(!data[i].staNos){
                data[i].staNos = data[i].agvStaNos;
                data[i].staNo = data[i].agvStaNo;
            }
        }
        var tableCache;
        layer.open({
            type: 1
@@ -170,10 +176,11 @@
                        {field: 'title', title: '商品', merge: true, align: 'center', width: 350},
                        {field: 'batch', title: '序列码', align: 'center'},
                        {field: 'anfme', title: '数量', align: 'center', width: 90, style: 'font-weight: bold'},
                        {field: 'locNo', title: '货位', align: 'center', width: 100, templet: '#locNoTpl'},
                        {field: 'locNo', title: '货位', align: 'center', templet: '#locNoTpl'},
                        {field: 'staNos', align: 'center', title: '出库站', merge: ['locNo'], templet: '#tbBasicTbStaNos'},
                        // {type: 'checkbox', merge: ['locNo']},
                    ]],
                    //cols: getCol(data),
                    done: function (res) {
                        tableMerge.render(this);
                        $('.layui-table-body.layui-table-main').css("overflow", "auto");
@@ -186,7 +193,8 @@
                    let data = tableCache[index];
                    for (let i = 0; i<tableCache.length; i++) {
                        if (tableCache[i].locNo === data.locNo) {
                            tableCache[i]['staNo'] = Number(obj.elem.value);
                            //tableCache[i]['staNo'] = Number(obj.elem.value);
                            tableCache[i]['staNo'] = obj.elem.value;
                        }
                    }
                    obj.othis.children().find("input").css("color", "blue");
@@ -231,7 +239,8 @@
                            // 确认
                            form.on('submit(staBatchSelectConfirm)', function (obj) {
                                let loadIdx = layer.load(2);
                                let batchSta = Number(obj.field.batchSta);
                                //let batchSta = Number(obj.field.batchSta);
                                let batchSta = obj.field.batchSta;
                                let arr = [];
                                for (let i = 0; i<tableCache.length; i++) {
                                    tableCache[i]['staNo'] = batchSta;
@@ -270,6 +279,13 @@
    function pakout(tableCache, layerIndex) {
        // let loadIndex = layer.load(2);
        for(var i=0; i<tableCache.length; i++){
            if(tableCache[i].agvStaNos){
                tableCache[i].agvStaNo = tableCache[i].staNo;
                tableCache[i].staNos = null;
                tableCache[i].staNo = null;
            }
        }
        notice.msg('正在生成出库任务......', {icon: 4});
        $.ajax({
            url: baseUrl + "/out/pakout/auth",
@@ -439,6 +455,12 @@
    }
    function pakoutPreviewMergeDialog(data) {
        for(var i=0; i<data.length; i++){
            if(!data[i].staNos){
                data[i].staNos = data[i].agvStaNos;
                data[i].staNo = data[i].agvStaNo;
            }
        }
        var mergeTabCache;
        layer.open({
            type: 1
@@ -466,7 +488,7 @@
                        {field: 'specs', title: '规格', align: 'center'},
                        {field: 'batch', title: '序列码', align: 'center'},
                        {field: 'anfme', title: '数量', align: 'center', width: 90, style: 'font-weight: bold'},
                        {field: 'locNo', title: '货位', align: 'center', width: 100, templet: '#locNoTpl'},
                        {field: 'locNo', title: '货位', align: 'center', templet: '#locNoTpl'},
                        {field: 'staNos', align: 'center', title: '出库站', merge: ['locNo'], templet: '#tbBasicTbStaNos'},
                        // {type: 'checkbox', merge: ['locNo']},
                    ]],
@@ -482,7 +504,8 @@
                    let data = mergeTabCache[index];
                    for (let i = 0; i<mergeTabCache.length; i++) {
                        if (mergeTabCache[i].locNo === data.locNo) {
                            mergeTabCache[i]['staNo'] = Number(obj.elem.value);
                            //mergeTabCache[i]['staNo'] = Number(obj.elem.value);
                            mergeTabCache[i]['staNo'] = obj.elem.value;
                        }
                    }
                    obj.othis.children().find("input").css("color", "blue");
@@ -527,7 +550,8 @@
                            // 确认
                            form.on('submit(staBatchSelectConfirm)', function (obj) {
                                let loadIdx = layer.load(2);
                                let batchSta = Number(obj.field.batchSta);
                                //let batchSta = Number(obj.field.batchSta);
                                let batchSta = obj.field.batchSta;
                                let arr = [];
                                for (let i = 0; i<mergeTabCache.length; i++) {
                                    mergeTabCache[i]['staNo'] = batchSta;
src/main/webapp/views/mat/mat.html
@@ -270,6 +270,7 @@
            <input type="radio" name="selectTemplate" value="1" title="模板一"  lay-filter="selectTemplateRadio" checked="">
            <input type="radio" name="selectTemplate" value="2" title="模板二" lay-filter="selectTemplateRadio">
            <input type="radio" name="selectTemplate" value="3" title="模板三" lay-filter="selectTemplateRadio">
            <input type="radio" name="selectTemplate" value="4" title="模板四" lay-filter="selectTemplateRadio">
        </div>
        <fieldset class="layui-elem-field site-demo-button" style="margin-top: 30px;text-align: left;">
            <legend>打印预览</legend>
@@ -334,6 +335,24 @@
                        <tr style="height: 74px">
                            <td align="center" colspan="1">规格</td>
                            <td align="center" colspan="1" style="overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">xxxxxxx</td>
                        </tr>
                    </table>
                </div>
                <!-- 预览图 4 -->
                <div id="template-preview-4" class="template-preview" style="display: none">
                    <table class="contain" width="280" style="overflow: hidden;font-size: xx-small;table-layout: fixed;">
                        <tr style="height: 74px">
                            <td align="center" scope="col" >商品</td>
                            <td align="center" scope="col" style="">xxxxxx-xx/xx</td>
                            <td align="center" scope="col" colspan="2">
                                <img class="template-code template-qrcode" src="" width="80%">
                                <div style="letter-spacing: 1px;margin-top: 1px; text-align: center"><span>xxxxxx</span></div>
                            </td>
                        </tr>
                        <tr style="height: 74px">
                            <td align="center" >规格</td>
                            <td align="center" colspan="3" >xxxxxxx</td>
                        </tr>
                    </table>
                </div>
@@ -426,6 +445,34 @@
    {{/each}}
</script>
<!-- 模板4 -->
<script type="text/template" id="templatePreview4" class="template-qrcode">
    {{#each data}}
    <table class="contain" width="400px" style="overflow: hidden;font-size: xx-small;table-layout: fixed; color:#000;">
        <tr style="height: 74px;font-size: 18px; font-weight: 400" >
            <td width="50px" align="center" scope="col" colspan="1">商品</td>
            <td width="170px" align="center" scope="col" colspan="1">{{this.maktx}}</td>
            <td align="center" scope="col" colspan="2" >
                <img class="template-code template-qrcode" src="{{this.barcodeUrl}}" width="80%">
                <div style="letter-spacing: 1px;margin-top: 1px; text-align: center; font-weight: 400">
                    <span>{{this.matnr}}</span>
                </div>
            </td>
        </tr>
        <tr style="height: 74px; font-size: 18px;font-weight: 400">
            <td width="50px" align="center" colspan="1">规格</td>
            <td align="center" colspan="3" style="
                overflow:hidden;
                text-overflow:ellipsis;
                -webkit-line-clamp: 3;
                -webkit-box-orient: vertical;
                word-break: break-all;
            ">{{this.specs}}</td>
        </tr>
    </table>
    {{/each}}
</script>
</body>
</html>