You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QuickBooks PHP API问题:无法将费用采购关联至发票

问题

使用QuickBooks PHP API创建发票时,时间条目可成功关联到发票,但新生成的费用(Purchase)无法通过代码关联到发票,无任何报错。费用已在QuickBooks中创建,前端可手动关联,但代码实现失败。

发票创建代码

$Invoice = new QuickBooksOnline\API\Data\IPPInvoice();

//Setup the Invoice Object
$Invoice->CustomerRef = $Values['CustomerId'];
$Invoice->EmailStatus = "NotSet";
$Invoice->BillEmail = new IPPEmailAddress();
$Invoice->BillEmail->Address = $Values['CustomerEmail'];
$Invoice->DocNumber = $Values['DocNumber'];
            
 //..then add all the time charges as line items
foreach ($Values['InvoiceTimeCharge'] as $LineItem) {
    $LineDetails = $Values['InvoiceLineItems'][array_search($LineItem['WHID'], array_column($Values['InvoiceLineItems'], "WHID"))];

    if ($LineItem['Type'] === "Time") {
        //Unset the type from the array we pass in to create a new Transaction
        unset($LineItem['Type']);

        $Line = new QuickBooksOnline\API\Data\IPPLine();
        $Line->Amount = (float)$LineItem['Hours'] * (float)$LineItem['HourlyRate'];
        $Line->DetailType = "SalesItemLineDetail";
        $Line->LinkedTxn = [
            new QuickBooksOnline\API\Data\IPPLinkedTxn([
                'TxnId' => $this->CreateTimeActivity($LineItem)['NewTimeActivityID'], 
                'TxnType' => 'TimeActivity'
            ])
        ];
        $Line->SalesItemLineDetail = new QuickBooksOnline\API\Data\IPPSalesItemLineDetail();
        $Line->SalesItemLineDetail->ItemRef = $Values['OfferingId'];
        $Line->SalesItemLineDetail->TaxCodeRef = "TAX";
        $Line->SalesItemLineDetail->Qty = $LineDetails['Quantity'];
        $Line->SalesItemLineDetail->UnitPrice = (float)$LineDetails['Rate'];
        $Line->Description = $LineDetails['Description'];
        $Invoice->Line[] = $Line;
    } elseif ($LineItem['Type'] === "Material") {

        if (isset($LineDetails['QBPaymentAccountID'])) {
            $Line = new QuickBooksOnline\API\Data\IPPLine();
            $Line->Amount = (float)$LineDetails['LineItemValue'];
            $Line->DetailType = "AccountBasedExpenseLineDetail";
            $Line->LinkedTxn = [
                new QuickBooksOnline\API\Data\IPPLinkedTxn([
                    'TxnId' => $this->CreatePurchase(
                        [
                            "PaymentAccount" => $LineDetails['QBPaymentAccountID'],
                            "LineItemCategory" => $LineDetails['QBExpenseCategoryID'],
                            "LineItemValue" => (float)$LineDetails['LineItemValue'],
                            "CostID" => $LineDetails['WHID'],
                            "CustomerId" => $Values['CustomerId'],
                            "Description" => $LineDetails['ExpenseDesc']
                        ]
                        )['NewPurchaseID'], 
                    'TxnType' => 'Expense'
                ])
            ];

            $Line->AccountBasedExpenseLineDetail = new QuickBooksOnline\API\Data\IPPAccountBasedExpenseLineDetail();
            $Line->AccountBasedExpenseLineDetail->AccountRef = $LineDetails['QBPaymentAccountID'];
            $Line->AccountBasedExpenseLineDetail->CustomerRef = $Values['CustomerId'];
            $Line->AccountBasedExpenseLineDetail->BillableStatus = "Billable";

            $Invoice->Line[] = $Line;
        } else {
            $Line = new QuickBooksOnline\API\Data\IPPLine();
            $Line->Amount = (float)$LineDetails['LineItemValue'];
            $Line->DetailType = "SalesItemLineDetail";
            $Line->SalesItemLineDetail = new QuickBooksOnline\API\Data\IPPSalesItemLineDetail();
            $Line->SalesItemLineDetail->ItemRef = $Values['OfferingId'];
            $Line->SalesItemLineDetail->Amount = (float)$LineDetails['LineItemValue'];
            $Line->Description = $LineDetails['Description'];
            $Invoice->Line[] = $Line;
        }
    }

}
            
// Create the new Invoice in QuickBooks
$NewInvoice = $this->_DataService->Add($Invoice);

$error = $this->_DataService->getLastError();

CreatePurchase函数代码

