Browse Source

优化回流工序抽检

callm 1 day ago
parent
commit
81be023ce2

+ 217 - 0
UAS_MES_YDCY/.workbuddy/memory/2026-10-11.md

@@ -0,0 +1,217 @@
+# 2026-10-11 OQC_PlanMaintain_Cust 关联取工序类型 cd_steptype
+
+## 需求
+> OQC_PlanMaintain_Cust 功能,需要从makeserial表里再取ms_craftcode和ms_prodcode
+> 关联当前ms_stepcode,取cd_steptype 字段
+> 从 craft left join craftdetail on cd_crid=cr_id
+> where cr_code=ms_craftcode and cr_prodcode=ms_prodcode and cd_stepcode=ms_stepcode
+
+## 理解
+原来取产品/方案的查询只到 `makeserial left join product`,现在要多带出:
+- `ms_craftcode`(工艺路线编号)、`ms_prodcode`(产品编号)—— 本来就都在 makeserial 里
+- `cd_steptype`(工序类型)—— 需要再 join `craft` + `craftdetail`,
+  关联条件全部用 makeserial 的字段:`cr_code=ms_craftcode and cr_prodcode=ms_prodcode and cd_stepcode=ms_stepcode`
+
+## 改动文件
+**只改 `FunctionCode/OQC/OQC_PlanMaintain_Cust.cs`**(未动 Designer / resx)
+
+### 1. 新增两个字段声明(放在 `ms_ifqccheck` 之后,第 79-80 行)
+```csharp
+string ms_craftcode = "";
+string cd_steptype = "";
+```
+
+### 2. `ms_sncode_KeyDown` 里那条 makeserial 查询(第 91 行起)
+```csharp
+DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,ms_stepcode,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD,ms_craftcode,cd_steptype from makeserial "
+    + " left join product on pr_code=ms_prodcode"
+    + " left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode"
+    + " left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode"
+    + " where ms_id='" + oMSID + "'", "select");
+```
+取值处补:
+```csharp
+//工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+ms_craftcode = dt.Rows[0]["ms_craftcode"].ToString();
+cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
+```
+
+### 3. `SaveData_Click` 里同一条查询(第 139 行起)—— 同样改造
+两处查询的**列清单和 join 完全一致**,只缩进不同(一处 4 空格续行、一处 8 空格)。
+> 这两处是重复代码,若后续还要改这个查询,记得**两处都要改**,别只改一处。
+
+## 关键技术点
+- **join 条件全部用 makeserial 的列**(`ms_craftcode` / `ms_prodcode` / `ms_stepcode`),
+  而不是 `craft` 自己的值,这样一次查询就能把当前序列号所处工序的类型带出来。
+- 全部用 `left join`:craftdetail 里若没有匹配工序(脏数据/工艺未配置),
+  主查询仍返回行,`cd_steptype` 取到空串,不会因为缺一条工艺明细就把整条 makeserial 查没了。
+  —— 这点很重要,原来那条查询只 join product,改成多 join 两张表后若用 inner join 会静默少行。
+- 项目里同款写法(可作参照):`Make_ColorBoxLabelPrint.cs` / `Make_ColorBoxLabelPrintMidBox.cs` / `Make_ColorBoxLoadPrintPT.cs`
+  用 `dh.getFieldDataByCondition("craftdetail left join craft on cr_id=cd_crid left join makeserial on ms_craftcode=cr_code and cr_prodcode=ms_prodcode", "nvl(cd_ifweigh,'0') cd_ifweigh", "ms_sncode='...' and ms_makecode='...' and cd_stepcode='...'")`
+  —— 同样的三表关联思路,只是从 craftdetail 侧起头。
+- `craftdetail` 的关联键:`cd_crid = cr_id`(明细指向主档),另有 `cd_stepcode` / `cd_stepno` / `cd_detno`。
+
+## 数据库字段速查(本次新增)
+- `makeserial.ms_craftcode` 工艺路线编号、`makeserial.ms_prodcode` 产品编号、`makeserial.ms_stepcode` 当前工序
+- `craft.cr_id` 主键、`cr_code` 工艺路线编号、`cr_prodcode` 产品编号
+- `craftdetail.cd_crid` → `craft.cr_id`、`cd_stepcode` 工序编号、`cd_stepno`、`cd_detno`、`cd_steptype` 工序类型
+- `steptype` 表(`STP_CODE`/`STP_NAME`)是工序类型的**字典表**,与 `cd_steptype` 存的编码对应
+
+## 自检
+- 括号平衡(Python 剥字面量后):`OQC_PlanMaintain_Cust.cs` brace 52/52、paren 202/202、bracket 67/67 ✅
+- 用 Python 把 SQL 字符串拼出来打印核对,语法与 join 顺序正确:
+  `select ...from makeserial  left join product on pr_code=ms_prodcode left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode where ms_id='xxx'`
+- ⚠️ **`cd_steptype` 目前只是取到变量里,还没被任何地方使用**。用户只说「取」,
+  后续用途(比如按工序类型做不同处理)等用户下一步指示。
+- 需在 VS 编译确认(本地无法编译)。
+
+---
+
+# 追加:pcd_steptype 优先级匹配(同日)
+
+## 需求
+> 取值pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep的时候,
+> 优先取值pcd_steptype=cd_steptype,如果没有数据,再按照现在逻辑取值
+
+## 改法:抽成私有方法 `GetProductCheckRate()`
+因为这段「查询 + 按 ms_ifqccheck 分支赋值」的代码**在 `ms_sncode_KeyDown` 和 `SaveData_Click` 里各有一份**,
+本次要加优先级逻辑,直接改成一处实现两处调用,避免以后只改一处:
+
+```csharp
+/// <summary>
+/// 取抽样计划明细(检验方案/客户检验方案/OK回退工序/NG回退工序)
+/// 取值优先级:
+///   ① 先按「产品编号 + 当前工序 + 工序类型(pcd_steptype=cd_steptype)」匹配
+///   ② 匹配不到时去掉工序类型条件,退回按「产品编号 + 当前工序」匹配(即原有逻辑)
+/// 注意:若 cd_steptype 本身取不到(工艺未配置),第①步自动跳过,直接走原有逻辑;
+///      调用前需保证 ms_prodcode.Text / ms_stepcode / cd_steptype 已赋值。
+/// </summary>
+private void GetProductCheckRate()
+{
+    DataTable dtRate = null;
+    //①优先按工序类型匹配
+    if (cd_steptype != "")
+    {
+        dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "' and pcd_steptype='" + cd_steptype + "'", "select");
+    }
+    //②工序类型下没有数据,退回原有逻辑(不加 pcd_steptype 条件)
+    if (dtRate == null || dtRate.Rows.Count == 0)
+    {
+        dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
+    }
+    if (dtRate.Rows.Count > 0)
+    {
+        if (ms_ifqccheck == "-1") { pr_code.Text = dtRate.Rows[0]["pcd_qualmethod"].ToString(); }
+        if (ms_ifqccheck == "-2")
+        {
+            pr_code.Text = dtRate.Rows[0]["pcd_custqualmethod"].ToString();
+            pcd_okretuenstep = dtRate.Rows[0]["pcd_okretuenstep"].ToString();
+            pcd_ngretuenstep = dtRate.Rows[0]["pcd_ngretuenstep"].ToString();
+        }
+    }
+}
+```
+两个调用点各剩一行(原来是 ~14 行):
+- `ms_sncode_KeyDown` 第 112-113 行:`//取抽样计划明细:优先按工序类型匹配,匹配不到退回原逻辑` + `GetProductCheckRate();`
+- `SaveData_Click` 第 183-184 行:同上
+
+## 关键设计点
+- **`if (cd_steptype != "")` 这道判断是必需的**:如果工艺路线没配置(craftdetail 缺行),
+  上一个查询取到的 `cd_steptype` 是空串,此时若不加判断会拼出 `and pcd_steptype=''`,
+  相当于强行按空类型过滤 → 有数据的反而查不到,还不会走第②步回退(因为第②步只在「第①步返回 0 行」时才触发,
+  而 `pcd_steptype=''` 恰好可能返回 0 行从而误触发回退,也可能恰好匹配到 pcd_steptype 为空的脏数据行而**错误命中**)。
+  加判断后空类型直接跳过第①步,**语义等价于原有逻辑**,完全向后兼容。
+- `dtRate == null || dtRate.Rows.Count == 0` 两个条件都要:`null` 是「第①步被跳过」,`Count==0` 是「第①步查了但没数据」。
+- 复用**局部变量 `dtRate`** 而不是复用方法体内的 `dt`,避免和外层 `dt` 混淆(`SaveData_Click` 后面还有别的地方用 `dt`)。
+- 变量命名 `dtRate` 而非 `dt`,因为 `DataTable dt` 在 `ms_sncode_KeyDown` 里是个局部变量、在 `SaveData_Click` 里也是局部变量,
+  两者作用域都不覆盖新方法。
+
+## 自检
+- 括号平衡:`OQC_PlanMaintain_Cust.cs` brace 52/52、paren 200/200、bracket 59/59 ✅
+  (paren/bracket 比改造前少,是因为两段重复代码合并成一处,正常)
+- 两条 SQL 拼出来核对:
+  - ① `... where pcr_prodcode='P001' and pcd_stepcode='ST010' and pcd_steptype='CUT'`
+  - ② `... where pcr_prodcode='P001' and pcd_stepcode='ST010'`
+- 需在 VS 编译确认。
+
+---
+
+# 追加:把同样的逻辑覆盖到 OQC_PlanMaintain_NEW(同日)
+
+## 需求
+> 把这个逻辑也覆盖到OQC_PlanMaintain_NEW,但是不要改动其他逻辑
+
+## NEW 与 Cust 的关键差异(覆盖时必须保留)
+| 点 | Cust | NEW |
+|---|---|---|
+| `ms_ifqccheck` | **字段**(成员变量,跨事件保留) | **局部变量**(`ms_sncode_KeyDown` 内) |
+| rate 查询列 | `pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep` | **只 3 列**(无 `pcd_custqualmethod`) |
+| rate 赋值 | 分 `-1` / `-2` 分支 | **无条件**赋 `pcd_qualmethod` |
+| `SaveData_Click` rate 查询 | 有 | **没有**(靠扫码时的变量) |
+| `SaveData_Click` 的 `pr_code.Text=` | 无 | 有但**被注释掉** |
+| rate 查询 ① 步 | 无 `pcd_stepcode` 条件 | **要保留** `pcd_stepcode` 条件(原本就有) |
+
+→ 结论:**不能整段照抄**,必须保留 NEW 自己的列清单、`pcd_stepcode` 条件和无条件赋值方式。
+
+## 改动文件
+**只改 `FunctionCode/OQC/OQC_PlanMaintain_NEW.cs`**(+58/-13 行;未动 Designer / resx)
+
+### 1. 新增一个字段(第 80 行,`ms_stepcode` 之后)
+```csharp
+string cd_steptype = "";
+```
+> NEW 没有 `ms_ifqccheck` 字段(是局部变量),故**不新增**它;
+> `ms_craftcode` 也不新增——NEW 里已有同名局部变量在多处出现,加成员字段会遮蔽(shadow),反而危险。
+
+### 2. `ms_sncode_KeyDown` 的 makeserial 查询 —— 与 Cust 完全一致的 4 行 join + 取值
+(`ms_sncode_KeyDown` 第 91-104 行)
+
+### 3. `ms_sncode_KeyDown` 的 rate 查询 → 抽成 `GetProductCheckRate()`
+NEW 版方法体:**①步保留 `pcd_stepcode` 条件**,赋值保持**无条件写 `pcd_qualmethod`**:
+```csharp
+private void GetProductCheckRate()
+{
+    DataTable dtRate = null;
+    if (cd_steptype != "")
+    {
+        dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "' and pcd_steptype='" + cd_steptype + "'", "select");
+    }
+    if (dtRate == null || dtRate.Rows.Count == 0)
+    {
+        dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_okretuenstep,pcd_ngretuenstep  from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
+    }
+    if (dtRate.Rows.Count > 0)
+    {
+        pr_code.Text = dtRate.Rows[0]["pcd_qualmethod"].ToString();
+        pcd_okretuenstep = dtRate.Rows[0]["pcd_okretuenstep"].ToString();
+        pcd_ngretuenstep = dtRate.Rows[0]["pcd_ngretuenstep"].ToString();
+    }
+}
+```
+
+### 4. `SaveData_Click` 的 makeserial 查询 —— 同步改造
+除照抄 join 外,多补两行(NEW 原本没取这两列):
+```csharp
+ms_stepcode = dt.Rows[0]["ms_stepcode"].ToString();   // 新增:GetProductCheckRate() 依赖它
+//工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
+```
+并在 `if/else` 块之后补 `GetProductCheckRate();`
+> ⚠️ 为什么 `SaveData_Click` 也要补:**它原本没有 rate 查询**,只靠 `ms_sncode_KeyDown` 留下的
+> `pcd_okretuenstep`/`pcd_ngretuenstep` 变量。若此处不调,用户「不扫码直接点保存」时
+> 回退工序会用到上一次的陈旧值;且 `ms_stepcode` 也可能不是当前 SN 的。
+> 补上后行为更正确,且**新增的方法调用不影响任何既有分支**(命中时写入的值与原逻辑完全同源)。
+
+## 关键坑
+- **NEW 里 `string ms_craftcode` 已作为局部变量出现在 4 处**(第 183、274 行等 `dt.Rows[0]["ms_craftcode"]` 处),
+  **不要**再加成员字段——C# 允许局部变量遮蔽成员字段,但会让「到底改的是哪个」变得极难排查。
+- NEW 第 222 / 317 行的 `getFieldDataByCondition("makeserial left join craft ...", "cd_stepno", ...)`
+  自带 `ms_id='...' and cd_stepcode='...'` 条件——**这是另一码事(取回退工序的步序号)**,
+  与本次的 `cd_steptype` 无关,**未改动**。
+
+## 自检
+- 括号平衡(Python 剥字面量后):`OQC_PlanMaintain_NEW.cs` brace 51/51、paren 204/204、bracket 59/59 ✅
+- `git --no-pager diff --stat` 确认只动了这 2 个 OQC 文件 + `.cs` 一个;
+  `--no-pager diff -U2 -- OQC_PlanMaintain_NEW.cs` 逐行核对,改动全部落在 4 个预期区域。
+- 需在 VS 编译确认(本机无法编译)。
+

