use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use super::super::common::IndustryAnomaly;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ManufacturingAnomaly {
YieldManipulation {
reported_yield: f64,
actual_yield: f64,
order_id: String,
},
LaborMisallocation {
from_order: String,
to_order: String,
hours: Decimal,
},
PhantomProduction {
order_id: String,
quantity: u32,
value: Decimal,
},
ObsoleteInventoryConcealment {
material_id: String,
age_months: u32,
value: Decimal,
},
StandardCostManipulation {
material_id: String,
actual_market_cost: Decimal,
recorded_standard: Decimal,
},
OverheadMisallocation {
from_product: String,
to_product: String,
amount: Decimal,
},
ScrapUnderreporting {
order_id: String,
actual_scrap: u32,
reported_scrap: u32,
value_hidden: Decimal,
},
WipCutoffManipulation {
order_id: String,
completion_percentage: f64,
recorded_percentage: f64,
},
BomQuantityManipulation {
product_id: String,
component_id: String,
standard_qty: f64,
actual_qty: f64,
},
CapacityOverstatement {
work_center: String,
actual_capacity: Decimal,
reported_capacity: Decimal,
},
InventoryCountFraud {
material_id: String,
actual_quantity: u32,
recorded_quantity: u32,
},
QualityRecordFalsification {
order_id: String,
actual_defect_rate: f64,
reported_defect_rate: f64,
},
}
impl IndustryAnomaly for ManufacturingAnomaly {
fn anomaly_type(&self) -> &str {
match self {
ManufacturingAnomaly::YieldManipulation { .. } => "yield_manipulation",
ManufacturingAnomaly::LaborMisallocation { .. } => "labor_misallocation",
ManufacturingAnomaly::PhantomProduction { .. } => "phantom_production",
ManufacturingAnomaly::ObsoleteInventoryConcealment { .. } => {
"obsolete_inventory_concealment"
}
ManufacturingAnomaly::StandardCostManipulation { .. } => "standard_cost_manipulation",
ManufacturingAnomaly::OverheadMisallocation { .. } => "overhead_misallocation",
ManufacturingAnomaly::ScrapUnderreporting { .. } => "scrap_underreporting",
ManufacturingAnomaly::WipCutoffManipulation { .. } => "wip_cutoff_manipulation",
ManufacturingAnomaly::BomQuantityManipulation { .. } => "bom_quantity_manipulation",
ManufacturingAnomaly::CapacityOverstatement { .. } => "capacity_overstatement",
ManufacturingAnomaly::InventoryCountFraud { .. } => "inventory_count_fraud",
ManufacturingAnomaly::QualityRecordFalsification { .. } => {
"quality_record_falsification"
}
}
}
fn severity(&self) -> u8 {
match self {
ManufacturingAnomaly::LaborMisallocation { .. } => 3,
ManufacturingAnomaly::OverheadMisallocation { .. } => 3,
ManufacturingAnomaly::WipCutoffManipulation { .. } => 3,
ManufacturingAnomaly::BomQuantityManipulation { .. } => 3,
ManufacturingAnomaly::CapacityOverstatement { .. } => 3,
ManufacturingAnomaly::YieldManipulation { .. } => 4,
ManufacturingAnomaly::ScrapUnderreporting { .. } => 4,
ManufacturingAnomaly::StandardCostManipulation { .. } => 4,
ManufacturingAnomaly::ObsoleteInventoryConcealment { .. } => 4,
ManufacturingAnomaly::PhantomProduction { .. } => 5,
ManufacturingAnomaly::InventoryCountFraud { .. } => 5,
ManufacturingAnomaly::QualityRecordFalsification { .. } => 5,
}
}
fn detection_difficulty(&self) -> &str {
match self {
ManufacturingAnomaly::LaborMisallocation { .. } => "moderate",
ManufacturingAnomaly::OverheadMisallocation { .. } => "moderate",
ManufacturingAnomaly::BomQuantityManipulation { .. } => "moderate",
ManufacturingAnomaly::YieldManipulation { .. } => "hard",
ManufacturingAnomaly::ScrapUnderreporting { .. } => "hard",
ManufacturingAnomaly::StandardCostManipulation { .. } => "hard",
ManufacturingAnomaly::WipCutoffManipulation { .. } => "hard",
ManufacturingAnomaly::CapacityOverstatement { .. } => "hard",
ManufacturingAnomaly::ObsoleteInventoryConcealment { .. } => "hard",
ManufacturingAnomaly::PhantomProduction { .. } => "expert",
ManufacturingAnomaly::InventoryCountFraud { .. } => "expert",
ManufacturingAnomaly::QualityRecordFalsification { .. } => "expert",
}
}
fn indicators(&self) -> Vec<String> {
match self {
ManufacturingAnomaly::YieldManipulation { .. } => vec![
"yield_exceeds_theoretical_maximum".to_string(),
"yield_variance_from_historical".to_string(),
"material_usage_inconsistent_with_yield".to_string(),
],
ManufacturingAnomaly::PhantomProduction { .. } => vec![
"production_without_material_issues".to_string(),
"labor_hours_inconsistent_with_output".to_string(),
"inventory_physical_count_variance".to_string(),
],
ManufacturingAnomaly::ObsoleteInventoryConcealment { .. } => vec![
"aged_inventory_without_reserve".to_string(),
"no_movement_extended_period".to_string(),
"market_price_below_cost".to_string(),
],
ManufacturingAnomaly::StandardCostManipulation { .. } => vec![
"standard_cost_variance_from_market".to_string(),
"unusually_favorable_ppv".to_string(),
"standard_cost_change_without_justification".to_string(),
],
ManufacturingAnomaly::ScrapUnderreporting { .. } => vec![
"scrap_rate_below_industry_average".to_string(),
"material_variance_without_scrap".to_string(),
"quality_rejects_not_matching_scrap".to_string(),
],
ManufacturingAnomaly::InventoryCountFraud { .. } => vec![
"count_adjusted_without_investigation".to_string(),
"consistent_small_adjustments".to_string(),
"cycle_count_variance_pattern".to_string(),
],
ManufacturingAnomaly::QualityRecordFalsification { .. } => vec![
"defect_rate_inconsistent_with_customer_returns".to_string(),
"quality_records_modified_after_fact".to_string(),
"statistical_process_control_anomalies".to_string(),
],
_ => vec!["general_manufacturing_anomaly".to_string()],
}
}
fn regulatory_concerns(&self) -> Vec<String> {
match self {
ManufacturingAnomaly::PhantomProduction { .. }
| ManufacturingAnomaly::InventoryCountFraud { .. } => vec![
"financial_statement_fraud".to_string(),
"sox_section_302".to_string(),
"sox_section_404".to_string(),
],
ManufacturingAnomaly::ObsoleteInventoryConcealment { .. }
| ManufacturingAnomaly::StandardCostManipulation { .. } => vec![
"gaap_inventory_valuation".to_string(),
"asc_330".to_string(),
"ias_2".to_string(),
],
ManufacturingAnomaly::QualityRecordFalsification { .. } => vec![
"product_safety".to_string(),
"fda_cgmp".to_string(),
"iso_9001".to_string(),
],
_ => vec!["general_gaap".to_string()],
}
}
}
impl ManufacturingAnomaly {
pub fn financial_impact(&self) -> Option<Decimal> {
match self {
ManufacturingAnomaly::PhantomProduction { value, .. } => Some(*value),
ManufacturingAnomaly::ObsoleteInventoryConcealment { value, .. } => Some(*value),
ManufacturingAnomaly::OverheadMisallocation { amount, .. } => Some(*amount),
ManufacturingAnomaly::ScrapUnderreporting { value_hidden, .. } => Some(*value_hidden),
ManufacturingAnomaly::StandardCostManipulation {
actual_market_cost,
recorded_standard,
..
} => Some((*recorded_standard - *actual_market_cost).abs()),
ManufacturingAnomaly::LaborMisallocation { hours, .. } => {
Some(*hours * Decimal::new(25, 0))
}
_ => None,
}
}
pub fn affects_financials(&self) -> bool {
!matches!(
self,
ManufacturingAnomaly::CapacityOverstatement { .. }
| ManufacturingAnomaly::QualityRecordFalsification { .. }
)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_yield_manipulation() {
let anomaly = ManufacturingAnomaly::YieldManipulation {
reported_yield: 0.98,
actual_yield: 0.92,
order_id: "PO001".to_string(),
};
assert_eq!(anomaly.anomaly_type(), "yield_manipulation");
assert_eq!(anomaly.severity(), 4);
assert_eq!(anomaly.detection_difficulty(), "hard");
assert!(!anomaly.indicators().is_empty());
}
#[test]
fn test_phantom_production() {
let anomaly = ManufacturingAnomaly::PhantomProduction {
order_id: "PO002".to_string(),
quantity: 100,
value: Decimal::new(10_000, 0),
};
assert_eq!(anomaly.severity(), 5);
assert_eq!(anomaly.detection_difficulty(), "expert");
assert_eq!(anomaly.financial_impact(), Some(Decimal::new(10_000, 0)));
assert!(anomaly.affects_financials());
}
#[test]
fn test_inventory_fraud() {
let anomaly = ManufacturingAnomaly::InventoryCountFraud {
material_id: "MAT001".to_string(),
actual_quantity: 100,
recorded_quantity: 150,
};
assert!(anomaly
.regulatory_concerns()
.contains(&"sox_section_404".to_string()));
}
#[test]
fn test_quality_falsification() {
let anomaly = ManufacturingAnomaly::QualityRecordFalsification {
order_id: "PO003".to_string(),
actual_defect_rate: 0.08,
reported_defect_rate: 0.02,
};
assert!(anomaly
.regulatory_concerns()
.contains(&"fda_cgmp".to_string()));
assert!(!anomaly.affects_financials());
}
}