cqp преди 1 месец
родител
ревизия
860c7d4e52
променени са 1 файла, в които са добавени 662 реда и са изтрити 293 реда
  1. 662 293
      app/Service/StatisticService.php

+ 662 - 293
app/Service/StatisticService.php

@@ -8,7 +8,6 @@ use App\Model\DailyPwOrderDetails;
 use App\Model\Device;
 use App\Model\Device;
 use App\Model\Employee;
 use App\Model\Employee;
 use App\Model\EmployeeDepartPermission;
 use App\Model\EmployeeDepartPermission;
-use App\Model\EmployeeRole;
 use App\Model\ExpenseClaimsDetails;
 use App\Model\ExpenseClaimsDetails;
 use App\Model\Fee;
 use App\Model\Fee;
 use App\Model\Item;
 use App\Model\Item;
@@ -287,7 +286,116 @@ class StatisticService extends StatisticCommonService
         return [true, $result];
         return [true, $result];
     }
     }
 
 
+    public function itemDeviceMonthStatistic($data, $user)
+    {
+        // 1. 基础校验与时间初始化
+        list($status, $month_start, $month_end) = $this->commonRule($data);
+        if (!$status) return [false, $month_start];
+
+        // 2. 获取设备工时数据 (项目+设备+月)
+        $month_device_list = DailyDwOrderDetails::Clear($user, $data)
+            ->where("order_time", ">=", $month_start)
+            ->where("order_time", "<", $month_end)
+            ->where('del_time', 0)
+            ->select(
+                "item_id",
+                "device_id",
+                DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"),
+                DB::raw("SUM(total_work_min) as total_work")
+            )
+            ->groupBy(DB::raw("FROM_UNIXTIME(order_time, '%Y-%m')"), "item_id", "device_id")->get()->toArray();
+
+        // 3. 获取折旧费主表及详情
+        $monthly_dd_query = MonthlyDdOrder::Clear($user, $data)->where('del_time', 0)
+            ->where("month", ">=", $month_start)->where("month", "<", $month_end);
+
+        $monthly_dd_ids = $monthly_dd_query->pluck('id')->toArray();
+        $monthly_dd_months = $monthly_dd_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
+            ->pluck("month_str", 'id')->toArray();
+
+        $device_salary_details = MonthlyDdOrderDetails::wherein('main_id', $monthly_dd_ids)
+            ->select("device_id", "depreciation_amount", "main_id")->get()->toArray();
+
+        // 4. 构建高精度折旧池 (单位:分)
+        $depreciation_pool = [];
+        foreach ($device_salary_details as $val) {
+            $m = $monthly_dd_months[$val['main_id']] ?? '';
+            if ($m) {
+                $key = $val['device_id'] . '_' . $m;
+                // 同一个设备在同一个月可能存在多条折旧记录,需累加
+                $depreciation_pool[$key] = ($depreciation_pool[$key] ?? 0) + (int)round($val['depreciation_amount'] * 100);
+            }
+        }
+
+        // 5. 计算全月总工时及初始化余额池
+        $device_monthly_total_min = [];
+        foreach ($month_device_list as $row) {
+            $key = $row['device_id'] . '_' . $row['order_month'];
+            $device_monthly_total_min[$key] = ($device_monthly_total_min[$key] ?? 0) + $row['total_work'];
+        }
+
+        $rem_depreciation_cents = $depreciation_pool; // 折旧余额池
+        $rem_hours_cents = []; // 工时余额池 (小时*100)
+        foreach ($device_monthly_total_min as $key => $min) {
+            $rem_hours_cents[$key] = (int)round(($min / 60), 2) * 100;
+        }
+
+        // 6. 统计项目数用于判断“最后一条”
+        $device_count = collect($month_device_list)->groupBy(fn($i) => $i['device_id'].'_'.$i['order_month'])
+            ->map(fn($g) => $g->count())->toArray();
+
+        // 7. 预加载设备与项目基础信息
+        $items_ids = collect($month_device_list)->pluck('item_id')->unique()->all();
+        $device_ids = collect($month_device_list)->pluck('device_id')->unique()->all();
+
+        $item_info = Item::TopClear($user, $data)->whereIn('id', $items_ids)->get()->keyBy('id');
+        $device_info = Device::TopClear($user, $data)->whereIn('id', $device_ids)->get()->keyBy('id');
 
 
+        // 8. 核心转换逻辑
+        $item_month_list = collect($month_device_list)->sortBy('order_month')->values()->transform(function ($item) use (
+            $item_info, $device_info, $depreciation_pool, $device_monthly_total_min,
+            &$device_count, &$rem_depreciation_cents, &$rem_hours_cents
+        ) {
+            $key = $item['device_id'] . '_' . $item['order_month'];
+            $total_min = $device_monthly_total_min[$key] ?? 0;
+
+            $item['item_title'] = $item_info[$item['item_id']]->title ?? "未知项目";
+            $item['item_code'] = $item_info[$item['item_id']]->code ?? "-";
+            $item['device_title'] = $device_info[$item['device_id']]->title ?? "未知设备";
+            $item['device_original'] = $device_info[$item['device_id']]->original_value ?? 0;
+
+            $item['month'] = $item['order_month'];
+            $item['total_depreciatio'] = ($depreciation_pool[$key] ?? 0) / 100;
+            $item['total_hours'] = round($total_min / 60, 2);
+
+            // 分摊比例计算
+            $ratio = $total_min > 0 ? ($item['total_work'] / $total_min) : 0;
+            $item['ratio'] = round($ratio, 4);
+
+            if (--$device_count[$key] > 0) {
+                // 非最后一条项目
+                $cur_hours = round($item['total_work'] / 60, 2);
+                $item['hours'] = $cur_hours;
+                $rem_hours_cents[$key] -= (int)round($cur_hours * 100);
+
+                $cur_depre = round($item['total_depreciatio'] * $ratio, 2);
+                $item['allocated_depreciatio'] = $cur_depre;
+                $rem_depreciation_cents[$key] -= (int)round($cur_depre * 100);
+            } else {
+                // 最后一条项目,余额补平
+                $item['hours'] = round(($rem_hours_cents[$key] ?? 0) / 100, 2);
+                $item['allocated_depreciatio'] = round(($rem_depreciation_cents[$key] ?? 0) / 100, 2);
+                // 比例也做个平账展示
+                $item['ratio'] = round($item['hours'] / ($item['total_hours'] ?: 1), 4);
+            }
+
+            return $item;
+        })->all();
+
+        return [true, $item_month_list];
+    }
+
+    //原方法---
     public function employeeMonthSalaryStatistic1($data, $user)
     public function employeeMonthSalaryStatistic1($data, $user)
     {
     {
         //项目编码、项目名称、天数、工资、日期
         //项目编码、项目名称、天数、工资、日期
@@ -560,115 +668,6 @@ class StatisticService extends StatisticCommonService
         return [true, $item_month_list];
         return [true, $item_month_list];
     }
     }
 
 
