quickbooks_types/models/
estimate.rs

1use chrono::NaiveDate;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5use super::common::{Addr, CustomField, Email, LinkedTxn, MetaData, NtRef, TxnTaxDetail};
6#[cfg(feature = "builder")]
7use crate::error::QBTypeError;
8use crate::{
9    common::EmailStatus, LineField, QBCreatable, QBDeletable, QBFullUpdatable, QBItem, QBPDFable,
10    QBSendable, QBSparseUpdateable,
11};
12
13#[skip_serializing_none]
14#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, Default)]
15#[serde(rename_all = "PascalCase", default)]
16#[cfg_attr(
17    feature = "builder",
18    derive(Builder),
19    builder(default, build_fn(error = "QBTypeError"), setter(into, strip_option))
20)]
21
22/// Estimate
23///
24/// Represents a proposal for a financial transaction between a business and its customer.
25/// It outlines proposed goods or services and their costs, which may later become an invoice.
26///
27/// Creation requirements:
28/// - `QBCreatable::can_create()` returns true when both `customer_ref` and at least one valid line are present.
29///
30/// Update semantics:
31/// - `QBFullUpdatable::can_full_update()` requires `has_read()` (ID + sync token) and `customer_ref`.
32///   If `email_status` is `EmailStatus::NeedToSend`, `bill_email` must also be set.
33///
34/// API reference:
35/// <https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/estimate>
36pub struct Estimate {
37    /// The unique ID of the entity
38    pub id: Option<String>,
39    /// The unique sync token of the entity, used for concurrency control
40    pub sync_token: Option<String>,
41    /// Metadata about the entity
42    #[serde(skip_serializing)]
43    pub meta_data: Option<MetaData>,
44    /// Reference to the customer for the estimate
45    pub customer_ref: Option<NtRef>,
46    /// Reference to the currency for the estimate
47    pub currency_ref: Option<NtRef>,
48    /// Email address where the estimate should be sent
49    pub bill_email: Option<Email>,
50    /// Date of the estimate in YYYY-MM-DD format
51    pub txn_date: Option<NaiveDate>,
52    /// Address information for where items are shipped from
53    pub ship_from_addr: Option<Addr>,
54    /// Date the items are expected to ship
55    pub ship_date: Option<NaiveDate>,
56    /// Reference to the class for the estimate
57    pub class_ref: Option<NtRef>,
58    /// Custom fields for the estimate
59    pub custom_field: Option<Vec<CustomField>>,
60    /// Status indicating whether the estimate has been printed
61    pub print_status: Option<String>,
62    /// Indicates if the entity is a sparse object
63    #[serde(rename = "sparse")]
64    pub sparse: Option<bool>,
65    /// Reference to the sales terms for the estimate
66    pub sales_term_ref: Option<NtRef>,
67    /// Status of the transaction (e.g., "Pending", "Accepted")
68    pub txn_status: Option<String>,
69    /// Global tax calculation method
70    pub global_tax_calculation: Option<String>,
71    /// Date when the estimate was accepted by the customer
72    pub accepted_date: Option<NaiveDate>,
73    /// Date when the estimate expires
74    pub expiration_date: Option<NaiveDate>,
75    /// Due date for the estimate
76    pub due_date: Option<NaiveDate>,
77    /// Document number for the estimate
78    pub doc_number: Option<String>,
79    /// Private note for the estimate (not visible to customers)
80    pub private_note: Option<String>,
81    /// Customer memo for the estimate
82    pub customer_memo: Option<NtRef>,
83    /// Status indicating whether the estimate has been emailed
84    pub email_status: Option<EmailStatus>,
85    /// Tax details for the transaction
86    pub txn_tax_detail: Option<TxnTaxDetail>,
87    /// Line items for the estimate
88    pub line: Option<LineField>,
89    /// Information about transactions linked to this estimate
90    pub linked_txn: Option<Vec<LinkedTxn>>,
91    /// Name of the person who accepted the estimate
92    pub accepted_by: Option<String>,
93    /// Exchange rate for the currency
94    pub exchange_rate: Option<f64>,
95    /// Shipping address for the estimate
96    pub ship_addr: Option<Addr>,
97    /// Reference to the department for the estimate
98    pub department_ref: Option<NtRef>,
99    /// Reference to the shipping method for the estimate
100    pub ship_method_ref: Option<NtRef>,
101    /// Billing address for the estimate
102    pub bill_addr: Option<Addr>,
103    /// Indicates if tax should be applied after discount
104    pub apply_tax_after_discount: Option<bool>,
105    /// Total amount of the estimate
106    pub total_amt: Option<f64>,
107    /// Reference to recurring data for the estimate
108    pub recur_data_ref: Option<NtRef>,
109    /// Reference to tax exemption information
110    pub tax_exemption_ref: Option<NtRef>,
111    /// Total amount in home currency
112    pub home_total_amt: Option<f64>,
113    /// Indicates if the address is free-form (not structured)
114    pub free_form_address: Option<bool>,
115}
116
117impl QBCreatable for Estimate {
118    fn can_create(&self) -> bool {
119        self.line.is_some() && self.customer_ref.is_some()
120    }
121}
122
123impl QBDeletable for Estimate {}
124
125impl QBFullUpdatable for Estimate {
126    fn can_full_update(&self) -> bool {
127        if !self.has_read() || self.customer_ref.is_none() {
128            false
129        } else if let Some(EmailStatus::NeedToSend) = self.email_status.as_ref() {
130            self.bill_email.is_some()
131        } else {
132            true
133        }
134    }
135}
136
137impl QBSparseUpdateable for Estimate {
138    fn can_sparse_update(&self) -> bool {
139        self.can_full_update()
140    }
141}
142
143impl QBSendable for Estimate {}
144impl QBPDFable for Estimate {}