unknown 1 mese fa
parent
commit
911a29bade
1 ha cambiato i file con 276 aggiunte e 85 eliminazioni
  1. 276 85
      application/service/UnifiedCostCalculationService.php

+ 276 - 85
application/service/UnifiedCostCalculationService.php

@@ -64,22 +64,37 @@ class UnifiedCostCalculationService
      */
     public function calculateAndSaveAll(array $param): array
     {
+        Log::info("开始成本计算,参数: " . json_encode($param));
+
         Db::startTrans();
         try {
+            if (!isset($param['month'])) {
+                throw new Exception("缺少必要参数: month");
+            }
+
             $month = $param['month'];
-            $sysId = $param['sys_id'] ?? '';
+            $sysId = $param['sys_id'] ?? uniqid();
 
-            // 1. 清空旧数据
+            Log::info("开始清空旧数据");
             $this->clearOldData($month);
 
-            // 2. 执行五项计算
+            Log::info("开始计算直接人工");
             $this->calculateDirectLabor($param);
+            Log::info("直接人工计算完成,记录数: " . count($this->monthlyCostDetails));
+
+            Log::info("开始计算直接水电");
             $this->calculateDirectUtilities($param);
+
+            Log::info("开始计算间接材料");
             $this->calculateIndirectMaterials($month);
+
+            Log::info("开始计算间接人工");
             $this->calculateIndirectLabor($month);
+
+            Log::info("开始计算分摊水电");
             $this->calculateApportionedUtilities($param);
 
-            // 3. 统一插入数据
+            Log::info("开始保存数据");
             $this->saveAllData($month, $sysId);
 
             Db::commit();
@@ -88,18 +103,26 @@ class UnifiedCostCalculationService
 
             return $this->buildSuccessResponse($month);
 
-        } catch (Exception $e) {
-            Db::rollback();
-            $this->logError($e);
-            return $this->buildErrorResponse($e);
-        } catch (\Throwable $t) {  // 添加更广泛的异常捕获
+        } catch (\Throwable $t) {  // 使用 Throwable 而不是 Exception
             Db::rollback();
-            Log::error("统一成本核算失败(Throwable): " . $t->getMessage() . " at " . $t->getFile() . ":" . $t->getLine());
+
+            // 记录详细的错误信息
+            $errorInfo = [
+                'message' => $t->getMessage(),
+                'code' => $t->getCode(),
+                'file' => $t->getFile(),
+                'line' => $t->getLine(),
+                'trace' => $t->getTraceAsString(),
+                'param' => $param
+            ];
+
+            Log::error("统一成本核算失败: " . json_encode($errorInfo, JSON_UNESCAPED_UNICODE));
+
             return [
                 'success' => false,
                 'message' => '成本核算失败: ' . $t->getMessage(),
                 'error' => $t->getMessage(),
-                'trace' => $t->getTraceAsString()
+                'error_details' => $errorInfo
             ];
         }
     }
