use heromodels::models::legal::{ Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus, }; // If BaseModelData's touch method or new method isn't directly part of the public API // of heromodels crate root, we might need to import heromodels_core. // For now, assuming `contract.base_data.touch()` would work if BaseModelData has a public `touch` method. // Or, if `heromodels::models::BaseModelData` is the path. // A helper for current timestamp (seconds since epoch) fn current_timestamp_secs() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() .as_secs() } fn main() { println!("Demonstrating Legal Contract Model Usage"); // Create contract signers let signer1 = ContractSigner::new( "signer-uuid-alice-001".to_string(), "Alice Wonderland".to_string(), "alice@example.com".to_string(), ) .status(SignerStatus::Pending) .comments("Awaiting Alice's review and signature."); let signer2 = ContractSigner::new( "signer-uuid-bob-002".to_string(), "Bob The Builder".to_string(), "bob@example.com".to_string(), ) .status(SignerStatus::Signed) .signed_at(current_timestamp_secs() - 86400) // Signed yesterday .comments("Bob has signed the agreement."); // Create contract revisions let revision1 = ContractRevision::new( 1, "Initial draft: This Service Agreement outlines the terms...".to_string(), current_timestamp_secs() - (86400 * 2), // 2 days ago "user-uuid-creator-charlie".to_string(), ) .comments("Version 1.0 - Initial draft for review."); let revision2 = ContractRevision::new( 2, "Updated draft: Added clause 5.b regarding data privacy...".to_string(), current_timestamp_secs() - 86400, // 1 day ago "user-uuid-editor-diana".to_string(), ) .comments("Version 2.0 - Incorporated feedback from legal team."); // Create a new contract // base_id (u32 for BaseModelData) and contract_id (String for UUID) let mut contract = Contract::new(101, "contract-uuid-main-789".to_string()) .title("Master Service Agreement (MSA) - Q3 2025") .description("Agreement for ongoing IT support services between TechSolutions Inc. and ClientCorp LLC.") .contract_type("MSA".to_string()) .status(ContractStatus::PendingSignatures) .created_by("user-uuid-admin-eve".to_string()) .terms_and_conditions("The full terms are detailed in the attached PDF, referenced as 'MSA_Q3_2025_Full.pdf'. This string can hold markdown or JSON summary.") .start_date(current_timestamp_secs() + (86400 * 7)) // Starts in 7 days .end_date(current_timestamp_secs() + (86400 * (365 + 7))) // Ends in 1 year + 7 days .renewal_period_days(30) .next_renewal_date(current_timestamp_secs() + (86400 * (365 + 7 - 30))) // Approx. 30 days before end_date .current_version(2) .add_signer(signer1.clone()) .add_signer(signer2.clone()) .add_revision(revision1.clone()) .add_revision(revision2.clone()); // The `#[model]` derive handles `created_at` and `updated_at` in `base_data`. // `base_data.touch()` might be called internally by setters or needs explicit call if fields are set directly. // For builder pattern, the final state of `base_data.updated_at` reflects the time of the last builder call if `touch()` is implicit. // If not, one might call `contract.base_data.touch()` after building. println!("\n--- Initial Contract Details ---"); println!("{:#?}", contract); // Simulate a status change and signing contract.set_status(ContractStatus::Signed); // This should call base_data.touch() contract = contract.last_signed_date(current_timestamp_secs()); // If set_status doesn't touch, and last_signed_date is not a builder method that touches: // contract.base_data.touch(); // Manually update timestamp if needed after direct field manipulation println!("\n--- Contract Details After Signing ---"); println!("{:#?}", contract); println!("\n--- Accessing Specific Fields ---"); println!("Contract Title: {}", contract.title); println!("Contract Status: {:?}", contract.status); println!("Contract ID (UUID): {}", contract.contract_id); println!("Base Model ID (u32): {}", contract.base_data.id); // From BaseModelData println!("Created At (timestamp): {}", contract.base_data.created_at); // From BaseModelData println!("Updated At (timestamp): {}", contract.base_data.modified_at); // From BaseModelData if let Some(first_signer_details) = contract.signers.first() { println!( "\nFirst Signer: {} ({})", first_signer_details.name, first_signer_details.email ); println!(" Status: {:?}", first_signer_details.status); if let Some(signed_time) = first_signer_details.signed_at { println!(" Signed At: {}", signed_time); } } if let Some(latest_rev) = contract.revisions.iter().max_by_key(|r| r.version) { println!("\nLatest Revision (v{}):", latest_rev.version); println!(" Content Snippet: {:.60}...", latest_rev.content); println!(" Created By: {}", latest_rev.created_by); println!(" Revision Created At: {}", latest_rev.created_at); } // Demonstrate reminder functionality println!("\n--- Reminder Functionality Demo ---"); let current_time = current_timestamp_secs(); // Check if we can send reminders to signers for (i, signer) in contract.signers.iter().enumerate() { println!("\nSigner {}: {} ({})", i + 1, signer.name, signer.email); println!(" Status: {:?}", signer.status); if signer.last_reminder_mail_sent_at.is_none() { println!(" Last reminder: Never sent"); } else { println!( " Last reminder: {}", signer.last_reminder_mail_sent_at.unwrap() ); } let can_send = signer.can_send_reminder(current_time); println!(" Can send reminder now: {}", can_send); if let Some(remaining) = signer.reminder_cooldown_remaining(current_time) { println!(" Cooldown remaining: {} seconds", remaining); } else { println!(" No cooldown active"); } } // Simulate sending a reminder to the first signer if let Some(first_signer) = contract.signers.get_mut(0) { if first_signer.can_send_reminder(current_time) { println!("\nSimulating reminder sent to: {}", first_signer.name); first_signer.mark_reminder_sent(current_time); println!( " Reminder timestamp updated to: {}", first_signer.last_reminder_mail_sent_at.unwrap() ); // Check cooldown after sending if let Some(remaining) = first_signer.reminder_cooldown_remaining(current_time) { println!(" New cooldown: {} seconds (30 minutes)", remaining); } } } // Demonstrate signature functionality println!("\n--- Signature Functionality Demo ---"); // Simulate signing with signature data if let Some(signer_to_sign) = contract.signers.get_mut(1) { println!("\nBefore signing:"); println!( " Signer: {} ({})", signer_to_sign.name, signer_to_sign.email ); println!(" Status: {:?}", signer_to_sign.status); println!(" Signed at: {:?}", signer_to_sign.signed_at); println!(" Signature data: {:?}", signer_to_sign.signature_data); // Example base64 signature data (1x1 transparent PNG) let signature_data = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==".to_string(); // Sign the contract with signature data signer_to_sign.sign( Some(signature_data.clone()), Some("I agree to all terms and conditions.".to_string()), ); println!("\nAfter signing:"); println!(" Status: {:?}", signer_to_sign.status); println!(" Signed at: {:?}", signer_to_sign.signed_at); println!(" Comments: {:?}", signer_to_sign.comments); println!( " Signature data length: {} characters", signer_to_sign .signature_data .as_ref() .map_or(0, |s| s.len()) ); println!( " Signature data preview: {}...", signer_to_sign .signature_data .as_ref() .map_or("None".to_string(), |s| s .chars() .take(50) .collect::()) ); } // Demonstrate signing without signature data if let Some(first_signer) = contract.signers.get_mut(0) { println!("\nSigning without signature data:"); println!(" Signer: {}", first_signer.name); first_signer.sign( None, Some("Signed electronically without visual signature.".to_string()), ); println!(" Status after signing: {:?}", first_signer.status); println!(" Signature data: {:?}", first_signer.signature_data); println!(" Comments: {:?}", first_signer.comments); } println!("\nLegal Contract Model demonstration complete."); }