# SalesInvoiceRow

One line on a sales invoice: a label, a quantity, a unit price and a VAT percentage. An invoice's subtotal, VAT total and total are computed from nothing but its rows, and a row that settles budget hours also records the minutes it covers.

## Used by
- GET /api/v2/sales-invoice-rows/{sales_invoice_row}: Read an invoice line (SalesInvoiceRow)
- PATCH /api/v2/sales-invoice-rows/{sales_invoice_row}: Update an invoice line (SalesInvoiceRow)
- GET /api/v2/sales-invoices/{sales_invoice}/rows: List an invoice's lines (SalesInvoiceRowList)
- POST /api/v2/sales-invoices/{sales_invoice}/rows: Add a line to an invoice (SalesInvoiceRow)

## SalesInvoiceRow fields
- id (integer, required): Unique identifier.
- sales_invoice_id (integer, required, server-assigned): The invoice this line belongs to. Taken from the path on create and never writable afterwards: a line cannot be moved to another invoice.
- sales_invoice (Ref or SalesInvoiceSummary, required): The invoice this line belongs to.
- subscription_item_id (integer or null, required): Subscription line this invoice line was generated from, when the row came out of a subscription renewal rather than being typed by hand.
- budget_id (integer or null, required): Project budget this invoice line settles, when the row was generated from budget hours.
- exact_item_id (string or null ≤ 255, required): Exact Online article identifier this line bills against. Empty is accepted, but a row without it fails the whole invoice's Exact sync.
- exact_vat_code (string or null ≤ 255, required): Exact Online VAT code, by code rather than by id.
- exact_cost_center (string or null ≤ 255, required): Exact Online cost-centre code.
- exact_gl_account (string or null ≤ 255, required): Exact Online general-ledger revenue account for this line.
- name (string ≤ 255, required): The line label, as printed on the invoice. Exact Online prints this when description is empty.
- description (string or null, required, multiline): Longer line text. Exact Online prints this in preference to name.
- quantity (decimal (2 dp), required): Number of units billed. Fractional and signed: a budget row carries hours, and a credit line carries a negative quantity.
- unit_price (decimal (2 dp), required): Price per unit, excluding VAT. This is what actually bills.
- hourly_rate (decimal (2 dp) or null, required): Rate plnnrly recomputes `unit_price` from when the aggregated-price toggle is switched on a budget row. Stored exactly as sent and `null` when omitted; it feeds no server-side computation.
- billable_minutes (integer, required): Minutes of budget time this line settles. Zero on a hand-typed row.
- is_aggregated_price (boolean, required): True: one line of quantity 1 priced at hours times rate. False: quantity carries the hours and `unit_price` the hourly rate.
- vat_rate (integer, required): VAT percentage as a whole number: 21 means 21%.
- vat_amount (decimal (2 dp), required, server-assigned): VAT on this line: quantity times `unit_price` times `vat_rate`, rounded to two decimals. Recomputed by the server whenever one of those three changes.
- total_amount (decimal (2 dp), required, server-assigned): Line total excluding VAT (quantity times `unit_price`), which is the convention the invoice form writes. Recomputed by the server whenever quantity or `unit_price` changes.
- sort_order (integer, required, server-assigned): Position of the line within its invoice, and the default sort key. Assigned on create; there is no way to reorder lines through the API.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- deleted_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Soft-delete timestamp, `null` while live.
- links (SalesInvoiceRowLinks, required)
  - self (url, required): The canonical URI of this record.

## Variants

### SalesInvoiceRowList

What a collection returns, and what `?view=summary` returns on a single record.

24 fields.
Identical to the detail object above.

### SalesInvoiceRowSummary

The embedded form: what you get when another resource carries this one and you did not `include` it.

3 fields.
Omits: sales_invoice_id, sales_invoice, subscription_item_id, budget_id, exact_item_id, exact_vat_code, exact_cost_center, exact_gl_account, description, quantity, unit_price, hourly_rate, billable_minutes, is_aggregated_price, vat_rate, vat_amount, sort_order, created_at, updated_at, deleted_at, links

### SalesInvoiceRowLinks

The `links` object on this resource.

Keys: self

## Example
```json
{
  "id": 1,
  "sales_invoice_id": 1,
  "sales_invoice": {
    "id": 1
  },
  "subscription_item_id": 1,
  "budget_id": 1,
  "exact_item_id": "…",
  "exact_vat_code": "…",
  "exact_cost_center": "…",
  "exact_gl_account": "…",
  "name": "Sprint 42: development",
  "description": "Feature work on the customer portal.",
  "quantity": "12.50",
  "unit_price": "95.00",
  "hourly_rate": "1250.00",
  "billable_minutes": 1,
  "is_aggregated_price": false,
  "vat_rate": 1,
  "vat_amount": "1250.00",
  "total_amount": "1250.00",
  "sort_order": 1,
  "created_at": "2026-03-04T09:15:00Z",
  "updated_at": "2026-03-04T09:15:00Z",
  "deleted_at": "2026-03-04T09:15:00Z",
  "links": {
    "self": "https://<your-url>/api/v2/work-items/1"
  }
}
```
