Explorar o código

超节损计算优化

unknown hai 4 días
pai
achega
21291a8724
Modificáronse 1 ficheiros con 196 adicións e 83 borrados
  1. 196 83
      application/api/controller/OrderSuperLoss.php

+ 196 - 83
application/api/controller/OrderSuperLoss.php

@@ -294,8 +294,8 @@ class OrderSuperLoss extends Api
         $data['外发废'] = array_sum($list);//外发废
         $data['工单质检废'] =  array_sum($quality);//质检废
         $data['机检废品'] = array_sum($jijian);
-        $plan_total = $this->getIntangiblePlanLossTotal($order, $params['yjno'], $gxh_values);
-        if($plan_total == 0){
+        $plan_total = $this->getIntangibleAllocationPlanLossTotal($order, $params['yjno'], $gxh_values);
+        if ($plan_total == 0) {
             $this->error('计划损耗数据为 0,请到工单资料管理页面修改工艺资料中选择正确的工序损耗');
             return;
         }
@@ -310,24 +310,18 @@ class OrderSuperLoss extends Api
         if ($params['type'] == 1){
             $total['total_waste'] = 0;
             $total['loss'] = 0;
+            $processIntangibleShares = $this->buildProcessIntangibleShareMap(
+                $order,
+                $params['yjno'],
+                $gxh_values,
+                floatval($data['intangible_loss'])
+            );
             foreach ($gy_data as $k=>$item){
                 $gy_data[$k]['Gy0_计划损耗'] = $item['Gy0_计划损耗']*$item['Gy0_ls']*$item['Gy0_ks'];
                 $gy_data[$k]['waste_quality'] = 0;
                 $gy_data[$k]['total_fp'] = $item['total_fp'] * $item['Gy0_ls'];
                 $gy_data[$k]['total_cl'] = $item['total_cl'] * $item['Gy0_ls'];
-                //无形损
-                if ($this->isSuperLossIntangibleExcluded((string)$item['Gy0_gxmc'])) {
-                    $gy_data[$k]['intangible_loss'] = 0;
-                } elseif ($this->isMachineInspectionProcess((string)$item['Gy0_gxmc'])) {
-                    $planTotalAll = $this->getIntangiblePlanLossTotalAll($order, $params['yjno'], $gxh_values);
-                    $gy_data[$k]['intangible_loss'] = $planTotalAll > 0
-                        ? (int)round($gy_data[$k]['Gy0_计划损耗'] / $planTotalAll * $data['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']);
-                }
+                $gy_data[$k]['intangible_loss'] = (int)($processIntangibleShares[$this->normalizeGxhKey($item['Gy0_gxh'])] ?? 0);
                 foreach ($waste_out as $entry) {
                     for ($i = 1; $i <= 13; $i++) {
                         $gxmcKey = "fp_gxmc" . $i;
@@ -426,7 +420,7 @@ class OrderSuperLoss extends Api
             $gy_data =db('工单_工艺资料')
                 ->alias('a')
                 ->field([
-                    'a.Gy0_yjno', 'a.Gy0_gxh', 'a.Gy0_ls', 'a.Gy0_ks' ,'a.Gy0_计划损耗', 'a.超节损承担比例', 'b.sczl_gxmc',
+                    'a.Gy0_yjno', 'a.Gy0_gxh', 'a.Gy0_ls', 'a.Gy0_ks' ,'a.Gy0_计划损耗', 'a.无形损承担比例', 'a.超节损承担比例', 'b.sczl_gxmc',
                     'SUM(b.sczl_zcfp) as sczl_zcfp', 'SUM(b.sczl_cl) as 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',
@@ -488,14 +482,6 @@ class OrderSuperLoss extends Api
                         }
                     }
                 }
-                //无形损分摊
-                $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']);
-                }
-
                 //班组成员
                 $nameArr = [];
                 for ($j = 1; $j <= 10; $j++) {
@@ -513,6 +499,13 @@ class OrderSuperLoss extends Api
                 unset($gy_data[$key]['sczl_bh']);
 
             }
+            $this->assignIntangibleLossToGyDataRows(
+                $gy_data,
+                $order,
+                $params['yjno'],
+                $gxh_values,
+                floatval($data['intangible_loss'])
+            );
             $i = 0;
             foreach ($gy_data as &$item) {
                 $sczl_bh1 = $item['sczl_bh1'];
@@ -1161,12 +1154,7 @@ class OrderSuperLoss extends Api
         $data['外发废'] = array_sum($list);//外发废
         $data['工单质检废'] = array_sum($quality);//质检废
         $data['机检废'] = array_sum($machine);//机检废
-        $plan_total = $this->getIntangiblePlanLossTotal(
-            $workOrderData['jjcp_gdbh'],
-            $workOrderData['jjcp_yjno'],
-            $gxh_values
-        );
-        $plan_total_all = $this->getIntangiblePlanLossTotalAll(
+        $plan_total = $this->getIntangibleAllocationPlanLossTotal(
             $workOrderData['jjcp_gdbh'],
             $workOrderData['jjcp_yjno'],
             $gxh_values
@@ -1323,15 +1311,6 @@ class OrderSuperLoss extends Api
             } else {
                 $gy_data[$kk]['质检废'] = (int)$gy_data[$kk]['zjfp'];
             }
-            //无形损分摊(分母与按工序打印一致:非检工序计划损耗合计,并考虑无形损承担比例)
-            $gy_data[$kk]['无形损分摊'] = $this->calcTeamIntangibleLossShare(
-                $vv,
-                $gy_data[$kk]['计划损耗'] ?? 0,
-                $gy_data[$kk]['Gy0_计划损耗'] ?? ($vv['Gy0_计划损耗'] ?? 0),
-                $plan_total,
-                floatval($data['intangible_loss']),
-                $plan_total_all
-            );
             if ($this->isMachineInspectionProcess((string)$gy_data[$kk]['sczl_gxmc'])) {
                 $gy_data[$kk]['质检废'] = 0;
                 $gy_data[$kk]['zjfp'] = 0;
@@ -1368,7 +1347,15 @@ class OrderSuperLoss extends Api
             unset($gy_data[$kk]['sczl_bh']);
             unset($gy_data[$kk]['bzdh']);
         }
-       
+
+        $this->assignIntangibleLossToGyDataRows(
+            $gy_data,
+            $workOrderData['jjcp_gdbh'],
+            $workOrderData['jjcp_yjno'],
+            $gxh_values,
+            floatval($data['intangible_loss'])
+        );
+
         $guige = \db('物料_存货编码')
             ->where('物料代码', $data['成品代号'])
             ->value('rtrim(规格)');
@@ -2372,18 +2359,6 @@ class OrderSuperLoss extends Api
         return $this->isMachineInspectionProcess($gxName) || $this->isSuperLossIntangibleExcluded($gxName);
     }
 
-    /** 全工序计划损耗合计(含机检),用于机检无形损分摊分母 */
-    private function getIntangiblePlanLossTotalAll(string $gdbh, string $yjno, array $gxhValues): float
-    {
-        if (empty($gxhValues)) {
-            return 0.0;
-        }
-
-        return floatval(db('工单_工艺资料')
-            ->where(['Gy0_gdbh' => $gdbh, 'Gy0_yjno' => $yjno, 'Gy0_gxh' => ['in', $gxhValues]])
-            ->value('SUM(Gy0_计划损耗 * Gy0_ls * Gy0_ks)'));
-    }
-
     /** 工序号查询兼容 9 / 09 / 9 */
     private function resolveGxhWhereValues($gxh): array
     {
@@ -2727,9 +2702,9 @@ class OrderSuperLoss extends Api
     }
 
     /**
-     * 无形损分摊分母:参与分摊的计划损耗(排除名称含「检」的工序)
+     * 无形损分摊分母:参与超节损工序的计划损耗合计(含机检/拆片等;外发不参与)
      */
-    private function getIntangiblePlanLossTotal(string $gdbh, string $yjno, array $gxhValues): float
+    private function getIntangibleAllocationPlanLossTotal(string $gdbh, string $yjno, array $gxhValues): float
     {
         if (empty($gxhValues)) {
             return 0.0;
@@ -2737,12 +2712,168 @@ 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 '%外发%')")
+            ->whereRaw("(Add_gxmc IS NULL OR rtrim(Add_gxmc) = '' OR Add_gxmc NOT LIKE '%外发%')")
             ->value('SUM(Gy0_计划损耗 * Gy0_ls * Gy0_ks)'));
     }
 
+    /**
+     * 按工序/班组计划损耗占比分摊印件无形损
+     */
+    private function calcIntangibleLossShareByPlanLoss(
+        float $planLoss,
+        float $planLossTotal,
+        float $intangibleLoss,
+        float $intangibleRate = 0.0
+    ): int {
+        if ($planLossTotal <= 0 || $intangibleLoss == 0.0 || $planLoss <= 0) {
+            return 0;
+        }
+        $share = ($planLoss / $planLossTotal) * $intangibleLoss;
+        if ($intangibleRate > 0) {
+            $share *= $intangibleRate;
+        }
+
+        return (int)round($share);
+    }
+
+    /**
+     * 按权重分配整数数量(最大余数法,保证合计等于 totalAmount)
+     */
+    private function allocateIntegerByWeights(int $totalAmount, array $weightsByKey): array
+    {
+        if ($totalAmount <= 0 || empty($weightsByKey)) {
+            return array_fill_keys(array_keys($weightsByKey), 0);
+        }
+        $sum = array_sum($weightsByKey);
+        if ($sum <= 0) {
+            return array_fill_keys(array_keys($weightsByKey), 0);
+        }
+
+        $result = [];
+        $fractions = [];
+        $assigned = 0;
+        foreach ($weightsByKey as $key => $weight) {
+            if ($weight <= 0) {
+                $result[$key] = 0;
+                $fractions[$key] = 0.0;
+                continue;
+            }
+            $exact = $totalAmount * $weight / $sum;
+            $result[$key] = (int)floor($exact);
+            $fractions[$key] = $exact - $result[$key];
+            $assigned += $result[$key];
+        }
+        $remain = $totalAmount - $assigned;
+        if ($remain > 0) {
+            arsort($fractions);
+            foreach (array_keys($fractions) as $key) {
+                if ($remain <= 0) {
+                    break;
+                }
+                if (($weightsByKey[$key] ?? 0) <= 0) {
+                    continue;
+                }
+                $result[$key]++;
+                $remain--;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 印件无形损 → 各工序:按 Gy0_计划损耗×联数×开数 占比分摊(外发不参与;可叠加无形损承担比例)
+     */
+    private function buildProcessIntangibleShareMap(
+        string $gdbh,
+        string $yjno,
+        array $gxhValues,
+        float $intangibleLoss
+    ): array {
+        if ($intangibleLoss <= 0 || empty($gxhValues)) {
+            return [];
+        }
+        $planTotal = $this->getIntangibleAllocationPlanLossTotal($gdbh, $yjno, $gxhValues);
+        if ($planTotal <= 0) {
+            return [];
+        }
+
+        $rows = db('工单_工艺资料')
+            ->field([
+                'Gy0_gxh', 'Gy0_计划损耗', 'Gy0_ls', 'Gy0_ks',
+                'rtrim(Gy0_gxmc) as gxmc', 'rtrim(Add_gxmc) as add_gxmc', '无形损承担比例',
+            ])
+            ->where(['Gy0_gdbh' => $gdbh, 'Gy0_yjno' => $yjno, 'Gy0_gxh' => ['in', $gxhValues]])
+            ->select();
+
+        $weights = [];
+        foreach ($rows as $row) {
+            $gxName = (string)($row['gxmc'] ?? '');
+            if (!empty($row['add_gxmc'])) {
+                $gxName .= '【' . $row['add_gxmc'] . '】';
+            }
+            if ($this->isSuperLossIntangibleExcluded($gxName)) {
+                continue;
+            }
+            $plan = floatval($row['Gy0_计划损耗']) * floatval($row['Gy0_ls']) * floatval($row['Gy0_ks']);
+            if ($plan <= 0) {
+                continue;
+            }
+            $rate = floatval($row['无形损承担比例'] ?? 0);
+            $weight = $plan * ($rate > 0 ? $rate : 1.0);
+            $gk = $this->normalizeGxhKey($row['Gy0_gxh']);
+            $weights[$gk] = ($weights[$gk] ?? 0.0) + $weight;
+        }
+
+        return $this->allocateIntegerByWeights((int)round($intangibleLoss), $weights);
+    }
+
+    /**
+     * 班组行无形损:先按工序计划损耗得工序份额,再按班组分摊计划损耗拆分到各行
+     */
+    private function assignIntangibleLossToGyDataRows(
+        array &$gyData,
+        string $gdbh,
+        string $yjno,
+        array $gxhValues,
+        float $intangibleLoss
+    ): void {
+        $processShares = $this->buildProcessIntangibleShareMap($gdbh, $yjno, $gxhValues, $intangibleLoss);
+        $indexesByGx = [];
+        foreach ($gyData as $idx => $row) {
+            $gk = $this->normalizeGxhKey($row['Gy0_gxh'] ?? '');
+            if ($gk === '') {
+                $gyData[$idx]['无形损分摊'] = 0;
+                continue;
+            }
+            $indexesByGx[$gk][] = $idx;
+        }
+
+        foreach ($indexesByGx as $gk => $indexes) {
+            $processShare = (int)($processShares[$gk] ?? 0);
+            if ($processShare <= 0) {
+                foreach ($indexes as $idx) {
+                    $gyData[$idx]['无形损分摊'] = 0;
+                }
+                continue;
+            }
+            $weights = [];
+            foreach ($indexes as $idx) {
+                $weights[$idx] = max(0.0, floatval($gyData[$idx]['计划损耗'] ?? 0));
+            }
+            if (array_sum($weights) <= 0) {
+                foreach ($indexes as $idx) {
+                    $weights[$idx] = 1.0;
+                }
+            }
+            $teamShares = $this->allocateIntegerByWeights($processShare, $weights);
+            foreach ($indexes as $idx) {
+                $gyData[$idx]['无形损分摊'] = (int)($teamShares[$idx] ?? 0);
+            }
+        }
+    }
+
     /**
      * 目标合格率:1 - Σ(工序计划损耗率),与 getOrderSuperLossCount type=1 一致
      */
@@ -2773,46 +2904,28 @@ class OrderSuperLoss extends Api
     }
 
     /**
-     * 班组无形损分摊(对齐 type=1 按工序打印逻辑)
+     * 班组无形损分摊:印件无形损 ×(本班组计划损耗 / 全部工序计划损耗合计)
      */
     private function calcTeamIntangibleLossShare(
         array $processRow,
         float $teamPlanLoss,
         float $processPlanLoss,
-        float $intangiblePlanTotal,
-        float $intangibleLoss,
-        float $intangiblePlanTotalAll = 0.0
+        float $planLossTotal,
+        float $intangibleLoss
     ): int {
-        if ($intangibleLoss == 0.0) {
-            return 0;
-        }
-
         $gxName = (string)($processRow['sczl_gxmc'] ?? $processRow['gxmc'] ?? '');
         if ($this->isSuperLossIntangibleExcluded($gxName)) {
             return 0;
         }
 
-        if ($this->isMachineInspectionProcess($gxName)) {
-            $denom = $intangiblePlanTotalAll > 0 ? $intangiblePlanTotalAll : $intangiblePlanTotal;
-            if ($denom <= 0) {
-                return 0;
-            }
-
-            return (int)round($processPlanLoss / $denom * $intangibleLoss);
-        }
-
-        if ($intangiblePlanTotal <= 0) {
-            return 0;
-        }
-
         $planBase = $teamPlanLoss > 0 ? $teamPlanLoss : $processPlanLoss;
-        $ratio = $planBase / $intangiblePlanTotal;
-        $share = $ratio * $intangibleLoss;
-        if (floatval($processRow['无形损承担比例'] ?? 0) > 0) {
-            $share *= floatval($processRow['无形损承担比例']);
-        }
 
-        return (int)round($share);
+        return $this->calcIntangibleLossShareByPlanLoss(
+            $planBase,
+            $planLossTotal,
+            $intangibleLoss,
+            floatval($processRow['无形损承担比例'] ?? 0)
+        );
     }
 
     /**