Поэтому я настроил example_mysql_mirror и заставил все это работать, но по какой-то причине он всегда перезаписывает изменения в MySQL независимо от qbsql_modify_timestamp. Я заметил зеркальную вики-страницу http://www.consolibyte.com/wiki/doku.php/quickbooks_integration_php_consolibyte_sqlmirror ссылается на другое поле (qbsql_modify_datetime), но просто записывает это как внутренние изменения, которые никогда не менялись в вики. Я обновляю таблицу «qb_iteminventory» и ключ «QuantityOnHand» для любых / всех строк.
Моя конечная цель — иметь возможность обновлять количество инвентаря для предметов. Зеркальный пример был просто первым рабочим примером того, что это не только возможно, но и легко сделать (вероятно, по той же причине, по которой он был создан).
После дальнейших раскопок я пришел к нескольким выводам, один из которых заключается в том, что PHP Dev Kit QuickBooks пример_mysql_mirror.php не является полным. Я видел ПРЕДУПРЕЖДЕНИЕ, и я использую последний код GitHub.
Я чувствую, что это проблема со временем (внутренняя обработка qbsql_modify_timestamp) или проблема с родителем (qb_iteminventory дочерний элемент / дочерний элемент родительского элемента, который также должен быть обновлен и т. Д.), Или даже отсутствующее поле (я заметил количествоOnHand, но сделал в MySQL не видят Количество Доступных. Возможно, требуется использовать другой отчет для обмена запасами 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;
}
?>
Зеркало 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:
По сути, вы в конечном итоге будете указывать веб-коннектор на что-то вроде этого:
// 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. Запросы SDK ОЧЕНЬ близко соответствуют пользовательскому интерфейсу, поэтому очень полезно знать, как выполнять действия в пользовательском интерфейсе.
Других решений пока нет …