Skip to main content

datasynth_core/models/sourcing/
qualification.rs

1//! Supplier qualification models for vendor evaluation and certification.
2
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use super::super::graph_properties::{GraphPropertyValue, ToNodeProperties};
8
9/// Status of supplier qualification.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
11#[serde(rename_all = "snake_case")]
12pub enum QualificationStatus {
13    /// Qualification not started
14    #[default]
15    Pending,
16    /// Qualification in progress
17    InProgress,
18    /// Supplier qualified (passed)
19    Qualified,
20    /// Conditionally qualified (with restrictions)
21    ConditionallyQualified,
22    /// Supplier disqualified (failed)
23    Disqualified,
24    /// Qualification expired
25    Expired,
26}
27
28/// A criterion used for qualification scoring.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct QualificationCriterion {
31    /// Criterion name (e.g., "Financial Stability", "Quality Management")
32    pub name: String,
33    /// Weight in total score (0.0 to 1.0)
34    pub weight: f64,
35    /// Minimum passing score (0.0 to 100.0)
36    pub min_score: f64,
37    /// Whether this criterion is mandatory (failing = disqualified)
38    pub is_mandatory: bool,
39}
40
41/// Score for a single qualification criterion.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct QualificationScore {
44    /// Criterion name
45    pub criterion_name: String,
46    /// Score achieved (0.0 to 100.0)
47    pub score: f64,
48    /// Whether the criterion was passed
49    pub passed: bool,
50    /// Evaluator comments
51    pub comments: Option<String>,
52}
53
54/// A supplier certification record.
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct SupplierCertification {
57    /// Certification ID
58    pub certification_id: String,
59    /// Vendor ID
60    pub vendor_id: String,
61    /// Certification type (e.g., "ISO 9001", "ISO 14001", "SOC 2")
62    pub certification_type: String,
63    /// Issuing body
64    pub issuing_body: String,
65    /// Issue date
66    pub issue_date: NaiveDate,
67    /// Expiry date
68    pub expiry_date: NaiveDate,
69    /// Is the certification currently valid
70    pub is_valid: bool,
71}
72
73/// Supplier qualification record.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SupplierQualification {
76    /// Unique qualification ID
77    pub qualification_id: String,
78    /// Vendor ID being qualified
79    pub vendor_id: String,
80    /// Sourcing project ID (if applicable)
81    pub sourcing_project_id: Option<String>,
82    /// Company code
83    pub company_code: String,
84    /// Qualification status
85    pub status: QualificationStatus,
86    /// Qualification start date
87    pub start_date: NaiveDate,
88    /// Qualification completion date
89    pub completion_date: Option<NaiveDate>,
90    /// Validity period end date
91    pub valid_until: Option<NaiveDate>,
92    /// Individual criterion scores
93    pub scores: Vec<QualificationScore>,
94    /// Overall weighted score
95    pub overall_score: f64,
96    /// Evaluator ID
97    pub evaluator_id: String,
98    /// Certifications provided
99    pub certifications: Vec<String>,
100    /// Conditions or restrictions (if conditionally qualified)
101    pub conditions: Option<String>,
102}
103
104impl ToNodeProperties for SupplierQualification {
105    fn node_type_name(&self) -> &'static str {
106        "supplier_qualification"
107    }
108    fn node_type_code(&self) -> u16 {
109        325
110    }
111    fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
112        let mut p = HashMap::new();
113        p.insert(
114            "qualificationId".into(),
115            GraphPropertyValue::String(self.qualification_id.clone()),
116        );
117        p.insert(
118            "vendorId".into(),
119            GraphPropertyValue::String(self.vendor_id.clone()),
120        );
121        p.insert(
122            "entityCode".into(),
123            GraphPropertyValue::String(self.company_code.clone()),
124        );
125        p.insert(
126            "status".into(),
127            GraphPropertyValue::String(format!("{:?}", self.status)),
128        );
129        p.insert(
130            "startDate".into(),
131            GraphPropertyValue::Date(self.start_date),
132        );
133        p.insert(
134            "overallScore".into(),
135            GraphPropertyValue::Float(self.overall_score),
136        );
137        p.insert(
138            "criterionCount".into(),
139            GraphPropertyValue::Int(self.scores.len() as i64),
140        );
141        p.insert(
142            "certificationCount".into(),
143            GraphPropertyValue::Int(self.certifications.len() as i64),
144        );
145        p.insert(
146            "isQualified".into(),
147            GraphPropertyValue::Bool(matches!(
148                self.status,
149                QualificationStatus::Qualified | QualificationStatus::ConditionallyQualified
150            )),
151        );
152        p
153    }
154}