files_sdk/admin/
invoices.rs

1//! Invoice operations
2//!
3//! Invoices represent billing line items for your Files.com account.
4
5use crate::{FilesClient, PaginationInfo, Result};
6use serde::{Deserialize, Serialize};
7
8/// An Invoice Line Item entity
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct InvoiceLineItemEntity {
11    /// Line item ID
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub id: Option<i64>,
14
15    /// Amount
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub amount: Option<f64>,
18
19    /// Created at
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub created_at: Option<String>,
22
23    /// Description
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub description: Option<String>,
26
27    /// Type (e.g., "invoice")
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub r#type: Option<String>,
30
31    /// Service end date
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub service_end_at: Option<String>,
34
35    /// Service start date
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub service_start_at: Option<String>,
38
39    /// Plan name
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub plan: Option<String>,
42
43    /// Site name
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub site: Option<String>,
46}
47
48/// An Account Line Item entity (Invoice)
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct AccountLineItemEntity {
51    /// Line item ID
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub id: Option<i64>,
54
55    /// Amount
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub amount: Option<f64>,
58
59    /// Balance
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub balance: Option<f64>,
62
63    /// Created at
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub created_at: Option<String>,
66
67    /// Currency (e.g., "USD")
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub currency: Option<String>,
70
71    /// Download URI
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub download_uri: Option<String>,
74
75    /// Associated invoice line items
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub invoice_line_items: Option<Vec<InvoiceLineItemEntity>>,
78}
79
80/// Handler for invoice operations
81pub struct InvoiceHandler {
82    client: FilesClient,
83}
84
85impl InvoiceHandler {
86    /// Create a new invoice handler
87    pub fn new(client: FilesClient) -> Self {
88        Self { client }
89    }
90
91    /// List invoices
92    ///
93    /// # Arguments
94    /// * `cursor` - Pagination cursor
95    /// * `per_page` - Results per page
96    ///
97    /// # Returns
98    /// Tuple of (invoices, pagination_info)
99    ///
100    /// # Example
101    /// ```no_run
102    /// use files_sdk::{FilesClient, InvoiceHandler};
103    ///
104    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
105    /// let client = FilesClient::builder().api_key("key").build()?;
106    /// let handler = InvoiceHandler::new(client);
107    /// let (invoices, _) = handler.list(None, None).await?;
108    /// # Ok(())
109    /// # }
110    /// ```
111    pub async fn list(
112        &self,
113        cursor: Option<&str>,
114        per_page: Option<i64>,
115    ) -> Result<(Vec<AccountLineItemEntity>, PaginationInfo)> {
116        let mut params = vec![];
117        if let Some(c) = cursor {
118            params.push(("cursor", c.to_string()));
119        }
120        if let Some(pp) = per_page {
121            params.push(("per_page", pp.to_string()));
122        }
123
124        let query = if params.is_empty() {
125            String::new()
126        } else {
127            format!(
128                "?{}",
129                params
130                    .iter()
131                    .map(|(k, v)| format!("{}={}", k, v))
132                    .collect::<Vec<_>>()
133                    .join("&")
134            )
135        };
136
137        let response = self.client.get_raw(&format!("/invoices{}", query)).await?;
138        let invoices: Vec<AccountLineItemEntity> = serde_json::from_value(response)?;
139
140        let pagination = PaginationInfo {
141            cursor_next: None,
142            cursor_prev: None,
143        };
144
145        Ok((invoices, pagination))
146    }
147
148    /// Get a specific invoice
149    ///
150    /// # Arguments
151    /// * `id` - Invoice ID
152    pub async fn get(&self, id: i64) -> Result<AccountLineItemEntity> {
153        let response = self.client.get_raw(&format!("/invoices/{}", id)).await?;
154        Ok(serde_json::from_value(response)?)
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_handler_creation() {
164        let client = FilesClient::builder().api_key("test-key").build().unwrap();
165        let _handler = InvoiceHandler::new(client);
166    }
167}