-    public function itemDeviceMonthStatistic($data, $user)
-    {
-        // 1. 基础校验与时间初始化
-        list($status, $month_start, $month_end) = $this->commonRule($data);
-        if (!$status) return [false, $month_start];
-
-        // 2. 获取设备工时数据 (项目+设备+月)
-        $month_device_list = DailyDwOrderDetails::Clear($user, $data)
-            ->where("order_time", ">=", $month_start)
-            ->where("order_time", "<", $month_end)
-            ->where('del_time', 0)
-            ->select(
-                "item_id",
-                "device_id",
-                DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"),
-                DB::raw("SUM(total_work_min) as total_work")
-            )
-            ->groupBy(DB::raw("FROM_UNIXTIME(order_time, '%Y-%m')"), "item_id", "device_id")->get()->toArray();
-
-        // 3. 获取折旧费主表及详情
-        $monthly_dd_query = MonthlyDdOrder::Clear($user, $data)->where('del_time', 0)
-            ->where("month", ">=", $month_start)->where("month", "<", $month_end);
-
-        $monthly_dd_ids = $monthly_dd_query->pluck('id')->toArray();
-        $monthly_dd_months = $monthly_dd_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
-            ->pluck("month_str", 'id')->toArray();
-
-        $device_salary_details = MonthlyDdOrderDetails::wherein('main_id', $monthly_dd_ids)
-            ->select("device_id", "depreciation_amount", "main_id")->get()->toArray();
-
-        // 4. 构建高精度折旧池 (单位:分)
-        $depreciation_pool = [];
-        foreach ($device_salary_details as $val) {
-            $m = $monthly_dd_months[$val['main_id']] ?? '';
-            if ($m) {
-                $key = $val['device_id'] . '_' . $m;
-                // 同一个设备在同一个月可能存在多条折旧记录,需累加
-                $depreciation_pool[$key] = ($depreciation_pool[$key] ?? 0) + (int)round($val['depreciation_amount'] * 100);
-            }
-        }
-
-        // 5. 计算全月总工时及初始化余额池
-        $device_monthly_total_min = [];
-        foreach ($month_device_list as $row) {
-            $key = $row['device_id'] . '_' . $row['order_month'];
-            $device_monthly_total_min[$key] = ($device_monthly_total_min[$key] ?? 0) + $row['total_work'];
-        }
-
-        $rem_depreciation_cents = $depreciation_pool; // 折旧余额池
-        $rem_hours_cents = []; // 工时余额池 (小时*100)
-        foreach ($device_monthly_total_min as $key => $min) {
-            $rem_hours_cents[$key] = (int)round(($min / 60), 2) * 100;
-        }
-
-        // 6. 统计项目数用于判断“最后一条”
-        $device_count = collect($month_device_list)->groupBy(fn($i) => $i['device_id'].'_'.$i['order_month'])
-            ->map(fn($g) => $g->count())->toArray();
-
-        // 7. 预加载设备与项目基础信息
-        $items_ids = collect($month_device_list)->pluck('item_id')->unique()->all();
-        $device_ids = collect($month_device_list)->pluck('device_id')->unique()->all();
-
-        $item_info = Item::TopClear($user, $data)->whereIn('id', $items_ids)->get()->keyBy('id');
-        $device_info = Device::TopClear($user, $data)->whereIn('id', $device_ids)->get()->keyBy('id');
-
-        // 8. 核心转换逻辑
-        $item_month_list = collect($month_device_list)->sortBy('order_month')->values()->transform(function ($item) use (
-            $item_info, $device_info, $depreciation_pool, $device_monthly_total_min,
-            &$device_count, &$rem_depreciation_cents, &$rem_hours_cents
-        ) {
-            $key = $item['device_id'] . '_' . $item['order_month'];
-            $total_min = $device_monthly_total_min[$key] ?? 0;
-
-            $item['item_title'] = $item_info[$item['item_id']]->title ?? "未知项目";
-            $item['item_code'] = $item_info[$item['item_id']]->code ?? "-";
-            $item['device_title'] = $device_info[$item['device_id']]->title ?? "未知设备";
-            $item['device_original'] = $device_info[$item['device_id']]->original_value ?? 0;
-
-            $item['month'] = $item['order_month'];
-            $item['total_depreciatio'] = ($depreciation_pool[$key] ?? 0) / 100;
-            $item['total_hours'] = round($total_min / 60, 2);
-
-            // 分摊比例计算
-            $ratio = $total_min > 0 ? ($item['total_work'] / $total_min) : 0;
-            $item['ratio'] = round($ratio, 4);
-
-            if (--$device_count[$key] > 0) {
-                // 非最后一条项目
-                $cur_hours = round($item['total_work'] / 60, 2);
-                $item['hours'] = $cur_hours;
-                $rem_hours_cents[$key] -= (int)round($cur_hours * 100);
-
-                $cur_depre = round($item['total_depreciatio'] * $ratio, 2);
-                $item['allocated_depreciatio'] = $cur_depre;
-                $rem_depreciation_cents[$key] -= (int)round($cur_depre * 100);
-            } else {
-                // 最后一条项目,余额补平
-                $item['hours'] = round(($rem_hours_cents[$key] ?? 0) / 100, 2);
-                $item['allocated_depreciatio'] = round(($rem_depreciation_cents[$key] ?? 0) / 100, 2);
-                // 比例也做个平账展示
-                $item['ratio'] = round($item['hours'] / ($item['total_hours'] ?: 1), 4);
-            }
-
-            return $item;
-        })->all();
-
-        return [true, $item_month_list];
-    }
-
     public function itemDeviceMonthStatistic1($data, $user)
     public function itemDeviceMonthStatistic1($data, $user)
     {
     {
         //项目编码、项目名称、设备名称、项目工时、研发工时占比、设备原值、设备折旧额、本项目帐面归集的折旧额、确定的本项目折旧额、加计调整金额、当月工时、日期
         //项目编码、项目名称、设备名称、项目工时、研发工时占比、设备原值、设备折旧额、本项目帐面归集的折旧额、确定的本项目折旧额、加计调整金额、当月工时、日期
@@ -804,70 +803,249 @@ class StatisticService extends StatisticCommonService
         return [true, $item_month_list];
         return [true, $item_month_list];
     }
     }
 
 
