m0_70156489 1 week ago
parent
commit
03d38002b3

+ 111 - 111
application/api/controller/Manufacture.php

@@ -940,21 +940,21 @@ class Manufacture extends Api
         // 定义要查询的生产工序及排序顺序
         $processList = ['裁剪', '车缝', '手工', '大烫', '总检', '包装'];
 
-        // 查询所有数据
+        // 仅查询菜单所需的去重维度,避免全量加载报工明细导致内存溢出
         $results = \db('设备_产量计酬')
             ->alias('a')
             ->join('设备_基本资料 b', 'a.sczl_jtbh = b.设备编号')
             ->field([
-                'DATE_FORMAT(a.sczl_rq, "%Y-%m-%d")' => '时间', // 转化为 "YYYY-MM-DD" 格式
-                'DATE_FORMAT(a.sczl_rq, "%Y-%m")' => '年月',    // 转化为 "YYYY-MM" 格式
-                'RTRIM(a.sczl_jtbh)' => '机台编号',
+                'DATE_FORMAT(a.sczl_rq, "%Y-%m-%d")' => '时间',
+                'DATE_FORMAT(a.sczl_rq, "%Y-%m")' => '年月',
                 'b.设备编号',
                 'b.设备编组',
-                'b.生产工序'
+                'b.生产工序',
             ])
-            ->whereNull('a.mod_rq') // 筛选无修改日期的数据
-            ->whereIn('b.生产工序', $processList) // 筛选指定的生产工序
-            ->order('a.sczl_rq ASC') // 按时间升序排序
+            ->whereNull('a.mod_rq')
+            ->whereIn('b.生产工序', $processList)
+            ->group('DATE_FORMAT(a.sczl_rq, "%Y-%m-%d"), DATE_FORMAT(a.sczl_rq, "%Y-%m"), b.设备编号, b.设备编组, b.生产工序')
+            ->order('a.sczl_rq ASC')
             ->select();
 
         // 初始化按年月分组的结果
@@ -1631,109 +1631,109 @@ class Manufacture extends Api
 
 
     //车缝派单删除
-    public function CfdataDel()
-    {
-        if ($this->request->isGet() === false)
-        {
-            $this->error('请求错误');
-        }
-        $param = $this->request->param();
-        if (empty($param)){
-            $this->error('参数错误');
-        }
-        $sql = \db('工单_排程班次')
-            ->where('UniqId',$param['UniqId'])
-            ->fetchSql(true)
-            ->update(['mod_rq'=>date('Y-m-d H:i:s',time())]);
-        $res = \db()->query($sql);
-        if ($res !== false){
-            $this->success('删除成功');
-        }else{
-            $this->error('删除失败');
-        }
-    }
+    // public function CfdataDel()
+    // {
+    //     if ($this->request->isGet() === false)
+    //     {
+    //         $this->error('请求错误');
+    //     }
+    //     $param = $this->request->param();
+    //     if (empty($param)){
+    //         $this->error('参数错误');
+    //     }
+    //     $sql = \db('工单_排程班次')
+    //         ->where('UniqId',$param['UniqId'])
+    //         ->fetchSql(true)
+    //         ->update(['mod_rq'=>date('Y-m-d H:i:s',time())]);
+    //     $res = \db()->query($sql);
+    //     if ($res !== false){
+    //         $this->success('删除成功');
+    //     }else{
+    //         $this->error('删除失败');
+    //     }
+    // }
 
     //车缝派单数据