@@ -118,27 +141,46 @@ class UnifiedCostCalculationService
      */
     protected function calculateDirectLabor(array $param): void
     {
-        $month = $param['month'];
-        $sysId = $param['sys_id'] ?? '';
-        $now = date('Y-m-d H:i:s');
+        try {
+            $month = $param['month'];
+            $sysId = $param['sys_id'] ?? '';
+            $now = date('Y-m-d H:i:s');
+
+            $list = Db::name('绩效工资汇总')
+                ->alias('a')
+                ->join('工单_工艺资料 b', 'a.sczl_gdbh = b.Gy0_gdbh and a.sczl_yjno = b.Gy0_yjno and a.sczl_gxh = b.Gy0_gxh')
+                ->join('设备_基本资料 c', 'a.sczl_jtbh = c.设备编号', 'LEFT')
+                ->join('工单_印件资料 d', 'a.sczl_gdbh = d.Yj_Gdbh and a.sczl_yjno = d.yj_Yjno')
+                ->field($this->getDirectLaborFields())
+                ->where('a.sys_ny', $month)
+                ->group('a.sczl_gdbh,a.sczl_yjno,a.sczl_gxh,a.sczl_jtbh')
+                ->select();
+
+            // 转换为数组(如果返回的是对象)
+            if (!is_array($list)) {
+                $list = $list->toArray();
+            }
 
-        $list = Db::name('绩效工资汇总')
-            ->alias('a')
-            ->join('工单_工艺资料 b', 'a.sczl_gdbh = b.Gy0_gdbh and a.sczl_yjno = b.Gy0_yjno and a.sczl_gxh = b.Gy0_gxh')
-            ->join('设备_基本资料 c', 'a.sczl_jtbh = c.设备编号', 'LEFT')
-            ->join('工单_印件资料 d', 'a.sczl_gdbh = d.Yj_Gdbh and a.sczl_yjno = d.yj_Yjno')
-            ->field($this->getDirectLaborFields())
-            ->where('a.sys_ny', $month)
-            ->group('a.sczl_gdbh,a.sczl_yjno,a.sczl_gxh,a.sczl_jtbh')
-            ->select();
+            Log::info("直接人工查询结果数: " . count($list));
 
-        foreach ($list as $item) {
-            $this->monthlyCostDetails[] = $this->buildMonthlyCostDetail($item, $month, $sysId, $now);
-        }
+            foreach ($list as $item) {
+                // 确保 $item 是数组
+                $itemArray = is_object($item) ? (array)$item : $item;
+                if (!is_array($itemArray)) {
+                    Log::warning("直接人工数据项格式不正确: " . gettype($item));
+                    continue;
+                }
 
-        Log::info("直接人工计算完成,记录数: " . count($list));
+                $this->monthlyCostDetails[] = $this->buildMonthlyCostDetail($itemArray, $month, $sysId, $now);
+            }
+
+        } catch (\Exception $e) {
+            Log::error("计算直接人工失败: " . $e->getMessage());
+            throw new Exception("直接人工计算失败: " . $e->getMessage());
+        }
     }
 
+
     /**
      * 获取直接人工查询字段
      */
@@ -315,21 +357,35 @@ class UnifiedCostCalculationService
         $workHours = [];
 
         foreach ($this->monthlyCostDetails as $detail) {
-            $machineCode = $detail['sczl_jtbh'] ?? '';
-            $hours = floatval($detail['占用机时'] ?? 0);
+            try {
+                // 确保 $detail 是数组
+                $detailArray = is_object($detail) ? (array)$detail : $detail;
+                if (!is_array($detailArray)) {
+                    Log::warning("成本明细数据格式不正确: " . gettype($detail));
+                    continue;
+                }
 
-            if (!empty($machineCode) && $hours > 0) {
-                $workHours[] = [
-                    'sczl_gdbh' => $detail['sczl_gdbh'] ?? '',
-                    'sczl_yjno' => $detail['sczl_yjno'] ?? '',
-                    'sczl_gxh' => $detail['sczl_gxh'] ?? '',
-                    'sczl_jtbh' => $machineCode,
-                    '占用机时' => $hours,
-                    '车间名称' => $detail['车间名称'] ?? '',
-                ];
+                $machineCode = $detailArray['sczl_jtbh'] ?? '';
+                $hours = floatval($detailArray['占用机时'] ?? 0);
+
+                if (!empty($machineCode) && $hours > 0) {
+                    $workHours[] = [
+                        'sczl_gdbh' => $detailArray['sczl_gdbh'] ?? '',
+                        'sczl_yjno' => $detailArray['sczl_yjno'] ?? '',
+                        'sczl_gxh' => $detailArray['sczl_gxh'] ?? '',
+                        'sczl_jtbh' => $machineCode,
+                        '占用机时' => $hours,
+                        '车间名称' => $detailArray['车间名称'] ?? '',
+                    ];
+                }
+            } catch (\Exception $e) {
+                Log::error("处理机台工作小时数时出错: " . $e->getMessage());
+                continue;
             }
         }
 
+        Log::info("从月度成本明细数据中获取机台运行时间,记录数: " . count($workHours));
+
         return $workHours;
     }
 
