1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use super::coso::{ControlScope, CosoComponent, CosoMaturityLevel, CosoPrinciple};
10use super::graph_properties::{GraphPropertyValue, ToNodeProperties};
11use super::user::UserPersona;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum ControlType {
17 Preventive,
19 Detective,
21 Monitoring,
23}
24
25impl std::fmt::Display for ControlType {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 Self::Preventive => write!(f, "Preventive"),
29 Self::Detective => write!(f, "Detective"),
30 Self::Monitoring => write!(f, "Monitoring"),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "snake_case")]
38pub enum ControlFrequency {
39 Transactional,
41 Daily,
43 Weekly,
45 Monthly,
47 Quarterly,
49 Annual,
51}
52
53impl std::fmt::Display for ControlFrequency {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Transactional => write!(f, "Transactional"),
57 Self::Daily => write!(f, "Daily"),
58 Self::Weekly => write!(f, "Weekly"),
59 Self::Monthly => write!(f, "Monthly"),
60 Self::Quarterly => write!(f, "Quarterly"),
61 Self::Annual => write!(f, "Annual"),
62 }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
68#[serde(rename_all = "snake_case")]
69pub enum RiskLevel {
70 Low,
72 Medium,
74 High,
76 Critical,
78}
79
80impl std::fmt::Display for RiskLevel {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 Self::Low => write!(f, "Low"),
84 Self::Medium => write!(f, "Medium"),
85 Self::High => write!(f, "High"),
86 Self::Critical => write!(f, "Critical"),
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub enum SoxAssertion {
95 Existence,
97 Completeness,
99 Valuation,
101 RightsAndObligations,
103 PresentationAndDisclosure,
105}
106
107impl ToNodeProperties for SoxAssertion {
108 fn node_type_name(&self) -> &'static str {
109 "sox_assertion"
110 }
111 fn node_type_code(&self) -> u16 {
112 502
113 }
114 fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
115 let mut p = HashMap::new();
116 p.insert("name".into(), GraphPropertyValue::String(self.to_string()));
117 p.insert(
118 "code".into(),
119 GraphPropertyValue::String(format!("{:?}", self)),
120 );
121 p
122 }
123}
124
125impl std::fmt::Display for SoxAssertion {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {
128 Self::Existence => write!(f, "Existence"),
129 Self::Completeness => write!(f, "Completeness"),
130 Self::Valuation => write!(f, "Valuation"),
131 Self::RightsAndObligations => write!(f, "RightsAndObligations"),
132 Self::PresentationAndDisclosure => write!(f, "PresentationAndDisclosure"),
133 }
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
139#[serde(rename_all = "snake_case")]
140pub enum ControlStatus {
141 #[default]
143 Effective,
144 Exception,
146 NotTested,
148 Remediated,
150}
151
152impl std::fmt::Display for ControlStatus {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self {
155 Self::Effective => write!(f, "Effective"),
156 Self::Exception => write!(f, "Exception"),
157 Self::NotTested => write!(f, "NotTested"),
158 Self::Remediated => write!(f, "Remediated"),
159 }
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct InternalControl {
166 pub control_id: String,
168 pub control_name: String,
170 pub control_type: ControlType,
172 pub objective: String,
174 pub frequency: ControlFrequency,
176 pub owner_role: UserPersona,
178 pub risk_level: RiskLevel,
180 pub description: String,
182 pub is_key_control: bool,
184 pub sox_assertion: SoxAssertion,
186 pub coso_component: CosoComponent,
188 pub coso_principles: Vec<CosoPrinciple>,
190 pub control_scope: ControlScope,
192 pub maturity_level: CosoMaturityLevel,
194}
195
196impl InternalControl {
197 pub fn new(
199 control_id: impl Into<String>,
200 control_name: impl Into<String>,
201 control_type: ControlType,
202 objective: impl Into<String>,
203 ) -> Self {
204 Self {
205 control_id: control_id.into(),
206 control_name: control_name.into(),
207 control_type,
208 objective: objective.into(),
209 frequency: ControlFrequency::Transactional,
210 owner_role: UserPersona::Controller,
211 risk_level: RiskLevel::Medium,
212 description: String::new(),
213 is_key_control: false,
214 sox_assertion: SoxAssertion::Existence,
215 coso_component: CosoComponent::ControlActivities,
216 coso_principles: vec![CosoPrinciple::ControlActions],
217 control_scope: ControlScope::TransactionLevel,
218 maturity_level: CosoMaturityLevel::Defined,
219 }
220 }
221
222 pub fn with_frequency(mut self, frequency: ControlFrequency) -> Self {
224 self.frequency = frequency;
225 self
226 }
227
228 pub fn with_owner(mut self, owner: UserPersona) -> Self {
230 self.owner_role = owner;
231 self
232 }
233
234 pub fn with_risk_level(mut self, level: RiskLevel) -> Self {
236 self.risk_level = level;
237 self
238 }
239
240 pub fn with_description(mut self, description: impl Into<String>) -> Self {
242 self.description = description.into();
243 self
244 }
245
246 pub fn as_key_control(mut self) -> Self {
248 self.is_key_control = true;
249 self
250 }
251
252 pub fn with_assertion(mut self, assertion: SoxAssertion) -> Self {
254 self.sox_assertion = assertion;
255 self
256 }
257
258 pub fn with_coso_component(mut self, component: CosoComponent) -> Self {
260 self.coso_component = component;
261 self
262 }
263
264 pub fn with_coso_principles(mut self, principles: Vec<CosoPrinciple>) -> Self {
266 self.coso_principles = principles;
267 self
268 }
269
270 pub fn with_control_scope(mut self, scope: ControlScope) -> Self {
272 self.control_scope = scope;
273 self
274 }
275
276 pub fn with_maturity_level(mut self, level: CosoMaturityLevel) -> Self {
278 self.maturity_level = level;
279 self
280 }
281
282 pub fn standard_controls() -> Vec<Self> {
287 vec![
288 Self::new(
294 "C001",
295 "Cash Account Daily Review",
296 ControlType::Detective,
297 "Review all cash transactions daily for unauthorized activity",
298 )
299 .with_frequency(ControlFrequency::Daily)
300 .with_owner(UserPersona::Controller)
301 .with_risk_level(RiskLevel::High)
302 .as_key_control()
303 .with_assertion(SoxAssertion::Existence)
304 .with_description(
305 "Daily reconciliation of cash accounts with bank statements and review of unusual transactions",
306 )
307 .with_coso_component(CosoComponent::ControlActivities)
308 .with_coso_principles(vec![
309 CosoPrinciple::ControlActions,
310 CosoPrinciple::OngoingMonitoring,
311 ])
312 .with_control_scope(ControlScope::TransactionLevel)
313 .with_maturity_level(CosoMaturityLevel::Managed),
314
315 Self::new(
317 "C002",
318 "Large Transaction Multi-Level Approval",
319 ControlType::Preventive,
320 "Transactions over $10,000 require additional approval levels",
321 )
322 .with_frequency(ControlFrequency::Transactional)
323 .with_owner(UserPersona::Manager)
324 .with_risk_level(RiskLevel::High)
325 .as_key_control()
326 .with_assertion(SoxAssertion::Valuation)
327 .with_description(
328 "Multi-level approval workflow for transactions exceeding defined thresholds",
329 )
330 .with_coso_component(CosoComponent::ControlActivities)
331 .with_coso_principles(vec![
332 CosoPrinciple::ControlActions,
333 CosoPrinciple::PoliciesAndProcedures,
334 ])
335 .with_control_scope(ControlScope::TransactionLevel)
336 .with_maturity_level(CosoMaturityLevel::Defined),
337
338 Self::new(
340 "C010",
341 "Three-Way Match",
342 ControlType::Preventive,
343 "Match purchase order, receipt, and invoice before payment",
344 )
345 .with_frequency(ControlFrequency::Transactional)
346 .with_owner(UserPersona::SeniorAccountant)
347 .with_risk_level(RiskLevel::Medium)
348 .as_key_control()
349 .with_assertion(SoxAssertion::Completeness)
350 .with_description(
351 "Automated matching of PO, goods receipt, and vendor invoice prior to payment release",
352 )
353 .with_coso_component(CosoComponent::ControlActivities)
354 .with_coso_principles(vec![
355 CosoPrinciple::ControlActions,
356 CosoPrinciple::TechnologyControls,
357 ])
358 .with_control_scope(ControlScope::ItApplicationControl)
359 .with_maturity_level(CosoMaturityLevel::Managed),
360
361 Self::new(
363 "C011",
364 "Vendor Master Data Maintenance",
365 ControlType::Preventive,
366 "Segregated access for vendor master data changes",
367 )
368 .with_frequency(ControlFrequency::Transactional)
369 .with_owner(UserPersona::SeniorAccountant)
370 .with_risk_level(RiskLevel::High)
371 .as_key_control()
372 .with_assertion(SoxAssertion::Existence)
373 .with_description(
374 "Restricted access to vendor master data with dual-approval for bank account changes",
375 )
376 .with_coso_component(CosoComponent::ControlActivities)
377 .with_coso_principles(vec![
378 CosoPrinciple::ControlActions,
379 CosoPrinciple::FraudRisk,
380 ])
381 .with_control_scope(ControlScope::TransactionLevel)
382 .with_maturity_level(CosoMaturityLevel::Defined),
383
384 Self::new(
386 "C020",
387 "Revenue Recognition Review",
388 ControlType::Detective,
389 "Review revenue entries for proper timing and classification",
390 )
391 .with_frequency(ControlFrequency::Monthly)
392 .with_owner(UserPersona::Controller)
393 .with_risk_level(RiskLevel::Critical)
394 .as_key_control()
395 .with_assertion(SoxAssertion::Valuation)
396 .with_description(
397 "Monthly review of revenue recognition to ensure compliance with ASC 606",
398 )
399 .with_coso_component(CosoComponent::ControlActivities)
400 .with_coso_principles(vec![
401 CosoPrinciple::ControlActions,
402 CosoPrinciple::ClearObjectives,
403 ])
404 .with_control_scope(ControlScope::TransactionLevel)
405 .with_maturity_level(CosoMaturityLevel::Managed),
406
407 Self::new(
409 "C021",
410 "Customer Credit Limit Check",
411 ControlType::Preventive,
412 "Automatic credit limit check before order acceptance",
413 )
414 .with_frequency(ControlFrequency::Transactional)
415 .with_owner(UserPersona::AutomatedSystem)
416 .with_risk_level(RiskLevel::Medium)
417 .with_assertion(SoxAssertion::Valuation)
418 .with_description(
419 "System-enforced credit limit validation at order entry",
420 )
421 .with_coso_component(CosoComponent::ControlActivities)
422 .with_coso_principles(vec![
423 CosoPrinciple::TechnologyControls,
424 CosoPrinciple::ControlActions,
425 ])
426 .with_control_scope(ControlScope::ItApplicationControl)
427 .with_maturity_level(CosoMaturityLevel::Optimized),
428
429 Self::new(
431 "C030",
432 "GL Account Reconciliation",
433 ControlType::Detective,
434 "Monthly reconciliation of all balance sheet accounts",
435 )
436 .with_frequency(ControlFrequency::Monthly)
437 .with_owner(UserPersona::SeniorAccountant)
438 .with_risk_level(RiskLevel::High)
439 .as_key_control()
440 .with_assertion(SoxAssertion::Completeness)
441 .with_description(
442 "Complete reconciliation of all balance sheet accounts with supporting documentation",
443 )
444 .with_coso_component(CosoComponent::MonitoringActivities)
445 .with_coso_principles(vec![CosoPrinciple::OngoingMonitoring])
446 .with_control_scope(ControlScope::TransactionLevel)
447 .with_maturity_level(CosoMaturityLevel::Managed),
448
449 Self::new(
451 "C031",
452 "Manual Journal Entry Review",
453 ControlType::Detective,
454 "Review of all manual journal entries over threshold",
455 )
456 .with_frequency(ControlFrequency::Daily)
457 .with_owner(UserPersona::Controller)
458 .with_risk_level(RiskLevel::High)
459 .as_key_control()
460 .with_assertion(SoxAssertion::Existence)
461 .with_description(
462 "Daily review of manual journal entries with supporting documentation",
463 )
464 .with_coso_component(CosoComponent::ControlActivities)
465 .with_coso_principles(vec![
466 CosoPrinciple::ControlActions,
467 CosoPrinciple::FraudRisk,
468 ])
469 .with_control_scope(ControlScope::TransactionLevel)
470 .with_maturity_level(CosoMaturityLevel::Managed),
471
472 Self::new(
474 "C032",
475 "Period Close Checklist",
476 ControlType::Detective,
477 "Comprehensive checklist for period-end close procedures",
478 )
479 .with_frequency(ControlFrequency::Monthly)
480 .with_owner(UserPersona::Controller)
481 .with_risk_level(RiskLevel::Medium)
482 .with_assertion(SoxAssertion::Completeness)
483 .with_description(
484 "Standardized period-end close checklist ensuring all procedures completed",
485 )
486 .with_coso_component(CosoComponent::ControlActivities)
487 .with_coso_principles(vec![
488 CosoPrinciple::PoliciesAndProcedures,
489 CosoPrinciple::ControlActions,
490 ])
491 .with_control_scope(ControlScope::TransactionLevel)
492 .with_maturity_level(CosoMaturityLevel::Defined),
493
494 Self::new(
496 "C040",
497 "Payroll Processing Review",
498 ControlType::Detective,
499 "Review of payroll processing for accuracy",
500 )
501 .with_frequency(ControlFrequency::Monthly)
502 .with_owner(UserPersona::Controller)
503 .with_risk_level(RiskLevel::High)
504 .as_key_control()
505 .with_assertion(SoxAssertion::Valuation)
506 .with_description(
507 "Monthly review of payroll journals and reconciliation to HR records",
508 )
509 .with_coso_component(CosoComponent::ControlActivities)
510 .with_coso_principles(vec![
511 CosoPrinciple::ControlActions,
512 CosoPrinciple::FraudRisk,
513 ])
514 .with_control_scope(ControlScope::TransactionLevel)
515 .with_maturity_level(CosoMaturityLevel::Managed),
516
517 Self::new(
519 "C050",
520 "Fixed Asset Addition Approval",
521 ControlType::Preventive,
522 "Multi-level approval for capital expenditures",
523 )
524 .with_frequency(ControlFrequency::Transactional)
525 .with_owner(UserPersona::Manager)
526 .with_risk_level(RiskLevel::Medium)
527 .with_assertion(SoxAssertion::Existence)
528 .with_description(
529 "Approval workflow for capital asset additions based on dollar thresholds",
530 )
531 .with_coso_component(CosoComponent::ControlActivities)
532 .with_coso_principles(vec![
533 CosoPrinciple::ControlActions,
534 CosoPrinciple::PoliciesAndProcedures,
535 ])
536 .with_control_scope(ControlScope::TransactionLevel)
537 .with_maturity_level(CosoMaturityLevel::Defined),
538
539 Self::new(
541 "C060",
542 "Intercompany Balance Reconciliation",
543 ControlType::Detective,
544 "Monthly reconciliation of intercompany balances",
545 )
546 .with_frequency(ControlFrequency::Monthly)
547 .with_owner(UserPersona::SeniorAccountant)
548 .with_risk_level(RiskLevel::High)
549 .as_key_control()
550 .with_assertion(SoxAssertion::Completeness)
551 .with_description(
552 "Full reconciliation of intercompany accounts between all entities",
553 )
554 .with_coso_component(CosoComponent::MonitoringActivities)
555 .with_coso_principles(vec![
556 CosoPrinciple::OngoingMonitoring,
557 CosoPrinciple::DeficiencyEvaluation,
558 ])
559 .with_control_scope(ControlScope::TransactionLevel)
560 .with_maturity_level(CosoMaturityLevel::Managed),
561
562 Self::new(
568 "C070",
569 "Code of Conduct and Ethics",
570 ControlType::Preventive,
571 "Establish and communicate ethical values and standards of conduct",
572 )
573 .with_frequency(ControlFrequency::Annual)
574 .with_owner(UserPersona::Controller)
575 .with_risk_level(RiskLevel::High)
576 .as_key_control()
577 .with_assertion(SoxAssertion::PresentationAndDisclosure)
578 .with_description(
579 "Annual review and acknowledgment of code of conduct by all employees; \
580 includes ethics hotline and whistleblower protections",
581 )
582 .with_coso_component(CosoComponent::ControlEnvironment)
583 .with_coso_principles(vec![
584 CosoPrinciple::IntegrityAndEthics,
585 CosoPrinciple::Accountability,
586 ])
587 .with_control_scope(ControlScope::EntityLevel)
588 .with_maturity_level(CosoMaturityLevel::Managed),
589
590 Self::new(
592 "C071",
593 "Audit Committee Oversight",
594 ControlType::Monitoring,
595 "Board and audit committee exercise independent oversight of internal control",
596 )
597 .with_frequency(ControlFrequency::Quarterly)
598 .with_owner(UserPersona::Controller)
599 .with_risk_level(RiskLevel::Critical)
600 .as_key_control()
601 .with_assertion(SoxAssertion::PresentationAndDisclosure)
602 .with_description(
603 "Quarterly audit committee meetings with review of internal control effectiveness, \
604 external auditor findings, and management representations",
605 )
606 .with_coso_component(CosoComponent::ControlEnvironment)
607 .with_coso_principles(vec![
608 CosoPrinciple::BoardOversight,
609 CosoPrinciple::OrganizationalStructure,
610 ])
611 .with_control_scope(ControlScope::EntityLevel)
612 .with_maturity_level(CosoMaturityLevel::Managed),
613
614 Self::new(
616 "C075",
617 "Enterprise Risk Assessment",
618 ControlType::Detective,
619 "Identify and assess risks to achievement of organizational objectives",
620 )
621 .with_frequency(ControlFrequency::Annual)
622 .with_owner(UserPersona::Controller)
623 .with_risk_level(RiskLevel::High)
624 .as_key_control()
625 .with_assertion(SoxAssertion::Completeness)
626 .with_description(
627 "Annual enterprise risk assessment process including fraud risk evaluation; \
628 risk register maintained and updated quarterly",
629 )
630 .with_coso_component(CosoComponent::RiskAssessment)
631 .with_coso_principles(vec![
632 CosoPrinciple::IdentifyRisks,
633 CosoPrinciple::FraudRisk,
634 CosoPrinciple::ChangeIdentification,
635 ])
636 .with_control_scope(ControlScope::EntityLevel)
637 .with_maturity_level(CosoMaturityLevel::Defined),
638
639 Self::new(
641 "C077",
642 "IT General Controls Program",
643 ControlType::Preventive,
644 "General controls over IT environment supporting financial reporting systems",
645 )
646 .with_frequency(ControlFrequency::Transactional)
647 .with_owner(UserPersona::AutomatedSystem)
648 .with_risk_level(RiskLevel::High)
649 .as_key_control()
650 .with_assertion(SoxAssertion::Existence)
651 .with_description(
652 "IT general controls including access management, change management, \
653 computer operations, and program development for systems supporting \
654 financial reporting",
655 )
656 .with_coso_component(CosoComponent::ControlActivities)
657 .with_coso_principles(vec![
658 CosoPrinciple::TechnologyControls,
659 CosoPrinciple::PoliciesAndProcedures,
660 ])
661 .with_control_scope(ControlScope::ItGeneralControl)
662 .with_maturity_level(CosoMaturityLevel::Managed),
663
664 Self::new(
666 "C078",
667 "Financial Information Quality",
668 ControlType::Detective,
669 "Obtain and use quality information for internal control",
670 )
671 .with_frequency(ControlFrequency::Monthly)
672 .with_owner(UserPersona::Controller)
673 .with_risk_level(RiskLevel::Medium)
674 .with_assertion(SoxAssertion::Valuation)
675 .with_description(
676 "Monthly data quality reviews for key financial reports; validation of \
677 data inputs, processing, and outputs supporting management decisions",
678 )
679 .with_coso_component(CosoComponent::InformationCommunication)
680 .with_coso_principles(vec![
681 CosoPrinciple::QualityInformation,
682 CosoPrinciple::InternalCommunication,
683 ])
684 .with_control_scope(ControlScope::EntityLevel)
685 .with_maturity_level(CosoMaturityLevel::Defined),
686
687 Self::new(
689 "C081",
690 "Internal Control Monitoring Program",
691 ControlType::Monitoring,
692 "Ongoing and periodic evaluations of internal control effectiveness",
693 )
694 .with_frequency(ControlFrequency::Quarterly)
695 .with_owner(UserPersona::Controller)
696 .with_risk_level(RiskLevel::High)
697 .as_key_control()
698 .with_assertion(SoxAssertion::Completeness)
699 .with_description(
700 "Continuous monitoring program with quarterly control testing, \
701 deficiency tracking, and remediation management; annual SOX 404 \
702 assessment and certification",
703 )
704 .with_coso_component(CosoComponent::MonitoringActivities)
705 .with_coso_principles(vec![
706 CosoPrinciple::OngoingMonitoring,
707 CosoPrinciple::DeficiencyEvaluation,
708 ])
709 .with_control_scope(ControlScope::EntityLevel)
710 .with_maturity_level(CosoMaturityLevel::Managed),
711 ]
712 }
713}
714
715#[cfg(test)]
716#[allow(clippy::unwrap_used)]
717mod tests {
718 use super::*;
719
720 #[test]
721 fn test_control_creation() {
722 let control = InternalControl::new(
723 "TEST001",
724 "Test Control",
725 ControlType::Preventive,
726 "Test objective",
727 )
728 .with_frequency(ControlFrequency::Daily)
729 .with_risk_level(RiskLevel::High)
730 .as_key_control();
731
732 assert_eq!(control.control_id, "TEST001");
733 assert_eq!(control.control_type, ControlType::Preventive);
734 assert_eq!(control.frequency, ControlFrequency::Daily);
735 assert_eq!(control.risk_level, RiskLevel::High);
736 assert!(control.is_key_control);
737 }
738
739 #[test]
740 fn test_standard_controls() {
741 let controls = InternalControl::standard_controls();
742 assert!(!controls.is_empty());
743
744 let key_controls: Vec<_> = controls.iter().filter(|c| c.is_key_control).collect();
746 assert!(key_controls.len() >= 5);
747
748 let preventive: Vec<_> = controls
750 .iter()
751 .filter(|c| c.control_type == ControlType::Preventive)
752 .collect();
753 let detective: Vec<_> = controls
754 .iter()
755 .filter(|c| c.control_type == ControlType::Detective)
756 .collect();
757
758 assert!(!preventive.is_empty());
759 assert!(!detective.is_empty());
760 }
761
762 #[test]
763 fn test_control_status_display() {
764 assert_eq!(ControlStatus::Effective.to_string(), "Effective");
765 assert_eq!(ControlStatus::Exception.to_string(), "Exception");
766 }
767}