-    public function CfdataAllList()
-    {
-        if ($this->request->isGet() === false){
-            $this->error('请求错误');
-        }
-        $list = \db('工单_排程班次')
-            ->where('mod_rq',null)
-            ->order('sys_rq desc')
-            ->field('订单编号,子订单编号,班组编号,sys_rq as 创建时间,sys_id as 创建人员,UniqId')
-            ->group('子订单编号')
-            ->select();
-        $this->success('成功',$list);
-    }
-
-     //获取产量计酬日期节点(用于菜单栏)
-public function getDateNodes()
-{
-    if ($this->request->isGet() === false) {
-        $this->error('请求错误');
-    }
-
-    //获取所有不重复的年份
-    $yearList = \db('设备_产量计酬')
-        ->field("DISTINCT DATE_FORMAT(sczl_rq, '%Y') as year")
-        ->where('sczl_rq', 'not null')
-        ->order('year', 'desc')
-        ->select();
-
-    foreach ($yearList as $key => $year) {
-        //根据年份获取不重复的月份
-        $monthList = \db('设备_产量计酬')
-            ->field("DISTINCT DATE_FORMAT(sczl_rq, '%m') as month")
-            ->where('sczl_rq', 'not null')
-            ->where("DATE_FORMAT(sczl_rq, '%Y') = '{$year['year']}'")
-            ->order('month', 'asc')
-            ->select();
-
-        foreach ($monthList as $k => $month) {
-            //根据年份和月份获取不重复的日期
-            $dayList = \db('设备_产量计酬')
-                ->field("DISTINCT DATE_FORMAT(sczl_rq, '%d') as day")
-                ->where('sczl_rq', 'not null')
-                ->where("DATE_FORMAT(sczl_rq, '%Y') = '{$year['year']}'")
-                ->where("DATE_FORMAT(sczl_rq, '%m') = '{$month['month']}'")
-                ->order('day', 'asc')
-                ->select();
-
-            $monthList[$k]['day_list'] = $dayList;
-        }
-
-        $yearList[$key]['month_list'] = $monthList;
-    }
-
-    $this->success('获取成功', $yearList);
-}
-//获取产量计酬详情数据
-public function getCfdataDetail()
-{
-    if ($this->request->isGet() === false) {
-        $this->error('请求错误');
-    }
-
-    $param = $this->request->param();
-    if (empty($param['year']) || empty($param['month']) || empty($param['day'])) {
-        $this->error('请选择日期');
-    }
-
-    //拼接日期进行筛选
-    $date = $param['year'] . '-' . $param['month'] . '-' . $param['day'];
-
-    //查询数据
-    $list = \db('设备_产量计酬')
-        ->where("DATE_FORMAT(sczl_rq, '%Y-%m-%d') = '{$date}'")
-        ->field('订单编号, 子订单编号, 款号, 尺码, 数量, sczl_bh')
-        ->select();
-
-    if (empty($list)) {
-        $this->success('暂无数据', []);
-    }
-
-    $this->success('获取成功', $list);
-}
+    // public function CfdataAllList()
+    // {
+    //     if ($this->request->isGet() === false){
+    //         $this->error('请求错误');
+    //     }
+    //     $list = \db('工单_排程班次')
+    //         ->where('mod_rq',null)
+    //         ->order('sys_rq desc')
+    //         ->field('订单编号,子订单编号,班组编号,sys_rq as 创建时间,sys_id as 创建人员,UniqId')
+    //         ->group('子订单编号')
+    //         ->select();
+    //     $this->success('成功',$list);
+    // }
+
+    //  //获取产量计酬日期节点(用于菜单栏)
+    // public function getDateNodes()
+    // {
+    //     if ($this->request->isGet() === false) {
+    //         $this->error('请求错误');
+    //     }
+
+    //     //获取所有不重复的年份
+    //     $yearList = \db('设备_产量计酬')
+    //         ->field("DISTINCT DATE_FORMAT(sczl_rq, '%Y') as year")
+    //         ->where('sczl_rq', 'not null')
+    //         ->order('year', 'desc')
+    //         ->select();
+
+    //     foreach ($yearList as $key => $year) {
+    //         //根据年份获取不重复的月份
+    //         $monthList = \db('设备_产量计酬')
+    //             ->field("DISTINCT DATE_FORMAT(sczl_rq, '%m') as month")
+    //             ->where('sczl_rq', 'not null')
+    //             ->where("DATE_FORMAT(sczl_rq, '%Y') = '{$year['year']}'")
+    //             ->order('month', 'asc')
+    //             ->select();
+
+    //         foreach ($monthList as $k => $month) {
+    //             //根据年份和月份获取不重复的日期
+    //             $dayList = \db('设备_产量计酬')
+    //                 ->field("DISTINCT DATE_FORMAT(sczl_rq, '%d') as day")
+    //                 ->where('sczl_rq', 'not null')
+    //                 ->where("DATE_FORMAT(sczl_rq, '%Y') = '{$year['year']}'")
+    //                 ->where("DATE_FORMAT(sczl_rq, '%m') = '{$month['month']}'")
+    //                 ->order('day', 'asc')
+    //                 ->select();
+
+    //             $monthList[$k]['day_list'] = $dayList;
+    //         }
+
+    //         $yearList[$key]['month_list'] = $monthList;
+    //     }
+
+    //     $this->success('获取成功', $yearList);
+    // }
+    // //获取产量计酬详情数据
+    // public function getCfdataDetail()
+    // {
+    //     if ($this->request->isGet() === false) {
+    //         $this->error('请求错误');
+    //     }
+
+    //     $param = $this->request->param();
+    //     if (empty($param['year']) || empty($param['month']) || empty($param['day'])) {
+    //         $this->error('请选择日期');
+    //     }
+
+    //     //拼接日期进行筛选
+    //     $date = $param['year'] . '-' . $param['month'] . '-' . $param['day'];
+
+    //     //查询数据
+    //     $list = \db('设备_产量计酬')
+    //         ->where("DATE_FORMAT(sczl_rq, '%Y-%m-%d') = '{$date}'")
+    //         ->field('订单编号, 子订单编号, 款号, 尺码, 数量, sczl_bh')
+    //         ->select();
+
+    //     if (empty($list)) {
+    //         $this->success('暂无数据', []);
+    //     }
+
+    //     $this->success('获取成功', $list);
+    // }
 }

+ 328 - 35
application/api/controller/ReportingWork.php

@@ -13,6 +13,9 @@ class ReportingWork extends Api
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
 
