# Vendor bills

> Not all of it is in the bill object. QuickBooks splits accrual bills from immediate card and cash purchases into two entirely different entities, and a vendor-spend figure that reads only Bill under-reports — often dramatically.

Source: https://lopifi.com/apis/bills
Part of: Lopifi — US GAAP books on autopilot, reviewed by a CPA (https://lopifi.com)

---

**Where does supplier spend actually live in each ledger?** Vendor spend aggregates several document types per source. Label the total precisely: billed is not the same as spent.

## Divergences between Xero, QuickBooks Online and NetSuite

### QuickBooks splits accrual from cash, and both are spend

A vendor invoice you will pay later is a Bill. A card swipe is a Purchase, which posts no accounts payable and does not appear in any Bill query. Companies that run most spend on cards can have the majority of their expenses invisible to a Bill-only integration.

**Handling:** Every spend aggregate reads Bill and Purchase — in Xero, Invoice ACCPAY and BankTransaction SPEND. Name the metric for what it contains.

### NetSuite's two sublists are not interchangeable

A vendor bill carries an expense sublist (account, amount, segments) and an item sublist (item, quantity, rate, receipt matching). Item lines participate in three-way match and inventory costing; expense lines do not. Flattening both into one lines array loses the distinction, and the loss shows up the moment someone asks why a bill will not match its receipt.

**Handling:** Carry a lineKind discriminator of 'account' | 'item' in the canonical model. QBO needs it too, for its two DetailTypes.

### Approval is real in NetSuite and decorative elsewhere

Xero's SUBMITTED status is a flag any user can set. QBO's API has no approval concept whatsoever. NetSuite has server-enforced multi-step routing that your write may enter and not clear — a successful API call does not mean an approved bill.

**Handling:** Approval state is ours for Xero and QBO, mirrored from NetSuite. Write-back is capability-gated and hidden where the capability is false.

### Three-way match exists in one of the three

Only NetSuite models goods receipt as a record and reconciles quantity and price across PO, receipt and bill. QBO offers a link between a bill and a purchase order, with no receipt and no variance; Xero's API exposes no bill-to-PO link at all, only a purchase order whose Status has moved to BILLED.

**Handling:** The match component computes the variance itself for QBO, from whatever quantities the linked documents carry, and labels it as our calculation rather than the ledger's. For Xero there is no link to compute from: any PO-to-bill pairing is ours, proposed and confirmed by the user.

## Canonical shape

```ts
type DocumentLine = {
  id: string;
  /** NetSuite expense vs item sublist; QBO's two DetailTypes; Xero's one shape. */
  kind: "account" | "item";
  description?: string;
  quantity?: string;
  unitAmount?: Money;
  accountId?: string;        // resolved even when the source only stored an item
  itemId?: string;
  net: Money;
  tax: TaxBreakdown;         // never a bare scalar
  dimensions: LineDimension[];
  billable?: { customerId: string; status: "billable" | "billed" | "not_billable" };
  source: SourceRef;
};
```
