quickbooks-types 0.1.1

Type definitions for QuickBooks Online API
Documentation
# QuickBooks Online Types for Rust


The `quickbooks-types` crate provides strongly typed Rust models for QuickBooks Online (QBO) API entities and report types. It focuses on data structures, validation helpers, and report parameter builders. Network access and API calls are intentionally out of scope—use your preferred HTTP client alongside these types.

- Crate: quickbooks-types
- Version: 0.1.1
- License: MIT

QuickBooks API docs: https://developer.intuit.com/app/developer/qbo/docs/get-started

---

## Status and scope


This crate:

- Defines data models for common QBO entities (e.g., Invoice, Customer, Vendor, Item, etc.)
- Provides traits that capture operation preconditions such as can_create, can_full_update, can_sparse_update, etc.
- Includes helper types for lines, references, taxes, contact info, and metadata
- Implements a reports module with rich report parameter builders and types
- Offers optional ergonomics via a builder feature, and an optional reports+Polars integration feature

This crate does not:

- Make HTTP requests
- Provide an async runtime or HTTP client
- Offer a general SQL-like query builder for QBO entities

---

## Installation


~~~toml
[dependencies]
quickbooks-types = "0.1.1"
~~~

Optional features:

- builder: derive Builder types and add convenience constructors for top-level entities
- polars: enable report parsing helpers that integrate with Polars (exposes `reports::QBPolarsError`)

Examples:

~~~toml
[dependencies]
quickbooks-types = { version = "0.1.1", features = ["builder"] }
# or

quickbooks-types = { version = "0.1.1", features = ["polars"] }
~~~

---

## Modules and re-exports


- Entities re-exported at crate root:
  - Account, Attachable, Bill, BillPayment, CompanyInfo, Customer, Employee, Estimate, Invoice, Item, Payment, Preferences, SalesReceipt, Vendor
- Nested modules:
  - `common`: shared value types (e.g., `NtRef`, `MetaData`, contact and address types, taxes)
  - `reports`: report data structures and parameter builders
    - `reports::types`: report enum-like structs and `<Report>Params`
    - `reports::params`: reusable parameter enums and typed IDs
    - `reports::models`: report models (e.g., `Report`, `Row`, `ColData`)
    - `reports::polars` (feature = "polars"): Polars integration

---

## Quick start: Entities


Basic construction and serialization of an invoice:

~~~rust
use chrono::NaiveDate;
use serde_json;
use quickbooks_types::{Invoice, Line, LineDetail, SalesItemLineDetail};
use quickbooks_types::common::NtRef;

// minimal invoice with one sales item line
let invoice = Invoice {
    customer_ref: Some(NtRef::from(("John Doe", "CUST-123"))),
    txn_date: NaiveDate::from_ymd_opt(2024, 10, 1),
    line: Some(vec![
        Line {
            amount: Some(100.0),
            line_detail: LineDetail::SalesItemLineDetail(SalesItemLineDetail {
                item_ref: Some(NtRef::from(("Widget A", "ITEM-001"))),
                qty: Some(1.0),
                unit_price: Some(100.0),
                ..Default::default()
            }),
            ..Default::default()
        }
    ]),
    ..Default::default()
};

let json = serde_json::to_string_pretty(&invoice).unwrap();
println!("{json}");
~~~

Operation checks (traits):

~~~rust
use quickbooks_types::{Invoice, QBCreatable, QBReadable, QBDeletable, QBFullUpdatable};

let mut invoice = Invoice::default();
assert!(!invoice.can_create()); // missing required fields (customer_ref + line)

invoice.customer_ref = Some(("John Doe", "CUST-123").into());
invoice.line = Some(vec![]);
assert!(!invoice.can_create()); // line must be a non-empty collection with amounts

// simulate entity read from QBO
invoice.id = Some("123".into());
invoice.sync_token = Some("2".into());
assert!(invoice.can_read());          // ID is set
assert!(!invoice.can_full_update());  // still fails creation rules

// deletion requires "has_read" (ID + sync_token)
assert!(invoice.can_delete());
~~~

Convert entities to references (`NtRef`):

~~~rust
use quickbooks_types::{Customer, QBToRef};

let mut customer = Customer::default();
customer.id = Some("CUST-123".into());
customer.display_name = Some("John Doe".into());

let customer_ref = customer.to_ref().unwrap();
// customer_ref.name == Some("John Doe"), customer_ref.value == Some("CUST-123")
~~~

---

## Builder feature


Enable the `builder` feature to get derived builders for most entity types and a convenience `::new()` associated function for top-level entities (e.g., `Invoice::new()` returns an `InvoiceBuilder`).

~~~toml
[dependencies]
quickbooks-types = { version = "0.1.1", features = ["builder"] }
~~~

~~~rust
use chrono::NaiveDate;
use quickbooks_types::common::NtRef;
use quickbooks_types::{Invoice, LineDetail, QBCreatable};
use quickbooks_types::{LineBuilder, SalesItemLineDetailBuilder};

let invoice = Invoice::new()
    .customer_ref(NtRef::from(("John Doe", "CUST-123")))
    .txn_date(NaiveDate::from_ymd_opt(2024, 10, 1).unwrap())
    .line(vec![
        LineBuilder::default()
            .amount(100.0)
            .line_detail(LineDetail::SalesItemLineDetail(
                SalesItemLineDetailBuilder::default()
                    .item_ref(NtRef::from(("Widget A", "ITEM-001")))
                    .qty(1.0)
                    .unit_price(100.0)
                    .build()
                    .unwrap(),
            ))
            .build()
            .unwrap(),
    ])
    .build()
    .unwrap();

