From aa8ef90f9f26a786c021b691c877a763cab2066e Mon Sep 17 00:00:00 2001 From: timurgordon Date: Tue, 20 May 2025 21:59:45 +0300 Subject: [PATCH] add flow and contract models --- .../src/models/flowbroker_models/flow.rs | 47 +++ .../src/models/flowbroker_models/flow_step.rs | 49 +++ .../src/models/flowbroker_models/mod.rs | 9 + .../signature_requirement.rs | 62 ++++ heromodels/src/models/legal/contract.rs | 296 ++++++++++++++++++ heromodels/src/models/legal/mod.rs | 3 + 6 files changed, 466 insertions(+) create mode 100644 heromodels/src/models/flowbroker_models/flow.rs create mode 100644 heromodels/src/models/flowbroker_models/flow_step.rs create mode 100644 heromodels/src/models/flowbroker_models/mod.rs create mode 100644 heromodels/src/models/flowbroker_models/signature_requirement.rs create mode 100644 heromodels/src/models/legal/contract.rs create mode 100644 heromodels/src/models/legal/mod.rs diff --git a/heromodels/src/models/flowbroker_models/flow.rs b/heromodels/src/models/flowbroker_models/flow.rs new file mode 100644 index 0000000..74c4201 --- /dev/null +++ b/heromodels/src/models/flowbroker_models/flow.rs @@ -0,0 +1,47 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Represents a signing flow. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[model] +pub struct Flow { + /// Base model data (id, created_at, updated_at). + pub base_data: BaseModelData, + + /// A unique UUID for the flow, for external reference. + #[index] + pub flow_uuid: String, + + /// Name of the flow. + #[index] + pub name: String, + + /// Current status of the flow (e.g., "Pending", "InProgress", "Completed", "Failed"). + pub status: String, +} + +impl Flow { + /// Create a new flow. + /// The `id` is the database primary key. + /// The `flow_uuid` should be a Uuid::new_v4().to_string(). + pub fn new(id: u32, flow_uuid: impl ToString, name: impl ToString, status: impl ToString) -> Self { + Self { + base_data: BaseModelData::new(id), + flow_uuid: flow_uuid.to_string(), + name: name.to_string(), + status: status.to_string(), + } + } + + // Builder methods for optional fields or to change initial values can be added here if needed. + // For example: + // pub fn name(mut self, name: impl ToString) -> Self { + // self.name = name.to_string(); + // self + // } + // pub fn status(mut self, status: impl ToString) -> Self { + // self.status = status.to_string(); + // self + // } +} diff --git a/heromodels/src/models/flowbroker_models/flow_step.rs b/heromodels/src/models/flowbroker_models/flow_step.rs new file mode 100644 index 0000000..0a3e9dc --- /dev/null +++ b/heromodels/src/models/flowbroker_models/flow_step.rs @@ -0,0 +1,49 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Represents a step within a signing flow. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[model] +pub struct FlowStep { + /// Base model data. + pub base_data: BaseModelData, + + /// Foreign key to the Flow this step belongs to. + #[index] + pub flow_id: u32, + + /// Optional description for the step. + pub description: Option, + + /// Order of this step within the flow. + #[index] + pub step_order: u32, + + /// Current status of the flow step (e.g., "Pending", "InProgress", "Completed", "Failed"). + pub status: String, +} + +impl FlowStep { + /// Create a new flow step. + pub fn new(id: u32, flow_id: u32, step_order: u32, status: impl ToString) -> Self { + Self { + base_data: BaseModelData::new(id), + flow_id, + description: None, + step_order, + status: status.to_string(), + } + } + + /// Sets the description for the flow step. + pub fn description(mut self, description: impl ToString) -> Self { + self.description = Some(description.to_string()); + self + } + + // pub fn status(mut self, status: impl ToString) -> Self { + // self.status = status.to_string(); + // self + // } +} diff --git a/heromodels/src/models/flowbroker_models/mod.rs b/heromodels/src/models/flowbroker_models/mod.rs new file mode 100644 index 0000000..57e0d5f --- /dev/null +++ b/heromodels/src/models/flowbroker_models/mod.rs @@ -0,0 +1,9 @@ +// Export flowbroker model submodules +pub mod flow; +pub mod flow_step; +pub mod signature_requirement; + +// Re-export key types for convenience +pub use flow::Flow; +pub use flow_step::FlowStep; +pub use signature_requirement::SignatureRequirement; diff --git a/heromodels/src/models/flowbroker_models/signature_requirement.rs b/heromodels/src/models/flowbroker_models/signature_requirement.rs new file mode 100644 index 0000000..cd33c1e --- /dev/null +++ b/heromodels/src/models/flowbroker_models/signature_requirement.rs @@ -0,0 +1,62 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; +use serde::{Deserialize, Serialize}; + +/// Represents a signature requirement for a flow step. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[model] +pub struct SignatureRequirement { + /// Base model data. + pub base_data: BaseModelData, + + /// Foreign key to the FlowStep this requirement belongs to. + #[index] + pub flow_step_id: u32, + + /// The public key required to sign the message. + pub public_key: String, + + /// The plaintext message to be signed. + pub message: String, + + /// The public key of the entity that signed the message, if signed. + pub signed_by: Option, + + /// The signature, if signed. + pub signature: Option, + + /// Current status of the signature requirement (e.g., "Pending", "SentToClient", "Signed", "Failed", "Error"). + pub status: String, +} + +impl SignatureRequirement { + /// Create a new signature requirement. + pub fn new(id: u32, flow_step_id: u32, public_key: impl ToString, message: impl ToString, status: impl ToString) -> Self { + Self { + base_data: BaseModelData::new(id), + flow_step_id, + public_key: public_key.to_string(), + message: message.to_string(), + signed_by: None, + signature: None, + status: status.to_string(), + } + } + + /// Sets the public key of the signer. + pub fn signed_by(mut self, signed_by: impl ToString) -> Self { + self.signed_by = Some(signed_by.to_string()); + self + } + + /// Sets the signature. + pub fn signature(mut self, signature: impl ToString) -> Self { + self.signature = Some(signature.to_string()); + self + } + + // pub fn status(mut self, status: impl ToString) -> Self { + // self.status = status.to_string(); + // self + // } +} diff --git a/heromodels/src/models/legal/contract.rs b/heromodels/src/models/legal/contract.rs new file mode 100644 index 0000000..0b7fe8e --- /dev/null +++ b/heromodels/src/models/legal/contract.rs @@ -0,0 +1,296 @@ +use heromodels_core::BaseModelData; +use heromodels_derive::model; +use std::fmt; +use serde::{Deserialize, Serialize}; + +// --- 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)] +pub struct ContractRevision { + pub version: u32, + pub content: String, + pub created_at: u64, // Timestamp + pub created_by: String, + pub comments: Option, +} + +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)] +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, // Timestamp + pub comments: Option, +} + +impl ContractSigner { + pub fn new(id: String, name: String, email: String) -> Self { + Self { + id, + name, + email, + status: SignerStatus::default(), + signed_at: None, + comments: 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 + } +} + +// --- Main Contract Model --- + +/// Represents a legal agreement +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[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, + pub end_date: Option, + pub renewal_period_days: Option, + pub next_renewal_date: Option, + + pub signers: Vec, + pub revisions: Vec, + pub current_version: u32, + pub last_signed_date: Option, +} + +impl Contract { + pub fn new(base_id: u32, contract_id: String) -> Self { + Self { + base_data: BaseModelData::new(base_id), + 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) -> 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) -> 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 + } +} diff --git a/heromodels/src/models/legal/mod.rs b/heromodels/src/models/legal/mod.rs new file mode 100644 index 0000000..17ada52 --- /dev/null +++ b/heromodels/src/models/legal/mod.rs @@ -0,0 +1,3 @@ +pub mod contract; + +pub use contract::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};