+    /** @var bool */
+    private static $reportingWorkLogTableReady = false;
+
 
     /**
      * 根据机台编号获取大工序和人员信息
@@ -127,6 +130,12 @@ class ReportingWork extends Api
         if (empty($params['part_code'])) {
             $this->error('部件编号不能为空');
         }
+        $orderInfo = db('工单_基本资料')
+            ->where('订单编号', $params['workorder'])
+            ->whereNull('Mod_rq')
+            ->field('订单数量')
+            ->find();
+        $manufactureQty = !empty($orderInfo) ? $orderInfo['订单数量'] : 0;
         $data = db('工单_基础工艺资料')
             ->where('work_order', $params['workorder'])
             ->where('part_code', $params['part_code'])
@@ -138,6 +147,24 @@ class ReportingWork extends Api
         usort($data, function ($a, $b) {
             return strnatcmp((string)$a['工艺编号'], (string)$b['工艺编号']);
         });
+        $reportList = db('设备_工分计酬')
+            ->where('work_order', $params['workorder'])
+            ->where('part_code', $params['part_code'])
+            ->whereNull('del_rq')
+            ->field('process_code, SUM(number) as completed_qty')
+            ->group('process_code')
+            ->select();
+        $completedMap = [];
+        foreach ($reportList as $report) {
+            $completedMap[trim((string)$report['process_code'])] = floatval($report['completed_qty']);
+        }
+        foreach ($data as $key => $row) {
+            $processCode = trim((string)$row['工艺编号']);
+            $completedQty = $completedMap[$processCode] ?? 0;
+            $data[$key]['制造数量'] = $manufactureQty;
+            $data[$key]['累计完成数'] = $completedQty;
+            $data[$key]['未完成数'] = floatval($manufactureQty) - $completedQty;
+        }
         $this->success('成功', $data);
     }
 
@@ -183,6 +210,7 @@ class ReportingWork extends Api
         }
 
         $insertData = [];
+        $logList = [];
         $now = date('Y-m-d H:i:s');
         foreach ($reportList as $idx => $item) {
             $rowNo = $idx + 1;
@@ -263,16 +291,33 @@ class ReportingWork extends Api
                 'mod_rq' => null,
                 'del_rq' => null,
             ];
+            $workOrder = !empty($item['work_order']) ? $item['work_order'] : $item['workorder'];
+            $logList[] = $this->buildReportingWorkLogRow([
+                'oper_type' => '新增报工',
+                'oper_user' => isset($item['sys_id']) ? $item['sys_id'] : '',
+                'oper_time' => $now,
+                'work_order' => $workOrder,
+                'majorprocess' => $item['majorprocess'],
+                'part_code' => $partCode,
+                'process_code' => $item['process_code'],
+                'process_name' => $item['process_name'],
+                'staff_no' => $item['staff_no'],
+                'staff_name' => $item['staff_name'],
+                'machine' => isset($item['machine']) ? $item['machine'] : '',
+                'report_date' => !empty($item['date']) ? $item['date'] : date('Y-m-d'),
+            ], '数量', '', (string)$number);
         }
 
         Db::startTrans();
         try {
             $result = db('设备_工分计酬')->insertAll($insertData);
             if ($result === false) {
-                Db::rollback();
-                $this->error('报工失败');
+                throw new \Exception('报工失败');
             }
+            $this->writeReportingWorkLogs($logList);
             Db::commit();
+        } catch (\think\exception\HttpResponseException $e) {
+            throw $e;
         } catch (\Exception $e) {
             Db::rollback();
             $this->error('报工失败:' . $e->getMessage());
@@ -399,9 +444,13 @@ class ReportingWork extends Api
     }
 
     /**
-     * 修改报工数据
+     * 修改工分报工数据
      * @ApiMethod (POST)
-     * @param array $list 报工数据
+     * @param int $id 报工记录ID
+     * @param float $number 数量
+     * @param float $production_hour 报工工时(可选,不传则按标准工时×数量重算)
+     * @param float $production_score 报工工分(可选,不传则按标准工分×数量重算)
+     * @param string $mod_id 修改人(可选)
      * @return array
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
@@ -416,44 +465,87 @@ class ReportingWork extends Api
         if (empty($params['id'])) {
             $this->error('ID不能为空');
         }
-        if (empty($params['mod_id'])) {
-            $this->error('修改人不能为空');
-        }
         if (!isset($params['number']) || $params['number'] === '') {
             $this->error('产量不能为空');
         }
 
+        $record = db('设备_工分计酬')
+            ->where('id', $params['id'])
+            ->whereNull('del_rq')
+            ->find();
+        if (empty($record)) {
+            $this->error('报工记录不存在或已删除');
+        }
+
         $number = floatval($params['number']);
-        $standardScore = (isset($params['standard_score']) && $params['standard_score'] !== '')
-            ? floatval($params['standard_score']) : null;
-        $productionScore = isset($params['production_score']) && $params['production_score'] !== ''
-            ? floatval($params['production_score']) : null;
+        $standardHour = floatval($record['standard_hour']);
+        $standardScore = floatval($record['standard_score']);
 
-        if ($standardScore !== null) {
-            $productionScore = round($standardScore * $number, 4);
-            $salary = $this->calcReportSalary($number, $standardScore);
-        } elseif ($productionScore !== null) {
-            $salary = round($productionScore * 0.067, 4);
-        } else {
-            $this->error('标准工分不能为空');
-        }
+        $productionHour = round($standardHour * $number, 4);
+        $productionScore = round($standardScore * $number, 4);
+        $salary = $this->calcReportSalary($number, $standardScore);
+        $operTime = date('Y-m-d H:i:s');
+        $operUser = !empty($params['mod_id']) ? $params['mod_id'] : ($record['sys_id'] ?? '');
 
         $data = [
-            'mod_id' => $params['mod_id'],
-            'mod_rq' => date('Y-m-d H:i:s'),
+            'mod_rq' => $operTime,
             'number' => $number,
-            'production_hour' => $params['production_hour'],
+            'production_hour' => $productionHour,
             'production_score' => $productionScore,
-            'money' => isset($params['money']) ? $params['money'] : 0,
             'salary' => $salary,
         ];
-        $result = db('设备_工分计酬')
-            ->where('id', $params['id'])
-            ->update($data);
-        if ($result === false) {
-            $this->error('修改失败');
+        if (!empty($params['mod_id'])) {
+            $data['mod_id'] = $params['mod_id'];
+        }
+
+        $logContext = [
+            'report_id' => intval($record['id']),
+            'oper_type' => '修改报工',
+            'oper_user' => $operUser,
+            'oper_time' => $operTime,
+            'work_order' => $record['work_order'],
+            'majorprocess' => $record['majorprocess'],
+            'part_code' => $record['part_code'],
+            'process_code' => $record['process_code'],
+            'process_name' => $record['process_name'],
+            'staff_no' => $record['staff_no'],
+            'staff_name' => $record['staff_name'],
+            'machine' => $record['machine'] ?? '',
+            'report_date' => $record['date'] ?? null,
+        ];
+        $logList = $this->buildReportingWorkChangeLogs($logContext, [
+            '数量' => [$record['number'], $number],
+            '报工工时' => [$record['production_hour'], $productionHour],
+            '报工工分' => [$record['production_score'], $productionScore],
+            '工资' => [$record['salary'], $salary],
+        ]);
+
+        Db::startTrans();
+        try {
+            $result = db('设备_工分计酬')
+                ->where('id', $params['id'])
+                ->whereNull('del_rq')
+                ->update($data);
+            if ($result === false) {
+                throw new \Exception('修改失败');
+            }
+            if ($result === 0) {
+                throw new \Exception('报工记录不存在或已删除');
+            }
+            $this->writeReportingWorkLogs($logList);
+            Db::commit();
+        } catch (\think\exception\HttpResponseException $e) {
+            throw $e;
+        } catch (\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
         }
-        $this->success('修改成功');
+        $this->success('修改成功', [
+            'number' => $number,
+            'production_hour' => $productionHour,
+            'production_score' => $productionScore,
+            'salary' => $salary,
+        ]);
     }
 
     /**
@@ -474,16 +566,121 @@ class ReportingWork extends Api
         if (empty($params['id'])) {
             $this->error('ID不能为空');
         }
+
+        $record = db('设备_工分计酬')
+            ->where('id', $params['id'])
+            ->whereNull('del_rq')
+            ->find();
+        if (empty($record)) {
+            $this->error('报工记录不存在或已删除');
+        }
+
+        $operTime = date('Y-m-d H:i:s');
+        $operUser = !empty($params['mod_id']) ? $params['mod_id'] : ($record['sys_id'] ?? '');
+        $logContext = [
+            'report_id' => intval($record['id']),
+            'oper_type' => '删除报工',
+            'oper_user' => $operUser,
+            'oper_time' => $operTime,
+            'work_order' => $record['work_order'],
+            'majorprocess' => $record['majorprocess'],
+            'part_code' => $record['part_code'],
+            'process_code' => $record['process_code'],
+            'process_name' => $record['process_name'],
+            'staff_no' => $record['staff_no'],
+            'staff_name' => $record['staff_name'],
+            'machine' => $record['machine'] ?? '',
+            'report_date' => $record['date'] ?? null,
+        ];
+        $logList = $this->buildReportingWorkChangeLogs($logContext, [
+            '数量' => [$record['number'], ''],
+            '报工工分' => [$record['production_score'], ''],
+            '工资' => [$record['salary'], ''],
+        ]);
+
         $data = [
-            'del_rq' => date('Y-m-d H:i:s'),
+            'del_rq' => $operTime,
         ];
-        $result = db('设备_工分计酬')
-            ->where('id', $params['id'])
-            ->update($data);
-        if ($result === false) {
-            $this->error('删除失败');
+        if (!empty($params['mod_id'])) {
+            $data['mod_id'] = $params['mod_id'];
+            $data['mod_rq'] = $operTime;
+        }
+
+        Db::startTrans();
+        try {
+            $result = db('设备_工分计酬')
+                ->where('id', $params['id'])
+                ->whereNull('del_rq')
+                ->update($data);
+            if ($result === false) {
+                throw new \Exception('删除失败');
+            }
+            if ($result === 0) {
+                throw new \Exception('报工记录不存在或已删除');
+            }
+            $this->writeReportingWorkLogs($logList);
+            Db::commit();
+        } catch (\think\exception\HttpResponseException $e) {
+            throw $e;
+        } catch (\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
         }
         $this->success('删除成功');
+    }
+
+    /**
+     * 工分报工操作日志查询
+     * @ApiMethod (GET)
+     * @param string $workorder 订单编号(可选)
+     * @param int $report_id 报工记录ID(可选)
+     * @param string $oper_type 操作类型(可选)
+     * @param string $date_start 操作时间起(可选,Y-m-d)
+     * @param string $date_end 操作时间止(可选,Y-m-d)
+     * @param int $page 页码,默认1
+     * @param int $limit 每页条数,默认20
+     */
+    public function GetReportingWorkLog()
+    {
+        if (!$this->request->isGet()) {
+            $this->error('请求方法错误');
+        }
+        $this->ensureReportingWorkLogTable();
+
+        $params = $this->request->param();
+        $page = max(1, intval($params['page'] ?? 1));
+        $limit = max(1, min(200, intval($params['limit'] ?? 20)));
+        $offset = ($page - 1) * $limit;
+
+        $query = Db::table('设备_工分报工操作日志');
+        if (!empty($params['workorder'])) {
+            $query->where('work_order', $params['workorder']);
+        }
+        if (!empty($params['report_id'])) {
+            $query->where('report_id', intval($params['report_id']));
+        }
+        if (!empty($params['oper_type'])) {
+            $query->where('oper_type', $params['oper_type']);
+        }
+        if (!empty($params['date_start'])) {
+            $query->where('oper_time', '>=', $params['date_start'] . ' 00:00:00');
+        }
+        if (!empty($params['date_end'])) {
+            $query->where('oper_time', '<=', $params['date_end'] . ' 23:59:59');
+        }
+
+        $total = (int)(clone $query)->count();
+        $list = $query
+            ->order('oper_time desc,id desc')
+            ->limit($offset, $limit)
+            ->select();
+
+        $this->success('成功', [
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+            'list' => $list,
+        ]);
 
     }
 
