Quellcode durchsuchen

公司初始化

cqp vor 1 Monat
Ursprung
Commit
5ee56be4d2
1 geänderte Dateien mit 51 neuen und 55 gelöschten Zeilen
  1. 51 55
      app/Service/StatisticService.php

+ 51 - 55
app/Service/StatisticService.php

@@ -1445,36 +1445,30 @@ class StatisticService extends StatisticCommonService
             )
             ->groupBy("order_month", "item_id", "employee_id")->get()->toArray();
 
-        // 2. 获取工资及主表月份映射
-        $ps_query = MonthlyPsOrder::Clear($user, $data)
+        // 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 = $ps_query->pluck('id')->toArray();
-        $monthly_ps_order_key_list = $ps_query
+        $monthly_ps_order_ids = $monthly_ps_order_query->pluck('id')->toArray();
+        $monthly_ps_order_key_list = $monthly_ps_order_query
             ->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))
             ->pluck('month_str', 'id')->toArray();
 
-        $month_employee_salary_raw = MonthlyPsOrderDetails::wherein('main_id', $monthly_ps_order_ids)
-            ->select(
-                "employee_id",
-                DB::raw("(base_salary + performance_salary + bonus + other) as salary"),
-                "main_id",
-                "social_insurance",
-                "public_housing_fund"
-            )
+        $month_employee_salary = MonthlyPsOrderDetails::wherein('main_id', $monthly_ps_order_ids)
+            ->select("employee_id", DB::raw("(base_salary + performance_salary + bonus + other) as salary"), "main_id", "social_insurance", "public_housing_fund")
             ->get()->toArray();
 
-        // 3. 构建高精度工资池与工时汇总
+        // 3. 汇总每个人每个月的工资 (单位:分)
         $salary_map = [];
