quickbooks_types/models/
item.rs

1use chrono::NaiveDate;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5use super::common::{MetaData, NtRef};
6#[cfg(feature = "builder")]
7use crate::error::QBTypeError;
8use crate::{QBCreatable, QBFullUpdatable, QBItem};
9
10#[skip_serializing_none]
11#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
12#[serde(rename_all = "PascalCase", default)]
13#[cfg_attr(
14    feature = "builder",
15    derive(Builder),
16    builder(default, build_fn(error = "QBTypeError"), setter(into, strip_option))
17)]
18/// Item
19///
20/// Represents a product or service that can be purchased or sold. Items determine posting accounts (income, expense, inventory) and pricing.
21///
22/// Creation requirements:
23/// - `QBCreatable::can_create()` returns true when:
24///   - `name` and `expense_account_ref` are present, and
25///   - for `ItemType::Inventory`: `income_account_ref`, `asset_account_ref`, `inv_start_date`, and `qty_on_hand` are present
26///   - for `ItemType::Service`: `income_account_ref` is present
27///   - for `ItemType::NonInventory`: no additional fields beyond `name` and `expense_account_ref`
28///   - if `item_type` is `None`: `asset_account_ref` must be present
29///
30/// Update semantics:
31/// - `QBFullUpdatable::can_full_update()` returns true when `has_read()` is true (ID + sync token) and `name` is present.
32///
33/// API reference:
34/// <https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/item>
35pub struct Item {
36    pub id: Option<String>,
37    pub sync_token: Option<String>,
38    #[serde(skip_serializing)]
39    pub meta_data: Option<MetaData>,
40
41    /// If true, the object is currently enabled for use by `QuickBooks`.
42    pub active: Option<bool>,
43
44    /// Reference to the Inventory Asset account that tracks the current value of the inventory.
45    /// If the same account is used for all inventory items, the current balance of this account will represent the current total value of the inventory.
46    /// Query the Account name list resource to determine the appropriate Account object for this reference.
47    /// Use `Account.id` and `Account.name` from that object for `AssetAccountRef.value` and `AssetAccountRef.name`, respectively.
48    ///
49    /// Required for Inventory item types.
50    pub asset_account_ref: Option<NtRef>,
51
52    /// Description of the item.
53    ///
54    /// * max character: maximum of 4000 chars
55    pub description: Option<String>,
56
57    /// Documentation unavailable.
58    #[serde(rename = "domain")]
59    pub domain: Option<String>,
60
61    /// Reference to the expense account used to pay the vendor for this item.
62    /// Must be an account with account type of Cost of Goods Sold.
63    /// Query the Account name list resource to determine the appropriate Account object for this reference.
64    /// Use `Account.id` and `Account.name` from that object for `ExpenseAccountRef.value` and `ExpenseAccountRef.name`, respectively.
65    ///
66    /// For France locales:
67    /// * This is an optional field.
68    /// * This is the purchase account id, If not provided it defaults to the default purchase account: 605100 and 601100 are the default expense accounts used for Service and Product type of item, respectively.
69    ///
70    /// Required for Inventory, `NonInventory`, and Service item types
71    pub expense_account_ref: Option<NtRef>,
72
73    /// Fully qualified name of the entity.
74    /// The fully qualified name prepends the topmost parent, followed by each sub element separated by colons.
75    /// Takes the form of Item:SubItem.
76    /// Returned from an existing object and not input on a new object.
77    /// Limited to 5 levels.
78    ///
79    /// * filterable
80    /// * read only
81    /// * system defined
82    pub fully_qualified_name: Option<String>,
83    pub income_account_ref: Option<NtRef>,
84    pub inv_start_date: Option<NaiveDate>,
85    pub sales_tax_included: Option<bool>,
86    pub sales_tax_code_ref: Option<NtRef>,
87    pub class_ref: Option<NtRef>,
88    pub source: Option<String>,
89    pub purchase_tax_included: Option<bool>,
90    pub reorder_point: Option<f64>,
91    pub purchase_dec: Option<String>,
92    pub pref_vendor_ref: Option<NtRef>,
93    pub purchase_tax_code_ref: Option<NtRef>,
94    pub purchase_cost: Option<f64>,
95    pub parent_ref: Option<NtRef>,
96    pub tax_classification_ref: Option<NtRef>,
97
98    /// Classification that specifies the use of this item.
99    /// Available when endpoint is evoked with the minorversion=3 query parameter.
100    /// Read-only after object is created.
101    /// Valid values include: Product and Service.
102    ///
103    /// Applicable for France companies only.
104    pub item_category_type: Option<String>,
105    #[serde(rename = "Type")]
106    pub item_type: Option<ItemType>,
107    pub level: Option<i64>,
108    pub name: Option<String>,
109    pub purchase_desc: Option<String>,
110    pub qty_on_hand: Option<i64>,
111    pub sku: Option<String>,
112    #[serde(rename = "sparse")]
113    pub sparse: Option<bool>,
114    pub sub_item: Option<bool>,
115    pub taxable: Option<bool>,
116    pub track_qty_on_hand: Option<bool>,
117    pub unit_price: Option<f64>,
118}
119
120/// Item Type
121///
122/// Classification that specifies whether the item is an inventory item, a service item, or a non-inventory item.
123#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
124pub enum ItemType {
125    Inventory,
126    Service,
127    #[default]
128    NonInventory,
129}
130
131impl QBCreatable for Item {
132    fn can_create(&self) -> bool {
133        self.name.is_some()
134            && self.expense_account_ref.is_some()
135            && match self.item_type.as_ref() {
136                Some(typ) => match *typ {
137                    ItemType::Inventory => {
138                        self.income_account_ref.is_some()
139                            && self.asset_account_ref.is_some()
140                            && self.inv_start_date.is_some()
141                            && self.qty_on_hand.is_some()
142                    }
143                    ItemType::Service => self.income_account_ref.is_some(),
144                    ItemType::NonInventory => true,
145                },
146                None => self.asset_account_ref.is_some(),
147            }
148    }
149}
150
151impl QBFullUpdatable for Item {
152    fn can_full_update(&self) -> bool {
153        self.has_read() && self.name.is_some()
154    }
155}