checkNetworkStatus($config['api_host'], $config['api_port']); if (!$bool) return [false, "网络连接失败: " . $msg]; $host = $config['api_host'] . ":" . $config['api_port']; // 2. 尝试从缓存获取 $token = Cache::get($key); if (! $token) { // 3. 缓存失效,请求新 Token $url = $host . "/api/System/GetToken"; $date = date("Y-m-d"); $json = [ "U8DbName" => $data['database'], "sUserId" => $config['user_id'], "sPassword" => $config['user_password'], "LoginDateTime" => $date, "bPersist" => true ]; $header = ['Content-Type:application/json']; // 调用你的 POST 辅助函数 list($status, $result) = $this->post_helper1($url, json_encode($json), $header); if (!$status) return [false, "用友token获取失败: " . $result]; if (!isset($result['code'])) return [false, '获取用友登录信息失败: 响应格式异常']; if ($result['code'] != 0) return [false, "U8错误: " . ($result['msg'] ?? '未知错误')]; $token = $result['data']['Token'] ?? ""; if (empty($token)) return [false, "接口返回的 Token 为空"]; // 30分钟 = 1800秒 Cache::put($key, $token, now()->addMinutes(30)); } return [true, ['host' => $host, 'token' => $token]]; } public function puAppVouch($record) { // 1. 查询本地请购单主表(严格匹配 MySQL 字段名) $order = PuAppVouch::where('del_time', 0) ->where('order_number', $record['order_number']) ->where('login_type', $record['login_type']) ->first(); if (empty($order)) return [false, '请购单不存在或已被删除']; $order_array = $order->toArray(); // 2. 查询本地请购单明细子表(多行数据) $details = PuAppVouchs::where('del_time', 0) ->where('main_id', $order_array['id']) ->get()->toArray(); if (empty($details)) return [false, '请购单明细不存在或已被删除']; // 3. 获取用友 U8 接口的 Token list($status, $msg) = $this->getToken($record); if (! $status) return [false, $msg]; // 4. 循环组装表体明细 (iBody) $iBody = []; foreach ($details as $index => $item) { $qty = (float)$item['quantity']; // 数量:quantity $taxUnitPrice = (float)$item['price']; // 原币含税单价:price $taxRate = (float)$item['rate']; // 税率:rate(例如 13 或 17) // --- 核心价格与金额计算逻辑 --- // 1. 原币价税合计 / 含税金额 (iOriSum) $iOriSum = round($qty * $taxUnitPrice, 2); // 2. 原币无税金额 / 金额 (iOriMoney) $iOriMoney = round($iOriSum / (1 + ($taxRate / 100)), 2); // 3. 原币税额 (iOriTaxPrice) $iOriTaxPrice = round($iOriSum - $iOriMoney, 2); // 4. 原币无税单价 (iOriCost) $iOriCost = round($taxUnitPrice / (1 + ($taxRate / 100)), 6); // 因为币种是人民币,汇率是 1,所以 本币价格 = 原币价格 $fMoney = $iOriMoney; // 本币无税金额 $iMoney = $iOriMoney; // 本币无税金额(对应你列出的 iMoney) $iTaxPrice = $iOriTaxPrice; // 本币税额 $iBody[] = [ "ivouchrowno" => $index + 1, // 行号且唯一 "cInvCode" => (string)$item['cInvCode'], // 存货编码 "fQuantity" => $qty, // fQuantity 数量 "iPerTaxRate" => (int)$taxRate, // iPerTaxRate 税率 "fNum" => 0, // 件数 "dRequirDate" => $item['dRequirDate'], // 需求日期 "dArriveDate" => $item['dArriveDate'], // 建议订货日期 "cexch_name" => "人民币", // 币种 "iExchRate" => 1, // 汇率 // --- 补充的单价字段 --- "iOriTaxCost" => $taxUnitPrice, // iOriTaxCost 原币含税单价 "fTaxPrice" => $taxUnitPrice, // fTaxPrice 含税单价 "fUnitPrice" => $iOriCost, // fUnitPrice 无税单价 "iOriCost" => $iOriCost, // iOriCost 原币单价 // --- 补充的金额/税额字段 --- "fMoney" => $fMoney, // fMoney 金额 (本币无税金额) "iMoney" => $iMoney, // iMoney 本币金额 "iOriMoney" => $iOriMoney, // iOriMoney 原币金额 "iOriTaxPrice" => $iOriTaxPrice, // iOriTaxPrice 原币税额 "iTaxPrice" => $iTaxPrice, // iTaxPrice 本币税额 "iOriSum" => $iOriSum, // iOriSum 原币价税合计 ]; } // 5. 组织请购单请求数据结构 $tmp = [ "Inum" => "PuAppVouch", "Data" => [ "iHead" => [ "cBusType" => $order_array['cBusType'], "cPTCode" => $order_array['cPTCode'], "cMemo" => "接口生成", "IsVerify" => true, "ddate" => $order_array['order_time'], "PriceCalKey" => "iOriTaxCost", "cMaker" => DDEmployee::where('userid', $order_array['crt_id'])->where('login_type', $record['login_type'])->value('name'), ], "iBody" => $iBody ] ]; $final_data = [$tmp]; // 6. 准备发送请求 $host = $msg['host'] ?? ""; $token = $msg['token'] ?? ""; $header = [ "Authorization: {$token}", 'Content-Type: application/json' ]; $url = rtrim($host, '/') . "/api/PuAppVouch/Add"; $json = json_encode($final_data, JSON_UNESCAPED_UNICODE); // 7. 发送 POST 请求 list($status, $result) = $this->post_helper1($url, $json, $header, 40); if (! $status) return [false, $result]; if (! isset($result['code'])) return [false, '接口响应异常,请检查用友服务']; if ($result['code'] != 0) return [false, $result['msg']]; // 8. 回写用友生成的单据编号 $code = $result['data'][0]['VouchCode']; $order->code = $code; $order->save(); return [true, '']; } public function poPomain($record) { // 1. 查询本地采购订单主表(对应 po_pomain 表) $order = PoPomain::where('del_time', 0) ->where('order_number', $record['order_number']) ->where('login_type', $record['login_type']) ->first(); if (empty($order)) return [false, '采购订单主表记录不存在或已被删除']; $order_array = $order->toArray(); // 2. 查询本地采购订单明细子表(对应 po_podetails 表) $details = PoPodetails::where('del_time', 0) ->where('main_id', $order_array['id']) ->get()->toArray(); if (empty($details)) return [false, '采购订单明细不存在或已被删除']; // 3. 获取用友 U8 接口的 Token 和 Host 基础信息 list($status, $msg) = $this->getToken($record); if (! $status) return [false, $msg]; // 4. 循环组装采购订单表体明细 (iBody) $iBody = []; foreach ($details as $index => $item) { $qty = (float)$item['quantity']; // 数量:quantity $taxUnitPrice = (float)$item['price']; // 原币含税单价:price $taxRate = (float)$item['rate']; // 税率:rate(例如 13 或 17) $iAppIds = $item['iAppIds'] ?? null; // U8 采购订单要求的明细行基础结构 $bodyRow = [ "ivouchrowno" => $index + 1, // 行号且唯一 "cInvCode" => $item['cInvCode'], // 存货编码 "iQuantity" => $qty, // 数量 "iPerTaxRate" => $taxRate, // 税率 "iTaxPrice" => $taxUnitPrice, // 原币含税单价(与表头 PriceCalKey 对应) "dArriveDate" => $item['dArriveDate'], // 建议/计划到货日期 "bGsp" => 0, // 是否质检,默认0 "iAppIds" => $iAppIds, ]; $iBody[] = $bodyRow; } $cappcode = null; if(! empty($order_array['cappcode'])) $cappcode = $order_array['cappcode'][0] ?? null; // 5. 组织采购订单请求的表头数据结构 (iHead) $iHead = [ "cVenCode" => $order_array['cVenCode'], // 供应商编码 "cDepCode" => $order_array['cDepCode'] ?? null, // 部门编码 "iDiscountTaxType" => 0, // 0--应税外加(默认), 1--应税内含 "iTaxRate" => (int)$order_array['iTaxRate'], // 税率 "nflat" => (int)($order_array['nflat'] ?? 1), // 汇率 "cexch_name" => !empty($order_array['cexch_name']) ? $order_array['cexch_name'] : "人民币", // 币种 "IsVerify" => true, // 是否审核 (默认生成即审核) "cMemo" => !empty($order_array['mark']) ? $order_array['mark'] : "接口生成", // 备注 "PriceCalKey" => "iTaxPrice", // 以含税单价作为计算基准 "dPODate" => $order_array['order_time'], // 单据日期 "cappcode" => $cappcode, // 请购单号 "cMaker" => DDEmployee::where('userid', $order_array['crt_id'])->where('login_type', $record['login_type'])->value('name'), ]; if(! empty($cappcode)) $iHead['cSource'] = 'app'; //参照的固定参数 // 6. 包装成接口所需的嵌套数组格式 $tmp = [ "Inum" => "PurchaseOrder", "Data" => [ "iHead" => $iHead, "iBody" => $iBody ] ]; $final_data = [$tmp]; // 7. 准备发送网络请求 $host = $msg['host'] ?? ""; $token = $msg['token'] ?? ""; $header = [ "Authorization: {$token}", 'Content-Type: application/json' ]; $url = rtrim($host, '/') . "/api/PurchaseOrder/Add"; $json = json_encode($final_data, JSON_UNESCAPED_UNICODE); // 8. 调用 POST 助手发送 list($status, $result) = $this->post_helper1($url, $json, $header, 40); if (! $status) return [false, $result]; if (! isset($result['code'])) return [false, '接口响应异常,请检查用友服务状态']; if ($result['code'] != 0) return [false, $result['msg']]; // 9. 成功后回写用友生成的采购订单编号 (VouchCode) $u8VouchCode = $result['data'][0]['VouchCode']; $order->code = $u8VouchCode; // 回写到用友单据编号字段 $order->save(); return [true, '']; } public function purchaseInAdd($record) { // 1. 查询本地采购入库单主表(对应 rd_record01 表) $order = RdRecord01::where('del_time', 0) ->where('order_number', $record['order_number']) ->where('login_type', $record['login_type']) ->first(); if (empty($order)) return [false, '采购入库单主表记录不存在或已被删除']; $order_array = $order->toArray(); // 2. 查询本地采购入库单明细子表(对应 rd_records01 表) $details = RdRecords01::where('del_time', 0) ->where('main_id', $order_array['id']) ->get()->toArray(); if (empty($details)) return [false, '采购入库单明细不存在或已被删除']; // 3. 获取用友 U8 接口的 Token 和 Host 基础信息 list($status, $msg) = $this->getToken($record); if (!$status) return [false, $msg]; // 4. 循环组装采购入库单表体明细 (iBody) $iBody = []; foreach ($details as $index => $item) { $qty = (float)$item['quantity']; $taxUnitPrice = (float)$item['price']; // 原币含税单价:price $taxRate = (float)$item['rate']; // 税率:rate(例如 13 或 17) // U8 采购入库单接口要求的明细行数据结构 $bodyRow = [ "iRowNo" => $index + 1, // 行号必填且唯一 "cInvCode" => $item['cInvCode'], // 存货编码 "cBatch" => $item['cBatch'] ?? null, // 批号(按需传入,默认空) "dMadeDate" => $item['dMadeDate'] ?? null, // 生产日期(按需传入,默认空) "dVDate" => $item['dVDate'] ?? null, // 失效日期(按需传入,默认空) "iTaxRate" => $taxRate, // 税率 "iOriTaxCost" => $taxUnitPrice, // 原币含税单价(与表头 PriceCalKey 对应) "iinvexchrate" => 0, // 换算率,默认0 "iQuantity" => $qty, // 实际入库数量(蓝单为正,红单为负) "iNum" => 0, // 辅计量件数,默认0 "iNQuantity" => $qty, // 应收应发数量(默认等于实际入库数量) "iNNum" => 0, // 应收应发辅计量数量,默认0 "iPOsID" => $item['iPOsID'] // 核心必填:采购订单子表唯一标识ID ]; $iBody[] = $bodyRow; } $cOrderCode = null; if(! empty($order_array['cOrderCode'])) $cOrderCode = $order_array['cOrderCode'][0] ?? null; // 5. 组织采购入库单请求的表头数据结构 (iHead) $iHead = [ "bIsRedVouch" => $order_array['bredvouch'] == 1 ? true : false , //是否红单 "IsVerify" => true, // 是否审核 "bCalPrice" => true, // 是否由接口自动计算金额 (方式一:拉取订单价格和税率) "PriceCalKey" => "iOriTaxCost", // 以含税单价作为计算基准 "cWhCode" => $order_array['warehouseCode'], // 仓库编码 "cPTCode" => $order_array['cPTCode'], // 采购类型编码 "cRdCode" => $order_array['cRdCode'], // 出入库/收发类别编码 "cVenCode" => $order_array['cVenCode'], // 供应商编码 "cExch_Name" => !empty($order_array['cexch_name']) ? $order_array['cexch_name'] : "人民币", // 币种 "iExchRate" => (int)($order_array['nflat'] ?? 1), // 汇率 "iTaxRate" => (int)$order_array['iTaxRate'], // 税率 "cDepCode" => $order_array['cDepCode'] ?? null, // 部门编码 "cOrderCode" => $cOrderCode, // 关联的采购订单号 "cBusType" => !empty($order_array['cBusType']) ? $order_array['cBusType'] : "普通采购", // 业务类型 "cSource" => "采购订单", // 单据来源固定为采购订单 "cMemo" => !empty($order_array['mark']) ? $order_array['mark'] : "API生成", // 备注 "dDate" => $order_array['order_time'], // 单据日期 "cMaker" => DDEmployee::where('userid', $order_array['crt_id'])->where('login_type', $record['login_type'])->value('name'), ]; // 6. 包装成接口所需的嵌套外层数组格式 $tmp = [ "Inum" => "PurchaseIn", "Data" => [ "iHead" => $iHead, "iBody" => $iBody ] ]; $final_data = [$tmp]; // 7. 准备发送网络请求 $host = $msg['host'] ?? ""; $token = $msg['token'] ?? ""; $header = [ "Authorization: {$token}", 'Content-Type: application/json' ]; $url = rtrim($host, '/') . "/api/PurchaseIn/Add"; $json = json_encode($final_data, JSON_UNESCAPED_UNICODE); // 8. 调用 POST 助手发送请求 list($status, $result) = $this->post_helper1($url, $json, $header, 40); if (!$status) return [false, $result]; if (!isset($result['code'])) return [false, '接口响应异常,请检查用友服务状态']; if ($result['code'] != 0) return [false, $result['msg']]; // 9. 成功后回写用友生成的采购入库单单据编号 (VouchCode) 到本地 code // 注意:文档中返回的 data 也是一个包裹数组 $u8VouchCode = $result['data'][0]['VouchCode']; $order->code = $u8VouchCode; $order->save(); return [true, '']; } public function post_helper1($url, $data, $header = [], $timeout = 20){ Log::channel('apiLog')->info('POST', ["api" => $url , "param" => json_decode($data,true) ,"header" => $header]); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_ENCODING, ''); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $r = curl_exec($ch); if ($r === false) { // 获取错误号 $errorNumber = curl_errno($ch); // 获取错误信息 $errorMessage = curl_error($ch); $message = "cURL Error #{$errorNumber}: {$errorMessage}"; Log::channel('apiLog')->info('POST结果', ["message" => $message ]); return [false, $message]; } curl_close($ch); $return = json_decode($r, true); unset($r); Log::channel('apiLog')->info('POST结果', ["message" => $return ]); return [true, $return]; } }