use chrono::NaiveDate;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupStructure {
pub parent_entity: String,
pub subsidiaries: Vec<SubsidiaryRelationship>,
pub associates: Vec<AssociateRelationship>,
}
impl GroupStructure {
pub fn new(parent_entity: String) -> Self {
Self {
parent_entity,
subsidiaries: Vec::new(),
associates: Vec::new(),
}
}
pub fn add_subsidiary(&mut self, subsidiary: SubsidiaryRelationship) {
self.subsidiaries.push(subsidiary);
}
pub fn add_associate(&mut self, associate: AssociateRelationship) {
self.associates.push(associate);
}
pub fn entity_count(&self) -> usize {
1 + self.subsidiaries.len() + self.associates.len()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubsidiaryRelationship {
pub entity_code: String,
pub ownership_percentage: Decimal,
pub voting_rights_percentage: Decimal,
pub consolidation_method: GroupConsolidationMethod,
pub acquisition_date: Option<NaiveDate>,
pub nci_percentage: Decimal,
pub functional_currency: String,
}
impl SubsidiaryRelationship {
pub fn new_full(entity_code: String, functional_currency: String) -> Self {
Self {
entity_code,
ownership_percentage: Decimal::from(100),
voting_rights_percentage: Decimal::from(100),
consolidation_method: GroupConsolidationMethod::FullConsolidation,
acquisition_date: None,
nci_percentage: Decimal::ZERO,
functional_currency,
}
}
pub fn new_with_ownership(
entity_code: String,
ownership_percentage: Decimal,
functional_currency: String,
acquisition_date: Option<NaiveDate>,
) -> Self {
let consolidation_method = GroupConsolidationMethod::from_ownership(ownership_percentage);
let nci_percentage = Decimal::from(100) - ownership_percentage;
Self {
entity_code,
ownership_percentage,
voting_rights_percentage: ownership_percentage,
consolidation_method,
acquisition_date,
nci_percentage,
functional_currency,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GroupConsolidationMethod {
FullConsolidation,
EquityMethod,
FairValue,
}
impl GroupConsolidationMethod {
pub fn from_ownership(ownership_pct: Decimal) -> Self {
if ownership_pct > Decimal::from(50) {
Self::FullConsolidation
} else if ownership_pct >= Decimal::from(20) {
Self::EquityMethod
} else {
Self::FairValue
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssociateRelationship {
pub entity_code: String,
pub ownership_percentage: Decimal,
pub equity_pickup: Decimal,
}
impl AssociateRelationship {
pub fn new(entity_code: String, ownership_percentage: Decimal) -> Self {
Self {
entity_code,
ownership_percentage,
equity_pickup: Decimal::ZERO,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NciMeasurement {
pub entity_code: String,
#[serde(with = "crate::serde_decimal")]
pub nci_percentage: Decimal,
#[serde(with = "crate::serde_decimal")]
pub nci_share_net_assets: Decimal,
#[serde(with = "crate::serde_decimal")]
pub nci_share_profit: Decimal,
#[serde(with = "crate::serde_decimal")]
pub total_nci: Decimal,
}
impl NciMeasurement {
pub fn compute(
entity_code: String,
nci_percentage: Decimal,
net_assets: Decimal,
net_income: Decimal,
) -> Self {
let hundred = Decimal::from(100);
let nci_pct_fraction = nci_percentage / hundred;
let nci_share_net_assets = net_assets * nci_pct_fraction;
let nci_share_profit = net_income * nci_pct_fraction;
let total_nci = nci_share_net_assets;
Self {
entity_code,
nci_percentage,
nci_share_net_assets,
nci_share_profit,
total_nci,
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
#[test]
fn test_group_consolidation_method_from_ownership() {
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(100)),
GroupConsolidationMethod::FullConsolidation
);
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(51)),
GroupConsolidationMethod::FullConsolidation
);
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(50)),
GroupConsolidationMethod::EquityMethod
);
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(20)),
GroupConsolidationMethod::EquityMethod
);
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(19)),
GroupConsolidationMethod::FairValue
);
assert_eq!(
GroupConsolidationMethod::from_ownership(dec!(0)),
GroupConsolidationMethod::FairValue
);
}
}