iati_types/
tx.rs

1use crate::OrgRef;
2use crate::money::{CurrencyCode, Money};
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5use std::str::FromStr;  
6
7/// IATI Transaction Type (from Transaction/@type).
8/// See https://iatistandard.org/en/iati-standard/activities/transaction/#transaction-type
9/// Unknown/extension codes are preserved via 'Unknown(u16)' variant.
10/// Note that 'Transaction/@type' is distinct from 'Transaction/transaction-type'.
11/// The latter is a child element with its own codelist (see TxType enum).
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 
14pub enum TxType {
15    IncomingFunds,      // 1
16    OutgoingCommitment, // 2
17    Disbursement,       // 3
18    Expenditure,        // 4
19    InterestPayment,    // 5
20    LoanRepayment,      // 6
21    Reimbursement,      // 7
22    PurchaseOfEquity,   // 8
23    SaleOfEquity,       // 9
24    CreditGuarantee,    // 10
25    IncomingCommitment, // 11
26    OutgoingPledge,     // 12
27    IncomingPledge,     // 13
28    Unknown(u16),
29}
30
31impl TxType {
32    /// Return the IATI code for this TxType.
33    pub fn code(self) -> u16 {
34        use TxType::*;
35        match self {
36            IncomingFunds => 1,
37            OutgoingCommitment => 2,
38            Disbursement => 3,
39            Expenditure => 4,
40            InterestPayment => 5,
41            LoanRepayment => 6,
42            Reimbursement => 7,
43            PurchaseOfEquity => 8,
44            SaleOfEquity => 9,
45            CreditGuarantee => 10,
46            IncomingCommitment => 11,
47            OutgoingPledge => 12,
48            IncomingPledge => 13,
49            Unknown(c) => c,
50        }
51    }
52}
53
54impl std::fmt::Display for TxType {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", self.code()) 
57    }
58}
59
60impl From<u16> for TxType {
61    fn from(code: u16) -> Self {
62        match code {
63            1 => TxType::IncomingFunds,
64            2 => TxType::OutgoingCommitment,
65            3 => TxType::Disbursement,
66            4 => TxType::Expenditure,
67            5 => TxType::InterestPayment,
68            6 => TxType::LoanRepayment,
69            7 => TxType::Reimbursement,
70            8 => TxType::PurchaseOfEquity,
71            9 => TxType::SaleOfEquity,
72            10 => TxType::CreditGuarantee,
73            11 => TxType::IncomingCommitment,
74            12 => TxType::OutgoingPledge,
75            13 => TxType::IncomingPledge,
76            c => TxType::Unknown(c),
77        }
78    }
79}
80
81impl FromStr for TxType {
82    type Err = std::num::ParseIntError; // parsing from string to u16 may fail
83    fn from_str(s: &str) -> Result<Self, Self::Err> {
84        let n: u16 = s.trim().parse()?; 
85        Ok(TxType::from(n))
86    }
87}
88
89/// Minimal transaction spine for rollups & FX.
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91#[derive(Debug, Clone, PartialEq)]
92pub struct Transaction {
93    pub tx_type: TxType, // from transaction-type/@code
94    pub date: NaiveDate, // from 'transaction-date/@iso-date'
95    pub value: Money,    // from <value> body + attributes
96    pub provider_org: Option<OrgRef>,
97    pub receiver_org: Option<OrgRef>,
98    /// optional hint for a resolved currency if caller has done FX lookup
99    pub currency_hint: Option<CurrencyCode>,
100}
101
102impl Transaction {
103    pub fn new(tx_type: TxType, date: NaiveDate, value: Money) -> Self {
104        Self {
105            tx_type,
106            date,
107            value,
108            provider_org: None, 
109            receiver_org: None,
110            currency_hint: None,
111        }
112    }
113
114    /// Set provider organisation (with builder helpers).
115    pub fn with_provider(mut self, org: OrgRef) -> Self {
116        self.provider_org = Some(org);
117        self
118    }
119
120    /// Set receiver organisation (with builder helpers).
121    pub fn with_receiver(mut self, org: OrgRef) -> Self {
122        self.receiver_org = Some(org);
123        self
124    }
125
126    /// Set a pre-resolved currency hint (builder-style).
127    pub fn with_currency_hint(mut self, code: CurrencyCode) -> Self {
128        self.currency_hint = Some(code);
129        self
130    }
131}