feat(installments): add InstallmentEntries struct for validated installment data

This commit is contained in:
Ayush
2026-03-12 17:38:25 +05:30
parent eb2ab8ff74
commit dbf712ccfa

View File

@@ -1249,7 +1249,7 @@ pub struct NetworkTransactionIdAndDecryptedWalletTokenDetails {
}
/// Billing frequency for a card installment plan
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum BillingFrequency {
/// Monthly billing
@@ -1396,6 +1396,72 @@ pub struct InstallmentOptionData {
pub interest_rate: InstallmentInterestRate,
}
/// A validated list of installment entries with no duplicate counts per billing frequency.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(try_from = "Vec<InstallmentOptionData>")]
pub struct InstallmentEntries(Vec<InstallmentOptionData>);
impl InstallmentEntries {
fn validate_no_duplicate_counts_per_frequency(
installments: &[InstallmentOptionData],
) -> Result<(), errors::ValidationError> {
installments
.iter()
.try_fold(
HashMap::<&BillingFrequency, HashSet<NonZeroU8>>::new(),
|mut seen, entry| {
entry
.number_of_installments
.as_slice()
.iter()
.try_fold(&mut seen, |seen, &count| {
seen.entry(&entry.billing_frequency)
.or_default()
.insert(count)
.then_some(seen)
.ok_or_else(|| {
error_stack::report!(
errors::ValidationError::InvalidValue {
message: format!(
"installment count {count} appears in multiple entries with the same billing frequency."
),
}
)
})
})?;
Ok(seen)
},
)
.map(|_| ())
}
}
impl TryFrom<Vec<InstallmentOptionData>> for InstallmentEntries {
type Error = Report<errors::ValidationError>;
fn try_from(entries: Vec<InstallmentOptionData>) -> Result<Self, errors::ValidationError> {
Self::validate_no_duplicate_counts_per_frequency(&entries)?;
Ok(Self(entries))
}
}
impl std::ops::Deref for InstallmentEntries {
type Target = Vec<InstallmentOptionData>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl IntoIterator for InstallmentEntries {
type Item = InstallmentOptionData;
type IntoIter = std::vec::IntoIter<InstallmentOptionData>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
/// Installment options grouped by payment method
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)]
pub struct InstallmentOption {
@@ -1403,7 +1469,8 @@ pub struct InstallmentOption {
#[schema(value_type = PaymentMethod)]
pub payment_method: common_enums::PaymentMethod,
/// List of available installment configurations
pub installments: Vec<InstallmentOptionData>,
#[schema(value_type = Vec<InstallmentOptionData>)]
pub installments: InstallmentEntries,
}
/// A list of installment options stored as a single JSONB column value.