Webコネクタを使用して友人のクイックブックとPHPサイトを作成した方法

ある日、クライアントがQuickbooks(以降QB)と私たちが行っているサイトの統合を必要としていました。 これに関して私が最初に疑問に思ったのは、「 QBとは何ですか。QBを実装することさえ可能ですか? 」です。



少しグーグルで、探していたものを見つけました。 Quickbooksは、小企業(米国で使用される主要市場)向けの会計プログラムです。 これは1Cのようなものですが、通常のGUIといくつかの便利な機能があります。 QBは、ユーザーが自分のコンピューター( Windowsのみ )にインストールし、数回クリックするだけで、会計を行う会社を展開するアプリケーションです。



さて、今、少なくとも、私は自分の敵を直接知っています。 統合に関しては、ここでのすべてはもう少し複雑です。 QBを統合できるものについては、 こちらをご覧ください 。 そこで何が見えますか:







うーん、PHP SDK(Coming Soon)が最後の希望です...私はほとんど絶望しましたが、 それは私を救いました。 このWebコネクタとはどのようなものですか? このオフサイトには、 QuickBooks Web Connectorプログラマーズガイドをダウンロードするための小さなページがあり、それだけです(少なくとも、オフサイトの情報を探すのにうんざりしています)。





Webコネクタとは何ですか?

Web Connectorは、QBとWebサーバーの間の一種の仲介者です(QBと共にインストールされます)。 タイムアウトまたはマウスクリックにより、彼はサイトの特定のURLにアクセスし、QBに問い合わせる必要があるサイトからリクエストを受け取り、それを渡します。 QBからの応答を待機し、彼が待機すると、サイトをノックしてQBから応答を送信します。



それでは始めましょう...

まず、Webコネクタにノッキングする場所を指示する必要があります。これは、 * .QWCファイルを使用して行われます。



clients.QWC
<?xml version="1.0"?> <QBWCXML> <AppName>QuickBooks Integrator (clients)</AppName> <AppID></AppID> <AppURL>http://localhost/quickbooks/clients.php</AppURL> <AppDescription>Export Customers from QB to csv file</AppDescription> <AppSupport>http://localhost/</AppSupport> <UserName>admin</UserName> <OwnerID>{90A44FB7-33D6-4815-AC85-AC86A7E7123B}</OwnerID> <FileID>{57F3B9B6-86F6-4FCC-B1FF-967DE1813123}</FileID> <QBType>QBFS</QBType> <IsReadOnly>false</IsReadOnly> </QBWCXML>
      
      













5分ごとにサービスを自動的に開始する必要がある場合は、次を追加する必要があります。



 <Scheduler> <RunEveryNMinutes>5</RunEveryNMinutes> </Scheduler>
      
      







AppURLの短い補足:サーバーでhttpsを構成する機能がない場合(または実際の証明書にお金がない場合)、2つの抜け穴があります。



1) QBが存在するホストでは、サーバーのIPとドメイン名をlocalhostで指定します。サーバーのApache設定でこのドメインを読むことを忘れないでください

