所以我设置了example_mysql_mirror并使其全部正常工作但由于某种原因它无论qbsql_modify_timestamp如何都会覆盖MySQL中的更改。我注意到镜像维基页面http://www.consolibyte.com/wiki/doku.php/quickbooks_integration_php_consolibyte_sqlmirror引用了一个不同的字段(qbsql_modify_datetime),但只是将其归结为维基中从未更改过的内部更改。我正在为任何/所有行更新表“qb_iteminventory”和密钥“QuantityOnHand”。
我的最终目标是能够更新商品的库存数量。镜像示例只是第一个工作示例,它不仅可行而且容易完成(可能与创建它的原因相同)。
在进一步挖掘后,我得出了一些结论,一个是QuickBooks PHP Dev Kit example_mysql_mirror.php未完成。我确实看到了警告,我正在使用最新的GitHub代码。
我觉得这是时间问题(内部qbsql_modify_timestamp处理)或父问题(qb_iteminventory是父项的子项/子项,也需要更新等),甚至缺少字段(我注意到了) QuantityOnHand但在MySQL中没有看到QuantityAvailable。可能需要为QB库存交换使用不同的报告(不确定是否需要这样做?)。
example_mysql_mirror.php
// I always program in E_STRICT error mode with error reporting turned on...
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// Set the include path
require_once dirname(__FILE__) . '/../../QuickBooks.php';
// You should make sure this matches the time-zone QuickBooks is running in
if (function_exists('date_default_timezone_set'))
{
date_default_timezone_set('America/Los_Angeles');
}
// The username and password the Web Connector will use to connect with
$username = '';
$password = '';
// Database connection string
//
// You *MUST* start with a fresh database! If the database you use has any
// quickbooks_* or qb_* related tables in it, then the schema *WILL NOT* build
// correctly!
//
// Currently, only MySQL is supported/tested.
$dsn = 'mysqli://:@localhost/spray_quickbooks';
// If the database has not been initialized, we need to initialize it (create
// schema and set up the username/password, etc.)
if (!QuickBooks_Utilities::initialized($dsn))
{
header('Content-Type: text/plain');
// It takes a really long time to build the schema...
set_time_limit(0);
$driver_options = array(
);
$init_options = array(
'quickbooks_sql_enabled' => true,
);
QuickBooks_Utilities::initialize($dsn, $driver_options, $init_options);
QuickBooks_Utilities::createUser($dsn, $username, $password);
exit;
}
// What mode do we want to run the mirror in?
//$mode = QuickBooks_WebConnector_Server_SQL::MODE_READONLY; // Read from QuickBooks only (no data will be pushed back to QuickBooks)
//$mode = QuickBooks_WebConnector_Server_SQL::MODE_WRITEONLY; // Write to QuickBooks only (no data will be copied into the SQL database)
$mode = QuickBooks_WebConnector_Server_SQL::MODE_READWRITE; // Keep both QuickBooks and the database in sync, reading and writing changes back and forth)
// What should we do if a conflict is found? (a record has been changed by another user or process that we're trying to update)
$conflicts = QuickBooks_WebConnector_Server_SQL::CONFLICT_LOG;
// What should we do with records deleted from QuickBooks?
//$delete = QuickBooks_WebConnector_Server_SQL::DELETE_REMOVE; // Delete the record from the database too
$delete = QuickBooks_WebConnector_Server_SQL::DELETE_FLAG; // Just flag it as deleted
// Hooks (optional stuff)
$hooks = array();
/*
// Hooks (optional stuff)
$hook_obj = new MyHookClass2('Keith Palmer');
$hooks = array(
// Register a hook which occurs when we perform an INSERT into the SQL database for a record from QuickBooks
// QuickBooks_SQL::HOOK_SQL_INSERT => 'my_function_name_for_inserts',
// QuickBooks_SQL::HOOK_SQL_INSERT => 'MyHookClass::myMethod',
// Register a hook which occurs when we perform an UPDATE on the SQL database for a record from QuickBooks
// QuickBooks_SQL::HOOK_SQL_UPDATE => 'my_function_name_for_updates',
// Example of registering multiple hooks for one hook type
// QuickBooks_SQL::HOOK_PREHANDLE => array(
// 'my_prehandle_function',
// array( $hook_obj, 'myMethod' ),
// ),
// Example of using the hook factory to use a pre-defined hook
// QuickBooks_SQL::HOOK_SQL_INSERT => QuickBooks_Hook_Factory::create(
// 'Relay_POST', // Relay the hook data to a remote URL via a HTTP POST
// 'http://localhost:8888/your_script.php'),
QuickBooks_SQL::SQL_INSERT => array(
QuickBooks_Hook_Factory::create(
'Relay_POST',
'http://localhost:8888/your_script.php',
array( '_secret' => 'J03lsN3at@pplication' ) ),
),
);
class MyHookClass
{
static public function myMethod($requestID, $user, $hook, &$err, $hook_data, $callback_config)
{
// do something here...
return true;
}
}
function my_prehandle_function($requestID, $user, $hook, &$err, $hook_data, $callback_config)
{
//print('here we are!');
return true;
}
class MyHookClass2
{
protected $_var;
public function __construct($var)
{
$this->_var = $var;
}
public function myMethod($requestID, $user, $hook, &$err, $hook_data, $callback_config)
{
//print('variable equals: ' . $this->_var);
return true;
}
}
*/
//
$soap_options = array();
//
$handler_options = array(
'deny_concurrent_logins' => false,
'deny_reallyfast_logins' => false,
);
//
$driver_options = array();
$ops = array(
QUICKBOOKS_OBJECT_SALESTAXITEM,
QUICKBOOKS_OBJECT_SALESTAXCODE,
QUICKBOOKS_OBJECT_CUSTOMER,
QUICKBOOKS_OBJECT_VENDOR,
QUICKBOOKS_OBJECT_TEMPLATE,
QUICKBOOKS_OBJECT_CUSTOMERTYPE,
QUICKBOOKS_OBJECT_VENDORTYPE,
QUICKBOOKS_OBJECT_ESTIMATE,
QUICKBOOKS_OBJECT_INVOICE,
QUICKBOOKS_OBJECT_CLASS,
QUICKBOOKS_OBJECT_INVOICE,
QUICKBOOKS_OBJECT_INVENTORYITEM,
/* Not quite sure why these are not being used
QUICKBOOKS_OBJECT_NONINVENTORYITEM,
QUICKBOOKS_OBJECT_SERVICEITEM,
QUICKBOOKS_OBJECT_SHIPMETHOD,
QUICKBOOKS_OBJECT_PAYMENTMETHOD,
QUICKBOOKS_OBJECT_TERMS,
QUICKBOOKS_OBJECT_PRICELEVEL,
QUICKBOOKS_OBJECT_ITEM,
*/
QUICKBOOKS_OBJECT_PAYMENTMETHOD,
QUICKBOOKS_OBJECT_COMPANY,
QUICKBOOKS_OBJECT_HOST,
QUICKBOOKS_OBJECT_PREFERENCES,
);
$ops_misc = array( // For fetching inventory levels, deleted transactions, etc.
QUICKBOOKS_DERIVE_INVENTORYLEVELS,
QUICKBOOKS_QUERY_DELETEDLISTS,
QUICKBOOKS_QUERY_DELETEDTRANSACTIONS,
// 'nothing',
);
//
$sql_options = array(
'only_import' => $ops,
'only_add' => $ops,
'only_modify' => $ops,
'only_misc' => $ops_misc,
);
//
$callback_options = array();
// $dsn_or_conn, $how_often, $mode, $conflicts, $users = null,
// $map = array(), $onerror = array(), $hooks = array(), $log_level, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array()
$Server = new QuickBooks_WebConnector_Server_SQL(
$dsn,
'1 minute',
$mode,
$conflicts,
$delete,
$username,
array(),
array(),
$hooks,
QUICKBOOKS_LOG_DEVELOP,
QUICKBOOKS_SOAPSERVER_BUILTIN,
QUICKBOOKS_WSDL,
$soap_options,
$handler_options,
$driver_options,
$sql_options,
$callback_options);
$Server->handle(true, true);
save.php
<?php
if (count($_POST) == 0) {
dErr("There is nothing to save. Try again later.");
}
// Compile list of rows to update
$updates = array();
foreach ($_POST as $key => $value) {
array_push($updates, array(explode('_', $key)[1], $value));
}
unset($key);
unset($value);
// TODO: Database updating
// IMPORTANT: Add validation and other selective functionality before adding saving
$qb = new mysqli('127.0.0.1', '', '', 'spray_quickbooks');
// Oh no! A connect_errno exists so the connection attempt failed!
if ($qb->connect_errno) {
dErr("Error: Failed to make a MySQL connection, here is why: <br />Errno: " . $qb->connect_errno . "<br />Error: " . $qb->connect_error);
}
$qbe = new mysqli('127.0.0.1', '', '', 'spray_qb_extras');
// Oh no! A connect_errno exists so the connection attempt failed!
if ($qbe->connect_errno) {
dErr("Error: Failed to make a MySQL connection, here is why: <br />Errno: " . $qbe->connect_errno . "<br />Error: " . $qbe->connect_error);
}
foreach ($updates as $update) {
// Perform an SQL query
$sql = "UPDATE qb_iteminventory SET QuantityOnHand='" . $update[1] . "' WHERE qbsql_id='" . $update[0] . "'";
if (!$qb_result = $qb->query($sql)) {
dErr("Error: Our query failed to execute and here is why: <br />Query: " . $sql . "<br />Errno: " . $qb->errno . "<br />Error: " . $qb->error);
}
}
unset($updates);
unset($update);
// Redirect back to where they came from
echo "<meta http-equiv=\"refresh\" content=\"0;url=".$_SERVER['HTTP_REFERER']."\"/>";
// Extra Functions
function dErr($msg) {
echo "<center><b>Sorry, we have encountered an error.</b><br /><br />";
echo $msg;
echo "</center>";
exit;
}
?>
更新了save.php (广告资源调整)
<?php
if (count($_POST) == 0) {
dErr("There is nothing to save. Please try again later.");
}
// Compile list of rows to update
$updates = array();
foreach ($_POST as $key => $value) {
array_push($updates, array(explode('_', $key)[1], $value));
}
/*unset($key);
unset($value);*/
// IMPORTANT: Add validation and other selective functionality before adding saving
// and stop saving unchanged items just because we can
$qb = new mysqli('127.0.0.1', '', '', 'spray_quickbooks');
// Oh no! A connect_errno exists so the connection attempt failed!
if ($qb->connect_errno) {
dErr("Error: Failed to make a MySQL connection, here is why: <br />Errno: " . $qb->connect_errno . "<br />Error: " . $qb->connect_error);
}
// IMPORTANT: ONLY UPDATE CHANGED ROWS. WE DONT WANT INVENTORY ADJUSTMENTS FOR UNCHANGED ITEMS!
foreach ($updates as $update) {
// Update QuantityOnHand still so our web interface can easily see the new quantity before QB sync
$sql = "UPDATE qb_iteminventory SET QuantityOnHand='" . $update[1] . "' WHERE qbsql_id='" . $update[0] . "'";
if (!$qb_result = $qb->query($sql)) {
dErr("Error: Our query failed to execute and here is why: <br />Query: " . $sql . "<br />Errno: " . $qb->errno . "<br />Error: " . $qb->error);
}
// Get a newly updated item so we can extract Item's FullName
$sql = "SELECT * FROM qb_iteminventory WHERE qbsql_id='" . $update[0] . "'";
if (!$qb_result = $qb->query($sql)) {
dErr("Error: Our query failed to execute and here is why: <br />Query: " . $sql . "<br />Errno: " . $qbe->errno . "<br />Error: " . $qbe->error);
}
$row = mysqli_fetch_assoc($qb_result);
//print_r($row);
// Generate unique TxnID
// Apparently QuickBooks will overwrite it with the permanent TxnID when it syncs
$tID = rand(1000, 9999);
// Insert new Item Adjustment
$sql = "INSERT INTO `qb_inventoryadjustment` ( `TxnID`, `TimeCreated`, `TimeModified`, `Account_FullName`, `TxnDate`, `RefNumber`, `Memo`, `qbsql_discov_datetime`, `qbsql_resync_datetime`, `qbsql_modify_timestamp` ) VALUES ( 'TxnID-" . $tID . "', now(), now(), 'Inventory Adjustments', CURDATE(), '" . $tID . "', NULL, NULL, NULL, now() )";
if (!$qb_result = $qb->query($sql)) {
dErr("Error: Our query failed to execute and here is why: <br />Query: " . $sql . "<br />Errno: " . $qb->errno . "<br />Error: " . $qb->error);
}
// Insert new Item Adjustment Line
$sql = "INSERT INTO `qb_inventoryadjustment_inventoryadjustmentline` ( `InventoryAdjustment_TxnID`, `SortOrder`, `TxnLineID`, `Item_FullName`, `QuantityAdjustment_NewQuantity` ) VALUES ( 'TxnID-" . $tID . "', '0', 'TxnLID-" . $tID . "', '" . $row['FullName'] . "', " . $update[1] . ");";
if (!$qb_result = $qb->query($sql)) {
dErr("Error: Our query failed to execute and here is why: <br />Query: " . $sql . "<br />Errno: " . $qb->errno . "<br />Error: " . $qb->error);
}
}
// TODO: Research whether this is really required, and to what extent
// INFO: Not sure why I feel like this is important
/*$qb_result->free();
$qb->close();
unset($updates);
unset($update);
unset($sql);*/
// Redirect back to where they came from
//echo "<meta http-equiv=\"refresh\" content=\"0;url=".$_SERVER['HTTP_REFERER']."\"/>";
// Extra Functions
function dErr($msg) {
echo "<center><b>Sorry, we have encountered an error.</b><br /><br />";
echo $msg;
echo "</center>";
exit;
}
?>
答案 0 :(得分:1)
库存调整的SQL镜像:
首先,通常的免责声明 - SQL镜像的东西是测试版,所以不要指望100%的功能(如发行说明中所示)。随着说......
QuickBooks不允许您通过编辑库存项目直接更新数量。如果你进入QuickBooks用户界面,你也会看到同样的行为。
所以这样的将不起作用:
$sql = "UPDATE qb_iteminventory SET QuantityOnHand='" . $update[1] . "' WHERE qbsql_id='" . $update[0] . "'";
上述查询将告诉QuickBooks只是替换现有的给定数量,这实际上在“会计世界”中不起作用,因为库存变化具有税务影响(企业对其承载的库存量征税),收入影响(库存变化通常意味着您要么从制造商那里购买更多物品,要么向顾客出售物品),以及物理影响(数量或某些变化,这意味着物理产品流向客户等)会计师/企业需要密切关注并且审核日志显示 以及为什么数量发生了变化。
相反,您在QuickBooks中更改库存的方式是通过单独的事务。例如:
Invoice
会减少手头的数量(你卖东西)Item Receipt
会增加手头的数量(您从制造商或供应商处购买的东西)Inventory Adjustment
QuickBooks SDK(以及镜像代码)遵循此约定 - 如果要更改数量,则需要创建事务。
您可能想要创建Inventory Adjustment
(qb_inventoryadjustment
SQL表)。像这样:
INSERT INTO `qb_inventoryadjustment` ( `TxnID`, `TimeCreated`, `TimeModified`, `Account_FullName`, `TxnDate`, `RefNumber`, `Memo`, `qbsql_discov_datetime`, `qbsql_resync_datetime`, `qbsql_modify_timestamp` )
VALUES ( 'TxnID-1234', now(), now(), 'Inventory Adjustments', '2016-12-23', '1234', 'Test adjustment', NULL, NULL, now() );
INSERT INTO `qb_inventoryadjustment_inventoryadjustmentline` ( `InventoryAdjustment_TxnID`, `SortOrder`, `TxnLineID`, `NewQuantity` )
VALUES ( 'TxnID-1234', '0', 'ABCD-1234', '10' );
如果您查看QuickBooks用户界面,您会看到Inventory Adjustment
个交易同时具有基本详细信息(参考编号,日期等)和行项目级别详细信息(项目和数量),因此您有确保同时提供(基本详细信息的qb_inventoryadjustment
表和行的qb_inventoryadjustment_inventoryadjustmentline
表)
确保您的$ops
数组中已启用Inventory Adjustments
:
$ops = array(
...
QUICKBOOKS_OBJECT_INVENTORYADJUSTMENT
...
);
QUICKBOOKS_OBJECT_INVENTORYITEM
仅同步实际产品本身,不会同步产品的实际数量变化。
如果您发现SQL Mirror因任何原因无法正常运行......
SQL镜像的东西是一个实验,它并不总是正常工作。嘘。 :-(
但是,如果你不介意编写一些代码,那就有一个很好的选择(嘿,你在StackOverflow上,所以你没有!)。
相反,请遵循GitHub项目链接的快速入门:
基本上,您最终会将Web连接器指向以下内容:
// Require the framework
require_once '../../QuickBooks.php';
// A username and password you'll use in:
// a) Your .QWC file
// b) The Web Connector
// c) The QuickBooks framework
$user = 'quickbooks';
$pass = 'password';
// Map QuickBooks actions to handler functions
$map = array(
// ...
);
// This is entirely optional, use it to trigger actions when an error is returned by QuickBooks
$errmap = array();
// An array of callback hooks
$hooks = array();
// Logging level
$log_level = QUICKBOOKS_LOG_DEVELOP; // Use this level until you're sure everything works!!!
// * MAKE SURE YOU CHANGE THE DATABASE CONNECTION STRING BELOW TO A VALID MYSQL USERNAME/PASSWORD/HOSTNAME *
$dsn = 'mysql://root:root@localhost/quickbooks_server';
if (!QuickBooks_Utilities::initialized($dsn))
{
// Initialize creates the neccessary database schema for queueing up requests and logging
QuickBooks_Utilities::initialize($dsn);
// This creates a username and password which is used by the Web Connector to authenticate
QuickBooks_Utilities::createUser($dsn, $user, $pass);
}
// Create a new server and tell it to handle the requests
$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level);
$response = $Server->handle(true, true);
然后,您可以通过以下方式轻松调整发送InventoryAdjustmentAdd
交易:
QUICKBOOKS_ADD_INVENTORYADJUSTMENT => array( '_quickbooks_inventoryadjustment_add_request', '_quickbooks_inventoryadjustment_add_response' )
添加到$map
-
/**
* Generate a qbXML response to add a particular customer to QuickBooks
*
* @return string A valid qbXML request
*/
function _quickbooks_inventoryadjustment_add_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $callback_config)
{
$Driver = QuickBooks_Driver_Singleton::getInstance();
$errnum = null;
$errmsg = null;
$data = $Driver->fetch($Driver->query("SELECT * FROM qb_inventoryadjustment WHERE qbsql_id = %d", $errnum, $errmsg, 0, 1, array( $ID )));
$res_lines = $Driver->query("SELECT * FROM qb_inventoryadjustment_inventoryadjustmentline WHERE InventoryAdjustment_TxnID = '%s' ORDER BY SortOrder ASC", $errnum, $errmsg, null, null, array( $data['TxnID'] ));
foreach ($data as $key => $value)
{
//$data[$key] = QuickBooks_Cast::cast(QUICKBOOKS_OBJECT_CUSTOMER, str_replace('_', ' ', $key), $value);
}
$str_action = 'InventoryAdjustmentAdd';
$TxnID = '';
$EditSequence = '';
if ($action == 'InventoryAdjustmentMod')
{
$str_action = 'InventoryAdjustmentMod';
$TxnID = '<TxnID>' . $data['TxnID'] . '</TxnID>';
$EditSequence = '<EditSequence>' . $data['EditSequence'] . '</EditSequence>';
}
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<' . $str_action . 'Rq requestID="' . $requestID . '">
<' . $str_action . '>
' . $TxnID . '
' . $EditSequence . '
<AccountRef>
<FullName>' . $data['Account_FullName'] . '</FullName>
</AccountRef>
<TxnDate>' . $data['TxnDate'] . '</TxnDate>
<!--<RefNumber>' . $data['RefNumber'] . '</RefNumber>-->
<Memo>' . $data['Memo'] . '</Memo>
';
while ($line = $Driver->fetch($res_lines))
{
$xml .= '
<InventoryAdjustmentLineAdd>
<ItemRef>';
if ($line['Item_ListID'])
{
$xml .= '
<ListID>' . $line['Item_ListID'] . '</ListID>';
}
else
{
$xml .= '
<FullName>' . $line['Item_FullName'] . '</FullName>';
}
$xml .= '
</ItemRef>
<QuantityAdjustment>';
if ($line['QuantityDifference'])
{
$xml .= '
<QuantityDifference>' . $line['QuantityDifference'] . '</QuantityDifference>';
}
else
{
$xml .= '
<NewQuantity>' . $line['NewQuantity'] . '</NewQuantity>';
}
$xml .= '
</QuantityAdjustment>
</InventoryAdjustmentLineAdd>
';
}
$xml .= '
</' . $str_action . '>
</' . $str_action . 'Rq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Receive a response from QuickBooks
*/
function _quickbooks_inventoryadjustment_add_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents, $callback_config)
{
$Driver = QuickBooks_Driver_Singleton::getInstance();
$datetime = date('Y-m-d H:i:s');
$errnum = null;
$errmsg = null;
$data = $Driver->fetch($Driver->query("SELECT * FROM qb_inventoryadjustment WHERE qbsql_id = %d", $errnum, $errmsg, 0, 1, array( $ID )));
if ($data)
{
// Get the existing lines
$res_lines = $Driver->query("SELECT * FROM qb_inventoryadjustment_inventoryadjustmentline WHERE InventoryAdjustment_TxnID = '%s' ORDER BY qbsql_id ASC ", $errnum, $errmsg, null, null, array( $data['TxnID'] ));
// Update ListID/EditSequence
$errnum = null;
$errmsg = null;
$Driver->query("
UPDATE
qb_inventoryadjustment
SET
TxnID = '%s',
EditSequence = '%s',
TimeCreated = '%s',
TimeModified = '%s',
RefNumber = '%s',
qbsql_discov_datetime = '%s',
qbsql_resync_datetime = '%s',
qbsql_modify_timestamp = '%s'
WHERE
qbsql_id = %d ", $errnum, $errmsg, 0, 1,
array(
$idents['TxnID'],
$idents['EditSequence'],
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
$idents['RefNumber'],
$datetime,
$datetime,
$datetime,
$ID ));
// Parse the XML we got back
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/InventoryAdjustmentAddRs');
$TxnLineIDs = array();
foreach ($List->children() as $InventoryAdjustment)
{
// Process the line items
foreach ($InventoryAdjustment->children() as $Child)
{
if ($Child->name() == 'InventoryAdjustmentLineRet')
{
// Store the TxnLineID
$TxnLineIDs[] = $Child->getChildDataAt('InventoryAdjustmentLineRet TxnLineID');
}
}
}
reset($TxnLineIDs);
while ($line = $Driver->fetch($res_lines))
{
$TxnLineID = current($TxnLineIDs);
next($TxnLineIDs);
// Update each line item with the TxnID and the TxnLineID
$Driver->query("
UPDATE
qb_inventoryadjustment_inventoryadjustmentline
SET
InventoryAdjustment_TxnID = '%s',
TxnLineID = '%s',
qbsql_discov_datetime = '%s',
qbsql_resync_datetime = '%s',
qbsql_modify_timestamp = '%s'
WHERE
qbsql_id = %d ", $errnum, $errmsg, null, null,
array(
$idents['TxnID'],
$TxnLineID,
$datetime,
$datetime,
$datetime,
$line['qbsql_id'] ));
}
}
}
}
这些函数^^^基本上与SQL镜像尝试做的事情相同,但是它们比SQL镜像更加可控和可调整,因为你可以完全控制那里的qbXML。
请记住,如果你走这条路线,只要你想要触发它发送给QuickBooks,就需要排队请求:
$Queue->enqueue(QUICKBOOKS_ADD_INVENTORYADJUSTMENT, $ID);
<强>最后:强>
我高度建议您尽可能使用QuickBooks UI熟悉自己。 SDK请求与UI非常匹配,因此了解如何在UI中执行操作非常有用。