-        $all_salary_cents = [];
-        foreach ($month_employee_salary_raw as $val) {
+        $all_salary = [];
+        foreach ($month_employee_salary as $val) {
             $month = $monthly_ps_order_key_list[$val['main_id']] ?? '';
             if ($month) {
                 $key = $val['employee_id'] . '_' . $month;
                 $salary_map[$key] = $val;
-                $all_salary_cents[$key] = [
+                $all_salary[$key] = [
                     "salary" => (int)round($val['salary'] * 100),
                     "social_insurance" => (int)round($val['social_insurance'] * 100),
                     "public_housing_fund" => (int)round($val['public_housing_fund'] * 100),
@@ -1482,94 +1476,96 @@ class StatisticService extends StatisticCommonService
             }
         }
 
+        // 4. 计算月总工时 (分钟) 及 余额池初始化 (小时*100)
         $employee_monthly_total_min = [];
         foreach ($month_employee_list as $row) {
             $key = $row['employee_id'] . '_' . $row['order_month'];
             $employee_monthly_total_min[$key] = ($employee_monthly_total_min[$key] ?? 0) + $row['total_work'];
         }
 
-        // 初始化工时余额池 (以展示的小时数为基准)
-        $all_min_cents = [];
-        foreach ($employee_monthly_total_min as $key => $totalMin) {
-            $all_min_cents[$key] = (int)round(($totalMin / 60), 2) * 100;
+        $all_min = [];
+        foreach ($employee_monthly_total_min as $key => $min) {
+            // 重要修复:以总分钟数计算出全月总小时数,作为余额池的起点
+            $all_min[$key] = (int)round($min / 60, 2) * 100;
         }
 
-        // 4. 组装数据并预加载
+        // 5. 初步构建待分摊列表
         $item_month_list = [];
         foreach ($month_employee_list as $item) {
             $key = $item['employee_id'] . '_' . $item['order_month'];
             $item_key = $item['order_month'] . '_' . $item['item_id'] . '_' . $item['employee_id'];
 
+            $total_min = $employee_monthly_total_min[$key] ?? 0;
+            $ratio = $total_min > 0 ? ($item['total_work'] / $total_min) : 0;
+
             $item_month_list[$item_key] = [
                 "month" => $item['order_month'],
                 "employee_id" => $item['employee_id'],
                 "item_id" => $item['item_id'],
-                "work_minutes_raw" => $item['total_work'],
-                "total_min_raw" => $employee_monthly_total_min[$key] ?? 0,
                 "salary" => $salary_map[$key]['salary'] ?? 0,
                 "social_insurance" => $salary_map[$key]['social_insurance'] ?? 0,
                 "public_housing_fund" => $salary_map[$key]['public_housing_fund'] ?? 0,
+                "radio" => round($ratio, 4),
+                "total_min_raw" => $total_min, // 保留原始分钟,不在这里修改
+                "work_min_raw" => $item['total_work'],
             ];
         }
 
+        // 6. 获取项目和人员信息
+        $items = collect($item_month_list)->pluck('item_id')->unique()->all();
         $employee_ids = collect($item_month_list)->pluck('employee_id')->unique()->all();
+
         $employee_key_list = Employee::TopClear($user, $data)->wherein('id', $employee_ids)
             ->select("major", "title", "id")->get()->keyBy('id')->toArray();
-
-        $items_ids = collect($item_month_list)->pluck('item_id')->unique()->all();
-        $item_info = Item::TopClear($user, $data)->wherein('id', $items_ids)
+        $item_info = Item::TopClear($user, $data)->wherein('id', $items)
             ->select("title", "code", "id")->get()->keyBy('id')->toArray();
 
+        // 7. 统计每人项目数用于平账
         $employee_count = collect($item_month_list)->groupBy(fn($i) => $i['employee_id'].'_'.$i['month'])
             ->map(fn($g) => $g->count())->toArray();
 
-        // 5. 最终循环计算
-        $result = collect($item_month_list)->sortBy('month')->values()->transform(function ($item) use ($item_info, $employee_key_list, &$employee_count, &$all_salary_cents, &$all_min_cents) {
+        // 8. 核心转换
+        $result = collect($item_month_list)->sortBy('month')->values()->transform(function ($item) use ($item_info, $employee_key_list, &$employee_count, &$all_salary, &$all_min) {
             $key = $item['employee_id'] . '_' . $item['month'];
 
-            // 基础信息填充
             $item['item_title'] = $item_info[$item['item_id']]['title'] ?? "未知项目";
             $item['item_code'] = $item_info[$item['item_id']]['code'] ?? "-";
             $item['employee_title'] = $employee_key_list[$item['employee_id']]['title'] ?? "未知人员";
             $item['major'] = $employee_key_list[$item['employee_id']]['major'] ?? "-";
 
-            // --- 修复应出勤工时逻辑 ---
-            // total_hours 直接使用计算好的固定值,不参与递减逻辑
-            $item['total_hours'] = round($item['total_min_raw'] / 60, 2);
+            // 应出勤工时:固定展示,不参与递减
+            $item['total_min'] = round($item['total_min_raw'] / 60, 2);
 
-            // 研发工时占比
-            $ratio = $item['total_min_raw'] > 0 ? ($item['work_minutes_raw'] / $item['total_min_raw']) : 0;
-            $item['radio'] = round($ratio, 4);
-
-            if (--$employee_count[$key] > 0) {
-                // 非最后一条
-                $work_h = round($item['work_minutes_raw'] / 60, 2);
+            if (isset($employee_count[$key]) && --$employee_count[$key] > 0) {
+                // 非最后一条项目
+                $work_h = round($item['work_min_raw'] / 60, 2);
                 $item['work_minutes'] = $work_h;
+                $all_min[$key] -= ($work_h * 100);
 
-                // 递减池
-                $all_min_cents[$key] -= (int)round($work_h * 100);
-
-                $ws = round($item['salary'] * $ratio, 2);
+                // 分摊工资
+                $ws = round($item['salary'] * ($item['work_min_raw'] / $item['total_min_raw']), 2);
                 $item['work_salary'] = $ws;
-                $all_salary_cents[$key]['salary'] -= (int)round($ws * 100);
+                if (isset($all_salary[$key])) $all_salary[$key]['salary'] -= ($ws * 100);
 
-                $wsi = round($item['social_insurance'] * $ratio, 2);
+                // 分摊社保
+                $wsi = round($item['social_insurance'] * ($item['work_min_raw'] / $item['total_min_raw']), 2);
                 $item['work_social_insurance'] = $wsi;
-                $all_salary_cents[$key]['social_insurance'] -= (int)round($wsi * 100);
+                if (isset($all_salary[$key])) $all_salary[$key]['social_insurance'] -= ($wsi * 100);
 
-                $wphf = round($item['public_housing_fund'] * $ratio, 2);
+                // 分摊公积金
+                $wphf = round($item['public_housing_fund'] * ($item['work_min_raw'] / $item['total_min_raw']), 2);
                 $item['work_public_housing_fund'] = $wphf;
-                $all_salary_cents[$key]['public_housing_fund'] -= (int)round($wphf * 100);
+                if (isset($all_salary[$key])) $all_salary[$key]['public_housing_fund'] -= ($wphf * 100);
+
             } else {
-                // 最后一条,拿回剩余金额/工时
-                $item['work_salary'] = round(($all_salary_cents[$key]['salary'] ?? 0) / 100, 2);
-                $item['work_social_insurance'] = round(($all_salary_cents[$key]['social_insurance'] ?? 0) / 100, 2);
-                $item['work_public_housing_fund'] = round(($all_salary_cents[$key]['public_housing_fund'] ?? 0) / 100, 2);
-                $item['work_minutes'] = round(($all_min_cents[$key] ?? 0) / 100, 2);
+                // 最后一条,从余额池取值(解决精度)
+                $item['work_salary'] = isset($all_salary[$key]) ? round($all_salary[$key]['salary'] / 100, 2) : 0;
+                $item['work_social_insurance'] = isset($all_salary[$key]) ? round($all_salary[$key]['social_insurance'] / 100, 2) : 0;
+                $item['work_public_housing_fund'] = isset($all_salary[$key]) ? round($all_salary[$key]['public_housing_fund'] / 100, 2) : 0;
+                $item['work_minutes'] = isset($all_min[$key]) ? round($all_min[$key] / 100, 2) : 0;
             }
 
-            // 移除辅助字段
-            unset($item['work_minutes_raw'], $item['total_min_raw']);
+            unset($item['total_min_raw'], $item['work_min_raw']);
             return $item;
         })->all();