librsigstopup 0.1.0

Super safe library untuk simulasi perhitungan top-up dengan JSON API, verbose logging, dan full trace
Documentation
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TotalCalculator;
impl TotalCalculator {
    #[allow(clippy::too_many_arguments)]
    pub fn calculate(
        pinjaman: Decimal,
        angsuran: Decimal,
        sisa_tenor: u32,
        asuransi: Decimal,
        pajak: Decimal,
        diskon: Decimal,
    ) -> Decimal {
        tracing::info!(
            event = "calculate_total",
            pinjaman = %pinjaman,
            angsuran = %angsuran,
            sisa_tenor = sisa_tenor,
            asuransi = %asuransi,
            pajak = %pajak,
            diskon = %diskon,
            "Calculating total"
        );
        let sisa_pokok = pinjaman - (angsuran * Decimal::from(sisa_tenor));
        let after_insurance = sisa_pokok - asuransi;
        let after_tax = after_insurance - pajak;
        let total = after_tax + diskon;
        tracing::info!(
            event = "total_calculated",
            sisa_pokok = %sisa_pokok,
            after_insurance = %after_insurance,
            after_tax = %after_tax,
            total = %total,
            formula = "(E4-(E5*E6))-E8-E9+E7",
            "Total calculated successfully"
        );
        total
    }
    pub fn calculate_with_breakdown(
        pinjaman: Decimal,
        angsuran: Decimal,
        sisa_tenor: u32,
        asuransi: Decimal,
        pajak: Decimal,
        diskon: Decimal,
    ) -> TotalBreakdown {
        let sisa_pokok = pinjaman - (angsuran * Decimal::from(sisa_tenor));
        let after_insurance = sisa_pokok - asuransi;
        let after_tax = after_insurance - pajak;
        let total = after_tax + diskon;
        TotalBreakdown {
            pinjaman,
            total_angsuran_belum_bayar: angsuran * Decimal::from(sisa_tenor),
            sisa_pokok,
            asuransi,
            pajak,
            diskon,
            total,
        }
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TotalBreakdown {
    pub pinjaman: Decimal,
    pub total_angsuran_belum_bayar: Decimal,
    pub sisa_pokok: Decimal,
    pub asuransi: Decimal,
    pub pajak: Decimal,
    pub diskon: Decimal,
    pub total: Decimal,
}
#[cfg(test)]
mod tests {
    use super::*;
    use rust_decimal_macros::dec;
    #[test]
    fn test_calculate_basic() {
        let total = TotalCalculator::calculate(
            dec!(10_000_000),
            dec!(500_000),
            5,
            dec!(150_000),
            dec!(150_000),
            dec!(200_000),
        );
        assert_eq!(total, dec!(7_400_000));
    }
    #[test]
    fn test_calculate_with_breakdown() {
        let breakdown = TotalCalculator::calculate_with_breakdown(
            dec!(10_000_000),
            dec!(500_000),
            5,
            dec!(150_000),
            dec!(150_000),
            dec!(200_000),
        );
        assert_eq!(breakdown.pinjaman, dec!(10_000_000));
        assert_eq!(breakdown.total_angsuran_belum_bayar, dec!(2_500_000));
        assert_eq!(breakdown.sisa_pokok, dec!(7_500_000));
        assert_eq!(breakdown.asuransi, dec!(150_000));
        assert_eq!(breakdown.pajak, dec!(150_000));
        assert_eq!(breakdown.diskon, dec!(200_000));
        assert_eq!(breakdown.total, dec!(7_400_000));
    }
    #[test]
    fn test_calculate_zero_diskon() {
        let total = TotalCalculator::calculate(
            dec!(5_000_000),
            dec!(250_000),
            4,
            dec!(75_000),
            dec!(120_000),
            dec!(0),
        );
        assert_eq!(total, dec!(3_805_000));
    }
    #[test]
    fn test_calculate_edge_case_small_values() {
        let total = TotalCalculator::calculate(
            dec!(1_000_000),
            dec!(100_000),
            1,
            dec!(15_000),
            dec!(120_000),
            dec!(50_000),
        );
        assert_eq!(total, dec!(815_000));
    }
}