@@ -361,30 +417,56 @@ class UnifiedCostCalculationService
         array $machineTotalHours
     ): array {
         $allocationResults = [];
+        $processedCount = 0;
+        $skippedCount = 0;
 
         foreach ($workOrderHours as $workOrder) {
             try {
-                $machineCode = $workOrder['sczl_jtbh'] ?? '';
-                $hours = floatval($workOrder['占用机时'] ?? 0);
-
-                if (empty($machineCode) ||
-                    !isset($machineUtilities[$machineCode]) ||
-                    $machineUtilities[$machineCode]['总费用'] <= 0 ||
-                    !isset($machineTotalHours[$machineCode]) ||
-                    $machineTotalHours[$machineCode] <= 0) {
+                // 确保 $workOrder 是数组
+                $orderArray = is_object($workOrder) ? (array)$workOrder : $workOrder;
+                if (!is_array($orderArray)) {
+                    Log::warning("工单小时数据格式不正确: " . gettype($workOrder));
+                    $skippedCount++;
+                    continue;
+                }
+
+                $machineCode = $orderArray['sczl_jtbh'] ?? '';
+                $hours = floatval($orderArray['占用机时'] ?? 0);
+
+                if (empty($machineCode)) {
+                    Log::debug("工单缺少机器编号: " . json_encode($orderArray));
+                    $skippedCount++;
+                    continue;
+                }
+
+                if (!isset($machineUtilities[$machineCode])) {
+                    Log::debug("机器 {$machineCode} 没有水电费用数据");
+                    $skippedCount++;
+                    continue;
+                }
+
+                if ($machineUtilities[$machineCode]['总费用'] <= 0) {
+                    Log::debug("机器 {$machineCode} 水电费用为0");
+                    $skippedCount++;
+                    continue;
+                }
+
+                if (!isset($machineTotalHours[$machineCode]) || $machineTotalHours[$machineCode] <= 0) {
+                    Log::warning("机器 {$machineCode} 总运行时间为0,无法分摊");
+                    $skippedCount++;
                     continue;
                 }
 
                 $allocationRatio = $hours / $machineTotalHours[$machineCode];
                 $allocatedAmount = round($machineUtilities[$machineCode]['总费用'] * $allocationRatio, 2);
 
-                $uniqueKey = $this->getWorkOrderUniqueKey($workOrder);
+                $uniqueKey = $this->getWorkOrderUniqueKey($orderArray);
 
                 $allocationResults[$uniqueKey] = [
                     'unique_key' => $uniqueKey,
-                    'sczl_gdbh' => $workOrder['sczl_gdbh'] ?? '',
-                    'sczl_yjno' => $workOrder['sczl_yjno'] ?? '',
-                    'sczl_gxh' => $workOrder['sczl_gxh'] ?? '',
+                    'sczl_gdbh' => $orderArray['sczl_gdbh'] ?? '',
+                    'sczl_yjno' => $orderArray['sczl_yjno'] ?? '',
+                    'sczl_gxh' => $orderArray['sczl_gxh'] ?? '',
                     'sczl_jtbh' => $machineCode,
                     '占用机时' => $hours,
                     '分摊比例' => $allocationRatio,
@@ -392,12 +474,18 @@ class UnifiedCostCalculationService
                     '机台总费用' => $machineUtilities[$machineCode]['总费用'],
                     '机台总工时' => $machineTotalHours[$machineCode],
                 ];
+
+                $processedCount++;
+
             } catch (\Exception $e) {
                 Log::error("分摊水电到工单时出错: " . $e->getMessage());
+                $skippedCount++;
                 continue;
             }
         }
 
+        Log::info("分摊水电处理完成,成功: {$processedCount}, 跳过: {$skippedCount}, 总计: " . count($workOrderHours));
+
         return $allocationResults;
     }
 
@@ -407,25 +495,66 @@ class UnifiedCostCalculationService
      */
     protected function getWorkOrderUniqueKey($workOrder): string
     {
-        // 确保参数是数组且包含必要的键
+        // 记录传入参数的类型和内容(用于调试)
+        $type = gettype($workOrder);
+        Log::debug("getWorkOrderUniqueKey 接收参数类型: {$type}");
+
+        // 如果是对象,转换为数组
+        if (is_object($workOrder)) {
+            $workOrder = (array)$workOrder;
+        }
+
+        // 确保是数组
         if (!is_array($workOrder)) {
-            Log::error("getWorkOrderUniqueKey 参数不是数组: " . gettype($workOrder));
+            Log::error("getWorkOrderUniqueKey 参数不是数组: {$type}, 值: " . print_r($workOrder, true));
             return 'invalid-' . uniqid();
         }
 
-        // 确保所有需要的键都存在
-        $gdbh = $workOrder['sczl_gdbh'] ?? $workOrder['工单编号'] ?? '';
-        $yjno = $workOrder['sczl_yjno'] ?? $workOrder['印件号'] ?? '';
-        $gxh = $workOrder['sczl_gxh'] ?? $workOrder['工序号'] ?? '';
-        $jtbh = $workOrder['sczl_jtbh'] ?? $workOrder['机器编号'] ?? '';
+        // 调试:记录数组内容
+        if (count($workOrder) < 4) {
+            Log::debug("getWorkOrderUniqueKey 数组内容: " . json_encode($workOrder));
+        }
 
-        return sprintf(
-            '%s-%s-%s-%s',
-            $gdbh,
-            $yjno,
-            $gxh,
-            $jtbh
-        );
+        // 安全地获取所有可能键名
+        $gdbh = '';
+        $yjno = '';
+        $gxh = '';
+        $jtbh = '';
+
+        // 尝试各种可能的键名
+        if (isset($workOrder['sczl_gdbh'])) {
+            $gdbh = $workOrder['sczl_gdbh'];
+        } elseif (isset($workOrder['工单编号'])) {
+            $gdbh = $workOrder['工单编号'];
+        }
+
+        if (isset($workOrder['sczl_yjno'])) {
+            $yjno = $workOrder['sczl_yjno'];
+        } elseif (isset($workOrder['印件号'])) {
+            $yjno = $workOrder['印件号'];
+        }
+
+        if (isset($workOrder['sczl_gxh'])) {
+            $gxh = $workOrder['sczl_gxh'];
+        } elseif (isset($workOrder['工序号'])) {
+            $gxh = $workOrder['工序号'];
+        }
+
+        if (isset($workOrder['sczl_jtbh'])) {
+            $jtbh = $workOrder['sczl_jtbh'];
+        } elseif (isset($workOrder['机器编号'])) {
+            $jtbh = $workOrder['机器编号'];
+        }
+
+        // 验证所有字段都不为空
+        if (empty($gdbh) || empty($yjno) || empty($gxh) || empty($jtbh)) {
+            Log::warning("工单唯一键字段不完整: gdbh={$gdbh}, yjno={$yjno}, gxh={$gxh}, jtbh={$jtbh}");
+        }
+
+        $key = sprintf('%s-%s-%s-%s', $gdbh, $yjno, $gxh, $jtbh);
+        Log::debug("生成的唯一键: {$key}");
+
+        return $key;
     }
 
     /**
@@ -440,16 +569,22 @@ class UnifiedCostCalculationService
 
         $updatedCount = 0;
         $totalAllocatedAmount = 0;
+        $notFoundCount = 0;
 
         foreach ($this->monthlyCostDetails as &$costDetail) {
             try {
-                $uniqueKey = $this->getWorkOrderUniqueKey($costDetail);
+                // 确保 $costDetail 是数组
+                $detailArray = is_object($costDetail) ? (array)$costDetail : $costDetail;
+
+                $uniqueKey = $this->getWorkOrderUniqueKey($detailArray);
 
                 if (isset($allocationResults[$uniqueKey])) {
-                    $allocatedAmount = $allocationResults[$uniqueKey]['分摊金额'];
+                    $allocatedAmount = $allocationResults[$uniqueKey]['分摊金额'] ?? 0;
                     $costDetail['直接水电'] = $allocatedAmount;
                     $totalAllocatedAmount += $allocatedAmount;
                     $updatedCount++;
+                } else {
+                    $notFoundCount++;
                 }
             } catch (\Exception $e) {
                 Log::error("更新直接水电费时出错: " . $e->getMessage());
@@ -457,7 +592,12 @@ class UnifiedCostCalculationService
             }
         }
 
-        Log::info("已更新直接水电费的工单数量: {$updatedCount}, 分配总金额: {$totalAllocatedAmount}");
+        Log::info("已更新直接水电费的工单数量: {$updatedCount}, 未找到的工单数: {$notFoundCount}, 分配总金额: {$totalAllocatedAmount}");
+
+        // 如果有大量未找到的工单,记录详细信息
+        if ($notFoundCount > 0 && $notFoundCount > $updatedCount) {
+            Log::warning("有大量工单未匹配到水电费分配结果,可能键名不匹配");
+        }
     }
 
     /**
@@ -891,20 +1031,40 @@ class UnifiedCostCalculationService
     ): void {
         $totalHours = array_sum($machineHours);
         if ($totalHours <= 0) {
+            Log::warning("全局分摊失败:总机时为0");
             return;
         }
 
         foreach ($machineHours as $machine => $hours) {
-            if ($hours <= 0) {
+            try {
+                // 确保 $machine 是有效的字符串键名
+                if (!is_string($machine) && !is_numeric($machine)) {
+                    Log::warning("无效的机器键名: " . print_r($machine, true));
+                    continue;
+                }
+
+                $machine = (string)$machine;
+
+                if ($hours <= 0) {
+                    continue;
+                }
+
+                if (!isset($allocations[$machine])) {
+                    $allocations[$machine] = [];
+                }
+
+                $machineAmount = round($amount * ($hours / $totalHours), 2);
+                $allocations[$machine][$subject] =
+                    ($allocations[$machine][$subject] ?? 0) + $machineAmount;
+
+            } catch (\Exception $e) {
+                Log::error("全局分摊到机器 {$machine} 时出错: " . $e->getMessage());
                 continue;
             }
-
-            $allocations[$machine][$subject] =
-                ($allocations[$machine][$subject] ?? 0) +
-                round($amount * ($hours / $totalHours), 2);
         }
     }
 
+
     /**
      * 根据机台获取楼层
      */
@@ -1101,20 +1261,44 @@ class UnifiedCostCalculationService
     protected function validateDataStructure(): void
     {
         if (empty($this->monthlyCostDetails)) {
+            Log::warning("月度成本明细数据为空");
             return;
         }
 
         $tableName = '成本v23_月度成本明细';
-        $columns = Db::query("DESCRIBE `{$tableName}`");
-        $columnNames = array_column($columns, 'Field');
 
-        foreach ($this->monthlyCostDetails as $index => &$row) {
-            if (count($row) !== count($columnNames)) {
-                $this->fixRowData($row, $columnNames, $index);
+        try {
+            $columns = Db::query("DESCRIBE `{$tableName}`");
+            $columnNames = array_column($columns, 'Field');
+
+            Log::info("表{$tableName}结构字段数: " . count($columnNames));
+
+            foreach ($this->monthlyCostDetails as $index => &$row) {
+                // 确保行是数组
+                $rowArray = is_object($row) ? (array)$row : $row;
+
+                if (count($rowArray) !== count($columnNames)) {
+                    Log::warning("第" . ($index + 1) . "行字段数不匹配: 数据" . count($rowArray) . "个,表" . count($columnNames) . "个");
+                    $this->fixRowData($rowArray, $columnNames, $index);
+                    $row = $rowArray;
+                }
+
+                // 检查所有数组键名是否为字符串
+                foreach ($rowArray as $key => $value) {
+                    if (!is_string($key) && !is_int($key)) {
+                        Log::error("发现非法键名类型 (行 {$index}, 键类型: " . gettype($key) . ")");
+                        // 转换为字符串
+                        unset($rowArray[$key]);
+                        $rowArray[(string)$key] = $value;
+                    }
+                }
             }
+        } catch (\Exception $e) {
+            Log::error("验证数据结构时出错: " . $e->getMessage());
         }
     }
 
+
     /**
      * 修复行数据
      */
@@ -1231,17 +1415,24 @@ class UnifiedCostCalculationService
     /**
      * 记录错误日志
      */
-    protected function logError(Exception $e): void
+    protected function logError(\Throwable $t): void
     {
-        Log::error("统一成本核算失败", [
-            'message' => $e->getMessage(),
-            'code' => $e->getCode(),
-            'file' => $e->getFile(),
-            'line' => $e->getLine(),
-            'trace' => $e->getTraceAsString()
-        ]);
+        $errorDetails = [
+            '时间' => date('Y-m-d H:i:s'),
+            '错误类型' => get_class($t),
+            '错误信息' => $t->getMessage(),
+            '错误代码' => $t->getCode(),
+            '文件' => $t->getFile(),
+            '行号' => $t->getLine(),
+            '堆栈跟踪' => $t->getTraceAsString(),
+            '月度成本明细数' => count($this->monthlyCostDetails),
+            '分摊系数数' => count($this->allocationFactors),
+        ];
+
+        Log::error("统一成本核算失败详情: " . json_encode($errorDetails, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
     }
 
+
     /**
      * 构建错误响应
      */