use serde::{Deserialize, Serialize};
use crate::models::PaginationParams;
use crate::filters::{common::ListFilters, invoice::InvoiceFilters};
#[derive(Debug, Clone)]
pub struct ListInvoicesRequest {
pub pagination: PaginationParams,
pub filters: InvoiceFilters,
pub search_term: Option<String>,
}
impl ListInvoicesRequest {
pub fn new() -> Self {
Self {
pagination: PaginationParams::default(),
filters: InvoiceFilters::default(),
search_term: None,
}
}
pub fn with_pagination(mut self, pagination: PaginationParams) -> Self {
self.pagination = pagination;
self
}
pub fn with_filters(mut self, filters: InvoiceFilters) -> Self {
self.filters = filters;
self
}
pub fn with_search_term(mut self, term: String) -> Self {
self.search_term = Some(term);
self
}
pub fn to_query_params(&self) -> Vec<(&str, String)> {
let mut params = self.pagination.to_query_params();
params.extend(self.filters.to_query_params());
if let Some(ref term) = self.search_term {
params.push(("search_term", term.clone()));
}
params
}
}
impl Default for ListInvoicesRequest {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct GetInvoiceRequest {
pub invoice_id: String,
}
impl GetInvoiceRequest {
pub fn new(invoice_id: String) -> Self {
Self { invoice_id }
}
}
#[derive(Debug, Clone)]
pub struct DeleteInvoiceRequest {
pub invoice_id: String,
}
impl DeleteInvoiceRequest {
pub fn new(invoice_id: String) -> Self {
Self { invoice_id }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BillingTime {
Anniversary,
Calendar,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InvoicePreviewCustomer {
#[serde(skip_serializing_if = "Option::is_none")]
pub external_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line1: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line2: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_identification_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shipping_address: Option<InvoicePreviewAddress>,
#[serde(skip_serializing_if = "Option::is_none")]
pub integration_customers: Option<Vec<InvoicePreviewIntegrationCustomer>>,
}
impl InvoicePreviewCustomer {
pub fn with_external_id(external_id: String) -> Self {
Self {
external_id: Some(external_id),
..Default::default()
}
}
pub fn new() -> Self {
Self::default()
}
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn with_address(
mut self,
address_line1: String,
address_line2: Option<String>,
city: Option<String>,
state: Option<String>,
country: Option<String>,
) -> Self {
self.address_line1 = Some(address_line1);
self.address_line2 = address_line2;
self.city = city;
self.state = state;
self.country = country;
self
}
pub fn with_currency(mut self, currency: String) -> Self {
self.currency = Some(currency);
self
}
pub fn with_timezone(mut self, timezone: String) -> Self {
self.timezone = Some(timezone);
self
}
pub fn with_tax_identification_number(mut self, tax_id: String) -> Self {
self.tax_identification_number = Some(tax_id);
self
}
pub fn with_shipping_address(mut self, address: InvoicePreviewAddress) -> Self {
self.shipping_address = Some(address);
self
}
pub fn with_integration_customers(
mut self,
integrations: Vec<InvoicePreviewIntegrationCustomer>,
) -> Self {
self.integration_customers = Some(integrations);
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InvoicePreviewAddress {
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line1: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address_line2: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub zipcode: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvoicePreviewIntegrationCustomer {
pub integration_type: String,
pub integration_code: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvoicePreviewCoupon {
pub code: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub coupon_type: Option<InvoicePreviewCouponType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub amount_cents: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub amount_currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub percentage_rate: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_duration: Option<i32>,
}
impl InvoicePreviewCoupon {
pub fn new(code: String) -> Self {
Self {
code,
name: None,
coupon_type: None,
amount_cents: None,
amount_currency: None,
percentage_rate: None,
frequency_duration: None,
}
}
pub fn with_name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn with_fixed_amount(mut self, amount_cents: i64, currency: String) -> Self {
self.coupon_type = Some(InvoicePreviewCouponType::FixedAmount);
self.amount_cents = Some(amount_cents);
self.amount_currency = Some(currency);
self
}
pub fn with_percentage(mut self, percentage_rate: String) -> Self {
self.coupon_type = Some(InvoicePreviewCouponType::Percentage);
self.percentage_rate = Some(percentage_rate);
self
}
pub fn with_frequency_duration(mut self, duration: i32) -> Self {
self.frequency_duration = Some(duration);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvoicePreviewCouponType {
FixedAmount,
Percentage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvoicePreviewSubscriptions {
pub external_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plan_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub terminated_at: Option<String>,
}
impl InvoicePreviewSubscriptions {
pub fn new(external_ids: Vec<String>) -> Self {
Self {
external_ids,
plan_code: None,
terminated_at: None,
}
}
pub fn with_plan_code(mut self, plan_code: String) -> Self {
self.plan_code = Some(plan_code);
self
}
pub fn with_terminated_at(mut self, terminated_at: String) -> Self {
self.terminated_at = Some(terminated_at);
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InvoicePreviewInput {
pub customer: InvoicePreviewCustomer,
#[serde(skip_serializing_if = "Option::is_none")]
pub plan_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscription_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_time: Option<BillingTime>,
#[serde(skip_serializing_if = "Option::is_none")]
pub coupons: Option<Vec<InvoicePreviewCoupon>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscriptions: Option<InvoicePreviewSubscriptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing_entity_code: Option<String>,
}
impl InvoicePreviewInput {
pub fn new(customer: InvoicePreviewCustomer) -> Self {
Self {
customer,
..Default::default()
}
}
pub fn for_customer(external_id: String) -> Self {
Self::new(InvoicePreviewCustomer::with_external_id(external_id))
}
pub fn with_plan_code(mut self, plan_code: String) -> Self {
self.plan_code = Some(plan_code);
self
}
pub fn with_subscription_at(mut self, subscription_at: String) -> Self {
self.subscription_at = Some(subscription_at);
self
}
pub fn with_billing_time(mut self, billing_time: BillingTime) -> Self {
self.billing_time = Some(billing_time);
self
}
pub fn with_coupons(mut self, coupons: Vec<InvoicePreviewCoupon>) -> Self {
self.coupons = Some(coupons);
self
}
pub fn with_subscriptions(mut self, subscriptions: InvoicePreviewSubscriptions) -> Self {
self.subscriptions = Some(subscriptions);
self
}
pub fn with_billing_entity_code(mut self, code: String) -> Self {
self.billing_entity_code = Some(code);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvoicePreviewRequest {
#[serde(flatten)]
pub input: InvoicePreviewInput,
}
impl InvoicePreviewRequest {
pub fn new(input: InvoicePreviewInput) -> Self {
Self { input }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateInvoiceFeeInput {
pub add_on_code: String,
pub units: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub unit_amount_cents: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_codes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_datetime: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to_datetime: Option<String>,
}
impl CreateInvoiceFeeInput {
pub fn new(add_on_code: String, units: f64) -> Self {
Self {
add_on_code,
units,
unit_amount_cents: None,
description: None,
tax_codes: None,
from_datetime: None,
to_datetime: None,
}
}
pub fn with_unit_amount_cents(mut self, unit_amount_cents: i64) -> Self {
self.unit_amount_cents = Some(unit_amount_cents);
self
}
pub fn with_description(mut self, description: String) -> Self {
self.description = Some(description);
self
}
pub fn with_tax_codes(mut self, tax_codes: Vec<String>) -> Self {
self.tax_codes = Some(tax_codes);
self
}
pub fn with_service_period(mut self, from_datetime: String, to_datetime: String) -> Self {
self.from_datetime = Some(from_datetime);
self.to_datetime = Some(to_datetime);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateInvoiceInput {
pub external_customer_id: String,
pub currency: String,
pub fees: Vec<CreateInvoiceFeeInput>,
#[serde(skip_serializing_if = "Option::is_none")]
pub purchase_order_number: Option<String>,
}
impl CreateInvoiceInput {
pub fn new(
external_customer_id: String,
currency: String,
fees: Vec<CreateInvoiceFeeInput>,
) -> Self {
Self {
external_customer_id,
currency,
fees,
purchase_order_number: None,
}
}
pub fn with_purchase_order_number(mut self, purchase_order_number: String) -> Self {
self.purchase_order_number = Some(purchase_order_number);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateInvoiceRequest {
pub invoice: CreateInvoiceInput,
}
impl CreateInvoiceRequest {
pub fn new(input: CreateInvoiceInput) -> Self {
Self { invoice: input }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateInvoiceMetadataInput {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub key: String,
pub value: String,
}
impl UpdateInvoiceMetadataInput {
pub fn new(key: String, value: String) -> Self {
Self {
id: None,
key,
value,
}
}
pub fn with_id(id: String, key: String, value: String) -> Self {
Self {
id: Some(id),
key,
value,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UpdateInvoiceInput {
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Vec<UpdateInvoiceMetadataInput>>,
}
impl UpdateInvoiceInput {
pub fn new() -> Self {
Self::default()
}
pub fn with_payment_status(mut self, payment_status: String) -> Self {
self.payment_status = Some(payment_status);
self
}
pub fn with_metadata(mut self, metadata: Vec<UpdateInvoiceMetadataInput>) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateInvoiceRequest {
#[serde(skip)]
pub lago_id: String,
pub invoice: UpdateInvoiceInput,
}
impl UpdateInvoiceRequest {
pub fn new(lago_id: String, input: UpdateInvoiceInput) -> Self {
Self {
lago_id,
invoice: input,
}
}
}
#[derive(Debug, Clone)]
pub struct ListCustomerInvoicesRequest {
pub external_customer_id: String,
pub pagination: PaginationParams,
pub filters: InvoiceFilters,
pub search_term: Option<String>,
}
impl ListCustomerInvoicesRequest {
pub fn new(external_customer_id: String) -> Self {
Self {
external_customer_id,
pagination: PaginationParams::default(),
filters: InvoiceFilters::default(),
search_term: None,
}
}
pub fn with_pagination(mut self, pagination: PaginationParams) -> Self {
self.pagination = pagination;
self
}
pub fn with_filters(mut self, filters: InvoiceFilters) -> Self {
self.filters = filters;
self
}
pub fn with_search_term(mut self, term: String) -> Self {
self.search_term = Some(term);
self
}
pub fn to_query_params(&self) -> Vec<(&str, String)> {
let mut params = self.pagination.to_query_params();
params.extend(self.filters.to_query_params());
if let Some(ref term) = self.search_term {
params.push(("search_term", term.clone()));
}
params
}
}
#[derive(Debug, Clone)]
pub struct RefreshInvoiceRequest {
pub lago_id: String,
}
impl RefreshInvoiceRequest {
pub fn new(lago_id: String) -> Self {
Self { lago_id }
}
}
#[derive(Debug, Clone)]
pub struct DownloadInvoiceRequest {
pub lago_id: String,
}
impl DownloadInvoiceRequest {
pub fn new(lago_id: String) -> Self {
Self { lago_id }
}
}
#[derive(Debug, Clone)]
pub struct RetryInvoiceRequest {
pub lago_id: String,
}
impl RetryInvoiceRequest {
pub fn new(lago_id: String) -> Self {
Self { lago_id }
}
}
#[derive(Debug, Clone)]
pub struct RetryInvoicePaymentRequest {
pub lago_id: String,
}
impl RetryInvoicePaymentRequest {
pub fn new(lago_id: String) -> Self {
Self { lago_id }
}
}
#[derive(Debug, Clone)]
pub struct VoidInvoiceRequest {
pub lago_id: String,
}
impl VoidInvoiceRequest {
pub fn new(lago_id: String) -> Self {
Self { lago_id }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn service_period_is_omitted_when_unset() {
let fee = CreateInvoiceFeeInput::new("setup_fee".to_string(), 1.0);
let json = serde_json::to_value(&fee).unwrap();
assert!(json.get("from_datetime").is_none());
assert!(json.get("to_datetime").is_none());
}
#[test]
fn service_period_is_serialized_when_set() {
let fee = CreateInvoiceFeeInput::new("setup_fee".to_string(), 1.0).with_service_period(
"2022-08-08T00:00:00Z".to_string(),
"2022-08-31T23:59:59Z".to_string(),
);
let json = serde_json::to_value(&fee).unwrap();
assert_eq!(json["from_datetime"], "2022-08-08T00:00:00Z");
assert_eq!(json["to_datetime"], "2022-08-31T23:59:59Z");
}
}