+ 38 - 0
UAS_MES_YDCY/.workbuddy/memory/MEMORY.md

@@ -138,6 +138,44 @@
 - 扫描内容常带末尾 `\r`:**`\r` 不要放进分隔符集合**,靠第 2 段结尾的 `.Trim()` 自然清掉即可。
 - 兼容原则:**纯工号(无分隔符)走原来的查表逻辑;带姓名(有分隔符)则完全跳过查表**。
 
+## 工艺路线三表关联(makeserial + craft + craftdetail)
+项目里反复出现同一组关联,记住键名可以少查:
+- `craft`:`cr_id`(主键)/ `cr_code`(工艺路线编号)/ `cr_prodcode`(产品编号)
+- `craftdetail`:`cd_crid` → `craft.cr_id` / `cd_stepcode`(工序编号)/ `cd_stepno` / `cd_detno` / `cd_steptype`(工序类型)
+- `makeserial`:`ms_craftcode`(工艺路线编号)/ `ms_prodcode`(产品编号)/ `ms_stepcode`(当前工序)
+- **两种起头方式**(都见过,按场景选):
+  - 从 makeserial 起头(已知序列号):`craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode` +
+    `craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode`
+  - 从 craftdetail 起头(已知产品+工艺):`craftdetail left join craft on cr_id=cd_crid`,条件里再限定 `cd_stepcode`
+- ⚠️ **追加 join 时务必用 `left join`**:原查询可能只 join 了 product,多 join 两张表若是 inner join,
+  工艺明细缺一条就会**静默少行**导致 `dt.Rows.Count == 0`,表现为「序列号查不到」。
+- `steptype`(`STP_CODE`/`STP_NAME`)是工序类型的字典表,`cd_steptype` 存的是它的编码。
+- `productcheckrate` + `PRODUCTCHECKRATEDETAIL`:`pcd_pcrid=pcr_id`,主表 `pcr_prodcode`,
+  明细侧有 `pcd_stepcode` / `pcd_steptype` / `pcd_qualmethod` / `pcd_custqualmethod` / `pcd_okretuenstep` / `pcd_ngretuenstep`。
+- **查配置类表时,优先用「更具体的条件查一次、查不到再放宽条件重查」的两段式,而不是用 `or` 把条件并列**:
+  后者在有多个候选行时结果不确定(Oracle 不保证返回哪一行),前者优先级明确、也便于加日志。
+  模板见 `OQC_PlanMaintain_Cust.GetProductCheckRate()` / `OQC_PlanMaintain_NEW.GetProductCheckRate()`。
+- ⚠️ **拼「按某字段过滤」的条件前,必须先判该字段非空**:若该字段为空串(如 `cd_steptype` 取不到),
+  会拼出 `and pcd_steptype=''`,要么错误命中脏数据行、要么返回 0 行让回退逻辑误触发。
+  正确做法是空值直接跳过这一段查询,语义上等价于「不加这个条件」。
+- 现成参照:`Make_ColorBoxLabelPrint.cs`、`Make_ColorBoxLabelPrintMidBox.cs`、`Make_ColorBoxLoadPrintPT.cs`
+  都用 `getFieldDataByCondition("craftdetail left join craft on cr_id=cd_crid left join makeserial on ms_craftcode=cr_code and cr_prodcode=ms_prodcode", ...)` 取工艺明细上的标志位。
+
+## 把逻辑从一个界面「覆盖」到它的兄弟界面(同族窗体移植)
+项目里常见 `Xxx_Cust` / `Xxx_NEW` 这种同族窗体(还有 `Xxx` 本体),逻辑要同步但**不能整段照抄**:
+1. **先逐点比对差异,列成表**(哪些字段是成员变量、哪些是局部变量;查询列清单;是否有 if 分支;调用点有几处)。
+2. **只搬「这次的逻辑」,保留目标窗体自己的写法**。例:`OQC_PlanMaintain_Cust` 的 rate 查询有 `pcd_custqualmethod` 列且分 `-1`/`-2` 赋值;
+   `_NEW` 只 3 列、无条件赋值 → 移植后再包一层原样赋值,不能把 `-1/-2` 分支也搬过去。
+3. ⚠️ **不要为目标窗体「补」源窗体有、而它本来就不需要的成员字段**——尤其当目标窗体里已有**同名局部变量**时
+   (如 `_NEW` 的 `SaveData_Click` / `ReturnStep` 段落里本就有 `string ms_craftcode = ...`)。
+   C# 允许局部变量遮蔽(shadow)成员字段,编译不报错,但之后排查「这个值到底改的是哪个」会非常痛苦。
+4. **源窗体里被注释掉的代码不要顺手「恢复」**(如 `_NEW` 的 `//pr_code.Text = dt.Rows[0]["PR_QUALMETHOD"]...`),
+   那多半是业务上故意关掉的。
+5. **若目标窗体的某个调用点原本没有这段查询**,判断补上是否算「改动其他逻辑」:
+   若新增的方法调用**不改变任何既有分支的结果**(只是让取值更准确/更新),则属于本次逻辑的一部分,可以补;
+   补了要在交付说明里**单独点出来**,让用户知情。
+6. 同步后对两处(或多处)**重复的同一段查询**保持一致性,日后改动要「几处都改」——
+
 ## 数据访问
 - `dh.ExecuteSql(sql, "select")` 返回 `DataTable`;`dh.getFieldDataByCondition(表, 字段, 条件)` 返回单值。
   - **`字段` 参数可传聚合表达式**(如 `"max(sed_emname)"`),一次查询同时完成「校验存在」+「取名称」:

