use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::config::TaxGroupConfig;
use crate::errors::{GroupError, GroupResult};
use crate::manifest::expansion::ExpandedEntity;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PillarTwoPlan {
pub in_scope_jurisdictions: Vec<String>,
pub entities_in_scope: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CbcReportPlan {
pub reporting_jurisdiction: String,
pub jurisdictions_to_report: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TransferPricingPlan {
pub master_file: bool,
pub local_file_jurisdictions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct TaxGroupPlan {
pub pillar_two: Option<PillarTwoPlan>,
pub cbc_report: Option<CbcReportPlan>,
pub transfer_pricing: Option<TransferPricingPlan>,
}
pub fn build_tax_group_plan(
cfg: &TaxGroupConfig,
entities: &[ExpandedEntity],
) -> GroupResult<TaxGroupPlan> {
let entity_countries: BTreeSet<String> = entities.iter().map(|e| e.country.clone()).collect();
let pillar_two = match &cfg.pillar_two {
Some(p2) if p2.enabled => {
for jur in &p2.jurisdictions {
if !entity_countries.contains(jur.as_str()) {
return Err(GroupError::Config(format!(
"pillar_two jurisdiction '{jur}' is not present in any entity's country"
)));
}
}
let in_scope_jurisdictions: BTreeSet<String> =
p2.jurisdictions.iter().cloned().collect();
let mut entities_in_scope: Vec<String> = entities
.iter()
.filter(|e| in_scope_jurisdictions.contains(&e.country))
.map(|e| e.code.clone())
.collect();
entities_in_scope.sort();
Some(PillarTwoPlan {
in_scope_jurisdictions: {
let mut v: Vec<String> = in_scope_jurisdictions.into_iter().collect();
v.sort();
v
},
entities_in_scope,
})
}
_ => None,
};
let cbc_report = match &cfg.cbc_report {
Some(cbc) if cbc.enabled => {
let reporting_jurisdiction = cbc.reporting_jurisdiction.clone().ok_or_else(|| {
GroupError::Config(
"cbc_report.reporting_jurisdiction is required when enabled = true".to_string(),
)
})?;
let jurisdictions_to_report: Vec<String> = entity_countries.iter().cloned().collect();
Some(CbcReportPlan {
reporting_jurisdiction,
jurisdictions_to_report,
})
}
_ => None,
};
let transfer_pricing = match &cfg.transfer_pricing {
Some(tp) => {
for jur in &tp.local_files_for {
if !entity_countries.contains(jur.as_str()) {
return Err(GroupError::Config(format!(
"transfer_pricing.local_files_for jurisdiction '{jur}' is not present in any entity's country"
)));
}
}
let mut local_file_jurisdictions = tp.local_files_for.clone();
local_file_jurisdictions.sort();
local_file_jurisdictions.dedup();
Some(TransferPricingPlan {
master_file: tp.master_file,
local_file_jurisdictions,
})
}
None => None,
};
Ok(TaxGroupPlan {
pillar_two,
cbc_report,
transfer_pricing,
})
}