false, 'message' => '日期参数错误:开始/结束日期超出考勤年月范围' ]; } // 2. 法定天数校验 $clockingInDay = Db::name('人事_考勤资料') ->where('kqzl_ny', $params['date']) ->field('法定天数') ->order('UniqId desc') ->find(); if (empty($clockingInDay)) { return ['success' => false, 'message' => '未查询到法定天数配置']; } $params['days'] = (int)$clockingInDay['法定天数']; // 3. 删除历史数据 Db::name('绩效工资汇总')->where('sczl_rq', 'between', [$startDate, $endDate])->delete(); // 4. 初始化最终数据容器 $finalData = []; // ====================== 设备产量计酬数据处理 ====================== $this->handleEquipmentProductionData($params, $finalData); // ====================== 拆片工序数据处理 ====================== $this->handleChipProcessData($params, $finalData); // ====================== 手工检验工序数据处理 ====================== $this->handleManualInspectionData($params, $finalData); // ====================== 包装计件工序数据处理 ====================== $this->handlePackagingPieceworkData($params, $finalData); // 5. 批量插入数据(核心:补充原代码缺失的插入逻辑) if (empty($finalData)) { return ['success' => false, 'message' => '未计算出有效工资数据']; } // 补充通用字段 foreach ($finalData as &$item) { $item['sys_ny'] = $attendanceMonth; $item['sys_rq'] = date('Y-m-d'); $item['sys_id'] = uniqid(); $item['法定天数'] = $params['days']; // 补充缺失的工资计算字段 $item['个人计件工资'] = $this->calculatePieceworkWage($item); $item['个人加班工资'] = $this->calculateOvertimeWage($item, $params); $item['达标定额'] = $item['日定额'] * $params['days']; $item['UniqID'] = $this->getNextUniqId(); } // 6. 批量插入数据库 $insertResult = Db::name('绩效工资汇总')->insertAll($finalData); if (!$insertResult) { throw new Exception('批量插入绩效工资数据失败'); } Log::info('工资计算完成', [ 'start_date' => (string)$startDate, 'end_date' => (string)$endDate, 'total_rows' => (int)count($finalData), 'insert_rows' => (int)$insertResult ]); return [ 'success' => true, 'message' => '工资计算完成', 'data' => [ 'total_rows' => count($finalData), 'insert_rows' => $insertResult ] ]; } catch (Exception $e) { LLog::error('工资计算服务执行失败', [ 'error' => (string)$e->getMessage(), 'trace' => (string)$e->getTraceAsString(), 'params' => json_encode($params, JSON_UNESCAPED_UNICODE) // 序列化复杂参数 ]); return [ 'success' => false, 'message' => '计算失败:' . $e->getMessage() ]; } } /** * 获取绩效工资汇总下一个UniqID * @return int */ private function getNextUniqId(): int { $lastUniqId = Db::name('绩效工资汇总')->field('UniqID')->order('UniqID desc')->find(); return empty($lastUniqId) ? 1 : (int)$lastUniqId['UniqID'] + 1; } /** * 计算个人计件工资 * @param array $item 单条数据 * @return string */ private function calculatePieceworkWage(array $item): string { if (empty($item['千件工价']) || empty($item['核算产量'])) { return '0.00'; } // 千件工价 * 核算产量 / 1000 $wage = (float)$item['千件工价'] * (float)$item['核算产量'] / 1000; return number_format($wage, 2); } /** * 计算个人加班工资 * @param array $item 单条数据 * @param array $params 全局参数 * @return string */ private function calculateOvertimeWage(array $item, array $params): string { // 示例逻辑:可根据实际业务调整 $overtimeWage = 0.00; if ($item['工时占比'] > 1) { // 超出定额部分按1.5倍计算加班 $overtimeRatio = $item['工时占比'] - 1; $overtimeWage = (float)$item['个人计件工资'] * $overtimeRatio * 1.5; } return number_format($overtimeWage, 2); } /** * 处理设备产量计酬数据 * @param array $params 全局参数 * @param array &$finalData 最终数据容器(引用传递) */ private function handleEquipmentProductionData(array $params, array &$finalData) { $where = ['a.sczl_rq' => ['between', [$params['start_date'], $params['end_date']]]]; // 查询印刷印后车间的机台 $sist = ['胶印车间', '凹丝印车间', '印后车间', '检验车间']; $jtbhs = Db::name('设备_基本资料') ->where('sys_sbID', '<>', '') ->where('使用部门', 'in', $sist) ->column('设备编号'); $fields = "a.sczl_gdbh,a.sczl_yjno,a.sczl_gxh,a.sczl_type as sczl_type,a.sczl_rq,a.sczl_jtbh,a.sczl_工价系数,a.sczl_ms,a.sczl_cl as 班组车头产量,a.sczl_Pgcl,a.sczl_zcfp, a.sczl_装版工时 as 装版工时,a.sczl_保养工时 as 保养工时,a.sczl_打样工时 as 打样工时,a.sczl_异常工时1 as 异常停机工时,a.sczl_设备运行工时 as 车头产量占用机时, a.sczl_bh1,a.sczl_bh2,a.sczl_bh3,a.sczl_bh4,a.sczl_bh5,a.sczl_bh6,a.sczl_bh7,a.sczl_bh8,a.sczl_bh9,a.sczl_bh10, a.sczl_rate1,a.sczl_rate2,a.sczl_rate3,a.sczl_rate4,a.sczl_rate5,a.sczl_rate6,a.sczl_rate7,a.sczl_rate8,a.sczl_rate9,a.sczl_rate10, a.sczl_废品率系数,a.UniqId, b.千件工价,b.日定额,b.补产标准,c.工价系数 as 工序难度系数,c.版距,c.印刷方式 ,d1.员工姓名 as name1,d2.员工姓名 as name2,d3.员工姓名 as name3,d4.员工姓名 as name4,d5.员工姓名 as name5,d6.员工姓名 as name6,d7.员工姓名 as name7,d8.员工姓名 as name8 ,d9.员工姓名 as name9,d10.员工姓名 as name10"; $list = Db::name('设备_产量计酬')->alias('a') ->field($fields) ->join('dic_lzde b', 'a.sczl_dedh = b.sys_bh', 'LEFT') ->join('工单_工艺资料 c', 'a.sczl_gdbh = c.Gy0_gdbh AND a.sczl_yjno = c.Gy0_yjno AND a.sczl_gxh = c.Gy0_gxh', 'LEFT') ->where($where) ->where('a.sczl_jtbh', 'in', $jtbhs) ->order('a.sczl_rq') ->group('UniqId') ->select(); foreach ($list as $value) { $num = 1; $value['班组车头产量'] = $value['班组车头产量'] - $value['sczl_zcfp']; $gxRate = 1.0000; // 计件产量计算逻辑 if (substr($value['sczl_jtbh'], 0, 2) == 'JP') {// 检品机 $gxRate = $value['sczl_废品率系数'] ?: 1; $value['班组车头产量'] = $value['班组车头产量'] * $value['sczl_Pgcl']; $byThePieceYield = round($value['班组车头产量'] * $gxRate); } elseif (in_array(substr($value['sczl_jtbh'], 0, 2), ['WY', 'DW']) || in_array(substr($value['sczl_jtbh'], 0, 3), ['YWY', 'YDW'])) {// 凹印机 $gxRate = $this->calculateGxRate($value); if (str_contains($value['印刷方式'], '张') && $value['版距'] > 0) { $value['版距'] = $value['版距'] / 1000; $value['班组车头产量'] = $value['班组车头产量'] * $value['版距']; } $byThePieceYield = str_replace(',', '', round($value['班组车头产量'] * floatval($gxRate))); } else {// 其他设备 if (in_array($value['sczl_jtbh'], ['YSY02#', 'YSY08#', 'YSY10#', 'SY03#'])) { $num = 1.1; } $gxRate = $this->calculateGxRate($value); $byThePieceYield = round($value['班组车头产量'] * floatval($gxRate) * $num); } // 补产产量/班组换算产量 $afterProductionYield = ($value['装版工时'] + $value['保养工时'] + $value['打样工时']) * $value['补产标准']; // 核算产量 $accountingYield = $byThePieceYield + $afterProductionYield; // 工时占比 $manHourRate = $value['日定额'] > 0 ? number_format($accountingYield / $value['日定额'], 4) : '0.0000'; // 循环处理10个员工维度 for ($i = 1; $i < 11; $i++) { $bhKey = 'sczl_bh' . $i; $xmKey = 'name' . $i; $rateKey = 'sczl_rate' . $i; if (!empty($value[$bhKey]) && $value[$bhKey] != '0000') { $finalData[] = [ 'sczl_gdbh' => $value['sczl_gdbh'], 'sczl_yjno' => $value['sczl_yjno'], 'sczl_gxh' => $value['sczl_gxh'], 'sczl_type' => $value['sczl_type'], 'sczl_rq' => $value['sczl_rq'], 'sczl_jtbh' => $value['sczl_jtbh'], '班组车头产量' => $value['班组车头产量'], '工价系数' => '0.0000', '工序难度系数' => $gxRate * $num, '装版工时' => $value['装版工时'], '保养工时' => $value['保养工时'], '打样工时' => $value['打样工时'], '异常停机工时' => $value['异常停机工时'], '车头产量占用机时' => $value['车头产量占用机时'], '日定额' => (int)$value['日定额'], '千件工价' => $value['千件工价'], '补产标准' => $value['补产标准'], '班组换算产量' => $afterProductionYield, '计时补差额工资' => '0.00', 'bh' => $value[$bhKey], 'xm' => $value[$xmKey], 'Rate' => $value[$rateKey], 'sczl_ms' => $value['sczl_ms'], '核算产量' => $accountingYield, '工时占比' => $manHourRate, ]; } } } } /** * 处理拆片工序数据 * @param array $params 全局参数 * @param array &$finalData 最终数据容器(引用传递) */ private function handleChipProcessData(array $params, array &$finalData) { $where = ['a.sczl_rq' => ['between', [$params['start_date'], $params['end_date']]]]; $list = Db::name('db_sczl')->alias('a') ->field('a.sczl_gdbh, a.sczl_yjno, a.sczl_gxh, sczl_type, a.sczl_rq, a.sczl_jtbh, a.sczl_ms, a.sczl_cl as 班组车头产量, a.sczl_fp as sczl_zcfp, a.sczl_装版工时 as 装版工时, a.sczl_保养工时 as 保养工时, a.sczl_打样工时 as 打样工时, a.sczl_异常停机工时 as 异常停机工时, a.sczl_设备运行工时 as 车头产量占用机时, a.sczl_bh1, a.sczl_rate1, b.千件工价, b.日定额, b.补产标准,a.sczl_工价系数 as 工序难度系数,a.拆片联拼系数,a.拆片条小盒系数, d.员工姓名') ->join('dic_lzde b', 'a.sczl_dedh = b.sys_bh', 'left') ->join('工单_工艺资料 c', 'a.sczl_gdbh = c.Gy0_gdbh AND a.sczl_yjno = c.Gy0_yjno AND a.sczl_gxh = c.Gy0_gxh', 'left') ->join('人事_基本资料 d', 'a.sczl_bh1 = d.员工编号', 'left') ->where($where) ->select(); foreach ($list as $value) { $num = 1; $value['班组车头产量'] = ($value['班组车头产量'] - $value['sczl_zcfp']) * $value['拆片联拼系数'] * $value['拆片条小盒系数']; if (in_array($value['sczl_jtbh'], ['YSY02#', 'YSY08#', 'YSY10#', 'SY03#'])) { $num = 1.1; } $gxRate = $value['工序难度系数'] ?: 1.0000; $byThePieceYield = round($value['班组车头产量'] * $gxRate * $num); $afterProductionYield = ($value['装版工时'] + $value['保养工时'] + $value['打样工时']) * $value['补产标准']; $accountingYield = $byThePieceYield + $afterProductionYield; $manHourRate = $value['日定额'] > 0 ? number_format($accountingYield / $value['日定额'], 4) : '0.0000'; $finalData[] = [ 'sczl_gdbh' => $value['sczl_gdbh'], 'sczl_yjno' => $value['sczl_yjno'], 'sczl_gxh' => $value['sczl_gxh'], 'sczl_type' => $value['sczl_type'], 'sczl_rq' => $value['sczl_rq'], 'sczl_jtbh' => $value['sczl_jtbh'], '班组车头产量' => $value['班组车头产量'], '工价系数' => '0.0000', '工序难度系数' => $gxRate * $num, '装版工时' => $value['装版工时'], '保养工时' => $value['保养工时'], '打样工时' => $value['打样工时'], '异常停机工时' => $value['异常停机工时'], '车头产量占用机时' => $value['车头产量占用机时'], '日定额' => (int)$value['日定额'], '千件工价' => $value['千件工价'], '补产标准' => $value['补产标准'], '班组换算产量' => $afterProductionYield, '计时补差额工资' => '0.00', 'bh' => $value['sczl_bh1'], 'xm' => $value['员工姓名'], 'Rate' => $value['sczl_rate1'], 'sczl_ms' => $value['sczl_ms'], '核算产量' => $accountingYield, '工时占比' => $manHourRate, ]; } } /** * 处理手工检验工序数据 * @param array $params 全局参数 * @param array &$finalData 最终数据容器(引用传递) */ private function handleManualInspectionData(array $params, array &$finalData) { $where = ['a.sczl_rq' => ['between', [$params['start_date'], $params['end_date']]]]; $fields = "a.sczl_gdbh,a.sczl_yjgx,sczl_gxmc AS sczl_type,a.sczl_rq,a.sczl_cl AS 班组车头产量, sczl_废品率系数 AS 工序难度系数,a.sczl_bh0,a.sczl_bh1,a.sczl_bh2,a.sczl_bh3,a.sczl_bh4,a.sczl_bh5,a.sczl_bh6, a.sczl_bh7,a.sczl_bh8,a.sczl_bh9,a.sczl_bh10,a.sczl_bh11,b.千件工价,b.日定额,b.补产标准,d0.员工姓名 AS name0, d1.员工姓名 AS name1,d2.员工姓名 AS name2,d3.员工姓名 AS name3,d4.员工姓名 AS name4,d5.员工姓名 AS name5, d6.员工姓名 AS name6,d7.员工姓名 AS name7,d8.员工姓名 AS name8,d9.员工姓名 AS name9,d10.员工姓名 AS name10, d10.员工姓名 AS name11,d10.员工姓名 AS name12,a.sczl_cl0,a.sczl_cl1,a.sczl_cl2,a.sczl_cl3,a.sczl_cl4,a.sczl_cl5,a.sczl_cl6, a.sczl_cl7,a.sczl_cl8,a.sczl_cl9,a.sczl_cl10,a.sczl_cl11,a.sczl_cl12,a.sczl_fp0,a.sczl_fp1,a.sczl_fp2,a.sczl_fp3, a.sczl_fp4,a.sczl_fp5,a.sczl_fp6,a.sczl_fp7,a.sczl_fp8,a.sczl_fp9,a.sczl_fp10,a.sczl_fp11,a.sczl_fp12"; $list = Db::name('db_手工检验')->alias('a') ->field($fields) ->join('dic_lzde b', 'a.sczl_dedh = b.sys_bh', 'LEFT') ->where($where) ->select(); // 循环连接人事表(原代码的循环join移到这里,避免SQL语法错误) for ($i = 0; $i <= 12; $i++) { $list = $this->joinStaffTable($list, $i); } foreach ($list as $value) { for ($i = 0; $i <= 12; $i++) { if (!empty($value['sczl_bh' . $i])) { $value['sczl_yjno'] = (int)substr($value['sczl_yjgx'], 0, 2); $value['sczl_gxh'] = (int)substr($value['sczl_yjgx'], 3, 2); $value['班组车头产量'] = $value['sczl_cl' . $i] * $value['sczl_fp' . $i]; $gxRate = $value['工序难度系数'] ?: 1.0000; $byThePieceYield = round($value['班组车头产量'] * $gxRate); $accountingYield = $byThePieceYield; $manHourRate = $value['日定额'] > 0 ? round($accountingYield / $value['日定额'], 4) : '0.0000'; $finalData[] = [ 'sczl_gdbh' => $value['sczl_gdbh'], 'sczl_yjno' => $value['sczl_yjno'], 'sczl_gxh' => $value['sczl_gxh'], 'sczl_type' => $value['sczl_type'], 'sczl_rq' => $value['sczl_rq'], 'sczl_jtbh' => '', '班组车头产量' => $value['班组车头产量'], '工价系数' => '0.0000', '工序难度系数' => $gxRate, '装版工时' => '0.00', '保养工时' => '0.00', '打样工时' => '0.00', '异常停机工时' => '0.00', '车头产量占用机时' => '0.00', '日定额' => (int)$value['日定额'], '千件工价' => $value['千件工价'], '补产标准' => $value['补产标准'], '班组换算产量' => '0', '计时补差额工资' => '0.00', 'bh' => $value['sczl_bh' . $i], 'xm' => $value['name' . $i], 'Rate' => '1.0000', 'sczl_ms' => '0.00', '核算产量' => $accountingYield, '工时占比' => $manHourRate, ]; } } } } /** * 处理包装计件工序数据(补充原代码截断部分) * @param array $params 全局参数 * @param array &$finalData 最终数据容器(引用传递) */ private function handlePackagingPieceworkData(array $params, array &$finalData) { $where = ['a.sczl_rq' => ['between', [$params['start_date'], $params['end_date']]]]; $list = Db::name('db_包装计件')->alias('a') ->field('a.sczl_gdbh1, a.sczl_gdbh2, a.sczl_gdbh3, a.sczl_gdbh4, a.sczl_gdbh5, a.sczl_gdbh6, a.sczl_yjGx1, a.sczl_yjGx2, a.sczl_yjGx3, a.sczl_yjGx4, a.sczl_yjGx5, a.sczl_yjGx6, rtrim(a.sczl_gxmc1) as gxmc1,rtrim(a.sczl_gxmc2) as gxmc2,rtrim(a.sczl_gxmc3) as gxmc3,rtrim(a.sczl_gxmc4) as gxmc4,rtrim(a.sczl_gxmc5) as gxmc5,rtrim(a.sczl_gxmc6) as gxmc6, a.sczl_cl1, a.sczl_cl2, a.sczl_cl3, a.sczl_cl4, a.sczl_cl5, a.sczl_cl6, a.sczl_返工产量1, a.sczl_返工产量2, a.sczl_返工产量3, a.sczl_返工产量4, a.sczl_返工产量5, a.sczl_返工产量6, a.sczl_Jtbh1,a.sczl_Jtbh2,a.sczl_Jtbh3,a.sczl_Jtbh4,a.sczl_Jtbh5,a.sczl_Jtbh6, a.sczl_dedh1,a.sczl_dedh2,a.sczl_dedh3,a.sczl_dedh4,a.sczl_dedh5,a.sczl_dedh6') ->where($where) ->select(); // 循环处理6个维度的包装数据 for ($i = 1; $i <= 6; $i++) { foreach ($list as $value) { $gdbhKey = 'sczl_gdbh' . $i; $yjGxKey = 'sczl_yjGx' . $i; $gxmcKey = 'gxmc' . $i; $clKey = 'sczl_cl' . $i; $reworkKey = 'sczl_返工产量' . $i; $jtbhKey = 'sczl_Jtbh' . $i; $dedhKey = 'sczl_dedh' . $i; if (empty($value[$gdbhKey])) continue; // 查询千件工价等配置 $lzdeData = Db::name('dic_lzde')->where('sys_bh', $value[$dedhKey])->find(); $dailyQuota = $lzdeData['日定额'] ?? 0; $piecePrice = $lzdeData['千件工价'] ?? 0; $supplementStandard = $lzdeData['补产标准'] ?? 0; // 产量计算(含返工产量) $totalYield = $value[$clKey] + $value[$reworkKey]; $accountingYield = $totalYield; $manHourRate = $dailyQuota > 0 ? number_format($accountingYield / $dailyQuota, 4) : '0.0000'; $finalData[] = [ 'sczl_gdbh' => $value[$gdbhKey], 'sczl_yjno' => (int)substr($value[$yjGxKey], 0, 2) ?: 0, 'sczl_gxh' => (int)substr($value[$yjGxKey], 3, 2) ?: 0, 'sczl_type' => $value[$gxmcKey], 'sczl_rq' => $params['start_date'], // 可根据实际业务调整 'sczl_jtbh' => $value[$jtbhKey] ?? '', '班组车头产量' => $totalYield, '工价系数' => '0.0000', '工序难度系数' => 1.0000, '装版工时' => '0.00', '保养工时' => '0.00', '打样工时' => '0.00', '异常停机工时' => '0.00', '车头产量占用机时' => '0.00', '日定额' => (int)$dailyQuota, '千件工价' => $piecePrice, '补产标准' => $supplementStandard, '班组换算产量' => '0', '计时补差额工资' => '0.00', 'bh' => '', // 包装工序员工编号需根据实际业务补充 'xm' => '', // 包装工序员工姓名需根据实际业务补充 'Rate' => '1.0000', 'sczl_ms' => '包装计件', '核算产量' => $accountingYield, '工时占比' => $manHourRate, ]; } } } /** * 计算工序难度系数 * @param array $value 单条数据 * @return float */ private function calculateGxRate(array $value): float { $sczlRate = $value['sczl_工价系数'] ?: 0; $processRate = $value['工序难度系数'] ?: 0; if ($sczlRate <= 0) { return $processRate > 0 ? $processRate : 1.0000; } return $processRate > 0 ? number_format($sczlRate * $processRate, 3) : $sczlRate; } /** * 手动连接人事表数据(替代原SQL循环join,避免语法错误) * @param array $list 原查询结果 * @param int $i 员工索引 * @return array */ private function joinStaffTable(array $list, int $i): array { $staffData = Db::name('人事_基本资料')->column('员工姓名', '员工编号'); foreach ($list as &$item) { $bhKey = 'sczl_bh' . $i; $xmKey = 'name' . $i; $item[$xmKey] = $staffData[$item[$bhKey]] ?? ''; } return $list; } }