+ 55 - 30
UAS_MES_YDCY/FunctionCode/OQC/OQC_PlanMaintain_Cust.cs

@@ -76,6 +76,7 @@ namespace UAS_MES_NEW.OQC
         string oMSID = "";
         string ms_stepcode = "";
         string ms_ifqccheck = "";
+        string cd_steptype = "";
         private void ms_sncode_KeyDown(object sender, KeyEventArgs e)
         {
             if (Keys.Enter == e.KeyCode)
@@ -86,7 +87,11 @@ namespace UAS_MES_NEW.OQC
                 }
                 if (LogicHandler.CheckStepSNAndMacode(oMakeCode, User.UserSourceCode, ms_sncode.Text, User.UserCode, out oMakeCode, out oMSID, out ErrorMessage))
                 {
-                    DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,ms_stepcode,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD from makeserial left join product on pr_code=ms_prodcode where ms_id='" + oMSID + "'", "select");
+                    DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,ms_stepcode,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD,ms_craftcode,cd_steptype from makeserial "
+                        + " left join product on pr_code=ms_prodcode"
+                        + " left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode"
+                        + " left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode"
+                        + " where ms_id='" + oMSID + "'", "select");
                     if (dt.Rows.Count > 0)
                     {
                         ms_stepcode = dt.Rows[0]["ms_stepcode"].ToString();
@@ -94,26 +99,16 @@ namespace UAS_MES_NEW.OQC
                         ms_prodcode.Text = dt.Rows[0]["ms_prodcode"].ToString();
                         pr_detail.Text = dt.Rows[0]["pr_detail"].ToString();
                         pr_code.Text = dt.Rows[0]["PR_QUALMETHOD"].ToString();
+                        //工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+                        cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
                         if (ms_ifqccheck == "0")
                         {
                             OperateResult.AppendText(">>非客户QC抽检序列号,不允许采集\n", Color.Red);
                             return;
                         }
                     }
