gogs 2 месяцев назад
Родитель
Сommit
2afb870604
1 измененных файлов с 103 добавлено и 79 удалено
  1. 103 79
      app/Service/StatisticService.php

+ 103 - 79
app/Service/StatisticService.php

@@ -56,11 +56,31 @@ class StatisticService extends Service
         $item = Item::Clear($user, $data);
         $item_title_key_list = $item->wherein('id', $item_ids)->pluck("title", "id")->toArray();
         $item_code_key_list = $item->wherein('id', $item_ids)->pluck("code", "id")->toArray();
-        $month_employee_list = collect($month_employee_list)->transform(function ($item) use ($employee_key_list, $item_title_key_list, $item_code_key_list) {
+        $collection = collect($month_employee_list);
+        $count = $collection->count();
+        $total_minutes = $collection->sum('total_work');
+        // 算出目标总工时(绝对标准)
+        $absolute_total_hours = round($total_minutes / 60, 2);
+
+        // 注意:这里使用 transform,参数名建议写准确
+        // 关键点:use 后面加了 & 符号
+        $month_employee_list = $collection->transform(function ($item, $index) use ($employee_key_list, $item_title_key_list, $item_code_key_list, &$absolute_total_hours, $count) {
+
             $item['employee_name'] = $employee_key_list[$item['employee_id']] ?? "未知员工({$item['employee_id']})";
             $item['item_title'] = $item_title_key_list[$item['item_id']] ?? "未知项目({$item['item_id']})";
             $item['item_code'] = $item_code_key_list[$item['item_id']] ?? "未知项目({$item['item_id']})";
-            $item['total_work_hours'] = round($item['total_work'] / 60, 2);
+
+            // 如果不是最后一条
+            if ($index < $count - 1) {
+                $current_hours = round($item['total_work'] / 60, 2);
+                $item['total_work_hours'] = $current_hours;
+                // 关键:递减外部的 $absolute_total_hours
+                $absolute_total_hours = round($absolute_total_hours - $current_hours, 2);
+            } else {
+                // 最后一条:直接拿剩下的“余额”
+                $item['total_work_hours'] = $absolute_total_hours;
+            }
+
             return $item;
         })->all();
         return [true, $month_employee_list];
@@ -239,8 +259,8 @@ class StatisticService extends Service
         }
 
         foreach ($item_month_list as $k => $v) {
-            $item_month_list[$k]['work_hours'] = round($v['work_minutes']  / 60,2);
-            $item_month_list[$k]['total_hours'] = round($v['total_min']  / 60);
+            $item_month_list[$k]['work_hours'] = round($v['work_minutes'] / 60, 2);
+            $item_month_list[$k]['total_hours'] = round($v['total_min'] / 60);
             unset($item_month_list[$k]['work_minutes']);
         }
         $item_month_list = collect($item_month_list)->sortBy('month')->values()->all();
@@ -371,16 +391,16 @@ class StatisticService extends Service
 
     private function commonRule($data)
     {
-        if(! empty($data['year'])){
+        if (!empty($data['year'])) {
             $return = $this->getYearRangeInfo($data['year']);
-            if(is_null($return)) return [false, '年度格式错误'];
+            if (is_null($return)) return [false, '年度格式错误'];
             list($data['month_start'], $data['month_end']) = $return;
-        }else{
-            if(isset($data['time']) && ! empty($data['time'])){
+        } else {
+            if (isset($data['time']) && !empty($data['time'])) {
                 $start = $this->changeDateToDate($data['time'][0]);
                 $end = $this->changeDateToDate($data['time'][1], true);
-                $data['month_start'] = date("Y-m-d",$start);
-                $data['month_end'] = date("Y-m-d",$end);
+                $data['month_start'] = date("Y-m-d", $start);
+                $data['month_end'] = date("Y-m-d", $end);
             }
         }
 
@@ -419,11 +439,11 @@ class StatisticService extends Service
 
             // 4. 构造日期字符串
             $startDate = $year . "-01-01";
-            $endDate   = $year . "-12-31";
+            $endDate = $year . "-12-31";
 
             return [
-                'start_date'  => $startDate,
-                'end_date'    => $endDate,
+                'start_date' => $startDate,
+                'end_date' => $endDate,
             ];
         } catch (\Exception $e) {
             return null;
@@ -443,7 +463,7 @@ class StatisticService extends Service
             ->where(function ($query) use ($month_start, $month_end) {
                 $query->where('start_time', '<=', $month_start)
                     ->orWhere('end_time', '>=', $month_end);
-            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time","asc")->get()->toArray();
+            })->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;
@@ -453,25 +473,25 @@ class StatisticService extends Service
         //第三步确定折旧费用
         $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);
+        list($item_fee_list, $fee_type_list) = $this->getFeeItemSalary($month_start, $month_end, $data, $user);
 
         //组合所有数据
         $return = [];
-        foreach ($item_key_list as $v){
+        foreach ($item_key_list as $v) {
 
             //其他费用是个数组
             $item_value = [
                 "code" => $v['code'],
                 "title" => $v['title'],
                 "state" => $v['state'] == 3 ? "完结" : "进行中",
-                "employee_salary" => $item_employee_list[$v['id']]['salary']??0,
-                "device_depreciation" => $item_device_list[$v['id']]['depreciation']??0,
+                "employee_salary" => $item_employee_list[$v['id']]['salary'] ?? 0,
+                "device_depreciation" => $item_device_list[$v['id']]['depreciation'] ?? 0,
                 "expense_type" => "费用化支出",
                 "fee_list" => collect($item_fee_list[$v['id']] ?? [])->values()->all(),
             ];
             $return[] = $item_value;
         }
-        return [true,["list"=>$return,"fee_type_list"=>$fee_type_list]];
+        return [true, ["list" => $return, "fee_type_list" => $fee_type_list]];
 
     }
 
@@ -678,18 +698,18 @@ class StatisticService extends Service
 
                 ];
             }
-            if($item['entrust_type'] == 1){
+            if ($item['entrust_type'] == 1) {
                 $item_key_list[$key][$rootId]['entrust1_amount'] += $item['amount'];
-            }elseif ($item['entrust_type'] == 2){
+            } elseif ($item['entrust_type'] == 2) {
                 $item_key_list[$key][$rootId]['entrust2_amount'] += $item['amount'];
             }
             $item_key_list[$key][$rootId]['total_amount'] += $item['amount'];
             //这边需要拿到头部所有的一级费用类型
-            if(!isset($type_list[$rootId])){
+            if (!isset($type_list[$rootId])) {
                 $type_list[$rootId] = [
-                    'sort' =>  $sort,
-                    'id' =>  $rootId,
-                    'title' =>  $title,
+                    'sort' => $sort,
+                    'id' => $rootId,
+                    'title' => $title,
                 ];
             }
 
@@ -697,10 +717,11 @@ class StatisticService extends Service
         // 使用 values() 丢弃原始键名,重新从 0 开始建立索引
         $type_list = collect($type_list)->sortBy('sort')->values()->all();
         // 4. 重置数组索引并返回
-        return [$item_key_list,$type_list];
+        return [$item_key_list, $type_list];
     }
 
-    public function auxiliaryStatistic($data,$user){
+    public function auxiliaryStatistic($data, $user)
+    {
         list($status, $month_start, $month_end) = $this->commonRule($data);
         if (!$status) return [false, $month_start];
         //项目编码、项目名称、项目状态、凭证日期、凭证种类、凭证号数、凭证摘要、会计凭证归集金额、N个一级费用类型、委内、委外
@@ -711,14 +732,14 @@ class StatisticService extends Service
             ->where(function ($query) use ($month_start, $month_end) {
                 $query->where('start_time', '<=', $month_start)
                     ->orWhere('end_time', '>', $month_end);
-            })->select("code", "title", "start_time", "end_time", "id", "state")->orderby("start_time","asc")->get()->toArray();
+            })->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);
+        $item_salary = $this->auxiliaryEmployee($user, $data, $month_start, $month_end);
+        $device_depreciation = $this->auxiliaryDevice($user, $data, $month_start, $month_end);
         $fee = Fee::Clear($user, $data);
         $fee = $fee->where('del_time', 0)->orderBy("sort", 'desc')->get()->toArray();
         $auxiliary = AuxiliaryAccountDetails::Clear($user, $data);
@@ -740,26 +761,26 @@ class StatisticService extends Service
                 "fee_id",
                 "type"
             )->get()->toArray();
