Skip to main content

concur_api/api/
invoice.rs

1//! SAP Concur Invoice Pay API v4.
2//!
3//! Endpoints: `invoice/provider-payment/v4/...`
4
5use crate::client::ConcurClient;
6use crate::error::Result;
7use reqwest::Method;
8use serde::{Deserialize, Serialize};
9
10/// Invoice Pay API.
11pub struct InvoicePayApi<'a> {
12    client: &'a ConcurClient,
13}
14
15impl<'a> InvoicePayApi<'a> {
16    pub fn new(client: &'a ConcurClient) -> Self {
17        Self { client }
18    }
19
20    /// List pending payments.
21    pub async fn list_payments(&self, query: &PaymentQuery) -> Result<PaymentsResponse> {
22        let mut path = "/invoice/provider-payment/v4/payments".to_string();
23        let mut params: Vec<(String, String)> = Vec::new();
24        if let Some(v) = &query.invoice_id {
25            params.push(("invoiceId".to_string(), v.clone()));
26        }
27        if let Some(v) = &query.invoice_date {
28            params.push(("invoiceDate".to_string(), v.clone()));
29        }
30        if let Some(v) = &query.create_date {
31            params.push(("createDate".to_string(), v.clone()));
32        }
33        if let Some(v) = &query.vendor_name {
34            params.push(("vendorName".to_string(), v.clone()));
35        }
36        if let Some(v) = &query.vendor_code {
37            params.push(("vendorCode".to_string(), v.clone()));
38        }
39        if let Some(v) = &query.vendor_addr_code {
40            params.push(("vendorAddrCode".to_string(), v.clone()));
41        }
42        if let Some(v) = &query.payment_status {
43            params.push(("PaymentStatus".to_string(), v.clone()));
44        }
45        if let Some(v) = &query.payment_group {
46            params.push(("PaymentGroup".to_string(), v.clone()));
47        }
48        if let Some(v) = &query.invoice_number {
49            params.push(("invoiceNumber".to_string(), v.clone()));
50        }
51        if let Some(v) = &query.payment_id {
52            params.push(("paymentId".to_string(), v.clone()));
53        }
54        if !params.is_empty() {
55            path.push('?');
56            path.push_str(
57                &params
58                    .iter()
59                    .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
60                    .collect::<Vec<_>>()
61                    .join("&"),
62            );
63        }
64        self.client
65            .request(Method::GET, &path)
66            .await?
67            .send()
68            .await?
69            .json()
70            .await
71            .map_err(Into::into)
72    }
73
74    /// Update a single payment status.
75    pub async fn update_payment(
76        &self,
77        payment_id: &str,
78        update: &PaymentUpdate,
79    ) -> Result<PaymentUpdateResult> {
80        let req = self
81            .client
82            .request(
83                Method::POST,
84                &format!("/invoice/provider-payment/v4/payments/{}", payment_id),
85            )
86            .await?;
87        let req = ConcurClient::with_json(req, update)?;
88        self.client.execute(req).await
89    }
90
91    /// Update multiple payment statuses in bulk.
92    pub async fn bulk_update_payments(
93        &self,
94        updates: &[PaymentUpdate],
95    ) -> Result<BulkUpdateResult> {
96        let req = self
97            .client
98            .request(
99                Method::POST,
100                "/invoice/provider-payment/v4/payments/bulkUpdate",
101            )
102            .await?;
103        let req = ConcurClient::with_json(req, updates)?;
104        self.client.execute(req).await
105    }
106}
107
108#[derive(Debug, Clone, Default)]
109pub struct PaymentQuery {
110    pub invoice_id: Option<String>,
111    pub invoice_date: Option<String>,
112    pub create_date: Option<String>,
113    pub vendor_name: Option<String>,
114    pub vendor_code: Option<String>,
115    pub vendor_addr_code: Option<String>,
116    pub payment_status: Option<String>,
117    pub payment_group: Option<String>,
118    pub invoice_number: Option<String>,
119    pub payment_id: Option<String>,
120}
121
122#[derive(Debug, Clone, Deserialize)]
123pub struct PaymentsResponse {
124    pub payments: Vec<serde_json::Value>,
125}
126
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
128pub struct PaymentUpdate {
129    #[serde(rename = "paymentId")]
130    pub payment_id: Option<String>,
131    #[serde(rename = "providerReference")]
132    pub provider_reference: Option<String>,
133    pub status: String,
134    #[serde(rename = "statusMessage")]
135    pub status_message: Option<String>,
136    #[serde(rename = "statusDate")]
137    pub status_date: Option<String>,
138    #[serde(rename = "paymentInitiationDate")]
139    pub payment_initiation_date: Option<String>,
140    #[serde(rename = "paymentSettlementDate")]
141    pub payment_settlement_date: Option<String>,
142    #[serde(rename = "thirdPartyPaymentIdentifier")]
143    pub third_party_payment_identifier: Option<String>,
144    #[serde(rename = "paymentMethod")]
145    pub payment_method: Option<String>,
146    #[serde(rename = "paidAmount")]
147    pub paid_amount: Option<Money>,
148    #[serde(rename = "paymentAdjustmentNotes")]
149    pub payment_adjustment_notes: Option<String>,
150    #[serde(rename = "fundingSourceRef")]
151    pub funding_source_ref: Option<String>,
152    #[serde(rename = "cashAccountCode")]
153    pub cash_account_code: Option<String>,
154    #[serde(rename = "liabilityAccountCode")]
155    pub liability_account_code: Option<String>,
156    #[serde(rename = "fundingRequestRef")]
157    pub funding_request_ref: Option<String>,
158    #[serde(rename = "checkNumber")]
159    pub check_number: Option<String>,
160    #[serde(rename = "fundingSettlementDate")]
161    pub funding_settlement_date: Option<String>,
162    #[serde(rename = "fundingCurrency")]
163    pub funding_currency: Option<String>,
164    #[serde(rename = "paymentCurrency")]
165    pub payment_currency: Option<String>,
166    #[serde(rename = "foreignExchangeRate")]
167    pub foreign_exchange_rate: Option<String>,
168}
169
170#[derive(Debug, Clone, Default, Serialize, Deserialize)]
171pub struct Money {
172    pub amount: String,
173    pub currency: String,
174}
175
176#[derive(Debug, Clone, Deserialize)]
177pub struct PaymentUpdateResult {
178    #[serde(rename = "paymentId")]
179    pub payment_id: String,
180    pub status: String,
181    #[serde(rename = "statusMessage")]
182    pub status_message: Option<String>,
183    #[serde(rename = "statusDate")]
184    pub status_date: Option<String>,
185}
186
187#[derive(Debug, Clone, Deserialize)]
188pub struct BulkUpdateResult {
189    #[serde(rename = "successfulPayments")]
190    pub successful_payments: Option<Vec<PaymentUpdateResult>>,
191    #[serde(rename = "failedPayments")]
192    pub failed_payments: Option<Vec<serde_json::Value>>,
193}