datasynth_core/models/audit/scots.rs
1//! Significant Classes of Transactions (SCOTS) per ISA 315 (Revised 2019).
2//!
3//! ISA 315.26 requires the auditor to identify and understand the significant
4//! classes of transactions (SCOTs), account balances and disclosures. SCOTs
5//! drive the design of the auditor's information-technology and internal-control
6//! understanding and the nature, timing, and extent of further audit procedures.
7//!
8//! Each SCOT is characterised by:
9//! - Its business process (O2C, P2P, R2R, H2R)
10//! - Transaction type (routine, non-routine, estimation)
11//! - Processing method (fully automated, semi-automated, manual)
12//! - A critical path of four stages (Initiation → Recording → Processing → Reporting)
13//! - Relevant financial statement assertions (from the CRA model)
14//! - For estimation SCOTs: an estimation complexity rating per ISA 540
15//!
16//! References:
17//! - ISA 315 (Revised 2019) §26 — Significant classes of transactions
18//! - ISA 330 §6 — Further audit procedures in response to SCOT assessment
19//! - ISA 540 — Accounting estimates (drives estimation complexity)
20
21use rust_decimal::Decimal;
22use serde::{Deserialize, Serialize};
23
24// ---------------------------------------------------------------------------
25// Enumerations
26// ---------------------------------------------------------------------------
27
28/// Significance level of a class of transactions for the audit.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum ScotSignificance {
32 /// Material individually or in aggregate; requires the most extensive procedures.
33 High,
34 /// Significant but not individually material; moderate extent of procedures.
35 Medium,
36 /// Below materiality; limited procedures or analytical only.
37 Low,
38}
39
40impl std::fmt::Display for ScotSignificance {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 let s = match self {
43 Self::High => "High",
44 Self::Medium => "Medium",
45 Self::Low => "Low",
46 };
47 write!(f, "{s}")
48 }
49}
50
51/// Type of transaction within a SCOT.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53#[serde(rename_all = "snake_case")]
54pub enum ScotTransactionType {
55 /// High-volume, standardised, recurring transactions with consistent processing.
56 Routine,
57 /// Infrequent or unusual transactions requiring significant judgment or
58 /// management approval (e.g. asset disposals, significant contracts).
59 NonRoutine,
60 /// Accounting estimates with inherent measurement uncertainty (ISA 540).
61 Estimation,
62}
63
64impl std::fmt::Display for ScotTransactionType {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 let s = match self {
67 Self::Routine => "Routine",
68 Self::NonRoutine => "Non-Routine",
69 Self::Estimation => "Estimation",
70 };
71 write!(f, "{s}")
72 }
73}
74
75/// How transactions within the SCOT are processed.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub enum ProcessingMethod {
79 /// System-initiated and system-posted — no manual intervention in normal processing.
80 FullyAutomated,
81 /// System-initiated but requires manual approval or manual journal entry
82 /// for certain steps (e.g. three-way match exception handling).
83 SemiAutomated,
84 /// Primarily manual processing — spreadsheet-based, clerk-prepared, etc.
85 Manual,
86}
87
88impl std::fmt::Display for ProcessingMethod {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 let s = match self {
91 Self::FullyAutomated => "Fully Automated",
92 Self::SemiAutomated => "Semi-Automated",
93 Self::Manual => "Manual",
94 };
95 write!(f, "{s}")
96 }
97}
98
99/// Complexity of the underlying estimate per ISA 540.
100///
101/// Only populated for `ScotTransactionType::Estimation` SCOTs.
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
103#[serde(rename_all = "snake_case")]
104pub enum EstimationComplexity {
105 /// Straightforward estimation with observable inputs (e.g. straight-line depreciation).
106 Simple,
107 /// Moderate complexity — some unobservable inputs or model uncertainty
108 /// (e.g. ECL provisioning with internal historical data).
109 Moderate,
110 /// Highly complex — significant unobservable inputs, multiple methodologies possible,
111 /// or high sensitivity to assumptions (e.g. pension obligations, level-3 fair value).
112 Complex,
113}
114
115impl std::fmt::Display for EstimationComplexity {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 let s = match self {
118 Self::Simple => "Simple",
119 Self::Moderate => "Moderate",
120 Self::Complex => "Complex",
121 };
122 write!(f, "{s}")
123 }
124}
125
126// ---------------------------------------------------------------------------
127// Critical path
128// ---------------------------------------------------------------------------
129
130/// A single stage in the SCOT's transaction processing critical path.
131///
132/// The standard four stages are:
133/// 1. Initiation — how transactions are initiated (automated trigger or manual request)
134/// 2. Recording — how the transaction is recorded in source documents / systems
135/// 3. Processing — system processing, matching, posting to the GL
136/// 4. Reporting — how the transaction flows into the financial statements
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct CriticalPathStage {
139 /// Stage name (e.g. "Initiation", "Recording", "Processing", "Reporting").
140 pub stage_name: String,
141 /// Brief description of how this stage operates for this SCOT.
142 pub description: String,
143 /// Whether this stage is fully automated (system-driven, no manual input).
144 pub is_automated: bool,
145 /// ID of the key internal control operating at this stage, if any.
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub key_control_id: Option<String>,
148}
149
150// ---------------------------------------------------------------------------
151// Main SCOT struct
152// ---------------------------------------------------------------------------
153
154/// A Significant Class of Transactions (SCOT) per ISA 315.
155///
156/// One SCOT is generated per major business process / transaction class.
157/// SCOTs drive the scope of the auditor's control and substantive testing.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct SignificantClassOfTransactions {
160 /// Unique identifier for this SCOT (deterministic slug).
161 pub id: String,
162 /// Entity / company code.
163 pub entity_code: String,
164 /// Descriptive name (e.g. "Revenue — Product Sales", "Purchases — Raw Materials").
165 pub scot_name: String,
166 /// Business process code driving this class (O2C, P2P, R2R, H2R, etc.).
167 pub business_process: String,
168 /// Significance of this SCOT for the audit.
169 pub significance_level: ScotSignificance,
170 /// Whether the transactions are routine, non-routine, or estimation-based.
171 pub transaction_type: ScotTransactionType,
172 /// Primary processing method for this class of transactions.
173 pub processing_method: ProcessingMethod,
174 /// Approximate number of transactions in the period.
175 pub volume: usize,
176 /// Aggregate monetary value of transactions in the period.
177 #[serde(with = "crate::serde_decimal")]
178 pub monetary_value: Decimal,
179 /// The four-stage critical path (Initiation → Recording → Processing → Reporting).
180 pub critical_path: Vec<CriticalPathStage>,
181 /// Financial statement assertions relevant to this SCOT (links to CRA assertions).
182 pub relevant_assertions: Vec<String>,
183 /// GL account areas affected by this SCOT.
184 pub related_account_areas: Vec<String>,
185 /// Estimation complexity — only set for `ScotTransactionType::Estimation` SCOTs.
186 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub estimation_complexity: Option<EstimationComplexity>,
188}
189
190// ---------------------------------------------------------------------------
191// Tests
192// ---------------------------------------------------------------------------
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use rust_decimal_macros::dec;
198
199 #[test]
200 fn scot_display_impls() {
201 assert_eq!(ScotSignificance::High.to_string(), "High");
202 assert_eq!(ScotTransactionType::Estimation.to_string(), "Estimation");
203 assert_eq!(
204 ProcessingMethod::FullyAutomated.to_string(),
205 "Fully Automated"
206 );
207 assert_eq!(EstimationComplexity::Complex.to_string(), "Complex");
208 }
209
210 #[test]
211 fn scot_structure() {
212 let scot = SignificantClassOfTransactions {
213 id: "SCOT-C001-REVENUE_PRODUCT_SALES".into(),
214 entity_code: "C001".into(),
215 scot_name: "Revenue — Product Sales".into(),
216 business_process: "O2C".into(),
217 significance_level: ScotSignificance::High,
218 transaction_type: ScotTransactionType::Routine,
219 processing_method: ProcessingMethod::SemiAutomated,
220 volume: 5_000,
221 monetary_value: dec!(10_000_000),
222 critical_path: vec![
223 CriticalPathStage {
224 stage_name: "Initiation".into(),
225 description: "Sales order created by customer / sales team".into(),
226 is_automated: false,
227 key_control_id: Some("C001".into()),
228 },
229 CriticalPathStage {
230 stage_name: "Recording".into(),
231 description: "System records SO upon credit approval".into(),
232 is_automated: true,
233 key_control_id: None,
234 },
235 ],
236 relevant_assertions: vec!["Occurrence".into(), "Accuracy".into()],
237 related_account_areas: vec!["Revenue".into(), "Trade Receivables".into()],
238 estimation_complexity: None,
239 };
240
241 assert_eq!(scot.critical_path.len(), 2);
242 assert!(scot.estimation_complexity.is_none());
243 assert_eq!(scot.significance_level, ScotSignificance::High);
244 }
245
246 #[test]
247 fn estimation_scot_has_complexity() {
248 let scot = SignificantClassOfTransactions {
249 id: "SCOT-C001-ECL_BAD_DEBT".into(),
250 entity_code: "C001".into(),
251 scot_name: "ECL / Bad Debt Provision".into(),
252 business_process: "R2R".into(),
253 significance_level: ScotSignificance::High,
254 transaction_type: ScotTransactionType::Estimation,
255 processing_method: ProcessingMethod::Manual,
256 volume: 12,
257 monetary_value: dec!(250_000),
258 critical_path: Vec::new(),
259 relevant_assertions: vec!["ValuationAndAllocation".into()],
260 related_account_areas: vec!["Trade Receivables".into(), "Provisions".into()],
261 estimation_complexity: Some(EstimationComplexity::Moderate),
262 };
263
264 assert!(scot.estimation_complexity.is_some());
265 assert_eq!(
266 scot.estimation_complexity.unwrap(),
267 EstimationComplexity::Moderate
268 );
269 }
270}