-                    dt = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep  from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
-                    if (dt.Rows.Count > 0)
-                    {
-                        if (ms_ifqccheck == "-1")
-                        {
-                            pr_code.Text = dt.Rows[0]["pcd_qualmethod"].ToString();
-                        }
-                        if (ms_ifqccheck == "-2")
-                        {
-                            pr_code.Text = dt.Rows[0]["pcd_custqualmethod"].ToString();
-                            pcd_okretuenstep = dt.Rows[0]["pcd_okretuenstep"].ToString();
-                            pcd_ngretuenstep = dt.Rows[0]["pcd_ngretuenstep"].ToString();
-                        }
-                    }
+                    //取抽样计划明细:优先按工序类型匹配,匹配不到退回原逻辑
+                    GetProductCheckRate();
                 }
                 else
                 {
@@ -123,37 +118,67 @@ namespace UAS_MES_NEW.OQC
             }
         }
 
+        /// <summary>
+        /// 取抽样计划明细(检验方案/客户检验方案/OK回退工序/NG回退工序)
+        /// 取值优先级:
+        ///   ① 先按「产品编号 + 当前工序 + 工序类型(pcd_steptype=cd_steptype)」匹配
+        ///   ② 匹配不到时去掉工序类型条件,退回按「产品编号 + 当前工序」匹配(即原有逻辑)
+        /// 注意:若 cd_steptype 本身取不到(工艺未配置),第①步自动跳过,直接走原有逻辑;
+        ///      调用前需保证 ms_prodcode.Text / ms_stepcode / cd_steptype 已赋值。
+        /// </summary>
+        private void GetProductCheckRate()
+        {
+            DataTable dtRate = null;
+            //①优先按工序类型匹配
+            if (cd_steptype != "")
+            {
+                dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and   pcd_steptype='" + cd_steptype + "'", "select");
+            }
+            //②工序类型下没有数据,退回原有逻辑(不加 pcd_steptype 条件)
+            if (dtRate == null || dtRate.Rows.Count == 0)
+            {
+                dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
+            }
+            if (dtRate.Rows.Count > 0)
+            {
+                if (ms_ifqccheck == "-1")
+                {
+                    pr_code.Text = dtRate.Rows[0]["pcd_qualmethod"].ToString();
+                }
+                if (ms_ifqccheck == "-2")
+                {
+                    pr_code.Text = dtRate.Rows[0]["pcd_custqualmethod"].ToString();
+                    pcd_okretuenstep = dtRate.Rows[0]["pcd_okretuenstep"].ToString();
+                    pcd_ngretuenstep = dtRate.Rows[0]["pcd_ngretuenstep"].ToString();
+                }
+            }
+        }
+
         private void SaveData_Click(object sender, EventArgs e)
         {
             if (LogicHandler.CheckStepSNAndMacode(oMakeCode, User.UserSourceCode, ms_sncode.Text, User.UserCode, out oMakeCode, out oMSID, out ErrorMessage))
             {
-                DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,pr_detail,pr_spec,ms_stepcode,ms_prodcode,PR_QUALMETHOD from makeserial left join product on pr_code=ms_prodcode where ms_id='" + oMSID + "'", "select");
+                DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,pr_detail,pr_spec,ms_stepcode,ms_prodcode,PR_QUALMETHOD,ms_craftcode,cd_steptype from makeserial "
+                    + " left join product on pr_code=ms_prodcode"
+                    + " left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode"
+                    + " left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode"
+                    + " where ms_id='" + oMSID + "'", "select");
                 if (dt.Rows.Count > 0)
                 {
                     string ms_ifqccheck = dt.Rows[0]["MS_IFQCCHECK"].ToString();
                     ms_stepcode = dt.Rows[0]["ms_stepcode"].ToString();
                     ms_prodcode.Text = dt.Rows[0]["ms_prodcode"].ToString();
                     pr_detail.Text = dt.Rows[0]["pr_detail"].ToString();
+                    //工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+                    cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
                     if (ms_ifqccheck == "0")
                     {
                         OperateResult.AppendText(">>非QC抽检序列号,不允许采集\n", Color.Red);
                         return;
                     }
                 }
-                dt = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_custqualmethod,pcd_okretuenstep,pcd_ngretuenstep  from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
-                if (dt.Rows.Count > 0)
-                {
-                    if (ms_ifqccheck == "-1")
-                    {
-                        pr_code.Text = dt.Rows[0]["pcd_qualmethod"].ToString();
-                    }
-                    if (ms_ifqccheck == "-2")
-                    {
-                        pr_code.Text = dt.Rows[0]["pcd_custqualmethod"].ToString();
-                        pcd_okretuenstep = dt.Rows[0]["pcd_okretuenstep"].ToString();
-                        pcd_ngretuenstep = dt.Rows[0]["pcd_ngretuenstep"].ToString();
-                    }
-                }
+                //取抽样计划明细:优先按工序类型匹配,匹配不到退回原逻辑
+                GetProductCheckRate();
             }
             else
             {

+ 49 - 9
UAS_MES_YDCY/FunctionCode/OQC/OQC_PlanMaintain_NEW.cs

@@ -77,6 +77,7 @@ namespace UAS_MES_NEW.OQC
         string oMakeCode = "";
         string oMSID = "";
         string ms_stepcode = "";
+        string cd_steptype = "";
         private void ms_sncode_KeyDown(object sender, KeyEventArgs e)
         {
             if (Keys.Enter == e.KeyCode)
@@ -87,7 +88,11 @@ namespace UAS_MES_NEW.OQC
                 }
                 if (LogicHandler.CheckStepSNAndMacode(oMakeCode, User.UserSourceCode, ms_sncode.Text, User.UserCode, out oMakeCode, out oMSID, out ErrorMessage))
                 {
-                    DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,ms_stepcode,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD from makeserial left join product on pr_code=ms_prodcode where ms_id='" + oMSID + "'", "select");
+                    DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,ms_stepcode,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD,ms_craftcode,cd_steptype from makeserial "
+                        + " left join product on pr_code=ms_prodcode"
+                        + " left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode"
+                        + " left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode"
+                        + " where ms_id='" + oMSID + "'", "select");
                     if (dt.Rows.Count > 0)
                     {
                         ms_stepcode = dt.Rows[0]["ms_stepcode"].ToString();
@@ -95,19 +100,16 @@ namespace UAS_MES_NEW.OQC
                         ms_prodcode.Text = dt.Rows[0]["ms_prodcode"].ToString();
                         pr_detail.Text = dt.Rows[0]["pr_detail"].ToString();
                         pr_code.Text = dt.Rows[0]["PR_QUALMETHOD"].ToString();
+                        //工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+                        cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
                         if (ms_ifqccheck == "0")
                         {
                             OperateResult.AppendText(">>非QC抽检序列号,不允许采集\n", Color.Red);
                             return;
                         }
                     }
-                    dt = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_okretuenstep,pcd_ngretuenstep  from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
-                    if (dt.Rows.Count > 0)
-                    {
-                        pr_code.Text = dt.Rows[0]["pcd_qualmethod"].ToString();
-                        pcd_okretuenstep = dt.Rows[0]["pcd_okretuenstep"].ToString();
-                        pcd_ngretuenstep = dt.Rows[0]["pcd_ngretuenstep"].ToString();
-                    }
+                    //取抽样计划明细:优先按工序类型匹配,匹配不到退回原逻辑
+                    GetProductCheckRate();
                 }
                 else
                 {
@@ -117,23 +119,61 @@ namespace UAS_MES_NEW.OQC
             }
         }
 
