自动化立体仓库 - WMS系统
1.出库单据转历史档有问题
2.新增单据历史档
3.修复入库完成转明细失败
14个文件已添加
5个文件已修改
1078 ■■■■■ 已修改文件
src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OrderPakoutLogController.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderDetlPakoutLog.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderPakoutLog.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/OrderDetlPakoutLogMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/OrderPakoutLogMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/OrderDetlPakoutLogService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/OrderPakoutLogService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/OrderDetlPakoutLogServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/OrderPakoutLogServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/WorkLogHandler.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/OrderDetlPakoutLogMapper.xml 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/OrderPakoutLogMapper.xml 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260430_order_pakout_history_resource.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakout/history.js 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/orderPakout/history.html 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/api/service/impl/WcsApiServiceImplTest.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/service/impl/WcsApiServiceImpl.java
@@ -734,6 +734,7 @@
        } else if (isTaskCompleteCallback(params)) {
            //任务
            boolean needUpdate = false;
            applyTaskCompleteWeight(params, mast);
            if (isInboundOrMoveTask(mast)) {
                if (isInboundOrMoveTaskCompletionHandled(mast)) {
                    return R.ok();
@@ -778,11 +779,9 @@
        if (!isTaskCompleteCallback(params) || Cools.isEmpty(params.getSuperTaskNo())) {
            return false;
        }
        WrkMastLog wrkMastLog = wrkMastLogService.selectOne(new EntityWrapper<WrkMastLog>().eq("wrk_no", params.getSuperTaskNo()));
        return wrkMastLog != null
                && wrkMastLog.getWrkSts() != null
                && (Objects.equals(wrkMastLog.getWrkSts().longValue(), INBOUND_FINISHED_WRK_STS)
                || Objects.equals(wrkMastLog.getWrkSts().longValue(), INBOUND_ERP_REPORT_PENDING_WRK_STS));
        return wrkMastLogService.selectCount(new EntityWrapper<WrkMastLog>()
                .eq("wrk_no", params.getSuperTaskNo())
                .in("wrk_sts", Arrays.asList((int) INBOUND_FINISHED_WRK_STS, (int) INBOUND_ERP_REPORT_PENDING_WRK_STS))) > 0;
    }
    private boolean isInboundOrMoveTask(WrkMast mast) {
@@ -791,9 +790,16 @@
        }
        Integer ioType = mast.getIoType();
        return Objects.equals(ioType, 1)
                || Objects.equals(ioType, 2)
                || Objects.equals(ioType, 10)
                || Objects.equals(ioType, 11)
                || Objects.equals(ioType, CHANGE_LOC_IO_TYPE);
    }
    private void applyTaskCompleteWeight(ReceviceTaskParams params, WrkMast mast) {
        if (params == null || mast == null || params.getWeight() == null) {
            return;
        }
        mast.setScWeight(BigDecimal.valueOf(params.getWeight()));
    }
    private boolean isOutboundCrnTaskRun(ReceviceTaskParams params) {
@@ -1421,7 +1427,7 @@
        if (Objects.isNull(wrkMast)) {
            return;
        }
        if (wrkMast.getIoType()==1 || wrkMast.getIoType()==11) {
        if (wrkMast.getIoType()==1 || wrkMast.getIoType()==10 || wrkMast.getIoType()==11) {
            wrkMast.setWrkSts(2L);
            wrkMast.setModiTime(new Date());
            wrkMastService.updateById(wrkMast);
src/main/java/com/zy/asrs/controller/OrderPakoutLogController.java
New file
@@ -0,0 +1,87 @@
package com.zy.asrs.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.annotations.ManagerAuth;
import com.core.common.R;
import com.zy.asrs.entity.OrderDetlPakoutLog;
import com.zy.asrs.entity.OrderPakoutLog;
import com.zy.asrs.service.OrderDetlPakoutLogService;
import com.zy.asrs.service.OrderPakoutLogService;
import com.zy.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("order/pakout/history")
public class OrderPakoutLogController extends BaseController {
    @Autowired
    private OrderPakoutLogService orderPakoutLogService;
    @Autowired
    private OrderDetlPakoutLogService orderDetlPakoutLogService;
    @RequestMapping(value = "/order/list/auth")
    @ManagerAuth(memo = "出库单据历史档查询")
    public R list(@RequestParam(defaultValue = "1") Integer curr,
                  @RequestParam(defaultValue = "10") Integer limit,
                  @RequestParam Map<String, Object> param) {
        excludeTrash(param);
        normalizeRange(param, "create_time", "createTimeStart", "createTimeEnd");
        normalizeRange(param, "update_time", "updateTimeStart", "updateTimeEnd");
        return R.ok(orderPakoutLogService.selectHistoryPage(toSimplePage(curr, limit, param)));
    }
    @RequestMapping(value = "/orderDetl/list/auth")
    @ManagerAuth(memo = "出库单据历史明细查询")
    public R detlList(@RequestParam(defaultValue = "1") Integer curr,
                      @RequestParam(defaultValue = "10") Integer limit,
                      @RequestParam Map<String, Object> param) {
        excludeTrash(param);
        return R.ok(orderDetlPakoutLogService.selectHistoryPage(toSimplePage(curr, limit, param)));
    }
    @RequestMapping(value = "/order/export/auth")
    @ManagerAuth(memo = "出库单据历史档导出")
    public R export(@RequestBody JSONObject param) {
        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
        Map<String, Object> map = excludeTrash(param.getJSONObject("orderPakoutLog"));
        normalizeRange(map, "create_time", "createTimeStart", "createTimeEnd");
        normalizeRange(map, "update_time", "updateTimeStart", "updateTimeEnd");
        List<OrderPakoutLog> list = orderPakoutLogService.selectHistoryAll(map);
        return R.ok(exportSupport(list, fields));
    }
    private void normalizeRange(Map<String, Object> param, String sourceKey, String startKey, String endKey) {
        Object rangeObj = param.remove(sourceKey);
        if (rangeObj == null) {
            return;
        }
        String range = String.valueOf(rangeObj);
        if (!range.contains(RANGE_TIME_LINK)) {
            param.put(sourceKey, range);
            return;
        }
        String[] dates = range.split(RANGE_TIME_LINK);
        if (dates.length > 0) {
            param.put(startKey, dates[0]);
        }
        if (dates.length > 1) {
            param.put(endKey, dates[1]);
        }
    }
    private <T> Page<T> toSimplePage(Integer pageNumber, Integer pageSize, Map<String, Object> map) {
        Page<T> page = new Page<>(pageNumber, pageSize);
        map.put("pageNumber", pageNumber);
        map.put("pageSize", pageSize);
        page.setCondition(map);
        return page;
    }
}
src/main/java/com/zy/asrs/entity/OrderDetlPakoutLog.java
New file
@@ -0,0 +1,9 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableName;
@TableName("man_order_detl_log_pakout")
public class OrderDetlPakoutLog extends OrderDetlPakout {
    private static final long serialVersionUID = 1L;
}
src/main/java/com/zy/asrs/entity/OrderPakoutLog.java
New file
@@ -0,0 +1,17 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableName;
@TableName("man_order_log_pakout")
public class OrderPakoutLog extends OrderPakout {
    private static final long serialVersionUID = 1L;
    @Override
    public String getPakinPakoutStatus$() {
        if (getPakinPakoutStatus() == null) {
            return null;
        }
        return super.getPakinPakoutStatus$();
    }
}
src/main/java/com/zy/asrs/mapper/OrderDetlPakoutLogMapper.java
New file
@@ -0,0 +1,18 @@
package com.zy.asrs.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.zy.asrs.entity.OrderDetlPakoutLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Mapper
@Repository
public interface OrderDetlPakoutLogMapper extends BaseMapper<OrderDetlPakoutLog> {
    List<OrderDetlPakoutLog> selectHistoryPage(Map<String, Object> map);
    Integer selectHistoryPageCount(Map<String, Object> map);
}
src/main/java/com/zy/asrs/mapper/OrderPakoutLogMapper.java
New file
@@ -0,0 +1,20 @@
package com.zy.asrs.mapper;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.zy.asrs.entity.OrderPakoutLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Mapper
@Repository
public interface OrderPakoutLogMapper extends BaseMapper<OrderPakoutLog> {
    List<OrderPakoutLog> selectHistoryPage(Map<String, Object> map);
    Integer selectHistoryPageCount(Map<String, Object> map);
    List<OrderPakoutLog> selectHistoryAll(Map<String, Object> map);
}
src/main/java/com/zy/asrs/service/OrderDetlPakoutLogService.java
New file
@@ -0,0 +1,10 @@
package com.zy.asrs.service;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.IService;
import com.zy.asrs.entity.OrderDetlPakoutLog;
public interface OrderDetlPakoutLogService extends IService<OrderDetlPakoutLog> {
    Page<OrderDetlPakoutLog> selectHistoryPage(Page<OrderDetlPakoutLog> page);
}
src/main/java/com/zy/asrs/service/OrderPakoutLogService.java
New file
@@ -0,0 +1,15 @@
package com.zy.asrs.service;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.IService;
import com.zy.asrs.entity.OrderPakoutLog;
import java.util.List;
import java.util.Map;
public interface OrderPakoutLogService extends IService<OrderPakoutLog> {
    Page<OrderPakoutLog> selectHistoryPage(Page<OrderPakoutLog> page);
    List<OrderPakoutLog> selectHistoryAll(Map<String, Object> map);
}
src/main/java/com/zy/asrs/service/impl/OrderDetlPakoutLogServiceImpl.java
New file
@@ -0,0 +1,19 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zy.asrs.entity.OrderDetlPakoutLog;
import com.zy.asrs.mapper.OrderDetlPakoutLogMapper;
import com.zy.asrs.service.OrderDetlPakoutLogService;
import org.springframework.stereotype.Service;
@Service("orderDetlPakoutLogService")
public class OrderDetlPakoutLogServiceImpl extends ServiceImpl<OrderDetlPakoutLogMapper, OrderDetlPakoutLog> implements OrderDetlPakoutLogService {
    @Override
    public Page<OrderDetlPakoutLog> selectHistoryPage(Page<OrderDetlPakoutLog> page) {
        page.setRecords(baseMapper.selectHistoryPage(page.getCondition()));
        page.setTotal(baseMapper.selectHistoryPageCount(page.getCondition()));
        return page;
    }
}
src/main/java/com/zy/asrs/service/impl/OrderPakoutLogServiceImpl.java
New file
@@ -0,0 +1,27 @@
package com.zy.asrs.service.impl;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.zy.asrs.entity.OrderPakoutLog;
import com.zy.asrs.mapper.OrderPakoutLogMapper;
import com.zy.asrs.service.OrderPakoutLogService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service("orderPakoutLogService")
public class OrderPakoutLogServiceImpl extends ServiceImpl<OrderPakoutLogMapper, OrderPakoutLog> implements OrderPakoutLogService {
    @Override
    public Page<OrderPakoutLog> selectHistoryPage(Page<OrderPakoutLog> page) {
        page.setRecords(baseMapper.selectHistoryPage(page.getCondition()));
        page.setTotal(baseMapper.selectHistoryPageCount(page.getCondition()));
        return page;
    }
    @Override
    public List<OrderPakoutLog> selectHistoryAll(Map<String, Object> map) {
        return baseMapper.selectHistoryAll(map);
    }
}
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -1596,16 +1596,25 @@
        if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
            throw new CoolException("保存工作历史档失败, workNo = " + wrkMast.getWrkNo());
        }
        int wrkDetlCount = 0;
        if (wrkMast.getIoType() != 10 && wrkMast.getIoType() != 110) {
            wrkDetlCount = wrkDetlService.selectCount(new EntityWrapper<WrkDetl>().eq("wrk_no", workNo));
            // 保存工作明细档历史档
            if (wrkDetlCount > 0 && !wrkDetlLogService.save(wrkMast.getWrkNo())) {
                throw new CoolException("保存工作明细历史档失败, workNo = " + wrkMast.getWrkNo());
            }
        }
        // 删除工作主档
        boolean wrkMastRes = wrkMastService.deleteById(wrkMast);
        if (wrkMast.getIoType() != 10 && wrkMast.getIoType() != 110) {
            // 保存工作明细档历史档
            if (!wrkDetlLogService.save(wrkMast.getWrkNo())) {
//                throw new CoolException("保存工作明细历史档失败, workNo = " + wrkMast.getWrkNo());
            }
        if (!wrkMastRes) {
            throw new CoolException("删除工作主档失败, workNo = " + wrkMast.getWrkNo());
        }
        if (wrkMast.getIoType() != 10 && wrkMast.getIoType() != 110 && wrkDetlCount > 0) {
            // 删除工作档明细
            boolean wrkDetlRes = wrkDetlService.delete(new EntityWrapper<WrkDetl>().eq("wrk_no", workNo));
            if (!wrkDetlRes) {
                throw new CoolException("删除工作明细档失败, workNo = " + wrkMast.getWrkNo());
            }
        }
        // 修改库位状态
src/main/java/com/zy/asrs/task/handler/WorkLogHandler.java
@@ -49,6 +49,7 @@
    public ReturnT<String> start(WrkMast wrkMast) {
        try {
            List<WrkDetl> wrkDetls = wrkDetlService.selectByWrkNo(wrkMast.getWrkNo());
            int wrkDetlCount = Cools.isEmpty(wrkDetls) ? 0 : wrkDetls.size();
            // 修改订单状态 作业中 ===>> 已完成
            if (!Cools.isEmpty(wrkDetls)) {
                Iterator<WrkDetl> iterator = wrkDetls.iterator();
@@ -109,17 +110,17 @@
            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
                exceptionHandle("保存工作历史档[workNo={0}]失败", wrkMast.getWrkNo());
            }
            // 保存工作明细档历史档
            if (wrkDetlCount > 0 && !wrkDetlLogService.save(wrkMast.getWrkNo())) {
                exceptionHandle("保存工作明细历史档[workNo={0}]失败", wrkMast.getWrkNo());
            }
            // 删除工作主档
            if (!wrkMastService.deleteById(wrkMast)) {
                exceptionHandle("删除工作主档[workNo={0}]失败", wrkMast.getWrkNo());
            }
            // 保存工作明细档历史档
            if (!wrkDetlLogService.save(wrkMast.getWrkNo())) {
//                exceptionHandle("保存工作明细历史档[workNo={0}]失败", wrkMast.getWrkNo());
            }
            // 删除工作明细档
            if (!wrkDetlService.delete(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()))) {
//                exceptionHandle("删除工作明细档[workNo={0}]失败", wrkMast.getWrkNo());
            if (wrkDetlCount > 0 && !wrkDetlService.delete(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()))) {
                exceptionHandle("删除工作明细档[workNo={0}]失败", wrkMast.getWrkNo());
            }
        } catch (Exception e) {
            log.error("fail", e);
src/main/resources/application.yml
@@ -114,7 +114,7 @@
  #  开关
  switch: true
  status-sync:
    enabled: true
    enabled: false
    # 失败时是否打 WARN/ERROR(本地无 WCS 时可设 false,需排查时再开)
    log-on-failure: true
    initial-delay: 10000
src/main/resources/mapper/OrderDetlPakoutLogMapper.xml
New file
@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.OrderDetlPakoutLogMapper">
    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.OrderDetlPakoutLog">
        <id column="id" property="id" />
        <result column="order_id" property="orderId" />
        <result column="order_no" property="orderNo" />
        <result column="anfme" property="anfme" />
        <result column="work_qty" property="workQty" />
        <result column="qty" property="qty" />
        <result column="matnr" property="matnr" />
        <result column="maktx" property="maktx" />
        <result column="batch" property="batch" />
        <result column="specs" property="specs" />
        <result column="model" property="model" />
        <result column="color" property="color" />
        <result column="brand" property="brand" />
        <result column="unit" property="unit" />
        <result column="price" property="price" />
        <result column="sku" property="sku" />
        <result column="units" property="units" />
        <result column="barcode" property="barcode" />
        <result column="origin" property="origin" />
        <result column="manu" property="manu" />
        <result column="manu_date" property="manuDate" />
        <result column="item_num" property="itemNum" />
        <result column="safe_qty" property="safeQty" />
        <result column="weight" property="weight" />
        <result column="length" property="manLength" />
        <result column="volume" property="volume" />
        <result column="three_code" property="threeCode" />
        <result column="supp" property="supp" />
        <result column="supp_code" property="suppCode" />
        <result column="be_batch" property="beBatch" />
        <result column="dead_time" property="deadTime" />
        <result column="dead_warn" property="deadWarn" />
        <result column="source" property="source" />
        <result column="inspect" property="inspect" />
        <result column="danger" property="danger" />
        <result column="status" property="status" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="memo" property="memo" />
        <result column="pakin_pakout_status" property="pakinPakoutStatus" />
        <result column="line_number" property="lineNumber" />
        <result column="standby1" property="standby1" />
        <result column="standby2" property="standby2" />
        <result column="standby3" property="standby3" />
        <result column="box_type1" property="boxType1" />
        <result column="box_type2" property="boxType2" />
        <result column="box_type3" property="boxType3" />
    </resultMap>
    <sql id="BaseColumnList">
        mdl.id,
        mdl.order_id,
        mdl.order_no,
        mdl.anfme,
        mdl.work_qty,
        mdl.qty,
        mdl.matnr,
        mdl.maktx,
        mdl.batch,
        mdl.specs,
        mdl.model,
        mdl.color,
        mdl.brand,
        mdl.unit,
        mdl.price,
        mdl.sku,
        mdl.units,
        mdl.barcode,
        mdl.origin,
        mdl.manu,
        mdl.manu_date,
        mdl.item_num,
        mdl.safe_qty,
        mdl.weight,
        mdl.length,
        mdl.volume,
        mdl.three_code,
        mdl.supp,
        mdl.supp_code,
        mdl.be_batch,
        mdl.dead_time,
        mdl.dead_warn,
        mdl.source,
        mdl.inspect,
        mdl.danger,
        mdl.status,
        mdl.create_by,
        mdl.create_time,
        mdl.update_by,
        mdl.update_time,
        mdl.memo,
        mdl.pakin_pakout_status,
        mdl.line_number,
        mdl.standby1,
        mdl.standby2,
        mdl.standby3,
        mdl.box_type1,
        mdl.box_type2,
        mdl.box_type3
    </sql>
    <sql id="HistoryCondition">
        <if test="order_id != null and order_id != ''">
            and mdl.order_id = #{order_id}
        </if>
        <if test="order_no != null and order_no != ''">
            and mdl.order_no like '%' + #{order_no} + '%'
        </if>
        <if test="matnr != null and matnr != ''">
            and mdl.matnr like '%' + #{matnr} + '%'
        </if>
        <if test="maktx != null and maktx != ''">
            and mdl.maktx like '%' + #{maktx} + '%'
        </if>
        <if test="batch != null and batch != ''">
            and mdl.batch like '%' + #{batch} + '%'
        </if>
        <if test="brand != null and brand != ''">
            and mdl.brand like '%' + #{brand} + '%'
        </if>
        <if test="standby1 != null and standby1 != ''">
            and mdl.standby1 like '%' + #{standby1} + '%'
        </if>
        <if test="memo != null and memo != ''">
            and mdl.memo like '%' + #{memo} + '%'
        </if>
        <if test="condition != null and condition != ''">
            and (
                mdl.order_no like '%' + #{condition} + '%'
                or mdl.matnr like '%' + #{condition} + '%'
                or mdl.maktx like '%' + #{condition} + '%'
                or mdl.batch like '%' + #{condition} + '%'
                or mdl.brand like '%' + #{condition} + '%'
                or mdl.standby1 like '%' + #{condition} + '%'
                or mdl.memo like '%' + #{condition} + '%'
            )
        </if>
    </sql>
    <select id="selectHistoryPage" parameterType="java.util.Map" resultMap="BaseResultMap">
        select * from
        (
            select
                ROW_NUMBER() over (order by mdl.line_number asc, mdl.id asc) as row,
                <include refid="BaseColumnList" />
            from man_order_detl_log_pakout mdl
            where 1=1
            <include refid="HistoryCondition" />
        ) t
        where t.row between ((#{pageNumber}-1)*#{pageSize}+1) and (#{pageNumber}*#{pageSize})
    </select>
    <select id="selectHistoryPageCount" parameterType="java.util.Map" resultType="java.lang.Integer">
        select count(1)
        from man_order_detl_log_pakout mdl
        where 1=1
        <include refid="HistoryCondition" />
    </select>
</mapper>
src/main/resources/mapper/OrderPakoutLogMapper.xml
New file
@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.OrderPakoutLogMapper">
    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.OrderPakoutLog">
        <id column="id" property="id" />
        <result column="uuid" property="uuid" />
        <result column="order_no" property="orderNo" />
        <result column="order_time" property="orderTime" />
        <result column="doc_type" property="docType" />
        <result column="item_id" property="itemId" />
        <result column="item_name" property="itemName" />
        <result column="allot_item_id" property="allotItemId" />
        <result column="def_number" property="defNumber" />
        <result column="number" property="number" />
        <result column="cstmr" property="cstmr" />
        <result column="cstmr_name" property="cstmrName" />
        <result column="tel" property="tel" />
        <result column="oper_memb" property="operMemb" />
        <result column="total_fee" property="totalFee" />
        <result column="discount" property="discount" />
        <result column="discount_fee" property="discountFee" />
        <result column="other_fee" property="otherFee" />
        <result column="act_fee" property="actFee" />
        <result column="pay_type" property="payType" />
        <result column="salesman" property="salesman" />
        <result column="account_day" property="accountDay" />
        <result column="post_fee_type" property="postFeeType" />
        <result column="post_fee" property="postFee" />
        <result column="pay_time" property="payTime" />
        <result column="send_time" property="sendTime" />
        <result column="ship_name" property="shipName" />
        <result column="ship_code" property="shipCode" />
        <result column="settle" property="settle" />
        <result column="status" property="status" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="memo" property="memo" />
        <result column="move_status" property="moveStatus" />
        <result column="pakin_pakout_status" property="pakinPakoutStatus" />
    </resultMap>
    <sql id="BaseColumnList">
        mol.id,
        mol.uuid,
        mol.order_no,
        mol.order_time,
        mol.doc_type,
        mol.item_id,
        mol.item_name,
        mol.allot_item_id,
        mol.def_number,
        mol.number,
        mol.cstmr,
        mol.cstmr_name,
        mol.tel,
        mol.oper_memb,
        mol.total_fee,
        mol.discount,
        mol.discount_fee,
        mol.other_fee,
        mol.act_fee,
        mol.pay_type,
        mol.salesman,
        mol.account_day,
        mol.post_fee_type,
        mol.post_fee,
        mol.pay_time,
        mol.send_time,
        mol.ship_name,
        mol.ship_code,
        mol.settle,
        mol.status,
        mol.create_by,
        mol.create_time,
        mol.update_by,
        mol.update_time,
        mol.memo,
        mol.move_status,
        mol.pakin_pakout_status
    </sql>
    <sql id="HistoryCondition">
        <if test="order_no != null and order_no != ''">
            and mol.order_no like '%' + #{order_no} + '%'
        </if>
        <if test="orderTime != null and orderTime != ''">
            and mol.order_time like '%' + #{orderTime} + '%'
        </if>
        <if test="doc_type != null and doc_type != ''">
            and mol.doc_type = #{doc_type}
        </if>
        <if test="settle != null and settle != ''">
            and mol.settle = #{settle}
        </if>
        <if test="move_status != null and move_status != ''">
            and mol.move_status = #{move_status}
        </if>
        <if test="createTimeStart != null and createTimeStart != ''">
            and mol.create_time &gt;= #{createTimeStart}
        </if>
        <if test="createTimeEnd != null and createTimeEnd != ''">
            and mol.create_time &lt;= #{createTimeEnd}
        </if>
        <if test="updateTimeStart != null and updateTimeStart != ''">
            and mol.update_time &gt;= #{updateTimeStart}
        </if>
        <if test="updateTimeEnd != null and updateTimeEnd != ''">
            and mol.update_time &lt;= #{updateTimeEnd}
        </if>
        <if test="memo != null and memo != ''">
            and mol.memo like '%' + #{memo} + '%'
        </if>
        <if test="condition != null and condition != ''">
            and (
                mol.order_no like '%' + #{condition} + '%'
                or mol.order_time like '%' + #{condition} + '%'
                or mol.cstmr_name like '%' + #{condition} + '%'
                or mol.memo like '%' + #{condition} + '%'
                or cast(mol.id as varchar) like '%' + #{condition} + '%'
            )
        </if>
    </sql>
    <select id="selectHistoryPage" parameterType="java.util.Map" resultMap="BaseResultMap">
        select * from
        (
            select
                ROW_NUMBER() over (order by mol.update_time desc, mol.create_time desc, mol.id desc) as row,
                <include refid="BaseColumnList" />
            from man_order_log_pakout mol
            where 1=1
            <include refid="HistoryCondition" />
        ) t
        where t.row between ((#{pageNumber}-1)*#{pageSize}+1) and (#{pageNumber}*#{pageSize})
    </select>
    <select id="selectHistoryPageCount" parameterType="java.util.Map" resultType="java.lang.Integer">
        select count(1)
        from man_order_log_pakout mol
        where 1=1
        <include refid="HistoryCondition" />
    </select>
    <select id="selectHistoryAll" parameterType="java.util.Map" resultMap="BaseResultMap">
        select
            <include refid="BaseColumnList" />
        from man_order_log_pakout mol
        where 1=1
        <include refid="HistoryCondition" />
        order by mol.update_time desc, mol.create_time desc, mol.id desc
    </select>
</mapper>
src/main/resources/sql/20260430_order_pakout_history_resource.sql
New file
@@ -0,0 +1,5 @@
if not exists (select 1 from sys_resource where code = 'orderPakout/history.html')
begin
    insert into sys_resource (code, name, resource_id, level, sort, status)
    values ('orderPakout/history.html', N'出库单据历史档', 40524, 2, 7, 1)
end
src/main/webapp/static/js/orderPakout/history.js
New file
@@ -0,0 +1,262 @@
var pageCurr;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).use(['table', 'laydate', 'form', 'layer'], function () {
    var table = layui.table;
    var $ = layui.jquery;
    var layer = layui.layer;
    var layDate = layui.laydate;
    var form = layui.form;
    $.ajax({
        url: baseUrl + "/docType/list/auth",
        headers: {'token': localStorage.getItem('token')},
        data: {
            limit: 9999
        },
        method: 'POST',
        success: function (res) {
            if (res.code === 200) {
                var template = Handlebars.compile($('#docTypeTpl').html());
                $('#docType-query').html(template(res.data));
                form.render('select');
            } else if (res.code === 403) {
                top.location.href = baseUrl + "/";
            } else {
                layer.msg(res.msg, {icon: 2});
            }
        }
    });
    var tableIns = table.render({
        elem: '#orderPakoutLog',
        headers: {token: localStorage.getItem('token')},
        url: baseUrl + '/order/pakout/history/order/list/auth',
        page: true,
        limit: 16,
        limits: [16, 30, 50, 100, 200, 500],
        toolbar: '#toolbar',
        cellMinWidth: 100,
        cols: [[
            {type: 'numbers', title: '#'},
            {field: 'orderNo', title: '单据编号', minWidth: 180},
            {field: 'docType$', align: 'center', title: '类型', width: 160},
            {field: 'settle$', align: 'center', title: '状态', templet: '#settleTpl', width: 140},
            {field: 'orderTime', align: 'center', title: '单据日期', width: 160},
            {field: 'createTime$', align: 'center', title: '创建时间', width: 180},
            {field: 'updateTime$', align: 'center', title: '迁移/更新时间', width: 180},
            {field: 'memo', align: 'center', title: '备注', minWidth: 240},
            {fixed: 'right', title: '操作', align: 'center', toolbar: '#operate', width: 110}
        ]],
        request: {
            pageName: 'curr',
            pageSize: 'limit'
        },
        parseData: function (res) {
            return {
                'code': res.code,
                'msg': res.msg,
                'count': res.data.total,
                'data': res.data.records
            };
        },
        response: {
            statusCode: 200
        },
        done: function (res, curr) {
            if (res.code === 403) {
                top.location.href = baseUrl + "/";
            }
            pageCurr = curr;
            limit();
        }
    });
    table.on('tool(orderPakoutLog)', function (obj) {
        if (obj.event === 'queryData') {
            openQueryData(obj.data);
        }
    });
    table.on('toolbar(orderPakoutLog)', function (obj) {
        if (obj.event === 'exportData') {
            exportData(obj);
        }
    });
    form.on('submit(search)', function () {
        pageCurr = 1;
        tableReload();
        return false;
    });
    form.on('submit(reset)', function () {
        pageCurr = 1;
        clearFormVal($('#search-box'));
        form.render('select');
        tableReload();
        return false;
    });
    function tableReload() {
        var searchData = {};
        $.each($('#search-box [name]').serializeArray(), function () {
            searchData[this.name] = this.value;
        });
        tableIns.reload({
            where: searchData,
            page: {
                curr: pageCurr || 1
            }
        });
    }
    function openQueryData(order) {
        layer.open({
            type: 1,
            title: '查询数据 - ' + order.orderNo,
            area: ['1150px', '680px'],
            shadeClose: true,
            content: $('#queryDataDialog').html(),
            success: function (layero) {
                var $layer = $(layero);
                $layer.find('[name="order_id"]').val(order.id);
                var detailTable = renderDetailTable(order.id);
                $layer.find('.detail-search').on('click', function () {
                    var detailSearchData = {};
                    $.each($layer.find('.detail-toolbar [name]').serializeArray(), function () {
                        detailSearchData[this.name] = this.value;
                    });
                    detailSearchData.order_id = order.id;
                    detailTable.reload({
                        where: detailSearchData,
                        page: {
                            curr: 1
                        }
                    });
                });
                $layer.find('.detail-reset').on('click', function () {
                    clearFormVal($layer.find('.detail-toolbar'));
                    $layer.find('[name="order_id"]').val(order.id);
                    detailTable.reload({
                        where: {
                            order_id: order.id
                        },
                        page: {
                            curr: 1
                        }
                    });
                });
            }
        });
    }
    function renderDetailTable(orderId) {
        return table.render({
            elem: '#orderPakoutLogDetail',
            headers: {token: localStorage.getItem('token')},
            url: baseUrl + '/order/pakout/history/orderDetl/list/auth',
            where: {
                order_id: orderId
            },
            page: true,
            limit: 10,
            limits: [10, 20, 50, 100, 200],
            height: 500,
            cellMinWidth: 100,
            cols: [[
                {type: 'numbers', title: '#'},
                {field: 'orderNo', align: 'center', title: '单据编号', width: 160},
                {field: 'lineNumber', align: 'center', title: '行号', width: 80},
                {field: 'matnr', align: 'center', title: '商品编码', width: 160},
                {field: 'maktx', align: 'center', title: '商品名称', width: 180},
                {field: 'batch', align: 'center', title: '批号', width: 120},
                {field: 'brand', align: 'center', title: '品牌', width: 120},
                {field: 'model', align: 'center', title: '型号', width: 120},
                {field: 'specs', align: 'center', title: '规格', width: 120},
                {field: 'standby1', align: 'center', title: '机台', width: 100},
                {field: 'unit', align: 'center', title: '单位', width: 80},
                {field: 'anfme', align: 'center', title: '数量', width: 100},
                {field: 'workQty', align: 'center', title: '作业数量', width: 110},
                {field: 'qty', align: 'center', title: '完成数量', width: 110},
                {field: 'createTime$', align: 'center', title: '创建时间', width: 180},
                {field: 'updateTime$', align: 'center', title: '更新时间', width: 180},
                {field: 'memo', align: 'center', title: '备注', minWidth: 180}
            ]],
            request: {
                pageName: 'curr',
                pageSize: 'limit'
            },
            parseData: function (res) {
                return {
                    'code': res.code,
                    'msg': res.msg,
                    'count': res.data.total,
                    'data': res.data.records
                };
            },
            response: {
                statusCode: 200
            }
        });
    }
    function exportData(obj) {
        layer.confirm('确定导出Excel吗', {shadeClose: true}, function () {
            var titles = [];
            var fields = [];
            obj.config.cols[0].map(function (col) {
                if (!col.type && col.hide !== true && col.toolbar == null) {
                    titles.push(col.title);
                    fields.push(col.field);
                }
            });
            var exportData = {};
            $.each($('#search-box [name]').serializeArray(), function () {
                exportData[this.name] = this.value;
            });
            $.ajax({
                url: baseUrl + "/order/pakout/history/order/export/auth",
                headers: {'token': localStorage.getItem('token')},
                data: JSON.stringify({
                    orderPakoutLog: exportData,
                    fields: fields
                }),
                dataType: 'json',
                contentType: 'application/json;charset=UTF-8',
                method: 'POST',
                success: function (res) {
                    layer.closeAll();
                    if (res.code === 200) {
                        table.exportFile(titles, res.data, 'xls');
                    } else if (res.code === 403) {
                        top.location.href = baseUrl + "/";
                    } else {
                        layer.msg(res.msg, {icon: 2});
                    }
                }
            });
        });
    }
    layDate.render({
        elem: '.layui-laydate-range',
        type: 'datetime',
        range: true
    });
});
function clearFormVal(el) {
    $(':input', el)
        .val('')
        .removeAttr('checked')
        .removeAttr('selected');
}
$('body').keydown(function () {
    if (event.keyCode === 13) {
        $("#search").click();
    }
});
src/main/webapp/views/orderPakout/history.html
New file
@@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
    <style>
        .detail-toolbar {
            padding: 10px 10px 0 10px;
        }
        .history-tips {
            color: #999;
            line-height: 28px;
        }
    </style>
</head>
<body>
<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <div id="search-box" class="layui-form toolbar">
                <div class="layui-form-item">
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input name="order_no" class="layui-input" type="text" placeholder="输入单据编号" autocomplete="off"/>
                        </div>
                    </div>
                    <div class="layui-inline" style="width: 300px">
                        <div class="layui-input-inline">
                            <input class="layui-input layui-laydate-range" name="create_time" type="text" placeholder="创建起始时间 - 终止时间" autocomplete="off" style="width: 300px">
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <select name="doc_type" id="docType-query"></select>
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <select name="settle">
                                <option value="">选择状态</option>
                                <option value="1">待处理</option>
                                <option value="2">作业中</option>
                                <option value="4">已完成</option>
                                <option value="6">上报完成</option>
                            </select>
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline">
                            <input name="condition" class="layui-input" type="text" placeholder="关键字" autocomplete="off"/>
                        </div>
                    </div>
                    <div class="layui-inline">
                        <button id="search" class="layui-btn icon-btn" lay-filter="search" lay-submit>
                            <i class="layui-icon">&#xe615;</i>搜索
                        </button>
                        <button id="reset" class="layui-btn layui-btn-primary icon-btn" lay-filter="reset" lay-submit>
                            <i class="layui-icon">&#xe666;</i>重置
                        </button>
                    </div>
                </div>
            </div>
            <table id="orderPakoutLog" lay-filter="orderPakoutLog"></table>
        </div>
    </div>
    <div class="layui-card">
        <div class="layui-card-body history-tips">
            出库单据历史档:展示已迁移到历史表的出库主档,点击“查询数据”可查看该单据的历史明细数据。
        </div>
    </div>
</div>
<script type="text/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-primary layui-btn-sm" id="btn-export" lay-event="exportData">导出</button>
    </div>
</script>
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-xs" lay-event="queryData">查询数据</a>
</script>
<script type="text/html" id="settleTpl">
    <span name="settle"
          {{# if( d.settle === 1){ }}
          class="layui-badge layui-badge-red"
          {{# }else if(d.settle === 2){ }}
          class="layui-badge layui-badge-green"
          {{# }else if(d.settle === 4){ }}
          class="layui-badge layui-badge-blue"
          {{# }else { }}
          class="layui-badge layui-badge-gray"
          {{# } }}
    >{{d.settle$ || d.settle || ''}}</span>
</script>
<script type="text/html" id="queryDataDialog">
    <div class="layui-form detail-toolbar">
        <input type="hidden" name="order_id">
        <div class="layui-form-item">
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input name="matnr" class="layui-input" type="text" placeholder="商品编码" autocomplete="off"/>
                </div>
            </div>
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input name="maktx" class="layui-input" type="text" placeholder="商品名称" autocomplete="off"/>
                </div>
            </div>
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input name="batch" class="layui-input" type="text" placeholder="批号" autocomplete="off"/>
                </div>
            </div>
            <div class="layui-inline">
                <div class="layui-input-inline">
                    <input name="standby1" class="layui-input" type="text" placeholder="机台" autocomplete="off"/>
                </div>
            </div>
            <div class="layui-inline">
                <button type="button" class="layui-btn icon-btn detail-search">
                    <i class="layui-icon">&#xe615;</i>搜索
                </button>
                <button type="button" class="layui-btn layui-btn-primary icon-btn detail-reset">
                    <i class="layui-icon">&#xe666;</i>重置
                </button>
            </div>
        </div>
    </div>
    <table id="orderPakoutLogDetail" lay-filter="orderPakoutLogDetail"></table>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/orderPakout/history.js" charset="utf-8"></script>
<script type="text/template" id="docTypeTpl">
    <option value="">选择类型</option>
    {{#each records}}
    <option value="{{docId}}">{{docName}}</option>
    {{/each}}
</script>
</body>
</html>
src/test/java/com/zy/api/service/impl/WcsApiServiceImplTest.java
@@ -22,6 +22,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -131,6 +132,63 @@
    }
    @Test
    void receviceTaskFromWcs_marksEmptyPalletInboundCompleteWhenTaskCompleteUsesCrnNotifyType() {
        WrkMast mast = moveMast(10);
        when(wrkMastService.selectOne(any())).thenReturn(mast);
        when(wrkMastService.updateById(mast)).thenReturn(true);
        service.receviceTaskFromWcs(new ReceviceTaskParams()
                .setNotifyType("Crn")
                .setMsgType("task_complete")
                .setSuperTaskNo("7597"));
        assertEquals(Long.valueOf(4L), mast.getWrkSts());
        verify(wrkMastService).updateById(mast);
    }
    @Test
    void receviceTaskFromWcs_recordsTaskCompleteWeightOnMast() {
        WrkMast mast = moveMast(1);
        when(wrkMastService.selectOne(any())).thenReturn(mast);
        when(wrkMastService.updateById(mast)).thenReturn(true);
        service.receviceTaskFromWcs(new ReceviceTaskParams()
                .setNotifyType("Crn")
                .setMsgType("task_complete")
                .setSuperTaskNo("7597")
                .setWeight(32.5D));
        assertEquals(new BigDecimal("32.5"), mast.getScWeight());
        assertEquals(Long.valueOf(4L), mast.getWrkSts());
        verify(wrkMastService).updateById(mast);
    }
    @Test
    void receviceTaskFromWcs_doesNotAutoCompleteUnsupportedIoTypeTwo() {
        WrkMast mast = moveMast(2);
        when(wrkMastService.selectOne(any())).thenReturn(mast);
        service.receviceTaskFromWcs(new ReceviceTaskParams()
                .setNotifyType("Crn")
                .setMsgType("task_complete")
                .setSuperTaskNo("7597"));
        assertEquals(Long.valueOf(2L), mast.getWrkSts());
        verify(wrkMastService, never()).updateById(mast);
    }
    @Test
    void updateWrkMastAfterPublish_marksEmptyPalletInboundRunning() {
        WrkMast mast = moveMast(10);
        mast.setWrkSts(1L);
        ReflectionTestUtils.invokeMethod(service, "updateWrkMastAfterPublish", mast);
        assertEquals(Long.valueOf(2L), mast.getWrkSts());
        verify(wrkMastService).updateById(mast);
    }
    @Test
    @SuppressWarnings("unchecked")
    void buildReassignCrnSearchOrder_currentThree_shouldSearchSmallerThenWrapDescending() {
        List<Integer> result = ReflectionTestUtils.invokeMethod(