| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- <?php
- namespace App\Service;
- use App\Model\DDEmployee;
- use App\Model\PoPodetails;
- use App\Model\PoPomain;
- use App\Model\PuAppVouch;
- use App\Model\PuAppVouchs;
- use App\Model\RdRecord01;
- use App\Model\RdRecords01;
- use Illuminate\Support\Facades\Cache;
- use Illuminate\Support\Facades\Log;
- class U8ThirdPartyService extends Service
- {
- public function getToken($data){
- if(empty($data['database'])) return [false, '账套不能为空'];
- $key = "xky_u8_api" . $data['database'];
- $config = config('wms');
- // 1. 自检网络通畅
- list($bool, $msg) = $this->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, 30);
- 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, 30);
- 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['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, 30);
- 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];
- }
- }
|