ReportingWork.php 34 KB

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