|
|
@@ -2788,7 +2788,6 @@ class ImportService extends Service
|
|
|
}
|
|
|
|
|
|
// 人员日工时单 ------------------------------
|
|
|
- // 人员日工时单导入 ------------------------------
|
|
|
public function dailyPwOrderImport($array, $user, $other_param)
|
|
|
{
|
|
|
$upload = $array[0];
|
|
|
@@ -2910,9 +2909,6 @@ class ImportService extends Service
|
|
|
return [true, ''];
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 人员日工时单:详细业务校验
|
|
|
- */
|
|
|
private function dailyPwOrderCheck(&$array, $user, $table_config)
|
|
|
{
|
|
|
$keys = array_column($table_config, 'key');
|
|
|
@@ -2923,6 +2919,155 @@ class ImportService extends Service
|
|
|
$startIdx = array_search('start_time', $keys);
|
|
|
$endIdx = array_search('end_time', $keys);
|
|
|
|
|
|
+ $topDepartId = $user['top_depart_id'];
|
|
|
+
|
|
|
+ // 1. 批量预加载基础档案
|
|
|
+ $empCodes = array_filter(array_unique(array_column($array, $empIdx)));
|
|
|
+ $empModels = Employee::where('del_time', 0)->where('top_depart_id', $topDepartId)
|
|
|
+ ->whereIn('number', $empCodes)->get(['id', 'number', 'title']);
|
|
|
+
|
|
|
+ $dbEmps = $empModels->pluck('id', 'number')->toArray();
|
|
|
+ $empDisplayMap = $empModels->mapWithKeys(function($item){
|
|
|
+ return [$item->id => "[{$item->number}]{$item->title}"];
|
|
|
+ })->toArray();
|
|
|
+
|
|
|
+ $itemCodes = array_filter(array_unique(array_column($array, $itemIdx)));
|
|
|
+ $dbItems = Item::where('del_time', 0)->where('top_depart_id', $topDepartId)
|
|
|
+ ->whereIn('code', $itemCodes)->pluck('id', 'code')->toArray();
|
|
|
+
|
|
|
+ // 2. 预加载已有单据
|
|
|
+ $allCodes = array_filter(array_unique(array_column($array, $codeIdx)));
|
|
|
+ $dbOrders = DB::table('daily_pw_order')->where('top_depart_id', $topDepartId)
|
|
|
+ ->whereIn('code', $allCodes)->where('del_time', 0)->get()->keyBy('code');
|
|
|
+
|
|
|
+ // --- 跨单据预查:提前解析日期并检索库内已存在的工时区间 ---
|
|
|
+ $allDates = [];
|
|
|
+ foreach ($array as $row) {
|
|
|
+ list($status, $ts) = $this->convertExcelCellToDate($row[$timeIdx] ?? '');
|
|
|
+ if ($status) $allDates[] = strtotime(date('Y-m-d', $ts));
|
|
|
+ }
|
|
|
+ $allDates = array_unique($allDates);
|
|
|
+
|
|
|
+ // 查询数据库:同公司、同日期、同项目、同人员的已有工时 (排除本次 Excel 涉及到的单号)
|
|
|
+ $dbExistingWork = DB::table('daily_pw_order_details as d')
|
|
|
+ ->join('daily_pw_order as m', 'd.main_id', '=', 'm.id')
|
|
|
+ ->where('m.top_depart_id', $topDepartId) // 核心:公司数据隔离
|
|
|
+ ->whereIn('m.order_time', $allDates)
|
|
|
+ ->whereIn('m.item_id', array_values($dbItems))
|
|
|
+ ->whereIn('d.employee_id', array_values($dbEmps))
|
|
|
+ ->where('m.del_time', 0)
|
|
|
+ ->where('d.del_time', 0)
|
|
|
+ ->whereNotIn('m.code', $allCodes) // 排除掉当前正在编辑的单据本身,避免自冲突
|
|
|
+ ->select('m.order_time', 'm.item_id', 'd.employee_id', 'd.start_time_hour', 'd.start_time_min', 'd.end_time_hour', 'd.end_time_min')
|
|
|
+ ->get()
|
|
|
+ ->groupBy(function($item) {
|
|
|
+ return $item->order_time . '_' . $item->item_id . '_' . $item->employee_id;
|
|
|
+ });
|
|
|
+
|
|
|
+ $errors = [];
|
|
|
+ $update_map = [];
|
|
|
+ $excelBillMap = [];
|
|
|
+ $timeOverlapMap = []; // 内部记录器:$date_$item_$emp => [ ['s'=>min, 'e'=>min], ... ]
|
|
|
+
|
|
|
+ // 3. 全量循环检查
|
|
|
+ foreach ($array as $rowIndex => $row) {
|
|
|
+ $displayLine = $rowIndex + 1;
|
|
|
+
|
|
|
+ // A. 日期预处理
|
|
|
+ list($status, $ts) = $this->convertExcelCellToDate($row[$timeIdx] ?? '');
|
|
|
+ if (!$status) {
|
|
|
+ $errors[] = "第{$displayLine}行:单据日期格式错误";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $ts = strtotime(date('Y-m-d', $ts));
|
|
|
+ $array[$rowIndex][$timeIdx] = $ts;
|
|
|
+
|
|
|
+ $valCode = trim($row[$codeIdx] ?? '');
|
|
|
+ $valItemCode = trim($row[$itemIdx] ?? '');
|
|
|
+ $itemId = $dbItems[$valItemCode] ?? 0;
|
|
|
+ $valEmpCode = trim($row[$empIdx] ?? '');
|
|
|
+ $empId = $dbEmps[$valEmpCode] ?? 0;
|
|
|
+ $empName = $empDisplayMap[$empId] ?? "工号:{$valEmpCode}";
|
|
|
+
|
|
|
+ // B. 基础档案存在性校验
|
|
|
+ if (!$itemId) $errors[] = "第{$displayLine}行:项目编号[{$valItemCode}]不存在";
|
|
|
+ if (!$empId) $errors[] = "第{$displayLine}行:人员工号[{$valEmpCode}]不存在";
|
|
|
+ if (!$itemId || !$empId) continue;
|
|
|
+
|
|
|
+ // C. 单号与编辑逻辑判定
|
|
|
+ if ($valCode !== '') {
|
|
|
+ if (isset($dbOrders[$valCode])) {
|
|
|
+ $dbOrder = $dbOrders[$valCode];
|
|
|
+ if ($dbOrder->order_time != $ts) {
|
|
|
+ $errors[] = "第{$displayLine}行:单据[{$valCode}]原日期为[" . date('Y-m-d', $dbOrder->order_time) . "],不允许跨日期编辑";
|
|
|
+ }
|
|
|
+ $update_map[$rowIndex] = $dbOrder->id;
|
|
|
+ } else {
|
|
|
+ $errors[] = "第{$displayLine}行:单据编号[{$valCode}]不存在,若要新增请清空该列";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // D. 时间合法性校验
|
|
|
+ list($sH, $sM) = $this->parseTimeHourMin($row[$startIdx] ?? '');
|
|
|
+ list($eH, $eM) = $this->parseTimeHourMin($row[$endIdx] ?? '');
|
|
|
+
|
|
|
+ if ($sH === null || $sM === null || $eH === null || $eM === null) {
|
|
|
+ $errors[] = "第{$displayLine}行:时间格式非法或超出00:00-23:59范围";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $currentStart = $sH * 60 + $sM;
|
|
|
+ $currentEnd = $eH * 60 + $eM;
|
|
|
+
|
|
|
+ if ($currentStart >= $currentEnd) {
|
|
|
+ $errors[] = "第{$displayLine}行:人员{$empName}的开始时间需早于结束时间";
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- E. 三位一体冲突校验 ---
|
|
|
+ $checkKey = $ts . "_" . $itemId . "_" . $empId;
|
|
|
+
|
|
|
+ // 1. 校验:数据库已有记录 (跨单据)
|
|
|
+ if (isset($dbExistingWork[$checkKey])) {
|
|
|
+ foreach ($dbExistingWork[$checkKey] as $p) {
|
|
|
+ $exStart = $p->start_time_hour * 60 + $p->start_time_min;
|
|
|
+ $exEnd = $p->end_time_hour * 60 + $p->end_time_min;
|
|
|
+ // 重叠判定公式: (StartA < EndB) && (StartB < EndA)
|
|
|
+ if ($currentStart < $exEnd && $exStart < $currentEnd) {
|
|
|
+ $errors[] = "第{$displayLine}行:人员{$empName}与已存在该时间重叠的工时单";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 校验:Excel内上方行记录 (内部重叠)
|
|
|
+ if (isset($timeOverlapMap[$checkKey])) {
|
|
|
+ foreach ($timeOverlapMap[$checkKey] as $period) {
|
|
|
+ if ($currentStart < $period['e'] && $period['s'] < $currentEnd) {
|
|
|
+ $errors[] = "第{$displayLine}行:人员{$empName}与表格内其他行时间重叠";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 校验通过,记录到内部记录器
|
|
|
+ $timeOverlapMap[$checkKey][] = ['s' => $currentStart, 'e' => $currentEnd];
|
|
|
+ }
|
|
|
+
|
|
|
+ $error_string = !empty($errors) ? implode('|', $errors) : "";
|
|
|
+ return [$error_string, $update_map, $dbEmps, $dbItems];
|
|
|
+ }
|
|
|
+
|
|
|
+ private function dailyPwOrderCheck1(&$array, $user, $table_config)
|
|
|
+ {
|
|
|
+ $keys = array_column($table_config, 'key');
|
|
|
+ $codeIdx = array_search('code', $keys);
|
|
|
+ $timeIdx = array_search('order_time', $keys);
|
|
|
+ $itemIdx = array_search('item_id', $keys);
|
|
|
+ $empIdx = array_search('employee_id', $keys);
|
|
|
+ $startIdx = array_search('start_time', $keys);
|
|
|
+ $endIdx = array_search('end_time', $keys);
|
|
|
+
|
|
|
// 1. 批量预加载基础档案
|
|
|
$empCodes = array_filter(array_unique(array_column($array, $empIdx)));
|
|
|
$dbEmps = Employee::where('del_time', 0)->where('top_depart_id', $user['top_depart_id'])
|