cqp 1 месяц назад
Родитель
Сommit
44ad5e47f0
1 измененных файлов с 127 добавлено и 1 удалено
  1. 127 1
      app/Service/StatisticService.php

+ 127 - 1
app/Service/StatisticService.php

@@ -97,7 +97,7 @@ class StatisticService extends StatisticCommonService
         return [true, $month_employee_list];
     }
 
-    public function employeeMonthSalaryStatistic($data, $user)
+    public function employeeMonthSalaryStatistic2($data, $user)
     {
         list($status, $month_start, $month_end) = $this->commonRule($data);
         if (!$status) return [false, $month_start];
@@ -252,6 +252,132 @@ class StatisticService extends StatisticCommonService
         return [true, $result];
     }
 
+    public function employeeMonthSalaryStatistic($data, $user)
+    {
+        // 1. 基础规则验证与时间范围获取
+        list($status, $month_start, $month_end) = $this->commonRule($data);
+        if (!$status) return [false, $month_start];
+
+        // 2. 查询员工工时明细 (按月、项目、员工聚合)
+        $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();
+
+        // 3. 查询工资主表 (为了获取月份字符串映射)
+        $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();
+        $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();
+
+        // 4. 查询工资详情 (计算每人在每个月的分摊基数)
+        $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")
+            ->get()->toArray();
+
+        // 汇总:员工工资映射表 及 每月总工资池
+        $salary_map = []; // [employee_id_month => salary]
+        $all_salary_pool = []; // [month => total_salary_of_month]
+        foreach ($month_employee_salary as $val) {
+            $month_str = $monthly_ps_order_key_list[$val['main_id']] ?? '';
+            if ($month_str) {
+                $s_key = $val['employee_id'] . '_' . $month_str;
+                $salary_map[$s_key] = $val['salary'];
+
+                if (!isset($all_salary_pool[$month_str])) $all_salary_pool[$month_str] = 0;
+                $all_salary_pool[$month_str] = round($all_salary_pool[$month_str] + $val['salary'], 2);
+            }
+        }
+
+        // 5. 计算员工全月总工时 (用于算比例)
+        $employee_monthly_total_min = [];
+        foreach ($month_employee_list as $row) {
+            $key = $row['employee_id'] . '_' . $row['order_month'];
+            if (!isset($employee_monthly_total_min[$key])) $employee_monthly_total_min[$key] = 0;
+            $employee_monthly_total_min[$key] += $row['total_work'];
+        }
+
+        // 6. 按项目分摊 (初步计算,不四舍五入)
+        $item_month_list = [];
+        foreach ($month_employee_list as $item) {
+            $e_key = $item['employee_id'] . '_' . $item['order_month'];
+            $item_key = $item['order_month'] . '_' . $item['item_id'];
+
+            if (!isset($item_month_list[$item_key])) {
+                $item_month_list[$item_key] = [
+                    "month"          => $item['order_month'],
+                    "item_id"        => $item['item_id'],
+                    "allocated_salary" => 0,
+                    "work_minutes"   => 0,
+                ];
+            }
+
+            $total_salary = $salary_map[$e_key] ?? 0;
+            $total_min    = $employee_monthly_total_min[$e_key] ?? 0;
+
+            if ($total_min > 0) {
+                // 这里是关键:(项目工时 / 员工月总工时) * 员工月工资
+                // 累加时保持高精度
+                $ratio = $item['total_work'] / $total_min;
+                $item_month_list[$item_key]['allocated_salary'] += ($ratio * $total_salary);
+            }
+            $item_month_list[$item_key]['work_minutes'] += $item['total_work'];
+        }
+
+        // 7. 准备项目基础信息
+        $item_month_list = collect($item_month_list)->sortBy('month')->values();
+        $item_ids = $item_month_list->pluck('item_id')->unique()->toArray();
+        $item_info = Item::TopClear($user, $data)->whereIn('id', $item_ids)->get()->keyBy('id');
+
+        // 8. 最终格式化与尾差修正
+        $month_group_count = $item_month_list->groupBy('month')->map->count()->toArray();
+        $month_day_pool = $item_month_list->groupBy('month')->map(fn($g) => round($g->sum('work_minutes') / 480))->toArray();
+
+        $final_list = $item_month_list->transform(function ($item) use (
+            $item_info,
+            &$all_salary_pool,
+            &$month_day_pool,
+            &$month_group_count
+        ) {
+            $month = $item['month'];
+            $item['item_title'] = $item_info[$item['item_id']]->title ?? "未知项目";
+            $item['item_code']  = $item_info[$item['item_id']]->code ?? "N/A";
+
+            if ($month_group_count[$month] > 1) {
+                // 非本月最后一个项目:四舍五入并扣除余额
+                $item['days'] = round($item['work_minutes'] / 480);
+                $item['allocated_salary'] = round($item['allocated_salary'], 2);
+
+                $month_day_pool[$month] -= $item['days'];
+                $all_salary_pool[$month] = round($all_salary_pool[$month] - $item['allocated_salary'], 2);
+                $month_group_count[$month]--;
+            } else {
+                // 本月最后一个项目:直接拿走剩下的所有钱和天数,解决精度问题
+                $item['days'] = $month_day_pool[$month];
+                $item['allocated_salary'] = $all_salary_pool[$month];
+            }
+
+            return $item;
+        });
+
+        return [true, $final_list->all()];
+    }
+
+
     public function employeeMonthSalaryStatistic1($data, $user)
     {
         //项目编码、项目名称、天数、工资、日期