datasynth-core 5.34.0

Core domain models, traits, and distributions for synthetic enterprise data generation
Documentation
//! External-expectation models — the ISA-520 substantive-analytics layer.
//!
//! An [`ExternalExpectation`] is a per-account *expected* period total derived from an exogenous
//! driver (prior-year actuals, a market/industry index, a macro series, or the budget) together with
//! a materiality tolerance band. It is the analytic input a substantive-analytics procedure compares
//! the realized GL total against: a deviation beyond the band is the ISA-520 "investigate" trigger.
//!
//! These records are the engine-side realization of the perfect-crime countermeasure (Phase 2): a
//! mimetic fraud preserves the per-entry ledger distribution and so evades the per-JE residual arms,
//! but it still inflates an account's *aggregate*, which deviates from an expectation anchored to the
//! account's *legitimate* level. Because the engine knows the generating process, each record also
//! carries the ground truth — the fraud contribution to the actual — so the substantive arm can be
//! scored against it (a band exceedance is a true positive iff the account was fraud-inflated).

use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};

use crate::models::chart_of_accounts::AccountType;

/// The exogenous basis an expectation is built on (ISA 520 expectation bases).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ExpectationDriver {
    /// Prior-period actual grown at an expected rate (trend analysis).
    #[default]
    PriorYear,
    /// A market / industry index the account is expected to track.
    MarketIndex,
    /// A macroeconomic series (e.g. GDP, CPI) the account is expected to track.
    MacroSeries,
    /// The budgeted amount for the account.
    Budget,
}

impl ExpectationDriver {
    /// Human-readable label for the expectation basis (used in the record's `basis` text).
    pub fn label(&self) -> &'static str {
        match self {
            ExpectationDriver::PriorYear => "prior-year actual",
            ExpectationDriver::MarketIndex => "market/industry index",
            ExpectationDriver::MacroSeries => "macroeconomic series",
            ExpectationDriver::Budget => "budget",
        }
    }
}

/// An ISA-520 substantive-analytics expectation for a single GL account over a fiscal year.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExternalExpectation {
    /// Unique expectation identifier.
    pub expectation_id: String,
    /// Company the account belongs to.
    pub company_code: String,
    /// GL account number.
    pub account_code: String,
    /// GL account description.
    pub account_description: String,
    /// GL account type.
    pub account_type: AccountType,
    /// Fiscal year the expectation applies to.
    pub fiscal_year: i32,
    /// Exogenous driver the expectation is built on.
    pub driver: ExpectationDriver,
    /// Human-readable expectation basis (the ISA-520 methodology note).
    pub basis: String,
    /// The driver's value (e.g. the prior-year actual, or the index level).
    #[serde(with = "crate::serde_decimal")]
    pub driver_value: Decimal,
    /// Expected period total for the account.
    #[serde(with = "crate::serde_decimal")]
    pub expected_value: Decimal,
    /// Materiality tolerance band as a fraction of the expectation.
    pub tolerance_pct: f64,
    /// Lower bound of the acceptable band (`expected * (1 - tolerance_pct)`).
    #[serde(with = "crate::serde_decimal")]
    pub lower_bound: Decimal,
    /// Upper bound of the acceptable band (`expected * (1 + tolerance_pct)`).
    #[serde(with = "crate::serde_decimal")]
    pub upper_bound: Decimal,
    /// Realized GL total for the account (legitimate + any fraud).
    #[serde(with = "crate::serde_decimal")]
    pub actual_value: Decimal,
    /// `actual_value - expected_value`.
    #[serde(with = "crate::serde_decimal")]
    pub deviation: Decimal,
    /// Deviation in units of the tolerance band (a z-like score; |·| > 1 means out of band).
    pub deviation_ratio: f64,
    /// Whether the actual breaches the tolerance band — the ISA-520 "investigate" trigger.
    pub exceeds_band: bool,
    /// Ground truth: the fraud contribution to the actual (`actual - legitimate`).
    #[serde(with = "crate::serde_decimal")]
    pub fraud_inflation: Decimal,
    /// Ground truth: whether the account received any fraud postings.
    pub is_fraud_inflated: bool,
}