use chrono::NaiveDate;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sox302Certification {
pub certification_id: Uuid,
pub company_code: String,
pub fiscal_year: u16,
pub period_end_date: NaiveDate,
pub certifier_role: CertifierRole,
pub certifier_name: String,
pub certification_date: NaiveDate,
pub report_type: ReportType,
pub reviewed_report: bool,
pub no_material_misstatement: bool,
pub fairly_presented: bool,
pub disclosure_controls_effective: bool,
pub disclosure_controls_evaluation_date: Option<NaiveDate>,
pub internal_control_designed_effectively: bool,
pub significant_changes_disclosed: bool,
pub significant_changes_description: Option<String>,
pub fraud_disclosed: bool,
pub fraud_description: Option<String>,
pub material_weaknesses: Vec<Uuid>,
pub significant_deficiencies: Vec<Uuid>,
pub certification_text: String,
}
impl Sox302Certification {
pub fn new(
company_code: impl Into<String>,
fiscal_year: u16,
period_end_date: NaiveDate,
certifier_role: CertifierRole,
certifier_name: impl Into<String>,
) -> Self {
Self {
certification_id: Uuid::now_v7(),
company_code: company_code.into(),
fiscal_year,
period_end_date,
certifier_role,
certifier_name: certifier_name.into(),
certification_date: chrono::Utc::now().date_naive(),
report_type: ReportType::AnnualReport10K,
reviewed_report: true,
no_material_misstatement: true,
fairly_presented: true,
disclosure_controls_effective: true,
disclosure_controls_evaluation_date: Some(period_end_date),
internal_control_designed_effectively: true,
significant_changes_disclosed: true,
significant_changes_description: None,
fraud_disclosed: false,
fraud_description: None,
material_weaknesses: Vec::new(),
significant_deficiencies: Vec::new(),
certification_text: String::new(),
}
}
pub fn generate_certification_text(&mut self) {
let report_name = match self.report_type {
ReportType::AnnualReport10K => "annual report on Form 10-K",
ReportType::QuarterlyReport10Q => "quarterly report on Form 10-Q",
};
self.certification_text = format!(
"I, {}, certify that:\n\n\
1. I have reviewed this {} of {};\n\n\
2. Based on my knowledge, this report does not contain any untrue statement of a \
material fact or omit to state a material fact necessary to make the statements \
made, in light of the circumstances under which such statements were made, not \
misleading with respect to the period covered by this report;\n\n\
3. Based on my knowledge, the financial statements, and other financial information \
included in this report, fairly present in all material respects the financial \
condition, results of operations and cash flows of the registrant as of, and for, \
the periods presented in this report;\n\n\
4. The registrant's other certifying officer and I are responsible for establishing \
and maintaining disclosure controls and procedures (as defined in Exchange Act \
Rules 13a-15(e) and 15d-15(e)) and internal control over financial reporting \
(as defined in Exchange Act Rules 13a-15(f) and 15d-15(f)) for the registrant \
and have:\n\n\
(a) Designed such disclosure controls and procedures, or caused such disclosure \
controls and procedures to be designed under our supervision, to ensure that \
material information relating to the registrant is made known to us;\n\n\
(b) Designed such internal control over financial reporting, or caused such \
internal control over financial reporting to be designed under our supervision, \
to provide reasonable assurance regarding the reliability of financial reporting;\n\n\
(c) Evaluated the effectiveness of the registrant's disclosure controls and \
procedures and presented in this report our conclusions about the effectiveness \
of the disclosure controls and procedures;\n\n\
(d) Disclosed in this report any change in the registrant's internal control over \
financial reporting that occurred during the registrant's most recent fiscal \
quarter that has materially affected, or is reasonably likely to materially \
affect, the registrant's internal control over financial reporting;\n\n\
5. The registrant's other certifying officer and I have disclosed, based on our most \
recent evaluation of internal control over financial reporting, to the registrant's \
auditors and the audit committee of the registrant's board of directors:\n\n\
(a) All significant deficiencies and material weaknesses in the design or operation \
of internal control over financial reporting which are reasonably likely to \
adversely affect the registrant's ability to record, process, summarize and \
report financial information; and\n\n\
(b) Any fraud, whether or not material, that involves management or other employees \
who have a significant role in the registrant's internal control over financial \
reporting.",
self.certifier_name,
report_name,
self.company_code
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CertifierRole {
Ceo,
Cfo,
PrincipalExecutiveOfficer,
PrincipalFinancialOfficer,
}
impl std::fmt::Display for CertifierRole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ceo => write!(f, "Chief Executive Officer"),
Self::Cfo => write!(f, "Chief Financial Officer"),
Self::PrincipalExecutiveOfficer => write!(f, "Principal Executive Officer"),
Self::PrincipalFinancialOfficer => write!(f, "Principal Financial Officer"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ReportType {
#[default]
AnnualReport10K,
QuarterlyReport10Q,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sox404Assessment {
pub assessment_id: Uuid,
pub company_code: String,
pub fiscal_year: u16,
pub assessment_date: NaiveDate,
pub framework: IcfrFramework,
pub icfr_effective: bool,
pub scope: Vec<ScopedEntity>,
#[serde(with = "datasynth_core::serde_decimal")]
pub materiality_threshold: Decimal,
pub key_controls_tested: usize,
pub key_controls_effective: usize,
pub deficiency_classification: DeficiencyClassificationSummary,
pub material_weaknesses: Vec<MaterialWeakness>,
pub significant_deficiencies: Vec<SignificantDeficiency>,
pub control_deficiencies: Vec<ControlDeficiency>,
pub remediation_actions: Vec<RemediationAction>,
pub management_conclusion: String,
pub management_report_date: NaiveDate,
}
impl Sox404Assessment {
pub fn new(
company_code: impl Into<String>,
fiscal_year: u16,
assessment_date: NaiveDate,
) -> Self {
Self {
assessment_id: Uuid::now_v7(),
company_code: company_code.into(),
fiscal_year,
assessment_date,
framework: IcfrFramework::Coso2013,
icfr_effective: true,
scope: Vec::new(),
materiality_threshold: Decimal::ZERO,
key_controls_tested: 0,
key_controls_effective: 0,
deficiency_classification: DeficiencyClassificationSummary::default(),
material_weaknesses: Vec::new(),
significant_deficiencies: Vec::new(),
control_deficiencies: Vec::new(),
remediation_actions: Vec::new(),
management_conclusion: String::new(),
management_report_date: assessment_date,
}
}
pub fn evaluate_effectiveness(&mut self) {
self.icfr_effective = self.material_weaknesses.is_empty();
}
pub fn effectiveness_rate(&self) -> f64 {
if self.key_controls_tested == 0 {
return 0.0;
}
(self.key_controls_effective as f64 / self.key_controls_tested as f64) * 100.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum IcfrFramework {
#[default]
Coso2013,
Coso1992,
Other,
}
impl std::fmt::Display for IcfrFramework {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Coso2013 => write!(f, "COSO 2013"),
Self::Coso1992 => write!(f, "COSO 1992"),
Self::Other => write!(f, "Other Framework"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScopedEntity {
pub entity_code: String,
pub entity_name: String,
#[serde(with = "datasynth_core::serde_decimal")]
pub revenue_percent: Decimal,
#[serde(with = "datasynth_core::serde_decimal")]
pub assets_percent: Decimal,
pub scope_conclusion: ScopeConclusion,
pub significant_accounts: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ScopeConclusion {
#[default]
InScope,
OutOfScope,
SpecificAccountsOnly,
CommonControl,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DeficiencyClassificationSummary {
pub deficiencies_identified: u32,
pub control_deficiencies: u32,
pub significant_deficiencies: u32,
pub material_weaknesses: u32,
pub remediated: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeficiencyMatrix {
pub likelihood: DeficiencyLikelihood,
pub magnitude: DeficiencyMagnitude,
pub classification: DeficiencyClassification,
}
impl DeficiencyMatrix {
pub fn classify(
likelihood: DeficiencyLikelihood,
magnitude: DeficiencyMagnitude,
) -> DeficiencyClassification {
match (likelihood, magnitude) {
(DeficiencyLikelihood::Probable, DeficiencyMagnitude::Material) => {
DeficiencyClassification::MaterialWeakness
}
(DeficiencyLikelihood::ReasonablyPossible, DeficiencyMagnitude::Material) => {
DeficiencyClassification::MaterialWeakness
}
(DeficiencyLikelihood::Probable, DeficiencyMagnitude::MoreThanInconsequential) => {
DeficiencyClassification::MaterialWeakness
}
(
DeficiencyLikelihood::ReasonablyPossible,
DeficiencyMagnitude::MoreThanInconsequential,
) => DeficiencyClassification::SignificantDeficiency,
(DeficiencyLikelihood::Probable, DeficiencyMagnitude::Inconsequential) => {
DeficiencyClassification::SignificantDeficiency
}
_ => DeficiencyClassification::ControlDeficiency,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DeficiencyLikelihood {
Remote,
#[default]
ReasonablyPossible,
Probable,
}
impl std::fmt::Display for DeficiencyLikelihood {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Remote => write!(f, "Remote"),
Self::ReasonablyPossible => write!(f, "Reasonably Possible"),
Self::Probable => write!(f, "Probable"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DeficiencyMagnitude {
Inconsequential,
#[default]
MoreThanInconsequential,
Material,
}
impl std::fmt::Display for DeficiencyMagnitude {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Inconsequential => write!(f, "Inconsequential"),
Self::MoreThanInconsequential => write!(f, "More Than Inconsequential"),
Self::Material => write!(f, "Material"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum DeficiencyClassification {
#[default]
ControlDeficiency,
SignificantDeficiency,
MaterialWeakness,
}
impl std::fmt::Display for DeficiencyClassification {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ControlDeficiency => write!(f, "Control Deficiency"),
Self::SignificantDeficiency => write!(f, "Significant Deficiency"),
Self::MaterialWeakness => write!(f, "Material Weakness"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialWeakness {
pub weakness_id: Uuid,
pub description: String,
pub affected_controls: Vec<String>,
pub affected_accounts: Vec<String>,
pub related_assertions: Vec<String>,
pub root_cause: String,
#[serde(default, with = "datasynth_core::serde_decimal::option")]
pub potential_misstatement: Option<Decimal>,
pub likelihood: DeficiencyLikelihood,
pub magnitude: DeficiencyMagnitude,
pub identification_date: NaiveDate,
pub remediated_by_year_end: bool,
pub remediation_date: Option<NaiveDate>,
pub related_finding_ids: Vec<Uuid>,
}
impl MaterialWeakness {
pub fn new(description: impl Into<String>, identification_date: NaiveDate) -> Self {
Self {
weakness_id: Uuid::now_v7(),
description: description.into(),
affected_controls: Vec::new(),
affected_accounts: Vec::new(),
related_assertions: Vec::new(),
root_cause: String::new(),
potential_misstatement: None,
likelihood: DeficiencyLikelihood::Probable,
magnitude: DeficiencyMagnitude::Material,
identification_date,
remediated_by_year_end: false,
remediation_date: None,
related_finding_ids: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignificantDeficiency {
pub deficiency_id: Uuid,
pub description: String,
pub affected_controls: Vec<String>,
pub affected_accounts: Vec<String>,
pub likelihood: DeficiencyLikelihood,
pub magnitude: DeficiencyMagnitude,
pub identification_date: NaiveDate,
pub remediated: bool,
}
impl SignificantDeficiency {
pub fn new(description: impl Into<String>, identification_date: NaiveDate) -> Self {
Self {
deficiency_id: Uuid::now_v7(),
description: description.into(),
affected_controls: Vec::new(),
affected_accounts: Vec::new(),
likelihood: DeficiencyLikelihood::ReasonablyPossible,
magnitude: DeficiencyMagnitude::MoreThanInconsequential,
identification_date,
remediated: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControlDeficiency {
pub deficiency_id: Uuid,
pub description: String,
pub affected_control: String,
pub identification_date: NaiveDate,
pub remediated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RemediationAction {
pub action_id: Uuid,
pub deficiency_id: Uuid,
pub description: String,
pub responsible_party: String,
pub target_date: NaiveDate,
pub completion_date: Option<NaiveDate>,
pub status: RemediationStatus,
pub remediation_tested: bool,
pub remediation_effective: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RemediationStatus {
#[default]
NotStarted,
InProgress,
Completed,
Deferred,
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
#[test]
fn test_sox_302_certification() {
let mut cert = Sox302Certification::new(
"ABC Corp",
2024,
NaiveDate::from_ymd_opt(2024, 12, 31).unwrap(),
CertifierRole::Ceo,
"John Smith",
);
cert.generate_certification_text();
assert!(!cert.certification_text.is_empty());
assert!(cert.certification_text.contains("John Smith"));
assert!(cert.disclosure_controls_effective);
}
#[test]
fn test_sox_404_assessment() {
let mut assessment = Sox404Assessment::new(
"ABC Corp",
2024,
NaiveDate::from_ymd_opt(2025, 2, 28).unwrap(),
);
assessment.key_controls_tested = 100;
assessment.key_controls_effective = 95;
assessment.materiality_threshold = dec!(100000);
assert_eq!(assessment.effectiveness_rate(), 95.0);
assert!(assessment.icfr_effective);
}
#[test]
fn test_sox_404_with_material_weakness() {
let mut assessment = Sox404Assessment::new(
"ABC Corp",
2024,
NaiveDate::from_ymd_opt(2025, 2, 28).unwrap(),
);
assessment.material_weaknesses.push(MaterialWeakness::new(
"Inadequate segregation of duties in accounts payable",
NaiveDate::from_ymd_opt(2024, 9, 30).unwrap(),
));
assessment.evaluate_effectiveness();
assert!(!assessment.icfr_effective);
}
#[test]
fn test_deficiency_matrix_classification() {
assert_eq!(
DeficiencyMatrix::classify(
DeficiencyLikelihood::Probable,
DeficiencyMagnitude::Material
),
DeficiencyClassification::MaterialWeakness
);
assert_eq!(
DeficiencyMatrix::classify(
DeficiencyLikelihood::ReasonablyPossible,
DeficiencyMagnitude::Material
),
DeficiencyClassification::MaterialWeakness
);
assert_eq!(
DeficiencyMatrix::classify(
DeficiencyLikelihood::ReasonablyPossible,
DeficiencyMagnitude::MoreThanInconsequential
),
DeficiencyClassification::SignificantDeficiency
);
assert_eq!(
DeficiencyMatrix::classify(
DeficiencyLikelihood::Remote,
DeficiencyMagnitude::Inconsequential
),
DeficiencyClassification::ControlDeficiency
);
}
}