2) 自作の証明書を入れて、信頼できるサーバーのリストに追加します。そうしないと機能しません(



qwcを追加するには、次のものが必要です。

-QBを有効にして、アプリケーションが動作する会社を開きます

-オープンWebコネクタ

-Web Connector'eで[ アプリケーション追加 ]ボタンをクリックし、qwcファイルを指定します。

-[OK]をクリックすると、QBはこのアプリケーションへのアクセスをQBデータベースに許可するかどうかを尋ねます(この場合はユーザー「admin」を選択する必要があります)

-最後のダイアログボックスで[完了]をクリックすると、Webコネクタに戻り、ユーザー「admin」のパスワードを入力します

-アプリケーションを起動するには、チェックボックスをオンにして、[ 選択更新 ]ボタンをクリックする必要があります











それで、Webコネクタを受信するためにサイトを準備する番です。

覚えているように、 localhost/quickbooks/clients.php



localhost/quickbooks/clients.php



作成して localhost/quickbooks/clients.php



。 WebコネクタはSOAPプロトコルを使用します。つまり、サイトはSOAPサーバーを上げる必要があります。



clients.php
 <?php /** * File for integration QB * QB Webconnector send soap request to this file * * @package QB SOAP */ /** * Log function * * @param string $mess */ function _log($mess = '') { $file_name = './log/clients.log'; if(!file_exists(dirname($file_name))) mkdir(dirname($file_name), 0777); $f = fopen($file_name, "ab"); fwrite($f, "==============================================\n"); fwrite($f, "[" . date("m/d/YH:i:s") . "] ".$mess."\n"); fclose($f); } /** * Log function * * @param string $mess */ function requestId($id = '') { $file_name = './log/clients_id.log'; if(!file_exists(dirname($file_name))) mkdir(dirname($file_name), 0777); // save id into file if(trim($id) !== ''){ $f = fopen($file_name, "c+b"); fwrite($f, $id); fclose($f); } $id = trim(file_get_contents($file_name)); return $id; } /** * System variables */ define('QB_LOGIN', 'admin'); define('QB_PASSWORD', ''); define('QB_TICKET', '93f91a390fa604207f40e8a94d0d8fd11005de108ec1664234305e17e'); /** * Main class for SOAP SERVER */ require 'qb_clients.php'; /** * Create SOAP server */ $server = new SoapServer("qbwebconnectorsvc.wsdl", array('cache_wsdl' => WSDL_CACHE_NONE)); $server->setClass("Qb_Clients"); $server->handle();
      
      







requestId()関数は、トランザクションIDをファイルに保存するために必要です。 さらに検討される例では、すべての顧客のリストを取得したいと考えています。これは、1000社を超える企業である可能性があります。 したがって、500の部分を受け取るので、より信頼性が高く、サーバーの負荷が少なくなります。 なぜ'QB_LOGINQB_PASSWORDおよびQB_TICKETが必要なですか? 最後の3行-これはSOAPサーバーの作成です。 qbwebconnectorsvc.wsdlこのファイルはサイトのオープンスペースで見つけましたが、どこにあったか覚えていません(しばらく前に再設計しました)。



Web Connectorが知っているのはclientVersionserverVersionauthenticatesendRequestXMLreceiveResponseXMLconnectionErrorgetLastErrorcloseConnectionの 8単語のみであることを忘れていました



qb.php
 <?php /** * File contain base QB class and Result class (empty class for Qb reaponse) */ /** * Response class (empty class) * * @package QB SOAP * @version 2013-10-20 */ class Response{ } /** * Base class for QuickBooks integration * * @package QB SOAP * @version 2013-10-20 */ class Qb { /** * Response object * @var string */ var $response = ''; /** * Constructor * * @return void * @access public * @version 2013-10-20 */ public function __construct() { $this->response = new Response(); } /** * Function return client version * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function clientVersion($param = '') { $response->clientVersionResult = ""; return $response; } /** * Function return server version * * @return string * @access public * @version 2013-10-20 */ public function serverVersion() { $this->response->serverVersionResult = ""; return $this->response; } /** * Function try authenticate user by username/password * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function authenticate($param = '') { if(($param->strUserName == QB_LOGIN) && ($param->strPassword == QB_PASSWORD)) $this->response->authenticateResult = array(QB_TICKET, ""); else $this->response->authenticateResult = array("", "nvu"); return $this->response; } /** * Function return last error * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function connectionError($param = '') { $this->response->connectionErrorResult = "connectionError"; return $this->response; } /** * Function return last error * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function getLastError($param = '') { $this->response->getLastErrorResult = "getLastError"; return $this->response; } /** * Function close connection * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function closeConnection($param = '') { $this->response->closeConnectionResult = "Complete"; return $this->response; } }
      
      











以下のファイルでは、リクエストの形成方法とデータ受信の処理方法を確認できます。



qb_clients.php
 <?php /** * File contains class Qb_Clients() extends Qb() */ /** * Include base class for SOAP SERVER */ require 'qb.php'; /** * Class for import all clients from Qb * * @package QB SOAP * @version 2013-10-20 */ class Qb_Clients extends Qb { /** * Function send request for Quickbooks * * @return string * @param object $param * @access public * @version 2013-10-20 */ public function sendRequestXML($param = '') { $id = requestId(); // <!-- ActiveStatus may have one of the following values: ActiveOnly [DEFAULT], InactiveOnly, All --> if($param->ticket == QB_TICKET){ $request = '<?xml version="1.0" encoding="utf-8"?> <?qbxml version="12.0"?> <QBXML> <QBXMLMsgsRq onError="stopOnError"> <CustomerQueryRq requestID="'.time().'" metaData="NoMetaData" iterator="'.(($id != '')?'Continue':'Start').'" '.(($id != '')?'iteratorID="'.$id.'"':'').'> <MaxReturned>500</MaxReturned> <ActiveStatus>ActiveOnly</ActiveStatus> </CustomerQueryRq> </QBXMLMsgsRq> </QBXML>'; $this->response->sendRequestXMLResult = $request; } else $this->response->sendRequestXMLResult = "E: Invalid ticket."; return $this->response; } /** * Function get response from QB * * @return string * @param object $param * @access public * @version 2013-03-15 */ public function receiveResponseXML($param = '') { $response = simplexml_load_string($param->response); $iteratorID = trim($response->QBXMLMsgsRs->CustomerQueryRs->attributes()->iteratorID); // set new iteratorID requestId($iteratorID); if( ($param->ticket == QB_TICKET) && isset($response->QBXMLMsgsRs->CustomerQueryRs->CustomerRet) ){ $rows = $response->QBXMLMsgsRs->CustomerQueryRs; settype($rows, 'array'); // if list contain only one item row if(isset($rows['CustomerRet']->ListID)) $rows = array($rows['CustomerRet']); else $rows = $rows['CustomerRet']; $data = array(); foreach ($rows as $i=>$r) { settype($r, 'array'); $data[] = array( 'qb_id' => trim($r['ListID']), 'qb_es' => trim($r['EditSequence']), 'is_active' => trim($r['IsActive']), 'phone' => trim($r['Phone']), 'notes' => trim($r['Notes']), 'fax' => trim($r['Fax']), 'company_name' => trim($r['Name']), 'b_email' => trim($r['Email']), 'b_email_other' => trim($r['Cc']), 'b_phone' => trim($r['AltPhone']), 'b_salutation' => trim($r['Salutation']), 'b_fname' => trim($r['FirstName']), 'b_lname' => trim($r['LastName']), 'b_address' => trim($r['BillAddress']->Addr1), 'b_address2' => trim($r['BillAddress']->Addr2), 'b_address3' => trim($r['BillAddress']->Addr3), 'b_city' => trim($r['BillAddress']->City), 'b_state' => trim($r['BillAddress']->State), 'b_country' => trim($r['BillAddress']->Country), 'b_zip' => trim($r['BillAddress']->PostalCode), ); } // echo data into log file _log(print_r($data,1)); $this->response->receiveResponseXMLResult = '30'; } else $this->response->receiveResponseXMLResult = '100'; return $this->response; } }
      
      







<?qbxml version="12.0"?>



という行は、qbxmlの12番目のバージョンを使用していると言います。 現時点では、これは利用可能な最新バージョンです(13番目と14番目のQBでサポートされています)。 qbxmlのバージョンが高いほど、QBで作業する可能性が大きくなります。 利用可能なすべてのクエリのリストは、 ここにあります 。 リンクをたどると、QBに送信できるすべての可能なリクエストが表示されます(それらは[メッセージ選択]リストに表示されます)。 [リクエスト]タブと[ レスポンス]タブ-選択したリクエストに応じて生成されます。



PS。 1つだけですが、 たとえば、「 CustomerAdd 」というリクエストを選択すると、このリクエストが「Contacts」ブロックをサポートしていることがわかります。これは、qbxmlの12番目のバージョンから利用できます。 しかし、実際には実装されていませんが、実装プロセス中のみです(ドキュメントに含まれているのは謎であるため、この機能が説明されているフォーラムに誤ってアクセスするまで、1時間以上この問題に取り組みました)。 したがって、何かがqbxml v.12機能しない場合、それが必要なのは事実ではありません:)



PSS ソースコードはこちらです。



All Articles