Bläddra i källkod

超节损计算优化

unknown 5 dagar sedan
förälder
incheckning
562a8ccd10
1 ändrade filer med 220 tillägg och 109 borttagningar
  1. 220 109
      application/api/controller/OrderSuperLoss.php

+ 220 - 109
application/api/controller/OrderSuperLoss.php

@@ -316,18 +316,12 @@ class OrderSuperLoss extends Api
                 $gy_data[$k]['total_fp'] = $item['total_fp'] * $item['Gy0_ls'];
                 $gy_data[$k]['total_cl'] = $item['total_cl'] * $item['Gy0_ls'];
                 //无形损
-                if (floatval($item['无形损承担比例']) > 0){
-                    if (strpos($item['Gy0_gxmc'],'机检') === false){
-                        $gy_data[$k]['intangible_loss'] = round(number_format($gy_data[$k]['Gy0_计划损耗']/$plan_total,4) * $data['intangible_loss'] * floatval($item['无形损承担比例']));
-                    }else{
-                        $gy_data[$k]['intangible_loss'] = 0;
-                    }
-                }else{
-                    if (strpos($item['Gy0_gxmc'],'机检') === false){
-                        $gy_data[$k]['intangible_loss'] = round(number_format($gy_data[$k]['Gy0_计划损耗']/$plan_total,4) * $data['intangible_loss']);
-                    }else{
-                        $gy_data[$k]['intangible_loss'] = 0;
-                    }
+                if ($this->isSuperLossIntangibleExcluded((string)$item['Gy0_gxmc'])) {
+                    $gy_data[$k]['intangible_loss'] = 0;
+                } elseif (floatval($item['无形损承担比例']) > 0) {
+                    $gy_data[$k]['intangible_loss'] = round(number_format($gy_data[$k]['Gy0_计划损耗'] / $plan_total, 4) * $data['intangible_loss'] * floatval($item['无形损承担比例']));
+                } else {
+                    $gy_data[$k]['intangible_loss'] = round(number_format($gy_data[$k]['Gy0_计划损耗'] / $plan_total, 4) * $data['intangible_loss']);
                 }
                 foreach ($waste_out as $entry) {
                     for ($i = 1; $i <= 13; $i++) {
@@ -364,11 +358,13 @@ class OrderSuperLoss extends Api
                 }else{
                     $moneyRate = 0.09;
                 }
-                if (strpos($item['Gy0_gxmc'],'机检') !== false){
+                if (strpos($item['Gy0_gxmc'], '机检') !== false) {
                     $gy_data[$k]['工序超节损金额'] = '0.000000';
                     $gy_data[$k]['waste_quality'] = $data['机检废品'];
-                }else{
-                    $gy_data[$k]['工序超节损金额'] = number_format($gy_data[$k]['loss'] * $item['超节损承担比例'] * $moneyRate,4);
+                } elseif ($this->isSuperLossRewardExcluded((string)$item['Gy0_gxmc'])) {
+                    $gy_data[$k]['工序超节损金额'] = '0.000000';
+                } else {
+                    $gy_data[$k]['工序超节损金额'] = number_format($gy_data[$k]['loss'] * $item['超节损承担比例'] * $moneyRate, 4);
                 }
                 $total['plan_loss'] += $gy_data[$k]['Gy0_计划损耗'];
                 $total['total_fp'] += $gy_data[$k]['total_fp'];
@@ -487,10 +483,11 @@ class OrderSuperLoss extends Api
                     }
                 }
                 //无形损分摊
-                if ($plan_total >0){
-                    $gy_data[$key]['无形损分摊'] = round($gy_data[$key]['计划损耗'] / $plan_total * $data['intangible_loss']);
-                }else{
+                $teamGxName = (string)($gy_data[$key]['sczl_gxmc'] ?? $value['sczl_gxmc'] ?? '');
+                if ($this->isSuperLossIntangibleExcluded($teamGxName) || $plan_total <= 0) {
                     $gy_data[$key]['无形损分摊'] = 0;
+                } else {
+                    $gy_data[$key]['无形损分摊'] = round($gy_data[$key]['计划损耗'] / $plan_total * $data['intangible_loss']);
                 }
 
                 //班组成员
@@ -531,10 +528,10 @@ class OrderSuperLoss extends Api
                 }else{
                     $moneyRate = 0.09;
                 }
-                if (strpos($item['sczl_gxmc'],'机检') !== false){
+                if ($this->isSuperLossRewardExcluded((string)$item['sczl_gxmc'])) {
                     $gy_data[$i]['节损奖'] = 0;
-                }else{
-                    $gy_data[$i]['节损奖'] = number_format($gy_data[$i]['节损数量'] *  $item['超节损承担比例'] * $moneyRate,4);
+                } else {
+                    $gy_data[$i]['节损奖'] = number_format($gy_data[$i]['节损数量'] * $item['超节损承担比例'] * $moneyRate, 4);
                 }
                 $gy_data[$i]['超节损承担比例'] = number_format($item['超节损承担比例'],4) * 100 .'%';
 
@@ -1023,8 +1020,10 @@ class OrderSuperLoss extends Api
             ->where('jjcp_yjno', $param['yjno'])
             ->column('SUM(jjcp_sl)');
         //查询工单资料、投料数据
-        $field = 'a.Gd_gdbh,rtrim(a.成品代号) as 成品代号,rtrim(a.成品名称) as 成品名称,
-                  rtrim(a.销售订单号) as 销售订单号,a.订单数量,c.yj_ls as ls,c.yj_ks as ks,SUM(b.st_sl) as 投料,
+        $field = 'a.Gd_gdbh,rtrim(a.成品代号) as 成品代号,rtrim(a.成品名称) as 成品名称,rtrim(a.Gd_客户代号) as Gd_客户代号,
+                  rtrim(a.Gd_客户名称) as Gd_客户名称,rtrim(a.Gd_cpdh) as Gd_cpdh,rtrim(a.Gd_cpmc) as Gd_cpmc,
+                  rtrim(a.销售订单号) as 销售订单号,a.订单数量,a.投料大箱,rtrim(a.计量单位) as 计量单位,
+                  c.yj_ls as ls,c.yj_ks as ks,SUM(b.st_sl) as 投料,
                   b.st_dw,c.yj_zzmc,c.yj_tlgg,SUM(b.mod_number) as mod_num';
         $data = db('工单_基本资料')
             ->alias('a')
@@ -1188,34 +1187,33 @@ class OrderSuperLoss extends Api
             ->group('a.Gy0_gxh')
             ->order('a.Gy0_gxh')
             ->select();
+        $payrollTeamSub = $this->buildPayrollTeamAggSubSql();
         $sczlTeamSub = $this->buildSczlTeamAggSubSql();
+        $gxmcExpr = $this->processGxmcSqlExpr();
         //按班组分的产量数据
         $gy_data = db('工单_工艺资料')
             ->alias('a')
             ->field([
                 'a.Gy0_gdbh', 'a.Gy0_yjno', 'a.Gy0_gxh', 'a.Gy0_ls', 'a.Gy0_ks', 'a.Gy0_计划损耗', 'a.超节损承担比例',
-                'CONCAT(`a`.`Gy0_gxmc`, "【", a.Add_gxmc,"】") AS sczl_gxmc', 'SUM(b.sczl_zcfp) as sczl_zcfp', 'SUM(b.sczl_cl) as total_cl',
+                $gxmcExpr . ' AS sczl_gxmc', 'b.sczl_zcfp', 'b.total_cl',
                 'b.sczl_bh1', 'b.sczl_bh2', 'b.sczl_bh3', 'b.sczl_bh4', 'b.sczl_bh5', 'b.sczl_bh6',
-                'b.sczl_bh7', 'b.sczl_bh8', 'b.sczl_bh9', 'b.sczl_bh10', 'GROUP_CONCAT(DISTINCT b.sczl_num SEPARATOR ",") AS 流程单',
+                'b.sczl_bh7', 'b.sczl_bh8', 'b.sczl_bh9', 'b.sczl_bh10', 'b.流程单',
                 'b.sczl_bzdh', 'b.sczl_rate1', 'b.sczl_rate2', 'b.sczl_rate3', 'b.sczl_rate4', 'b.sczl_rate5',
                 'b.sczl_rate6', 'b.sczl_rate7', 'b.sczl_rate8', 'b.sczl_rate9', 'b.sczl_rate10',
                 'c.gxmc', 'c.cl', 'c.fp', 'c.sczl_bh1 as sczl_bh',
                 'd.使用部门 as 使用部门', 'c.bzdh', 'b.sczl_jtbh', 'a.损耗系数', 'a.Gy0_ms', 'a.Gy0_Rate0',
-                'a.Gy0_Rate1', 'a.无形损承担比例', 'a.Gy0_计划接货数*a.Gy0_ls*a.Gy0_ks as 计划产量', 'e.Gd_客户代号', 'e.Gd_客户名称',
-                'e.Gd_cpdh', 'e.Gd_cpmc', 'e.销售订单号', 'e.投料大箱', 'e.订单数量', 'e.计量单位', 'rtrim(a.Gy0_SITE) as Gy0_SITE',
-                'rtrim(a.印刷方式) as 印刷方式','rtrim(a.版距) as 版距'
+                'a.Gy0_Rate1', 'a.无形损承担比例', 'a.Gy0_计划接货数*a.Gy0_ls*a.Gy0_ks as 计划产量',
+                'rtrim(a.Gy0_SITE) as Gy0_SITE',
+                'rtrim(a.印刷方式) as 印刷方式', 'rtrim(a.版距) as 版距',
             ])
-            ->join('设备_产量计酬 b', 'a.Gy0_gdbh = b.sczl_gdbh AND a.Gy0_yjno = b.sczl_yjno AND a.Gy0_gxh = b.sczl_gxh', 'left')
+            ->join([$payrollTeamSub => 'b'], 'a.Gy0_gdbh = b.sczl_gdbh AND a.Gy0_yjno = b.sczl_yjno AND a.Gy0_gxh = b.sczl_gxh', 'left')
             ->join([$sczlTeamSub => 'c'], 'a.Gy0_gdbh = c.sczl_gdbh AND a.Gy0_yjno = c.sczl_yjno AND a.Gy0_gxh = c.sczl_gxh AND IFNULL(b.sczl_bh1,\'\') = IFNULL(c.sczl_bh1,\'\')', 'left')
             ->join('设备_基本资料 d', 'd.设备编号 = b.sczl_jtbh', 'left')
-            ->join('工单_基本资料 e', 'a.Gy0_gdbh = e.Gd_gdbh AND a.Gy0_yjno = e.行号')
             ->where([
                 'a.Gy0_gdbh' => $workOrderData['jjcp_gdbh'],
                 'a.Gy0_yjno' => $workOrderData['jjcp_yjno'],
                 'a.Gy0_gxh' => ['in', $gxh_values],
             ])
-            ->group('a.Gy0_gxh,b.sczl_jtbh,b.sczl_bh1, CASE WHEN b.sczl_bh2 = "000000" THEN "" ELSE b.sczl_bh2 END, b.sczl_bh3,
-                         b.sczl_bh4, b.sczl_bh5, b.sczl_bh6, b.sczl_bh7, b.sczl_bh8, b.sczl_bh9')
             ->order('a.Gy0_gxh asc, b.开工时间 asc')
             ->select();
         $gy_data = $this->appendMissingSuperLossProcessRows(
@@ -1343,39 +1341,42 @@ class OrderSuperLoss extends Api
             unset($gy_data[$kk]['bzdh']);
         }
        
-        //计算超节损金额
-        foreach ($gy_data as $n => $item) {
-            //节损数量
-            if ($item['无形损分摊'] > 0) {
-                $total_waste = $item['质检废'] + $item['sczl_zcfp'] + $item['无形损分摊'];
-            } else {
-                $total_waste = $item['质检废'] + $item['sczl_zcfp'];
-            }
-            $gy_data[$n]['节损数量'] = $item['计划损耗'] - $total_waste;
-            //节损奖
-            $guige = \db('物料_存货编码')
-                ->where('物料代码',$data['成品代号'])
-                ->value('rtrim(规格)');
+        $guige = \db('物料_存货编码')
+            ->where('物料代码', $data['成品代号'])
+            ->value('rtrim(规格)');
+        $moneyRate = (strpos((string)$guige, '小盒') !== false) ? 0.02 : 0.09;
 
-            //工序超节损金额
-            if (strpos($guige,'小盒') !== false){
-                $moneyRate = 0.02;
-            }else{
-                $moneyRate = 0.09;
-            }
-            if (strpos($item['sczl_gxmc'], '机检') !== false) {
-                $gy_data[$n]['节损奖'] = 0;
-            } else {
-                $gy_data[$n]['节损奖'] = number_format($gy_data[$n]['节损数量'] * $item['超节损承担比例'] * $moneyRate, 4);
-            }
+        //计算超节损金额(班组行先汇总废品,节损数/金额按工序级:工序计划损耗 - 报废合计)
+        foreach ($gy_data as $n => $item) {
             $gy_data[$n]['sys_ny'] = date('Ym', strtotime($workOrderData['jjcp_sj']));
-
             $gy_data[$n]['超节损核算单价'] = $moneyRate;
             $total['plan_loss'] += $item['计划损耗'];
             $total['total_fp'] += $item['sczl_zcfp'];
             $total['waste_quality'] += $item['质检废'];
             $total['waste_intangible'] += $item['无形损分摊'];
-            $total['reward'] += floatval(str_replace(',', '', $gy_data[$n]['节损奖']));
+        }
+
+        $processGxSummary = [];
+        foreach ($gy_data as $n => $item) {
+            $gk = $this->normalizeGxhKey($item['Gy0_gxh']);
+            if ($gk === '') {
+                continue;
+            }
+            if (!isset($processGxSummary[$gk])) {
+                $processGxSummary[$gk] = [
+                    'plan_loss' => (int)$item['Gy0_计划损耗'],
+                    'zcfp' => 0,
+                    'zj' => 0,
+                    'intangible' => 0,
+                    'row_indexes' => [],
+                    'gxmc' => $item['sczl_gxmc'],
+                    'ratio' => $item['超节损承担比例'],
+                ];
+            }
+            $processGxSummary[$gk]['zcfp'] += (int)$item['sczl_zcfp'];
+            $processGxSummary[$gk]['zj'] += (int)$item['质检废'];
+            $processGxSummary[$gk]['intangible'] += (int)$item['无形损分摊'];
+            $processGxSummary[$gk]['row_indexes'][] = $n;
         }
 
         $rewardMultiplier = $this->resolveSuperLossRewardMultiplier(
@@ -1385,17 +1386,30 @@ class OrderSuperLoss extends Api
             $feedQty
         );
         $data['reward_rate'] = (string)$rewardMultiplier;
-        if ($rewardMultiplier > 1) {
-            $total['reward'] = 0;
-            foreach ($gy_data as $n => $item) {
-                $baseReward = floatval(str_replace(',', '', (string)$gy_data[$n]['节损奖']));
-                if ($baseReward != 0.0) {
-                    $gy_data[$n]['节损奖'] = number_format($baseReward * $rewardMultiplier, 4);
+        $total['reward'] = 0;
+        foreach ($processGxSummary as $summary) {
+            $totalWaste = $summary['zcfp'] + $summary['zj'] + $summary['intangible'];
+            $processCjsSl = $summary['plan_loss'] - $totalWaste;
+            if ($this->isSuperLossRewardExcluded((string)$summary['gxmc'])) {
+                $processCjsJe = 0.0;
+            } else {
+                $processCjsJe = $processCjsSl * floatval($summary['ratio']) * $moneyRate;
+            }
+            if ($rewardMultiplier > 1 && $processCjsJe != 0.0) {
+                $processCjsJe *= $rewardMultiplier;
+            }
+            foreach ($summary['row_indexes'] as $idx => $n) {
+                if ($idx === 0) {
+                    $gy_data[$n]['节损数量'] = $processCjsSl;
+                    $gy_data[$n]['节损奖'] = number_format($processCjsJe, 4);
+                } else {
+                    $gy_data[$n]['节损数量'] = 0;
+                    $gy_data[$n]['节损奖'] = '0.0000';
                 }
-                $total['reward'] += floatval(str_replace(',', '', $gy_data[$n]['节损奖']));
+                $total['reward'] += floatval(str_replace(',', '', (string)$gy_data[$n]['节损奖']));
             }
         }
-        
+
         //整合数据
         $result = [];
         foreach ($gy_data as $key => $value) {
@@ -1459,17 +1473,17 @@ class OrderSuperLoss extends Api
                 'sczl_rate7' => $value['sczl_rate7'],
                 'sczl_rate8' => $value['sczl_rate8'],
                 '类型' => '',
-                '客户代号' => $value['Gd_客户代号'],
-                '客户名称' => $value['Gd_客户名称'],
-                '产品代号' => $value['Gd_cpdh'],
-                '产品名称' => $value['Gd_cpmc'],
-                '销售订单号' => $value['销售订单号'],
-                '订单数量' => $value['订单数量'],
-                '计量单位' => $value['计量单位'],
+                '客户代号' => $data['Gd_客户代号'] ?? '',
+                '客户名称' => $data['Gd_客户名称'] ?? '',
+                '产品代号' => $data['Gd_cpdh'] ?? $data['成品代号'],
+                '产品名称' => $data['Gd_cpmc'] ?? $data['成品名称'],
+                '销售订单号' => $data['销售订单号'],
+                '订单数量' => $data['订单数量'],
+                '计量单位' => $data['计量单位'] ?? '',
                 '实际投料' => $data['实际投料'],
                 '入仓数量' => $total_number[0],
                 '入仓日期' => $workOrderData['jjcp_sj'],
-                '大箱数' => $value['投料大箱'],
+                '大箱数' => $data['投料大箱'] ?? '',
                 '废品合计' => $data['工单质检废'] + $data['zcfp'],
                 'sys_id' => '',
                 'sys_rq' => date('Y-m-d H:i:s', time()),
@@ -1741,37 +1755,50 @@ class OrderSuperLoss extends Api
         //查询工序数据
         $gy_field = '  
             `工序` AS Gy0_gxh,  
-            `工序名称` AS Gy0_gxmc,  
-            `印件及工序` AS Gy0_yjno,  
-            `联数` AS Gy0_ls,  
-            计划损耗 AS Gy0_计划损耗,  
-            `计划产量` AS Gy0_计划接货数,  
+            MAX(`工序名称`) AS Gy0_gxmc,  
+            MAX(`印件及工序`) AS Gy0_yjno,  
+            MAX(`联数`) AS Gy0_ls,  
+            MAX(`计划损耗`) AS Gy0_计划损耗,  
+            MAX(`计划产量`) AS Gy0_计划接货数,  
             SUM(`班组分摊无形损`) AS Gy0_无形损,  
-            SUM(`CjsSl`) AS loss,  
-            `印件工序产量` AS total_cl,  
+            MAX(`印件工序产量`) AS total_cl,  
             SUM(`班组制程废品`) AS total_fp,  
             SUM(`班组分摊质检废品`) AS waste_quality,
-            SUM(`CjsJe`) AS 工序超节损金额,
-            `无形损承担比例`,
-            `超节损承担比例`
+            MAX(`超节损承担比例`) AS 超节损承担比例,
+            MAX(`无形损承担比例`) AS 无形损承担比例
         ';
         $gy_data = \db('工单_质量考核汇总')
             ->field($gy_field)
             ->where($where)
             ->group('Gy0_gxh')
             ->select();
-        foreach ($gy_data as $key=>$value){
-            if (strpos($value['Gy0_gxmc'],'分切') === false) {
-                $Multiple = 1;
-            }
+        $guige = \db('物料_存货编码')
+            ->where('物料代码', $list['成品代号'] ?? $list['产品代号'] ?? '')
+            ->value('rtrim(规格)');
+        $moneyRate = (strpos((string)$guige, '小盒') !== false) ? 0.02 : 0.09;
+        foreach ($gy_data as $key => $value) {
             //报废合计
             $gy_data[$key]['total_waste'] = $value['Gy0_无形损'] + $value['total_fp'] + $value['waste_quality'];
+            //工序节损数 = 工序计划损耗 - 报废合计
+            $gy_data[$key]['loss'] = (int)$value['Gy0_计划损耗'] - (int)$gy_data[$key]['total_waste'];
+            if ($this->isSuperLossRewardExcluded((string)$value['Gy0_gxmc'])) {
+                $gy_data[$key]['工序超节损金额'] = 0;
+            } else {
+                $gy_data[$key]['工序超节损金额'] = round(
+                    $gy_data[$key]['loss'] * floatval($value['超节损承担比例']) * $moneyRate,
+                    4
+                );
+            }
+            $denom = (float)$value['Gy0_计划接货数'];
+            if ($denom <= 0) {
+                $denom = 1;
+            }
             //工序实际报废率
-            $gy_data[$key]['actual_loss_rate'] = ((number_format($gy_data[$key]['total_waste']/$value['Gy0_计划接货数'],4))*100).'%';
+            $gy_data[$key]['actual_loss_rate'] = ((number_format($gy_data[$key]['total_waste'] / $denom, 4)) * 100) . '%';
             //工序目标报废率
-            $gy_data[$key]['target_loss_rate'] = ((number_format($value['Gy0_计划损耗']/$value['Gy0_计划接货数'],4))*100).'%';
+            $gy_data[$key]['target_loss_rate'] = ((number_format($value['Gy0_计划损耗'] / $denom, 4)) * 100) . '%';
             //占投料报废率
-            $gy_data[$key]['loss_rate'] = ((number_format(($value['Gy0_计划损耗']-$gy_data[$key]['total_waste'])/$list['实际投料'],4))*100).'%';
+            $gy_data[$key]['loss_rate'] = ((number_format(($value['Gy0_计划损耗'] - $gy_data[$key]['total_waste']) / $list['实际投料'], 4)) * 100) . '%';
         }
         $list['gy_data'] = $gy_data;
         $total = [
@@ -2250,6 +2277,81 @@ class OrderSuperLoss extends Api
         $this->success('成功',$data);
     }
 
+    /**
+     * 工序号统一为与 getSuperLossProcessGxhList 一致的两位字符串,避免 9/09 重复补行
+     */
+    private function normalizeGxhKey($gxh): string
+    {
+        if ($gxh === null || $gxh === '') {
+            return '';
+        }
+        $n = (int)$gxh;
+
+        return $n < 10 ? sprintf('%02d', $n) : (string)$n;
+    }
+
+    /**
+     * 工艺资料工序展示名(Add_gxmc 为空时不拼 null)
+     */
+    private function processGxmcSqlExpr(): string
+    {
+        return 'CONCAT(rtrim(a.Gy0_gxmc), IF(a.Add_gxmc IS NULL OR rtrim(a.Add_gxmc) = \'\', \'\', CONCAT(\'【\', rtrim(a.Add_gxmc), \'】\')))';
+    }
+
+    /** 机检、外发:不参与无形损分摊 */
+    private function isSuperLossIntangibleExcluded(string $gxName): bool
+    {
+        return strpos($gxName, '机检') !== false || strpos($gxName, '外发') !== false;
+    }
+
+    /** 机检、外发:不计超节损金额(仍计制程废、工序节损数) */
+    private function isSuperLossRewardExcluded(string $gxName): bool
+    {
+        return $this->isSuperLossIntangibleExcluded($gxName);
+    }
+
+    /**
+     * 产量计酬按班组聚合,避免与工单基本资料等 JOIN 时 SUM(sczl_zcfp) 被放大
+     */
+    private function buildPayrollTeamAggSubSql(): string
+    {
+        return db('设备_产量计酬')
+            ->field([
+                'sczl_gdbh',
+                'sczl_yjno',
+                'sczl_gxh',
+                'sczl_jtbh',
+                'sczl_bh1',
+                'sczl_bh2',
+                'sczl_bh3',
+                'sczl_bh4',
+                'sczl_bh5',
+                'sczl_bh6',
+                'sczl_bh7',
+                'sczl_bh8',
+                'sczl_bh9',
+                'sczl_bh10',
+                'MAX(sczl_bzdh) as sczl_bzdh',
+                'MAX(sczl_gxmc) as sczl_gxmc',
+                'MAX(sczl_rate1) as sczl_rate1',
+                'MAX(sczl_rate2) as sczl_rate2',
+                'MAX(sczl_rate3) as sczl_rate3',
+                'MAX(sczl_rate4) as sczl_rate4',
+                'MAX(sczl_rate5) as sczl_rate5',
+                'MAX(sczl_rate6) as sczl_rate6',
+                'MAX(sczl_rate7) as sczl_rate7',
+                'MAX(sczl_rate8) as sczl_rate8',
+                'MAX(sczl_rate9) as sczl_rate9',
+                'MAX(sczl_rate10) as sczl_rate10',
+                'SUM(sczl_zcfp) as sczl_zcfp',
+                'SUM(sczl_cl) as total_cl',
+                'GROUP_CONCAT(DISTINCT sczl_num SEPARATOR ",") AS 流程单',
+                'MIN(开工时间) as 开工时间',
+            ])
+            ->group('sczl_gdbh, sczl_yjno, sczl_gxh, sczl_jtbh, sczl_bh1, CASE WHEN sczl_bh2 = "000000" THEN "" ELSE sczl_bh2 END, sczl_bh3, sczl_bh4, sczl_bh5, sczl_bh6, sczl_bh7, sczl_bh8, sczl_bh9')
+            ->buildSql();
+    }
+
     /**
      * db_sczl 按工序聚合,避免与产量计酬 JOIN 时 SUM 被放大
      */
@@ -2290,8 +2392,16 @@ class OrderSuperLoss extends Api
                     SELECT sczl_gxh FROM 设备_产量计酬 WHERE sczl_gdbh = ? AND sczl_yjno = ?
                     UNION
                     SELECT sczl_gxh FROM db_sczl WHERE sczl_gdbh = ? AND sczl_yjno = ? AND sczl_gxmc LIKE '%拆片%'
+                    UNION
+                    SELECT Gy0_gxh AS sczl_gxh FROM 工单_工艺资料
+                        WHERE Gy0_gdbh = ? AND Gy0_yjno = ?
+                        AND (Gy0_gxmc LIKE '%外发%' OR IFNULL(Add_gxmc, '') LIKE '%外发%')
+                    UNION
+                    SELECT Gy0_gxh AS sczl_gxh FROM 工单_工艺资料
+                        WHERE Gy0_gdbh = ? AND Gy0_yjno = ?
+                        AND (Gy0_gxmc LIKE '%拆片%' OR IFNULL(Add_gxmc, '') LIKE '%拆片%')
                 ) AS combined_result";
-        $gxhArr = Db::query($sql, [$gdbh, $yjno, $gdbh, $yjno]);
+        $gxhArr = Db::query($sql, [$gdbh, $yjno, $gdbh, $yjno, $gdbh, $yjno, $gdbh, $yjno]);
         $gxhValues = array_column($gxhArr, 'sczl_gxh');
         foreach ($gxhValues as $k => $v) {
             if ((int)$v < 10) {
@@ -2314,6 +2424,8 @@ class OrderSuperLoss extends Api
         return floatval(db('工单_工艺资料')
             ->where(['Gy0_gdbh' => $gdbh, 'Gy0_yjno' => $yjno, 'Gy0_gxh' => ['in', $gxhValues]])
             ->where('Gy0_gxmc', 'NOT LIKE', '%检%')
+            ->where('Gy0_gxmc', 'NOT LIKE', '%外发%')
+            ->whereRaw("(Add_gxmc IS NULL OR Add_gxmc NOT LIKE '%外发%')")
             ->value('SUM(Gy0_计划损耗 * Gy0_ls * Gy0_ks)'));
     }
 
@@ -2361,7 +2473,7 @@ class OrderSuperLoss extends Api
         }
 
         $gxName = (string)($processRow['sczl_gxmc'] ?? $processRow['gxmc'] ?? '');
-        if (strpos($gxName, '机检') !== false) {
+        if ($this->isSuperLossIntangibleExcluded($gxName)) {
             return 0;
         }
 
@@ -2386,32 +2498,39 @@ class OrderSuperLoss extends Api
     ): array {
         $present = [];
         foreach ($gyData as $row) {
-            $present[(string)$row['Gy0_gxh']] = true;
+            $present[$this->normalizeGxhKey($row['Gy0_gxh'])] = true;
         }
 
-        $missing = array_values(array_diff($gxhValues, array_keys($present)));
+        $missing = [];
+        foreach ($gxhValues as $gv) {
+            if (!isset($present[$this->normalizeGxhKey($gv)])) {
+                $missing[] = $gv;
+            }
+        }
         if (empty($missing)) {
             return $gyData;
         }
 
+        $payrollProcessSub = db('设备_产量计酬')
+            ->field('sczl_gdbh, sczl_yjno, sczl_gxh, SUM(sczl_zcfp) as sczl_zcfp, SUM(sczl_cl) as total_cl')
+            ->group('sczl_gdbh, sczl_yjno, sczl_gxh')
+            ->buildSql();
         $sczlProcessSub = $this->buildSczlProcessAggSubSql();
+        $gxmcExpr = $this->processGxmcSqlExpr();
         $processRows = db('工单_工艺资料')
             ->alias('a')
             ->field([
                 'a.Gy0_gdbh', 'a.Gy0_yjno', 'a.Gy0_gxh', 'a.Gy0_ls', 'a.Gy0_ks', 'a.Gy0_计划损耗', 'a.超节损承担比例',
-                'CONCAT(`a`.`Gy0_gxmc`, "【", a.Add_gxmc,"】") AS sczl_gxmc',
-                'SUM(b.sczl_zcfp) as sczl_zcfp', 'SUM(b.sczl_cl) as total_cl',
+                $gxmcExpr . ' AS sczl_gxmc',
+                'b.sczl_zcfp', 'b.total_cl',
                 'c.cl', 'c.fp',
                 'a.无形损承担比例', 'a.Gy0_Rate0', 'a.Gy0_Rate1', 'a.损耗系数', 'a.Gy0_ms',
                 'a.Gy0_计划接货数*a.Gy0_ls*a.Gy0_ks as 计划产量',
                 'rtrim(a.Gy0_SITE) as Gy0_SITE', 'rtrim(a.印刷方式) as 印刷方式', 'rtrim(a.版距) as 版距',
-                'e.Gd_客户代号', 'e.Gd_客户名称', 'e.Gd_cpdh', 'e.Gd_cpmc', 'e.销售订单号', 'e.投料大箱', 'e.订单数量', 'e.计量单位',
             ])
-            ->join('设备_产量计酬 b', 'a.Gy0_gdbh = b.sczl_gdbh AND a.Gy0_yjno = b.sczl_yjno AND a.Gy0_gxh = b.sczl_gxh', 'left')
+            ->join([$payrollProcessSub => 'b'], 'a.Gy0_gdbh = b.sczl_gdbh AND a.Gy0_yjno = b.sczl_yjno AND a.Gy0_gxh = b.sczl_gxh', 'left')
             ->join([$sczlProcessSub => 'c'], 'a.Gy0_gdbh = c.sczl_gdbh AND a.Gy0_yjno = c.sczl_yjno AND a.Gy0_gxh = c.sczl_gxh', 'left')
-            ->join('工单_基本资料 e', 'a.Gy0_gdbh = e.Gd_gdbh AND a.Gy0_yjno = e.行号', 'left')
             ->where(['a.Gy0_gdbh' => $gdbh, 'a.Gy0_yjno' => $yjno, 'a.Gy0_gxh' => ['in', $missing]])
-            ->group('a.Gy0_gxh')
             ->select();
 
         foreach ($processRows as $item) {
@@ -2424,14 +2543,6 @@ class OrderSuperLoss extends Api
                 $item['sczl_bh' . $j] = '';
                 $item['sczl_rate' . $j] = 0;
             }
-            $item['Gd_客户代号'] = $item['Gd_客户代号'] ?? '';
-            $item['Gd_客户名称'] = $item['Gd_客户名称'] ?? '';
-            $item['Gd_cpdh'] = $item['Gd_cpdh'] ?? '';
-            $item['Gd_cpmc'] = $item['Gd_cpmc'] ?? '';
-            $item['销售订单号'] = $item['销售订单号'] ?? '';
-            $item['投料大箱'] = $item['投料大箱'] ?? '';
-            $item['订单数量'] = $item['订单数量'] ?? '';
-            $item['计量单位'] = $item['计量单位'] ?? '';
             $gyData[] = $item;
         }