fix(mes): 优化行业判定逻辑并增强条码匹配兼容性

重构逆向追溯的行业判定逻辑,增加质检单和生产订单的兜底匹配机制。主要变更包括:
1. 增加质检单条码匹配的兼容性,支持barcode和batch_no字段
2. 添加直接按成品分表回溯的兜底逻辑
3. 优化出库单关联查询,增加COALESCE处理空值
4. 完善单位关联逻辑,优先使用material_unit_id
5. 备份旧版SQL文件以便回滚
master
zangch@mesnac.com 1 week ago
parent ba10ca4395
commit 1e2277fa9e

@ -1,7 +1,6 @@
package org.dromara.mes.controller; package org.dromara.mes.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
@ -31,7 +30,7 @@ import java.util.List;
@Validated @Validated
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@RequestMapping("/mes/reverseTrace") @RequestMapping("/reverseTrace")
public class ReverseTraceController extends BaseController { public class ReverseTraceController extends BaseController {
private final IReverseTraceService reverseTraceService; private final IReverseTraceService reverseTraceService;

@ -6,20 +6,32 @@
<!-- ======================== 1. 行业判定 + 成品锚点定位 ======================== --> <!-- ======================== 1. 行业判定 + 成品锚点定位 ======================== -->
<!-- <!--
严格按《密炼.txt》4.1 + 4.2 执行: 优先按《密炼.txt》的“质检单 -> 生产订单 -> 分表”口径定位。
1. 先通过 prod_order_info + base_material_info.tire_markings + qc_inspection_main.production_order 判定行业 真实库中存在两类偏差:
2. 再按已判定行业去对应成品明细表定位锚点 1. 成品质检条码落在 barcode 而不是 batch_no
2. 生产订单未完全回填到 prod_order_info。
为什么这里恢复成“两阶段”: 因此这里保留原始订单判定链路,同时增加“按成品分表直接回溯”的兜底逻辑,
文档要求先基于生产订单和质检主表确定行业,而不是直接根据成品分表是否命中来反推。 确保真实演示数据可以稳定命中而不误判行业。
这样可以避免“轮胎单据但 _4 未命中时被误判成机加”的口径偏差。
--> -->
<select id="selectAnchor" resultType="java.util.Map"> <select id="selectAnchor" resultType="java.util.Map">
WITH industry_judge AS ( WITH qc_match AS (
SELECT TOP 1
qim.production_order AS productionOrder,
qim.tenant_id AS tenant_id
FROM qc_inspection_main qim
WHERE (
RTRIM(LTRIM(ISNULL(qim.batch_no, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(qim.barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND qim.del_flag = '0'
ORDER BY qim.inspection_end_time DESC, qim.create_time DESC
),
industry_judge AS (
SELECT TOP 1 SELECT TOP 1
po.product_order_id AS productOrderId, po.product_order_id AS productOrderId,
po.order_code AS orderCode, po.order_code AS orderCode,
po.material_id AS materialId, po.material_id AS materialId,
po.tenant_id AS tenant_id,
CASE CASE
WHEN ISNULL(RTRIM(LTRIM(bmi.tire_markings)), '') &lt;&gt; '' THEN 'TIRE' WHEN ISNULL(RTRIM(LTRIM(bmi.tire_markings)), '') &lt;&gt; '' THEN 'TIRE'
ELSE 'JJ' ELSE 'JJ'
@ -28,13 +40,8 @@
LEFT JOIN base_material_info bmi LEFT JOIN base_material_info bmi
ON bmi.material_id = po.material_id ON bmi.material_id = po.material_id
AND bmi.del_flag = '0' AND bmi.del_flag = '0'
WHERE EXISTS ( INNER JOIN qc_match qm
SELECT 1 ON qm.productionOrder = po.order_code
FROM qc_inspection_main qim
WHERE qim.production_order = po.order_code
AND qim.batch_no = #{batchCode}
AND qim.del_flag = '0'
)
ORDER BY po.create_time DESC ORDER BY po.create_time DESC
) )
SELECT TOP 1 SELECT TOP 1
@ -53,6 +60,7 @@
FROM ( FROM (
<!-- 轮胎行业:从 _4 硫化明细表查找 --> <!-- 轮胎行业:从 _4 硫化明细表查找 -->
SELECT TOP 1 SELECT TOP 1
1 AS matchPriority,
ij.industryType AS industryType, ij.industryType AS industryType,
ij.productOrderId AS productOrderId, ij.productOrderId AS productOrderId,
ij.orderCode AS orderCode, ij.orderCode AS orderCode,
@ -82,6 +90,7 @@
<!-- 机加行业:从通用明细表查找 --> <!-- 机加行业:从通用明细表查找 -->
SELECT TOP 1 SELECT TOP 1
1 AS matchPriority,
ij.industryType AS industryType, ij.industryType AS industryType,
ij.productOrderId AS productOrderId, ij.productOrderId AS productOrderId,
ij.orderCode AS orderCode, ij.orderCode AS orderCode,
@ -106,8 +115,74 @@
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode})) OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
) )
ORDER BY d.real_end_time DESC, d.create_time DESC ORDER BY d.real_end_time DESC, d.create_time DESC
UNION ALL
<!-- 轮胎行业兜底:直接按成品条码命中硫化分表,兼容真实库未完整回填订单的情况 -->
SELECT TOP 1
2 AS matchPriority,
'TIRE' AS industryType,
p.product_order_id AS productOrderId,
po.order_code AS orderCode,
COALESCE(po.material_id, p.material_id) AS materialId,
#{batchCode} AS batchCode,
p.plan_id AS finalPlanId,
p.plan_code AS finalPlanCode,
d.plan_detail_id AS finalPlanDetailId,
d.plan_detail_code AS finalPlanDetailCode,
d.material_barcode AS materialBarcode,
d.return_barcode AS returnBarcode,
d.real_end_time AS productionDate
FROM prod_plan_info_4 p
INNER JOIN prod_product_plan_detail_4 d
ON d.plan_id = p.plan_id
AND ISNULL(d.del_flag, '0') = '0'
LEFT JOIN prod_order_info po
ON po.product_order_id = p.product_order_id
LEFT JOIN base_material_info bmi
ON bmi.material_id = COALESCE(po.material_id, p.material_id)
AND bmi.del_flag = '0'
WHERE (
RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND ISNULL(RTRIM(LTRIM(bmi.tire_markings)), '') &lt;&gt; ''
ORDER BY d.real_end_time DESC, d.create_time DESC
UNION ALL
<!-- 机加行业兜底:直接按成品条码命中通用分表 -->
SELECT TOP 1
2 AS matchPriority,
'JJ' AS industryType,
p.product_order_id AS productOrderId,
po.order_code AS orderCode,
COALESCE(po.material_id, p.material_id) AS materialId,
#{batchCode} AS batchCode,
p.plan_id AS finalPlanId,
p.plan_code AS finalPlanCode,
d.plan_detail_id AS finalPlanDetailId,
d.plan_detail_code AS finalPlanDetailCode,
d.material_barcode AS materialBarcode,
d.return_barcode AS returnBarcode,
d.real_end_time AS productionDate
FROM prod_plan_info p
INNER JOIN prod_product_plan_detail d
ON d.plan_id = p.plan_id
AND ISNULL(d.del_flag, '0') = '0'
LEFT JOIN prod_order_info po
ON po.product_order_id = p.product_order_id
LEFT JOIN base_material_info bmi
ON bmi.material_id = COALESCE(po.material_id, p.material_id)
AND bmi.del_flag = '0'
WHERE (
RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND ISNULL(RTRIM(LTRIM(bmi.tire_markings)), '') = ''
ORDER BY d.real_end_time DESC, d.create_time DESC
) anchor ) anchor
ORDER BY anchor.productionDate DESC ORDER BY anchor.matchPriority ASC, anchor.productionDate DESC
</select> </select>
<!-- ======================== 2. 成品信息查询 ======================== --> <!-- ======================== 2. 成品信息查询 ======================== -->
@ -138,7 +213,10 @@
) THEN N'已入库' ) THEN N'已入库'
WHEN EXISTS ( WHEN EXISTS (
SELECT 1 FROM qc_inspection_main qim SELECT 1 FROM qc_inspection_main qim
WHERE qim.batch_no = #{batchCode} WHERE (
RTRIM(LTRIM(ISNULL(qim.batch_no, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(qim.barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND qim.del_flag = '0' AND qim.del_flag = '0'
AND qim.result = '0' AND qim.result = '0'
) THEN N'已质检' ) THEN N'已质检'
@ -172,7 +250,10 @@
) THEN N'已入库' ) THEN N'已入库'
WHEN EXISTS ( WHEN EXISTS (
SELECT 1 FROM qc_inspection_main qim SELECT 1 FROM qc_inspection_main qim
WHERE qim.batch_no = #{batchCode} WHERE (
RTRIM(LTRIM(ISNULL(qim.batch_no, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(qim.barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND qim.del_flag = '0' AND qim.del_flag = '0'
AND qim.result = '0' AND qim.result = '0'
) THEN N'已质检' ) THEN N'已质检'
@ -203,14 +284,14 @@
c.address AS deliveryAddress, c.address AS deliveryAddress,
CONVERT(VARCHAR(19), COALESCE(wor.create_time, wso.audit_time), 120) AS outboundTime, CONVERT(VARCHAR(19), COALESCE(wor.create_time, wso.audit_time), 120) AS outboundTime,
CONVERT(VARCHAR(32), wor.outstock_qty) AS outboundQty, CONVERT(VARCHAR(32), wor.outstock_qty) AS outboundQty,
wso.outstock_code AS invoiceNo COALESCE(wso.outstock_code, wor.outstock_code) AS invoiceNo
FROM wms_outstock_record wor FROM wms_outstock_record wor
INNER JOIN wms_outstock_order wso LEFT JOIN wms_outstock_order wso
ON wso.outstock_id = wor.outstock_id ON wso.outstock_id = wor.outstock_id
LEFT JOIN wms_base_customer c LEFT JOIN wms_base_customer c
ON c.customer_id = wso.customer_id ON c.customer_id = COALESCE(wso.customer_id, wor.customer_id)
WHERE RTRIM(LTRIM(ISNULL(wor.batch_code, ''))) = RTRIM(LTRIM(#{batchCode})) WHERE RTRIM(LTRIM(ISNULL(wor.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
ORDER BY wor.create_time DESC ORDER BY COALESCE(wor.create_time, wso.audit_time) DESC
</select> </select>
<!-- ======================== 4. 成品质检主信息 ======================== --> <!-- ======================== 4. 成品质检主信息 ======================== -->
@ -218,7 +299,11 @@
SELECT TOP 1 SELECT TOP 1
qim.inspection_id AS inspectionId, qim.inspection_id AS inspectionId,
qim.inspection_no AS qcCode, qim.inspection_no AS qcCode,
qim.batch_no AS batchCode, COALESCE(
NULLIF(RTRIM(LTRIM(qim.batch_no)), ''),
NULLIF(RTRIM(LTRIM(qim.barcode)), ''),
#{batchCode}
) AS batchCode,
qim.material_code AS productCode, qim.material_code AS productCode,
qim.material_name AS productName, qim.material_name AS productName,
CAST(NULL AS VARCHAR(200)) AS spec, CAST(NULL AS VARCHAR(200)) AS spec,
@ -233,7 +318,10 @@
FROM qc_inspection_main qim FROM qc_inspection_main qim
LEFT JOIN qc_inspection_type qit LEFT JOIN qc_inspection_type qit
ON qit.type_id = qim.inspection_type AND qit.del_flag = '0' ON qit.type_id = qim.inspection_type AND qit.del_flag = '0'
WHERE qim.batch_no = #{batchCode} WHERE (
RTRIM(LTRIM(ISNULL(qim.batch_no, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(qim.barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
AND qim.del_flag = '0' AND qim.del_flag = '0'
ORDER BY qim.inspection_end_time DESC, qim.create_time DESC ORDER BY qim.inspection_end_time DESC, qim.create_time DESC
</select> </select>
@ -323,7 +411,8 @@
SELECT TOP 1 SELECT TOP 1
p4.product_order_id, p4.product_order_id,
p4.plan_id, p4.plan_id,
p4.plan_code p4.plan_code,
p4.tenant_id AS tenant_id
FROM prod_plan_info_4 p4 FROM prod_plan_info_4 p4
WHERE p4.product_order_id = #{productOrderId} WHERE p4.product_order_id = #{productOrderId}
ORDER BY p4.create_time DESC, p4.plan_id DESC ORDER BY p4.create_time DESC, p4.plan_id DESC
@ -537,7 +626,8 @@
pis.production_barcode, pis.production_barcode,
pis.input_barcode, pis.input_barcode,
pis.materiel_id, pis.materiel_id,
pis.feeding_time pis.feeding_time,
pis.tenant_id
FROM prod_input_scan_info pis FROM prod_input_scan_info pis
WHERE RTRIM(LTRIM(ISNULL(pis.production_barcode, ''))) = RTRIM(LTRIM(#{productionBarcode})) WHERE RTRIM(LTRIM(ISNULL(pis.production_barcode, ''))) = RTRIM(LTRIM(#{productionBarcode}))
), ),
@ -545,12 +635,19 @@
SELECT SELECT
qim.inspection_id, qim.inspection_id,
qim.inspection_no, qim.inspection_no,
qim.batch_no, COALESCE(
NULLIF(RTRIM(LTRIM(qim.batch_no)), ''),
NULLIF(RTRIM(LTRIM(qim.barcode)), '')
) AS traceBatch,
qim.material_code, qim.material_code,
qim.supplier_name, qim.supplier_name,
qim.result, qim.result,
qim.tenant_id,
ROW_NUMBER() OVER ( ROW_NUMBER() OVER (
PARTITION BY qim.batch_no, qim.material_code PARTITION BY COALESCE(
NULLIF(RTRIM(LTRIM(qim.batch_no)), ''),
NULLIF(RTRIM(LTRIM(qim.barcode)), '')
), qim.material_code
ORDER BY qim.inspection_end_time DESC, qim.create_time DESC ORDER BY qim.inspection_end_time DESC, qim.create_time DESC
) AS rn ) AS rn
FROM qc_inspection_main qim FROM qc_inspection_main qim
@ -560,9 +657,10 @@
SELECT SELECT
wor.batch_code, wor.batch_code,
wor.material_id, wor.material_id,
wor.tenant_id,
SUM(wor.outstock_qty) AS outstock_qty SUM(wor.outstock_qty) AS outstock_qty
FROM wms_outstock_record wor FROM wms_outstock_record wor
GROUP BY wor.batch_code, wor.material_id GROUP BY wor.batch_code, wor.material_id, wor.tenant_id
) )
SELECT SELECT
ir.production_barcode AS productionBarcode, ir.production_barcode AS productionBarcode,
@ -572,7 +670,7 @@
ir.input_barcode AS batchCode, ir.input_barcode AS batchCode,
ql.supplier_name AS supplier, ql.supplier_name AS supplier,
CONVERT(VARCHAR(32), os.outstock_qty) AS qty, CONVERT(VARCHAR(32), os.outstock_qty) AS qty,
mu.unit_name AS unit, COALESCE(mu.unit_name, bmi.material_unit) AS unit,
CONVERT(VARCHAR(19), ir.feeding_time, 120) AS inTime, CONVERT(VARCHAR(19), ir.feeding_time, 120) AS inTime,
ql.inspection_no AS qcCode, ql.inspection_no AS qcCode,
CASE ql.result CASE ql.result
@ -585,9 +683,10 @@
LEFT JOIN base_material_info bmi LEFT JOIN base_material_info bmi
ON bmi.material_id = ir.materiel_id AND bmi.del_flag = '0' ON bmi.material_id = ir.materiel_id AND bmi.del_flag = '0'
LEFT JOIN base_measurement_unit_info mu LEFT JOIN base_measurement_unit_info mu
ON mu.unit_id = bmi.unit_id -- 这里优先按物料主数据真实字段 material_unit_id 关联,避免旧口径 unit_id 在当前库中不存在导致 SQL 失败
ON mu.unit_id = bmi.material_unit_id
LEFT JOIN qc_latest ql LEFT JOIN qc_latest ql
ON ql.batch_no = ir.input_barcode ON ql.traceBatch = RTRIM(LTRIM(ir.input_barcode))
AND ql.material_code = bmi.material_code AND ql.material_code = bmi.material_code
AND ql.rn = 1 AND ql.rn = 1
LEFT JOIN outstock_sum os LEFT JOIN outstock_sum os

@ -0,0 +1,599 @@
<?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="org.dromara.mes.mapper.ReverseTraceMapper">
<!-- ======================== 1. 行业判定 + 成品锚点定位 ======================== -->
<!--
严格按《密炼.txt》4.1 + 4.2 执行:
1. 先通过 prod_order_info + base_material_info.tire_markings + qc_inspection_main.production_order 判定行业
2. 再按已判定行业去对应成品明细表定位锚点
为什么这里恢复成“两阶段”:
文档要求先基于生产订单和质检主表确定行业,而不是直接根据成品分表是否命中来反推。
这样可以避免“轮胎单据但 _4 未命中时被误判成机加”的口径偏差。
-->
<select id="selectAnchor" resultType="java.util.Map">
WITH industry_judge AS (
SELECT TOP 1
po.product_order_id AS productOrderId,
po.order_code AS orderCode,
po.material_id AS materialId,
CASE
WHEN ISNULL(RTRIM(LTRIM(bmi.tire_markings)), '') &lt;&gt; '' THEN 'TIRE'
ELSE 'JJ'
END AS industryType
FROM prod_order_info po
LEFT JOIN base_material_info bmi
ON bmi.material_id = po.material_id
AND bmi.del_flag = '0'
WHERE EXISTS (
SELECT 1
FROM qc_inspection_main qim
WHERE qim.production_order = po.order_code
AND qim.batch_no = #{batchCode}
AND qim.del_flag = '0'
)
ORDER BY po.create_time DESC
)
SELECT TOP 1
anchor.industryType,
anchor.productOrderId,
anchor.orderCode,
anchor.materialId,
anchor.batchCode,
anchor.finalPlanId,
anchor.finalPlanCode,
anchor.finalPlanDetailId,
anchor.finalPlanDetailCode,
anchor.materialBarcode,
anchor.returnBarcode,
anchor.productionDate
FROM (
<!-- 轮胎行业:从 _4 硫化明细表查找 -->
SELECT TOP 1
ij.industryType AS industryType,
ij.productOrderId AS productOrderId,
ij.orderCode AS orderCode,
ij.materialId AS materialId,
#{batchCode} AS batchCode,
p.plan_id AS finalPlanId,
p.plan_code AS finalPlanCode,
d.plan_detail_id AS finalPlanDetailId,
d.plan_detail_code AS finalPlanDetailCode,
d.material_barcode AS materialBarcode,
d.return_barcode AS returnBarcode,
d.real_end_time AS productionDate
FROM prod_plan_info_4 p
INNER JOIN industry_judge ij
ON ij.productOrderId = p.product_order_id
AND ij.industryType = 'TIRE'
INNER JOIN prod_product_plan_detail_4 d
ON d.plan_id = p.plan_id
AND ISNULL(d.del_flag, '0') = '0'
WHERE (
RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
ORDER BY d.real_end_time DESC, d.create_time DESC
UNION ALL
<!-- 机加行业:从通用明细表查找 -->
SELECT TOP 1
ij.industryType AS industryType,
ij.productOrderId AS productOrderId,
ij.orderCode AS orderCode,
ij.materialId AS materialId,
#{batchCode} AS batchCode,
p.plan_id AS finalPlanId,
p.plan_code AS finalPlanCode,
d.plan_detail_id AS finalPlanDetailId,
d.plan_detail_code AS finalPlanDetailCode,
d.material_barcode AS materialBarcode,
d.return_barcode AS returnBarcode,
d.real_end_time AS productionDate
FROM prod_plan_info p
INNER JOIN industry_judge ij
ON ij.productOrderId = p.product_order_id
AND ij.industryType = 'JJ'
INNER JOIN prod_product_plan_detail d
ON d.plan_id = p.plan_id
AND ISNULL(d.del_flag, '0') = '0'
WHERE (
RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
)
ORDER BY d.real_end_time DESC, d.create_time DESC
) anchor
ORDER BY anchor.productionDate DESC
</select>
<!-- ======================== 2. 成品信息查询 ======================== -->
<!--
成品状态映射优先级:已出库 > 已入库 > 已质检 > 生产完成
已出库wms_outstock_record 中有该批次码的记录
已入库wms_instock_record 中有该批次码的记录
已质检qc_inspection_main 中有该批次码且 result='0'(合格)的记录
其他:默认生产完成
-->
<select id="selectProductInfo" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$ProductInfo">
<!-- 轮胎行业 -->
<if test="industryType == 'TIRE'">
SELECT TOP 1
#{batchCode} AS batchCode,
bmi.material_code AS productCode,
bmi.material_name AS productName,
bmi.material_spec AS spec,
CONVERT(VARCHAR(19), d.real_end_time, 120) AS productionDate,
CASE
WHEN EXISTS (
SELECT 1 FROM wms_outstock_record wor
WHERE RTRIM(LTRIM(ISNULL(wor.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
) THEN N'已出库'
WHEN EXISTS (
SELECT 1 FROM wms_instock_record wir
WHERE RTRIM(LTRIM(ISNULL(wir.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
) THEN N'已入库'
WHEN EXISTS (
SELECT 1 FROM qc_inspection_main qim
WHERE qim.batch_no = #{batchCode}
AND qim.del_flag = '0'
AND qim.result = '0'
) THEN N'已质检'
ELSE N'生产完成'
END AS status
FROM prod_plan_info_4 p
INNER JOIN prod_product_plan_detail_4 d
ON d.plan_id = p.plan_id AND ISNULL(d.del_flag, '0') = '0'
LEFT JOIN base_material_info bmi
ON bmi.material_id = p.material_id AND bmi.del_flag = '0'
WHERE RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
ORDER BY d.real_end_time DESC, d.create_time DESC
</if>
<!-- 机加行业 -->
<if test="industryType != 'TIRE'">
SELECT TOP 1
#{batchCode} AS batchCode,
bmi.material_code AS productCode,
bmi.material_name AS productName,
bmi.material_spec AS spec,
CONVERT(VARCHAR(19), d.real_end_time, 120) AS productionDate,
CASE
WHEN EXISTS (
SELECT 1 FROM wms_outstock_record wor
WHERE RTRIM(LTRIM(ISNULL(wor.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
) THEN N'已出库'
WHEN EXISTS (
SELECT 1 FROM wms_instock_record wir
WHERE RTRIM(LTRIM(ISNULL(wir.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
) THEN N'已入库'
WHEN EXISTS (
SELECT 1 FROM qc_inspection_main qim
WHERE qim.batch_no = #{batchCode}
AND qim.del_flag = '0'
AND qim.result = '0'
) THEN N'已质检'
ELSE N'生产完成'
END AS status
FROM prod_plan_info p
INNER JOIN prod_product_plan_detail d
ON d.plan_id = p.plan_id AND ISNULL(d.del_flag, '0') = '0'
LEFT JOIN base_material_info bmi
ON bmi.material_id = p.material_id AND bmi.del_flag = '0'
WHERE RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
ORDER BY d.real_end_time DESC, d.create_time DESC
</if>
</select>
<!-- ======================== 3. 客户信息查询 ======================== -->
<!--
仅当存在出库记录时返回数据。
关联链路wms_outstock_record -> wms_outstock_order -> wms_base_customer
-->
<select id="selectCustomerInfo" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$CustomerData">
SELECT TOP 1
c.customer_code AS customerCode,
c.customer_name AS customerName,
c.contact_person AS contactPerson,
c.contact_phone AS contactPhone,
c.address AS deliveryAddress,
CONVERT(VARCHAR(19), COALESCE(wor.create_time, wso.audit_time), 120) AS outboundTime,
CONVERT(VARCHAR(32), wor.outstock_qty) AS outboundQty,
wso.outstock_code AS invoiceNo
FROM wms_outstock_record wor
INNER JOIN wms_outstock_order wso
ON wso.outstock_id = wor.outstock_id
LEFT JOIN wms_base_customer c
ON c.customer_id = wso.customer_id
WHERE RTRIM(LTRIM(ISNULL(wor.batch_code, ''))) = RTRIM(LTRIM(#{batchCode}))
ORDER BY wor.create_time DESC
</select>
<!-- ======================== 4. 成品质检主信息 ======================== -->
<select id="selectQcInfo" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$QcInfo">
SELECT TOP 1
qim.inspection_id AS inspectionId,
qim.inspection_no AS qcCode,
qim.batch_no AS batchCode,
qim.material_code AS productCode,
qim.material_name AS productName,
CAST(NULL AS VARCHAR(200)) AS spec,
CONVERT(VARCHAR(19), qim.inspection_end_time, 120) AS qcTime,
qit.type_name AS qcType,
qim.inspector AS inspector,
CASE qim.result
WHEN '0' THEN N'合格'
WHEN '1' THEN N'不合格'
ELSE N'未判定'
END AS result
FROM qc_inspection_main qim
LEFT JOIN qc_inspection_type qit
ON qit.type_id = qim.inspection_type AND qit.del_flag = '0'
WHERE qim.batch_no = #{batchCode}
AND qim.del_flag = '0'
ORDER BY qim.inspection_end_time DESC, qim.create_time DESC
</select>
<!-- ======================== 5. 质检明细查询 ======================== -->
<!--
标准值展示逻辑:
- detect_type='1'(计量型): 展示"下限 ~ 上限"
- 其他(计数型/文本型): 展示 spec_inspection 或 spec_name
-->
<select id="selectQcCheckItems" resultType="org.dromara.mes.domain.vo.TraceQcCheckItemVo">
SELECT
qir.item_name AS itemName,
CASE
WHEN qir.detect_type = '1' THEN
CONCAT(
ISNULL(CONVERT(VARCHAR(50), qir.lower_limit), ''),
' ~ ',
ISNULL(CONVERT(VARCHAR(50), qir.upper_limit), '')
)
ELSE
ISNULL(qir.spec_inspection, qir.spec_name)
END AS standard,
CASE
WHEN qir.detect_type = '1' THEN CONVERT(VARCHAR(50), qir.detect_value)
ELSE ISNULL(qir.problem_detail, N'')
END AS actual,
CASE qir.detect_result
WHEN '0' THEN N'合格'
WHEN '1' THEN N'不合格'
ELSE N'未判定'
END AS result
FROM qc_inspection_result qir
WHERE qir.inspection_id = #{inspectionId}
AND qir.del_flag = '0'
ORDER BY qir.create_time ASC
</select>
<!-- ======================== 6. 生产订单信息 ======================== -->
<select id="selectProductionOrder" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$ProductionOrder">
SELECT
po.order_code AS orderCode,
#{batchCode} AS batchCode,
bmi.material_name AS productName,
po.dispatch_type AS dispatchType,
CASE po.dispatch_type
WHEN '1' THEN N'产线'
WHEN '2' THEN N'工艺路线'
WHEN '3' THEN N'工序'
ELSE po.dispatch_type
END AS dispatchTypeName,
<!-- 派工信息:根据派工类型关联不同表获取名称 -->
CASE po.dispatch_type
WHEN '1' THEN (SELECT TOP 1 pli.prod_line_name FROM prod_base_prod_line_info pli WHERE pli.prod_line_id = po.dispatch_id)
WHEN '2' THEN (SELECT TOP 1 rt.route_name FROM prod_base_route rt WHERE rt.route_id = po.dispatch_id)
WHEN '3' THEN (SELECT TOP 1 pi.process_name FROM prod_base_process_info pi WHERE pi.process_id = po.dispatch_id)
ELSE CONVERT(VARCHAR(64), po.dispatch_id)
END AS dispatchInfo,
CONVERT(VARCHAR(32), po.plan_amount) AS planQty,
CONVERT(VARCHAR(32), po.dispatch_amount) AS dispatchedQty,
CONVERT(VARCHAR(32), po.complete_amount) AS completedQty,
CONVERT(VARCHAR(19), po.real_begin_time, 120) AS startTime,
CONVERT(VARCHAR(19), po.real_end_time, 120) AS endTime,
CASE po.order_status
WHEN '2' THEN N'已完成'
WHEN '3' THEN N'已开始'
WHEN '1' THEN N'已发布'
WHEN '4' THEN N'暂停'
ELSE N'待处理'
END AS status
FROM prod_order_info po
LEFT JOIN base_material_info bmi
ON bmi.material_id = po.material_id AND bmi.del_flag = '0'
WHERE po.product_order_id = #{productOrderId}
</select>
<!-- ======================== 7. 生产工单列表 - 轮胎行业 ======================== -->
<!--
为什么使用 UNION ALL 四张表:
轮胎行业按工序分为密炼(_1)、半制品(_2)、成型(_3)、硫化(_4) 四张表。
同一 product_order_id 下可能有多道工序的工单,需要汇总展示。
Service 层已经通过锚点查到了 product_order_id这里直接按订单过滤
可以避免再次按 batchCode 扫描硫化明细表,减少一次无效回表。
-->
<select id="selectWorkOrderListTire" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$WorkOrder">
WITH final_plan AS (
SELECT TOP 1
p4.product_order_id,
p4.plan_id,
p4.plan_code
FROM prod_plan_info_4 p4
WHERE p4.product_order_id = #{productOrderId}
ORDER BY p4.create_time DESC, p4.plan_id DESC
)
SELECT * FROM (
<!-- 密炼 _1 -->
SELECT
p1.process_order AS processSeq,
p1.plan_code AS workOrderCode,
bp.process_code AS processCode,
bp.process_name AS processName,
CONVERT(VARCHAR(64), p1.release_id) AS machineNo,
bm.machine_name AS machineName,
CONVERT(VARCHAR(19), COALESCE(MAX(d1.real_begin_time), p1.real_begin_time), 120) AS startTime,
CONVERT(VARCHAR(19), COALESCE(MAX(d1.real_end_time), p1.real_end_time), 120) AS endTime,
MAX(d1.user_name) AS worker,
CASE COALESCE(MAX(d1.plan_detail_status), p1.plan_status)
WHEN '3' THEN N'已完成'
WHEN '2' THEN N'已开始'
WHEN '1' THEN N'未开始'
ELSE N'待处理'
END AS status,
p1.plan_id AS planId,
'TIRE' AS industryType
FROM final_plan fp
INNER JOIN prod_plan_info_1 p1 ON p1.product_order_id = fp.product_order_id
LEFT JOIN prod_product_plan_detail_1 d1 ON d1.plan_id = p1.plan_id AND ISNULL(d1.del_flag, '0') = '0'
LEFT JOIN prod_base_process_info bp ON bp.process_id = p1.process_id
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = p1.release_id
GROUP BY p1.process_order, p1.plan_code, bp.process_code, bp.process_name,
p1.release_id, bm.machine_name, p1.real_begin_time, p1.real_end_time, p1.plan_status, p1.plan_id
UNION ALL
<!-- 半制品 _2 -->
SELECT
p2.process_order AS processSeq,
p2.plan_code AS workOrderCode,
bp.process_code AS processCode,
bp.process_name AS processName,
CONVERT(VARCHAR(64), p2.release_id) AS machineNo,
bm.machine_name AS machineName,
CONVERT(VARCHAR(19), COALESCE(MAX(d2.real_begin_time), p2.real_begin_time), 120) AS startTime,
CONVERT(VARCHAR(19), COALESCE(MAX(d2.real_end_time), p2.real_end_time), 120) AS endTime,
MAX(d2.user_name) AS worker,
CASE COALESCE(MAX(d2.plan_detail_status), p2.plan_status)
WHEN '3' THEN N'已完成'
WHEN '2' THEN N'已开始'
WHEN '1' THEN N'未开始'
ELSE N'待处理'
END AS status,
p2.plan_id AS planId,
'TIRE' AS industryType
FROM final_plan fp
INNER JOIN prod_plan_info_2 p2 ON p2.product_order_id = fp.product_order_id
LEFT JOIN prod_product_plan_detail_2 d2 ON d2.plan_id = p2.plan_id AND ISNULL(d2.del_flag, '0') = '0'
LEFT JOIN prod_base_process_info bp ON bp.process_id = p2.process_id
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = p2.release_id
GROUP BY p2.process_order, p2.plan_code, bp.process_code, bp.process_name,
p2.release_id, bm.machine_name, p2.real_begin_time, p2.real_end_time, p2.plan_status, p2.plan_id
UNION ALL
<!-- 成型 _3 -->
SELECT
p3.process_order AS processSeq,
p3.plan_code AS workOrderCode,
bp.process_code AS processCode,
bp.process_name AS processName,
CONVERT(VARCHAR(64), p3.release_id) AS machineNo,
bm.machine_name AS machineName,
CONVERT(VARCHAR(19), COALESCE(MAX(d3.real_begin_time), p3.real_begin_time), 120) AS startTime,
CONVERT(VARCHAR(19), COALESCE(MAX(d3.real_end_time), p3.real_end_time), 120) AS endTime,
MAX(d3.user_name) AS worker,
CASE COALESCE(MAX(d3.plan_detail_status), p3.plan_status)
WHEN '3' THEN N'已完成'
WHEN '2' THEN N'已开始'
WHEN '1' THEN N'未开始'
ELSE N'待处理'
END AS status,
p3.plan_id AS planId,
'TIRE' AS industryType
FROM final_plan fp
INNER JOIN prod_plan_info_3 p3 ON p3.product_order_id = fp.product_order_id
LEFT JOIN prod_product_plan_detail_3 d3 ON d3.plan_id = p3.plan_id AND ISNULL(d3.del_flag, '0') = '0'
LEFT JOIN prod_base_process_info bp ON bp.process_id = p3.process_id
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = p3.release_id
GROUP BY p3.process_order, p3.plan_code, bp.process_code, bp.process_name,
p3.release_id, bm.machine_name, p3.real_begin_time, p3.real_end_time, p3.plan_status, p3.plan_id
UNION ALL
<!-- 硫化 _4 -->
SELECT
p4.process_order AS processSeq,
p4.plan_code AS workOrderCode,
bp.process_code AS processCode,
bp.process_name AS processName,
CONVERT(VARCHAR(64), p4.release_id) AS machineNo,
bm.machine_name AS machineName,
CONVERT(VARCHAR(19), COALESCE(MAX(d4.real_begin_time), p4.real_begin_time), 120) AS startTime,
CONVERT(VARCHAR(19), COALESCE(MAX(d4.real_end_time), p4.real_end_time), 120) AS endTime,
MAX(d4.user_name) AS worker,
CASE COALESCE(MAX(d4.plan_detail_status), p4.plan_status)
WHEN '3' THEN N'已完成'
WHEN '2' THEN N'已开始'
WHEN '1' THEN N'未开始'
ELSE N'待处理'
END AS status,
p4.plan_id AS planId,
'TIRE' AS industryType
FROM final_plan fp
INNER JOIN prod_plan_info_4 p4 ON p4.product_order_id = fp.product_order_id
LEFT JOIN prod_product_plan_detail_4 d4 ON d4.plan_id = p4.plan_id AND ISNULL(d4.del_flag, '0') = '0'
LEFT JOIN prod_base_process_info bp ON bp.process_id = p4.process_id
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = p4.release_id
GROUP BY p4.process_order, p4.plan_code, bp.process_code, bp.process_name,
p4.release_id, bm.machine_name, p4.real_begin_time, p4.real_end_time, p4.plan_status, p4.plan_id
) t
ORDER BY t.processSeq ASC, t.planId ASC
</select>
<!-- ======================== 8. 生产工单列表 - 机加行业 ======================== -->
<select id="selectWorkOrderListJj" resultType="org.dromara.mes.domain.vo.ReverseTraceVo$WorkOrder">
SELECT
p.process_order AS processSeq,
p.plan_code AS workOrderCode,
bp.process_code AS processCode,
bp.process_name AS processName,
CONVERT(VARCHAR(64), p.release_id) AS machineNo,
bm.machine_name AS machineName,
CONVERT(VARCHAR(19), COALESCE(d.real_begin_time, p.real_begin_time), 120) AS startTime,
CONVERT(VARCHAR(19), COALESCE(d.real_end_time, p.real_end_time), 120) AS endTime,
d.user_name AS worker,
CASE COALESCE(d.plan_detail_status, p.plan_status)
WHEN '3' THEN N'已完成'
WHEN '2' THEN N'已开始'
WHEN '1' THEN N'未开始'
ELSE N'待处理'
END AS status,
p.plan_id AS planId,
'JJ' AS industryType
FROM prod_plan_info p
INNER JOIN prod_product_plan_detail d
ON d.plan_id = p.plan_id AND ISNULL(d.del_flag, '0') = '0'
LEFT JOIN prod_base_process_info bp ON bp.process_id = p.process_id
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = p.release_id
WHERE RTRIM(LTRIM(ISNULL(d.material_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
OR RTRIM(LTRIM(ISNULL(d.return_barcode, ''))) = RTRIM(LTRIM(#{batchCode}))
ORDER BY p.process_order ASC, p.create_time ASC
</select>
<!-- ======================== 9. 查询工单产出条码 ======================== -->
<!--
根据行业类型查不同的明细表,获取该工单下的所有产出条码。
产出条码优先取 material_barcode为空则取 return_barcode。
-->
<select id="selectProductionBarcodes" resultType="java.lang.String">
<if test="industryType == 'TIRE'">
<if test="tableSuffix == '_1'">
SELECT COALESCE(NULLIF(RTRIM(LTRIM(d.material_barcode)), ''), RTRIM(LTRIM(d.return_barcode)))
FROM prod_product_plan_detail_1 d
WHERE d.plan_id = #{planId} AND ISNULL(d.del_flag, '0') = '0'
AND (ISNULL(RTRIM(LTRIM(d.material_barcode)), '') != '' OR ISNULL(RTRIM(LTRIM(d.return_barcode)), '') != '')
</if>
<if test="tableSuffix == '_2'">
SELECT COALESCE(NULLIF(RTRIM(LTRIM(d.material_barcode)), ''), RTRIM(LTRIM(d.return_barcode)))
FROM prod_product_plan_detail_2 d
WHERE d.plan_id = #{planId} AND ISNULL(d.del_flag, '0') = '0'
AND (ISNULL(RTRIM(LTRIM(d.material_barcode)), '') != '' OR ISNULL(RTRIM(LTRIM(d.return_barcode)), '') != '')
</if>
<if test="tableSuffix == '_3'">
SELECT COALESCE(NULLIF(RTRIM(LTRIM(d.material_barcode)), ''), RTRIM(LTRIM(d.return_barcode)))
FROM prod_product_plan_detail_3 d
WHERE d.plan_id = #{planId} AND ISNULL(d.del_flag, '0') = '0'
AND (ISNULL(RTRIM(LTRIM(d.material_barcode)), '') != '' OR ISNULL(RTRIM(LTRIM(d.return_barcode)), '') != '')
</if>
<if test="tableSuffix == '_4'">
SELECT COALESCE(NULLIF(RTRIM(LTRIM(d.material_barcode)), ''), RTRIM(LTRIM(d.return_barcode)))
FROM prod_product_plan_detail_4 d
WHERE d.plan_id = #{planId} AND ISNULL(d.del_flag, '0') = '0'
AND (ISNULL(RTRIM(LTRIM(d.material_barcode)), '') != '' OR ISNULL(RTRIM(LTRIM(d.return_barcode)), '') != '')
</if>
</if>
<if test="industryType != 'TIRE'">
SELECT COALESCE(NULLIF(RTRIM(LTRIM(d.material_barcode)), ''), RTRIM(LTRIM(d.return_barcode)))
FROM prod_product_plan_detail d
WHERE d.plan_id = #{planId} AND ISNULL(d.del_flag, '0') = '0'
AND (ISNULL(RTRIM(LTRIM(d.material_barcode)), '') != '' OR ISNULL(RTRIM(LTRIM(d.return_barcode)), '') != '')
</if>
</select>
<!-- ======================== 10. 原材料投料信息查询 ======================== -->
<!--
核心条码链路:
产出条码 -> prod_input_scan_info.production_barcode -> input_barcode -> 关联原材料
三层关联:
1. input_record: 从扫描表获取投入记录
2. qc_latest: 取每条投入批次最新的质检记录ROW_NUMBER 窗口函数)
3. outstock_sum: 按批次 + 物料汇总出库数量,避免分批领料时数量被最后一笔覆盖
为什么使用 CTE
质检记录按 batch_no + material_code 分组取最新,出库记录按批次和物料汇总,
CTE 使复杂聚合逻辑更清晰可读。
-->
<select id="selectMaterialInputs" resultType="org.dromara.mes.domain.vo.TraceMaterialInputVo">
WITH input_record AS (
SELECT
pis.prod_input_scan_info_id,
pis.production_barcode,
pis.input_barcode,
pis.materiel_id,
pis.feeding_time
FROM prod_input_scan_info pis
WHERE RTRIM(LTRIM(ISNULL(pis.production_barcode, ''))) = RTRIM(LTRIM(#{productionBarcode}))
),
qc_latest AS (
SELECT
qim.inspection_id,
qim.inspection_no,
qim.batch_no,
qim.material_code,
qim.supplier_name,
qim.result,
ROW_NUMBER() OVER (
PARTITION BY qim.batch_no, qim.material_code
ORDER BY qim.inspection_end_time DESC, qim.create_time DESC
) AS rn
FROM qc_inspection_main qim
WHERE qim.del_flag = '0'
),
outstock_sum AS (
SELECT
wor.batch_code,
wor.material_id,
SUM(wor.outstock_qty) AS outstock_qty
FROM wms_outstock_record wor
GROUP BY wor.batch_code, wor.material_id
)
SELECT
ir.production_barcode AS productionBarcode,
ir.prod_input_scan_info_id AS inputScanId,
bmi.material_code AS materialCode,
bmi.material_name AS materialName,
ir.input_barcode AS batchCode,
ql.supplier_name AS supplier,
CONVERT(VARCHAR(32), os.outstock_qty) AS qty,
mu.unit_name AS unit,
CONVERT(VARCHAR(19), ir.feeding_time, 120) AS inTime,
ql.inspection_no AS qcCode,
CASE ql.result
WHEN '0' THEN N'合格'
WHEN '1' THEN N'不合格'
ELSE N'未判定'
END AS qcResult,
ql.inspection_id AS inspectionId
FROM input_record ir
LEFT JOIN base_material_info bmi
ON bmi.material_id = ir.materiel_id AND bmi.del_flag = '0'
LEFT JOIN base_measurement_unit_info mu
ON mu.unit_id = bmi.unit_id
LEFT JOIN qc_latest ql
ON ql.batch_no = ir.input_barcode
AND ql.material_code = bmi.material_code
AND ql.rn = 1
LEFT JOIN outstock_sum os
ON os.batch_code = ir.input_barcode
AND os.material_id = ir.materiel_id
ORDER BY ir.feeding_time DESC, ir.prod_input_scan_info_id DESC
</select>
</mapper>
Loading…
Cancel
Save