315 lines
9.2 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|