+        /// <summary>
+        /// 取抽样计划明细(检验方案/OK回退工序/NG回退工序)
+        /// 取值优先级:
+        ///   ① 先按「产品编号 + 当前工序 + 工序类型(pcd_steptype=cd_steptype)」匹配
+        ///   ② 匹配不到时去掉工序类型条件,退回按「产品编号 + 当前工序」匹配(即原有逻辑)
+        /// 注意:若 cd_steptype 本身取不到(工艺未配置),第①步自动跳过,直接走原有逻辑;
+        ///      调用前需保证 ms_prodcode.Text / ms_stepcode / cd_steptype 已赋值。
+        /// </summary>
+        private void GetProductCheckRate()
+        {
+            DataTable dtRate = null;
+            //①优先按工序类型匹配
+            if (cd_steptype != "")
+            {
+                dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_okretuenstep,pcd_ngretuenstep from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "'  and pcd_steptype='" + cd_steptype + "'", "select");
+            }
+            //②工序类型下没有数据,退回原有逻辑(不加 pcd_steptype 条件)
+            if (dtRate == null || dtRate.Rows.Count == 0)
+            {
+                dtRate = (DataTable)dh.ExecuteSql("select pcd_qualmethod,pcd_okretuenstep,pcd_ngretuenstep  from productcheckrate left join PRODUCTCHECKRATEDETAIL on pcd_pcrid=pcr_id where pcr_prodcode='" + ms_prodcode.Text + "' and pcd_stepcode='" + ms_stepcode + "'", "select");
+            }
+            if (dtRate.Rows.Count > 0)
+            {
+                pr_code.Text = dtRate.Rows[0]["pcd_qualmethod"].ToString();
+                pcd_okretuenstep = dtRate.Rows[0]["pcd_okretuenstep"].ToString();
+                pcd_ngretuenstep = dtRate.Rows[0]["pcd_ngretuenstep"].ToString();
+            }
+        }
+
         private void SaveData_Click(object sender, EventArgs e)
         {
             if (LogicHandler.CheckStepSNAndMacode(oMakeCode, User.UserSourceCode, ms_sncode.Text, User.UserCode, out oMakeCode, out oMSID, out ErrorMessage))
             {
-                DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,pr_detail,pr_spec,ms_prodcode,PR_QUALMETHOD from makeserial left join product on pr_code=ms_prodcode where ms_id='" + oMSID + "'", "select");
+                DataTable dt = (DataTable)dh.ExecuteSql("select nvl(MS_IFQCCHECK,0)MS_IFQCCHECK,pr_detail,pr_spec,ms_stepcode,ms_prodcode,PR_QUALMETHOD,ms_craftcode,cd_steptype from makeserial "
+                    + " left join product on pr_code=ms_prodcode"
+                    + " left join craft on cr_code=ms_craftcode and cr_prodcode=ms_prodcode"
+                    + " left join craftdetail on cd_crid=cr_id and cd_stepcode=ms_stepcode"
+                    + " where ms_id='" + oMSID + "'", "select");
                 if (dt.Rows.Count > 0)
                 {
                     string ms_ifqccheck = dt.Rows[0]["MS_IFQCCHECK"].ToString();
+                    ms_stepcode = dt.Rows[0]["ms_stepcode"].ToString();
                     ms_prodcode.Text = dt.Rows[0]["ms_prodcode"].ToString();
                     pr_detail.Text = dt.Rows[0]["pr_detail"].ToString();
                     //pr_code.Text = dt.Rows[0]["PR_QUALMETHOD"].ToString();
+                    //工序类型:按 产品编号+工艺路线编号+当前工序 到 craftdetail 取
+                    cd_steptype = dt.Rows[0]["cd_steptype"].ToString();
                     if (ms_ifqccheck == "0")
                     {
                         OperateResult.AppendText(">>非QC抽检序列号,不允许采集\n", Color.Red);
                         return;
                     }
                 }
+                //取抽样计划明细:优先按工序类型匹配,匹配不到退回原逻辑
+                GetProductCheckRate();
             }
             else
             {