ReportingWork.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use \think\Request;
  5. use \think\Db;
  6. /**
  7. * 车间工分报工
  8. */
  9. class ReportingWork extends Api
  10. {
  11. protected $noNeedLogin = ['*'];
  12. protected $noNeedRight = ['*'];
  13. /** @var bool */
  14. private static $reportingWorkLogTableReady = false;
  15. /**
  16. * 根据机台编号获取大工序和人员信息
  17. * @ApiMethod (GET)
  18. * @param string $machine 机台编号
  19. * @return array
  20. * @throws \think\db\exception\DataNotFoundException
  21. * @throws \think\db\exception\ModelNotFoundException
  22. * @throws \think\exception\DbException
  23. */
  24. public function getMajorprocessAndPerson()
  25. {
  26. if (!$this->request->isGet()) {
  27. $this->error('请求方法错误');
  28. }
  29. $params = $this->request->param();
  30. if (empty($params['machine'])) {
  31. $this->error('机台编号不能为空');
  32. }
  33. //获取大工序
  34. $majorprocess = db('设备_基本资料')
  35. ->where('设备编号', $params['machine'])
  36. ->field('生产工序,设备编组')
  37. ->find();
  38. if (empty($majorprocess)) {
  39. $this->error('设备信息');
  40. }
  41. //小组信息
  42. // $person = db('人员_小组资料')
  43. // ->where('team_name', $majorprocess['设备编组'])
  44. // ->where('status', 1)
  45. // ->field('team_name as 小组名称,staff_no as 员工编号,staff_name as 员工姓名')
  46. // ->select();
  47. $person = Db::name('人员_小组资料')->alias('a')
  48. ->field('
  49. b.staff_no as 员工编号,
  50. b.staff_name as 员工姓名,
  51. a.team_name as 小组名称
  52. ')
  53. ->join('人员_基本资料 b', 'a.staff_no = b.staff_no')
  54. ->where('a.team_name', $majorprocess['设备编组'])
  55. ->whereNull('a.mod_rq')
  56. ->whereNull('b.mod_rq')
  57. ->order('a.id asc')
  58. ->select();
  59. if (empty($person)) {
  60. $this->error('未找到人员信息');
  61. }
  62. $data['majorprocess'] = $majorprocess;
  63. $data['person'] = $person;
  64. $this->success('成功', $data);
  65. }
  66. /**
  67. * 获取订单工艺数据
  68. * @ApiMethod (GET)
  69. * @param string $workorder 工单编号
  70. * @param string $majorprocess 大工序
  71. * @return array
  72. * @throws \think\db\exception\DataNotFoundException
  73. * @throws \think\db\exception\ModelNotFoundException
  74. * @throws \think\exception\DbException
  75. *
  76. */
  77. public function GetOrderProcess()
  78. {
  79. if (!$this->request->isGet()) {
  80. $this->error('请求方法错误');
  81. }
  82. $params = $this->request->param();
  83. if (empty($params['workorder'])) {
  84. $this->error('工单编号不能为空');
  85. }
  86. if (empty($params['majorprocess'])) {
  87. $this->error('大工序不能为空');
  88. }
  89. $data = db('工单_基础工艺资料')
  90. ->where('work_order', $params['workorder'])
  91. ->where('big_process', $params['majorprocess'])
  92. ->whereNull('del_rq')
  93. // ->where('status', 1)
  94. ->field('part_code as 部件编号,part_name as 部件名称')
  95. ->group('part_code')
  96. ->select();
  97. usort($data, function ($a, $b) {
  98. return strnatcmp((string)$a['部件编号'], (string)$b['部件编号']);
  99. });
  100. $this->success('成功', $data);
  101. }
  102. /**
  103. * 获取车缝部件的工艺资料
  104. * @ApiMethod (GET)
  105. * @param string $part_code 部件编号
  106. * @param string $workorder 工单编号
  107. * @param string $sys_id 小组名称(可选,传了则返回本组完成数)
  108. * @return array
  109. * @throws \think\db\exception\DataNotFoundException
  110. * @throws \think\db\exception\ModelNotFoundException
  111. * @throws \think\exception\DbException
  112. */
  113. public function GetCarProcess()
  114. {
  115. if (!$this->request->isGet()) {
  116. $this->error('请求方法错误');
  117. }
  118. $params = $this->request->param();
  119. if (empty($params['workorder'])) {
  120. $this->error('工单编号不能为空');
  121. }
  122. if (empty($params['part_code'])) {
  123. $this->error('部件编号不能为空');
  124. }
  125. $sysId = isset($params['sys_id']) ? trim((string)$params['sys_id']) : '';
  126. $orderInfo = db('工单_基本资料')
  127. ->where('订单编号', $params['workorder'])
  128. ->whereNull('Mod_rq')
  129. ->field('订单数量')
  130. ->find();
  131. $manufactureQty = !empty($orderInfo) ? $orderInfo['订单数量'] : 0;
  132. $data = db('工单_基础工艺资料')
  133. ->where('work_order', $params['workorder'])
  134. ->where('part_code', $params['part_code'])
  135. ->whereNull('del_rq')
  136. // 已拆分母工序 status=1 不展示,仅返回可报工工序 status=0
  137. ->where('status', 0)
  138. ->field('process_code as 工艺编号,process_name as 工艺名称,standard_hour as 标准工时,standard_minutes as 标准分钟,
  139. standard_score as 标准工分,coefficient as 系数,remark as 备注,money as 金额,status')
  140. ->select();
  141. usort($data, function ($a, $b) {
  142. return strnatcmp((string)$a['工艺编号'], (string)$b['工艺编号']);
  143. });
  144. // 累计完成数:全部小组
  145. $reportList = db('设备_工分计酬')
  146. ->where('work_order', $params['workorder'])
  147. ->where('part_code', $params['part_code'])
  148. ->whereNull('del_rq')
  149. ->field('process_code, SUM(number) as completed_qty')
  150. ->group('process_code')
  151. ->select();
  152. $completedMap = [];
  153. foreach ($reportList as $report) {
  154. $completedMap[trim((string)$report['process_code'])] = floatval($report['completed_qty']);
  155. }
  156. // 本组完成数:按 sys_id(小组名称)过滤
  157. $groupCompletedMap = [];
  158. if ($sysId !== '') {
  159. $groupReportList = db('设备_工分计酬')
  160. ->where('work_order', $params['workorder'])
  161. ->where('part_code', $params['part_code'])
  162. ->where('sys_id', $sysId)
  163. ->whereNull('del_rq')
  164. ->field('process_code, SUM(number) as completed_qty')
  165. ->group('process_code')
  166. ->select();
  167. foreach ($groupReportList as $report) {
  168. $groupCompletedMap[trim((string)$report['process_code'])] = floatval($report['completed_qty']);
  169. }
  170. }
  171. foreach ($data as $key => $row) {
  172. $processCode = trim((string)$row['工艺编号']);
  173. $completedQty = $completedMap[$processCode] ?? 0;
  174. $groupCompletedQty = $groupCompletedMap[$processCode] ?? 0;
  175. $data[$key]['制造数量'] = $manufactureQty;
  176. $data[$key]['累计完成数'] = $completedQty;
  177. $data[$key]['本组完成数'] = $groupCompletedQty;
  178. $data[$key]['未完成数'] = floatval($manufactureQty) - $completedQty;
  179. }
  180. $this->success('成功', $data);
  181. }
  182. /**
  183. * 工分报工接口
  184. * @ApiMethod (POST)
  185. * @param array $list 报工数据
  186. * @return array
  187. * @throws \think\db\exception\DataNotFoundException
  188. * @throws \think\db\exception\ModelNotFoundException
  189. * @throws \think\exception\DbException
  190. */
  191. public function ReportingWork()
  192. {
  193. if (!$this->request->isPost()) {
  194. $this->error('请求方法错误');
  195. }
  196. $params = $this->request->post();
  197. $reportList = [];
  198. if (isset($params[0]) && is_array($params[0])) {
  199. $reportList = $params;
  200. } elseif (!empty($params['list']) && is_array($params['list'])) {
  201. $reportList = $params['list'];
  202. } elseif (!empty($params['data']) && is_array($params['data'])) {
  203. $reportList = $params['data'];
  204. } else {
  205. $rawBody = file_get_contents('php://input');
  206. $decoded = json_decode($rawBody, true);
  207. if (is_array($decoded)) {
  208. if (isset($decoded[0]) && is_array($decoded[0])) {
  209. $reportList = $decoded;
  210. } elseif (!empty($decoded['list']) && is_array($decoded['list'])) {
  211. $reportList = $decoded['list'];
  212. } elseif (!empty($decoded['data']) && is_array($decoded['data'])) {
  213. $reportList = $decoded['data'];
  214. }
  215. }
  216. }
  217. if (empty($reportList)) {
  218. $this->error('请传入报工数据');
  219. }
  220. $insertData = [];
  221. $logList = [];
  222. $now = date('Y-m-d H:i:s');
  223. foreach ($reportList as $idx => $item) {
  224. $rowNo = $idx + 1;
  225. if (empty($item['staff_no'])) {
  226. $this->error('第' . $rowNo . '条员工编号不能为空');
  227. }
  228. if (empty($item['staff_name'])) {
  229. $this->error('第' . $rowNo . '条员工姓名不能为空');
  230. }
  231. if (empty($item['work_order']) && empty($item['workorder'])) {
  232. $this->error('第' . $rowNo . '条订单编号不能为空');
  233. }
  234. if (empty($item['majorprocess'])) {
  235. $this->error('第' . $rowNo . '条大工序不能为空');
  236. }
  237. $isSewing = $item['majorprocess'] === '车缝';
  238. if ($isSewing && (!isset($item['part_code']) || $item['part_code'] === '')) {
  239. $this->error('第' . $rowNo . '条车缝工序必须填写部件编号');
  240. }
  241. if (empty($item['process_code'])) {
  242. $this->error('第' . $rowNo . '条工艺编号不能为空');
  243. }
  244. if (!isset($item['number']) || $item['number'] === '') {
  245. $this->error('第' . $rowNo . '条产量不能为空');
  246. }
  247. $workOrder = !empty($item['work_order']) ? trim((string)$item['work_order']) : trim((string)$item['workorder']);
  248. $processCode = trim((string)$item['process_code']);
  249. $partCode = null;
  250. if (isset($item['part_code']) && $item['part_code'] !== '') {
  251. $partCode = intval($item['part_code']);
  252. }
  253. $number = floatval($item['number']);
  254. if ($number <= 0) {
  255. $this->error('第' . $rowNo . '条产量必须大于0');
  256. }
  257. // 校验工序在工艺资料中仍存在,并以工艺表为准写入名称/工分(避免前端缓存旧数据)
  258. $processQuery = db('工单_基础工艺资料')
  259. ->where('work_order', $workOrder)
  260. ->where('process_code', $processCode)
  261. ->whereNull('del_rq');
  262. if ($partCode !== null) {
  263. $processQuery->where('part_code', $partCode);
  264. }
  265. $processInfo = $processQuery
  266. ->field('process_code,process_name,part_code,standard_hour,standard_minutes,standard_score,money,coefficient,big_process')
  267. ->find();
  268. if (empty($processInfo)) {
  269. $this->error('第' . $rowNo . '条工序不存在或已删除(订单' . $workOrder . ' 工序号' . $processCode
  270. . ($partCode !== null ? ' 部件' . $partCode : '') . '),请刷新工艺后重试');
  271. }
  272. $processName = trim((string)$processInfo['process_name']);
  273. $standardHour = floatval($processInfo['standard_hour']);
  274. $standardScore = floatval($processInfo['standard_score']);
  275. $standardMinutes = floatval($processInfo['standard_minutes']);
  276. $money = floatval($processInfo['money']);
  277. $coefficient = isset($processInfo['coefficient']) && $processInfo['coefficient'] !== ''
  278. ? $processInfo['coefficient']
  279. : (isset($item['coefficient']) && $item['coefficient'] !== '' ? $item['coefficient'] : 'C');
  280. if ($partCode === null && isset($processInfo['part_code']) && $processInfo['part_code'] !== '') {
  281. $partCode = intval($processInfo['part_code']);
  282. }
  283. $productionHour = round($standardHour * $number, 2);
  284. $productionScore = round($standardScore * $number, 2);
  285. $salary = $this->calcReportSalary($number, $standardScore);
  286. $sysId = isset($item['sys_id']) ? trim((string)$item['sys_id']) : '';
  287. $insertData[] = [
  288. 'staff_no' => $item['staff_no'],
  289. 'staff_name' => $item['staff_name'],
  290. 'work_order' => $workOrder,
  291. 'date' => !empty($item['date']) ? $item['date'] : date('Y-m-d'),
  292. 'majorprocess' => $item['majorprocess'],
  293. 'part_code' => $partCode,
  294. 'process_code' => $processCode,
  295. 'process_name' => $processName,
  296. 'standard_hour' => $standardHour,
  297. 'standard_score' => $standardScore,
  298. 'standard_minutes' => $standardMinutes,
  299. 'money' => $money,
  300. 'coefficient' => $coefficient,
  301. 'number' => $number,
  302. 'production_hour' => $productionHour,
  303. 'production_score' => $productionScore,
  304. 'salary' => $salary,
  305. 'machine' => isset($item['machine']) ? trim((string)$item['machine']) : '',
  306. 'sys_id' => $sysId,
  307. 'sys_rq' => $now,
  308. 'mod_id' => isset($item['mod_id']) ? $item['mod_id'] : '',
  309. 'mod_rq' => null,
  310. 'del_rq' => null,
  311. ];
  312. $logList[] = $this->buildReportingWorkLogRow([
  313. 'oper_type' => '新增报工',
  314. 'oper_user' => $sysId !== '' ? $sysId : (isset($item['mod_id']) ? $item['mod_id'] : ''),
  315. 'oper_time' => $now,
  316. 'work_order' => $workOrder,
  317. 'majorprocess' => $item['majorprocess'],
  318. 'part_code' => $partCode,
  319. 'process_code' => $processCode,
  320. 'process_name' => $processName,
  321. 'staff_no' => $item['staff_no'],
  322. 'staff_name' => $item['staff_name'],
  323. 'machine' => isset($item['machine']) ? trim((string)$item['machine']) : '',
  324. 'report_date' => !empty($item['date']) ? $item['date'] : date('Y-m-d'),
  325. ], '数量', '', (string)$number);
  326. }
  327. Db::startTrans();
  328. try {
  329. $result = db('设备_工分计酬')->insertAll($insertData);
  330. if ($result === false) {
  331. throw new \Exception('报工失败');
  332. }
  333. $this->writeReportingWorkLogs($logList);
  334. Db::commit();
  335. } catch (\think\exception\HttpResponseException $e) {
  336. throw $e;
  337. } catch (\Exception $e) {
  338. Db::rollback();
  339. $this->error('报工失败:' . $e->getMessage());
  340. }
  341. $this->success('报工成功', ['count' => count($insertData)]);
  342. }
  343. //报工工分数据表左侧菜单栏
  344. public function GetReportingWorkLeft()
  345. {
  346. if (!$this->request->isGet()) {
  347. $this->error('请求方法错误');
  348. }
  349. $startDate = date('Y-m-d', strtotime('-39 day'));
  350. $endDate = date('Y-m-d');
  351. $list = db('设备_工分计酬')
  352. ->where('del_rq', null)
  353. ->where('date', 'between time', [$startDate, $endDate])
  354. ->where('machine', '<>', '')
  355. ->field('machine,date')
  356. ->order('machine asc,date desc')
  357. ->select();
  358. $menuMap = [];
  359. foreach ($list as $row) {
  360. $machine = $row['machine'];
  361. $date = $row['date'];
  362. if (!isset($menuMap[$machine])) {
  363. $menuMap[$machine] = [];
  364. }
  365. if (!in_array($date, $menuMap[$machine])) {
  366. $menuMap[$machine][] = $date;
  367. }
  368. }
  369. $data = [];
  370. foreach ($menuMap as $machine => $dateList) {
  371. $children = [];
  372. foreach ($dateList as $date) {
  373. $children[] = [
  374. 'date' => $date,
  375. ];
  376. }
  377. $data[] = [
  378. 'machine' => $machine,
  379. 'children' => $children,
  380. ];
  381. }
  382. $this->success('成功', $data);
  383. }
  384. /**
  385. * 机台报工数据
  386. * @ApiMethod (GET)
  387. * @param string $date 日期
  388. * @param string $machine 机台编号
  389. * @return array
  390. * @throws \think\db\exception\DataNotFoundException
  391. * @throws \think\db\exception\ModelNotFoundException
  392. * @throws \think\exception\DbException
  393. */
  394. public function GetReportingWorkData()
  395. {
  396. if (!$this->request->isGet()) {
  397. $this->error('请求方法错误');
  398. }
  399. $params = $this->request->param();
  400. if (empty($params['date'])) {
  401. $this->error('日期不能为空');
  402. }
  403. if (empty($params['machine'])) {
  404. $this->error('机台编号不能为空');
  405. }
  406. $rows = db('设备_工分计酬')
  407. ->where('del_rq', null)
  408. ->where('date', 'like', $params['date'] . '%')
  409. ->where('machine', $params['machine'])
  410. ->field('id,work_order,majorprocess,part_code,process_code,process_name,staff_no,staff_name,standard_hour,
  411. standard_score,money,coefficient,number,production_hour,production_score,
  412. salary,machine,sys_id,sys_rq')
  413. ->order('process_code asc,staff_no asc')
  414. ->select();
  415. $processMap = [];
  416. foreach ($rows as $row) {
  417. $groupKey = $row['majorprocess'] . '|' . $row['process_code'] . '|' . $row['process_name'] . '|' . $row['part_code'];
  418. if (!isset($processMap[$groupKey])) {
  419. $processMap[$groupKey] = [
  420. 'work_order' => $row['work_order'],
  421. 'majorprocess' => $row['majorprocess'],
  422. 'part_code' => $row['part_code'],
  423. 'process_code' => $row['process_code'],
  424. 'process_name' => $row['process_name'],
  425. 'machine' => $row['machine'],
  426. 'date' => $params['date'],
  427. 'staffs' => [],
  428. ];
  429. }
  430. $processMap[$groupKey]['staffs'][] = [
  431. 'staff_no' => $row['staff_no'],
  432. 'staff_name' => $row['staff_name'],
  433. 'standard_hour' => $row['standard_hour'],
  434. 'standard_score' => $row['standard_score'],
  435. 'money' => $row['money'],
  436. 'coefficient' => $row['coefficient'],
  437. 'number' => $row['number'],
  438. 'production_hour' => $row['production_hour'],
  439. 'production_score' => $row['production_score'],
  440. 'salary' => $row['salary'],
  441. 'sys_id' => $row['sys_id'],
  442. 'sys_rq' => $row['sys_rq'],
  443. 'id' => $row['id'],
  444. ];
  445. }
  446. $data = array_values($processMap);
  447. $this->success('成功', $data);
  448. }
  449. /**
  450. * 修改工分报工数据
  451. * @ApiMethod (POST)
  452. * @param int $id 报工记录ID
  453. * @param float $number 数量
  454. * @param float $production_hour 报工工时(可选,不传则按标准工时×数量重算)
  455. * @param float $production_score 报工工分(可选,不传则按标准工分×数量重算)
  456. * @param string $mod_id 修改人(可选)
  457. * @return array
  458. * @throws \think\db\exception\DataNotFoundException
  459. * @throws \think\db\exception\ModelNotFoundException
  460. * @throws \think\exception\DbException
  461. */
  462. public function UpdateReportingWork()
  463. {
  464. if (!$this->request->isPost()) {
  465. $this->error('请求方法错误');
  466. }
  467. $params = $this->request->post();
  468. if (empty($params['id'])) {
  469. $this->error('ID不能为空');
  470. }
  471. if (!isset($params['number']) || $params['number'] === '') {
  472. $this->error('产量不能为空');
  473. }
  474. $record = db('设备_工分计酬')
  475. ->where('id', $params['id'])
  476. ->whereNull('del_rq')
  477. ->find();
  478. if (empty($record)) {
  479. $this->error('报工记录不存在或已删除');
  480. }
  481. $number = floatval($params['number']);
  482. if ($number <= 0) {
  483. $this->error('产量必须大于0');
  484. }
  485. $standardHour = floatval($record['standard_hour']);
  486. $standardScore = floatval($record['standard_score']);
  487. $productionHour = round($standardHour * $number, 2);
  488. $productionScore = round($standardScore * $number, 2);
  489. $salary = $this->calcReportSalary($number, $standardScore);
  490. $operTime = date('Y-m-d H:i:s');
  491. $operUser = !empty($params['mod_id']) ? $params['mod_id'] : ($record['sys_id'] ?? '');
  492. $data = [
  493. 'mod_rq' => $operTime,
  494. 'number' => $number,
  495. 'production_hour' => $productionHour,
  496. 'production_score' => $productionScore,
  497. 'salary' => $salary,
  498. ];
  499. if (!empty($params['mod_id'])) {
  500. $data['mod_id'] = $params['mod_id'];
  501. }
  502. $logContext = [
  503. 'report_id' => intval($record['id']),
  504. 'oper_type' => '修改报工',
  505. 'oper_user' => $operUser,
  506. 'oper_time' => $operTime,
  507. 'work_order' => $record['work_order'],
  508. 'majorprocess' => $record['majorprocess'],
  509. 'part_code' => $record['part_code'],
  510. 'process_code' => $record['process_code'],
  511. 'process_name' => $record['process_name'],
  512. 'staff_no' => $record['staff_no'],
  513. 'staff_name' => $record['staff_name'],
  514. 'machine' => $record['machine'] ?? '',
  515. 'report_date' => $record['date'] ?? null,
  516. ];
  517. $logList = $this->buildReportingWorkChangeLogs($logContext, [
  518. '数量' => [$record['number'], $number],
  519. '报工工时' => [$record['production_hour'], $productionHour],
  520. '报工工分' => [$record['production_score'], $productionScore],
  521. '工资' => [$record['salary'], $salary],
  522. ]);
  523. Db::startTrans();
  524. try {
  525. $result = db('设备_工分计酬')
  526. ->where('id', $params['id'])
  527. ->whereNull('del_rq')
  528. ->update($data);
  529. if ($result === false) {
  530. throw new \Exception('修改失败');
  531. }
  532. if ($result === 0) {
  533. throw new \Exception('报工记录不存在或已删除');
  534. }
  535. $this->writeReportingWorkLogs($logList);
  536. Db::commit();
  537. } catch (\think\exception\HttpResponseException $e) {
  538. throw $e;
  539. } catch (\Exception $e) {
  540. Db::rollback();
  541. $this->error($e->getMessage());
  542. }
  543. $this->success('修改成功', [
  544. 'number' => $number,
  545. 'production_hour' => $productionHour,
  546. 'production_score' => $productionScore,
  547. 'salary' => $salary,
  548. ]);
  549. }
  550. /**
  551. * 删除报工数据
  552. * @ApiMethod (POST)
  553. * @param array $id ID
  554. * @return array
  555. * @throws \think\db\exception\DataNotFoundException
  556. * @throws \think\db\exception\ModelNotFoundException
  557. * @throws \think\exception\DbException
  558. */
  559. public function DeleteReportingWork()
  560. {
  561. if (!$this->request->isPost()) {
  562. $this->error('请求方法错误');
  563. }
  564. $params = $this->request->post();
  565. if (empty($params['id'])) {
  566. $this->error('ID不能为空');
  567. }
  568. $record = db('设备_工分计酬')
  569. ->where('id', $params['id'])
  570. ->whereNull('del_rq')
  571. ->find();
  572. if (empty($record)) {
  573. $this->error('报工记录不存在或已删除');
  574. }
  575. $operTime = date('Y-m-d H:i:s');
  576. $operUser = !empty($params['mod_id']) ? $params['mod_id'] : ($record['sys_id'] ?? '');
  577. $logContext = [
  578. 'report_id' => intval($record['id']),
  579. 'oper_type' => '删除报工',
  580. 'oper_user' => $operUser,
  581. 'oper_time' => $operTime,
  582. 'work_order' => $record['work_order'],
  583. 'majorprocess' => $record['majorprocess'],
  584. 'part_code' => $record['part_code'],
  585. 'process_code' => $record['process_code'],
  586. 'process_name' => $record['process_name'],
  587. 'staff_no' => $record['staff_no'],
  588. 'staff_name' => $record['staff_name'],
  589. 'machine' => $record['machine'] ?? '',
  590. 'report_date' => $record['date'] ?? null,
  591. ];
  592. $logList = $this->buildReportingWorkChangeLogs($logContext, [
  593. '数量' => [$record['number'], ''],
  594. '报工工分' => [$record['production_score'], ''],
  595. '工资' => [$record['salary'], ''],
  596. ]);
  597. $data = [
  598. 'del_rq' => $operTime,
  599. ];
  600. if (!empty($params['mod_id'])) {
  601. $data['mod_id'] = $params['mod_id'];
  602. $data['mod_rq'] = $operTime;
  603. }
  604. Db::startTrans();
  605. try {
  606. $result = db('设备_工分计酬')
  607. ->where('id', $params['id'])
  608. ->whereNull('del_rq')
  609. ->update($data);
  610. if ($result === false) {
  611. throw new \Exception('删除失败');
  612. }
  613. if ($result === 0) {
  614. throw new \Exception('报工记录不存在或已删除');
  615. }
  616. $this->writeReportingWorkLogs($logList);
  617. Db::commit();
  618. } catch (\think\exception\HttpResponseException $e) {
  619. throw $e;
  620. } catch (\Exception $e) {
  621. Db::rollback();
  622. $this->error($e->getMessage());
  623. }
  624. $this->success('删除成功');
  625. }
  626. /**
  627. * 工分报工操作日志查询
  628. * @ApiMethod (GET)
  629. * @param string $workorder 订单编号(可选)
  630. * @param int $report_id 报工记录ID(可选)
  631. * @param string $oper_type 操作类型(可选)
  632. * @param string $date_start 操作时间起(可选,Y-m-d)
  633. * @param string $date_end 操作时间止(可选,Y-m-d)
  634. * @param int $page 页码,默认1
  635. * @param int $limit 每页条数,默认20
  636. */
  637. public function GetReportingWorkLog()
  638. {
  639. if (!$this->request->isGet()) {
  640. $this->error('请求方法错误');
  641. }
  642. $this->ensureReportingWorkLogTable();
  643. $params = $this->request->param();
  644. $page = max(1, intval($params['page'] ?? 1));
  645. $limit = max(1, min(200, intval($params['limit'] ?? 20)));
  646. $offset = ($page - 1) * $limit;
  647. $query = Db::table('设备_工分报工操作日志');
  648. if (!empty($params['workorder'])) {
  649. $query->where('work_order', $params['workorder']);
  650. }
  651. if (!empty($params['report_id'])) {
  652. $query->where('report_id', intval($params['report_id']));
  653. }
  654. if (!empty($params['oper_type'])) {
  655. $query->where('oper_type', $params['oper_type']);
  656. }
  657. if (!empty($params['date_start'])) {
  658. $query->where('oper_time', '>=', $params['date_start'] . ' 00:00:00');
  659. }
  660. if (!empty($params['date_end'])) {
  661. $query->where('oper_time', '<=', $params['date_end'] . ' 23:59:59');
  662. }
  663. $total = (int)(clone $query)->count();
  664. $list = $query
  665. ->order('oper_time desc,id desc')
  666. ->limit($offset, $limit)
  667. ->select();
  668. $this->success('成功', [
  669. 'total' => $total,
  670. 'page' => $page,
  671. 'limit' => $limit,
  672. 'list' => $list,
  673. ]);
  674. }
  675. /**
  676. * 查询机台报工记录
  677. * @ApiMethod (GET)
  678. * @param string $date 日期
  679. * @param string $machine 机台编号
  680. * @return array
  681. * @throws \think\db\exception\DataNotFoundException
  682. * @throws \think\db\exception\ModelNotFoundException
  683. * @throws \think\exception\DbException
  684. */
  685. public function GetReportingWorkRecord()
  686. {
  687. if (!$this->request->isGet()) {
  688. $this->error('请求方法错误');
  689. }
  690. $params = $this->request->param();
  691. if (empty($params['date'])) {
  692. $this->error('日期不能为空');
  693. }
  694. if (empty($params['machine'])) {
  695. $this->error('机台编号不能为空');
  696. }
  697. $rows = db('设备_工分计酬')->alias('a')
  698. ->join('工单_基本资料 b', 'a.work_order = b.订单编号', 'LEFT')
  699. ->where('a.del_rq', null)
  700. ->where('b.Mod_rq', null)
  701. ->where('a.date', 'like', $params['date'] . '%')
  702. ->where('a.machine', $params['machine'])
  703. ->field('
  704. a.work_order,
  705. a.majorprocess,
  706. a.part_code,
  707. a.process_code,
  708. a.process_name,
  709. a.staff_no,
  710. a.staff_name,
  711. a.standard_hour,
  712. a.standard_score,
  713. a.money,
  714. a.coefficient,
  715. a.number,
  716. a.production_hour,
  717. a.production_score,
  718. a.salary,machine,
  719. a.sys_id,
  720. a.sys_rq,
  721. a.id,
  722. b.生产款号,
  723. b.款式
  724. ')
  725. ->order('a.process_code asc,a.staff_no asc,a.sys_rq desc')
  726. ->select();
  727. $this->success('成功', $rows);
  728. }
  729. /**
  730. * 按新公式批量重算历史报工工资(数量 × 工分 × 0.067,保留2位)
  731. * @ApiMethod (POST)
  732. * @param string workorder 可选,限定工单编号
  733. * @param string date_start 可选,报工日期起(Y-m-d)
  734. * @param string date_end 可选,报工日期止(Y-m-d)
  735. */
  736. public function recalculateHistoricalSalary()
  737. {
  738. if (!$this->request->isPost()) {
  739. $this->error('请求方法错误');
  740. }
  741. $params = $this->request->post();
  742. $applyFilters = function ($query) use ($params) {
  743. if (!empty($params['workorder'])) {
  744. $query->where('work_order', $params['workorder']);
  745. }
  746. if (!empty($params['date_start'])) {
  747. $query->where('date', '>=', $params['date_start']);
  748. }
  749. if (!empty($params['date_end'])) {
  750. $query->where('date', '<=', $params['date_end']);
  751. }
  752. return $query;
  753. };
  754. $total = (int)$applyFilters(Db::table('设备_工分计酬')->whereNull('del_rq'))->count();
  755. if ($total === 0) {
  756. $this->error('没有需要重算的报工数据');
  757. }
  758. Db::startTrans();
  759. try {
  760. $affected = $applyFilters(Db::table('设备_工分计酬')->whereNull('del_rq'))->update([
  761. 'production_score' => Db::raw('ROUND(`number` * `standard_score`, 2)'),
  762. 'salary' => Db::raw('ROUND(`number` * `standard_score` * 0.067, 2)'),
  763. ]);
  764. if ($affected === false) {
  765. throw new \Exception('批量更新失败');
  766. }
  767. Db::commit();
  768. } catch (\think\exception\HttpResponseException $e) {
  769. throw $e;
  770. } catch (\Exception $e) {
  771. Db::rollback();
  772. $this->error('重算失败:' . $e->getMessage());
  773. }
  774. $this->success('重算成功', [
  775. 'matched' => $total,
  776. 'updated' => (int)$affected,
  777. ]);
  778. }
  779. /**
  780. * 确保工分报工操作日志表存在
  781. */
  782. private function ensureReportingWorkLogTable()
  783. {
  784. if (self::$reportingWorkLogTableReady) {
  785. return;
  786. }
  787. $sql = <<<SQL
  788. CREATE TABLE IF NOT EXISTS `设备_工分报工操作日志` (
  789. `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  790. `report_id` int(11) DEFAULT NULL COMMENT '报工记录ID',
  791. `oper_type` varchar(32) NOT NULL DEFAULT '' COMMENT '操作类型',
  792. `oper_user` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人',
  793. `oper_time` datetime NOT NULL COMMENT '操作时间',
  794. `work_order` varchar(64) NOT NULL DEFAULT '' COMMENT '订单编号',
  795. `majorprocess` varchar(32) NOT NULL DEFAULT '' COMMENT '大工序',
  796. `part_code` int(11) DEFAULT NULL COMMENT '部件编号',
  797. `process_code` varchar(32) NOT NULL DEFAULT '' COMMENT '工艺编号',
  798. `process_name` varchar(128) NOT NULL DEFAULT '' COMMENT '工艺名称',
  799. `staff_no` varchar(32) NOT NULL DEFAULT '' COMMENT '员工编号',
  800. `staff_name` varchar(64) NOT NULL DEFAULT '' COMMENT '员工姓名',
  801. `machine` varchar(64) NOT NULL DEFAULT '' COMMENT '机台编号',
  802. `report_date` date DEFAULT NULL COMMENT '报工日期',
  803. `change_field` varchar(32) NOT NULL DEFAULT '' COMMENT '变更字段',
  804. `old_value` varchar(255) NOT NULL DEFAULT '' COMMENT '旧值',
  805. `new_value` varchar(255) NOT NULL DEFAULT '' COMMENT '新值',
  806. PRIMARY KEY (`id`),
  807. KEY `idx_report_id` (`report_id`),
  808. KEY `idx_work_order` (`work_order`),
  809. KEY `idx_oper_time` (`oper_time`)
  810. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工分报工操作日志'
  811. SQL;
  812. Db::execute($sql);
  813. self::$reportingWorkLogTableReady = true;
  814. }
  815. /**
  816. * 写入工分报工操作日志
  817. * @param array $logs
  818. */
  819. private function writeReportingWorkLogs(array $logs)
  820. {
  821. if (empty($logs)) {
  822. return;
  823. }
  824. $this->ensureReportingWorkLogTable();
  825. $result = Db::table('设备_工分报工操作日志')->insertAll($logs);
  826. if ($result === false) {
  827. throw new \Exception('写入工分报工操作日志失败');
  828. }
  829. }
  830. /**
  831. * 构建单条工分报工操作日志
  832. */
  833. private function buildReportingWorkLogRow(array $context, $changeField, $oldValue, $newValue)
  834. {
  835. return [
  836. 'report_id' => isset($context['report_id']) ? $context['report_id'] : null,
  837. 'oper_type' => $context['oper_type'] ?? '',
  838. 'oper_user' => $context['oper_user'] ?? '',
  839. 'oper_time' => $context['oper_time'] ?? date('Y-m-d H:i:s'),
  840. 'work_order' => $context['work_order'] ?? '',
  841. 'majorprocess' => $context['majorprocess'] ?? '',
  842. 'part_code' => isset($context['part_code']) ? $context['part_code'] : null,
  843. 'process_code' => $context['process_code'] ?? '',
  844. 'process_name' => $context['process_name'] ?? '',
  845. 'staff_no' => $context['staff_no'] ?? '',
  846. 'staff_name' => $context['staff_name'] ?? '',
  847. 'machine' => $context['machine'] ?? '',
  848. 'report_date' => !empty($context['report_date']) ? $context['report_date'] : null,
  849. 'change_field' => (string)$changeField,
  850. 'old_value' => $oldValue === null ? '' : (string)$oldValue,
  851. 'new_value' => $newValue === null ? '' : (string)$newValue,
  852. ];
  853. }
  854. /**
  855. * 构建字段变更日志(仅记录有变化的字段)
  856. */
  857. private function buildReportingWorkChangeLogs(array $context, array $changes)
  858. {
  859. $logs = [];
  860. foreach ($changes as $field => $values) {
  861. $oldValue = $values[0];
  862. $newValue = $values[1];
  863. if ((string)$oldValue === (string)$newValue) {
  864. continue;
  865. }
  866. $logs[] = $this->buildReportingWorkLogRow($context, $field, $oldValue, $newValue);
  867. }
  868. return $logs;
  869. }
  870. /**
  871. * 报工工资:数量 × 工分 × 0.067(保留2位,与库字段 decimal(10,2) 一致)
  872. * @param mixed $number
  873. * @param mixed $standardScore
  874. * @return float
  875. */
  876. private function calcReportSalary($number, $standardScore)
  877. {
  878. return round(floatval($number) * floatval($standardScore) * 0.067, 2);
  879. }
  880. }