Skip to main content

datasynth_core/models/audit/
service_organization.rs

1//! Service organization and SOC report models per ISA 402.
2//!
3//! ISA 402 addresses the responsibilities of the user auditor when the user entity
4//! uses services provided by a service organization.  When the services form part
5//! of the user entity's information system relevant to financial reporting, the
6//! auditor must obtain an understanding of how that affects the assessment of
7//! risks of material misstatement.
8//!
9//! SOC 1 reports (Service Organization Control 1) describe the controls at a
10//! service organization relevant to user entity's financial reporting.
11
12use chrono::NaiveDate;
13use serde::{Deserialize, Serialize};
14
15/// A service organization used by one or more audited entities.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ServiceOrganization {
18    /// Unique identifier
19    pub id: String,
20    /// Name of the service organization
21    pub name: String,
22    /// Type of service provided
23    pub service_type: ServiceType,
24    /// Entity codes of user entities served by this organization
25    pub entities_served: Vec<String>,
26}
27
28/// Type of service provided by the service organization.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
30#[serde(rename_all = "snake_case")]
31pub enum ServiceType {
32    /// Payroll processing services
33    #[default]
34    PayrollProcessor,
35    /// Cloud hosting and infrastructure services
36    CloudHosting,
37    /// Payment processing services
38    PaymentProcessor,
39    /// IT managed services
40    ItManagedServices,
41    /// Data centre colocation
42    DataCentre,
43}
44
45/// A SOC 1 report obtained from or about a service organization.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct SocReport {
48    /// Unique identifier for this report
49    pub id: String,
50    /// Reference to the service organization
51    pub service_org_id: String,
52    /// Type of SOC report
53    pub report_type: SocReportType,
54    /// Start of the period covered by the report
55    pub report_period_start: NaiveDate,
56    /// End of the period covered by the report
57    pub report_period_end: NaiveDate,
58    /// Opinion issued by the service auditor
59    pub opinion_type: SocOpinionType,
60    /// Control objectives included in the report
61    pub control_objectives: Vec<ControlObjective>,
62    /// Exceptions noted during testing
63    pub exceptions_noted: Vec<SocException>,
64}
65
66/// SOC report type.
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
68#[serde(rename_all = "snake_case")]
69pub enum SocReportType {
70    /// SOC 1 Type I: design of controls at a point in time
71    Soc1Type1,
72    /// SOC 1 Type II: design and operating effectiveness over a period
73    #[default]
74    Soc1Type2,
75}
76
77/// Opinion issued by the service auditor on the SOC report.
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
79#[serde(rename_all = "snake_case")]
80pub enum SocOpinionType {
81    /// Unmodified opinion — controls are suitably designed and operating effectively
82    #[default]
83    Unmodified,
84    /// Qualified opinion — one or more control objectives have exceptions
85    Qualified,
86    /// Adverse opinion — controls are not suitably designed or not operating effectively
87    Adverse,
88}
89
90/// A control objective within a SOC report.
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct ControlObjective {
93    /// Unique identifier for this objective
94    pub id: String,
95    /// Description of the control objective
96    pub description: String,
97    /// Number of controls tested against this objective
98    pub controls_tested: u32,
99    /// Whether all tested controls operated effectively
100    pub controls_effective: bool,
101}
102
103/// An exception noted during SOC report testing.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct SocException {
106    /// ID of the control objective to which this exception relates
107    pub control_objective_id: String,
108    /// Description of the exception
109    pub description: String,
110    /// Service organization's management response to the exception
111    pub management_response: String,
112    /// Impact assessment for the user entity
113    pub user_entity_impact: String,
114}
115
116/// A complementary user entity control (CUEC) mapped to a SOC objective.
117///
118/// User entities are responsible for implementing certain controls to complement
119/// the controls at the service organization.  These are documented by the user
120/// auditor per ISA 402 requirements.
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct UserEntityControl {
123    /// Unique identifier
124    pub id: String,
125    /// Reference to the SOC report this control relates to
126    pub soc_report_id: String,
127    /// Description of the user entity control
128    pub description: String,
129    /// ID of the SOC control objective this control maps to
130    pub mapped_objective: String,
131    /// Whether the control has been implemented
132    pub implemented: bool,
133    /// Operating effectiveness assessment
134    pub operating_effectiveness: ControlEffectiveness,
135}
136
137/// Operating effectiveness assessment for a user entity control.
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
139#[serde(rename_all = "snake_case")]
140pub enum ControlEffectiveness {
141    /// Control is operating effectively
142    #[default]
143    Effective,
144    /// Control has minor exceptions but is substantially effective
145    EffectiveWithExceptions,
146    /// Control is not operating effectively
147    Ineffective,
148    /// Control has not been tested
149    NotTested,
150}
151
152impl ServiceOrganization {
153    /// Create a new service organization.
154    pub fn new(
155        name: impl Into<String>,
156        service_type: ServiceType,
157        entities_served: Vec<String>,
158    ) -> Self {
159        Self {
160            id: crate::clock::next_document_id().to_string(),
161            name: name.into(),
162            service_type,
163            entities_served,
164        }
165    }
166}
167
168impl SocReport {
169    /// Create a new SOC report.
170    pub fn new(
171        service_org_id: impl Into<String>,
172        report_type: SocReportType,
173        report_period_start: NaiveDate,
174        report_period_end: NaiveDate,
175        opinion_type: SocOpinionType,
176    ) -> Self {
177        Self {
178            id: crate::clock::next_document_id().to_string(),
179            service_org_id: service_org_id.into(),
180            report_type,
181            report_period_start,
182            report_period_end,
183            opinion_type,
184            control_objectives: Vec::new(),
185            exceptions_noted: Vec::new(),
186        }
187    }
188}
189
190impl ControlObjective {
191    /// Create a new control objective.
192    pub fn new(
193        description: impl Into<String>,
194        controls_tested: u32,
195        controls_effective: bool,
196    ) -> Self {
197        Self {
198            id: crate::clock::next_document_id().to_string(),
199            description: description.into(),
200            controls_tested,
201            controls_effective,
202        }
203    }
204}
205
206impl UserEntityControl {
207    /// Create a new user entity control.
208    pub fn new(
209        soc_report_id: impl Into<String>,
210        description: impl Into<String>,
211        mapped_objective: impl Into<String>,
212        implemented: bool,
213        operating_effectiveness: ControlEffectiveness,
214    ) -> Self {
215        Self {
216            id: crate::clock::next_document_id().to_string(),
217            soc_report_id: soc_report_id.into(),
218            description: description.into(),
219            mapped_objective: mapped_objective.into(),
220            implemented,
221            operating_effectiveness,
222        }
223    }
224}