use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use crate::db::{SledModel, Storable, SledDB, SledDBError}; use crate::models::gov::{Meeting, Vote}; /// ResolutionStatus represents the status of a resolution #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum ResolutionStatus { Draft, Proposed, Approved, Rejected, Withdrawn, } /// Resolution represents a board resolution #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Resolution { pub id: u32, pub company_id: u32, pub meeting_id: Option, pub vote_id: Option, pub title: String, pub description: String, pub text: String, pub status: ResolutionStatus, pub proposed_by: u32, // User ID pub proposed_at: DateTime, pub approved_at: Option>, pub rejected_at: Option>, pub created_at: DateTime, pub updated_at: DateTime, pub approvals: Vec, } /// Approval represents an approval of a resolution by a board member #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Approval { pub id: u32, pub resolution_id: u32, pub user_id: u32, pub name: String, pub approved: bool, pub comments: String, pub created_at: DateTime, } impl Resolution { /// Create a new resolution with default values pub fn new( id: u32, company_id: u32, title: String, description: String, text: String, proposed_by: u32, ) -> Self { let now = Utc::now(); Self { id, company_id, meeting_id: None, vote_id: None, title, description, text, status: ResolutionStatus::Draft, proposed_by, proposed_at: now, approved_at: None, rejected_at: None, created_at: now, updated_at: now, approvals: Vec::new(), } } /// Propose the resolution pub fn propose(&mut self) { self.status = ResolutionStatus::Proposed; self.proposed_at = Utc::now(); self.updated_at = Utc::now(); } /// Approve the resolution pub fn approve(&mut self) { self.status = ResolutionStatus::Approved; self.approved_at = Some(Utc::now()); self.updated_at = Utc::now(); } /// Reject the resolution pub fn reject(&mut self) { self.status = ResolutionStatus::Rejected; self.rejected_at = Some(Utc::now()); self.updated_at = Utc::now(); } /// Withdraw the resolution pub fn withdraw(&mut self) { self.status = ResolutionStatus::Withdrawn; self.updated_at = Utc::now(); } /// Add an approval to the resolution pub fn add_approval(&mut self, user_id: u32, name: String, approved: bool, comments: String) -> &Approval { let id = if self.approvals.is_empty() { 1 } else { self.approvals.iter().map(|a| a.id).max().unwrap_or(0) + 1 }; let approval = Approval { id, resolution_id: self.id, user_id, name, approved, comments, created_at: Utc::now(), }; self.approvals.push(approval); self.updated_at = Utc::now(); self.approvals.last().unwrap() } /// Find an approval by user ID pub fn find_approval_by_user_id(&self, user_id: u32) -> Option<&Approval> { self.approvals.iter().find(|a| a.user_id == user_id) } /// Get all approvals pub fn get_approvals(&self) -> &[Approval] { &self.approvals } /// Get approval count pub fn approval_count(&self) -> usize { self.approvals.iter().filter(|a| a.approved).count() } /// Get rejection count pub fn rejection_count(&self) -> usize { self.approvals.iter().filter(|a| !a.approved).count() } /// Link this resolution to a meeting pub fn link_to_meeting(&mut self, meeting_id: u32) { self.meeting_id = Some(meeting_id); self.updated_at = Utc::now(); } /// Link this resolution to a vote pub fn link_to_vote(&mut self, vote_id: u32) { self.vote_id = Some(vote_id); self.updated_at = Utc::now(); } /// Get the meeting associated with this resolution pub fn get_meeting(&self, db: &crate::db::DB) -> Result, SledDBError> { match self.meeting_id { Some(meeting_id) => { let meeting = db.get::(&meeting_id.to_string())?; Ok(Some(meeting)) } None => Ok(None), } } /// Get the vote associated with this resolution pub fn get_vote(&self, db: &crate::db::DB) -> Result, SledDBError> { match self.vote_id { Some(vote_id) => { let vote = db.get::(&vote_id.to_string())?; Ok(Some(vote)) } None => Ok(None), } } } // Implement Storable trait (provides default dump/load) impl Storable for Resolution {} impl Storable for Approval {} // Implement SledModel trait impl SledModel for Resolution { fn get_id(&self) -> String { self.id.to_string() } fn db_prefix() -> &'static str { "resolution" } }