-
-    /**
-     * 根据前端 ISO 时间字符串获取该年份的起止日期和时间戳
-     * 适配:2019-12-31T16:00:00.000Z 这种带时区的数据
-     *
-     * @param string $isoStr 前端传来的时间字符串
-     * @return array|null
-     */
-    public function getYearRangeInfo($isoStr)
-    {
-        if (empty($isoStr)) return null;
-
-        try {
-            // 1. 解析 ISO 8601 字符串
-            $date = new \DateTime($isoStr);
-
-            // 2. 强制转为中国时区(PRC),处理 16:00:00Z 这种 UTC 偏移
-            $date->setTimezone(new \DateTimeZone('PRC'));
-
-            // 3. 提取年份
-            $year = $date->format('Y');
-
-            // 4. 构造日期字符串
-            $startDate = $year . "-01-01";
-            $endDate = $year . "-12-31";
-
-            return [
-                 $startDate,
-                 $endDate,
-            ];
-        } catch (\Exception $e) {
-//            var_dump($e->getMessage());die;
-            return null;
-        }
-    }
-
     public function employeeAttendanceMonthStatistic($data, $user)
     public function employeeAttendanceMonthStatistic($data, $user)
     {
     {
-        //项目编码、项目名称、项目状态、支出类型、允许加计扣除金额合计、人员人工费用、折旧费用、其他费用、前N项小计、其他相关费用合计、委内费用、委外费用、
+        // 1. 基础规则验证
         list($status, $month_start, $month_end) = $this->commonRule($data);
         list($status, $month_start, $month_end) = $this->commonRule($data);
-        if (!$status) {
-            return [false, $month_start];
-        }
-        //第一步确定项目
-        $item = Item::TopClear($user, $data);
-        $item_list = $item->where('del_time', 0)
+        if (!$status) return [false, $month_start];
+
+        // 2. 确定该区间内涉及的项目
+        $item_list = Item::TopClear($user, $data)
+            ->where('del_time', 0)
             ->where(function ($query) use ($month_start, $month_end) {
             ->where(function ($query) use ($month_start, $month_end) {
                 $query->where('start_time', '>=', $month_start)
                 $query->where('start_time', '>=', $month_start)
                     ->where('end_time', '<', $month_end);
                     ->where('end_time', '<', $month_end);
-            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time", "asc")->get()->toArray();
-        $item_key_list = [];
-        foreach ($item_list as $v) {
-            $item_key_list[$v['id']] = $v;
-        }
-        //第二步确定人员费用
+            })
+            ->select("code", "title", "start_time", "end_time", "id", "state")
+            ->orderby("start_time", "asc")
+            ->get();
+
+        $item_key_list = $item_list->keyBy('id')->toArray();
+
+        // 3. 获取精准分摊的人员费用(内部已处理精度,返回单位为:分)
         $item_employee_list = $this->getEmployeeItemSalary($month_start, $month_end, $data, $user);
         $item_employee_list = $this->getEmployeeItemSalary($month_start, $month_end, $data, $user);
-        //第三步确定折旧费用
+
+        // 4. 获取精准分摊的折旧费用(内部已处理精度,返回单位为:分)
         $item_device_list = $this->getDeviceItemSalary($month_start, $month_end, $data, $user);
         $item_device_list = $this->getDeviceItemSalary($month_start, $month_end, $data, $user);
-        //第四步其他费用
+
+        // 5. 获取其他报销费用
         list($item_fee_list, $fee_type_list) = $this->getFeeItemSalary($month_start, $month_end, $data, $user);
         list($item_fee_list, $fee_type_list) = $this->getFeeItemSalary($month_start, $month_end, $data, $user);
 
 
-        //组合所有数据
-        $return = [];
-        foreach ($item_key_list as $v) {
+        // 6. 组合所有数据
+        $result = [];
+        foreach ($item_key_list as $itemId => $v) {
+
+            // 从分摊结果中取值,注意单位换算回“元”
+            $salary_cents = $item_employee_list[$itemId]['salary'] ?? 0;
+            $dep_cents    = $item_device_list[$itemId]['depreciation'] ?? 0;
+
+            $item_value = [
+                "code"                => $v['code'],
+                "title"               => $v['title'],
+                "state"               => $v['state'] == 3 ? "完结" : "进行中",
+                "employee_salary"     => round($salary_cents / 100, 2),
+                "device_depreciation" => round($dep_cents / 100, 2),
+                "expense_type"        => "费用化支出",
+                "fee_list"            => collect($item_fee_list[$itemId] ?? [])->values()->all(),
+            ];
+            $result[] = $item_value;
+        }
+
+        return [true, ["list" => $result, "fee_type_list" => $fee_type_list]];
+    }
+
+    private function getEmployeeItemSalary($month_start, $month_end, $data, $user)
+    {
+        // 获取工时明细(按月、人、项目分组)
+        $month_employee_list = DailyPwOrderDetails::Clear($user, $data)
+            ->where("order_time", ">=", $month_start)
+            ->where("order_time", "<", $month_end)
+            ->where('del_time', 0)
+            ->select(
+                "item_id",
+                "employee_id",
+                DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"),
+                DB::raw("SUM(total_work_min) as total_work")
+            )
+            ->groupBy("order_month", "item_id", "employee_id")
+            ->get()->toArray();
+
+        // 获取工资主表并建立月份映射
+        $ps_query = MonthlyPsOrder::Clear($user, $data)
+            ->where('del_time', 0)
+            ->where("month", ">=", $month_start)
+            ->where("month", "<", $month_end);
+
+        $ps_ids = $ps_query->pluck('id')->toArray();
+        $month_map = $ps_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
+            ->pluck('month_str', 'id')->toArray();
+
+        // 获取工资明细并转为整数“分”
+        $salary_details = MonthlyPsOrderDetails::whereIn('main_id', $ps_ids)
+            ->select("employee_id", "main_id", DB::raw("(base_salary + performance_salary + bonus + other) as salary"))
+            ->get();
+
+        $all_salary_pool = []; // 存放 [人员_月份 => 总金额分]
+        foreach ($salary_details as $val) {
+            $m = $month_map[$val['main_id']] ?? '';
+            if ($m) {
+                $k = $val['employee_id'] . '_' . $m;
+                $all_salary_pool[$k] = (int)round($val['salary'] * 100);
+            }
+        }
+
+        // 统计每个人每月的总工时和参与的项目数(用于尾差平账)
+        $emp_monthly_total_min = [];
+        $emp_item_count = [];
+        foreach ($month_employee_list as $row) {
+            $k = $row['employee_id'] . '_' . $row['order_month'];
+            $emp_monthly_total_min[$k] = ($emp_monthly_total_min[$k] ?? 0) + $row['total_work'];
+            $emp_item_count[$k] = ($emp_item_count[$k] ?? 0) + 1;
+        }
+
+        $rem_salary = $all_salary_pool; // 剩余金额池
+        $return_item_list = [];
+
+        foreach ($month_employee_list as $item) {
+            $k = $item['employee_id'] . '_' . $item['order_month'];
+            $total_s = $all_salary_pool[$k] ?? 0;
+            $total_m = $emp_monthly_total_min[$k] ?? 0;
+
+            if ($total_m > 0 && --$emp_item_count[$k] > 0) {
+                // 非最后一个项目:按比例计算分值(取整,余额存入池子)
+                $allocated = (int)floor(($item['total_work'] / $total_m) * $total_s);
+                $rem_salary[$k] -= $allocated;
+            } else {
+                // 最后一个项目:直接取剩余全部金额
+                $allocated = $rem_salary[$k] ?? 0;
+            }
+
+            $itemId = $item['item_id'];
+            if (!isset($return_item_list[$itemId])) $return_item_list[$itemId]['salary'] = 0;
+            $return_item_list[$itemId]['salary'] += $allocated;
+        }
+
+        return $return_item_list;
+    }
+
+    private function getDeviceItemSalary($month_start, $month_end, $data, $user)
+    {
+        // 获取设备工时明细
+        $month_device_list = DailyDwOrderDetails::Clear($user, $data)
+            ->where("order_time", ">=", $month_start)
+            ->where("order_time", "<", $month_end)
+            ->where('del_time', 0)
+            ->select(
+                "item_id",
+                "device_id",
+                DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"),
+                DB::raw("SUM(total_work_min) as total_work")
+            )
+            ->groupBy("order_month", "item_id", "device_id")
+            ->get()->toArray();
+
+        // 获取折旧主表
+        $dd_query = MonthlyDdOrder::Clear($user, $data)
+            ->where('del_time', 0)
+            ->where("month", ">=", $month_start)
+            ->where("month", "<", $month_end);
+
+        $dd_ids = $dd_query->pluck('id')->toArray();
+        $month_map = $dd_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
+            ->pluck("month_str", 'id')->toArray();
+
+        // 获取折旧明细并转为整数“分”
+        $device_details = MonthlyDdOrderDetails::whereIn('main_id', $dd_ids)
+            ->select("device_id", "depreciation_amount", "main_id")
+            ->get();
+
+        $all_dep_pool = [];
+        foreach ($device_details as $val) {
+            $m = $month_map[$val['main_id']] ?? '';
+            if ($m) {
+                $k = $val['device_id'] . '_' . $m;
+                $all_dep_pool[$k] = (int)round($val['depreciation_amount'] * 100);
+            }
+        }
+
+        // 统计设备每月总工时和项目数
+        $dev_monthly_total_min = [];
+        $dev_item_count = [];
+        foreach ($month_device_list as $row) {
+            $k = $row['device_id'] . '_' . $row['order_month'];
+            $dev_monthly_total_min[$k] = ($dev_monthly_total_min[$k] ?? 0) + $row['total_work'];
+            $dev_item_count[$k] = ($dev_item_count[$k] ?? 0) + 1;
+        }
+
+        $rem_dep = $all_dep_pool;
+        $return_item_list = [];
+
+        foreach ($month_device_list as $item) {
+            $k = $item['device_id'] . '_' . $item['order_month'];
+            $total_d = $all_dep_pool[$k] ?? 0;
+            $total_m = $dev_monthly_total_min[$k] ?? 0;
+
+            if ($total_m > 0 && --$dev_item_count[$k] > 0) {
+                $allocated = (int)floor(($item['total_work'] / $total_m) * $total_d);
+                $rem_dep[$k] -= $allocated;
+            } else {
+                $allocated = $rem_dep[$k] ?? 0;
+            }
+
+            $itemId = $item['item_id'];
+            if (!isset($return_item_list[$itemId])) $return_item_list[$itemId]['depreciation'] = 0;
+            $return_item_list[$itemId]['depreciation'] += $allocated;
+        }
+
+        return $return_item_list;
+    }
+
+    private function getFeeItemSalary($month_start, $month_end, $data, $user)
+    {
+        //确认所有项目、费用
+        $expense = ExpenseClaimsDetails::Clear($user, $data);
+        $expense_list = $expense->where("claim_date", ">=", $month_start)->where("claim_date", "<", $month_end)
+            ->where('del_time', 0)
+            ->select(
+                "fee_id",
+                "amount",
+                "item_id",
+                "entrust_type"
+            )->get()->toArray();
+        //需要根据分类去汇总
+        $fee = Fee::TopClear($user, $data);
+        $fee = $fee->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
+        return $this->groupListByRoot($expense_list, $fee);
+    }
+
+    //原方法-------
+    public function employeeAttendanceMonthStatistic1($data, $user)
+    {
+        //项目编码、项目名称、项目状态、支出类型、允许加计扣除金额合计、人员人工费用、折旧费用、其他费用、前N项小计、其他相关费用合计、委内费用、委外费用、
+        list($status, $month_start, $month_end) = $this->commonRule($data);
+        if (!$status) return [false, $month_start];
+
+        //第一步确定项目
+        $item = Item::TopClear($user, $data);
+        $item_list = $item->where('del_time', 0)
+            ->where(function ($query) use ($month_start, $month_end) {
+                $query->where('start_time', '>=', $month_start)
+                    ->where('end_time', '<', $month_end);
+            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time", "asc")->get()->toArray();
+        $item_key_list = [];
+        foreach ($item_list as $v) {
+            $item_key_list[$v['id']] = $v;
+        }
+        //第二步确定人员费用
+        $item_employee_list = $this->getEmployeeItemSalary($month_start, $month_end, $data, $user);
+        //第三步确定折旧费用
+        $item_device_list = $this->getDeviceItemSalary($month_start, $month_end, $data, $user);
+        //第四步其他费用
+        list($item_fee_list, $fee_type_list) = $this->getFeeItemSalary($month_start, $month_end, $data, $user);
+
+        //组合所有数据
+        $return = [];
+        foreach ($item_key_list as $v) {
 
 
             //其他费用是个数组
             //其他费用是个数组
             $item_value = [
             $item_value = [
@@ -885,7 +1063,7 @@ class StatisticService extends StatisticCommonService
 
 
     }
     }
 
 
-    private function getEmployeeItemSalary($month_start, $month_end, $data, $user)
+    private function getEmployeeItemSalary1($month_start, $month_end, $data, $user)
     {
     {
         $month_employee = DailyPwOrderDetails::Clear($user, $data);
         $month_employee = DailyPwOrderDetails::Clear($user, $data);
         $month_employee_list = $month_employee->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)
         $month_employee_list = $month_employee->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)
@@ -993,7 +1171,7 @@ class StatisticService extends StatisticCommonService
         return $return_item_list;
         return $return_item_list;
     }
     }
 
 
-    private function getDeviceItemSalary($month_start, $month_end, $data, $user)
+    private function getDeviceItemSalary1($month_start, $month_end, $data, $user)
     {
     {
         //确认所有项目、设备、设备工时
         //确认所有项目、设备、设备工时
         $month_device = DailyDwOrderDetails::Clear($user, $data);
         $month_device = DailyDwOrderDetails::Clear($user, $data);
@@ -1093,24 +1271,6 @@ class StatisticService extends StatisticCommonService
         return $return_item_list;
         return $return_item_list;
     }
     }
 
 
-    private function getFeeItemSalary($month_start, $month_end, $data, $user)
-    {
-        //确认所有项目、费用
-        $expense = ExpenseClaimsDetails::Clear($user, $data);
-        $expense_list = $expense->where("claim_date", ">=", $month_start)->where("claim_date", "<", $month_end)
-            ->where('del_time', 0)
-            ->select(
-                "fee_id",
-                "amount",
-                "item_id",
-                "entrust_type"
-            )->get()->toArray();
-        //需要根据分类去汇总
-        $fee = Fee::TopClear($user, $data);
-        $fee = $fee->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
-        return $this->groupListByRoot($expense_list, $fee);
-    }
-
     /**
     /**
      * 将报销明细按照一级费用和项目分类进行分组
      * 将报销明细按照一级费用和项目分类进行分组
      * * @param array $list 报销明细列表 (含 fee_id, amount 等)
      * * @param array $list 报销明细列表 (含 fee_id, amount 等)
@@ -1200,55 +1360,45 @@ class StatisticService extends StatisticCommonService
     {
     {
         list($status, $month_start, $month_end) = $this->commonRule($data);
         list($status, $month_start, $month_end) = $this->commonRule($data);
         if (!$status) return [false, $month_start];
         if (!$status) return [false, $month_start];
-        //项目编码、项目名称、项目状态、凭证日期、凭证种类、凭证号数、凭证摘要、会计凭证归集金额、N个一级费用类型、委内、委外
 
 
-        //确认所有项目
-        $item = Item::TopClear($user, $data);
-        $item_list = $item->where('del_time', 0)
+        // 1. 获取项目列表
+        $item_list = Item::TopClear($user, $data)->where('del_time', 0)
             ->where(function ($query) use ($month_start, $month_end) {
             ->where(function ($query) use ($month_start, $month_end) {
-                $query->where('start_time', '>=', $month_start)
-                    ->where('end_time', '<', $month_end);
-            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time", "asc")->get()->toArray();
-        $item_key_list = [];
-        foreach ($item_list as $v) {
-            $item_key_list[$v['id']] = $v;
-        }
-        //获取该区间内所有项目人工费、折旧费
+                $query->where('start_time', '>=', $month_start)->where('end_time', '<', $month_end);
+            })->select("code", "title", "id", "state")->orderby("start_time", "asc")->get()->keyBy('id')->toArray();
+
+        // 2. 获取分摊的人工和折旧 (此时结果内部已是“分”)
         $item_salary = $this->auxiliaryEmployee($user, $data, $month_start, $month_end);
         $item_salary = $this->auxiliaryEmployee($user, $data, $month_start, $month_end);
         $device_depreciation = $this->auxiliaryDevice($user, $data, $month_start, $month_end);
         $device_depreciation = $this->auxiliaryDevice($user, $data, $month_start, $month_end);
-        $fee = Fee::TopClear($user, $data);
-        $fee = $fee->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
-        $auxiliary = AuxiliaryAccountDetails::Clear($user, $data);
-        $auxiliary_list = $auxiliary->where("voucher_date", ">=", $month_start)->where("voucher_date", "<", $month_end)
-            ->where('del_time', 0)
-            ->select(
-                "item_id",
-                "voucher_date",
-                "voucher_type",
-                "voucher_no",
-                "voucher_remark",
-                "voucher_amount",
-                "aggregation_amount",
-                "entrust_type",
-                "entrust1_amount",
-                "entrust2_amount",
-                "entrust2_amount",
-                "total_amount",
-                "fee_id",
-                "type"
-            )->get()->toArray();
-        list($fee_amount, $fee_type_list) = $this->auxiliaryGroupListByRoot($auxiliary_list, $fee);
 
 
-        //找到项目和设备的费用然后进行比例计算
+        // 3. 处理凭证费用
+        $fee = Fee::TopClear($user, $data)->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
+        $auxiliary_list = AuxiliaryAccountDetails::Clear($user, $data)
+            ->where("voucher_date", ">=", $month_start)->where("voucher_date", "<", $month_end)
+            ->where('del_time', 0)->get()->toArray();
+
+        list($fee_amount, $fee_type_list) = $this->auxiliaryGroupListByRoot($auxiliary_list, $fee);
 
 
         $return = [];
         $return = [];
         foreach ($fee_amount as $v) {
         foreach ($fee_amount as $v) {
-            //人工费用
+            $v_month = date("Y-m", $v['voucher_date']);
+
+            // 类型 1(人工) 或 2(折旧) 需要遍历项目进行分摊显示
             if ($v['type'] == 1 || $v['type'] == 2) {
             if ($v['type'] == 1 || $v['type'] == 2) {
-//                var_dump();
-                foreach ($item_key_list as $vv) {
-                    if(!isset($item_salary[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_salary'])) continue;
-                    $detail = [
+                foreach ($item_list as $itemId => $vv) {
+                    $search_key = $itemId . "_" . $v_month;
+
+                    // 只有当该项目在该月有实际分摊金额时才产生记录
+                    $allocated_amount = 0;
+                    if ($v['type'] == 1) {
+                        $allocated_amount = $item_salary[$search_key]['allocated_salary'] ?? 0;
+                    } else {
+                        $allocated_amount = $device_depreciation[$search_key]['depreciation'] ?? 0;
+                    }
+
+                    if ($allocated_amount <= 0) continue;
+
+                    $return[] = [
                         "code" => $vv['code'],
                         "code" => $vv['code'],
                         "title" => $vv['title'],
                         "title" => $vv['title'],
                         "state" => $vv['state'] == 3 ? "进行中" : "已完成",
                         "state" => $vv['state'] == 3 ? "进行中" : "已完成",
@@ -1258,19 +1408,18 @@ class StatisticService extends StatisticCommonService
                         "voucher_remark" => $v['voucher_remark'],
                         "voucher_remark" => $v['voucher_remark'],
                         "voucher_amount" => $v['voucher_amount'],
                         "voucher_amount" => $v['voucher_amount'],
                         "aggregation_amount" => $v['aggregation_amount'],
                         "aggregation_amount" => $v['aggregation_amount'],
-                        "total_amount" => $v['type'] == 1 ?round( $item_salary[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_salary']/100,2) : (isset($device_depreciation[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['depreciation']) ?round($device_depreciation[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['depreciation']/100,2) : 0),
+                        "total_amount" => round($allocated_amount / 100, 2), // 转回元
                         "fee_id" => $v['fee_id'],
                         "fee_id" => $v['fee_id'],
                         "entrust1_amount" => $v['entrust1_amount'],
                         "entrust1_amount" => $v['entrust1_amount'],
                         "entrust2_amount" => $v['entrust2_amount'],
                         "entrust2_amount" => $v['entrust2_amount'],
                         "type" => $v['type'],
                         "type" => $v['type'],
                     ];
                     ];
-                    $return[] = $detail;
                 }
                 }
-
             } else {
             } else {
-                if (!isset($item_key_list[$v['item_id']])) continue;
-                $item_value = $item_key_list[$v['item_id']];
-                $detail = [
+                // 普通凭证费用
+                if (!isset($item_list[$v['item_id']])) continue;
+                $item_value = $item_list[$v['item_id']];
+                $return[] = [
                     "code" => $item_value['code'],
                     "code" => $item_value['code'],
                     "title" => $item_value['title'],
                     "title" => $item_value['title'],
                     "state" => $item_value['state'] == 3 ? "进行中" : "已完成",
                     "state" => $item_value['state'] == 3 ? "进行中" : "已完成",
@@ -1286,78 +1435,15 @@ class StatisticService extends StatisticCommonService
                     "entrust2_amount" => $v['entrust2_amount'],
                     "entrust2_amount" => $v['entrust2_amount'],
                     "type" => $v['type'],
                     "type" => $v['type'],
                 ];
                 ];
-                $return[] = $detail;
             }
             }
-
-
         }
         }
 
 
         return [true, [
         return [true, [
             'fee_type_list' => $fee_type_list,
             'fee_type_list' => $fee_type_list,
             'list' => $return,
             'list' => $return,
         ]];
         ]];
-
-
-    }
-
-
-    /**
-     * 将报销明细按照一级费用和项目分类进行分组
-     * * @param array $list 报销明细列表 (含 fee_id, amount 等)
-     * @param array $fee_type_list 费用类型树 (含 id, parent_id, title)
-     * @return array
-     */
-    public function auxiliaryGroupListByRoot(array $list, array $fee_type_list)
-    {
-        // 1. 建立 ID 索引,方便快速查找
-        $idMap = array_column($fee_type_list, null, 'id');
-
-        // 2. 预处理映射表:让所有子 ID 直接指向它的最顶层“祖宗” ID
-        $childToRoot = [];
-        foreach ($fee_type_list as $type) {
-            $current = $type;
-            // 向上追溯直到 parent_id 为 0,即找到一级分类
-            while ($current['parent_id'] != 0) {
-                $current = $idMap[$current['parent_id']];
-            }
-            $childToRoot[$type['id']] = [
-                'id' => $current['id'],
-                'title' => $current['title'],
-                'sort' => $current['sort']
-            ];
-        }
-
-        // 3. 遍历明细数据进行分组
-        $type_list = [];
-        foreach ($list as $k => $item) {
-            if ($item['item_id'] == 0 || $item['fee_id'] == 0) {
-                continue;
-            }
-            $feeId = $item['fee_id'];
-            // 获取该费用对应的一级分类信息
-            if (!isset($childToRoot[$feeId])) continue;
-            $rootId = $childToRoot[$feeId]['id'];
-            $title = $childToRoot[$feeId]['title'];
-            $sort = $childToRoot[$feeId]['sort'];
-            $item['fee_id'] = $rootId;
-            $list[$k] = $item;
-
-            //这边需要拿到头部所有的一级费用类型
-            if (!isset($type_list[$rootId])) {
-                $type_list[$rootId] = [
-                    'sort' => $sort,
-                    'id' => $rootId,
-                    'title' => $title,
-                ];
-            }
-        }
-        // 使用 values() 丢弃原始键名,重新从 0 开始建立索引
-        $type_list = collect($type_list)->sortBy('sort')->values()->all();
-        // 4. 重置数组索引并返回
-        return [$list, $type_list];
     }
     }
 
 
-
     public function auxiliaryEmployee($user, $data, $month_start, $month_end)
     public function auxiliaryEmployee($user, $data, $month_start, $month_end)
     {
     {
         //确认所有项目、人员、人员工时
         //确认所有项目、人员、人员工时
@@ -1574,6 +1660,289 @@ class StatisticService extends StatisticCommonService
         return $return_item_list;
         return $return_item_list;
     }
     }
 
 
+    //原方法----
+    public function auxiliaryStatistic1($data, $user)
+    {
+        list($status, $month_start, $month_end) = $this->commonRule($data);
+        if (!$status) return [false, $month_start];
+        //项目编码、项目名称、项目状态、凭证日期、凭证种类、凭证号数、凭证摘要、会计凭证归集金额、N个一级费用类型、委内、委外
+
+        //确认所有项目
+        $item = Item::TopClear($user, $data);
+        $item_list = $item->where('del_time', 0)
+            ->where(function ($query) use ($month_start, $month_end) {
+                $query->where('start_time', '>=', $month_start)
+                    ->where('end_time', '<', $month_end);
+            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time", "asc")->get()->toArray();
+        $item_key_list = [];
+        foreach ($item_list as $v) {
+            $item_key_list[$v['id']] = $v;
+        }
+        //获取该区间内所有项目人工费、折旧费
+        $item_salary = $this->auxiliaryEmployee($user, $data, $month_start, $month_end);
+        $device_depreciation = $this->auxiliaryDevice($user, $data, $month_start, $month_end);
+        $fee = Fee::TopClear($user, $data);
+        $fee = $fee->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
+        $auxiliary = AuxiliaryAccountDetails::Clear($user, $data);
+        $auxiliary_list = $auxiliary->where("voucher_date", ">=", $month_start)->where("voucher_date", "<", $month_end)
+            ->where('del_time', 0)
+            ->select(
+                "item_id",
+                "voucher_date",
+                "voucher_type",
+                "voucher_no",
+                "voucher_remark",
+                "voucher_amount",
+                "aggregation_amount",
+                "entrust_type",
+                "entrust1_amount",
+                "entrust2_amount",
+                "entrust2_amount",
+                "total_amount",
+                "fee_id",
+                "type"
+            )->get()->toArray();
+        list($fee_amount, $fee_type_list) = $this->auxiliaryGroupListByRoot($auxiliary_list, $fee);
+
+        //找到项目和设备的费用然后进行比例计算
+
+        $return = [];
+        foreach ($fee_amount as $v) {
+            //人工费用
+            if ($v['type'] == 1 || $v['type'] == 2) {
+//                var_dump();
+                foreach ($item_key_list as $vv) {
+                    if(!isset($item_salary[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_salary'])) continue;
+                    $detail = [
+                        "code" => $vv['code'],
+                        "title" => $vv['title'],
+                        "state" => $vv['state'] == 3 ? "进行中" : "已完成",
+                        "voucher_date" => date("Y-m-d", $v['voucher_date']),
+                        "voucher_type" => $v['voucher_type'],
+                        "voucher_no" => $v['voucher_no'],
+                        "voucher_remark" => $v['voucher_remark'],
+                        "voucher_amount" => $v['voucher_amount'],
+                        "aggregation_amount" => $v['aggregation_amount'],
+                        "total_amount" => $v['type'] == 1 ?round( $item_salary[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_salary']/100,2) : (isset($device_depreciation[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['depreciation']) ?round($device_depreciation[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['depreciation']/100,2) : 0),
+                        "fee_id" => $v['fee_id'],
+                        "entrust1_amount" => $v['entrust1_amount'],
+                        "entrust2_amount" => $v['entrust2_amount'],
+                        "type" => $v['type'],
+                    ];
+                    $return[] = $detail;
+                }
+
+            } else {
+                if (!isset($item_key_list[$v['item_id']])) continue;
+                $item_value = $item_key_list[$v['item_id']];
+                $detail = [
+                    "code" => $item_value['code'],
+                    "title" => $item_value['title'],
+                    "state" => $item_value['state'] == 3 ? "进行中" : "已完成",
+                    "voucher_date" => date("Y-m-d", $v['voucher_date']),
+                    "voucher_type" => $v['voucher_type'],
+                    "voucher_no" => $v['voucher_no'],
+                    "voucher_remark" => $v['voucher_remark'],
+                    "voucher_amount" => $v['voucher_amount'],
+                    "aggregation_amount" => $v['aggregation_amount'],
+                    "total_amount" => $v['total_amount'],
+                    "fee_id" => $v['fee_id'],
+                    "entrust1_amount" => $v['entrust1_amount'],
+                    "entrust2_amount" => $v['entrust2_amount'],
+                    "type" => $v['type'],
+                ];
+                $return[] = $detail;
+            }
+
+
+        }
+
+        return [true, [
+            'fee_type_list' => $fee_type_list,
+            'list' => $return,
+        ]];
+
+
+    }
+
+    public function auxiliaryEmployee1($user, $data, $month_start, $month_end)
+    {
+        // 1. 获取工时明细
+        $month_employee_list = DailyPwOrderDetails::Clear($user, $data)
+            ->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)
+            ->where('del_time', 0)
+            ->select("item_id", "employee_id", DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"), DB::raw("SUM(total_work_min) as total_work"))
+            ->groupBy("order_month", "item_id", "employee_id")->get()->toArray();
+
+        // 2. 获取工资并转为“分”
+        $monthly_ps_order_query = MonthlyPsOrder::Clear($user, $data)->where('del_time', 0)
+            ->where("month", ">=", $month_start)->where("month", "<", $month_end);
+        $monthly_ps_order_ids = $monthly_ps_order_query->pluck('id')->toArray();
+        $month_map = $monthly_ps_order_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
+            ->pluck('month_str', 'id')->toArray();
+
+        $salary_details = MonthlyPsOrderDetails::whereIn('main_id', $monthly_ps_order_ids)
+            ->select("employee_id", "main_id", DB::raw("(base_salary + performance_salary + bonus + other) as salary"))
+            ->get();
+
+        $salary_pool = []; // [emp_month => salary_cents]
+        foreach ($salary_details as $val) {
+            $m = $month_map[$val['main_id']] ?? '';
+            if ($m) {
+                $k = $val['employee_id'] . '_' . $m;
+                $salary_pool[$k] = (int)round($val['salary'] * 100);
+            }
+        }
+
+        // 3. 计算个人月总工时及项目计数
+        $emp_total_min = [];
+        $emp_item_count = [];
+        foreach ($month_employee_list as $row) {
+            $k = $row['employee_id'] . '_' . $row['order_month'];
+            $emp_total_min[$k] = ($emp_total_min[$k] ?? 0) + $row['total_work'];
+            $emp_item_count[$k] = ($emp_item_count[$k] ?? 0) + 1;
+        }
+
+        // 4. 分摊(保留整数分)
+        $rem_salary = $salary_pool;
+        $return_item_list = [];
+
+        foreach ($month_employee_list as $item) {
+            $k = $item['employee_id'] . '_' . $item['order_month'];
+            $total_s = $salary_pool[$k] ?? 0;
+            $total_m = $emp_total_min[$k] ?? 0;
+
+            if ($total_m > 0 && --$emp_item_count[$k] > 0) {
+                // 非最后一个项目,取地板值避免超支
+                $allocated = (int)floor(($item['total_work'] / $total_m) * $total_s);
+                $rem_salary[$k] -= $allocated;
+            } else {
+                // 最后一个项目,取池子剩余所有钱
+                $allocated = $rem_salary[$k] ?? 0;
+            }
+
+            $res_key = $item['item_id'] . '_' . $item['order_month'];
+            $return_item_list[$res_key]['allocated_salary'] = ($return_item_list[$res_key]['allocated_salary'] ?? 0) + $allocated;
+        }
+
+        return $return_item_list;
+    }
+
+    public function auxiliaryDevice1($user, $data, $month_start, $month_end)
+    {
+        // 1. 获取设备工时
+        $month_device_list = DailyDwOrderDetails::Clear($user, $data)
+            ->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)->where('del_time', 0)
+            ->select("item_id", "device_id", DB::raw("FROM_UNIXTIME(order_time, '%Y-%m') as order_month"), DB::raw("SUM(total_work_min) as total_work"))
+            ->groupBy("order_month", "item_id", "device_id")->get()->toArray();
+
+        // 2. 获取折旧费转为“分”
+        $monthly_dd_query = MonthlyDdOrder::Clear($user, $data)->where('del_time', 0)
+            ->where("month", ">=", $month_start)->where("month", "<", $month_end);
+        $monthly_dd_ids = $monthly_dd_query->pluck('id')->toArray();
+        $month_map = $monthly_dd_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
+            ->pluck("month_str", 'id')->toArray();
+
+        $device_details = MonthlyDdOrderDetails::whereIn('main_id', $monthly_dd_ids)
+            ->select("device_id", "depreciation_amount", "main_id")->get();
+
+        $dep_pool = [];
+        foreach ($device_details as $val) {
+            $m = $month_map[$val['main_id']] ?? '';
+            if ($m) {
+                $k = $val['device_id'] . '_' . $m;
+                $dep_pool[$k] = (int)round($val['depreciation_amount'] * 100);
+            }
+        }
+
+        // 3. 统计设备月总工时及项目数
+        $dev_total_min = [];
+        $dev_item_count = [];
+        foreach ($month_device_list as $row) {
+            $k = $row['device_id'] . '_' . $row['order_month'];
+            $dev_total_min[$k] = ($dev_total_min[$k] ?? 0) + $row['total_work'];
+            $dev_item_count[$k] = ($dev_item_count[$k] ?? 0) + 1;
+        }
+
+        // 4. 分摊
+        $rem_dep = $dep_pool;
+        $return_item_list = [];
+
+        foreach ($month_device_list as $item) {
+            $k = $item['device_id'] . '_' . $item['order_month'];
+            $total_d = $dep_pool[$k] ?? 0;
+            $total_m = $dev_total_min[$k] ?? 0;
+
+            if ($total_m > 0 && --$dev_item_count[$k] > 0) {
+                $allocated = (int)floor(($item['total_work'] / $total_m) * $total_d);
+                $rem_dep[$k] -= $allocated;
+            } else {
+                $allocated = $rem_dep[$k] ?? 0;
+            }
+
+            $res_key = $item['item_id'] . '_' . $item['order_month'];
+            $return_item_list[$res_key]['depreciation'] = ($return_item_list[$res_key]['depreciation'] ?? 0) + $allocated;
+        }
+
+        return $return_item_list;
+    }
+
+    /**
+     * 将报销明细按照一级费用和项目分类进行分组
+     * * @param array $list 报销明细列表 (含 fee_id, amount 等)
+     * @param array $fee_type_list 费用类型树 (含 id, parent_id, title)
+     * @return array
+     */
+    public function auxiliaryGroupListByRoot(array $list, array $fee_type_list)
+    {
+        // 1. 建立 ID 索引,方便快速查找
+        $idMap = array_column($fee_type_list, null, 'id');
+
+        // 2. 预处理映射表:让所有子 ID 直接指向它的最顶层“祖宗” ID
+        $childToRoot = [];
+        foreach ($fee_type_list as $type) {
+            $current = $type;
+            // 向上追溯直到 parent_id 为 0,即找到一级分类
+            while ($current['parent_id'] != 0) {
+                $current = $idMap[$current['parent_id']];
+            }
+            $childToRoot[$type['id']] = [
+                'id' => $current['id'],
+                'title' => $current['title'],
+                'sort' => $current['sort']
+            ];
+        }
+
+        // 3. 遍历明细数据进行分组
+        $type_list = [];
+        foreach ($list as $k => $item) {
+            if ($item['item_id'] == 0 || $item['fee_id'] == 0) {
+                continue;
+            }
+            $feeId = $item['fee_id'];
+            // 获取该费用对应的一级分类信息
+            if (!isset($childToRoot[$feeId])) continue;
+            $rootId = $childToRoot[$feeId]['id'];
+            $title = $childToRoot[$feeId]['title'];
+            $sort = $childToRoot[$feeId]['sort'];
+            $item['fee_id'] = $rootId;
+            $list[$k] = $item;
+
+            //这边需要拿到头部所有的一级费用类型
+            if (!isset($type_list[$rootId])) {
+                $type_list[$rootId] = [
+                    'sort' => $sort,
+                    'id' => $rootId,
+                    'title' => $title,
+                ];
+            }
+        }
+        // 使用 values() 丢弃原始键名,重新从 0 开始建立索引
+        $type_list = collect($type_list)->sortBy('sort')->values()->all();
+        // 4. 重置数组索引并返回
+        return [$list, $type_list];
+    }
+
     public function itemEmployeeSalaryStatistic($data, $user)
     public function itemEmployeeSalaryStatistic($data, $user)
     {
     {
         list($status, $month_start, $month_end) = $this->commonRule($data);
         list($status, $month_start, $month_end) = $this->commonRule($data);