196 lines
5.4 KiB
Rust
196 lines
5.4 KiB
Rust
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<u32>,
|
|
pub vote_id: Option<u32>,
|
|
pub title: String,
|
|
pub description: String,
|
|
pub text: String,
|
|
pub status: ResolutionStatus,
|
|
pub proposed_by: u32, // User ID
|
|
pub proposed_at: DateTime<Utc>,
|
|
pub approved_at: Option<DateTime<Utc>>,
|
|
pub rejected_at: Option<DateTime<Utc>>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
pub approvals: Vec<Approval>,
|
|
}
|
|
|
|
/// 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<Utc>,
|
|
}
|
|
|
|
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<Option<Meeting>, SledDBError> {
|
|
match self.meeting_id {
|
|
Some(meeting_id) => {
|
|
let meeting = db.get::<Meeting>(&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<Option<Vote>, SledDBError> {
|
|
match self.vote_id {
|
|
Some(vote_id) => {
|
|
let vote = db.get::<Vote>(&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"
|
|
}
|
|
} |