hostbasket/actix_mvc_app/src/models/contract.rs
2025-05-01 03:56:55 +03:00

315 lines
9.2 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
/// Contract status enum
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContractStatus {
Draft,
PendingSignatures,
Signed,
Active,
Expired,
Cancelled
}
impl ContractStatus {
pub fn as_str(&self) -> &str {
match self {
ContractStatus::Draft => "Draft",
ContractStatus::PendingSignatures => "Pending Signatures",
ContractStatus::Signed => "Signed",
ContractStatus::Active => "Active",
ContractStatus::Expired => "Expired",
ContractStatus::Cancelled => "Cancelled",
}
}
}
/// Contract type enum
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContractType {
Service,
Employment,
NDA,
SLA,
Partnership,
Distribution,
License,
Membership,
Other
}
impl ContractType {
pub fn as_str(&self) -> &str {
match self {
ContractType::Service => "Service Agreement",
ContractType::Employment => "Employment Contract",
ContractType::NDA => "Non-Disclosure Agreement",
ContractType::SLA => "Service Level Agreement",
ContractType::Partnership => "Partnership Agreement",
ContractType::Distribution => "Distribution Agreement",
ContractType::License => "License Agreement",
ContractType::Membership => "Membership Agreement",
ContractType::Other => "Other",
}
}
}
/// Contract signer status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SignerStatus {
Pending,
Signed,
Rejected
}
impl SignerStatus {
pub fn as_str(&self) -> &str {
match self {
SignerStatus::Pending => "Pending",
SignerStatus::Signed => "Signed",
SignerStatus::Rejected => "Rejected",
}
}
}
/// Contract signer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractSigner {
pub id: String,
pub name: String,
pub email: String,
pub status: SignerStatus,
pub signed_at: Option<DateTime<Utc>>,
pub comments: Option<String>,
}
impl ContractSigner {
/// Creates a new contract signer
pub fn new(name: String, email: String) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name,
email,
status: SignerStatus::Pending,
signed_at: None,
comments: None,
}
}
/// Signs the contract
pub fn sign(&mut self, comments: Option<String>) {
self.status = SignerStatus::Signed;
self.signed_at = Some(Utc::now());
self.comments = comments;
}
/// Rejects the contract
pub fn reject(&mut self, comments: Option<String>) {
self.status = SignerStatus::Rejected;
self.signed_at = Some(Utc::now());
self.comments = comments;
}
}
/// Contract revision
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractRevision {
pub version: u32,
pub content: String,
pub created_at: DateTime<Utc>,
pub created_by: String,
pub comments: Option<String>,
}
impl ContractRevision {
/// Creates a new contract revision
pub fn new(version: u32, content: String, created_by: String, comments: Option<String>) -> Self {
Self {
version,
content,
created_at: Utc::now(),
created_by,
comments,
}
}
}
/// Table of Contents item for multi-page contracts
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TocItem {
pub title: String,
pub file: String,
pub children: Vec<TocItem>,
}
/// Contract model
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contract {
pub id: String,
pub title: String,
pub description: String,
pub contract_type: ContractType,
pub status: ContractStatus,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub created_by: String,
pub effective_date: Option<DateTime<Utc>>,
pub expiration_date: Option<DateTime<Utc>>,
pub signers: Vec<ContractSigner>,
pub revisions: Vec<ContractRevision>,
pub current_version: u32,
pub organization_id: Option<String>,
// Multi-page markdown support
pub content_dir: Option<String>,
pub toc: Option<Vec<TocItem>>,
}
impl Contract {
/// Creates a new contract
pub fn new(title: String, description: String, contract_type: ContractType, created_by: String, organization_id: Option<String>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
title,
description,
contract_type,
status: ContractStatus::Draft,
created_at: Utc::now(),
updated_at: Utc::now(),
created_by,
effective_date: None,
expiration_date: None,
signers: Vec::new(),
revisions: Vec::new(),
current_version: 1,
organization_id,
content_dir: None,
toc: None,
}
}
/// Adds a signer to the contract
pub fn add_signer(&mut self, name: String, email: String) {
let signer = ContractSigner::new(name, email);
self.signers.push(signer);
self.updated_at = Utc::now();
}
/// Adds a revision to the contract
pub fn add_revision(&mut self, content: String, created_by: String, comments: Option<String>) {
let new_version = self.current_version + 1;
let revision = ContractRevision::new(new_version, content, created_by, comments);
self.revisions.push(revision);
self.current_version = new_version;
self.updated_at = Utc::now();
}
/// Sends the contract for signatures
pub fn send_for_signatures(&mut self) -> Result<(), String> {
if self.revisions.is_empty() {
return Err("Cannot send contract without content".to_string());
}
if self.signers.is_empty() {
return Err("Cannot send contract without signers".to_string());
}
self.status = ContractStatus::PendingSignatures;
self.updated_at = Utc::now();
Ok(())
}
/// Checks if all signers have signed
pub fn is_fully_signed(&self) -> bool {
if self.signers.is_empty() {
return false;
}
self.signers.iter().all(|signer| signer.status == SignerStatus::Signed)
}
/// Marks the contract as signed if all signers have signed
pub fn finalize_if_signed(&mut self) -> bool {
if self.is_fully_signed() {
self.status = ContractStatus::Signed;
self.updated_at = Utc::now();
true
} else {
false
}
}
/// Cancels the contract
pub fn cancel(&mut self) {
self.status = ContractStatus::Cancelled;
self.updated_at = Utc::now();
}
/// Gets the latest revision
pub fn latest_revision(&self) -> Option<&ContractRevision> {
self.revisions.last()
}
/// Gets a specific revision
pub fn get_revision(&self, version: u32) -> Option<&ContractRevision> {
self.revisions.iter().find(|r| r.version == version)
}
/// Gets the number of pending signers
pub fn pending_signers_count(&self) -> usize {
self.signers.iter().filter(|s| s.status == SignerStatus::Pending).count()
}
/// Gets the number of signed signers
pub fn signed_signers_count(&self) -> usize {
self.signers.iter().filter(|s| s.status == SignerStatus::Signed).count()
}
/// Gets the number of rejected signers
pub fn rejected_signers_count(&self) -> usize {
self.signers.iter().filter(|s| s.status == SignerStatus::Rejected).count()
}
}
/// Contract filter for listing contracts
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractFilter {
pub status: Option<ContractStatus>,
pub contract_type: Option<ContractType>,
pub created_by: Option<String>,
pub organization_id: Option<String>,
}
/// Contract statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractStatistics {
pub total_contracts: usize,
pub draft_contracts: usize,
pub pending_signature_contracts: usize,
pub signed_contracts: usize,
pub expired_contracts: usize,
pub cancelled_contracts: usize,
}
impl ContractStatistics {
/// Creates new contract statistics from a list of contracts
pub fn new(contracts: &[Contract]) -> Self {
let total_contracts = contracts.len();
let draft_contracts = contracts.iter().filter(|c| c.status == ContractStatus::Draft).count();
let pending_signature_contracts = contracts.iter().filter(|c| c.status == ContractStatus::PendingSignatures).count();
let signed_contracts = contracts.iter().filter(|c| c.status == ContractStatus::Signed).count();
let expired_contracts = contracts.iter().filter(|c| c.status == ContractStatus::Expired).count();
let cancelled_contracts = contracts.iter().filter(|c| c.status == ContractStatus::Cancelled).count();
Self {
total_contracts,
draft_contracts,
pending_signature_contracts,
signed_contracts,
expired_contracts,
cancelled_contracts,
}
}
}