db/heromodels/src/models/legal/contract.rs
2025-06-25 20:46:15 +03:00

374 lines
10 KiB
Rust

use heromodels_core::BaseModelData;
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::{SystemTime, UNIX_EPOCH};
// --- Helper Functions ---
/// Helper function to get current timestamp in seconds
fn current_timestamp_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
// --- Enums ---
/// Defines the possible statuses of a contract
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ContractStatus {
Draft,
PendingSignatures,
Signed,
Active,
Expired,
Cancelled,
}
impl Default for ContractStatus {
fn default() -> Self {
ContractStatus::Draft
}
}
impl fmt::Display for ContractStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self) // Outputs the variant name, e.g., "Draft"
}
}
/// Defines the status of a contract signer
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SignerStatus {
Pending,
Signed,
Rejected,
}
impl Default for SignerStatus {
fn default() -> Self {
SignerStatus::Pending
}
}
impl fmt::Display for SignerStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self) // Outputs the variant name, e.g., "Pending"
}
}
// --- Structs for nested data ---
/// ContractRevision represents a version of the contract content
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct ContractRevision {
pub version: u32,
pub content: String,
pub created_at: u64, // Timestamp
pub created_by: String,
pub comments: Option<String>,
}
impl ContractRevision {
pub fn new(version: u32, content: String, created_at: u64, created_by: String) -> Self {
Self {
version,
content,
created_at,
created_by,
comments: None,
}
}
pub fn comments(mut self, comments: impl ToString) -> Self {
self.comments = Some(comments.to_string());
self
}
}
/// ContractSigner represents a party involved in signing a contract
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct ContractSigner {
pub id: String, // Unique ID for the signer (UUID string)
pub name: String,
pub email: String,
pub status: SignerStatus,
pub signed_at: Option<u64>, // Timestamp
pub comments: Option<String>,
pub last_reminder_mail_sent_at: Option<u64>, // Unix timestamp of last reminder sent
pub signature_data: Option<String>, // Base64 encoded signature image data
}
impl ContractSigner {
pub fn new(id: String, name: String, email: String) -> Self {
Self {
id,
name,
email,
status: SignerStatus::default(),
signed_at: None,
comments: None,
last_reminder_mail_sent_at: None,
signature_data: None,
}
}
pub fn status(mut self, status: SignerStatus) -> Self {
self.status = status;
self
}
pub fn signed_at(mut self, signed_at: u64) -> Self {
self.signed_at = Some(signed_at);
self
}
pub fn clear_signed_at(mut self) -> Self {
self.signed_at = None;
self
}
pub fn comments(mut self, comments: impl ToString) -> Self {
self.comments = Some(comments.to_string());
self
}
pub fn clear_comments(mut self) -> Self {
self.comments = None;
self
}
pub fn last_reminder_mail_sent_at(mut self, timestamp: u64) -> Self {
self.last_reminder_mail_sent_at = Some(timestamp);
self
}
pub fn clear_last_reminder_mail_sent_at(mut self) -> Self {
self.last_reminder_mail_sent_at = None;
self
}
pub fn signature_data(mut self, signature_data: impl ToString) -> Self {
self.signature_data = Some(signature_data.to_string());
self
}
pub fn clear_signature_data(mut self) -> Self {
self.signature_data = None;
self
}
/// Helper method to check if a reminder can be sent (30-minute rate limiting)
pub fn can_send_reminder(&self, current_timestamp: u64) -> bool {
match self.last_reminder_mail_sent_at {
None => true, // No reminder sent yet
Some(last_sent) => {
let thirty_minutes_in_seconds = 30 * 60; // 30 minutes = 1800 seconds
current_timestamp >= last_sent + thirty_minutes_in_seconds
}
}
}
/// Helper method to get remaining cooldown time in seconds
pub fn reminder_cooldown_remaining(&self, current_timestamp: u64) -> Option<u64> {
match self.last_reminder_mail_sent_at {
None => None, // No cooldown if no reminder sent yet
Some(last_sent) => {
let thirty_minutes_in_seconds = 30 * 60; // 30 minutes = 1800 seconds
let cooldown_end = last_sent + thirty_minutes_in_seconds;
if current_timestamp < cooldown_end {
Some(cooldown_end - current_timestamp)
} else {
None // Cooldown has expired
}
}
}
}
/// Helper method to update the reminder timestamp to current time
pub fn mark_reminder_sent(&mut self, current_timestamp: u64) {
self.last_reminder_mail_sent_at = Some(current_timestamp);
}
/// Signs the contract with optional signature data and comments
pub fn sign(&mut self, signature_data: Option<String>, comments: Option<String>) {
self.status = SignerStatus::Signed;
self.signed_at = Some(current_timestamp_secs());
self.signature_data = signature_data;
if let Some(comment) = comments {
self.comments = Some(comment);
}
}
}
// --- Main Contract Model ---
/// Represents a legal agreement
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[model]
pub struct Contract {
pub base_data: BaseModelData, // Provides id (u32), created_at (u64), updated_at (u64)
#[index]
pub contract_id: String, // Unique ID for the contract (UUID string)
pub title: String,
pub description: String,
#[index]
pub contract_type: String,
#[index]
pub status: crate::models::ContractStatus, // Use re-exported path for #[model] macro
pub created_by: String,
pub terms_and_conditions: String,
pub start_date: Option<u64>,
pub end_date: Option<u64>,
pub renewal_period_days: Option<i32>,
pub next_renewal_date: Option<u64>,
pub signers: Vec<ContractSigner>,
pub revisions: Vec<ContractRevision>,
pub current_version: u32,
pub last_signed_date: Option<u64>,
}
impl Contract {
pub fn new(_base_id: u32, contract_id: String) -> Self {
Self {
base_data: BaseModelData::new(),
contract_id,
title: String::new(),
description: String::new(),
contract_type: String::new(),
status: ContractStatus::default(),
created_by: String::new(),
terms_and_conditions: String::new(),
start_date: None,
end_date: None,
renewal_period_days: None,
next_renewal_date: None,
signers: Vec::new(),
revisions: Vec::new(),
current_version: 0,
last_signed_date: None,
}
}
// Builder methods
pub fn title(mut self, title: impl ToString) -> Self {
self.title = title.to_string();
self
}
pub fn description(mut self, description: impl ToString) -> Self {
self.description = description.to_string();
self
}
pub fn contract_type(mut self, contract_type: impl ToString) -> Self {
self.contract_type = contract_type.to_string();
self
}
pub fn status(mut self, status: ContractStatus) -> Self {
self.status = status;
self
}
pub fn created_by(mut self, created_by: impl ToString) -> Self {
self.created_by = created_by.to_string();
self
}
pub fn terms_and_conditions(mut self, terms: impl ToString) -> Self {
self.terms_and_conditions = terms.to_string();
self
}
pub fn start_date(mut self, start_date: u64) -> Self {
self.start_date = Some(start_date);
self
}
pub fn clear_start_date(mut self) -> Self {
self.start_date = None;
self
}
pub fn end_date(mut self, end_date: u64) -> Self {
self.end_date = Some(end_date);
self
}
pub fn clear_end_date(mut self) -> Self {
self.end_date = None;
self
}
pub fn renewal_period_days(mut self, days: i32) -> Self {
self.renewal_period_days = Some(days);
self
}
pub fn clear_renewal_period_days(mut self) -> Self {
self.renewal_period_days = None;
self
}
pub fn next_renewal_date(mut self, date: u64) -> Self {
self.next_renewal_date = Some(date);
self
}
pub fn clear_next_renewal_date(mut self) -> Self {
self.next_renewal_date = None;
self
}
pub fn add_signer(mut self, signer: ContractSigner) -> Self {
self.signers.push(signer);
self
}
pub fn signers(mut self, signers: Vec<ContractSigner>) -> Self {
self.signers = signers;
self
}
pub fn add_revision(mut self, revision: ContractRevision) -> Self {
self.revisions.push(revision);
self
}
pub fn revisions(mut self, revisions: Vec<ContractRevision>) -> Self {
self.revisions = revisions;
self
}
pub fn current_version(mut self, version: u32) -> Self {
self.current_version = version;
self
}
pub fn last_signed_date(mut self, date: u64) -> Self {
self.last_signed_date = Some(date);
self
}
pub fn clear_last_signed_date(mut self) -> Self {
self.last_signed_date = None;
self
}
// Example methods for state changes
pub fn set_status(&mut self, status: crate::models::ContractStatus) {
self.status = status;
// self.base_data.touch(); // Assume #[model] handles timestamp updates
}
}