assert!(invoice.can_create());
~~~

Attachable convenience (builder):

~~~rust
use quickbooks_types::Attachable;

#[cfg(feature = "builder")]

{
    use quickbooks_types::AttachableBuilder;

    // AttachableBuilder::file(...) sets file_path, derives file_name and content_type from extension
    let attachable = Attachable::new()
        .note("Invoice backup")
        // .file(&std::path::Path::new("docs/invoice.pdf"))?
        .build();

    assert!(attachable.is_ok());
}
~~~

---

## Reports


The `reports` module provides:

- Richly-typed report models: `reports::models::{Report, Row, ColData, ...}`
- Strongly-typed parameter builders per report: `reports::types::<Report>Params`
- Common enums and ID wrappers for parameters: `reports::params::*`

Build a query string for a report:

~~~rust
use chrono::NaiveDate;
use quickbooks_types::reports::types::*;
use quickbooks_types::reports::params::*;

let balance_sheet = BalanceSheetParams::new()
    .accounting_method(AccountingMethod::Cash)
    .start_date(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap())
    .end_date(NaiveDate::from_ymd_opt(2024, 12, 31).unwrap())
    .date_macro(DateMacro::ThisFiscalYear)
    .summarize_column_by(SummarizeColumnBy::Month);

println!("{}", balance_sheet.to_query_string());
// -> accounting_method=Cash&start_date=2024-01-01&end_date=2024-12-31&date_macro=This Fiscal Year&summarize_column_by=Month
~~~

Another example:

~~~rust
use chrono::NaiveDate;
use quickbooks_types::reports::types::*;
use quickbooks_types::reports::params::*;

let ap_aging = APAgingDetailParams::new()
    .as_of_date(NaiveDate::from_ymd_opt(2024, 6, 30).unwrap())
    .aging_method(AgingMethod::Current)
    .vendor(VendorId(789))
    .column("Name")
    .column("DueDate")
    .column("Amount");

println!("{}", ap_aging.to_query_string());
~~~

Parsing a report result:

~~~rust
use quickbooks_types::reports::models::Report;

let json = r#"{
  "Header": { "ReportName": "BalanceSheet" },
  "Columns": { "Column": [ { "ColTitle": "Total", "ColType": "Money" } ] },
  "Rows": { "Row": [] }
}"#;

let report: Report = serde_json::from_str(json).unwrap();
assert_eq!(report.name(), Some("BalanceSheet"));
~~~

Polars integration (feature = "polars"):

- Enables `reports::polars` helpers and `reports::QBPolarsError`
- Provide your own conversion to Polars DataFrames depending on your use case

---

## Entities included


Top-level entities for which `QBItem` is implemented and a `Display` impl is provided:

- Account, Attachable, Bill, BillPayment, CompanyInfo, Customer, Employee, Estimate, Invoice, Item, Payment, Preferences, SalesReceipt, Vendor

Supporting value types (non-exhaustive):

- `common`: `NtRef`, `MetaData`, `Addr`, `Email`, `PhoneNumber`, `WebAddr`, `TxnTaxDetail`, `LinkedTxn`, and others
- `line`: `Line`, `LineDetail` and its variants (e.g., `SalesItemLineDetail`, `SubTotalLineDetail`, etc.), `LineField` (alias for `Vec<Line>`)

Operation traits (selected):

- `QBCreatable`: `can_create(&self) -> bool` – entity meets required fields for creation
- `QBReadable`: `can_read(&self) -> bool` – true if ID is present
- `QBDeletable`: `can_delete(&self) -> bool` – default requires both ID and sync_token
- `QBFullUpdatable`: `can_full_update(&self) -> bool` – type-specific requirements
- `QBSparseUpdateable`: `can_sparse_update(&self) -> bool` – typically `can_full_update()` + `sparse == true`
- `QBVoidable`: `can_void(&self) -> bool` – default requires both ID and sync_token
- `QBSendable` / `QBPDFable`: marker traits for entities that can be emailed / retrieved as PDF in QBO
- `QBToRef`: `to_ref(&self) -> Result<NtRef, QBTypeError>` – convert to reference usable in other entities

Note: These methods validate local preconditions only; they do not contact QBO.

---

## Errors


~~~rust
use quickbooks_types::QBTypeError;

#[derive(Debug, thiserror::Error)]

pub enum QBTypeError {
    #[cfg(feature = "builder")]
    #[error("Error validating QB Object in builder: {0}")]
    ValidationError(String),

    #[error("Missing field in QB Object: {0}")]
    MissingField(&'static str),

    #[error("QB Item could not be referenced!")]
    QBToRefError,
}
~~~

---

## Tips


- IDs and `sync_token` are required for delete and void operations (`has_read()` check)
- Builders (feature = "builder") return `Result<_, QBTypeError>` on `build()`
- Some enums contain placeholder or minimal variants where the QBO surface is large; open an issue or PR to extend as needed

---

## Contributing


Issues and PRs are welcome. If you need additional entities, fields, or extended report handling, feel free to propose a change.

---

## License


MIT. See the LICENSE file for details.