|
@@ -97,7 +97,7 @@ class StatisticService extends StatisticCommonService
|
|
|
return [true, $month_employee_list];
|
|
return [true, $month_employee_list];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function employeeMonthSalaryStatistic($data, $user)
|
|
|
|
|
|
|
+ public function employeeMonthSalaryStatistic2($data, $user)
|
|
|
{
|
|
{
|
|
|
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];
|
|
@@ -252,6 +252,132 @@ class StatisticService extends StatisticCommonService
|
|
|
return [true, $result];
|
|
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)
|
|
public function employeeMonthSalaryStatistic1($data, $user)
|
|
|
{
|
|
{
|
|
|
//项目编码、项目名称、天数、工资、日期
|
|
//项目编码、项目名称、天数、工资、日期
|