@@ -597,6 +794,102 @@ class ReportingWork extends Api
         ]);
     }
 
+    /**
+     * 确保工分报工操作日志表存在
+     */
+    private function ensureReportingWorkLogTable()
+    {
+        if (self::$reportingWorkLogTableReady) {
+            return;
+        }
+
+        $sql = <<<SQL
+CREATE TABLE IF NOT EXISTS `设备_工分报工操作日志` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `report_id` int(11) DEFAULT NULL COMMENT '报工记录ID',
+  `oper_type` varchar(32) NOT NULL DEFAULT '' COMMENT '操作类型',
+  `oper_user` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人',
+  `oper_time` datetime NOT NULL COMMENT '操作时间',
+  `work_order` varchar(64) NOT NULL DEFAULT '' COMMENT '订单编号',
+  `majorprocess` varchar(32) NOT NULL DEFAULT '' COMMENT '大工序',
+  `part_code` int(11) DEFAULT NULL COMMENT '部件编号',
+  `process_code` varchar(32) NOT NULL DEFAULT '' COMMENT '工艺编号',
+  `process_name` varchar(128) NOT NULL DEFAULT '' COMMENT '工艺名称',
+  `staff_no` varchar(32) NOT NULL DEFAULT '' COMMENT '员工编号',
+  `staff_name` varchar(64) NOT NULL DEFAULT '' COMMENT '员工姓名',
+  `machine` varchar(64) NOT NULL DEFAULT '' COMMENT '机台编号',
+  `report_date` date DEFAULT NULL COMMENT '报工日期',
+  `change_field` varchar(32) NOT NULL DEFAULT '' COMMENT '变更字段',
+  `old_value` varchar(255) NOT NULL DEFAULT '' COMMENT '旧值',
+  `new_value` varchar(255) NOT NULL DEFAULT '' COMMENT '新值',
+  PRIMARY KEY (`id`),
+  KEY `idx_report_id` (`report_id`),
+  KEY `idx_work_order` (`work_order`),
+  KEY `idx_oper_time` (`oper_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工分报工操作日志'
+SQL;
+        Db::execute($sql);
+        self::$reportingWorkLogTableReady = true;
+    }
+
+    /**
+     * 写入工分报工操作日志
+     * @param array $logs
+     */
+    private function writeReportingWorkLogs(array $logs)
+    {
+        if (empty($logs)) {
+            return;
+        }
+        $this->ensureReportingWorkLogTable();
+        $result = Db::table('设备_工分报工操作日志')->insertAll($logs);
+        if ($result === false) {
+            throw new \Exception('写入工分报工操作日志失败');
+        }
+    }
+
+    /**
+     * 构建单条工分报工操作日志
+     */
+    private function buildReportingWorkLogRow(array $context, $changeField, $oldValue, $newValue)
+    {
+        return [
+            'report_id' => isset($context['report_id']) ? $context['report_id'] : null,
+            'oper_type' => $context['oper_type'] ?? '',
+            'oper_user' => $context['oper_user'] ?? '',
+            'oper_time' => $context['oper_time'] ?? date('Y-m-d H:i:s'),
+            'work_order' => $context['work_order'] ?? '',
+            'majorprocess' => $context['majorprocess'] ?? '',
+            'part_code' => isset($context['part_code']) ? $context['part_code'] : null,
+            'process_code' => $context['process_code'] ?? '',
+            'process_name' => $context['process_name'] ?? '',
+            'staff_no' => $context['staff_no'] ?? '',
+            'staff_name' => $context['staff_name'] ?? '',
+            'machine' => $context['machine'] ?? '',
+            'report_date' => !empty($context['report_date']) ? $context['report_date'] : null,
+            'change_field' => (string)$changeField,
+            'old_value' => $oldValue === null ? '' : (string)$oldValue,
+            'new_value' => $newValue === null ? '' : (string)$newValue,
+        ];
+    }
+
+    /**
+     * 构建字段变更日志(仅记录有变化的字段)
+     */
+    private function buildReportingWorkChangeLogs(array $context, array $changes)
+    {
+        $logs = [];
+        foreach ($changes as $field => $values) {
+            $oldValue = $values[0];
+            $newValue = $values[1];
+            if ((string)$oldValue === (string)$newValue) {
+                continue;
+            }
+            $logs[] = $this->buildReportingWorkLogRow($context, $field, $oldValue, $newValue);
+        }
+        return $logs;
+    }
+
     /**
      * 报工工资:数量 × 工分 × 0.067
      * @param mixed $number

+ 16 - 1
application/api/controller/WorkOrderProcess.php

@@ -1835,7 +1835,7 @@ class WorkOrderProcess extends Api
         $workOrderInfo = Db::name('工单_基本资料')
             ->where('订单编号', $workorder)
             ->whereNull('Mod_rq')
-            ->field('订单编号, 生产款号, 款式')
+            ->field('订单编号, 生产款号, 款式, 订单数量')
             ->find();
 
         if (empty($workOrderInfo)) {
@@ -1886,10 +1886,12 @@ class WorkOrderProcess extends Api
         }
 
         // 4. 组装返回数据
+        $manufactureQty = $workOrderInfo['订单数量'] ?? 0;
         $data = [
             '订单编号' => $workOrderInfo['订单编号'],
             '生产款号' => $workOrderInfo['生产款号'],
             '款式' => $workOrderInfo['款式'],
+            '订单数量' => $manufactureQty,
             '工序列表' => [],
         ];
 
@@ -1902,6 +1904,9 @@ class WorkOrderProcess extends Api
                     '工序号' => $process['工序号'],
                     '工序名称' => $process['工序名称'],
                     '部件名称' => $process['部件名称'] ?? '',
+                    '制造数量' => $manufactureQty,
+                    '累计完成数' => 0,
+                    '未完成数' => floatval($manufactureQty),
                     'staffs' => [],
                 ];
             }
@@ -1928,6 +1933,16 @@ class WorkOrderProcess extends Api
             }
         }
 
+        // 7. 计算每道工序累计完成数、未完成数
+        foreach ($processMap as $processCode => $process) {
+            $completedQty = 0;
+            foreach ($process['staffs'] as $staff) {
+                $completedQty += floatval($staff['数量']);
+            }
+            $processMap[$processCode]['累计完成数'] = $completedQty;
+            $processMap[$processCode]['未完成数'] = floatval($manufactureQty) - $completedQty;
+        }
+
         $resultList = array_values($processMap);
 
         // 最终按工序号数字排序

+ 323 - 68
application/api/controller/WorkOrderSpotCheck.php

@@ -2320,10 +2320,14 @@ class WorkOrderSpotCheck extends Api{
      * 手工、大烫、总检、包装完工报工【通用的报工操作函数】
      */
     function reportWork($params) {
+        $list = [];
         // 查询设备_产量计酬表的数据
         $bglist = \db('设备_产量计酬')->field('订单编号,子订单编号,数量,ci_num,款号,sys_rq,serial,serial_num')
             ->where('UniqId', $params['UniqId'])->fetchSql(true)->find();
         $list_gd = \db()->query($bglist);
+        if (empty($list_gd)) {
+            $this->error('报工记录不存在');
+        }
 
         // 查询工单基本资料表的数据
         $orderlist = \db('工单_基本资料')->where('订单编号', $list_gd[0]['订单编号'])->find();
@@ -2437,6 +2441,9 @@ class WorkOrderSpotCheck extends Api{
                 $order = $order_id[0];
                 $sql = \db('工单_基本资料')->where('订单编号', $order)->fetchSql(true)->find();
                 $list_gd = \db()->query($sql); // 查询的结果可能是多条记录,确保是数组
+                if (empty($list_gd)) {
+                    $this->error('没有找到对应的订单信息');
+                }
 
                 if($list_gd[0]['gd_statu'] == '1-计划中' || $list_gd[0]['gd_statu'] == '已裁剪' || $list_gd[0]['gd_statu'] == '2-生产中'){
                     DB::name('工单_基本资料')
@@ -2698,14 +2705,25 @@ class WorkOrderSpotCheck extends Api{
 
         // 初始化失败计数器
         $failureCount = 0;
+        $logList = [];
+        $operTime = date('Y-m-d H:i:s');
+        $operUser = isset($param['sys_id']) ? $param['sys_id'] : '';
 
         // 开启事务
         \db()->startTrans();
         try {
             foreach ($printIdArray as $uniqId) {
-                // 根据 UniqId 查询设备_产量计酬表中的数据
+                $uniqId = trim($uniqId);
+                if ($uniqId === '') {
+                    $failureCount++;
+                    continue;
+                }
+
+                // 根据 UniqId 查询未删除的产量报工记录
                 $result = \db('设备_产量计酬')->where('UniqId', $uniqId)
-                    ->field('子订单编号, 订单编号, 尺码, 数量, sczl_bh, 工序名称')
+                    ->where(function ($query) {
+                        $query->whereNull('mod_rq')->whereOr('mod_rq', '');
+                    })
                     ->find();
 
                 // 如果查询不到数据,跳过当前处理
@@ -2714,74 +2732,61 @@ class WorkOrderSpotCheck extends Api{
                     continue;
                 }
 
-                // 判断工序名称是否是“裁剪”
-                if (isset($param['sys_id']) && strpos($param['sys_id'], '裁剪') !== false) {
-                    // 如果工序名称包含“裁剪”,更新工单_印件资料表
-                    $yresult = \db('工单_印件资料')->where('子订单编号', $result['子订单编号'])
-                        ->whereNull('Mod_rq')  // 仅更新没有修改日期的记录
-                        ->find();
-
-                    // 如果没有查询到工单印件资料表的数据,跳过
-                    if (!$yresult) {
-                        $failureCount++;
-                        continue;
-                    }
-
-                    // 处理尺码和数量更新
-                    $size = $result['尺码'];
-                    $quantity = $result['数量'];
-                    $updateData = [];
-
-                    // 遍历工单中的尺码字段(cm1 到 cm10),找到匹配的尺码
-                    for ($i = 1; $i <= 14; $i++) {
-                        $cmField = 'cm' . $i;
-                        $scslField = 'scsl' . $i;
-                        if (isset($yresult[$cmField]) && $yresult[$cmField] === $size) {
-                            // 找到匹配的尺码后,更新对应的数量字段
-                            if (isset($yresult[$scslField])) {
-                                $yresult[$scslField] -= $quantity;
-                                if ($yresult[$scslField] < 0) {
-                                    $yresult[$scslField] = 0;  // 防止数量为负数
-                                }
-                                $updateData[$scslField] = $yresult[$scslField];
-                            }
-                            break;
-                        }
-                    }
-
-                    // 计算更新后的总数量
-                    $newTotal = 0;
-                    for ($i = 1; $i <= 14; $i++) {
-                        $scslField = 'scsl' . $i;
-                        if (isset($yresult[$scslField])) {
-                            $newTotal += $yresult[$scslField];
-                        }
-                    }
-
-                    // 更新工单_印件资料表
-                    \db('工单_印件资料')
-                        ->where('子订单编号', $result['子订单编号'])
-                        ->update(['sctotal' => $newTotal] + $updateData);
+                $processName = $result['工序名称'] ?? '';
+                $isCutOrSew = in_array($processName, ['裁剪', '车缝'], true);
+                $displayQty = $isCutOrSew ? $result['数量'] : ($result['s_num'] ?? $result['数量']);
+
+                // 裁剪报工删除时回退印件实裁数(按工序名称判断,印件不存在也不阻断删除)
+                if ($processName === '裁剪') {
+                    $this->syncCuttingPrintPartQty(
+                        $result['子订单编号'],
+                        $result['尺码'],
+                        -floatval($result['数量'])
+                    );
                 }
 
-                // 更新设备_产量计酬表
-                $updateData = [
-                    'mod_rq' => date('Y-m-d H:i:s'),
-                    'delsys_id' => $param['sys_id']
-                ];
-
-                // 执行更新操作
-                $updateResult = \db('设备_产量计酬')->where('UniqId', $uniqId)->update($updateData);
+                // 软删除产量报工记录
+                $updateResult = \db('设备_产量计酬')
+                    ->where('UniqId', $uniqId)
+                    ->where(function ($query) {
+                        $query->whereNull('mod_rq')->whereOr('mod_rq', '');
+                    })
+                    ->update([
+                        'mod_rq' => $operTime,
+                        'delsys_id' => $operUser,
+                    ]);
 
                 // 如果更新失败,记录失败
-                if ($updateResult === false) {
+                if ($updateResult === false || $updateResult === 0) {
                     $failureCount++;
+                    continue;
                 }
+
+                $logList[] = $this->buildProductionReportLogRow([
+                    'uniq_id' => $uniqId,
+                    'oper_type' => '删除产量报工',
+                    'oper_user' => $operUser,
+                    'oper_time' => $operTime,
+                    'work_order' => $result['订单编号'] ?? '',
+                    'sub_order_no' => $result['子订单编号'] ?? '',
+                    'process_name' => $processName,
+                    'process_code' => $result['工序编号'] ?? '',
+                    'size' => $result['尺码'] ?? '',
+                    'machine' => $result['sczl_jtbh'] ?? '',
+                    'team' => $result['sczl_bh'] ?? '',
+                    'report_date' => !empty($result['sczl_rq']) ? date('Y-m-d', strtotime($result['sczl_rq'])) : null,
+                ], '数量', $displayQty, '');
+            }
+
+            if (!empty($logList)) {
+                $this->writeProductionReportLogs($logList);
             }
 
             // 提交事务
             \db()->commit();
 
+        } catch (\think\exception\HttpResponseException $e) {
+            throw $e;
         } catch (\Exception $e) {
             // 发生异常时回滚事务
             \db()->rollback();
@@ -3294,20 +3299,15 @@ class WorkOrderSpotCheck extends Api{
             ->where('b.Mod_rq',null)
             ->where('c.Mod_rq',null)
             ->find();
-//        if (empty($param['Mod_rq'])) {  // 如果 Mod_rq 为空,才关联物料库存表
-//            $list = $list->join('物料_库存 a', 'a.物料编号 = b.BOM_物料编码 AND a.物料名称 = b.BOM_物料名称 AND a.Mod_rq IS NULL', 'left')
-//                ->field('a.批次号, a.入仓总量, a.库存数量, a.领用数量, a.退还数量, a.单位, a.实际门幅, a.departname');
-//        }
 
-//        $list = $list->where('c.关联编号', $param['关联编号'])
-//            ->where('c.订单编号', $param['订单编号'])
-//            ->where('b.BOM_物料编码', $param['物料编码'])
-//            ->find();
+        if (empty($list)) {
+            $this->success('成功', []);
+        }
 
         $orderList = \db('工单关联表')
             ->where('关联编号',$param['关联编号'])
             ->column('GROUP_CONCAT(DISTINCT 订单编号 SEPARATOR ",") AS 关联订单');
-        $list['关联订单'] = $orderList[0];
+        $list['关联订单'] = isset($orderList[0]) ? $orderList[0] : '';
         $this->success('成功',$list);
     }
 
@@ -3450,4 +3450,259 @@ class WorkOrderSpotCheck extends Api{
             $this->error('缺少工序,参数错误');
         }
     }
+
+    /**
+     * 修改产量报工数量
+     * @ApiMethod (POST)
+     * @param string $UniqId 报工唯一标识
+     * @param float $number 修改后数量
+     * @param string $mod_id 修改人(可选)
+     */
+    public function updateReport()
+    {
+        if (!$this->request->isPost()) {
+            $this->error('请求方法错误');
+        }
+        $params = $this->request->post();
+        if (empty($params['UniqId'])) {
+            $this->error('UniqId不能为空');
+        }
+        if (!isset($params['number']) || $params['number'] === '') {
+            $this->error('数量不能为空');
+        }
+
+        $record = db('设备_产量计酬')
+            ->where('UniqId', $params['UniqId'])
+            ->where(function ($query) {
+                $query->whereNull('mod_rq')->whereOr('mod_rq', '');
+            })
+            ->find();
+        if (empty($record)) {
+            $this->error('报工记录不存在或已删除');
+        }
+
+        $number = floatval($params['number']);
+        if ($number < 0) {
+            $this->error('数量不能为负数');
+        }
+
+        $processName = $record['工序名称'] ?? '';
+        $isCutOrSew = in_array($processName, ['裁剪', '车缝'], true);
+        $oldDisplayQty = floatval($isCutOrSew ? $record['数量'] : ($record['s_num'] ?? $record['数量']));
+        if ($oldDisplayQty == $number) {
+            $this->error('数量未发生变化');
+        }
+
+        $operTime = date('Y-m-d H:i:s');
+        $operUser = !empty($params['mod_id']) ? $params['mod_id'] : ($record['sys_id'] ?? '');
+        $updateData = [];
+
+        if ($isCutOrSew) {
+            $updateData['数量'] = $number;
+            if (isset($record['ci_num']) && floatval($record['ci_num']) == floatval($record['数量'])) {
+                $updateData['ci_num'] = $number;
+            }
+        } else {
+            $updateData['s_num'] = $number;
+            $updateData['数量'] = $number;
+            if (isset($record['ci_num']) && floatval($record['ci_num']) == floatval($record['s_num'] ?? $record['数量'])) {
+                $updateData['ci_num'] = $number;
+            }
+        }
+
+        $logContext = [
+            'uniq_id' => $params['UniqId'],
+            'oper_type' => '修改产量报工',
+            'oper_user' => $operUser,
+            'oper_time' => $operTime,
+            'work_order' => $record['订单编号'] ?? '',
+            'sub_order_no' => $record['子订单编号'] ?? '',
+            'process_name' => $processName,
+            'process_code' => $record['工序编号'] ?? '',
+            'size' => $record['尺码'] ?? '',
+            'machine' => $record['sczl_jtbh'] ?? '',
+            'team' => $record['sczl_bh'] ?? '',
+            'report_date' => !empty($record['sczl_rq']) ? date('Y-m-d', strtotime($record['sczl_rq'])) : null,
+        ];
+        $logList = [
+            $this->buildProductionReportLogRow($logContext, '数量', $oldDisplayQty, $number),
+        ];
+
+        Db::startTrans();
+        try {
+            if ($processName === '裁剪') {
+                $this->syncCuttingPrintPartQty($record['子订单编号'], $record['尺码'], $number - $oldDisplayQty);
+            }
+            if ($processName === '包装') {
+                $this->syncPackagingWarehouseQty($record['订单编号'], $number - $oldDisplayQty);
+            }
+
+            $result = db('设备_产量计酬')
+                ->where('UniqId', $params['UniqId'])
+                ->where(function ($query) {
+                    $query->whereNull('mod_rq')->whereOr('mod_rq', '');
+                })
+                ->update($updateData);
+            if ($result === false) {
+                throw new \Exception('修改失败');
+            }
+            if ($result === 0) {
+                throw new \Exception('报工记录不存在或已删除');
+            }
+
+            $this->writeProductionReportLogs($logList);
+            Db::commit();
+        } catch (\think\exception\HttpResponseException $e) {
+            throw $e;
+        } catch (\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+
+        $this->success('修改成功', [
+            'UniqId' => $params['UniqId'],
+            'number' => $number,
+        ]);
+    }
+
+    /** @var bool */
+    private static $productionReportLogTableReady = false;
+
+    /**
+     * 裁剪报工数量变更时,同步工单印件实裁数
+     */
+    private function syncCuttingPrintPartQty($subOrderNo, $size, $delta)
+    {
+        if ($delta == 0 || empty($subOrderNo) || $size === null || $size === '') {
+            return;
+        }
+        $yresult = db('工单_印件资料')
+            ->where('子订单编号', $subOrderNo)
+            ->whereNull('Mod_rq')
+            ->find();
+        if (empty($yresult)) {
+            return;
+        }
+
+        $partUpdate = [];
+        for ($i = 1; $i <= 14; $i++) {
+            $cmField = 'cm' . $i;
+            $scslField = 'scsl' . $i;
+            if (isset($yresult[$cmField]) && $yresult[$cmField] === $size) {
+                $newVal = floatval($yresult[$scslField]) + floatval($delta);
+                if ($newVal < 0) {
+                    $newVal = 0;
+                }
+                $partUpdate[$scslField] = $newVal;
+                $yresult[$scslField] = $newVal;
+                break;
+            }
+        }
+
+        $newTotal = 0;
+        for ($i = 1; $i <= 14; $i++) {
+            $scslField = 'scsl' . $i;
+            if (isset($yresult[$scslField])) {
+                $newTotal += floatval($yresult[$scslField]);
+            }
+        }
+
+        db('工单_印件资料')
+            ->where('子订单编号', $subOrderNo)
+            ->whereNull('Mod_rq')
+            ->update(['sctotal' => $newTotal] + $partUpdate);
+    }
+
+    /**
+     * 包装报工数量变更时,同步工单入仓数量
+     */
+    private function syncPackagingWarehouseQty($workOrder, $delta)
+    {
+        if ($delta == 0 || empty($workOrder)) {
+            return;
+        }
+        $orderInfo = db('工单_基本资料')
+            ->where('订单编号', $workOrder)
+            ->whereNull('Mod_rq')
+            ->field('工单入仓数量')
+            ->find();
+        if (empty($orderInfo)) {
+            return;
+        }
+        $newQty = floatval($orderInfo['工单入仓数量']) + floatval($delta);
+        if ($newQty < 0) {
+            $newQty = 0;
+        }
+        db('工单_基本资料')
+            ->where('订单编号', $workOrder)
+            ->whereNull('Mod_rq')
+            ->update(['工单入仓数量' => $newQty]);
+    }
+
+    private function ensureProductionReportLogTable()
+    {
+        if (self::$productionReportLogTableReady) {
+            return;
+        }
+
+        $sql = <<<SQL
+CREATE TABLE IF NOT EXISTS `设备_产量报工操作日志` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `uniq_id` varchar(64) NOT NULL DEFAULT '' COMMENT '报工UniqId',
+  `oper_type` varchar(32) NOT NULL DEFAULT '' COMMENT '操作类型',
+  `oper_user` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人',
+  `oper_time` datetime NOT NULL COMMENT '操作时间',
+  `work_order` varchar(64) NOT NULL DEFAULT '' COMMENT '订单编号',
+  `sub_order_no` varchar(64) NOT NULL DEFAULT '' COMMENT '子订单编号',
+  `process_code` varchar(32) NOT NULL DEFAULT '' COMMENT '工序编号',
+  `process_name` varchar(32) NOT NULL DEFAULT '' COMMENT '工序名称',
+  `size` varchar(32) NOT NULL DEFAULT '' COMMENT '尺码',
+  `machine` varchar(64) NOT NULL DEFAULT '' COMMENT '机台编号',
+  `team` varchar(64) NOT NULL DEFAULT '' COMMENT '小组',
+  `report_date` date DEFAULT NULL COMMENT '报工日期',
+  `change_field` varchar(32) NOT NULL DEFAULT '' COMMENT '变更字段',
+  `old_value` varchar(255) NOT NULL DEFAULT '' COMMENT '旧值',
+  `new_value` varchar(255) NOT NULL DEFAULT '' COMMENT '新值',
+  PRIMARY KEY (`id`),
+  KEY `idx_uniq_id` (`uniq_id`),
+  KEY `idx_work_order` (`work_order`),
+  KEY `idx_oper_time` (`oper_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产量报工操作日志'
+SQL;
+        Db::execute($sql);
+        self::$productionReportLogTableReady = true;
+    }
+
+    private function writeProductionReportLogs(array $logs)
+    {
+        if (empty($logs)) {
+            return;
+        }
+        $this->ensureProductionReportLogTable();
+        $result = Db::table('设备_产量报工操作日志')->insertAll($logs);
+        if ($result === false) {
+            throw new \Exception('写入产量报工操作日志失败');
+        }
+    }
+
+    private function buildProductionReportLogRow(array $context, $changeField, $oldValue, $newValue)
+    {
+        return [
+            'uniq_id' => $context['uniq_id'] ?? '',
+            'oper_type' => $context['oper_type'] ?? '',
+            'oper_user' => $context['oper_user'] ?? '',
+            'oper_time' => $context['oper_time'] ?? date('Y-m-d H:i:s'),
+            'work_order' => $context['work_order'] ?? '',
+            'sub_order_no' => $context['sub_order_no'] ?? '',
+            'process_code' => $context['process_code'] ?? '',
+            'process_name' => $context['process_name'] ?? '',
+            'size' => $context['size'] ?? '',
+            'machine' => $context['machine'] ?? '',
+            'team' => $context['team'] ?? '',
+            'report_date' => !empty($context['report_date']) ? $context['report_date'] : null,
+            'change_field' => (string)$changeField,
+            'old_value' => $oldValue === null ? '' : (string)$oldValue,
+            'new_value' => $newValue === null ? '' : (string)$newValue,
+        ];
+    }
 }