-        list($fee_amount,$fee_type_list) =  $this->auxiliaryGroupListByRoot($auxiliary_list,$fee);
+        list($fee_amount, $fee_type_list) = $this->auxiliaryGroupListByRoot($auxiliary_list, $fee);
 
         //找到项目和设备的费用然后进行比例计算
 
         $return = [];
-        foreach ($fee_amount as $v){
+        foreach ($fee_amount as $v) {
             //人工费用
-            if($v['type'] == 1||$v['type'] == 2){
-                foreach ($item_key_list as $vv){
+            if ($v['type'] == 1 || $v['type'] == 2) {
+                foreach ($item_key_list as $vv) {
                     $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 ? $item_salary[$vv['id']."_".date("Y-m",$v['voucher_date'])]['allocated_salary'] : ($device_depreciation[$vv['id']."_".date("Y-m",$v['voucher_date'])]['allocated_depreciation']??0) ,
+                        "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 ? $item_salary[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_salary'] : ($device_depreciation[$vv['id'] . "_" . date("Y-m", $v['voucher_date'])]['allocated_depreciation'] ?? 0),
                         "fee_id" => $v['fee_id'],
                         "entrust1_amount" => $v['entrust1_amount'],
                         "entrust2_amount" => $v['entrust2_amount'],
@@ -768,20 +789,20 @@ class StatisticService extends Service
                     $return[] = $detail;
                 }
 
-            }else{
-                if(!isset($item_key_list[$v['item_id']])) continue;
+            } 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'] ,
+                    "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'],
@@ -793,7 +814,7 @@ class StatisticService extends Service
 
         }
 
-        return [true,[
+        return [true, [
             'fee_type_list' => $fee_type_list,
             'list' => $return,
         ]];
@@ -830,8 +851,8 @@ class StatisticService extends Service
 
         // 3. 遍历明细数据进行分组
         $type_list = [];
-        foreach ($list as $k=>$item) {
-            if($item['item_id'] == 0||$item['fee_id'] == 0) {
+        foreach ($list as $k => $item) {
+            if ($item['item_id'] == 0 || $item['fee_id'] == 0) {
                 continue;
             }
             $feeId = $item['fee_id'];
@@ -840,26 +861,27 @@ class StatisticService extends Service
             $rootId = $childToRoot[$feeId]['id'];
             $title = $childToRoot[$feeId]['title'];
             $sort = $childToRoot[$feeId]['sort'];
-            $item['fee_id'] =  $rootId;
+            $item['fee_id'] = $rootId;
             $list[$k] = $item;
 
             //这边需要拿到头部所有的一级费用类型
-            if(!isset($type_list[$rootId])){
+            if (!isset($type_list[$rootId])) {
                 $type_list[$rootId] = [
-                    'sort' =>  $sort,
-                    'id' =>  $rootId,
-                    'title' =>  $title,
+                    'sort' => $sort,
+                    'id' => $rootId,
+                    'title' => $title,
                 ];
             }
         }
         // 使用 values() 丢弃原始键名,重新从 0 开始建立索引
         $type_list = collect($type_list)->sortBy('sort')->values()->all();
         // 4. 重置数组索引并返回
-        return [$list,$type_list];
+        return [$list, $type_list];
     }
 
 
-    public function auxiliaryEmployee($user,$data,$month_start,$month_end){
+    public function auxiliaryEmployee($user, $data, $month_start, $month_end)
+    {
         //确认所有项目、人员、人员工时
         $month_employee = DailyPwOrderDetails::Clear($user, $data);
         $month_employee_list = $month_employee->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)
@@ -934,7 +956,8 @@ class StatisticService extends Service
         return $item_year_list;
     }
 
-    public function auxiliaryDevice($user,$data,$month_start,$month_end){
+    public function auxiliaryDevice($user, $data, $month_start, $month_end)
+    {
         $month_device = DailyDwOrderDetails::Clear($user, $data);
         $month_device_list = $month_device->where("order_time", ">=", $month_start)->where("order_time", "<", $month_end)
             ->where('del_time', 0)
@@ -978,7 +1001,7 @@ class StatisticService extends Service
         // 3. 计算分摊天数与工资
         $item_year_list = [];
         foreach ($month_device_list as $item) {
-            $key = $item['device_id'] . '_' . $row['order_month'] ;
+            $key = $item['device_id'] . '_' . $row['order_month'];
             $device_key = $item['item_id'] . '_' . $item['order_month'];
             $total_depreciatio = $depreciatio_map[$key] ?? 0;
             $total_min = $device_monthly_total_min[$key] ?? 0;
@@ -1002,7 +1025,8 @@ class StatisticService extends Service
         return $item_year_list;
     }
 
-    public function itemEmployeeSalaryStatistic($data,$user){
+    public function itemEmployeeSalaryStatistic($data, $user)
+    {
         list($status, $month_start, $month_end) = $this->commonRule($data);
         if (!$status) return [false, $month_start];
         //项目编码、项目名称、姓名、人员类别、应出勤工时、研发出勤工时、研发工时占比、归集工资总额、归集社保金额、归集公积金、研发工资总额、研发社保金额、研发公积金
@@ -1030,7 +1054,7 @@ class StatisticService extends Service
             ->pluck('month_str', 'id')
             ->toArray();
         $month_employee_salary = MonthlyPsOrderDetails::wherein('main_id', $monthly_ps_order_ids)
-            ->select("employee_id", "salary", "main_id","social_insurance","public_housing_fund")
+            ->select("employee_id", "salary", "main_id", "social_insurance", "public_housing_fund")
             ->get()->toArray();
         //查询所有项目人员的工时比例
         //汇总每个人每个月工资
@@ -1056,7 +1080,7 @@ class StatisticService extends Service
         $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'] ;
+            $item_key = $item['order_month'] . '_' . $item['item_id'] . '_' . $item['employee_id'];
             if (!isset($item_month_list[$item_key])) {
                 $item_month_list[$item_key] = [
                     "month" => $item['order_month'],
@@ -1064,8 +1088,8 @@ class StatisticService extends Service
                     "employee_id" => $item['employee_id'],
                     "work_minutes" => 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,
+                    "social_insurance" => $salary_map[$key]['social_insurance'] ?? 0,
+                    "public_housing_fund" => $salary_map[$key]['public_housing_fund'] ?? 0,
                     "item_id" => $item['item_id'],
                 ];
             }
@@ -1074,8 +1098,8 @@ class StatisticService extends Service
 
             // B. 计算工资分摊:(项目工时 / 月总工时) * 月工资
             if ($total_min > 0) {
-                $ratio =  round($item['total_work'] / $total_min,4);
-            }else{
+                $ratio = round($item['total_work'] / $total_min, 4);
+            } else {
                 $ratio = 0;
             }
             $item_month_list[$item_key]['radio'] = $ratio;
@@ -1086,25 +1110,25 @@ class StatisticService extends Service
         $items = collect($item_month_list)->pluck('item_id')->unique()->values()->all();
         $employee_ids = collect($item_month_list)->pluck('employee_id')->unique()->values()->all();
         $employee = Employee::Clear($user, $data);
-        $employee_list = $employee->wherein('id', $employee_ids)->select("major","title", "id")->get()->toArray();
+        $employee_list = $employee->wherein('id', $employee_ids)->select("major", "title", "id")->get()->toArray();
         $employee_key_list = [];
-        foreach ($employee_list as $v){
+        foreach ($employee_list as $v) {
             $employee_key_list[$v['id']] = $v;
         }
         $item = Item::Clear($user, $data);
         $item_title_key_list = $item->wherein('id', $items)->pluck("title", "id")->toArray();
         $item_code_key_list = $item->wherein('id', $items)->pluck("code", "id")->toArray();
         //项目编码、项目名称、姓名、人员类别、应出勤工时、研发出勤工时、研发工时占比、归集工资总额、归集社保金额、归集公积金、研发工资总额、研发社保金额、研发公积金
-        $item_month_list = collect($item_month_list)->transform(function ($item) use ($item_title_key_list, $item_code_key_list,$employee_key_list) {
+        $item_month_list = collect($item_month_list)->transform(function ($item) use ($item_title_key_list, $item_code_key_list, $employee_key_list) {
             $item['item_title'] = $item_title_key_list[$item['item_id']] ?? "未知项目({$item['item_id']})";
             $item['item_code'] = $item_code_key_list[$item['item_id']] ?? "未知项目({$item['item_id']})";
             $item['employee_title'] = $employee_key_list[$item['employee_id']]['title'] ?? "未知人员({$item['employee_id']})";
             $item['major'] = $employee_key_list[$item['employee_id']]['major'] ?? "未知人员({$item['employee_id']})";
-            $item['total_min'] = round($item['total_min']/60,2);
-            $item['work_minutes'] = round($item['work_minutes']/60,2);
-            $item['work_salary'] = round($item['salary']*$item['radio'],2);
-            $item['work_social_insurance'] = round($item['social_insurance']*$item['radio'],2);
-            $item['work_public_housing_fund'] = round($item['public_housing_fund']*$item['radio'],2);
+            $item['total_min'] = round($item['total_min'] / 60, 2);
+            $item['work_minutes'] = round($item['work_minutes'] / 60, 2);
+            $item['work_salary'] = round($item['salary'] * $item['radio'], 2);
+            $item['work_social_insurance'] = round($item['social_insurance'] * $item['radio'], 2);
+            $item['work_public_housing_fund'] = round($item['public_housing_fund'] * $item['radio'], 2);
             return $item;
         })->all();
         return [true, $item_month_list];