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}