Skip to main content

datasynth_core/models/sourcing/
contract.rs

1//! Procurement contract models.
2
3use chrono::NaiveDate;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6
7/// Type of procurement contract.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
9#[serde(rename_all = "snake_case")]
10pub enum ContractType {
11    /// Fixed price contract
12    #[default]
13    FixedPrice,
14    /// Blanket/framework agreement with quantity commitments
15    Blanket,
16    /// Time and materials contract
17    TimeAndMaterials,
18    /// Cost-plus contract
19    CostPlus,
20    /// Service level agreement
21    ServiceAgreement,
22}
23
24/// Status of a procurement contract.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
26#[serde(rename_all = "snake_case")]
27pub enum ContractStatus {
28    /// Contract drafted
29    #[default]
30    Draft,
31    /// Pending approval
32    PendingApproval,
33    /// Active and in force
34    Active,
35    /// Suspended (temporarily inactive)
36    Suspended,
37    /// Expired
38    Expired,
39    /// Terminated early
40    Terminated,
41    /// Renewed (new contract created)
42    Renewed,
43}
44
45/// Contract terms and conditions.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ContractTerms {
48    /// Payment terms (e.g., "NET30", "2/10 NET30")
49    pub payment_terms: String,
50    /// Delivery terms (incoterms)
51    pub delivery_terms: Option<String>,
52    /// Warranty period in months
53    pub warranty_months: Option<u32>,
54    /// Early termination penalty percentage
55    pub early_termination_penalty_pct: Option<f64>,
56    /// Auto-renewal enabled
57    pub auto_renewal: bool,
58    /// Notice period for termination (days)
59    pub termination_notice_days: u32,
60    /// Price adjustment clause enabled
61    pub price_adjustment_clause: bool,
62    /// Maximum annual price increase percentage
63    pub max_annual_price_increase_pct: Option<f64>,
64}
65
66impl Default for ContractTerms {
67    fn default() -> Self {
68        Self {
69            payment_terms: "NET30".to_string(),
70            delivery_terms: None,
71            warranty_months: None,
72            early_termination_penalty_pct: None,
73            auto_renewal: false,
74            termination_notice_days: 90,
75            price_adjustment_clause: false,
76            max_annual_price_increase_pct: None,
77        }
78    }
79}
80
81/// Service level agreement within a contract.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ContractSla {
84    /// SLA metric name (e.g., "on_time_delivery", "defect_rate")
85    pub metric_name: String,
86    /// Target value
87    pub target_value: f64,
88    /// Minimum acceptable value
89    pub minimum_value: f64,
90    /// Penalty for breach (percentage of contract value)
91    pub breach_penalty_pct: f64,
92    /// Measurement frequency (monthly, quarterly, etc.)
93    pub measurement_frequency: String,
94}
95
96/// Line item within a contract.
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct ContractLineItem {
99    /// Line number
100    pub line_number: u16,
101    /// Material/service ID
102    pub material_id: Option<String>,
103    /// Description
104    pub description: String,
105    /// Contracted unit price
106    #[serde(with = "rust_decimal::serde::str")]
107    pub unit_price: Decimal,
108    /// Unit of measure
109    pub uom: String,
110    /// Minimum order quantity
111    pub min_quantity: Option<Decimal>,
112    /// Maximum/committed quantity
113    pub max_quantity: Option<Decimal>,
114    /// Quantity released (ordered) so far
115    #[serde(default)]
116    pub quantity_released: Decimal,
117    /// Value released so far
118    #[serde(default, with = "rust_decimal::serde::str")]
119    pub value_released: Decimal,
120}
121
122/// A procurement contract.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct ProcurementContract {
125    /// Unique contract identifier
126    pub contract_id: String,
127    /// Company code
128    pub company_code: String,
129    /// Contract type
130    pub contract_type: ContractType,
131    /// Current status
132    pub status: ContractStatus,
133    /// Vendor ID
134    pub vendor_id: String,
135    /// Contract title
136    pub title: String,
137    /// Sourcing project ID (origin)
138    pub sourcing_project_id: Option<String>,
139    /// Winning bid ID (origin)
140    pub bid_id: Option<String>,
141    /// Start date
142    pub start_date: NaiveDate,
143    /// End date
144    pub end_date: NaiveDate,
145    /// Total contract value
146    #[serde(with = "rust_decimal::serde::str")]
147    pub total_value: Decimal,
148    /// Value consumed so far
149    #[serde(with = "rust_decimal::serde::str")]
150    pub consumed_value: Decimal,
151    /// Contract terms
152    pub terms: ContractTerms,
153    /// SLAs
154    pub slas: Vec<ContractSla>,
155    /// Line items
156    pub line_items: Vec<ContractLineItem>,
157    /// Spend category
158    pub category_id: String,
159    /// Contract owner
160    pub owner_id: String,
161    /// Amendment count
162    pub amendment_count: u32,
163    /// Previous contract ID (if renewal)
164    pub previous_contract_id: Option<String>,
165}