|
|
@@ -1428,6 +1428,121 @@ class StatisticService extends StatisticCommonService
|
|
|
}
|
|
|
|
|
|
public function itemEmployeeSalaryStatistic($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();
|
|
|
+
|
|
|
+ // 3. 获取工资主表与月份映射
|
|
|
+ $ps_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();
|
|
|
+ $month_map = $ps_query->select('id', DB::raw("FROM_UNIXTIME(month, '%Y-%m') as month_str"))->pluck('month_str', 'id')->toArray();
|
|
|
+
|
|
|
+ // 4. 获取详细工资并按人+月汇总 (单位:分)
|
|
|
+ $salary_details = MonthlyPsOrderDetails::whereIn('main_id', $monthly_ps_order_ids)
|
|
|
+ ->select("employee_id", "main_id", "social_insurance", "public_housing_fund",
|
|
|
+ DB::raw("(base_salary + performance_salary + bonus + other) as salary_orig"))->get();
|
|
|
+
|
|
|
+ $salary_pool = []; // 格式:[key][field] => cents
|
|
|
+ foreach ($salary_details as $val) {
|
|
|
+ $m = $month_map[$val['main_id']] ?? '';
|
|
|
+ if ($m) {
|
|
|
+ $key = $val['employee_id'] . '_' . $m;
|
|
|
+ if (!isset($salary_pool[$key])) {
|
|
|
+ $salary_pool[$key] = ['s' => 0, 'si' => 0, 'phf' => 0];
|
|
|
+ }
|
|
|
+ $salary_pool[$key]['s'] += (int)round($val['salary_orig'] * 100);
|
|
|
+ $salary_pool[$key]['si'] += (int)round($val['social_insurance'] * 100);
|
|
|
+ $salary_pool[$key]['phf'] += (int)round($val['public_housing_fund'] * 100);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 统计总工时与项目计数
|
|
|
+ $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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 分摊计算
|
|
|
+ $rem_pool = $salary_pool; // 剩余金额池
|
|
|
+ $rem_work_pool = []; // 剩余工时池 (小时*100)
|
|
|
+ foreach ($emp_total_min as $k => $min) {
|
|
|
+ $rem_work_pool[$k] = (int)round(($min / 60) * 100);
|
|
|
+ }
|
|
|
+
|
|
|
+ $final_list = [];
|
|
|
+ foreach ($month_employee_list as $item) {
|
|
|
+ $key = $item->employee_id . '_' . $item->order_month;
|
|
|
+ $total_m = $emp_total_min[$key] ?? 0;
|
|
|
+
|
|
|
+ $current_hours_cents = (int)round(($item->total_work / 60) * 100);
|
|
|
+
|
|
|
+ if ($total_m > 0 && --$emp_item_count[$key] > 0) {
|
|
|
+ // 非最后一项:按高精度比例计算
|
|
|
+ $ratio = $item->total_work / $total_m;
|
|
|
+
|
|
|
+ $work_s = (int)floor($ratio * $salary_pool[$key]['s']);
|
|
|
+ $work_si = (int)floor($ratio * $salary_pool[$key]['si']);
|
|
|
+ $work_phf = (int)floor($ratio * $salary_pool[$key]['phf']);
|
|
|
+
|
|
|
+ $rem_pool[$key]['s'] -= $work_s;
|
|
|
+ $rem_pool[$key]['si'] -= $work_si;
|
|
|
+ $rem_pool[$key]['phf'] -= $work_phf;
|
|
|
+ $rem_work_pool[$key] -= $current_hours_cents;
|
|
|
+ } else {
|
|
|
+ // 最后一项:平账
|
|
|
+ $work_s = $rem_pool[$key]['s'] ?? 0;
|
|
|
+ $work_si = $rem_pool[$key]['si'] ?? 0;
|
|
|
+ $work_phf = $rem_pool[$key]['phf'] ?? 0;
|
|
|
+ $current_hours_cents = $rem_work_pool[$key] ?? 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ $final_list[] = [
|
|
|
+ 'month' => $item->order_month,
|
|
|
+ 'item_id' => $item->item_id,
|
|
|
+ 'employee_id' => $item->employee_id,
|
|
|
+ 'total_hours' => round($total_m / 60, 2),
|
|
|
+ 'work_hours' => round($current_hours_cents / 100, 2),
|
|
|
+ 'ratio' => $total_m > 0 ? round($item->total_work / $total_m, 4) : 0,
|
|
|
+ // 归集金额
|
|
|
+ 'total_salary' => ($salary_pool[$key]['s'] ?? 0) / 100,
|
|
|
+ 'total_si' => ($salary_pool[$key]['si'] ?? 0) / 100,
|
|
|
+ 'total_phf' => ($salary_pool[$key]['phf'] ?? 0) / 100,
|
|
|
+ // 研发分摊金额
|
|
|
+ 'work_salary' => $work_s / 100,
|
|
|
+ 'work_si' => $work_si / 100,
|
|
|
+ 'work_phf' => $work_phf / 100,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 补充基础信息 (Item, Employee)
|
|
|
+ $i_ids = collect($final_list)->pluck('item_id')->unique()->all();
|
|
|
+ $e_ids = collect($final_list)->pluck('employee_id')->unique()->all();
|
|
|
+ $i_info = Item::TopClear($user, $data)->whereIn('id', $i_ids)->get()->keyBy('id');
|
|
|
+ $e_info = Employee::TopClear($user, $data)->whereIn('id', $e_ids)->get()->keyBy('id');
|
|
|
+
|
|
|
+ // 8. 格式化输出
|
|
|
+ $result = collect($final_list)->sortBy('month')->transform(function ($v) use ($i_info, $e_info) {
|
|
|
+ $v['item_title'] = $i_info[$v['item_id']]->title ?? "未知({$v['item_id']})";
|
|
|
+ $v['item_code'] = $i_info[$v['item_id']]->code ?? "-";
|
|
|
+ $v['employee_title'] = $e_info[$v['employee_id']]->title ?? "未知({$v['employee_id']})";
|
|
|
+ $v['major'] = $e_info[$v['employee_id']]->major ?? "-";
|
|
|
+ return $v;
|
|
|
+ })->values()->all();
|
|
|
+
|
|
|
+ return [true, $result];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function itemEmployeeSalaryStatistic1($data, $user)
|
|
|
{
|
|
|
list($status, $month_start, $month_end) = $this->commonRule($data);
|
|
|
if (!$status) return [false, $month_start];
|