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}