U8ThirdPartyService.php 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984
  1. <?php
  2. namespace App\Service;
  3. use Illuminate\Support\Facades\Cache;
  4. use Illuminate\Support\Facades\Log;
  5. class U8ThirdPartyService extends Service
  6. {
  7. const one = 1;
  8. const two = 2;
  9. const three = 3;
  10. const four = 4;
  11. const five = 5;
  12. const six = 6;
  13. const seven = 7;
  14. const eight = 8;
  15. const type_all = [
  16. self::one => '采购入库',
  17. self::two => '采购退货',
  18. self::three => '材料出库',
  19. self::four => '销售出库',
  20. self::five => '产成品入库',
  21. self::six => '销售退货',
  22. self::seven => '其他入',
  23. self::eight => '其他出',
  24. ];
  25. public function settleU8Data($data){
  26. if(empty($data['type'])) return [false, 'type类型不能为空'];
  27. if(! isset(self::type_all[$data['type']])) return [false, 'type类型错误'];
  28. $type = $data['type'];
  29. list($status, $msg) = $this->getToken();
  30. if(! $status) return [false, $msg];
  31. $data['u8_data'] = $msg;
  32. if($type == self::one){
  33. list($status, $msg) = $this->purchaseIn($data);
  34. }elseif ($type == self::two){
  35. list($status, $msg) = $this->purchaseReturn($data);
  36. }elseif ($type == self::three){
  37. list($status, $msg) = $this->materialOut($data);
  38. }elseif ($type == self::four){
  39. list($status, $msg) = $this->saleOut($data);
  40. }elseif ($type == self::five){
  41. list($status, $msg) = $this->productIn($data);
  42. }elseif ($type == self::six){
  43. list($status, $msg) = $this->saleReturn($data);
  44. }elseif ($type == self::seven){
  45. list($status, $msg) = $this->otherIn($data);
  46. }elseif ($type == self::eight){
  47. list($status, $msg) = $this->otherOut($data);
  48. }
  49. return [$status, $msg];
  50. }
  51. public function getToken(){
  52. $key = "drb_u8_api" . env('SQLSRV_DATABASE');
  53. $config = config('wms.drb');
  54. // 1. 自检网络通畅
  55. list($bool, $msg) = $this->checkNetworkStatus($config['api_host'], $config['api_port']);
  56. if (!$bool) return [false, "网络连接失败: " . $msg];
  57. $host = $config['api_host'] . ":" . $config['api_port'];
  58. // 2. 尝试从缓存获取
  59. $token = Cache::get($key);
  60. if (! $token) {
  61. // 3. 缓存失效,请求新 Token
  62. $url = $host . "/api/System/GetToken";
  63. $date = date("Y-m-d");
  64. $json = [
  65. "U8DbName" => $config['database'],
  66. "sUserId" => $config['user_id'],
  67. "sPassword" => $config['user_password'],
  68. "LoginDateTime" => $date,
  69. "bPersist" => true
  70. ];
  71. $header = ['Content-Type:application/json'];
  72. // 调用你的 POST 辅助函数
  73. list($status, $result) = $this->post_helper1($url, json_encode($json), $header);
  74. if (!$status) return [false, "用友token获取失败: " . $result];
  75. if (!isset($result['code'])) return [false, '获取用友登录信息失败: 响应格式异常'];
  76. if ($result['code'] != 0) return [false, "U8错误: " . ($result['msg'] ?? '未知错误')];
  77. $token = $result['data']['Token'] ?? "";
  78. if (empty($token)) return [false, "接口返回的 Token 为空"];
  79. // 30分钟 = 1800秒
  80. Cache::put($key, $token, now()->addMinutes(30));
  81. }
  82. return [true, ['host' => $host, 'token' => $token]];
  83. }
  84. public function purchaseIn1($data){
  85. if(empty($data['orderId'])) return [false, '采购到货单ID不能为空'];
  86. if(empty($data['orderNo'])) return [false, '采购到货单单号不能为空'];
  87. if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
  88. //获取采购到货单明细
  89. $service = new U8ThirtyPartyDatabaseServerService();
  90. $details_map = $service->getCGDHDetail1s($data['orderId']);
  91. if(empty($details_map)) return [false, '采购到货单明细不存在'];
  92. $existingLineNums = array_column($data['detail'], 'lineNum');
  93. $maxLineNum = empty($existingLineNums) ? 0 : max($existingLineNums);
  94. $body = [];
  95. $seenLineNums = []; // 用于记录已经处理过的原始行号
  96. foreach ($data['detail'] as $key => $value) {
  97. if(empty($value['lineNum'])) return [false, '行号不能为空'];
  98. if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  99. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  100. if(empty($value['productDate']) || ! $this->validateProductDate($value['productDate'])) return [false, '生产日期为空或格式错误'];
  101. if(empty($value['failureDate']) || ! $this->validateProductDate($value['failureDate'])) return [false, '失效日期为空或格式错误'];
  102. if(empty($value['lot'])) return [false, '批号不能为空'];
  103. if(empty($value['iQuantity']) || ! is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  104. $currentLineNum = $value['lineNum'];
  105. // 2. 判断行号是否重复
  106. if (in_array($currentLineNum, $seenLineNums)) {
  107. $maxLineNum++;
  108. $currentLineNum = $maxLineNum;
  109. $editProp = 'A'; // 或者根据你的需求设为空或其他标志
  110. $o_tmp = $details_map[$value['materialCode']] ?? [];
  111. } else {
  112. $seenLineNums[] = $currentLineNum;
  113. $editProp = 'M';
  114. }
  115. // 3. 组织最终数据
  116. $body[] = [
  117. 'ivouchrowno' =>$currentLineNum,
  118. 'dPDate' => $value['productDate'],
  119. 'dVDate' => $value['failureDate'],
  120. 'cBatch' => $value['lot'],
  121. 'iQuantity' => $value['iQuantity'],
  122. 'editprop' => $editProp,
  123. ];
  124. }
  125. //调用所需
  126. $host = $data['u8_data']['host'];
  127. $token = $data['u8_data']['token'];
  128. //采购到货单弃审
  129. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  130. $url = $host . "/api/PuArrVouch/UnVerify";
  131. $json = [
  132. "VouchId" => $data['orderId'],
  133. ];
  134. $json = json_encode($json);
  135. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  136. if(! $status) return [false, $result];
  137. if(! isset($result['code'])) return [false, '采购到货单弃审失败'];
  138. if($result['code'] != 0) return [false, $result['msg']];
  139. //采购到货单编辑并审核
  140. $url = $host . "/api/PuArrVouch/Update";
  141. $json_final[] = [
  142. "Inum" => "PuArrVouch",
  143. "data" =>[
  144. "iHead" => [
  145. "cCode" => $data['orderNo'],
  146. "IsVerify" => true,
  147. "debug" => true,
  148. ],
  149. "iBody" => $body,
  150. ],
  151. ];
  152. $json = json_encode($json_final);
  153. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  154. if(! $status) return [false, $result];
  155. if(! isset($result['code'])) return [false, '采购到货单编辑并审核失败'];
  156. if($result['code'] != 0) return [false, $result['msg']];
  157. return [true, ''];
  158. }
  159. /**
  160. * 采购到货单拆行/更新处理
  161. */
  162. public function purchaseIn($data)
  163. {
  164. if (empty($data['orderId'])) return [false, '采购到货单ID不能为空'];
  165. if (empty($data['orderNo'])) return [false, '采购到货单单号不能为空'];
  166. if (empty($data['detail'])) return [false, '表体信息detail不能为空'];
  167. // 1. 获取到货单明细
  168. $service = new U8ThirtyPartyDatabaseServerService();
  169. $result = $service->getCGDHDetails($data['orderId']);
  170. if (empty($result)) return [false, '采购到货单明细不存在'];
  171. // 取出第一行作为模板,保留 ID, cCode 等主表关联信息
  172. $templateRow = reset($result);
  173. $mainId = $templateRow['ID'];
  174. // 2. 存货管控校验(批次/保质期)
  175. list($status, $msg) = $service->checkInventoryControl(array_column($data['detail'], 'materialCode'));
  176. if (!$status) return [false, $msg];
  177. $insertData = [];
  178. $rowNo = 1;
  179. foreach ($data['detail'] as $value) {
  180. if (!isset($result[$value['materialCode']])) continue;
  181. $map = $result[$value['materialCode']]; // 获取原始行信息(单价、税率等)
  182. // 计算单价
  183. $oldQty = (float)($map['iQuantity'] ?? 0);
  184. if ($oldQty <= 0) continue;
  185. $unitPrice = (float)$map['iOriSum'] / $oldQty;
  186. $unitMoney = (float)$map['iOriMoney'] / $oldQty;
  187. $unitLocalSum = (float)$map['iSum'] / $oldQty;
  188. $unitLocalMoney = (float)$map['iMoney'] / $oldQty;
  189. $newQty = (float)$value['iQuantity'];
  190. // 组织新行数据
  191. $newRow = $map;
  192. // 清理旧行主键和业务累积状态(关键!)
  193. unset($newRow['Autoid']);
  194. $newRow['fInspectQuantity'] = 0; // 已报检量清零
  195. $newRow['fValidQuantity'] = 0; // 合格量清零
  196. $newRow['iQuantity'] = $newQty;
  197. $newRow['ivouchrowno'] = $rowNo++; // 重新排序行号
  198. // 填充计算后的金额和批次日期
  199. $newRow['iOriMoney'] = round($unitMoney * $newQty, 2);
  200. $newRow['iOriSum'] = round($unitPrice * $newQty, 2);
  201. $newRow['iOriTaxPrice'] = round(($unitPrice - $unitMoney) * $newQty, 2);
  202. $newRow['iMoney'] = round($unitLocalMoney * $newQty, 2);
  203. $newRow['iSum'] = round($unitLocalSum * $newQty, 2);
  204. $newRow['iTaxPrice'] = round(($unitLocalSum - $unitLocalMoney) * $newQty, 2);
  205. $newRow['cBatch'] = $value['lot'];
  206. $newRow['dPDate'] = $this->formatAndValidateDate($value['productDate']);
  207. $newRow['dVDate'] = $this->formatAndValidateDate($value['failureDate']);
  208. $insertData[] = $newRow;
  209. }
  210. // 3. 执行数据库操作:先删后插
  211. list($status, $msg) = $service->rebuildDhDetails($mainId, $insertData);
  212. if (!$status) return [false, $msg];
  213. //查询采购到货单
  214. $order = $service->getCgOrder($data['orderId']);
  215. if(empty($order)) return [false, '采购到货单不存在'];
  216. //生成来料报检单
  217. $inspect = [
  218. "Inum" => "ArrInspect",
  219. "Data" => [
  220. "iHead" => [
  221. // 注意:这里使用的是主表的 ID
  222. "CSOURCEID" => $order['ID'],
  223. "CDEPCODE" => $order['cDepCode'],
  224. "DDATE" => date("Y-m-d"),
  225. "CCHECKTYPECODE" => 'ARR',
  226. ],
  227. "iBody" => []
  228. ]
  229. ];
  230. foreach ($order['details'] as $item) {
  231. $qty = (float)($item['iQuantity'] ?? 0);
  232. if ($qty <= 0) continue;
  233. $inspect["Data"]["iBody"][] = [
  234. "SOURCEAUTOID" => $item['Autoid'],
  235. "ITESTSTYLE" => 0,
  236. "CINVCODE" => $item['cInvCode'],
  237. "FCHANGRATE" => (float)($item['iInvExchRate'] ?? 0),
  238. "FQUANTITY" => $qty,
  239. "CBATCH" => $item['cBatch'],
  240. 'DPRODATE' => $item['dPDate'],
  241. 'DVDATE' => $item['dVDate'],
  242. ];
  243. }
  244. // 4. 封装成数组返回
  245. $final_data = [$inspect];
  246. //调用所需
  247. $host = $data['u8_data']['host'];
  248. $token = $data['u8_data']['token'];
  249. //报检单
  250. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  251. $url = $host . "/api/QmArr/ArrInspectAdd";
  252. $json = json_encode($final_data);
  253. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  254. if(! $status) return [false, $result];
  255. if(! isset($result['code'])) return [false, '报检单生成失败'];
  256. if($result['code'] != 0) return [false, $result['msg']];
  257. return [true, ''];
  258. }
  259. public function materialOut($data)
  260. {
  261. if (empty($data['orderId'])) return [false, '领料申请单ID不能为空'];
  262. if (empty($data['orderNo'])) return [false, '领料申请单单号不能为空'];
  263. if (empty($data['warehouseCode'])) return [false, '仓库不能为空'];
  264. if (empty($data['detail'])) return [false, '表体信息detail不能为空'];
  265. // 1. 获取领料申请单明细
  266. $service = new U8ThirtyPartyDatabaseServerService();
  267. $result = $service->getLlSQDetails($data['orderId']);
  268. if (empty($result)) return [false, '领料申请单明细不存在'];
  269. // 取出第一行作为模板,获取主表 ID
  270. $templateRow = reset($result);
  271. $mainId = $templateRow['ID'];
  272. // 2. 存货管控校验(批次/保质期)
  273. list($status, $msg) = $service->checkInventoryControl(array_column($data['detail'], 'materialCode'));
  274. if (!$status) return [false, $msg];
  275. $insertData = [];
  276. $rowNo = 1;
  277. foreach ($data['detail'] as $value) {
  278. if (!isset($result[$value['materialCode']])) continue;
  279. // 获取原始行(保留生产订单关联 morderdid、仓库 cwhcode 等信息)
  280. $map = $result[$value['materialCode']];
  281. $newQty = (float)$value['iQuantity'];
  282. // 组织新行数据
  283. $newRow = $map;
  284. // 清理主键
  285. unset($newRow['AutoID']);
  286. $newRow['iQuantity'] = $newQty; // 申请数量
  287. $newRow['irowno'] = $rowNo++; // 重新排序行号
  288. $newRow['cBatch'] = $value['lot']; // 批号
  289. // 生产日期与失效日期 (U8 领料申请单通常字段名为 dMadeDate 和 dVDate 或 dmadedate)
  290. $newRow['dMadeDate'] = !empty($value['productDate']) ? $this->formatAndValidateDate($value['productDate']) : null;
  291. $newRow['dVDate'] = !empty($value['failureDate']) ? $this->formatAndValidateDate($value['failureDate']) : null;
  292. $insertData[] = $newRow;
  293. }
  294. // 3. 执行数据库操作:先删后插
  295. list($status, $msg) = $service->rebuildLLDetails($mainId, $insertData);
  296. if (!$status) return [false, $msg];
  297. //查询领料申请单
  298. $order = $service->getLLOrder($data['orderId']);
  299. if(empty($order)) return [false, '领料单不存在'];
  300. //仓库区分出入库类别
  301. if($data['warehouseCode'] == "01"){
  302. $cRdCode = "0201";
  303. }else{
  304. $cRdCode = "0203";
  305. }
  306. $materialOut = [
  307. "Inum" => "MaterialOut",
  308. "Data" => [
  309. "iHead" => [
  310. "IsVerify" => true,
  311. "cWhCode" => $data['warehouseCode'], // 仓库
  312. "cVouchType" => "11",
  313. "cRdCode" => $cRdCode, // 出入库类别
  314. "cDepCode" => $order['cDepCode'], // 领用部门
  315. "cSource" => "领料申请单",
  316. "cBusCode" => $order['cCode'], // 领料申请单单号
  317. "cBusType" => "领料",
  318. "cMemo" => "接口生成",
  319. "dDate" => date("Y-m-d"),
  320. ],
  321. "iBody" => []
  322. ]
  323. ];
  324. foreach ($order['details'] as $index => $item) {
  325. // 这里的数量取值要对应你 getLLOrder 查出来的字段名,通常是 fQty
  326. $qty = $item['iQuantity'];
  327. if ($qty <= 0) continue;
  328. $materialOut["Data"]["iBody"][] = [
  329. "iRowNo" => $index + 1,
  330. "cInvCode" => $item['cInvCode'],
  331. "cBatch" => $item['cBatch'], // 批号
  332. "iQuantity" => $qty, // 实际出库数量
  333. "iNQuantity" => $qty, // 实收数量
  334. "iinvexchrate" => (float)($item['iinvexchrate'] ?? 1),
  335. "iMaIDs" => $item['AutoID'],
  336. "dMadeDate" => $item['dMadeDate'], // 生产日期
  337. "dVDate" => $item['dVDate'], // 失效日期
  338. ];
  339. }
  340. // 封装成数组返回
  341. $final_data = [$materialOut];
  342. //调用所需
  343. $host = $data['u8_data']['host'];
  344. $token = $data['u8_data']['token'];
  345. //材料出库单
  346. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  347. $url = $host . "/api/MaterialOut/Add";
  348. $json = json_encode($final_data);
  349. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  350. if(! $status) return [false, $result];
  351. if(! isset($result['code'])) return [false, '材料出库单生成失败'];
  352. if($result['code'] != 0) return [false, $result['msg']];
  353. return [true, ''];
  354. }
  355. //采购到货单弃审的版本
  356. public function materialOut1($data){
  357. if(empty($data['orderId'])) return [false, '领料申请单ID不能为空'];
  358. if(empty($data['orderNo'])) return [false, '领料申请单单号不能为空'];
  359. if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
  360. $body = [];
  361. foreach ($data['detail'] as $key => $value){
  362. if(empty($value['lineNum'])) return [false, '行号不能为空'];
  363. if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  364. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  365. if(empty($value['productDate']) || ! $this->validateProductDate($value['productDate'])) return [false, '生产日期为空或格式错误'];
  366. if(empty($value['failureDate']) || ! $this->validateProductDate($value['failureDate'])) return [false, '失效日期为空或格式错误'];
  367. if(empty($value['lot'])) return [false, '批号不能为空'];
  368. $body[] = [
  369. 'ivouchrowno' => $value['lineNum'],
  370. 'dPDate' => $value['productDate'],
  371. 'dVDate' => $value['failureDate'],
  372. 'cBatch' => $value['lot'],
  373. 'editprop' => 'M',
  374. ];
  375. }
  376. //调用所需
  377. $host = $data['u8_data']['host'];
  378. $token = $data['u8_data']['token'];
  379. //采购到货单弃审
  380. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  381. $url = $host . "/api/PuArrVouch/UnVerify";
  382. $json = [
  383. "VouchId" => $data['orderId'],
  384. ];
  385. $json = json_encode($json);
  386. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  387. if(! $status) return [false, $result];
  388. if(! isset($result['code'])) return [false, '采购到货单弃审失败'];
  389. if($result['code'] != 0) return [false, $result['msg']];
  390. //采购到货单编辑并审核
  391. $url = $host . "/api/PuArrVouch/Update";
  392. $json_final[] = [
  393. "Inum" => "PuArrVouch",
  394. "data" =>[
  395. "iHead" => [
  396. "cCode" => $data['orderNo'],
  397. "IsVerify" => true,
  398. "debug" => true,
  399. ],
  400. "iBody" => $body,
  401. ],
  402. ];
  403. $json = json_encode($json_final);
  404. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  405. if(! $status) return [false, $result];
  406. if(! isset($result['code'])) return [false, '采购到货单编辑并审核失败'];
  407. if($result['code'] != 0) return [false, $result['msg']];
  408. return [true, ''];
  409. }
  410. public function purchaseReturn($data){
  411. if(empty($data['orderId'])) return [false, '采购退货单ID不能为空'];
  412. // 1. 获取到货单(退货单)及明细
  413. $service = new U8ThirtyPartyDatabaseServerService();
  414. $order = $service->getArrivalVouchById($data['orderId']);
  415. if(empty($order)) return [false, '采购退货单数据不存在'];
  416. // 获取 Token 相关信息
  417. $host = $data['u8_data']['host'] ?? "";
  418. $token = $data['u8_data']['token'] ?? "";
  419. // 2. 组织单一仓库的红字采购入库单
  420. $iBody = [];
  421. foreach ($order['details'] as $index => $item) {
  422. // 强制转为负数,生成红字入库单
  423. $qty = abs((float)$item['iQuantity']) * -1;
  424. $iBody[] = [
  425. "iRowNo" => $index + 1,
  426. "cInvCode" => $item['cInvCode'],
  427. "cBatch" => $item['cBatch'] ?? '',
  428. "iinvexchrate" => (float)($item['iinvexchrate'] ?? 1),
  429. "iQuantity" => $qty, // 负数
  430. "iNQuantity" => $qty, // 负数
  431. "iArrsId" => $item['iArrsId'],
  432. "dMadeDate" => $item['dPDate'],
  433. "dVDate" => $item['dVDate'],
  434. "iTaxRate" => $item['iTaxRate'] ?? 0,
  435. "iTaxUnitPrice"=> $item['iOriTaxCost'] ?? 0,
  436. ];
  437. }
  438. if(empty($iBody)) return [false, '表体明细为空'];
  439. $final_data = [
  440. [
  441. "Inum" => "PurchaseIn",
  442. "Data" => [
  443. "iHead" => [
  444. "IsVerify" => true,
  445. "bIsRedVouch" => true, // 必须为红字
  446. "bCalPrice" => true, // 自动计算金额
  447. "cWhCode" => "01",
  448. "cRdCode" => $order['rd_code'],
  449. "cDepCode" => $order['depart_code'],
  450. "cARVCode" => $order['no'], // 关联来源单号
  451. "cSource" => "采购到货单", // 即使是退货单,参照来源也写这个
  452. "cBusType" => "普通采购",
  453. "cMemo" => "接口生成",
  454. "dDate" => date("Y-m-d"),
  455. "iExchRate"=> 1,
  456. ],
  457. "iBody" => $iBody
  458. ]
  459. ]
  460. ];
  461. // 3. 调用 API
  462. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  463. $url = $host . "/api/PurchaseIn/Add";
  464. $json = json_encode($final_data);
  465. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  466. if(! $status) return [false, $result];
  467. if(! isset($result['code'])) return [false, '红字采购入库单生成失败'];
  468. if($result['code'] != 0) return [false, $result['msg']];
  469. return [true, ''];
  470. }
  471. public function productIn($data){
  472. if(empty($data['orderId'])) return [false, '产品报检单ID不能为空'];
  473. if(empty($data['orderNo'])) return [false, '产品报检单单号不能为空'];
  474. if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
  475. // 1. 获取报检单明细 (建议 getBjOrder 返回以 cInvCode 为 key 的数组)
  476. $service = new U8ThirtyPartyDatabaseServerService();
  477. $result = $service->getBjOrder($data['orderId']);
  478. if (empty($result)) return [false, '报检单明细不存在'];
  479. // 取出第一行作为模板,获取主表 ID
  480. $templateRow = reset($result);
  481. $mainId = $templateRow['ID'];
  482. // 2. 存货管控校验(批次/保质期)
  483. list($status, $msg) = $service->checkInventoryControl(array_column($data['detail'], 'materialCode'));
  484. if (!$status) return [false, $msg];
  485. $insertData = [];
  486. $rowNo = 1;
  487. foreach ($data['detail'] as $value) {
  488. if (!isset($result[$value['materialCode']])) continue;
  489. // 获取原始行(保留来源单据关联,如采购到货单行 AutoID 等)
  490. $map = $result[$value['materialCode']];
  491. $newQty = (float)$value['iQuantity'];
  492. // 组织新行数据
  493. $newRow = $map;
  494. // 清理子表主键
  495. unset($newRow['AUTOID']);
  496. // --- 核心字段修正 (报检单子表字段通常为大写) ---
  497. $newRow['FQUANTITY'] = $newQty; // 报检数量
  498. $newRow['CBATCH'] = $value['lot']; // 批号
  499. // 报检单的日期字段通常是 DPRODATE (生产日期) 和 DVDATE (失效日期)
  500. $newRow['DPRODATE'] = !empty($value['productDate']) ? $this->formatAndValidateDate($value['productDate']) : null;
  501. $newRow['DVDATE'] = !empty($value['failureDate']) ? $this->formatAndValidateDate($value['failureDate']) : null;
  502. $insertData[] = $newRow;
  503. }
  504. // 3. 执行数据库操作:先删后插
  505. list($status, $msg) = $service->rebuildBjDetails($mainId, $insertData);
  506. if (!$status) return [false, $msg];
  507. // $service = new U8ThirtyPartyDatabaseServerService();
  508. // $result = $service->getBjOrder($data['orderId']);
  509. //
  510. // $tmp = $update = $insert = [];
  511. // foreach ($data['detail'] as $key => $value) {
  512. // if(empty($value['lineNum'])) return [false, '行号不能为空'];
  513. // if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  514. // if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  515. // if(empty($value['productDate'])) return [false, '生产日期不能为空'];
  516. // $return = $this->formatAndValidateDate($value['productDate']);
  517. // if(! $return) return [false, '生产日期格式错误'];
  518. // $productDate = $return;
  519. // if(empty($value['failureDate'])) return [false, '失效日期不能为空'];
  520. // $return = $this->formatAndValidateDate($value['failureDate']);
  521. // if(! $return) return [false, '生产日期格式错误'];
  522. // $failureDate= $return;
  523. // if(empty($value['lot'])) return [false, '批号不能为空'];
  524. // if(empty($value['iQuantity']) || ! is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  525. //
  526. // if(isset($result[$value['materialCode']])){
  527. // $map = $result[$value['materialCode']];
  528. // $update_detail = [
  529. // 'CBATCH' => $value['lot'],
  530. // 'FQUANTITY' => $value['iQuantity'],
  531. // 'DPRODATE' => $productDate,
  532. // 'DVDATE' => $failureDate,
  533. // ];
  534. // if(! isset($tmp[$value['materialCode']])){
  535. // $update[$map['AUTOID']] = $update_detail;
  536. // $tmp[$value['materialCode']] = $value['materialCode'];
  537. // }else{
  538. // $map['AUTOID'] = null;
  539. // $insert[] = array_merge($map, $update_detail);
  540. // }
  541. // }
  542. // }
  543. //
  544. // list($status,$msg) = $service->checkInventoryControl(array_values(array_column($data['detail'],'materialCode')));
  545. // if(! $status) return [false, $msg];
  546. //
  547. // list($status, $msg) = $service->updateBjOrder($update, $insert);
  548. // if(! $status) return [false, $msg];
  549. return [true, ''];
  550. }
  551. public function saleOut($data){
  552. if(empty($data['orderId'])) return [false, '销售订单ID不能为空'];
  553. if(empty($data['orderNo'])) return [false, '销售订单号不能为空'];
  554. if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
  555. //获取销售订单信息
  556. $service = new U8ThirtyPartyDatabaseServerService();
  557. $order = $service->getXsOrder($data['orderId']);
  558. if(empty($order)) return [false, '销售订单不存在'];
  559. $detail_map = [];
  560. foreach ($data['detail'] as $key => $value) {
  561. if(empty($value['lineNum'])) return [false, '行号不能为空'];
  562. if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  563. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  564. if(empty($value['productDate'])) return [false, '生产日期不能为空'];
  565. $return = $this->formatAndValidateDate($value['productDate']);
  566. if(! $return) return [false, '生产日期格式错误'];
  567. $productDate = $return;
  568. if(empty($value['failureDate'])) return [false, '失效日期不能为空'];
  569. $return = $this->formatAndValidateDate($value['failureDate']);
  570. if(! $return) return [false, '生产日期格式错误'];
  571. $failureDate= $return;
  572. if(empty($value['lot'])) return [false, '批号不能为空'];
  573. if(empty($value['iQuantity']) || ! is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  574. $detail_map[$value['materialCode']][] = [
  575. 'productDate' => $productDate,
  576. 'failureDate' => $failureDate,
  577. 'lot' => $value['lot'],
  578. 'iQuantity' => $value['iQuantity'],
  579. ];
  580. }
  581. // 2. 组织发货单 (DispatchList) 结构
  582. $tmp = [
  583. "Inum" => "DispatchList",
  584. "Data" => [
  585. "iHead" => [
  586. "cVouchType" => "05",
  587. "cSTCode" => (string)($order['cSTCode'] ?? "02"), // 示例中是02
  588. "cDepCode" => (string)($order['cDepCode'] ?? "01"),
  589. "IsVerify" => true,
  590. "cMemo" => "接口生成",
  591. "dDate" => date("Y-m-d"),
  592. "cSoCode" => (string)$order['cSOCode'],
  593. "ccuscode" => (string)$order['cCusCode'],
  594. "cexch_name" => (string)($order['cexch_name'] ?? "人民币"),
  595. "iexchrate" => (float)($order['iExchRate'] ?? 1.0), // 强制转浮点,去掉引号
  596. "iTaxRate" => (float)($order['iTaxRate'] ?? 13.0), // 强制转浮点
  597. ],
  598. "iBody" => []
  599. ]
  600. ];
  601. // 3. 遍历销售订单明细组织表体
  602. $key = 0;
  603. foreach ($order['details'] as $item) {
  604. $p_t = $detail_map[$item['cInvCode']] ?? [];
  605. if (empty($p_t)) continue;
  606. foreach ($p_t as $value) {
  607. $key++;
  608. $qty = (float)$value['iQuantity'];
  609. $taxRate = (float)($item['iTaxRate'] ?? 13.0);
  610. $taxUnitPrice = (float)$item['iTaxUnitPrice']; // 含税单价 (如 100)
  611. // 价税合计 (iSum)
  612. $iSum = round($qty * $taxUnitPrice, 2);
  613. // 无税金额 (imoney) = 价税合计 / (1 + 税率/100)
  614. $iMoney = round($iSum / (1 + ($taxRate / 100)), 2);
  615. // 税额 (itax)
  616. $iTax = round($iSum - $iMoney, 2);
  617. // 无税单价 (iunitprice)
  618. $iUnitPrice = round($taxUnitPrice / (1 + ($taxRate / 100)), 10);
  619. $tmp["Data"]["iBody"][] = [
  620. "iRowNo" => (int)$key, // 强制转整型
  621. "cInvCode" => (string)$item['cInvCode'],
  622. "iTaxRate" => (float)$taxRate,
  623. "cGroupCode" => (string)($item['cGroupCode'] ?? "01"),
  624. "iGroupType" => "0",
  625. "cUnitID" => (string)($item['cUnitID'] ?? "003"),
  626. "cWhCode" => "06",
  627. "iQuantity" => (float)$qty,
  628. "iNum" => 0, // 辅计量数量
  629. "iSum" => (float)$iSum,
  630. "iInvExchRate" => 1.0, // 换算率,必填
  631. "imoney" => (float)$iMoney,
  632. "itax" => (float)$iTax,
  633. "idiscount" => 0,
  634. "inatmoney" => (float)$iMoney,
  635. "inattax" => (float)$iTax,
  636. "inatdiscount" => 0,
  637. "inatsum" => (float)$iSum,
  638. "itaxunitprice" => (float)$taxUnitPrice,
  639. "iunitprice" => (float)$iUnitPrice,
  640. "inatunitprice" => (float)$iUnitPrice,
  641. "dvDate" => (string)$value['failureDate'], // 示例中只需失效日期
  642. // "dMDate" => (string)$value['productDate'], // todo
  643. "iSOsID" => (int)$item['iSOsID'], // 强制转整型,去掉引号
  644. "cBatch" => (string)$value['lot']
  645. ];
  646. }
  647. }
  648. $final_data = [$tmp];
  649. //调用所需
  650. $host = $data['u8_data']['host'];
  651. $token = $data['u8_data']['token'];
  652. //销售发货单
  653. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  654. $url = $host . "/api/Dispatch/Add";
  655. $json = json_encode($final_data);
  656. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  657. if(! $status) return [false, $result];
  658. if(! isset($result['code'])) return [false, '销售发货单生成失败'];
  659. if($result['code'] != 0) return [false, $result['msg']];
  660. $first = $result['data'][0];
  661. $id = $first['VouchId'];
  662. // $id = 1000000882;
  663. $fhOrder = $service->getFhOrder($id);
  664. if(empty($fhOrder)) return [false, '销售发货单获取失败'];
  665. // 2. 初始化销售出库单结构
  666. $saleOut = [
  667. "Inum" => "SaleOut",
  668. "Data" => [
  669. "iHead" => [
  670. "IsVerify" => true, // 自动审核
  671. "cWhCode" => "06", // 仓库编码
  672. "cRdCode" => "0299", // 出入库类别编码
  673. "cVouchType" => "32", // 单据类型编码
  674. "cMemo" => "接口生成",
  675. "cSource" => "发货单",
  676. "cSTCode" => $fhOrder['cSTCode'], // 销售类型
  677. "DLCode" => $fhOrder['cDLCode'], // 关联的发货单号
  678. "cDepCode" => $fhOrder['cDepCode'], // 部门
  679. "dDate" => date("Y-m-d"), // 出库日期
  680. ],
  681. "iBody" => []
  682. ]
  683. ];
  684. // 3. 遍历发货单明细组织表体
  685. foreach ($fhOrder['details'] as $index => $item) {
  686. // 计算待出库数量 (总发货数 - 已出库数)
  687. $qty = (float)($item['iQuantity'] ?? 0);
  688. $outQty = (float)($item['fOutQuantity'] ?? 0);
  689. $pendingQty = $qty - $outQty;
  690. // 如果该行已全部出库,则跳过(可选逻辑)
  691. if ($pendingQty <= 0) continue;
  692. $saleOut["Data"]["iBody"][] = [
  693. "iRowNo" => $index + 1,
  694. "cInvCode" => $item['cInvCode'] ?? "",
  695. "cPosition" => $item['cPosition'] ?? "", // 货位
  696. "cBatch" => $item['cBatch'] ?? "", // 批号
  697. "iinvexchrate" => (float)($item['iExchRate'] ?? 1), // 换算率
  698. "iQuantity" => $pendingQty, // 本次出库数量
  699. "iNQuantity" => $pendingQty, // 对应实收数量
  700. "iDLsID" => $item['iDLsID'], // 核心:发货单子表ID
  701. "dMadeDate" => $item['dMDate'], // 生产
  702. "dVDate" => $item['dvDate'], // 失效
  703. // "iUnitCost" => (float)($item['iUnitPrice'] ?? 0), // 无税单价
  704. // "iPrice" => round((float)($item['iUnitPrice'] ?? 0) * $pendingQty, 2), // 无税金额
  705. ];
  706. }
  707. // 4. 封装成数组
  708. $final_data = [$saleOut];
  709. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  710. $url = $host . "/api/SaleOut/Add";
  711. $json = json_encode($final_data);
  712. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  713. if(! $status) return [false, $result];
  714. if(! isset($result['code'])) return [false, '销售出库单生成失败'];
  715. if($result['code'] != 0) return [false, $result['msg']];
  716. return [true, ''];
  717. }
  718. public function saleOut1($data){
  719. if(empty($data['orderId'])) return [false, '销售订单ID不能为空'];
  720. if(empty($data['orderNo'])) return [false, '销售订单号不能为空'];
  721. if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
  722. //获取销售订单信息
  723. $service = new U8ThirtyPartyDatabaseServerService();
  724. $order = $service->getXsOrder($data['orderId']);
  725. if(empty($order)) return [false, '销售订单不存在'];
  726. $orderItemMap = [];
  727. foreach ($order['details'] as $detail) {
  728. // 这里的 iTaxUnitPrice 是订单里的含税单价
  729. $orderItemMap[$detail['cInvCode']] = [
  730. 'iTaxUnitPrice' => $detail['iTaxUnitPrice'] ?? 0,
  731. 'iTaxRate' => $detail['iTaxRate'] ?? 13,
  732. 'iUnitPrice' => $detail['iUnitPrice'] ?? 0,
  733. ];
  734. }
  735. $detail_map = [];
  736. foreach ($data['detail'] as $key => $value) {
  737. if(empty($value['lineNum'])) return [false, '行号不能为空'];
  738. if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  739. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  740. if(empty($value['productDate'])) return [false, '生产日期不能为空'];
  741. $return = $this->formatAndValidateDate($value['productDate']);
  742. if(! $return) return [false, '生产日期格式错误'];
  743. $productDate = $return;
  744. if(empty($value['failureDate'])) return [false, '失效日期不能为空'];
  745. $return = $this->formatAndValidateDate($value['failureDate']);
  746. if(! $return) return [false, '生产日期格式错误'];
  747. $failureDate= $return;
  748. if(empty($value['lot'])) return [false, '批号不能为空'];
  749. if(empty($value['iQuantity']) || ! is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  750. $detail_map[$value['materialCode']][] = [
  751. 'productDate' => $productDate,
  752. 'failureDate' => $failureDate,
  753. 'lot' => $value['lot'],
  754. 'iQuantity' => $value['iQuantity'],
  755. ];
  756. }
  757. // 2. 组织发货单 (DispatchList) 结构
  758. $tmp = [
  759. "Inum" => "DispatchList",
  760. "Data" => [
  761. "iHead" => [
  762. "cVouchType" => "05",
  763. "cSTCode" => $order['cSTCode'] ?? "01",
  764. "cDepCode" => $order['cDepCode'] ?? "01",
  765. "IsVerify" => true,
  766. "cMemo" => "接口生成",
  767. "dDate" => date("Y-m-d"),
  768. "bCalPrice" => false,
  769. "PriceCalKey" => "iSum",
  770. "iExchRate" => $order['iExchRate'] ?? 1,
  771. "cexch_name" => $order['cexch_name'] ?? "人民币",
  772. "cCusCode" => $order['cCusCode'],
  773. "iTaxRate" => $order['iTaxRate'] ?? 13,
  774. "cinvoicecompany" => $order['cCusCode'],
  775. ],
  776. "iBody" => []
  777. ]
  778. ];
  779. // 3. 组织表体
  780. $rowKey = 0;
  781. foreach ($detail_map as $invCode => $items) {
  782. // 获取该存货在订单里的价格信息
  783. $priceInfo = $orderItemMap[$invCode];
  784. foreach ($items as $value) {
  785. $rowKey++;
  786. $quantity = $value['iQuantity'];
  787. $unitPrice = $priceInfo['iTaxUnitPrice']; // 直接使用订单里的含税单价
  788. // 计算价税合计:数量 * 含税单价
  789. $sum = round($quantity * $unitPrice, 2);
  790. $tmp["Data"]["iBody"][] = [
  791. "iRowNo" => $rowKey,
  792. "cInvCode" => $invCode,
  793. "iTaxRate" => $priceInfo['iTaxRate'],
  794. "cWhCode" => "06",
  795. "iQuantity" => $quantity,
  796. "iSum" => $sum, // 总金额
  797. "iMoney" => $sum,
  798. "cBatch" => $value['lot'],
  799. "dMDate" => $value['productDate'],
  800. "dvDate" => $value['failureDate'],
  801. "iTaxUnitPrice" => $unitPrice, // 含税单价
  802. "iUnitPrice" => $priceInfo['iUnitPrice'], // 原订单无税单价
  803. ];
  804. }
  805. }
  806. // 4. 封装成数组返回
  807. $final_data = [$tmp];
  808. //调用所需
  809. $host = $data['u8_data']['host'];
  810. $token = $data['u8_data']['token'];
  811. //销售发货单
  812. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  813. $url = $host . "/api/Dispatch/Add";
  814. $json = json_encode($final_data);
  815. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  816. if(! $status) return [false, $result];
  817. if(! isset($result['code'])) return [false, '销售发货单生成失败'];
  818. if($result['code'] != 0) return [false, $result['msg']];
  819. $first = $result['data'][0];
  820. $id = $first['VouchId'];
  821. // $id = 1000000882;
  822. $fhOrder = $service->getFhOrder($id);
  823. if(empty($fhOrder)) return [false, '销售发货单获取失败'];
  824. // 2. 初始化销售出库单结构
  825. $saleOut = [
  826. "Inum" => "SaleOut",
  827. "Data" => [
  828. "iHead" => [
  829. "IsVerify" => true, // 自动审核
  830. "cWhCode" => "06", // 仓库编码
  831. "cRdCode" => "0299", // 出入库类别编码
  832. "cVouchType" => "32", // 单据类型编码
  833. "cMemo" => "接口生成",
  834. "cSource" => "发货单",
  835. "cSTCode" => $fhOrder['cSTCode'], // 销售类型
  836. "DLCode" => $fhOrder['cDLCode'], // 关联的发货单号
  837. "cDepCode" => $fhOrder['cDepCode'], // 部门
  838. "dDate" => date("Y-m-d"), // 出库日期
  839. ],
  840. "iBody" => []
  841. ]
  842. ];
  843. // 3. 遍历发货单明细组织表体
  844. foreach ($fhOrder['details'] as $index => $item) {
  845. // 计算待出库数量 (总发货数 - 已出库数)
  846. $qty = (float)($item['iQuantity'] ?? 0);
  847. $outQty = (float)($item['fOutQuantity'] ?? 0);
  848. $pendingQty = $qty - $outQty;
  849. // 如果该行已全部出库,则跳过(可选逻辑)
  850. if ($pendingQty <= 0) continue;
  851. $saleOut["Data"]["iBody"][] = [
  852. "iRowNo" => $index + 1,
  853. "cInvCode" => $item['cInvCode'] ?? "",
  854. "cPosition" => $item['cPosition'] ?? "", // 货位
  855. "cBatch" => $item['cBatch'] ?? "", // 批号
  856. "iinvexchrate" => (float)($item['iExchRate'] ?? 1), // 换算率
  857. "iQuantity" => $pendingQty, // 本次出库数量
  858. "iNQuantity" => $pendingQty, // 对应实收数量
  859. "iDLsID" => $item['iDLsID'], // 核心:发货单子表ID
  860. "dMadeDate" => $item['dMDate'], // 生产
  861. "dVDate" => $item['dvDate'], // 失效
  862. "iUnitCost" => (float)($item['iUnitPrice'] ?? 0), // 无税单价
  863. "iPrice" => round((float)($item['iUnitPrice'] ?? 0) * $pendingQty, 2), // 无税金额
  864. ];
  865. }
  866. // 4. 封装成数组
  867. $final_data = [$saleOut];
  868. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  869. $url = $host . "/api/SaleOut/Add";
  870. $json = json_encode($final_data);
  871. list($status, $result) = $this->post_helper1($url,$json, $header, 30);
  872. if(! $status) return [false, $result];
  873. if(! isset($result['code'])) return [false, '销售出库单生成失败'];
  874. if($result['code'] != 0) return [false, $result['msg']];
  875. return [true, ''];
  876. }
  877. public function otherIn($data) {
  878. // 1. 基础校验
  879. if (empty($data['warehouseCode'])) return [false, '仓库编码不能为空'];
  880. if (empty($data['detail'])) return [false, '明细数据不能为空'];
  881. $iBody = [];
  882. foreach ($data['detail'] as $key => $value) {
  883. if(empty($value['lineNum'])) return [false, '行号不能为空'];
  884. if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
  885. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  886. if(empty($value['productDate'])) return [false, '生产日期不能为空'];
  887. $return = $this->formatAndValidateDate($value['productDate']);
  888. if(! $return) return [false, '生产日期格式错误'];
  889. $productDate = $return;
  890. if(empty($value['failureDate'])) return [false, '失效日期不能为空'];
  891. $return = $this->formatAndValidateDate($value['failureDate']);
  892. if(! $return) return [false, '生产日期格式错误'];
  893. $failureDate= $return;
  894. if(empty($value['lot'])) return [false, '批号不能为空'];
  895. if(empty($value['iQuantity']) || ! is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  896. $iBody[] = [
  897. "iRowNo" => $key + 1,
  898. "cInvCode" => $value['materialCode'], // 存货编码
  899. "cBatch" => $value['lot'] ?? "", // 批号
  900. "iQuantity" => $value['iQuantity'],
  901. "iinvexchrate" => 0,
  902. "iNum" => 0,
  903. "dMadeDate" => $productDate, // 生产日期
  904. "dVDate" => $failureDate, // 失效日期
  905. ];
  906. }
  907. // 2. 组织表头 (iHead)
  908. $iHead = [
  909. "cWhCode" => $data['warehouseCode'], // 仓库编码
  910. "cRdCode" => "0109", // 收发类别 其他入
  911. "cDepCode" => "", // 部门
  912. "IsVerify" => ! isset($data['IsVerify']) ?? false, // 是否自动审核
  913. "cMemo" => "接口生成",
  914. "dDate" => $data['dDate'] ?? date("Y-m-d"), // 单据日期
  915. ];
  916. // 4. 封装成要求的数组结构
  917. $final_data = [
  918. [
  919. "Inum" => "OtherIn",
  920. "Data" => [
  921. "iHead" => $iHead,
  922. "iBody" => $iBody
  923. ]
  924. ]
  925. ];
  926. // 5. 准备发送请求
  927. $host = $data['u8_data']['host'] ?? "";
  928. $token = $data['u8_data']['token'] ?? "";
  929. $url = $host . "/api/OtherIn/Add";
  930. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  931. // 执行提交 (假设你已经定义了 post_helper1)
  932. $json = json_encode($final_data);
  933. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  934. if(! $status) return [false, $result];
  935. if(! isset($result['code'])) return [false, '其他入库单生成失败'];
  936. if($result['code'] != 0) return [false, $result['msg']];
  937. return [true, $result];
  938. }
  939. public function otherOut($data) {
  940. // 1. 基础校验
  941. if (empty($data['warehouseCode'])) return [false, '仓库编码不能为空'];
  942. if (empty($data['detail'])) return [false, '明细数据不能为空'];
  943. $iBody = [];
  944. foreach ($data['detail'] as $key => $value) {
  945. if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
  946. // 格式化生产日期
  947. $productDate = null;
  948. if(!empty($value['productDate'])){
  949. $productDate = $this->formatAndValidateDate($value['productDate']);
  950. if(!$productDate) return [false, '生产日期格式错误'];
  951. }
  952. // 格式化失效日期
  953. $failureDate = null;
  954. if(!empty($value['failureDate'])){
  955. $failureDate = $this->formatAndValidateDate($value['failureDate']);
  956. if(!$failureDate) return [false, '失效日期格式错误'];
  957. }
  958. if(empty($value['iQuantity']) || !is_numeric($value['iQuantity'])) return [false, '存货数量错误'];
  959. $iBody[] = [
  960. "iRowNo" => $key + 1,
  961. "cInvCode" => $value['materialCode'], // 存货编码
  962. "cBatch" => $value['lot'] ?? "", // 批号
  963. "iQuantity" => $value['iQuantity'],
  964. "iinvexchrate" => 0,
  965. "dMadeDate" => $productDate, // 生产日期
  966. "dVDate" => $failureDate, // 失效日期
  967. ];
  968. }
  969. // 2. 组织表头 (iHead)
  970. $iHead = [
  971. "cWhCode" => $data['warehouseCode'], // 仓库编码
  972. "cRdCode" => "0209", // 收发类别:其他出库
  973. "cDepCode" => "", // 部门
  974. "IsVerify" => isset($data['IsVerify']) ?? false, // 是否自动审核
  975. "cMemo" => "接口生成",
  976. "dDate" => $data['dDate'] ?? date("Y-m-d"), // 单据日期
  977. ];
  978. // 4. 封装成要求的数组结构 - Inum 改为 OtherOut
  979. $final_data = [
  980. [
  981. "Inum" => "OtherOut",
  982. "Data" => [
  983. "iHead" => $iHead,
  984. "iBody" => $iBody
  985. ]
  986. ]
  987. ];
  988. // 5. 准备发送请求
  989. $host = $data['u8_data']['host'] ?? "";
  990. $token = $data['u8_data']['token'] ?? "";
  991. // 地址改为 OtherOut
  992. $url = $host . "/api/OtherOut/Add";
  993. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  994. // 执行提交
  995. $json = json_encode($final_data);
  996. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  997. if(! $status) return [false, "网络请求失败: " . $result];
  998. if(! isset($result['code'])) return [false, '其他出库单生成失败'];
  999. if($result['code'] != 0) return [false, "U8错误: " . $result['msg']];
  1000. return [true, $result];
  1001. }
  1002. public function saleReturn1($data) {
  1003. if(empty($data['orderId'])) return [false, '销售退货单ID不能为空'];
  1004. // 1. 获取销售退货单/订单信息
  1005. $service = new U8ThirtyPartyDatabaseServerService();
  1006. $order = $service->getXsThOrder($data['orderId']);
  1007. if(empty($order)) return [false, '销售退货单信息不存在'];
  1008. $iBody = [];
  1009. foreach ($order['details'] as $key => $value) {
  1010. // 确保数量为负数(红字单据要求)
  1011. $qty = (float)($value['iQuantity'] ?? 0);
  1012. $redQty = $qty > 0 ? -$qty : $qty;
  1013. $iBody[] = [
  1014. "iRowNo" => $key + 1,
  1015. "cInvCode" => $value['cInvCode'], // 存货编码
  1016. "cPosition" => $value['cPosition'] ?? "", // 货位
  1017. "cBatch" => $value['cBatch'] ?? "", // 批号
  1018. "iinvexchrate" => (float)($value['iinvexchrate'] ?? 1),
  1019. "iQuantity" => $redQty, // 红字数量
  1020. "iNQuantity" => $redQty, // 实退数量
  1021. "iNum" => (float)($value['iNum'] ?? 0),
  1022. "iNNum" => (float)($value['iNum'] ?? 0),
  1023. // 核心关联:如果是参照发货单退货,需要 iDLsID (发货单子表Autoid)
  1024. "iDLsID" => $value['iDLsID'] ?? 0,
  1025. "dMadeDate" => $value['dMDate'], // 生产日期
  1026. "dVDate" => $value['dvDate'], // 失效日期
  1027. ];
  1028. }
  1029. // 2. 组织表头 (iHead)
  1030. $iHead = [
  1031. "IsVerify" => $data['IsVerify'] ?? false, // 是否自动审核
  1032. "bIsRedVouch" => true, // 核心:必须为 true (红字单据)
  1033. "cWhCode" => $order['cWhCode'], // 仓库
  1034. "cRdCode" => $order['cRdCode'], // 收发类别(如:销售出库/退货)
  1035. "cMemo" => "接口生成",
  1036. "cSource" => "发货单", // 来源类型
  1037. "cSTCode" => $order['cSTCode'], // 销售类型
  1038. "DLCode" => $order['cDLCode'], // 关联的发货单号
  1039. "dDate" => date("Y-m-d"), // 单据日期
  1040. ];
  1041. // 3. 封装成 SaleOut 结构
  1042. $final_data = [
  1043. [
  1044. "Inum" => "SaleOut",
  1045. "Data" => [
  1046. "iHead" => $iHead,
  1047. "iBody" => $iBody
  1048. ]
  1049. ]
  1050. ];
  1051. // 4. 发送请求
  1052. $host = $data['u8_data']['host'] ?? "";
  1053. $token = $data['u8_data']['token'] ?? "";
  1054. $url = $host . "/api/SaleOut/Add";
  1055. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1056. $json = json_encode($final_data);
  1057. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1058. if(! $status) return [false, $result];
  1059. if(! isset($result['code'])) return [false, '红字销售出库单生成失败'];
  1060. if($result['code'] != 0) return [false, $result['msg']];
  1061. return [true, $result];
  1062. }
  1063. public function saleReturn($data) {
  1064. if(empty($data['orderId'])) return [false, '销售退货单ID不能为空'];
  1065. // 1. 获取销售退货单信息 (DispatchList & DispatchLists)
  1066. $service = new U8ThirtyPartyDatabaseServerService();
  1067. $order = $service->getXsThOrder($data['orderId']);
  1068. if(empty($order)) return [false, '销售退货单信息不存在'];
  1069. // --- 核心修改:按仓库分组 ---
  1070. $groupDetails = [];
  1071. foreach ($order['details'] as $item) {
  1072. $whCode = $item['cWhCode'] ?? $order['cWhCode']; // 优先取明细里的仓库,没有则取主表的
  1073. $groupDetails[$whCode][] = $item;
  1074. }
  1075. $final_data = [];
  1076. // 2. 遍历每个仓库分组,生成各自的单据
  1077. foreach ($groupDetails as $whCode => $details) {
  1078. $iBody = [];
  1079. foreach ($details as $index => $value) {
  1080. $qty = (float)($value['iQuantity'] ?? 0);
  1081. $redQty = $qty > 0 ? -$qty : $qty; // 强制红字
  1082. $iBody[] = [
  1083. "iRowNo" => $index + 1,
  1084. "cInvCode" => $value['cInvCode'],
  1085. "cPosition" => $value['cPosition'] ?? "",
  1086. "cBatch" => $value['cBatch'] ?? "",
  1087. "iinvexchrate" => (float)($value['iinvexchrate'] ?? 1),
  1088. "iQuantity" => $redQty,
  1089. "iNQuantity" => $redQty,
  1090. "iNum" => (float)($value['iNum'] ?? 0),
  1091. "iNNum" => (float)($value['iNum'] ?? 0),
  1092. "iDLsID" => $value['iDLsID'] ?? 0, // 关键:关联发货单行ID
  1093. "dMadeDate" => $value['dMDate'] ?? null,
  1094. "dVDate" => $value['dvDate'] ?? null,
  1095. ];
  1096. }
  1097. // 组织该单据的表头
  1098. $iHead = [
  1099. "IsVerify" => true,
  1100. "bIsRedVouch" => true,
  1101. "cWhCode" => $whCode, // 当前分组的仓库
  1102. "cRdCode" => "0204",
  1103. "cMemo" => "接口生成",
  1104. "cSource" => "发货单",
  1105. "cSTCode" => $order['cSTCode'],
  1106. "DLCode" => $order['cDLCode'] ?? "",
  1107. "dDate" => date("Y-m-d"),
  1108. ];
  1109. // 放入大数组
  1110. $final_data[] = [
  1111. "Inum" => "SaleOut",
  1112. "Data" => [
  1113. "iHead" => $iHead,
  1114. "iBody" => $iBody
  1115. ]
  1116. ];
  1117. }
  1118. // 3. 发送请求
  1119. $host = $data['u8_data']['host'] ?? "";
  1120. $token = $data['u8_data']['token'] ?? "";
  1121. $url = $host . "/api/SaleOut/Add";
  1122. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1123. $json = json_encode($final_data);
  1124. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1125. if(!$status) return [false, $result];
  1126. if(!isset($result['code'])) return [false, '红字销售出库单生成失败'];
  1127. if($result['code'] != 0) return [false, $result['msg']];
  1128. return [true, $result];
  1129. }
  1130. //检验单生成产成品入库单
  1131. public function productInByZj($data){
  1132. $record = $data['record'];
  1133. $payload = $data['payload'];
  1134. $id = $record['u8_id'];
  1135. $type = $data['type'];
  1136. // 数量逻辑判断
  1137. $num = ($type == 1) ? (float)$payload['hg_quantity'] : (float)$payload['rb_quantity'];
  1138. // 获取单据 产品检验单
  1139. $service = new U8ThirtyPartyDatabaseServerService();
  1140. $order = $service->getJyOrder($id);
  1141. if(empty($order)) return [false, '产品检验单不存在'];
  1142. // 获取单据 生产订单子表 (用于获取生产订单累计数量 iNQuantity)
  1143. $product_detail = $service->getScDetails($order['SOURCEID']);
  1144. if(empty($product_detail)) return [false, '生产订单子表数据不存在'];
  1145. // u8 token 获取
  1146. list($status, $msg) = $this->getToken();
  1147. if(! $status) return [false, $msg];
  1148. // 组织 ProductIn 结构
  1149. $tmp = [
  1150. "Inum" => "ProductIn",
  1151. "Data" => [
  1152. "iHead" => [
  1153. "cWhCode" => (string)($order['cWhCode'] ?? "06"), // 产成品库
  1154. "cRdCode" => "0103", // 入库类别
  1155. "cDepCode" => $order['CINSPECTDEPCODE'],
  1156. "cMemo" => "接口生成",
  1157. "cSource" => "产品检验单",
  1158. "cBusType" => "成品入库",
  1159. "cMPoCode" => (string)$order['SOURCECODE'], // 生产订单号
  1160. "cChkCode" => (string)$order['CCHECKCODE'], // 检验单号
  1161. "dDate" => date("Y-m-d"),
  1162. "IsVerify" => true,
  1163. ],
  1164. "iBody" => []
  1165. ]
  1166. ];
  1167. // 组织表体
  1168. $tmp["Data"]["iBody"][] = [
  1169. "iRowNo" => 1,
  1170. "cInvCode" => (string)($order['CINVCODE'] ?? ''),
  1171. "iinvexchrate" => (float)($order['FCHANGRATE'] ?? 0), // 换算率转为数字
  1172. "iQuantity" => (float)$num, // 本次入库数量
  1173. "iNQuantity" => (float)($product_detail['Qty'] ?? 0), // 生产订单总数量
  1174. "iNum" => 0, // 辅计数量
  1175. "iNNum" => 0, // 生产订单辅计总数量
  1176. "iMPoIds" => (int)($order['SOURCEAUTOID'] ?? 0), // 生产订单子表ID,必须是整型
  1177. "cBatch" => (string)($order['CBATCH'] ?? ''),
  1178. "dMadeDate" => (string)($order['DPRODATE'] ?? ''),
  1179. "dVDate" => (string)($order['DVDATE'] ?? ''),
  1180. ];
  1181. $final_data = [$tmp];
  1182. // 调用参数
  1183. $host = $msg['host'] ?? "";
  1184. $token = $msg['token'] ?? "";
  1185. $url = $host . "/api/ProductIn/Add";
  1186. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1187. $json = json_encode($final_data);
  1188. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1189. if(! $status) return [false, $result];
  1190. if(! isset($result['code'])) return [false, '产成品入库单生成失败'];
  1191. if($result['code'] != 0) return [false, $result['msg']];
  1192. return [true, ''];
  1193. }
  1194. public function productInByZj2($data){
  1195. $record = $data['record'];
  1196. $payload = $data['payload'];
  1197. $id = $record['u8_id'];
  1198. $type = $data['type'];
  1199. if($type == 1){
  1200. $num = $payload['hg_quantity'];
  1201. }else{
  1202. $num = $payload['rb_quantity'];
  1203. }
  1204. //获取单据 产品检验单
  1205. $service = new U8ThirtyPartyDatabaseServerService();
  1206. $order = $service->getJyOrder($id);
  1207. if(empty($order)) return [false, '产品检验单不存在'];
  1208. //获取单据 生产订单子表
  1209. $product_detail = $service->getScDetails($order['SOURCEID']);
  1210. if(empty($product_detail)) return [false, '生产订单子表数据不存在'];
  1211. //u8 token
  1212. list($status, $msg) = $this->getToken();
  1213. if(! $status) return [false, $msg];
  1214. $tmp = [
  1215. "Inum" => "ProductIn",
  1216. "Data" => [
  1217. "iHead" => [
  1218. "IsVerify" => true,
  1219. "cWhCode" => "06", // 产成品库
  1220. "cRdCode" => '0103', // 入库类别 产成品入库
  1221. "cDepCode" => $order['CINSPECTDEPCODE'] ?? '', // 部门
  1222. "cMemo" => "接口生成",
  1223. "cSource" => "产品检验单",
  1224. "cBusType" => "成品入库",
  1225. "cMPoCode" => $order['SOURCECODE'], // 生产订单号
  1226. "cChkCode" => $order['CCHECKCODE'], // 检验单号
  1227. "dDate" => date("Y-m-d"),
  1228. "bIsRedVouch" => false, // 如果是红字入库请设为true
  1229. "bCalPrice" => true, // 是否由接口计算金额
  1230. ],
  1231. "iBody" => []
  1232. ]
  1233. ];
  1234. //一个检验单只有一行
  1235. $tmp["Data"]["iBody"][] = [
  1236. "iRowNo" => 1,
  1237. "cInvCode" => $order['cInvCode'] ?? '',
  1238. "cAssUnit" => $order['CUNITID'] ?? '',
  1239. "cPosition" => $order['cPosition'] ?? '',
  1240. "cBatch" => $order['cBatch'] ?? '',
  1241. "iinvexchrate" => $order['FCHANGRATE'] ?? 0,
  1242. "iQuantity" => $num, // 数量
  1243. "iNQuantity" => $product_detail['Qty'], // 生产订单产品数量
  1244. "iMPoIds" => $order['SOURCEAUTOID'] ?? 0, // 生产订单子表ID
  1245. "dMadeDate" => $order['DPRODATE'] ?? '', // 生产日期
  1246. "iMassDate" => $order['IMASSDATE'], // 保质期
  1247. "cMassUnit" => $order['CMASSUNIT'], // 保质期单位
  1248. "dVDate" => $order['DVDATE'] ?? '', // 失效日期
  1249. ];
  1250. $final_data = [$tmp];
  1251. //调用所需
  1252. $host = $msg['host'] ?? "";
  1253. $token = $msg['token'] ?? "";
  1254. //产成品入库单生成
  1255. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  1256. $url = $host . "/api/ProductIn/Add";
  1257. $json = json_encode($final_data);
  1258. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1259. if(! $status) return [false, $result];
  1260. if(! isset($result['code'])) return [false, '产成品入库单生成并审核失败'];
  1261. if($result['code'] != 0) return [false, $result['msg']];
  1262. return [true, ''];
  1263. }
  1264. public function productInByZj1($data){
  1265. $record = $data['record'];
  1266. $payload = $data['payload'];
  1267. $id = $record['u8_id'];
  1268. $type = $data['type'];
  1269. // 确定入库数量
  1270. $num = ($type == 1) ? $payload['hg_quantity'] : $payload['rb_quantity'];
  1271. // 1. 获取检验单数据(用于提取物料、批次、仓库等基本信息,即便不参照也需要这些数据)
  1272. $service = new U8ThirtyPartyDatabaseServerService();
  1273. $order = $service->getJyOrder($id);
  1274. if(empty($order)) return [false, '产品检验单不存在'];
  1275. // 2. 获取 U8 Token
  1276. list($status, $msg) = $this->getToken();
  1277. if(! $status) return [false, $msg];
  1278. // 3. 组织产成品入库单 (不参照模式)
  1279. $tmp = [
  1280. "Inum" => "ProductIn",
  1281. "Data" => [
  1282. "iHead" => [
  1283. "IsVerify" => true, // 自动审核
  1284. "cWhCode" => $order['CWHCODE'] ?? "06", // 产成品库
  1285. "cRdCode" => '0103', // 产成品入库类别编码 (根据U8实际修改,通常为12)
  1286. "cDepCode" => $order['CINSPECTDEPCODE'],
  1287. "cMemo" => "接口生成",
  1288. "cSource" => "库存", // 关键:设为库存即为手动入库
  1289. "cBusType" => "成品入库",
  1290. "dDate" => date("Y-m-d"),
  1291. "bIsRedVouch" => false,
  1292. "bCalPrice" => true,
  1293. ],
  1294. "iBody" => [
  1295. [
  1296. "iRowNo" => 1,
  1297. "cInvCode" => $order['CINVCODE'] ?? '',
  1298. "cBatch" => $order['CBATCH'] ?? '',
  1299. "iinvexchrate" => $order['FCHANGRATE'] ?? 0,
  1300. "iQuantity" => $num, // 入库数量
  1301. "iNQuantity" => $num, // 保持一致
  1302. "dMadeDate" => $order['DPRODATE'] ?? '', // 生产日期
  1303. "dVDate" => $order['DVDATE'] ?? '', // 失效日期
  1304. ]
  1305. ]
  1306. ]
  1307. ];
  1308. $final_data = [$tmp];
  1309. // 4. 调用接口
  1310. $host = $msg['host'] ?? "";
  1311. $token = $msg['token'] ?? "";
  1312. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1313. $url = $host . "/api/ProductIn/Add";
  1314. $json = json_encode($final_data);
  1315. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1316. if(! $status) return [false, $result];
  1317. if(! isset($result['code'])) return [false, '产成品入库单生成失败'];
  1318. if($result['code'] != 0) return [false, $result['msg']];
  1319. return [true, ''];
  1320. }
  1321. //检验单生成其他入库单
  1322. public function otherInByZj($data){
  1323. $record = $data['record'];
  1324. $payload = $data['payload'];
  1325. $id = $record['u8_id'];
  1326. $num = $payload['hg_not_quantity'];
  1327. //获取单据 检验单
  1328. $service = new U8ThirtyPartyDatabaseServerService();
  1329. $order = $service->getJyOrder($id);
  1330. if(empty($order)) return [false, '检验单不存在'];
  1331. //u8 token
  1332. list($status, $msg) = $this->getToken();
  1333. if(! $status) return [false, $msg];
  1334. $tmp = [
  1335. "Inum" => "OtherIn",
  1336. "Data" => [
  1337. "iHead" => [
  1338. "IsVerify" => true,
  1339. "cWhCode" => "53", // 成品不良品库
  1340. "cRdCode" => '0109', // 入库类别 其他入库
  1341. "cDepCode" => '', // 部门
  1342. "cMemo" => "接口生成",
  1343. "dDate" => date("Y-m-d"),
  1344. ],
  1345. "iBody" => []
  1346. ]
  1347. ];
  1348. //一个检验单只有一行
  1349. $tmp["Data"]["iBody"][] = [
  1350. "iRowNo" => 1,
  1351. "cInvCode" => $order['CINVCODE'] ?? '',
  1352. "cAssUnit" => $order['CUNITID'] ?? '',
  1353. "cPosition" => $order['cPosition'] ?? '',
  1354. "cBatch" => $order['CBATCH'] ?? '',
  1355. "iinvexchrate" => $order['FCHANGRATE'] ?? 0,
  1356. "iQuantity" => $num, // 数量
  1357. "dMadeDate" => $order['DPRODATE'] ?? '', // 生产日期
  1358. "dVDate" => $order['DVDATE'] ?? '',
  1359. ];
  1360. $final_data = [$tmp];
  1361. //调用所需
  1362. $host = $msg['host'] ?? "";
  1363. $token = $msg['token'] ?? "";
  1364. //产成品入库单生成
  1365. $header = ["Authorization: {$token}",'Content-Type:application/json'];
  1366. $url = $host . "/api/OtherIn/Add";
  1367. $json = json_encode($final_data);
  1368. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1369. if(! $status) return [false, $result];
  1370. if(! isset($result['code'])) return [false, '其他入库单生成并审核失败'];
  1371. if($result['code'] != 0) return [false, $result['msg']];
  1372. return [true, ''];
  1373. }
  1374. //检验单生产采购入库单
  1375. public function purchaseInByZj2($data)
  1376. {
  1377. $record = $data['record'];
  1378. $payload = $data['payload'];
  1379. $id = $record['u8_id']; // 这里的 ID 应该是检验单主表的 ID
  1380. $type = $data['type'];
  1381. if($type == 1){
  1382. $num = $payload['hg_quantity'];
  1383. }else{
  1384. $num = $payload['rb_quantity'];
  1385. }
  1386. // 1. 获取检验单明细 (需要包含来源到货单子表ID: SOURCEAUTOID)
  1387. $service = new U8ThirtyPartyDatabaseServerService();
  1388. $order = $service->getJyOrder2($id);
  1389. if(empty($order)) return [false, '检验单不存在'];
  1390. // 2. 获取 U8 Token
  1391. list($status, $msg) = $this->getToken();
  1392. if(! $status) return [false, $msg];
  1393. // 3. 组织采购入库单 (PurchaseIn) 结构
  1394. $tmp = [
  1395. "Inum" => "PurchaseIn",
  1396. "Data" => [
  1397. "iHead" => [
  1398. "IsVerify" => true, // 自动审核
  1399. "bCalPrice" => true, // 自动计算价格
  1400. "cWhCode" => $order['CWHCODE'], // 仓库编码
  1401. "cRdCode" => "0101", // 入库类别:原材料采购入库
  1402. "cSource" => "来料检验单", // 来源
  1403. "cBusType" => "普通采购",
  1404. "cChkCode" => $order['CCHECKCODE'] ?? '', // 关联检验单号
  1405. "cMemo" => "接口生成",
  1406. "dDate" => date("Y-m-d"),
  1407. ],
  1408. "iBody" => []
  1409. ]
  1410. ];
  1411. // 4. 组织表体
  1412. $tmp["Data"]["iBody"][] = [
  1413. "iRowNo" => 1,
  1414. "cInvCode" => $order['CINVCODE'] ?? '',
  1415. "cPosition" => $order['cPosition'] ?? '',
  1416. "cBatch" => $order['CBATCH'],
  1417. "iinvexchrate" => (float)($order['FCHANGRATE'] ?? 1),
  1418. "iQuantity" => $num, // 应收数量(入库数量)
  1419. "iNQuantity" => $num, // 实收数量
  1420. "iArrsId" => $order['SOURCEAUTOID'], // 子表id
  1421. "dMadeDate" => $order['DPRODATE'], // 生产日期
  1422. "dVDate" => $order['DVDATE'], // 失效日期
  1423. "iUnitCost" => $order['iCost'] ?? 0,
  1424. "iPrice" => round(($order['iCost'] ?? 0) * $num, 2),
  1425. "iTaxUnitPrice"=> $order['iOriTaxCost'] ?? 0,
  1426. "iSum" => round(($order['iOriTaxCost'] ?? 0) * $num, 2),
  1427. "iTaxRate" => $order['iTaxRate'] ?? 0,
  1428. ];
  1429. $final_data = [$tmp];
  1430. // 5. 调用 API
  1431. $host = $msg['host'] ?? "";
  1432. $token = $msg['token'] ?? "";
  1433. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1434. $url = $host . "/api/PurchaseIn/Add";
  1435. $json = json_encode($final_data);
  1436. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1437. if(! $status) return [false, $result];
  1438. if(! isset($result['code'])) return [false, '采购入库单生成失败'];
  1439. if($result['code'] != 0) return [false, $result['msg']];
  1440. return [true, ''];
  1441. }
  1442. public function purchaseInByZj1($data)
  1443. {
  1444. $record = $data['record'];
  1445. $payload = $data['payload'];
  1446. $type = $data['type'];
  1447. // 确定入库数量
  1448. $num = ($type == 1) ? $payload['hg_quantity'] : $payload['rb_quantity'];
  1449. // 1. 获取基础数据 (即使不参照,我们也需要从检验单拿到物料、仓库、供应商等基本信息)
  1450. $service = new U8ThirtyPartyDatabaseServerService();
  1451. $order = $service->getJyOrder2($record['u8_id']);
  1452. if(empty($order)) return [false, '检验单不存在'];
  1453. // 2. 获取 U8 Token
  1454. list($status, $msg) = $this->getToken();
  1455. if(! $status) return [false, $msg];
  1456. // 3. 组织采购入库单 (不参照模式)
  1457. $tmp = [
  1458. "Inum" => "PurchaseIn",
  1459. "Data" => [
  1460. "iHead" => [
  1461. "IsVerify" => true, // 自动审核
  1462. "bCalPrice" => true, // 自动计算价格
  1463. "PriceCalKey" => "iOriTaxCost", // 以含税单价为准计算
  1464. "cWhCode" => "01", // 仓库
  1465. "cVenCode" => $order['CVENCODE'], // 供应商(不参照时必填)
  1466. "cPTCode" => "01", // 采购类型:委外或普通
  1467. "cRdCode" => "0101", // 入库类别编码
  1468. "cDepCode" => $order['CDEPCODE'] ?? '', // 部门
  1469. "cBusType" => "普通采购",
  1470. "cSource" => "库存", // 设为库存即为不参照模式
  1471. "cMemo" => "接口生成",
  1472. "dDate" => date("Y-m-d"),
  1473. "cExch_Name" => "人民币",
  1474. "iExchRate" => 1,
  1475. ],
  1476. "iBody" => [
  1477. [
  1478. "iRowNo" => 1,
  1479. "cInvCode" => $order['CINVCODE'],
  1480. "cPosition" => $order['CPOSITION'] ?? '',
  1481. "cBatch" => $order['CBATCH'] ?? '',
  1482. "iQuantity" => $num, // 数量
  1483. "iNQuantity" => $num, // 实收数量
  1484. "iOriTaxCost" => $order['iOriTaxCost'], // 含税单价
  1485. "iTaxRate" => $order['iTaxRate'], // 税率
  1486. "dMadeDate" => $order['DPRODATE'], // 生产日期
  1487. "dVDate" => $order['DVDATE'], // 失效日期
  1488. // 注意:不参照时,不要传 iArrsId
  1489. ]
  1490. ]
  1491. ]
  1492. ];
  1493. $final_data = [$tmp];
  1494. // 4. 调用 API
  1495. $host = $msg['host'] ?? "";
  1496. $token = $msg['token'] ?? "";
  1497. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1498. $url = $host . "/api/PurchaseIn/Add";
  1499. $json = json_encode($final_data);
  1500. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1501. if(! $status) return [false, $result];
  1502. if(! isset($result['code'])) return [false, '采购入库单生成失败'];
  1503. if($result['code'] != 0) return [false, $result['msg']];
  1504. return [true, ''];
  1505. }
  1506. public function purchaseInByZj($data)
  1507. {
  1508. $record = $data['record'];
  1509. $payload = $data['payload'];
  1510. $type = $data['type'];
  1511. // 确定入库数量
  1512. $num = ($type == 1) ? (float)$payload['hg_quantity'] : (float)$payload['rb_quantity'];
  1513. // 1. 获取基础数据 (检验单信息)
  1514. $service = new U8ThirtyPartyDatabaseServerService();
  1515. $order = $service->getJyOrder2($record['u8_id']);
  1516. if(empty($order)) return [false, '检验单不存在'];
  1517. // 2. 获取 U8 Token
  1518. list($status, $msg) = $this->getToken();
  1519. if(! $status) return [false, $msg];
  1520. // --- 核心金额计算逻辑 ---
  1521. $qty = (float)$num;
  1522. $taxRate = (float)($order['iTaxRate'] ?? 13.0); // 税率
  1523. $taxUnitPrice = (float)($order['iOriTaxCost'] ?? 0); // 原币含税单价
  1524. // 1. 价税合计 (iSum)
  1525. $iSum = round($qty * $taxUnitPrice, 2);
  1526. // 2. 原币无税金额 (imoney) = 价税合计 / (1 + 税率/100)
  1527. $iMoney = round($iSum / (1 + ($taxRate / 100)), 2);
  1528. // 3. 税额 (itax)
  1529. $iTax = round($iSum - $iMoney, 2);
  1530. // 4. 原币无税单价 (iunitprice)
  1531. $iUnitPrice = round($taxUnitPrice / (1 + ($taxRate / 100)), 6);
  1532. // 3. 组织采购入库单
  1533. $tmp = [
  1534. "Inum" => "PurchaseIn",
  1535. "Data" => [
  1536. "iHead" => [
  1537. "IsVerify" => true,
  1538. "bCalPrice" => true, // 开启自动计算
  1539. "PriceCalKey" => "iOriTaxCost", // 以含税单价为准
  1540. "cWhCode" => (string)($order['CWHCODE'] ?? "01"),
  1541. "cDepCode" => (string)($order['CDEPCODE']),
  1542. "cVenCode" => (string)$order['CVENCODE'],
  1543. "cRdCode" => "0101",
  1544. "iExchRate" => (float)($order['IEXCHRATE'] ?? 1.0),
  1545. "iTaxRate" => (float)$taxRate,
  1546. "cExch_Name" => "人民币",
  1547. "cSource" => "来料检验单",
  1548. "cBusType" => "普通采购",
  1549. "cMemo" => "接口生成",
  1550. "dDate" => date("Y-m-d"),
  1551. "cChkCode" => (string)$order['CCHECKCODE']
  1552. ],
  1553. "iBody" => [
  1554. [
  1555. "iRowNo" => 1,
  1556. "cInvCode" => (string)$order['CINVCODE'],
  1557. "iQuantity" => (float)$qty,
  1558. "iNum" => 0,
  1559. "iNQuantity" => (float)($order['IQUANTITY'] ?? $qty),
  1560. "iNNum" => 0,
  1561. "iArrsId" => (int)$order['SOURCEAUTOID'], // 强制转 int,关联到货单子表
  1562. // --- 新增价格字段 ---
  1563. "iOriTaxCost" => (float)$taxUnitPrice, // 原币含税单价
  1564. "iOriCost" => (float)$iUnitPrice, // 原币无税单价
  1565. "iOriMoney" => (float)$iMoney, // 原币无税金额
  1566. "iOriTaxPrice" => (float)$iTax, // 原币税额
  1567. "iOriSum" => (float)$iSum, // 原币价税合计
  1568. "iTaxRate" => (float)$taxRate, // 税率
  1569. // 本币字段 (inat...) 建议也加上,防止 U8 换算误差
  1570. "fNatMoney" => (float)$iMoney, // 本币无税金额
  1571. "fNatTax" => (float)$iTax, // 本币税额
  1572. "fNatSum" => (float)$iSum, // 本币价税合计
  1573. "cBatch" => (string)($order['CBATCH'] ?? ''),
  1574. "dMadeDate" => (string)($order['DPRODATE'] ?? ''),
  1575. "dVDate" => (string)($order['DVDATE'] ?? ''),
  1576. ]
  1577. ]
  1578. ]
  1579. ];
  1580. // 3. 组织采购入库单 (参照来料检验单模式)
  1581. // $tmp = [
  1582. // "Inum" => "PurchaseIn",
  1583. // "Data" => [
  1584. // "iHead" => [
  1585. // "IsVerify" => true, // 示例中为 false
  1586. // "cWhCode" => (string)($order['CWHCODE'] ?? "01"),
  1587. // "cDepCode" => (string)($order['CDEPCODE']),
  1588. // "cVenCode" => (string)$order['CVENCODE'],
  1589. // "cRdCode" => "0101",
  1590. // "iExchRate" => (float)($order['IEXCHRATE'] ?? 1.0),
  1591. // "iTaxRate" => (float)($order['ITAXRATE'] ?? 13.0),
  1592. // "cExch_Name" => "人民币",
  1593. // "cSource" => "来料检验单", // 匹配示例中的来源
  1594. // "cBusType" => "普通采购",
  1595. // "cMemo" => "接口生成",
  1596. // "dDate" => date("Y-m-d"),
  1597. // "cChkCode" => (string)$order['CCHECKCODE'] // 检验单号
  1598. // ],
  1599. // "iBody" => [
  1600. // [
  1601. // "iRowNo" => 1,
  1602. // "cInvCode" => (string)$order['CINVCODE'],
  1603. // "iQuantity" => (float)$num, // 本次入库实收数量
  1604. // "iNum" => 0,
  1605. // "iNQuantity" => (float)($order['IQUANTITY'] ?? $num), // 检验单关联数量
  1606. // "iNNum" => 0,
  1607. //// "iCheckIdBaks" => (int)$order['ICHECKIDBAKS'],
  1608. // "iArrsId" => $order['SOURCEAUTOID'], // 子表id
  1609. // "cBatch" => (string)($order['CBATCH'] ?? ''),
  1610. // "dMadeDate" => (string)($order['DPRODATE'] ?? ''),
  1611. // "dVDate" => (string)($order['DVDATE'] ?? ''),
  1612. // ]
  1613. // ]
  1614. // ]
  1615. // ];
  1616. $final_data = [$tmp];
  1617. // 4. 调用 API
  1618. $host = $msg['host'] ?? "";
  1619. $token = $msg['token'] ?? "";
  1620. $header = ["Authorization: {$token}", 'Content-Type:application/json'];
  1621. $url = $host . "/api/PurchaseIn/Add";
  1622. $json = json_encode($final_data);
  1623. list($status, $result) = $this->post_helper1($url, $json, $header, 30);
  1624. if(! $status) return [false, $result];
  1625. if(! isset($result['code'])) return [false, '采购入库单生成失败'];
  1626. if($result['code'] != 0) return [false, $result['msg']];
  1627. return [true, ''];
  1628. }
  1629. public function post_helper1($url, $data, $header = [], $timeout = 20){
  1630. Log::channel('apiLog')->info('POST', ["api" => $url , "param" => json_decode($data,true) ,"header" => $header]);
  1631. $ch = curl_init();
  1632. curl_setopt($ch, CURLOPT_URL, $url);
  1633. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  1634. curl_setopt($ch, CURLOPT_ENCODING, '');
  1635. curl_setopt($ch, CURLOPT_POST, 1);
  1636. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  1637. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  1638. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  1639. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  1640. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  1641. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  1642. if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1643. $r = curl_exec($ch);
  1644. if ($r === false) {
  1645. // 获取错误号
  1646. $errorNumber = curl_errno($ch);
  1647. // 获取错误信息
  1648. $errorMessage = curl_error($ch);
  1649. $message = "cURL Error #{$errorNumber}: {$errorMessage}";
  1650. Log::channel('apiLog')->info('POST结果', ["message" => $message ]);
  1651. return [false, $message];
  1652. }
  1653. curl_close($ch);
  1654. $return = json_decode($r, true);
  1655. unset($r);
  1656. Log::channel('apiLog')->info('POST结果', ["message" => $return ]);
  1657. return [true, $return];
  1658. }
  1659. function validateProductDate($dateStr) {
  1660. // SQL Server 常用的 datetime 格式包含 .v (毫秒)
  1661. // 注意:这里的格式必须严格对应 " 2025-12-13 00:00:00.000"
  1662. // 如果字符串开头有空格,格式字符串里也要留空格
  1663. $format = 'Y-m-d H:i:s.v';
  1664. $d = \DateTime::createFromFormat($format, $dateStr);
  1665. // 检查是否转换成功,并且转换后的格式与原字符串完全一致
  1666. return $d && $d->format($format) === $dateStr;
  1667. }
  1668. /**
  1669. * 校验并格式化日期
  1670. * 如果日期合法,返回格式化后的字符串;如果不合法,返回 false
  1671. */
  1672. public function formatAndValidateDate($dateStr) {
  1673. try {
  1674. // DateTime 构造函数会自动识别多种日期格式
  1675. // trim($dateStr) 用于去除可能存在的首尾空格
  1676. $d = new \DateTime(trim($dateStr));
  1677. // 统一转换为 SQL Server 喜欢的格式: 2026-03-11 00:00:00.000
  1678. return $d->format('Y-m-d H:i:s.v');
  1679. } catch (\Exception $e) {
  1680. // 如果传入的字符串无法解析为日期,会抛出异常
  1681. return false;
  1682. }
  1683. }
  1684. }