use chrono::NaiveDate;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::super::graph_properties::{GraphPropertyValue, ToNodeProperties};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ContractType {
#[default]
FixedPrice,
Blanket,
TimeAndMaterials,
CostPlus,
ServiceAgreement,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ContractStatus {
#[default]
Draft,
PendingApproval,
Active,
Suspended,
Expired,
Terminated,
Renewed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractTerms {
pub payment_terms: String,
pub delivery_terms: Option<String>,
pub warranty_months: Option<u32>,
pub early_termination_penalty_pct: Option<f64>,
pub auto_renewal: bool,
pub termination_notice_days: u32,
pub price_adjustment_clause: bool,
pub max_annual_price_increase_pct: Option<f64>,
}
impl Default for ContractTerms {
fn default() -> Self {
Self {
payment_terms: "NET30".to_string(),
delivery_terms: None,
warranty_months: None,
early_termination_penalty_pct: None,
auto_renewal: false,
termination_notice_days: 90,
price_adjustment_clause: false,
max_annual_price_increase_pct: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractSla {
pub metric_name: String,
pub target_value: f64,
pub minimum_value: f64,
pub breach_penalty_pct: f64,
pub measurement_frequency: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractLineItem {
pub line_number: u16,
pub material_id: Option<String>,
pub description: String,
#[serde(with = "crate::serde_decimal")]
pub unit_price: Decimal,
pub uom: String,
pub min_quantity: Option<Decimal>,
pub max_quantity: Option<Decimal>,
#[serde(default)]
pub quantity_released: Decimal,
#[serde(default, with = "crate::serde_decimal")]
pub value_released: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcurementContract {
pub contract_id: String,
pub company_code: String,
pub contract_type: ContractType,
pub status: ContractStatus,
pub vendor_id: String,
pub title: String,
pub sourcing_project_id: Option<String>,
pub bid_id: Option<String>,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
#[serde(with = "crate::serde_decimal")]
pub total_value: Decimal,
#[serde(with = "crate::serde_decimal")]
pub consumed_value: Decimal,
pub terms: ContractTerms,
pub slas: Vec<ContractSla>,
pub line_items: Vec<ContractLineItem>,
pub category_id: String,
pub owner_id: String,
pub amendment_count: u32,
pub previous_contract_id: Option<String>,
#[serde(default)]
pub purchase_order_ids: Vec<String>,
}
impl ToNodeProperties for ProcurementContract {
fn node_type_name(&self) -> &'static str {
"procurement_contract"
}
fn node_type_code(&self) -> u16 {
324
}
fn to_node_properties(&self) -> HashMap<String, GraphPropertyValue> {
let mut p = HashMap::new();
p.insert(
"contractId".into(),
GraphPropertyValue::String(self.contract_id.clone()),
);
p.insert(
"entityCode".into(),
GraphPropertyValue::String(self.company_code.clone()),
);
p.insert(
"contractType".into(),
GraphPropertyValue::String(format!("{:?}", self.contract_type)),
);
p.insert(
"status".into(),
GraphPropertyValue::String(format!("{:?}", self.status)),
);
p.insert(
"vendorId".into(),
GraphPropertyValue::String(self.vendor_id.clone()),
);
p.insert(
"title".into(),
GraphPropertyValue::String(self.title.clone()),
);
p.insert(
"startDate".into(),
GraphPropertyValue::Date(self.start_date),
);
p.insert("endDate".into(), GraphPropertyValue::Date(self.end_date));
p.insert(
"totalValue".into(),
GraphPropertyValue::Decimal(self.total_value),
);
p.insert(
"consumedValue".into(),
GraphPropertyValue::Decimal(self.consumed_value),
);
p.insert(
"lineItemCount".into(),
GraphPropertyValue::Int(self.line_items.len() as i64),
);
p.insert(
"amendmentCount".into(),
GraphPropertyValue::Int(self.amendment_count as i64),
);
p.insert(
"isActive".into(),
GraphPropertyValue::Bool(matches!(self.status, ContractStatus::Active)),
);
p
}
}