Quickbooks PHP Dev Kit Web连接器SalesRep导入

时间:2017-01-27 02:28:57

标签: sql-server xml quickbooks

我正在使用Consolibyte提供的优秀Quickbooks PHP Dev Kit,我正在将销售代表导入到数据库中。下面的代码是我对请求xml的代码。 Quickbooks文件有超过3000个销售代表,响应xml无效。我知道我可以使用metaData来获取计数并使用MaxReturned来获取有限的记录。不确定迭代器中是否存在?

我将xml传递给mssql数据库并在那里进行解析。问题是当xml无效时,它会导致sqlserver在查询运行时挂起 - 所以我需要以某种方式验证xml,然后再将其传递给sql。

任何帮助都将不胜感激。

$xml = '<?xml version="1.0" encoding="utf-8"?>
            <?qbxml version="' . $version . '"?>
            <QBXML>
                <QBXMLMsgsRq onError="stopOnError">
                    <SalesRepQueryRq>
                        <ActiveStatus>ActiveOnly</ActiveStatus>
                        <FromModifiedDate>' . $last . '</FromModifiedDate>
                        <IncludeRetElement>ListID</IncludeRetElement>
                        <IncludeRetElement>SalesRepEntityRef</IncludeRetElement>
                    </SalesRepQueryRq>
                </QBXMLMsgsRq>
            </QBXML>';

    return $xml;

----使用以下代码更新--- @keith - 感谢您抽出宝贵时间作出回应。以下是我的请求/响应函数

   //sales rep list import request
function _quickbooks_salesrep_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
    $last = _quickbooks_get_last_run($user, $action); //get last run time
    _quickbooks_set_last_run($user, $action); //set current run

    // Build the request
    $xml = '<?xml version="1.0" encoding="utf-8"?>
            <?qbxml version="' . $version . '"?>
            <QBXML>
                <QBXMLMsgsRq onError="stopOnError">
                    <SalesRepQueryRq>
                        <ActiveStatus>ActiveOnly</ActiveStatus>
                        <FromModifiedDate>' . $last . '</FromModifiedDate>
                        <IncludeRetElement>ListID</IncludeRetElement>
                        <IncludeRetElement>SalesRepEntityRef</IncludeRetElement>
                    </SalesRepQueryRq>
                </QBXMLMsgsRq>
            </QBXML>';

    return $xml;
}

//sales rep list import response
function _quickbooks_salesrep_import_response ($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
      global $conn;
      $sql = '{call dbo.spQBAddUpdateSalesRep (?)}';
      $params = array($xml);
      $stmt = dbQuery($sql,$conn,$params);
      dbCloseQuery($stmt);
    //QuickBooks_Utilities::log(QB_QUICKBOOKS_DSN, 'rep xml - ' . $xml);
    return true;
}

这是我在传递xml的存储过程中所拥有的内容

@strXML XML --- this is passed as parameter

BEGIN TRY
BEGIN TRANSACTION

UPDATE dbo.qb_salesrep SET
             TimeCreated = SUB.TimeCreated
            ,TimeModified = SUB.TimeModified
            ,EditSequence = SUB.EditSequence
            ,Initial = SUB.Initial
            ,IsActive = SUB.IsActive
            ,SalesRepEntityRef_ListID = SUB.SalesRepEntityRef_ListID
            ,SalesRepEntityRef_FullName = SUB.SalesRepEntityRef_FullName
        FROM
            (SELECT
                 ListID = x.c.value('ListID[1]','VARCHAR(36)')
                ,TimeCreated = x.c.value('TimeCreated[1]','DATETIME')
                ,TimeModified = x.c.value('TimeModified[1]','DATETIME')
                ,EditSequence = x.c.value('EditSequence[1]','VARCHAR(16)')
                ,Initial = x.c.value('Initial[1]','VARCHAR(5)')
                ,IsActive = x.c.value('IsActive[1]','BIT')
                ,SalesRepEntityRef_ListID = x.c.value('SalesRepEntityRef[1]/ListID[1]','VARCHAR(36)')
                ,SalesRepEntityRef_FullName = x.c.value('SalesRepEntityRef[1]/FullName[1]','VARCHAR(41)')
            FROM
                @strXML.nodes('QBXML/QBXMLMsgsRs/SalesRepQueryRs/SalesRepRet') x(c)) AS SUB
        WHERE
            dbo.qb_salesrep.ListID = SUB.ListID 

INSERT INTO dbo.qb_salesrep (
                 ListID
                ,TimeCreated
                ,TimeModified
                ,EditSequence
                ,Initial
                ,IsActive
                ,SalesRepEntityRef_ListID
                ,SalesRepEntityRef_FullName
            )
            SELECT
                 ListID = x.c.value('ListID[1]','VARCHAR(36)')
                ,TimeCreated = x.c.value('TimeCreated[1]','DATETIME')
                ,TimeModified = x.c.value('TimeModified[1]','DATETIME')
                ,EditSequence = x.c.value('EditSequence[1]','VARCHAR(16)')
                ,Initial = x.c.value('Initial[1]','VARCHAR(5)')
                ,IsActive = x.c.value('IsActive[1]','BIT')
                ,SalesRepEntityRef_ListID = x.c.value('SalesRepEntityRef[1]/ListID[1]','VARCHAR(36)')
                ,SalesRepEntityRef_FullName = x.c.value('SalesRepEntityRef[1]/FullName[1]','VARCHAR(41)')
            FROM
                @strXML.nodes('QBXML/QBXMLMsgsRs/SalesRepQueryRs/SalesRepRet') x(c)
            WHERE
                x.c.value('ListID[1]','VARCHAR(36)') NOT IN (SELECT ListID FROM dbo.qb_salesrep) 

COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RETURN
END CATCH

- 所以发生的事情是从quickbooks返回的响应没有很好的形成xml - 它最终被切断了。我猜是因为xml中的数据太多了。因此,当它被传递给sql存储过程时 - 它会锁定插入/更新,因为它没有很好地形成xml。所以我希望以某种方式验证xml并能够检索小块。

我使用SalesRepQueryRq中的metaData获得了总记录数。如果我在请求中将MaxRuturned设置为50,那么一切都可以正常运行。

这是来自网络连接器的信息

描述: QBWC1042:ReceiveResponseXML失败 错误消息:操作已超时请参阅QWCLog以获取更多详细信息。记得打开登录。

我使用了QUICKBOOKS_LOG_DEVELOP,最后一行是 receiveResponseXML()

Incoming XML response: <?xml version="1.0" ?> <QBXML> <QBXMLMsgsRs> <SalesRepQueryRs requestID="6" statusCode="0" statusSeverity="Info" statusMessage="Status OK"> <SalesRepRet> <ListID>31A0000-1193779738</ListID> <SalesRepEntityRef> <ListID>60F0002-1193780133</ListID> <FullName>*BRANDT&apos;S</FullName> </SalesRepEntityRef> </SalesRepRet>
.....
.....
</Sales

这是xml结束的方式

2 个答案:

答案 0 :(得分:1)

  

- 所以发生的事情是从quickbooks返回的响应没有很好地形成xml

不,这不是正在发生的事情。 XML QuickBooks返回完全没问题。

  

我猜是因为xml中有太多数据。

您的问题有关,因为存在大量数据......但 实际上与QuickBooks中的无效XML无关。

  

因此,当它被传递给sql存储过程时 - 它会锁定插入/更新,因为它没有很好地形成xml。

不,这不是事情爆发的原因。

  

所以我希望以某种方式验证xml并能够检索小块。

无需验证XML。返回的XML QuickBooks完全有效。

然而,将结果分解为较小的块 是个好主意。

  

如果我在请求中将MaxRuturned设置为50,那么一切都可以正常运行。

这是一个很大的暗示。它与无效的XML无关......您的问题与在XML中返回了多少结果有关。

  

描述:QBWC1042:ReceiveResponseXML失败错误消息:操作已超时请参阅QWCLog以获取更多详细信息。记得打开登录。

现在我们处于重要位置!

这是关键:

  • 操作已超时

不幸的是,你没有发布Web连接器日志文件的其余部分,所以我们没有得到其他有用的信息。

Web连接器具有硬编码,2分钟超时。如果您已发布其余日志,我认为我们会看到Web连接器日志中的时间戳显示Web连接器开始向您发送数据与连接关闭以及错误显示之间有2分钟的差距在日志中。

我敢打赌,如果您对代码进行基准测试,您还会发现代码需要2分钟以上才能处理来自QuickBooks的响应。

  

我使用了QUICKBOOKS_LOG_DEVELOP,最后一行是receiveResponseXML()

我假设你是从quickbooks_log SQL表中提取的。我打赌如果你检查了你的列类型,你会发现列上的最大长度只是切断了XML,因为它无法将所有数据都放入列中。

  

不确定是否存在迭代器?

此时您可以查看文档:

并且很容易确定SalesRepQueryRq不支持迭代器。

但是,您可以通过查询数据块伪造迭代器。例如,使用NameRangeFilter

<NameRangeFilter> <!-- optional -->
<FromName >STRTYPE</FromName> <!-- optional -->
<ToName >STRTYPE</ToName> <!-- optional -->
</NameRangeFilter>

检索从AaAz的所有内容。然后对BaBz执行相同的操作。然后是CaCz等等。您最终会获得所有数据,但是会以较小的块数获得。

TLDR:查看其余日志。下次,从日志中发布整个相关部分。您的代码很可能花费的时间太长,超出Web连接器2分钟超时。

答案 1 :(得分:0)

嗯,这适用于SQL-Server:

declare @xml xml=
   '<?xml version="1.0" encoding="utf-8"?>
    <?qbxml version="3"?>
    <QBXML>
        <QBXMLMsgsRq onError="stopOnError">
            <SalesRepQueryRq>
                <ActiveStatus>ActiveOnly</ActiveStatus>
                <FromModifiedDate>2017-01-27T08:30:15</FromModifiedDate>
                <IncludeRetElement>ListID</IncludeRetElement>
                <IncludeRetElement>SalesRepEntityRef</IncludeRetElement>
            </SalesRepQueryRq>
        </QBXMLMsgsRq>
    </QBXML>';
select @xml;

你是如何将它传递给MS-Sql-Server的?这是一个related answer,它解释了编码和参数类型可能出现的麻烦来源。

提示:让SQL-Server中的参数为NVARCHAR(MAX)类型。我开始在没有<? ... ?>处理指令)的情况下传递XML。关闭第一行,让这从<QBXML>开始。并确保填入. $ last .的日期为ISO8601(2017-01-27T08:30:15)。

在SQL Server中,您可以将传入的字符串分配给XML类型的变量。