U8ThirdPartyService.php 84 KB

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