|
|
@@ -1385,6 +1385,140 @@ class ImportService extends Service
|
|
|
}
|
|
|
|
|
|
private function monthPwOrderCheck(&$array, $user, $table_config)
|
|
|
+ {
|
|
|
+ $keys = array_column($table_config, 'key');
|
|
|
+ $codeIdx = array_search('code', $keys);
|
|
|
+ $monthIdx = array_search('month', $keys);
|
|
|
+ $empIdx = array_search('employee_id', $keys);
|
|
|
+ $numIdx = array_search('total_days', $keys);
|
|
|
+ $num2Idx = array_search('rd_total_days', $keys);
|
|
|
+ $num3Idx = array_search('total_hours', $keys);
|
|
|
+ $num4Idx = array_search('rd_total_hours', $keys);
|
|
|
+
|
|
|
+ $topDepartId = $user['top_depart_id'];
|
|
|
+ $errors = [];
|
|
|
+
|
|
|
+ // --- 1. 预处理月份与工号 ---
|
|
|
+ $allEmpNumbers = [];
|
|
|
+ $allMonthsTs = [];
|
|
|
+ foreach ($array as $rowIndex => $row) {
|
|
|
+ $valMonthRaw = trim($row[$monthIdx] ?? '');
|
|
|
+ list($mStatus, $valMonthTs) = $this->convertExcelCellToDate($valMonthRaw);
|
|
|
+ if (!$mStatus) {
|
|
|
+ $errors[] = "第" . ($rowIndex + 1) . "行:月份格式错误";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $valMonthTs = strtotime(date('Y-m-01', $valMonthTs));
|
|
|
+ $array[$rowIndex][$monthIdx] = $valMonthTs;
|
|
|
+ $allMonthsTs[] = $valMonthTs;
|
|
|
+ if (!empty($row[$empIdx])) $allEmpNumbers[] = trim($row[$empIdx]);
|
|
|
+ }
|
|
|
+ $allMonthsTs = array_unique($allMonthsTs);
|
|
|
+ $allEmpNumbers = array_unique($allEmpNumbers);
|
|
|
+
|
|
|
+ // --- 2. 批量预加载 ---
|
|
|
+ // A. 人员档案
|
|
|
+ $empModels = Employee::where('del_time', 0)->whereIn('number', $allEmpNumbers)
|
|
|
+ ->where('top_depart_id', $topDepartId)->get(['id', 'number', 'title'])->keyBy('number');
|
|
|
+ $dbEmps = $empModels->pluck('id', 'number')->toArray();
|
|
|
+
|
|
|
+ // B. 系统考勤基准 (按月获取)
|
|
|
+ $systemStatsMap = [];
|
|
|
+ $service = new EmployeeService();
|
|
|
+ foreach ($allMonthsTs as $ts) {
|
|
|
+ list($sStatus, $stats) = $service->getEmployeesMonthStats(array_values($dbEmps), $ts, $user);
|
|
|
+ if (!$sStatus) {
|
|
|
+ $errors[] = "月份[" . date('Y-m', $ts) . "]:" . $stats;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $systemStatsMap[$ts] = $stats;
|
|
|
+ }
|
|
|
+ if (!empty($errors)) return [implode('|', $errors), [], []];
|
|
|
+
|
|
|
+ // C. 核心修正:单据查重(建立 月份 -> 单号 的映射,避免循环内查询)
|
|
|
+ $allCodes = array_filter(array_unique(array_column($array, $codeIdx)));
|
|
|
+
|
|
|
+ // 1) 所有的 Excel 涉及月份在数据库中已有的单据映射
|
|
|
+ $existingMonthsMap = DB::table('monthly_pw_order')
|
|
|
+ ->where('top_depart_id', $topDepartId)
|
|
|
+ ->where('del_time', 0)
|
|
|
+ ->whereIn('month', $allMonthsTs)
|
|
|
+ ->pluck('code', 'month') // 注意:这里直接查出 month => code
|
|
|
+ ->toArray();
|
|
|
+
|
|
|
+ // 2) 用户填写的单据号对应的数据库记录
|
|
|
+ $dbOrdersByCode = DB::table('monthly_pw_order')
|
|
|
+ ->whereIn('code', $allCodes)
|
|
|
+ ->where('top_depart_id', $topDepartId)
|
|
|
+ ->where('del_time', 0)
|
|
|
+ ->get()
|
|
|
+ ->keyBy('code');
|
|
|
+
|
|
|
+ // --- 3. 业务逻辑全量检查 ---
|
|
|
+ $excelAggregator = [];
|
|
|
+ $update_map = [];
|
|
|
+
|
|
|
+ foreach ($array as $rowIndex => $row) {
|
|
|
+ $line = $rowIndex + 1;
|
|
|
+ $valCode = trim($row[$codeIdx] ?? '');
|
|
|
+ $valMonthTs = $row[$monthIdx];
|
|
|
+ $valEmpNum = trim($row[$empIdx] ?? '');
|
|
|
+ $monthStr = date('Y-m', $valMonthTs);
|
|
|
+
|
|
|
+ if (!isset($empModels[$valEmpNum])) {
|
|
|
+ $errors[] = "第{$line}行:工号[{$valEmpNum}]不存在";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $empId = $empModels[$valEmpNum]->id;
|
|
|
+ $empTitle = "[{$valEmpNum}]" . $empModels[$valEmpNum]->title;
|
|
|
+
|
|
|
+ // --- 核心修正:单据单号提示逻辑 ---
|
|
|
+ if ($valCode !== '') {
|
|
|
+ if (isset($dbOrdersByCode[$valCode])) {
|
|
|
+ if ($dbOrdersByCode[$valCode]->month != $valMonthTs) {
|
|
|
+ $errors[] = "第{$line}行:单据[{$valCode}]月份应为" . date('Y-m', $dbOrdersByCode[$valCode]->month) . ",请核实";
|
|
|
+ }
|
|
|
+ $update_map[$rowIndex] = $dbOrdersByCode[$valCode]->id;
|
|
|
+ } else {
|
|
|
+ $errors[] = "第{$line}行:填写的单据编号[{$valCode}]在系统中不存在";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没填单号,但该月已存在单据,从预加载的 existingMonthsMap 中获取正确单号
|
|
|
+ if (isset($existingMonthsMap[$valMonthTs])) {
|
|
|
+ $correctCode = $existingMonthsMap[$valMonthTs];
|
|
|
+ $errors[] = "第{$line}行:月份[{$monthStr}]已存在研发单,单号为:[{$correctCode}],请填写该单号进行编辑";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- 考勤上限校验 ---
|
|
|
+ $sysEmpData = $systemStatsMap[$valMonthTs][$empId] ?? null;
|
|
|
+ if ($sysEmpData) {
|
|
|
+ $inTotalDays = (float)($row[$numIdx] ?? 0);
|
|
|
+ $inRdDays = (float)($row[$num2Idx] ?? 0);
|
|
|
+ $inTotalHours= (float)($row[$num3Idx] ?? 0);
|
|
|
+ $inRdHours = (float)($row[$num4Idx] ?? 0);
|
|
|
+
|
|
|
+ // 使用误差范围比较,避免浮点数精度问题
|
|
|
+ if (abs($inTotalDays - $sysEmpData['attendance_days']) > 0.01) {
|
|
|
+ $errors[] = "第{$line}行:{$empTitle}出勤总天数({$inTotalDays})与核算({$sysEmpData['attendance_days']})不符";
|
|
|
+ }
|
|
|
+ if (abs($inTotalHours - $sysEmpData['final_work_hour']) > 0.01) {
|
|
|
+ $errors[] = "第{$line}行:{$empTitle}出勤总工时({$inTotalHours})与核算({$sysEmpData['final_work_hour']})不符";
|
|
|
+ }
|
|
|
+ if ($inRdDays > $inTotalDays + 0.01) $errors[] = "第{$line}行:{$empTitle}研发天数超过总天数";
|
|
|
+ if ($inRdHours > $inTotalHours + 0.01) $errors[] = "第{$line}行:{$empTitle}研发工时超过总工时";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($excelAggregator[$valCode ?: $valMonthTs]['emps'][$valEmpNum])) {
|
|
|
+ $errors[] = "第{$line}行:人员在同单据内重复";
|
|
|
+ }
|
|
|
+ $excelAggregator[$valCode ?: $valMonthTs]['emps'][$valEmpNum] = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return [!empty($errors) ? implode('|', $errors) : "", $update_map, $dbEmps];
|
|
|
+ }
|
|
|
+
|
|
|
+ private function monthPwOrderCheck1(&$array, $user, $table_config)
|
|
|
{
|
|
|
$keys = array_column($table_config, 'key');
|
|
|
$codeIdx = array_search('code', $keys);
|
|
|
@@ -1634,8 +1768,151 @@ class ImportService extends Service
|
|
|
$codeIdx = array_search('code', $keys);
|
|
|
$monthIdx = array_search('month', $keys);
|
|
|
$devIdx = array_search('device_id', $keys);
|
|
|
- $startIdx = array_search('start_time', $keys);
|
|
|
- $endIdx = array_search('end_time', $keys);
|
|
|
+ $numIdx = array_search('total_days', $keys);
|
|
|
+ $num2Idx = array_search('rd_total_days', $keys);
|
|
|
+ $num3Idx = array_search('total_hours', $keys);
|
|
|
+ $num4Idx = array_search('rd_total_hours', $keys);
|
|
|
+
|
|
|
+ $topDepartId = $user['top_depart_id'];
|
|
|
+ $errors = [];
|
|
|
+
|
|
|
+ // --- 1. 提取月份和资产编码 ---
|
|
|
+ $allDevCodes = [];
|
|
|
+ $allMonthsTs = [];
|
|
|
+ foreach ($array as $rowIndex => $row) {
|
|
|
+ list($mStatus, $valMonthTs) = $this->convertExcelCellToDate($row[$monthIdx] ?? '');
|
|
|
+ if (!$mStatus) {
|
|
|
+ $errors[] = "第" . ($rowIndex + 1) . "行:月份格式错误";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $valMonthTs = strtotime(date('Y-m-01', $valMonthTs));
|
|
|
+ $array[$rowIndex][$monthIdx] = $valMonthTs;
|
|
|
+ $allMonthsTs[] = $valMonthTs;
|
|
|
+ if (!empty($row[$devIdx])) $allDevCodes[] = trim($row[$devIdx]);
|
|
|
+ }
|
|
|
+ $allMonthsTs = array_unique($allMonthsTs);
|
|
|
+
|
|
|
+ // --- 2. 预加载档案与基准 ---
|
|
|
+ $devModels = Device::where('del_time', 0)
|
|
|
+ ->whereIn('code', $allDevCodes)
|
|
|
+ ->where('top_depart_id', $topDepartId)
|
|
|
+ ->get(['id', 'code', 'title'])
|
|
|
+ ->keyBy('code');
|
|
|
+ $dbDevs = $devModels->pluck('id', 'code')->toArray();
|
|
|
+
|
|
|
+ $systemStatsMap = [];
|
|
|
+ $allDeviceIds = array_values($dbDevs);
|
|
|
+ $service = new DeviceService();
|
|
|
+ foreach ($allMonthsTs as $ts) {
|
|
|
+ list($sStatus, $stats) = $service->getDevicesMonthStats($allDeviceIds, $ts, $user);
|
|
|
+ if (!$sStatus) {
|
|
|
+ $errors[] = "月份[" . date('Y-m', $ts) . "]解析失败:{$stats}";
|
|
|
+ $systemStatsMap[$ts] = null;
|
|
|
+ } else {
|
|
|
+ $systemStatsMap[$ts] = $stats;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果基准获取有误(如日历未设),直接返回错误,不执行后续逻辑
|
|
|
+ if (!empty($errors)) return [implode('|', $errors), [], []];
|
|
|
+
|
|
|
+ // --- 3. 核心修正:单据查重与单号映射 (避免循环内查询) ---
|
|
|
+ $allCodes = array_filter(array_unique(array_column($array, $codeIdx)));
|
|
|
+
|
|
|
+ // 获取该租户相关月份已存在的单据映射 (Month => Code)
|
|
|
+ $existingMonthsMap = DB::table('monthly_dw_order')
|
|
|
+ ->where('top_depart_id', $topDepartId)
|
|
|
+ ->where('del_time', 0)
|
|
|
+ ->whereIn('month', $allMonthsTs)
|
|
|
+ ->pluck('code', 'month')
|
|
|
+ ->toArray();
|
|
|
+
|
|
|
+ // 获取 Excel 中填写的单号对应的数据库记录 (Code => Object)
|
|
|
+ $dbOrdersByCode = DB::table('monthly_dw_order')
|
|
|
+ ->whereIn('code', $allCodes)
|
|
|
+ ->where('top_depart_id', $topDepartId)
|
|
|
+ ->where('del_time', 0)
|
|
|
+ ->get()
|
|
|
+ ->keyBy('code');
|
|
|
+
|
|
|
+ $excelAggregator = [];
|
|
|
+ $update_map = [];
|
|
|
+
|
|
|
+ // --- 4. 循环校验 ---
|
|
|
+ foreach ($array as $rowIndex => $row) {
|
|
|
+ $line = $rowIndex + 1;
|
|
|
+ $valMonthTs = $row[$monthIdx];
|
|
|
+ $monthStr = date('Y-m', $valMonthTs);
|
|
|
+ $valDevCode = trim($row[$devIdx] ?? '');
|
|
|
+ $valCode = trim($row[$codeIdx] ?? '');
|
|
|
+
|
|
|
+ // A. 设备档案检查
|
|
|
+ if (!isset($devModels[$valDevCode])) {
|
|
|
+ $errors[] = "第{$line}行:设备编码[{$valDevCode}]不存在";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $deviceId = $devModels[$valDevCode]->id;
|
|
|
+ $devTitle = "[{$valDevCode}]" . $devModels[$valDevCode]->title;
|
|
|
+
|
|
|
+ // B. 单据号存在性与意图校验
|
|
|
+ if ($valCode !== '') {
|
|
|
+ // 意图:编辑已存在的单据
|
|
|
+ if (isset($dbOrdersByCode[$valCode])) {
|
|
|
+ $dbOrder = $dbOrdersByCode[$valCode];
|
|
|
+ if ($dbOrder->month != $valMonthTs) {
|
|
|
+ $errors[] = "第{$line}行:单据[{$valCode}]所属月份为" . date('Y-m', $dbOrder->month) . ",与当前月份不符";
|
|
|
+ }
|
|
|
+ $update_map[$rowIndex] = $dbOrder->id;
|
|
|
+ } else {
|
|
|
+ // 填了单号但库里没有
|
|
|
+ $errors[] = "第{$line}行:单据编号[{$valCode}]无效或不存在";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 意图:新增。校验是否该月已有单据
|
|
|
+ if (isset($existingMonthsMap[$valMonthTs])) {
|
|
|
+ $correctCode = $existingMonthsMap[$valMonthTs];
|
|
|
+ $errors[] = "第{$line}行:月份[{$monthStr}]已存在研发单,单号为:[{$correctCode}],请填写该单号进行更新";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // C. 考勤基准对比
|
|
|
+ $sysData = $systemStatsMap[$valMonthTs][$deviceId] ?? null;
|
|
|
+ if ($sysData) {
|
|
|
+ $inTotalDays = (float)($row[$numIdx] ?? 0);
|
|
|
+ $inRdDays = (float)($row[$num2Idx] ?? 0);
|
|
|
+ $inTotalHours= (float)($row[$num3Idx] ?? 0);
|
|
|
+ $inRdHours = (float)($row[$num4Idx] ?? 0);
|
|
|
+
|
|
|
+ // 1. 内部逻辑:研发 <= 总额
|
|
|
+ if ($inRdDays > $inTotalDays + 0.01) $errors[] = "第{$line}行:{$devTitle}研发天数大于总天数";
|
|
|
+ if ($inRdHours > $inTotalHours + 0.01) $errors[] = "第{$line}行:{$devTitle}研发工时大于总工时";
|
|
|
+
|
|
|
+ // 2. 外部逻辑:总额 == 系统核算
|
|
|
+ if (abs($inTotalDays - $sysData['attendance_days']) > 0.01) {
|
|
|
+ $errors[] = "第{$line}行:{$devTitle}总天数({$inTotalDays})不等于系统核算({$sysData['attendance_days']})";
|
|
|
+ }
|
|
|
+ if (abs($inTotalHours - $sysData['final_work_hour']) > 0.01) {
|
|
|
+ $errors[] = "第{$line}行:{$devTitle}总工时({$inTotalHours})不等于系统核算({$sysData['final_work_hour']})";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // D. 组内查重 (单号或月份作为聚合维度)
|
|
|
+ $aggKey = $valCode ?: "NEW_" . $valMonthTs;
|
|
|
+ if (isset($excelAggregator[$aggKey]['devs'][$valDevCode])) {
|
|
|
+ $errors[] = "第{$line}行:资产编码[{$valDevCode}]在同单据中重复";
|
|
|
+ }
|
|
|
+ $excelAggregator[$aggKey]['devs'][$valDevCode] = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ $error_string = !empty($errors) ? implode('|', $errors) : "";
|
|
|
+ return [$error_string, $update_map, $dbDevs];
|
|
|
+ }
|
|
|
+
|
|
|
+ private function monthDwOrderCheck1(&$array, $user, $table_config)
|
|
|
+ {
|
|
|
+ $keys = array_column($table_config, 'key');
|
|
|
+ $codeIdx = array_search('code', $keys);
|
|
|
+ $monthIdx = array_search('month', $keys);
|
|
|
+ $devIdx = array_search('device_id', $keys);
|
|
|
|
|
|
$numIdx = array_search('total_days', $keys);
|
|
|
$num2Idx = array_search('rd_total_days', $keys);
|
|
|
@@ -1742,23 +2019,6 @@ class ImportService extends Service
|
|
|
$excelAggregator[$aggKey]['devs'][$valDev] = true;
|
|
|
}
|
|
|
|
|
|
- // F. 日期范围校验
|
|
|
- list($sS, $startTime) = $this->convertExcelCellToDate($row[$startIdx] ?? '');
|
|
|
- list($eS, $endTime) = $this->convertExcelCellToDate($row[$endIdx] ?? '');
|
|
|
- if (!$sS || !$eS) {
|
|
|
- $errors[] = "第{$displayLine}行:明细日期格式错误";
|
|
|
- } else {
|
|
|
- if ($startTime > $endTime) {
|
|
|
- $errors[] = "第{$displayLine}行:开始日期不能大于结束日期";
|
|
|
- }
|
|
|
- $monthEndTs = strtotime("+1 month", $valMonthTs) - 1;
|
|
|
- if ($startTime < $valMonthTs || $endTime > $monthEndTs) {
|
|
|
- $errors[] = "第{$displayLine}行:明细日期超出[" . date('Y-m', $valMonthTs) . "]范围";
|
|
|
- }
|
|
|
- $array[$rowIndex][$startIdx] = $startTime;
|
|
|
- $array[$rowIndex][$endIdx] = $endTime;
|
|
|
- }
|
|
|
-
|
|
|
// G. 数字格式校验 (checkNumber 方法)
|
|
|
foreach ([$numIdx => '天数', $num2Idx => '研发天数', $num3Idx => '工时', $num4Idx => '研发工时'] as $idx => $label) {
|
|
|
$res = $this->checkNumber($row[$idx] ?? 0, 2, 'non-negative');
|