U8ThirtyPartyDatabaseServerService.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. <?php
  2. namespace App\Service;
  3. use Illuminate\Support\Facades\DB;
  4. use Illuminate\Support\Facades\Log;
  5. class U8ThirtyPartyDatabaseServerService extends Service
  6. {
  7. protected $connectionName = 'u8_third_sqlserver';
  8. /**
  9. * 获取一个具备“自愈”功能的数据库连接
  10. * 解决 Windows 环境下远程主机强迫关闭连接 (08S02) 的问题
  11. */
  12. private function safeDb()
  13. {
  14. $conn = DB::connection($this->connectionName);
  15. try {
  16. // 心跳探测:执行一个极轻量的查询
  17. $conn->getPdo()->query('SELECT 1');
  18. } catch (\Throwable $e) {
  19. // 捕获到任何连接异常(如 08S02 / 08001)
  20. Log::channel('apiLog')->warning("U8连接丢失,正在尝试重连...", [
  21. 'msg' => $e->getMessage()
  22. ]);
  23. // 彻底销毁旧连接句柄
  24. $conn->disconnect();
  25. DB::purge($this->connectionName);
  26. // 重新获取新连接
  27. $conn = DB::connection($this->connectionName);
  28. }
  29. return $conn;
  30. }
  31. /**
  32. * 写入分期收款发出商品未记账表 (IA_SA_UnAccountVouch)
  33. * @param string $cMemo 对应发货单备注中的接口标识
  34. * @return array
  35. */
  36. public function insertUnAccountVouch($cMemo)
  37. {
  38. try {
  39. $db = $this->safeDb();
  40. return $db->transaction(function () use ($db, $cMemo) {
  41. // 1. 模拟查询:关联发货单主子表,找到需要处理的数据
  42. // U8 中 DispatchList 是发货单主表,DispatchLists 是子表
  43. $pendingData = $db->table('DispatchList as H')
  44. ->join('DispatchLists as D', 'H.DLID', '=', 'D.DLID')
  45. ->where('H.cMemo', '接口生成')
  46. ->where('H.dDate', '>=', '2026-04-01')
  47. ->where('H.dDate', '<=', '2026-04-30')
  48. ->whereNotExists(function ($query) use ($db) {
  49. $query->select($db->raw(1))
  50. ->from('IA_SA_UnAccountVouch as IA')
  51. ->whereRaw('IA.IDUN = H.DLID')
  52. ->whereRaw('IA.IDSUN = D.iDLsid');
  53. })
  54. ->select([
  55. 'H.DLID as IDUN', // 发货单主表ID
  56. 'D.iDLsid as IDSUN', // 发货单子表ID
  57. ])
  58. ->get();
  59. if ($pendingData->isEmpty()) {
  60. return [false, "未找到备注为 {$cMemo} 的发货单数据"];
  61. }
  62. $insertData = [];
  63. foreach ($pendingData as $row) {
  64. $insertData[] = [
  65. 'IDUN' => $row->IDUN,
  66. 'IDSUN' => $row->IDSUN,
  67. 'cVouTypeUN' => '05', // 写死固定值
  68. 'cBustypeUN' => '分期收款', // 写死固定值
  69. ];
  70. }
  71. // 2. 执行批量插入
  72. if (!empty($insertData)) {
  73. $db->table('IA_SA_UnAccountVouch')->insert($insertData);
  74. }
  75. return [true, "成功写入 " . count($insertData) . " 条数据"];
  76. });
  77. } catch (\Throwable $e) {
  78. return [false, "写入 IA_SA_UnAccountVouch 失败: " . $e->getMessage()];
  79. }
  80. }
  81. // Service 内部查询
  82. public function getPendingBills($config, $lastId)
  83. {
  84. try {
  85. $db = $this->safeDb();
  86. $mainTable = $config['main'];
  87. $detailTable = $config['detail'];
  88. $mainKey = $config['main_key'];
  89. $foreignKey = $config['key'];
  90. $whereRaw = $config['whereRaw'];
  91. $main_field = $config['main_field'];
  92. $son_field = $config['son_field'];
  93. $limit = $config['limit'];
  94. // 1. 获取主表
  95. $mainRows = $db->table($mainTable)
  96. ->lock('WITH(NOLOCK)')
  97. ->where($mainKey, '>', $lastId) // 改用 ID
  98. ->whereRaw($whereRaw)
  99. ->select($main_field)
  100. ->orderBy($mainKey, 'ASC') // 按 ID 排序保证分页连续
  101. ->limit($limit)
  102. ->get();
  103. if ($mainRows->isEmpty()) return [true, []];
  104. if (empty($foreignKey) || empty($son_field)) {
  105. $array = $mainRows->map(function ($item) {
  106. return (array) $item;
  107. })->toArray();
  108. return [true, $array];
  109. }
  110. // 2. 批量获取子表
  111. $mainIds = $mainRows->pluck('id')->toArray();
  112. $details = $db->table($detailTable. ' as detail')
  113. ->lock('WITH(NOLOCK)')
  114. ->whereIn($foreignKey, $mainIds)
  115. ->select($son_field)
  116. ->addSelect(DB::raw("ROW_NUMBER() OVER(PARTITION BY detail.{$foreignKey} ORDER BY detail.AutoID) as lineNum"))
  117. ->get()
  118. ->groupBy('id');
  119. // 3. 组合
  120. $result = $mainRows->map(function($main) use ($details, $foreignKey) {
  121. $res = (array)$main;
  122. $mainId = $res['id'];
  123. $res['details'] = isset($details[$mainId])
  124. ? $details[$mainId]->map(function($item) {
  125. return (array)$item; // 强制把子表对象转成数组
  126. })->values()->toArray()
  127. : [];
  128. return $res;
  129. })->toArray();
  130. return [true, $result];
  131. } catch (\Throwable $e) {
  132. return [false, $e->getMessage()];
  133. }
  134. }
  135. public function getArrivalVouchById($orderId)
  136. {
  137. try {
  138. $db = $this->safeDb();
  139. // 1. 获取主表 (PU_ArrivalVouch) 并关联 采购类型表 (PurchaseType)
  140. $main = $db->table('PU_ArrivalVouch as main')
  141. ->leftJoin('PurchaseType as pt', 'main.cPTCode', '=', 'pt.cPTCode') // 关联采购类型表
  142. ->lock('WITH(NOLOCK)')
  143. ->where('main.ID', $orderId)
  144. ->select([
  145. 'main.ID as id',
  146. 'main.cCode as no',
  147. 'main.cVenCode as supply_code',
  148. 'main.cDepCode as depart_code',
  149. 'main.dDate as order_date',
  150. 'main.cBusType as b_type',
  151. 'main.cPTCode as cg_type',
  152. 'pt.cRdCode as rd_code'
  153. ])
  154. ->first();
  155. if (!$main) return [false, '采购退货单数据不存在'];
  156. $order = (array)$main;
  157. // 2. 获取子表 (PU_ArrivalVouchs)
  158. $details = $db->table('PU_ArrivalVouchs as detail')
  159. ->lock('WITH(NOLOCK)')
  160. ->where('ID', $orderId) // 子表里的 ID 关联主表的 ID
  161. ->select([
  162. 'detail.cInvCode',
  163. 'detail.iQuantity',
  164. 'detail.iQuantity as iNNum',
  165. 'detail.iinvexchrate',
  166. 'detail.cBatch',
  167. 'detail.dPDate',
  168. 'detail.dVDate',
  169. 'detail.Autoid as iArrsId',
  170. 'detail.ivouchrowno as iRowNo',
  171. 'detail.iOriTaxCost',
  172. 'detail.iTaxRate',
  173. // 'detail.cordercode',
  174. 'detail.cWhCode',
  175. ])
  176. ->orderBy('detail.ivouchrowno', 'ASC') // 按 U8 原始行号排序
  177. ->get();
  178. // 3. 组合数据并返回
  179. $order['details'] = $details->map(function ($item) {
  180. return (array)$item;
  181. })->toArray();
  182. return [true, $order];
  183. }catch (\Throwable $e) {
  184. return [false, $e->getMessage()];
  185. }
  186. }
  187. public function getInventoryData($config, $lastCode = null)
  188. {
  189. try {
  190. $db = $this->safeDb();
  191. $query = $db->table('Inventory as main')
  192. ->leftJoin('InventoryClass as sub', 'main.cInvCCode', '=', 'sub.cInvCCode')
  193. ->leftJoin('ComputationUnit as unit', 'main.cComUnitCode', '=', 'unit.cComUnitCode')
  194. ->select([
  195. 'main.cInvCode as product_code',
  196. 'main.cInvName as product_name',
  197. 'main.cInvStd as product_size',
  198. 'main.cInvCCode as product_category_code',
  199. 'sub.cInvCName as product_category_name', // 直接联查分类名
  200. 'main.cComUnitCode as product_unit',
  201. 'unit.cComUnitName as product_unit_title',
  202. // 'main.fOutExcess as out_limit',
  203. // 'main.cCurrencyName as common_name',
  204. 'main.fGrossW as grossWeight',
  205. 'main.bFree1 as param_one',
  206. 'main.bFree2 as param_two',
  207. 'main.bInvBatch as param_three',
  208. 'main.bInvQuality as param_four',
  209. // 关键点:将二进制 ufts 转换为 0x... 字符串
  210. DB::raw("master.sys.fn_varbintohexstr(main.pubufts) as ufts_str")
  211. ]);
  212. // 使用 product_code 做分页依据
  213. if ($lastCode) {
  214. $query->where('main.cInvCode', '>', $lastCode);
  215. }
  216. if (!empty($config['whereRaw'])) {
  217. $query->whereRaw($config['whereRaw']);
  218. }
  219. // 必须按编码排序,保证分页不重不漏
  220. $items = $query->orderBy('main.cInvCode', 'asc')
  221. ->limit($config['limit'])
  222. ->get();
  223. return [true, json_decode(json_encode($items), true)];
  224. } catch (\Throwable $e) {
  225. return [false, $e->getMessage()];
  226. }
  227. }
  228. //获取单个产品报检单的明细
  229. public function getBjOrder($id){
  230. try {
  231. $db = $this->safeDb();
  232. $array = $db->table('QMINSPECTVOUCHERS as detail')
  233. ->lock('WITH(NOLOCK)')
  234. ->where('detail.ID', $id)
  235. ->select('detail.*')
  236. ->get()
  237. ->map(function ($value) {
  238. return (array)$value;
  239. })
  240. ->keyBy('CINVCODE')
  241. ->toArray();
  242. return [true, $array];
  243. } catch (\Throwable $e) {
  244. return [false, $e->getMessage()];
  245. }
  246. }
  247. public function getJyOrder($id)
  248. {
  249. try {
  250. $db = $this->safeDb();
  251. // 1. 获取表头信息 (主表)
  252. $main = $db->table('QMCHECKVOUCHER')
  253. ->lock('WITH(NOLOCK)')
  254. ->where('ID', $id)
  255. ->first();
  256. if (!$main) return [false, '检验单不存在'];
  257. $main = (array)$main;
  258. return [true, $main];
  259. } catch (\Throwable $e) {
  260. return [false, $e->getMessage()];
  261. }
  262. }
  263. public function getScDetails($id)
  264. {
  265. try {
  266. $db = $this->safeDb();
  267. // 1. 获取表头信息 (主表)
  268. $main = $db->table('mom_orderdetail')
  269. ->lock('WITH(NOLOCK)')
  270. ->where('MoDid', $id)
  271. ->first();
  272. if (!$main) {
  273. return [false, '生成订单明细不存在']; // 或者返回空数组 []
  274. }
  275. $main = (array)$main;
  276. return [true, $main];
  277. } catch (\Throwable $e) {
  278. return [false, $e->getMessage()];
  279. }
  280. }
  281. //获取单个销售订单的信息
  282. public function getXsOrder($id)
  283. {
  284. try {
  285. $db = $this->safeDb();
  286. // 1. 获取销售订单主表信息 (SO_SOMain)
  287. $main = $db->table('SO_SOMain')
  288. ->lock('WITH(NOLOCK)')
  289. ->where('ID', $id)
  290. ->first();
  291. if (!$main) {
  292. return [false, '销售订单不存在'];
  293. }
  294. $main = (array)$main;
  295. // 2. 获取销售订单子表信息 (SO_SODetails)
  296. $details = $db->table('SO_SODetails')
  297. ->lock('WITH(NOLOCK)')
  298. ->where('ID', $id) // 子表通过 ID 关联主表
  299. ->get()
  300. ->map(function ($value) {
  301. return (array)$value;
  302. })
  303. ->toArray();
  304. // 3. 将子表数据放入 details 键中
  305. $main['details'] = $details;
  306. return [true, $main];
  307. } catch (\Throwable $e) {
  308. return [false, $e->getMessage()];
  309. }
  310. }
  311. public function getXsThOrder($id)
  312. {
  313. try {
  314. $db = $this->safeDb();
  315. // 1. 获取销售退货单主表信息 (DispatchList)
  316. $main = $db->table('DispatchList')
  317. ->lock('WITH(NOLOCK)')
  318. ->where('DLID', $id) // 注意:主键叫 DLID
  319. ->first();
  320. if (!$main) {
  321. return [false, '销售退货单不存在'];
  322. }
  323. $main = (array)$main;
  324. // 2. 获取销售退货单子表信息 (DispatchLists)
  325. $details =$db->table('DispatchLists')
  326. ->lock('WITH(NOLOCK)')
  327. ->where('DLID', $id) // 通过 DLID 关联
  328. ->get()
  329. ->map(function ($value) {
  330. return (array)$value;
  331. })
  332. ->toArray();
  333. // 3. 将子表数据放入 details 键中
  334. $main['details'] = $details;
  335. return [true, $main];
  336. } catch (\Throwable $e) {
  337. return [false, $e->getMessage()];
  338. }
  339. }
  340. public function getFhOrder($id)
  341. {
  342. try {
  343. $db = $this->safeDb();
  344. // 1. 获取销售发货单主表信息 (DispatchList)
  345. $main = $db->table('DispatchList')
  346. ->lock('WITH(NOLOCK)')
  347. ->where('DLID', $id) // U8发货单主键通常是 DLID
  348. ->first();
  349. if (!$main) {
  350. return [false, '销售发货单不存在'];
  351. }
  352. $main = (array)$main;
  353. // 2. 获取销售发货单子表信息 (DispatchLists)
  354. $details = $db->table('DispatchLists') // 注意这里是 DispatchLists
  355. ->lock('WITH(NOLOCK)')
  356. ->where('DLID', $id) // 子表通过 DLID 关联主表
  357. ->get()
  358. ->map(function ($value) {
  359. return (array)$value;
  360. })
  361. ->toArray();
  362. // 3. 将子表数据放入 details 键中
  363. $main['details'] = $details;
  364. return [true, $main];
  365. } catch (\Throwable $e) {
  366. return [false, $e->getMessage()];
  367. }
  368. }
  369. public function getCGDHDetails($id)
  370. {
  371. try {
  372. $db = $this->safeDb();
  373. // 2. 获取采购到货单子表信息 (PU_ArrivalVouchs)
  374. $details = $db->table('PU_ArrivalVouchs')
  375. ->lock('WITH(NOLOCK)')
  376. ->where('ID', $id) // 通过 ID 关联主表
  377. ->get()
  378. ->map(function ($value) {
  379. return (array)$value;
  380. });
  381. // 3. 将子表数据以 cInvCode 为 Key 进行组织
  382. // 注意:如果一张单据里有重复的存货编码,后面的行会覆盖前面的行
  383. $details = $details->keyBy('cInvCode')->toArray();
  384. return [true, $details];
  385. } catch (\Throwable $e) {
  386. return [false, $e->getMessage()];
  387. }
  388. }
  389. public function getLlSQDetails($id)
  390. {
  391. try {
  392. $db = $this->safeDb();
  393. // 1. 获取领料申请单子表信息 (MaterialAppVouchs)
  394. $details = $db->table('MaterialAppVouchs') // 领料申请单子表名
  395. ->lock('WITH(NOLOCK)')
  396. ->where('ID', $id) // 通过 ID 关联主表
  397. ->get()
  398. ->map(function ($value) {
  399. return (array)$value;
  400. });
  401. // 2. 将子表数据以 cInvCode 为 Key 进行组织
  402. // 提示:领料申请单中同一存货可能出现在不同行(如:对应不同的生产订单行)
  403. // 如果确定 cInvCode 唯一,可用 keyBy;如果不唯一,建议直接返回数组或 keyBy('AutoID')
  404. $details = $details->keyBy('cInvCode')->toArray();
  405. return [true, $details];
  406. } catch (\Throwable $e) {
  407. return [false, $e->getMessage()];
  408. }
  409. }
  410. public function getCgOrder($id)
  411. {
  412. try {
  413. $db = $this->safeDb();
  414. // 1. 获取采购到货单主表信息 (PU_ArrivalVouch)
  415. // 关键:关联键是 ID
  416. $main = $db->table('PU_ArrivalVouch')
  417. ->lock('WITH(NOLOCK)')
  418. ->where('ID', $id)
  419. ->first();
  420. if (!$main) {
  421. return [false, '采购到货单不存在'];
  422. }
  423. $main = (array)$main;
  424. // 2. 获取采购到货单子表信息 (PU_ArrivalVouchs)
  425. $details = $db->table('PU_ArrivalVouchs')
  426. ->lock('WITH(NOLOCK)')
  427. ->where('ID', $id) // 子表通过 ID 关联主表
  428. ->get()
  429. ->map(function ($value) {
  430. return (array)$value;
  431. })
  432. ->toArray();
  433. // 3. 将子表数据放入 details 键中
  434. $main['details'] = $details;
  435. return [true, $main];
  436. } catch (\Throwable $e) {
  437. return [false, $e->getMessage()];
  438. }
  439. }
  440. public function getJyOrder2($id)
  441. {
  442. try {
  443. $db = $this->safeDb();
  444. // 关联采购到货单子表 (PU_ArrivalVouchs) 获取金额字段
  445. $order = $db->table('QMCHECKVOUCHER as qm')
  446. ->leftJoin('PU_ArrivalVouchs as arr', 'qm.SOURCEAUTOID', '=', 'arr.Autoid')
  447. ->select(
  448. 'qm.*',
  449. 'arr.iCost', // 原币无税单价
  450. 'arr.iOriTaxCost', // 原币含税单价
  451. 'arr.iTaxRate' // 税率
  452. )
  453. ->where('qm.ID', $id) // 假设这是检验单主表ID
  454. ->first();
  455. return [true, $order ? (array)$order : []];
  456. } catch (\Throwable $e) {
  457. return [false, $e->getMessage()];
  458. }
  459. }
  460. public function getLLOrder($id)
  461. {
  462. try {
  463. $db = $this->safeDb();
  464. // 关键:关联键是 ID
  465. $main = $db->table('MaterialAppVouch')
  466. ->lock('WITH(NOLOCK)')
  467. ->where('ID', $id)
  468. ->first();
  469. if (!$main) {
  470. return [false, '领料单不存在'];
  471. }
  472. $main = (array)$main;
  473. // 2. 获取采购到货单子表信息 (PU_ArrivalVouchs)
  474. $details = $db->table('MaterialAppVouchs')
  475. ->lock('WITH(NOLOCK)')
  476. ->where('ID', $id) // 子表通过 ID 关联主表
  477. ->get()
  478. ->map(function ($value) {
  479. return (array)$value;
  480. })
  481. ->toArray();
  482. // 3. 将子表数据放入 details 键中
  483. $main['details'] = $details;
  484. return [true, $main];
  485. } catch (\Throwable $e) {
  486. return [false, $e->getMessage()];
  487. }
  488. }
  489. public function checkInventoryControl($invCodes)
  490. {
  491. try {
  492. $db = $this->safeDb();
  493. if (empty($invCodes)) {
  494. return [false, '存货编码为空'];
  495. }
  496. // 1. 查询存货档案
  497. $list = $db->table('Inventory')
  498. ->whereIn('cInvCode', $invCodes)
  499. ->select(['cInvCode', 'cInvName', 'bInvBatch', 'bInvQuality'])
  500. ->get();
  501. $noBatch = []; // 未开启批次管理的
  502. $noExpiration = []; // 未开启保质期管理的
  503. $notFound = array_diff($invCodes, $list->pluck('cInvCode')->toArray()); // 数据库中不存在的
  504. foreach ($list as $item) {
  505. // 检查批次管理 (bInvBatch 为 0 表示未开启)
  506. if (empty($item->bInvBatch)) {
  507. $noBatch[] = $item->cInvCode . '(' . $item->cInvName . ')';
  508. }
  509. // 检查保质期管理 (bInvQuality 为 0 表示未开启)
  510. if (empty($item->bInvQuality)) {
  511. $noExpiration[] = $item->cInvCode . '(' . $item->cInvName . ')';
  512. }
  513. }
  514. // 2. 组织提示结果
  515. $messages = [];
  516. if (!empty($noBatch)) {
  517. $messages[] = "【未开启批次管理】: " . implode(', ', $noBatch);
  518. }
  519. if (!empty($noExpiration)) {
  520. $messages[] = "【未开启保质期管理】: " . implode(', ', $noExpiration);
  521. }
  522. if (!empty($notFound)) {
  523. $messages[] = "【档案不存在】: " . implode(', ', $notFound);
  524. }
  525. if (empty($messages)) {
  526. return [true, ''];
  527. }
  528. return [false, implode("\n", $messages)];
  529. } catch (\Throwable $e) {
  530. return [false, $e->getMessage()];
  531. }
  532. }
  533. public function rebuildLLDetails($mainId, $insertData)
  534. {
  535. try {
  536. $db = $this->safeDb();
  537. $accId = '200';
  538. return $db->transaction(function () use ($mainId, $insertData, $db, $accId) {
  539. // 1. 获取并锁定当前的流水号
  540. $identity = $db->table('UFSystem.dbo.UA_Identity')
  541. ->where('cAcc_Id', $accId)
  542. ->where('cVouchType', 'mv')
  543. ->lockForUpdate()
  544. ->first();
  545. if (!$identity) return [false, "未在 UA_Identity 中找到账套 {$accId} 的领料申请单(mv)类型配置"];
  546. // 拿到当前的“起始种子”
  547. $currentSeed = (int)$identity->iChildId;
  548. // 2. 删除旧明细
  549. $db->table('MaterialAppVouchs')->where('ID', $mainId)->delete();
  550. // 3. 分配新 AutoID
  551. $newData = [];
  552. foreach ($insertData as $row) {
  553. $currentSeed++; // 每有一行,种子就往上加 1
  554. $row['AutoID'] = 1000000000 + $currentSeed;
  555. $row['ID'] = $mainId;
  556. $newData[] = $row;
  557. }
  558. if (!empty($newData)) {
  559. $db->table('MaterialAppVouchs')->insert($newData);
  560. }
  561. // 4. 【关键】此时的 $currentSeed 已经是经过循环累加后的最大值了
  562. // 比如初始 2000,循环 3 次,现在就是 2003
  563. $db->statement("
  564. UPDATE UFSystem.dbo.UA_Identity
  565. SET iChildId = ?
  566. WHERE cAcc_Id = ? AND cVouchType = 'mv'
  567. ", [$currentSeed, $accId]);
  568. return [true, ''];
  569. });
  570. } catch (\Throwable $e) {
  571. return [false, "领料申请单更新失败: " . $e->getMessage()];
  572. }
  573. }
  574. public function rebuildDhDetails($mainId, $insertData)
  575. {
  576. try {
  577. $db = $this->safeDb();
  578. $accId = '200';
  579. return $db->transaction(function () use ($mainId, $insertData, $db, $accId) {
  580. // 1. 获取并锁定当前的流水号 (cVouchType 为 PuArrival)
  581. // 使用 lockForUpdate() 确保在事务结束前,U8 软件或其他脚本拿不到这个号
  582. $identity = $db->table('UFSystem.dbo.UA_Identity')
  583. ->where('cAcc_Id', $accId)
  584. ->where('cVouchType', 'PuArrival')
  585. ->lockForUpdate()
  586. ->first();
  587. if (!$identity) {
  588. return [false, "未在 UA_Identity 中找到账套 {$accId} 的采购到货单(PuArrival)配置"];
  589. }
  590. // 获取当前流水种子
  591. $currentSeed = (int)$identity->iChildId;
  592. // 2. 删除旧明细 (PU_ArrivalVouchs)
  593. $db->table('PU_ArrivalVouchs')
  594. ->where('ID', $mainId)
  595. ->delete();
  596. // 3. 准备新数据并分配 Autoid
  597. $newData = [];
  598. foreach ($insertData as $row) {
  599. $currentSeed++; // 种子自增
  600. // 这里的 1000000000 是 U8 常用的年度前缀偏移量,请确保与你账套一致
  601. $row['Autoid'] = 1000000000 + $currentSeed;
  602. $row['ID'] = $mainId;
  603. $newData[] = $row;
  604. }
  605. // 4. 插入新明细
  606. if (!empty($newData)) {
  607. $db->table('PU_ArrivalVouchs')->insert($newData);
  608. }
  609. // 5. 【关键】同步更新系统流水表,确保 U8 软件开单不撞号
  610. $db->statement("
  611. UPDATE UFSystem.dbo.UA_Identity
  612. SET iChildId = ?
  613. WHERE cAcc_Id = ? AND cVouchType = 'PuArrival'
  614. ", [$currentSeed, $accId]);
  615. return [true, ''];
  616. });
  617. } catch (\Throwable $e) {
  618. return [false, "采购到货单明细重建失败: " . $e->getMessage()];
  619. }
  620. }
  621. public function rebuildBjDetails($mainId, $insertData)
  622. {
  623. try {
  624. $db = $this->safeDb();
  625. return $db->transaction(function () use ($mainId, $insertData, $db) {
  626. // 1. 删除旧明细
  627. $db->table('QMINSPECTVOUCHERS')
  628. ->where('ID', $mainId)
  629. ->delete();
  630. $maxAutoid = $db->table('QMINSPECTVOUCHERS')
  631. ->max('Autoid') ?? 0;
  632. // 过滤掉不可插入的字段,特别是 UFTS
  633. foreach ($insertData as $key => $row) {
  634. $maxAutoid++;
  635. // 彻底清理:移除 UFTS 字段,防止插入报错及 JSON 转换报错
  636. unset($row['UFTS']);
  637. $row['Autoid'] = $maxAutoid;
  638. $insertData[$key] = $row;
  639. }
  640. // 4. 批量插入
  641. $db->table('QMINSPECTVOUCHERS')
  642. ->insert($insertData);
  643. return [true, '操作成功'];
  644. });
  645. } catch (\Throwable $e) {
  646. // 这里返回错误信息时,确保不包含二进制内容
  647. return [false, "重组报检单失败: " . $e->getMessage()];
  648. }
  649. }
  650. public function rebuildDhDetails1($mainId, $insertData)
  651. {
  652. try {
  653. $db = $this->safeDb();
  654. return $db->transaction(function () use ($mainId, $insertData,$db) {
  655. // 1. 删除旧明细
  656. $db->table('PU_ArrivalVouchs')
  657. ->where('ID', $mainId)
  658. ->delete();
  659. // 2. 获取新的起始 Autoid
  660. $maxAutoid = $db->table('PU_ArrivalVouchs')
  661. ->max('Autoid') ?? 0;
  662. // 3. 分配新 Autoid 并插入
  663. foreach ($insertData as &$row) {
  664. $maxAutoid++;
  665. $row['Autoid'] = $maxAutoid;
  666. }
  667. $db->table('PU_ArrivalVouchs')
  668. ->insert($insertData);
  669. return [true, ''];
  670. });
  671. } catch (\Throwable $e) {
  672. return [false, "重建明细失败: " . $e->getMessage()];
  673. }
  674. }
  675. public function rebuildLLDetails1($mainId, $insertData)
  676. {
  677. try {
  678. $db = $this->safeDb();
  679. return $db->transaction(function () use ($mainId, $insertData,$db) {
  680. // 1. 删除旧明细 (领料申请单子表)
  681. $db->table('MaterialAppVouchs')
  682. ->where('ID', $mainId)
  683. ->delete();
  684. // 2. 获取新的起始 autoid
  685. $maxAutoid = $db->table('MaterialAppVouchs')
  686. ->max('AutoID') ?? 0;
  687. // 3. 分配新 autoid 并插入
  688. foreach ($insertData as &$row) {
  689. $maxAutoid++;
  690. $row['AutoID'] = $maxAutoid;
  691. }
  692. $db->table('MaterialAppVouchs')
  693. ->insert($insertData);
  694. return [true, ''];
  695. });
  696. } catch (\Throwable $e) {
  697. return [false, "重组领料申请单失败: " . $e->getMessage()];
  698. }
  699. }
  700. }