1use crate::client::SquareClient;
6use crate::api::{Verb, SquareAPI};
7use crate::errors::{InventoryChangeBodyBuildError, SquareError, ValidationError};
8use crate::response::SquareResponse;
9use crate::objects::{CatalogObject, InventoryChange, InventoryPhysicalCount,
10 InventoryTransfer};
11use crate::objects::enums::{InventoryChangeType, InventoryState};
12
13use serde::{Deserialize, Serialize};
14use uuid::Uuid;
15use square_ox_derive::Builder;
16use crate::builder::{AddField, Builder, ParentBuilder, Validate, Buildable};
17
18
19impl SquareClient {
20 pub fn inventory(&self) -> Inventory {
41 Inventory {
42 client: &self,
43 }
44 }
45}
46
47pub struct Inventory<'a> {
50 client: &'a SquareClient
51}
52
53impl<'a> Inventory<'a> {
54
55 pub async fn batch_change(self, body: InventoryChangeBody)
58 -> Result<SquareResponse, SquareError>{
59 self.client.request(
60 Verb::POST,
61 SquareAPI::Inventory("/changes/batch-create".to_string()),
62 Some(&body),
63 None,
64 ).await
65 }
66
67 pub async fn retrieve_count(self, object_id: String, location_id: Option<String>)
71 -> Result<SquareResponse, SquareError>{
72 let parameters = match location_id {
73 Some(location_id) => Some(vec![("location_id".to_string(), location_id)]),
74 None => None
75 };
76
77 self.client.request(
78 Verb::GET,
79 SquareAPI::Inventory(format!("/{}", object_id)),
80 None::<&CatalogObject>,
81 parameters,
82 ).await
83 }
84
85 pub async fn retrieve_adjustment(self, adjustment_id: String)
88 -> Result<SquareResponse, SquareError>{
89 self.client.request(
90 Verb::GET,
91 SquareAPI::Inventory(format!("/adjustments/{}", adjustment_id)),
92 None::<&CatalogObject>,
93 None,
94 ).await
95 }
96
97 pub async fn retrieve_transfer(self, transfer_id: String)
100 -> Result<SquareResponse, SquareError>{
101 self.client.request(
102 Verb::GET,
103 SquareAPI::Inventory(format!("/transfer/{}", transfer_id)),
104 None::<&CatalogObject>,
105 None,
106 ).await
107 }
108
109 pub async fn retrieve_physical_count(self, physical_count_id: String)
112 -> Result<SquareResponse, SquareError>{
113 self.client.request(
114 Verb::GET,
115 SquareAPI::Inventory(format!("/physical-counts/{}", physical_count_id)),
116 None::<&CatalogObject>,
117 None,
118 ).await
119 }
120
121 pub async fn batch_retrieve_counts(self, body: BatchRetrieveCounts)
125 -> Result<SquareResponse, SquareError>{
126 self.client.request(
127 Verb::POST,
128 SquareAPI::Inventory("/counts/batch-retrieve".to_string()),
129 Some(&body),
130 None,
131 ).await
132 }
133}
134
135#[derive(Clone, Debug, Serialize, Deserialize, Default, Builder)]
139pub struct InventoryChangeBody {
140 #[builder_rand("uuid")]
141 idempotency_key: Option<String>,
142 #[builder_validate("len")]
143 changes: Vec<InventoryChange>,
144 ignore_unchanged_counts: Option<bool>,
145}
146
147impl AddField<InventoryChange> for InventoryChangeBody {
148 fn add_field(&mut self, field: InventoryChange) {
149 self.changes.push(field);
150 }
151}
152
153
154#[derive(Clone, Debug, Serialize, Deserialize, Default)]
158pub struct BatchRetrieveCounts {
159 catalog_object_ids: Vec<String>,
160 cursor: Option<String>,
161 limit: Option<i32>,
162 location_ids: Vec<String>,
163 states: Option<Vec<InventoryState>>,
164 updated_after: Option<String>,
165}
166
167impl Validate for BatchRetrieveCounts {
168 fn validate(self) -> Result<Self, ValidationError> where Self: Sized {
169 if self.location_ids.len() > 0 && self.catalog_object_ids.len() > 0 {
170 Ok(self)
171 } else {
172 Err(ValidationError)
173 }
174 }
175}
176
177impl<T: ParentBuilder> Builder<BatchRetrieveCounts, T> {
178 pub fn object_ids(mut self, ids: Vec<String>) -> Self {
179 self.body.catalog_object_ids = ids;
180
181 self
182 }
183
184 pub fn location_ids(mut self, ids: Vec<String>) -> Self {
185 self.body.location_ids = ids;
186
187 self
188 }
189
190 pub fn add_location_id(mut self, id: String) -> Self {
191 self.body.location_ids.push(id);
192
193 self
194 }
195}
196
197#[cfg(test)]
198mod test_inventory {
199 use crate::builder::BackIntoBuilder;
200 use crate::objects::enums::InventoryState;
201 use super::*;
202
203 #[tokio::test]
204 async fn test_retrieve_count() {
205 use dotenv::dotenv;
206 use std::env;
207
208 dotenv().ok();
209 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
210 let sut = SquareClient::new(&access_token);
211
212 let input = (
213 "DUHTRFG3OEYAXG5I22XLFN23".to_string(),
214 "L1JC53TYHS40Z".to_string()
215 );
216
217 let res = sut.inventory()
218 .retrieve_count(input.0, Some(input.1))
219 .await;
220
221 assert!(res.is_ok())
222 }
223
224 #[tokio::test]
225 async fn test_change_body_builder() {
226 let expected = InventoryChangeBody {
227 idempotency_key: None,
228 changes: vec![
229 InventoryChange {
230 adjustment: None,
231 measurement_unit: None,
232 measurement_unit_id: None,
233 physical_count: Some(InventoryPhysicalCount {
234 id: None,
235 catalog_object_id: "".to_string(),
236 catalog_object_type: None,
237 created_at: None,
238 location_id: "L1JC53TYHS40Z".to_string(),
239 occurred_at: "2022-07-09T12:25:34Z".to_string(),
240 quantity: "30".to_string(),
241 reference_id: None,
242 source: None,
243 state: InventoryState::InStock,
244 team_member_id: None
245 }),
246 transfer: None,
247 inventory_change_type: InventoryChangeType::PhysicalCount
248 }
249 ],
250 ignore_unchanged_counts: None
251 };
252
253 let mut actual = Builder::from(InventoryChangeBody::default())
254 .sub_builder_from(InventoryChange::default())
255 .change_type(InventoryChangeType::PhysicalCount)
256 .sub_builder_from(InventoryPhysicalCount::default())
257 .catalog_object_id("".to_string())
258 .location_id("L1JC53TYHS40Z".to_string())
259 .occurred_at("2022-07-09T12:25:34Z".to_string())
260 .quantity("30".to_string())
261 .state(InventoryState::InStock)
262 .build()
263 .unwrap()
264 .build()
265 .unwrap()
266 .build()
267 .unwrap();
268
269 assert!(actual.idempotency_key.is_some());
270
271 actual.idempotency_key = None;
272
273 assert_eq!(format!("{:?}",expected), format!("{:?}",actual));
274 }
275
276 async fn test_batch_change() {
278 use dotenv::dotenv;
279 use std::env;
280
281 dotenv().ok();
282 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
283 let sut = SquareClient::new(&access_token);
284
285 let input = InventoryChangeBody {
286 idempotency_key: Some(Uuid::new_v4().to_string()),
287 changes: vec![
288 InventoryChange {
289 adjustment: None,
290 measurement_unit: None,
291 measurement_unit_id: None,
292 physical_count: Some(InventoryPhysicalCount {
293 id: None,
294 catalog_object_id: "DUHTRFG3OEYAXG5I22XLFN23".to_string(),
295 catalog_object_type: None,
296 created_at: None,
297 location_id: "L1JC53TYHS40Z".to_string(),
298 occurred_at: "2022-07-09T12:25:34Z".to_string(),
299 quantity: "30".to_string(),
300 reference_id: None,
301 source: None,
302 state: InventoryState::InStock,
303 team_member_id: None
304 }),
305 transfer: None,
306 inventory_change_type: InventoryChangeType::PhysicalCount
307 }
308 ],
309 ignore_unchanged_counts: None
310 };
311
312 let res = sut.inventory()
313 .batch_change(input)
314 .await;
315
316 assert!(res.is_ok())
317 }
318}