Bladeren bron

小高薪

cqp 3 maanden geleden
bovenliggende
commit
544469464d
3 gewijzigde bestanden met toevoegingen van 228 en 24 verwijderingen
  1. 149 4
      app/Service/ImportService.php
  2. 78 19
      app/Service/PersonWorkService.php
  3. 1 1
      config/excel/dailyPwOrder.php

+ 149 - 4
app/Service/ImportService.php

@@ -2788,7 +2788,6 @@ class ImportService extends Service
     }
     }
 
 
     // 人员日工时单 ------------------------------
     // 人员日工时单 ------------------------------
-    // 人员日工时单导入 ------------------------------
     public function dailyPwOrderImport($array, $user, $other_param)
     public function dailyPwOrderImport($array, $user, $other_param)
     {
     {
         $upload = $array[0];
         $upload = $array[0];
@@ -2910,9 +2909,6 @@ class ImportService extends Service
         return [true, ''];
         return [true, ''];
     }
     }
 
 
-    /**
-     * 人员日工时单:详细业务校验
-     */
     private function dailyPwOrderCheck(&$array, $user, $table_config)
     private function dailyPwOrderCheck(&$array, $user, $table_config)
     {
     {
         $keys = array_column($table_config, 'key');
         $keys = array_column($table_config, 'key');
@@ -2923,6 +2919,155 @@ class ImportService extends Service
         $startIdx = array_search('start_time', $keys);
         $startIdx = array_search('start_time', $keys);
         $endIdx   = array_search('end_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. 批量预加载基础档案
         // 1. 批量预加载基础档案
         $empCodes = array_filter(array_unique(array_column($array, $empIdx)));
         $empCodes = array_filter(array_unique(array_column($array, $empIdx)));
         $dbEmps = Employee::where('del_time', 0)->where('top_depart_id', $user['top_depart_id'])
         $dbEmps = Employee::where('del_time', 0)->where('top_depart_id', $user['top_depart_id'])

+ 78 - 19
app/Service/PersonWorkService.php

@@ -495,37 +495,96 @@ class PersonWorkService extends Service
     public function dailyPwOrderRule(&$data, $user, $is_add = true){
     public function dailyPwOrderRule(&$data, $user, $is_add = true){
         if(empty($data['order_time'])) return [false, '单据日期不能为空'];
         if(empty($data['order_time'])) return [false, '单据日期不能为空'];
         $data['order_time'] = $this->changeDateToDate($data['order_time']);
         $data['order_time'] = $this->changeDateToDate($data['order_time']);
-        if(empty($data['item_id'])) return [false, '项目不能为空'];
-        $bool = Item::where('del_time',0)->where('id', $data['item_id'])->exists();
-        if(! $bool) return [false, '项目不存在或已被删除'];
+        $orderTime = $data['order_time'];
+        $itemId = $data['item_id'] ?? 0;
+
+        if(empty($itemId)) return [false, '项目不能为空'];
+        $bool = Item::where('del_time',0)->where('id', $itemId)->exists();
+        if(!$bool) return [false, '项目不存在或已被删除'];
 
 
         $data['top_depart_id'] = $user['top_depart_id'];
         $data['top_depart_id'] = $user['top_depart_id'];
         if(empty($data['details'])) return [false, '人员日工时单明细不能为空'];
         if(empty($data['details'])) return [false, '人员日工时单明细不能为空'];
+
+        // --- 1. 批量预获取人员信息,用于报错提示 ---
+        $allEmpIds = array_filter(array_unique(array_column($data['details'], 'employee_id')));
+
+        // 如果需要工号+姓名,建议这样获取:
+        $empDisplayMap = Employee::whereIn('id', $allEmpIds)
+            ->get(['id', 'number', 'title'])
+            ->mapWithKeys(function($item){
+                return [$item->id => "[{$item->number}]{$item->title}"];
+            })->toArray();
+
+        // 2. 本次提交内部重叠记录器
+        $internalOverlap = [];
+
         foreach ($data['details'] as $key => $value){
         foreach ($data['details'] as $key => $value){
-            if(empty($value['employee_id'])) return [false, '人员不能为空'];
-            if(empty($data['item_id'])) return [false,'项目不能为空'];
-            $res = $this->checkNumber($data['start_time_hour'],0,'non-negative');
-            if(! $res['valid']) return [false,'研发时段开始点:' . $res['error']];
-            $res = $this->checkNumber($data['start_time_min'],0,'non-negative');
-            if(! $res['valid']) return [false,'研发时段开始分:' . $res['error']];
-            $res = $this->checkNumber($data['end_time_hour'],0,'non-negative');
-            if(! $res['valid']) return [false,'研发时段结束点:' . $res['error']];
-            $res = $this->checkNumber($data['end_time_hour'],0,'non-negative');
-            if(! $res['valid']) return [false,'研发时段结束分:' . $res['error']];
-            $res = $this->checkNumber($data['total_work_min'],2,'positive');
-            if(! $res['valid']) return [false,'研发合计工时(分):' . $res['error']];
+            $empId = $value['employee_id'] ?? 0;
+            if(empty($empId)) return [false, '人员不能为空'];
+
+            $empName = $empDisplayMap[$empId] ?? "ID:{$empId}";
+
+            // 校验数字有效性 (修正为使用 $value)
+            $res = $this->checkNumber($value['start_time_hour'], 0, 'non-negative');
+            if(!$res['valid']) return [false, "人员{$empName}开始点:" . $res['error']];
+            $res = $this->checkNumber($value['start_time_min'], 0, 'non-negative');
+            if(!$res['valid']) return [false, "人员{$empName}开始分:" . $res['error']];
+            $res = $this->checkNumber($value['end_time_hour'], 0, 'non-negative');
+            if(!$res['valid']) return [false, "人员{$empName}结束点:" . $res['error']];
+            $res = $this->checkNumber($value['end_time_min'], 0, 'non-negative');
+            if(!$res['valid']) return [false, "人员{$empName}结束分:" . $res['error']];
+
+            $currentStart = $value['start_time_hour'] * 60 + $value['start_time_min'];
+            $currentEnd   = $value['end_time_hour'] * 60 + $value['end_time_min'];
+
+            if ($currentStart >= $currentEnd) {
+                return [false, "人员{$empName}:开始时间必须早于结束时间"];
+            }
+
+            // --- 3. 内部重叠校验(防止一次提交多行重复) ---
+            if (isset($internalOverlap[$empId])) {
+                foreach ($internalOverlap[$empId] as $period) {
+                    if ($currentStart < $period['e'] && $period['s'] < $currentEnd) {
+                        return [false, "人员{$empName}在本次提交的多行明细中时间段重叠"];
+                    }
+                }
+            }
+            $internalOverlap[$empId][] = ['s' => $currentStart, 'e' => $currentEnd];
+
+            $query = DB::table('daily_pw_order_details as d')
+                ->join('daily_pw_order as m', 'd.main_id', '=', 'm.id')
+                ->where('m.top_depart_id', $data['top_depart_id'])
+                ->where('m.order_time', $orderTime)
+                ->where('m.item_id', $itemId)
+                ->where('d.employee_id', $empId)
+                ->where('m.del_time', 0)
+                ->where('d.del_time', 0);
+
+            if (!$is_add && !empty($data['id'])) {
+                $query->where('m.id', '<>', $data['id']);
+            }
+
+            $existingPeriods = $query->select('d.start_time_hour', 'd.start_time_min', 'd.end_time_hour', 'd.end_time_min')->get();
+
+            foreach ($existingPeriods as $p) {
+                $exStart = $p->start_time_hour * 60 + $p->start_time_min;
+                $exEnd   = $p->end_time_hour * 60 + $p->end_time_min;
+
+                if ($currentStart < $exEnd && $exStart < $currentEnd) {
+                    return [false, "人员{$empName}在该项目该日已有其他工时单创建重叠的时间段数据"];
+                }
+            }
+
             $data['details'][$key]['top_depart_id'] = $data['top_depart_id'];
             $data['details'][$key]['top_depart_id'] = $data['top_depart_id'];
         }
         }
 
 
-        if($is_add){
-
-        }else{
+        if(!$is_add){
             if(empty($data['id'])) return [false,'ID不能为空'];
             if(empty($data['id'])) return [false,'ID不能为空'];
             $bool = DailyPwOrder::where('top_depart_id', $data['top_depart_id'])
             $bool = DailyPwOrder::where('top_depart_id', $data['top_depart_id'])
                 ->where('id',$data['id'])
                 ->where('id',$data['id'])
                 ->where('del_time',0)
                 ->where('del_time',0)
                 ->exists();
                 ->exists();
-            if(! $bool) return [false, '人员日工时单不存在或已被删除'];
+            if(!$bool) return [false, '人员日工时单不存在或已被删除'];
         }
         }
 
 
         return [true, ''];
         return [true, ''];

+ 1 - 1
config/excel/dailyPwOrder.php

@@ -61,7 +61,7 @@ return [
             'key' =>'employee_title',
             'key' =>'employee_title',
             'export' =>'employee_title',
             'export' =>'employee_title',
             'value' => '人员名称',
             'value' => '人员名称',
-            'required' => true,
+            'required' => false,
             'is_main' => false,
             'is_main' => false,
             'default' => "",
             'default' => "",
             'unique' => false,
             'unique' => false,