//Adds a purchase record to quickbooks (initially for expenses) 
public function CreatePurchase($Values) {

    // Create a new QuickBooks's Invoice & Line object 
    $PurchaseItem = new QuickBooksOnline\API\Facades\Purchase();

    $PurchaseValues = [
        'AccountRef' => [
            'value' => $Values['PaymentAccount'], 
        ],
        'PaymentType' => 'Cash',
        'TotalAmt' => $Values['LineItemValue'], // The total amount of the expense
        'Line' => [
            [
                'Amount' => $Values['LineItemValue'], // The amount of the line item
                'DetailType' => 'AccountBasedExpenseLineDetail',
                'AccountBasedExpenseLineDetail' => [
                    'AccountRef' => [
                        'value' => $Values['LineItemCategory'], // The ID of the expense account for the line item
                    ],
                    'CustomerRef' => [
                        'value' => $Values['CustomerId']
                    ],
                    "BillableStatus" => "Billable"
                ],
                'Description' => $Values['Description']
            ]
        ]
    ];
    
     // Create the new Invoice in QuickBooks
    $NewPurchase = $this->_DataService->Add($PurchaseItem->create($PurchaseValues));

    $error = $this->_DataService->getLastError();

    if ($error) {
        //throw new Exception($error->getIntuitErrorDetail());
        return ["status" => false, "error" => $error->getIntuitErrorDetail()];
    } else {
        return ["status" => true, "NewPurchaseID" => $NewPurchase->Id, "NewPurchaseObject" => $NewPurchase, "XrefID" => $XrefUpdateResult];
    }

}

解决方案

问题出在两个关键错误:

1. LinkedTxn的TxnType不匹配

你通过CreatePurchase创建的是Purchase类型的交易,但代码里关联时指定的TxnType是'Expense',QuickBooks API要求这里必须严格匹配实际交易类型,需要改为'Purchase'。

2. 发票行的DetailType错误

发票属于销售类交易,不能使用针对费用的AccountBasedExpenseLineDetail,必须改用SalesItemLineDetail,同时确保关联的项目是可计费的,并且参数设置正确。

修改后的Material类型发票行代码如下:

elseif ($LineItem['Type'] === "Material") {
    if (isset($LineDetails['QBPaymentAccountID'])) {
        $purchaseResult = $this->CreatePurchase([
            "PaymentAccount" => $LineDetails['QBPaymentAccountID'],
            "LineItemCategory" => $LineDetails['QBExpenseCategoryID'],
            "LineItemValue" => (float)$LineDetails['LineItemValue'],
            "CostID" => $LineDetails['WHID'],
            "CustomerId" => $Values['CustomerId'],
            "Description" => $LineDetails['ExpenseDesc']
        ]);
        
        // 先确认采购创建成功
        if (!$purchaseResult['status']) {
            // 处理错误逻辑
            continue;
        }

        $Line = new QuickBooksOnline\API\Data\IPPLine();
        $Line->Amount = (float)$LineDetails['LineItemValue'];
        $Line->DetailType = "SalesItemLineDetail";
        $Line->LinkedTxn = [
            new QuickBooksOnline\API\Data\IPPLinkedTxn([
                'TxnId' => $purchaseResult['NewPurchaseID'], 
                'TxnType' => 'Purchase' // 修正为Purchase
            ])
        ];
        $Line->SalesItemLineDetail = new QuickBooksOnline\API\Data\IPPSalesItemLineDetail();
        $Line->SalesItemLineDetail->ItemRef = $Values['OfferingId']; // 确保此Item支持计费
        $Line->SalesItemLineDetail->TaxCodeRef = "TAX"; // 根据实际情况调整
        $Line->SalesItemLineDetail->BillableStatus = "Billable";
        $Line->SalesItemLineDetail->CustomerRef = $Values['CustomerId'];
        $Line->Description = $LineDetails['Description'];

        $Invoice->Line[] = $Line;
    } else {
        // 原代码逻辑保留
        $Line = new QuickBooksOnline\API\Data\IPPLine();
        $Line->Amount = (float)$LineDetails['LineItemValue'];
        $Line->DetailType = "SalesItemLineDetail";
        $Line->SalesItemLineDetail = new QuickBooksOnline\API\Data\IPPSalesItemLineDetail();
        $Line->SalesItemLineDetail->ItemRef = $Values['OfferingId'];
        $Line->SalesItemLineDetail->Amount = (float)$LineDetails['LineItemValue'];
        $Line->Description = $LineDetails['Description'];
        $Invoice->Line[] = $Line;
    }
}

额外注意事项

  • 确保$Values['OfferingId']对应的Item在QuickBooks中设置为可计费(Billable)。
  • 创建Purchase后,务必检查返回结果的status,避免使用无效的TxnId进行关联。
  • 如果关联后仍有问题,可以开启QuickBooks API的调试日志,查看实际发送的请求和返回的响应,确认参数是否符合API规范。

内容的提问来源于stack exchange,提问作者Rob Santoro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:04:56