1use crate::client::ConcurClient;
6use crate::error::Result;
7use crate::models::{Link, Money, OwnerRef, StatusRef};
8use reqwest::Method;
9use serde::{Deserialize, Serialize};
10
11pub struct RequestApi<'a> {
13 client: &'a ConcurClient,
14}
15
16impl<'a> RequestApi<'a> {
17 pub fn new(client: &'a ConcurClient) -> Self {
18 Self { client }
19 }
20
21 pub async fn list(&self, query: &RequestListQuery) -> Result<RequestList> {
23 let mut path = "/travelrequest/v4/requests".to_string();
24 let mut params: Vec<(String, String)> = Vec::new();
25 if let Some(v) = &query.view {
26 params.push(("view".to_string(), v.clone()));
27 }
28 if let Some(v) = &query.user_id {
29 params.push(("userId".to_string(), v.clone()));
30 }
31 if let Some(v) = query.start {
32 params.push(("start".to_string(), v.to_string()));
33 }
34 if let Some(v) = query.limit {
35 params.push(("limit".to_string(), v.to_string()));
36 }
37 if let Some(v) = &query.approved_before {
38 params.push(("approvedBefore".to_string(), v.clone()));
39 }
40 if let Some(v) = &query.approved_after {
41 params.push(("approvedAfter".to_string(), v.clone()));
42 }
43 if let Some(v) = &query.modified_before {
44 params.push(("modifiedBefore".to_string(), v.clone()));
45 }
46 if let Some(v) = &query.modified_after {
47 params.push(("modifiedAfter".to_string(), v.clone()));
48 }
49 if let Some(v) = &query.sort_field {
50 params.push(("sortField".to_string(), v.clone()));
51 }
52 if let Some(v) = &query.sort_order {
53 params.push(("sortOrder".to_string(), v.clone()));
54 }
55 if !params.is_empty() {
56 path.push('?');
57 path.push_str(
58 ¶ms
59 .iter()
60 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
61 .collect::<Vec<_>>()
62 .join("&"),
63 );
64 }
65 self.client
66 .request(Method::GET, &path)
67 .await?
68 .send()
69 .await?
70 .json()
71 .await
72 .map_err(Into::into)
73 }
74
75 pub async fn get(&self, request_uuid: &str, user_id: Option<&str>) -> Result<Request> {
77 let mut path = format!("/travelrequest/v4/requests/{}", request_uuid);
78 if let Some(uid) = user_id {
79 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
80 }
81 self.client
82 .request(Method::GET, &path)
83 .await?
84 .send()
85 .await?
86 .json()
87 .await
88 .map_err(Into::into)
89 }
90
91 pub async fn create(&self, request: &Request, user_id: Option<&str>) -> Result<Request> {
93 let mut path = "/travelrequest/v4/requests".to_string();
94 if let Some(uid) = user_id {
95 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
96 }
97 let req = self.client.request(Method::POST, &path).await?;
98 let req = ConcurClient::with_json(req, request)?;
99 self.client.execute(req).await
100 }
101
102 pub async fn update(
104 &self,
105 request_uuid: &str,
106 request: &Request,
107 user_id: Option<&str>,
108 ) -> Result<Request> {
109 let mut path = format!("/travelrequest/v4/requests/{}", request_uuid);
110 if let Some(uid) = user_id {
111 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
112 }
113 let req = self.client.request(Method::PUT, &path).await?;
114 let req = ConcurClient::with_json(req, request)?;
115 self.client.execute(req).await
116 }
117
118 pub async fn delete(&self, request_uuid: &str, user_id: Option<&str>) -> Result<()> {
120 let mut path = format!("/travelrequest/v4/requests/{}", request_uuid);
121 if let Some(uid) = user_id {
122 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
123 }
124 self.client
125 .execute_unit(self.client.request(Method::DELETE, &path).await?)
126 .await
127 }
128}
129
130pub struct RequestWorkflowApi<'a> {
132 client: &'a ConcurClient,
133}
134
135impl<'a> RequestWorkflowApi<'a> {
136 pub fn new(client: &'a ConcurClient) -> Self {
137 Self { client }
138 }
139
140 pub async fn action(
142 &self,
143 request_uuid: &str,
144 action: &str,
145 comment: Option<&str>,
146 user_id: Option<&str>,
147 company_id: Option<&str>,
148 ) -> Result<Request> {
149 let mut path = format!("/travelrequest/v4/requests/{}/{}", request_uuid, action);
150 let mut params: Vec<(String, String)> = Vec::new();
151 if let Some(c) = comment {
152 params.push(("comment".to_string(), c.to_string()));
153 }
154 if let Some(uid) = user_id {
155 params.push(("userId".to_string(), uid.to_string()));
156 }
157 if let Some(cid) = company_id {
158 params.push(("companyID".to_string(), cid.to_string()));
159 }
160 if !params.is_empty() {
161 path.push('?');
162 path.push_str(
163 ¶ms
164 .iter()
165 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
166 .collect::<Vec<_>>()
167 .join("&"),
168 );
169 }
170 let body = comment.map(|c| serde_json::json!({ "comment": c }));
171 let req = self.client.request(Method::POST, &path).await?;
172 let req = match body {
173 Some(b) => ConcurClient::with_json(req, &b)?,
174 None => req,
175 };
176 self.client.execute(req).await
177 }
178}
179
180pub struct ExpectedExpensesApi<'a> {
182 client: &'a ConcurClient,
183}
184
185impl<'a> ExpectedExpensesApi<'a> {
186 pub fn new(client: &'a ConcurClient) -> Self {
187 Self { client }
188 }
189
190 pub async fn list(
192 &self,
193 request_uuid: &str,
194 user_id: Option<&str>,
195 ) -> Result<Vec<ExpectedExpense>> {
196 let mut path = format!("/travelrequest/v4/requests/{}/expenses", request_uuid);
197 if let Some(uid) = user_id {
198 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
199 }
200 self.client
201 .request(Method::GET, &path)
202 .await?
203 .send()
204 .await?
205 .json()
206 .await
207 .map_err(Into::into)
208 }
209
210 pub async fn create(
212 &self,
213 request_uuid: &str,
214 expense: &ExpectedExpense,
215 user_id: Option<&str>,
216 ) -> Result<ExpectedExpense> {
217 let mut path = format!("/travelrequest/v4/requests/{}/expenses", request_uuid);
218 if let Some(uid) = user_id {
219 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
220 }
221 let req = self.client.request(Method::POST, &path).await?;
222 let req = ConcurClient::with_json(req, expense)?;
223 self.client.execute(req).await
224 }
225
226 pub async fn get(&self, expense_uuid: &str, user_id: Option<&str>) -> Result<ExpectedExpense> {
228 let mut path = format!("/travelrequest/v4/expenses/{}", expense_uuid);
229 if let Some(uid) = user_id {
230 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
231 }
232 self.client
233 .request(Method::GET, &path)
234 .await?
235 .send()
236 .await?
237 .json()
238 .await
239 .map_err(Into::into)
240 }
241
242 pub async fn update(
244 &self,
245 expense_uuid: &str,
246 expense: &ExpectedExpense,
247 user_id: Option<&str>,
248 ) -> Result<ExpectedExpense> {
249 let mut path = format!("/travelrequest/v4/expenses/{}", expense_uuid);
250 if let Some(uid) = user_id {
251 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
252 }
253 let req = self.client.request(Method::PUT, &path).await?;
254 let req = ConcurClient::with_json(req, expense)?;
255 self.client.execute(req).await
256 }
257
258 pub async fn delete(&self, expense_uuid: &str, user_id: Option<&str>) -> Result<()> {
260 let mut path = format!("/travelrequest/v4/expenses/{}", expense_uuid);
261 if let Some(uid) = user_id {
262 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
263 }
264 self.client
265 .execute_unit(self.client.request(Method::DELETE, &path).await?)
266 .await
267 }
268}
269
270pub struct RequestAllocationsApi<'a> {
272 client: &'a ConcurClient,
273}
274
275impl<'a> RequestAllocationsApi<'a> {
276 pub fn new(client: &'a ConcurClient) -> Self {
277 Self { client }
278 }
279
280 pub async fn get(
282 &self,
283 request_uuid: &str,
284 allocation_uuid: &str,
285 user_id: Option<&str>,
286 ) -> Result<Vec<RequestAllocation>> {
287 let mut path = format!(
288 "/travelrequest/v4/requests/{}/allocations/{}",
289 request_uuid, allocation_uuid
290 );
291 if let Some(uid) = user_id {
292 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
293 }
294 self.client
295 .request(Method::GET, &path)
296 .await?
297 .send()
298 .await?
299 .json()
300 .await
301 .map_err(Into::into)
302 }
303
304 pub async fn update(
306 &self,
307 request_uuid: &str,
308 allocation_uuid: &str,
309 allocation: &RequestAllocation,
310 user_id: Option<&str>,
311 ) -> Result<Vec<RequestAllocation>> {
312 let mut path = format!(
313 "/travelrequest/v4/requests/{}/allocations/{}",
314 request_uuid, allocation_uuid
315 );
316 if let Some(uid) = user_id {
317 path.push_str(&format!("?userId={}", urlencoding::encode(uid)));
318 }
319 let req = self.client.request(Method::PUT, &path).await?;
320 let req = ConcurClient::with_json(req, allocation)?;
321 self.client.execute(req).await
322 }
323}
324
325#[derive(Debug, Clone, Default)]
326pub struct RequestListQuery {
327 pub view: Option<String>,
328 pub user_id: Option<String>,
329 pub start: Option<u32>,
330 pub limit: Option<u32>,
331 pub approved_before: Option<String>,
332 pub approved_after: Option<String>,
333 pub modified_before: Option<String>,
334 pub modified_after: Option<String>,
335 pub sort_field: Option<String>,
336 pub sort_order: Option<String>,
337}
338
339#[derive(Debug, Clone, Deserialize)]
340pub struct RequestList {
341 pub data: Vec<Request>,
342 pub operations: Option<Vec<Link>>,
343}
344
345#[derive(Debug, Clone, Default, Serialize, Deserialize)]
346pub struct Request {
347 pub id: Option<String>,
348 pub href: Option<String>,
349 #[serde(rename = "requestId")]
350 pub request_id: Option<String>,
351 pub name: Option<String>,
352 #[serde(rename = "businessPurpose")]
353 pub business_purpose: Option<String>,
354 pub comment: Option<String>,
355 #[serde(rename = "approvalStatus")]
356 pub approval_status: Option<StatusRef>,
357 pub approved: Option<bool>,
358 #[serde(rename = "pendingApproval")]
359 pub pending_approval: Option<bool>,
360 #[serde(rename = "canceledPostApproval")]
361 pub canceled_post_approval: Option<bool>,
362 pub closed: Option<bool>,
363 pub owner: Option<OwnerRef>,
364 #[serde(rename = "startDate")]
365 pub start_date: Option<String>,
366 #[serde(rename = "endDate")]
367 pub end_date: Option<String>,
368 #[serde(rename = "startTime")]
369 pub start_time: Option<String>,
370 #[serde(rename = "endTime")]
371 pub end_time: Option<String>,
372 #[serde(rename = "creationDate")]
373 pub creation_date: Option<String>,
374 #[serde(rename = "lastModified")]
375 pub last_modified: Option<String>,
376 #[serde(rename = "submitDate")]
377 pub submit_date: Option<String>,
378 #[serde(rename = "totalApprovedAmount")]
379 pub total_approved_amount: Option<Money>,
380 #[serde(rename = "totalPostedAmount")]
381 pub total_posted_amount: Option<Money>,
382 #[serde(rename = "totalRemainingAmount")]
383 pub total_remaining_amount: Option<Money>,
384 #[serde(rename = "mainDestination")]
385 pub main_destination: Option<serde_json::Value>,
386 pub policy: Option<serde_json::Value>,
387 #[serde(rename = "travelAgency")]
388 pub travel_agency: Option<serde_json::Value>,
389 pub expenses: Option<Vec<serde_json::Value>>,
390 pub operations: Option<Vec<Link>>,
391}
392
393#[derive(Debug, Clone, Default, Serialize, Deserialize)]
394pub struct ExpectedExpense {
395 pub id: Option<String>,
396 pub href: Option<String>,
397 #[serde(rename = "expenseType")]
398 pub expense_type: Option<serde_json::Value>,
399 #[serde(rename = "transactionDate")]
400 pub transaction_date: Option<String>,
401 #[serde(rename = "transactionAmount")]
402 pub transaction_amount: Option<Money>,
403 #[serde(rename = "postedAmount")]
404 pub posted_amount: Option<Money>,
405 #[serde(rename = "approvedAmount")]
406 pub approved_amount: Option<Money>,
407 #[serde(rename = "remainingAmount")]
408 pub remaining_amount: Option<Money>,
409 #[serde(rename = "businessPurpose")]
410 pub business_purpose: Option<String>,
411 pub location: Option<serde_json::Value>,
412 #[serde(rename = "tripData")]
413 pub trip_data: Option<serde_json::Value>,
414 pub allocations: Option<Vec<RequestAllocation>>,
415}
416
417#[derive(Debug, Clone, Default, Serialize, Deserialize)]
418pub struct RequestAllocation {
419 #[serde(rename = "allocationId")]
420 pub allocation_id: Option<String>,
421 #[serde(rename = "expenseId")]
422 pub expense_id: Option<String>,
423 #[serde(rename = "allocationAmount")]
424 pub allocation_amount: Option<Money>,
425 #[serde(rename = "approvedAmount")]
426 pub approved_amount: Option<Money>,
427 #[serde(rename = "postedAmount")]
428 pub posted_amount: Option<Money>,
429 pub percentage: Option<i32>,
430 #[serde(rename = "percentEdited")]
431 pub percent_edited: Option<bool>,
432 #[serde(rename = "systemAllocation")]
433 pub system_allocation: Option<bool>,
434}