...
This commit is contained in:
parent
d32a41d579
commit
98af5a3b02
@ -7,8 +7,7 @@ use herodb::models::gov::{
|
|||||||
User,
|
User,
|
||||||
Vote, VoteOption, Ballot, VoteStatus,
|
Vote, VoteOption, Ballot, VoteStatus,
|
||||||
Resolution, ResolutionStatus, Approval,
|
Resolution, ResolutionStatus, Approval,
|
||||||
Committee, CommitteeRole, CommitteeMember,
|
Committee, CommitteeRole, CommitteeMember
|
||||||
ComplianceRequirement, ComplianceDocument, ComplianceAudit
|
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -34,9 +33,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.register_model::<Vote>()
|
.register_model::<Vote>()
|
||||||
.register_model::<Resolution>()
|
.register_model::<Resolution>()
|
||||||
.register_model::<Committee>()
|
.register_model::<Committee>()
|
||||||
.register_model::<ComplianceRequirement>()
|
|
||||||
.register_model::<ComplianceDocument>()
|
|
||||||
.register_model::<ComplianceAudit>()
|
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
println!("\n1. Creating a Company");
|
println!("\n1. Creating a Company");
|
||||||
|
@ -43,7 +43,7 @@ pub struct DB {
|
|||||||
db_path: PathBuf,
|
db_path: PathBuf,
|
||||||
|
|
||||||
// Type map for generic operations
|
// Type map for generic operations
|
||||||
type_map: HashMap<TypeId, Arc<dyn DbOperations>>,
|
type_map: HashMap<TypeId, Arc<RwLock<dyn DbOperations>>>,
|
||||||
|
|
||||||
// Transaction state
|
// Transaction state
|
||||||
transaction: Arc<RwLock<Option<TransactionState>>>,
|
transaction: Arc<RwLock<Option<TransactionState>>>,
|
||||||
@ -58,7 +58,7 @@ pub struct DBBuilder {
|
|||||||
|
|
||||||
/// Trait for model registration
|
/// Trait for model registration
|
||||||
pub trait ModelRegistration: Send + Sync {
|
pub trait ModelRegistration: Send + Sync {
|
||||||
fn register(&self, path: &Path) -> DbResult<(TypeId, Box<dyn DbOperations>)>;
|
fn register(&self, path: &Path) -> DbResult<(TypeId, Arc<RwLock<dyn DbOperations>>)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of ModelRegistration for any Model type
|
/// Implementation of ModelRegistration for any Model type
|
||||||
@ -75,9 +75,9 @@ impl<T: Model> ModelRegistrar<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Model> ModelRegistration for ModelRegistrar<T> {
|
impl<T: Model> ModelRegistration for ModelRegistrar<T> {
|
||||||
fn register(&self, path: &Path) -> DbResult<(TypeId, Box<dyn DbOperations>)> {
|
fn register(&self, path: &Path) -> DbResult<(TypeId, Arc<RwLock<dyn DbOperations>>)> {
|
||||||
let store = OurDbStore::<T>::open(path.join(T::db_prefix()))?;
|
let store = OurDbStore::<T>::open(path.join(T::db_prefix()))?;
|
||||||
Ok((TypeId::of::<T>(), Box::new(store) as Box<dyn DbOperations>))
|
Ok((TypeId::of::<T>(), Arc::new(RwLock::new(store)) as Arc<RwLock<dyn DbOperations>>))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +116,13 @@ impl DBBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register all models
|
// Register all models
|
||||||
let mut type_map: HashMap<TypeId, Arc<dyn DbOperations>> = HashMap::new();
|
let mut type_map: HashMap<TypeId, Arc<RwLock<dyn DbOperations>>> = HashMap::new();
|
||||||
|
|
||||||
for registration in self.model_registrations {
|
for registration in self.model_registrations {
|
||||||
let (type_id, store) = registration.register(&base_path).map_err(|e| {
|
let (type_id, store) = registration.register(&base_path).map_err(|e| {
|
||||||
EvalAltResult::ErrorSystem("Could not register type".to_string(), Box::new(e))
|
EvalAltResult::ErrorSystem("Could not register type".to_string(), Box::new(e))
|
||||||
})?;
|
})?;
|
||||||
type_map.insert(type_id, store.into());
|
type_map.insert(type_id, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
let transaction = Arc::new(RwLock::new(None));
|
let transaction = Arc::new(RwLock::new(None));
|
||||||
@ -179,7 +179,8 @@ impl DB {
|
|||||||
// Get the database operations for this model type
|
// Get the database operations for this model type
|
||||||
if let Some(db_ops) = self.type_map.get(&model_type) {
|
if let Some(db_ops) = self.type_map.get(&model_type) {
|
||||||
// Just pass the raw serialized data to a special raw insert method
|
// Just pass the raw serialized data to a special raw insert method
|
||||||
return db_ops.insert_raw(serialized);
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
return db_ops_guard.insert_raw(serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(DbError::GeneralError(format!(
|
Err(DbError::GeneralError(format!(
|
||||||
@ -211,7 +212,8 @@ impl DB {
|
|||||||
.type_map
|
.type_map
|
||||||
.get(&model_type)
|
.get(&model_type)
|
||||||
.ok_or_else(|| DbError::TypeError)?;
|
.ok_or_else(|| DbError::TypeError)?;
|
||||||
db_ops.delete(id)?;
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
db_ops_guard.delete(id)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,7 +268,10 @@ impl DB {
|
|||||||
|
|
||||||
// Execute directly
|
// Execute directly
|
||||||
match self.type_map.get(&TypeId::of::<T>()) {
|
match self.type_map.get(&TypeId::of::<T>()) {
|
||||||
Some(db_ops) => db_ops.insert(model),
|
Some(db_ops) => {
|
||||||
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
db_ops_guard.insert(model)
|
||||||
|
},
|
||||||
None => Err(DbError::TypeError),
|
None => Err(DbError::TypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +340,8 @@ impl DB {
|
|||||||
// If no pending value, look up from the database
|
// If no pending value, look up from the database
|
||||||
match self.type_map.get(&TypeId::of::<T>()) {
|
match self.type_map.get(&TypeId::of::<T>()) {
|
||||||
Some(db_ops) => {
|
Some(db_ops) => {
|
||||||
let result_any = db_ops.get(id)?;
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
let result_any = db_ops_guard.get(id)?;
|
||||||
// We expect the result to be of type T since we looked it up by TypeId
|
// We expect the result to be of type T since we looked it up by TypeId
|
||||||
match result_any.downcast::<T>() {
|
match result_any.downcast::<T>() {
|
||||||
Ok(t) => Ok(*t),
|
Ok(t) => Ok(*t),
|
||||||
@ -370,7 +376,10 @@ impl DB {
|
|||||||
|
|
||||||
// Execute directly
|
// Execute directly
|
||||||
match self.type_map.get(&TypeId::of::<T>()) {
|
match self.type_map.get(&TypeId::of::<T>()) {
|
||||||
Some(db_ops) => db_ops.delete(id),
|
Some(db_ops) => {
|
||||||
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
db_ops_guard.delete(id)
|
||||||
|
},
|
||||||
None => Err(DbError::TypeError),
|
None => Err(DbError::TypeError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,7 +389,8 @@ impl DB {
|
|||||||
// Look up the correct DB operations for type T in our type map
|
// Look up the correct DB operations for type T in our type map
|
||||||
match self.type_map.get(&TypeId::of::<T>()) {
|
match self.type_map.get(&TypeId::of::<T>()) {
|
||||||
Some(db_ops) => {
|
Some(db_ops) => {
|
||||||
let result_any = db_ops.list()?;
|
let db_ops_guard = db_ops.read().unwrap();
|
||||||
|
let result_any = db_ops_guard.list()?;
|
||||||
// We expect the result to be of type Vec<T> since we looked it up by TypeId
|
// We expect the result to be of type Vec<T> since we looked it up by TypeId
|
||||||
match result_any.downcast::<Vec<T>>() {
|
match result_any.downcast::<Vec<T>>() {
|
||||||
Ok(vec_t) => Ok(*vec_t),
|
Ok(vec_t) => Ok(*vec_t),
|
||||||
@ -396,7 +406,8 @@ impl DB {
|
|||||||
// Look up the correct DB operations for type T in our type map
|
// Look up the correct DB operations for type T in our type map
|
||||||
match self.type_map.get(&TypeId::of::<T>()) {
|
match self.type_map.get(&TypeId::of::<T>()) {
|
||||||
Some(db_ops) => {
|
Some(db_ops) => {
|
||||||
let result_any = db_ops.get_history(id, depth)?;
|
let mut db_ops_guard = db_ops.write().unwrap();
|
||||||
|
let result_any = db_ops_guard.get_history(id, depth)?;
|
||||||
let mut result = Vec::with_capacity(result_any.len());
|
let mut result = Vec::with_capacity(result_any.len());
|
||||||
|
|
||||||
for item in result_any {
|
for item in result_any {
|
||||||
@ -415,7 +426,7 @@ impl DB {
|
|||||||
// Register a model type with this DB instance
|
// Register a model type with this DB instance
|
||||||
pub fn register<T: Model>(&mut self) -> DbResult<()> {
|
pub fn register<T: Model>(&mut self) -> DbResult<()> {
|
||||||
let store = OurDbStore::<T>::open(&self.db_path)?;
|
let store = OurDbStore::<T>::open(&self.db_path)?;
|
||||||
self.type_map.insert(TypeId::of::<T>(), Arc::new(store));
|
self.type_map.insert(TypeId::of::<T>(), Arc::new(RwLock::new(store)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ use std::any::Any;
|
|||||||
|
|
||||||
/// Trait for type-erased database operations
|
/// Trait for type-erased database operations
|
||||||
pub trait DbOperations: Send + Sync {
|
pub trait DbOperations: Send + Sync {
|
||||||
fn delete(&self, id: u32) -> DbResult<()>;
|
fn delete(&mut self, id: u32) -> DbResult<()>;
|
||||||
fn get(&self, id: u32) -> DbResult<Box<dyn Any>>;
|
fn get(&mut self, id: u32) -> DbResult<Box<dyn Any>>;
|
||||||
fn list(&self) -> DbResult<Box<dyn Any>>;
|
fn list(&self) -> DbResult<Box<dyn Any>>;
|
||||||
fn insert(&self, model: &dyn Any) -> DbResult<()>;
|
fn insert(&mut self, model: &dyn Any) -> DbResult<()>;
|
||||||
fn insert_raw(&self, serialized: &[u8]) -> DbResult<()>;
|
fn insert_raw(&mut self, serialized: &[u8]) -> DbResult<()>;
|
||||||
fn get_history(&self, id: u32, depth: u8) -> DbResult<Vec<Box<dyn Any>>>;
|
fn get_history(&mut self, id: u32, depth: u8) -> DbResult<Vec<Box<dyn Any>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A store implementation using OurDB as the backend
|
/// A store implementation using OurDB as the backend
|
||||||
@ -50,12 +50,13 @@ impl<T: Model> OurDbStore<T> {
|
|||||||
|
|
||||||
/// Inserts or updates a model instance in the database
|
/// Inserts or updates a model instance in the database
|
||||||
pub fn insert(&mut self, model: &T) -> DbResult<()> {
|
pub fn insert(&mut self, model: &T) -> DbResult<()> {
|
||||||
let id = model.get_id();
|
|
||||||
// Use the new method name
|
// Use the new method name
|
||||||
let data = model.to_bytes()?;
|
let data = model.to_bytes()?;
|
||||||
|
|
||||||
|
// Don't pass the ID when using incremental mode
|
||||||
|
// OurDB will automatically assign an ID
|
||||||
self.db.set(OurDBSetArgs {
|
self.db.set(OurDBSetArgs {
|
||||||
id: Some(id),
|
id: None,
|
||||||
data: &data,
|
data: &data,
|
||||||
}).map_err(DbError::OurDbError)?;
|
}).map_err(DbError::OurDbError)?;
|
||||||
|
|
||||||
@ -112,16 +113,13 @@ impl<T: Model> OurDbStore<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Model> DbOperations for OurDbStore<T> {
|
impl<T: Model> DbOperations for OurDbStore<T> {
|
||||||
fn delete(&self, _id: u32) -> DbResult<()> {
|
fn delete(&mut self, id: u32) -> DbResult<()> {
|
||||||
// We need to mutably borrow self, but the trait requires &self
|
self.delete(id)
|
||||||
// This is a design issue that needs to be fixed at the trait level
|
|
||||||
Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, _id: u32) -> DbResult<Box<dyn Any>> {
|
fn get(&mut self, id: u32) -> DbResult<Box<dyn Any>> {
|
||||||
// We need to mutably borrow self, but the trait requires &self
|
let result = self.get(id)?;
|
||||||
// This is a design issue that needs to be fixed at the trait level
|
Ok(Box::new(result))
|
||||||
Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(&self) -> DbResult<Box<dyn Any>> {
|
fn list(&self) -> DbResult<Box<dyn Any>> {
|
||||||
@ -130,21 +128,29 @@ impl<T: Model> DbOperations for OurDbStore<T> {
|
|||||||
Ok(Box::new(result))
|
Ok(Box::new(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&self, _model: &dyn Any) -> DbResult<()> {
|
fn insert(&mut self, model: &dyn Any) -> DbResult<()> {
|
||||||
// We need to mutably borrow self, but the trait requires &self
|
// Downcast the Any to T
|
||||||
// This is a design issue that needs to be fixed at the trait level
|
if let Some(model_t) = model.downcast_ref::<T>() {
|
||||||
Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string()))
|
self.insert(model_t)
|
||||||
|
} else {
|
||||||
|
Err(DbError::TypeError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_raw(&self, _serialized: &[u8]) -> DbResult<()> {
|
fn insert_raw(&mut self, serialized: &[u8]) -> DbResult<()> {
|
||||||
// We need to mutably borrow self, but the trait requires &self
|
// Deserialize the raw bytes to a model
|
||||||
// This is a design issue that needs to be fixed at the trait level
|
let model = T::from_bytes(serialized)?;
|
||||||
Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string()))
|
self.insert(&model)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_history(&self, _id: u32, _depth: u8) -> DbResult<Vec<Box<dyn Any>>> {
|
fn get_history(&mut self, id: u32, depth: u8) -> DbResult<Vec<Box<dyn Any>>> {
|
||||||
// We need to mutably borrow self, but the trait requires &self
|
let history = self.get_history(id, depth)?;
|
||||||
// This is a design issue that needs to be fixed at the trait level
|
let mut result = Vec::with_capacity(history.len());
|
||||||
Err(DbError::GeneralError("DbOperations trait needs to be updated to use &mut self".to_string()))
|
|
||||||
|
for item in history {
|
||||||
|
result.push(Box::new(item) as Box<dyn Any>);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,496 +0,0 @@
|
|||||||
# Governance Module Enhancement Plan (Revised)
|
|
||||||
|
|
||||||
## 1. Current State Analysis
|
|
||||||
|
|
||||||
The governance module currently consists of:
|
|
||||||
- **Company**: Company model with basic company information
|
|
||||||
- **Shareholder**: Shareholder model for managing company ownership
|
|
||||||
- **Meeting**: Meeting and Attendee models for board meetings
|
|
||||||
- **User**: User model for system users
|
|
||||||
- **Vote**: Vote, VoteOption, and Ballot models for voting
|
|
||||||
|
|
||||||
All models implement the `Storable` and `SledModel` traits for database integration, but the module has several limitations:
|
|
||||||
- Not imported in src/models/mod.rs, making it inaccessible to the rest of the project
|
|
||||||
- No mod.rs file to organize and re-export the types
|
|
||||||
- No README.md file to document the purpose and usage
|
|
||||||
- Inconsistent imports across files (e.g., crate::db vs crate::core)
|
|
||||||
- Limited utility methods and relationships between models
|
|
||||||
- No integration with other modules like biz, mcc, or circle
|
|
||||||
|
|
||||||
## 2. Planned Enhancements
|
|
||||||
|
|
||||||
### 2.1 Module Organization and Integration
|
|
||||||
|
|
||||||
- Create a mod.rs file to organize and re-export the types
|
|
||||||
- Add the governance module to src/models/mod.rs
|
|
||||||
- Create a README.md file to document the purpose and usage
|
|
||||||
- Standardize imports across all files
|
|
||||||
|
|
||||||
### 2.2 New Models
|
|
||||||
|
|
||||||
#### 2.2.1 Resolution Model
|
|
||||||
|
|
||||||
Create a new `resolution.rs` file with a Resolution model for managing board resolutions:
|
|
||||||
- Resolution information (title, description, text)
|
|
||||||
- Resolution status (Draft, Proposed, Approved, Rejected)
|
|
||||||
- Voting results and approvals
|
|
||||||
- Integration with Meeting and Vote models
|
|
||||||
|
|
||||||
### 2.3 Enhanced Relationships and Integration
|
|
||||||
|
|
||||||
#### 2.3.1 Integration with Biz Module
|
|
||||||
|
|
||||||
- Link Company with biz::Customer and biz::Contract
|
|
||||||
- Link Shareholder with biz::Customer
|
|
||||||
- Link Meeting with biz::Invoice for expense tracking
|
|
||||||
|
|
||||||
#### 2.3.2 Integration with MCC Module
|
|
||||||
|
|
||||||
- Link Meeting with mcc::Calendar and mcc::Event
|
|
||||||
- Link User with mcc::Contact
|
|
||||||
- Link Vote with mcc::Message for notifications
|
|
||||||
|
|
||||||
#### 2.3.3 Integration with Circle Module
|
|
||||||
|
|
||||||
- Link Company with circle::Circle for group-based access control
|
|
||||||
- Link User with circle::Member for role-based permissions
|
|
||||||
|
|
||||||
### 2.4 Utility Methods and Functionality
|
|
||||||
|
|
||||||
- Add filtering and searching methods to all models
|
|
||||||
- Add relationship management methods between models
|
|
||||||
- Add validation and business logic methods
|
|
||||||
|
|
||||||
## 3. Implementation Plan
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart TD
|
|
||||||
A[Review Current Models] --> B[Create mod.rs and Update models/mod.rs]
|
|
||||||
B --> C[Standardize Imports and Fix Inconsistencies]
|
|
||||||
C --> D[Create Resolution Model]
|
|
||||||
D --> E[Implement Integration with Other Modules]
|
|
||||||
E --> F[Add Utility Methods]
|
|
||||||
F --> G[Create README.md and Documentation]
|
|
||||||
G --> H[Write Tests]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.1 Detailed Changes
|
|
||||||
|
|
||||||
#### 3.1.1 Module Organization
|
|
||||||
|
|
||||||
Create a new `mod.rs` file in the governance directory:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
pub mod company;
|
|
||||||
pub mod shareholder;
|
|
||||||
pub mod meeting;
|
|
||||||
pub mod user;
|
|
||||||
pub mod vote;
|
|
||||||
pub mod resolution;
|
|
||||||
|
|
||||||
// Re-export all model types for convenience
|
|
||||||
pub use company::{Company, CompanyStatus, BusinessType};
|
|
||||||
pub use shareholder::{Shareholder, ShareholderType};
|
|
||||||
pub use meeting::{Meeting, Attendee, MeetingStatus, AttendeeRole, AttendeeStatus};
|
|
||||||
pub use user::User;
|
|
||||||
pub use vote::{Vote, VoteOption, Ballot, VoteStatus};
|
|
||||||
pub use resolution::{Resolution, ResolutionStatus, Approval};
|
|
||||||
|
|
||||||
// Re-export database components from db module
|
|
||||||
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
|
|
||||||
```
|
|
||||||
|
|
||||||
Update `src/models/mod.rs` to include the governance module:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
pub mod biz;
|
|
||||||
pub mod mcc;
|
|
||||||
pub mod circle;
|
|
||||||
pub mod governance;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.1.2 Resolution Model (`resolution.rs`)
|
|
||||||
|
|
||||||
```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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.1.3 Enhanced Company Model (`company.rs`)
|
|
||||||
|
|
||||||
Add integration with other modules:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
impl Company {
|
|
||||||
// ... existing methods ...
|
|
||||||
|
|
||||||
/// Link this company to a Circle for access control
|
|
||||||
pub fn link_to_circle(&mut self, circle_id: u32) -> Result<(), SledDBError> {
|
|
||||||
// Implementation details
|
|
||||||
self.updated_at = Utc::now();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Link this company to a Customer in the biz module
|
|
||||||
pub fn link_to_customer(&mut self, customer_id: u32) -> Result<(), SledDBError> {
|
|
||||||
// Implementation details
|
|
||||||
self.updated_at = Utc::now();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all resolutions for this company
|
|
||||||
pub fn get_resolutions(&self, db: &SledDB<Resolution>) -> Result<Vec<Resolution>, SledDBError> {
|
|
||||||
let all_resolutions = db.list()?;
|
|
||||||
let company_resolutions = all_resolutions
|
|
||||||
.into_iter()
|
|
||||||
.filter(|resolution| resolution.company_id == self.id)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(company_resolutions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.1.4 Enhanced Meeting Model (`meeting.rs`)
|
|
||||||
|
|
||||||
Add integration with other modules:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
impl Meeting {
|
|
||||||
// ... existing methods ...
|
|
||||||
|
|
||||||
/// Link this meeting to a Calendar Event in the mcc module
|
|
||||||
pub fn link_to_event(&mut self, event_id: u32) -> Result<(), SledDBError> {
|
|
||||||
// Implementation details
|
|
||||||
self.updated_at = Utc::now();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all resolutions discussed in this meeting
|
|
||||||
pub fn get_resolutions(&self, db: &SledDB<Resolution>) -> Result<Vec<Resolution>, SledDBError> {
|
|
||||||
let all_resolutions = db.list()?;
|
|
||||||
let meeting_resolutions = all_resolutions
|
|
||||||
.into_iter()
|
|
||||||
.filter(|resolution| resolution.meeting_id == Some(self.id))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(meeting_resolutions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.1.5 Enhanced Vote Model (`vote.rs`)
|
|
||||||
|
|
||||||
Add integration with Resolution model:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
impl Vote {
|
|
||||||
// ... existing methods ...
|
|
||||||
|
|
||||||
/// Get the resolution associated with this vote
|
|
||||||
pub fn get_resolution(&self, db: &SledDB<Resolution>) -> Result<Option<Resolution>, SledDBError> {
|
|
||||||
let all_resolutions = db.list()?;
|
|
||||||
let vote_resolution = all_resolutions
|
|
||||||
.into_iter()
|
|
||||||
.find(|resolution| resolution.vote_id == Some(self.id));
|
|
||||||
|
|
||||||
Ok(vote_resolution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.1.6 Create README.md
|
|
||||||
|
|
||||||
Create a README.md file to document the purpose and usage of the governance module.
|
|
||||||
|
|
||||||
## 4. Data Model Diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
classDiagram
|
|
||||||
class Company {
|
|
||||||
+u32 id
|
|
||||||
+String name
|
|
||||||
+String registration_number
|
|
||||||
+DateTime incorporation_date
|
|
||||||
+String fiscal_year_end
|
|
||||||
+String email
|
|
||||||
+String phone
|
|
||||||
+String website
|
|
||||||
+String address
|
|
||||||
+BusinessType business_type
|
|
||||||
+String industry
|
|
||||||
+String description
|
|
||||||
+CompanyStatus status
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
+add_shareholder()
|
|
||||||
+link_to_circle()
|
|
||||||
+link_to_customer()
|
|
||||||
+get_resolutions()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Shareholder {
|
|
||||||
+u32 id
|
|
||||||
+u32 company_id
|
|
||||||
+u32 user_id
|
|
||||||
+String name
|
|
||||||
+f64 shares
|
|
||||||
+f64 percentage
|
|
||||||
+ShareholderType type_
|
|
||||||
+DateTime since
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
+update_shares()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Meeting {
|
|
||||||
+u32 id
|
|
||||||
+u32 company_id
|
|
||||||
+String title
|
|
||||||
+DateTime date
|
|
||||||
+String location
|
|
||||||
+String description
|
|
||||||
+MeetingStatus status
|
|
||||||
+String minutes
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
+Vec~Attendee~ attendees
|
|
||||||
+add_attendee()
|
|
||||||
+update_status()
|
|
||||||
+update_minutes()
|
|
||||||
+find_attendee_by_user_id()
|
|
||||||
+confirmed_attendees()
|
|
||||||
+link_to_event()
|
|
||||||
+get_resolutions()
|
|
||||||
}
|
|
||||||
|
|
||||||
class User {
|
|
||||||
+u32 id
|
|
||||||
+String name
|
|
||||||
+String email
|
|
||||||
+String password
|
|
||||||
+String company
|
|
||||||
+String role
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vote {
|
|
||||||
+u32 id
|
|
||||||
+u32 company_id
|
|
||||||
+String title
|
|
||||||
+String description
|
|
||||||
+DateTime start_date
|
|
||||||
+DateTime end_date
|
|
||||||
+VoteStatus status
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
+Vec~VoteOption~ options
|
|
||||||
+Vec~Ballot~ ballots
|
|
||||||
+Vec~u32~ private_group
|
|
||||||
+add_option()
|
|
||||||
+add_ballot()
|
|
||||||
+get_resolution()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Resolution {
|
|
||||||
+u32 id
|
|
||||||
+u32 company_id
|
|
||||||
+Option~u32~ meeting_id
|
|
||||||
+Option~u32~ vote_id
|
|
||||||
+String title
|
|
||||||
+String description
|
|
||||||
+String text
|
|
||||||
+ResolutionStatus status
|
|
||||||
+u32 proposed_by
|
|
||||||
+DateTime proposed_at
|
|
||||||
+Option~DateTime~ approved_at
|
|
||||||
+Option~DateTime~ rejected_at
|
|
||||||
+DateTime created_at
|
|
||||||
+DateTime updated_at
|
|
||||||
+Vec~Approval~ approvals
|
|
||||||
+propose()
|
|
||||||
+approve()
|
|
||||||
+reject()
|
|
||||||
+add_approval()
|
|
||||||
+link_to_meeting()
|
|
||||||
+link_to_vote()
|
|
||||||
}
|
|
||||||
|
|
||||||
Company "1" -- "many" Shareholder: has
|
|
||||||
Company "1" -- "many" Meeting: holds
|
|
||||||
Company "1" -- "many" Vote: conducts
|
|
||||||
Company "1" -- "many" Resolution: issues
|
|
||||||
Meeting "1" -- "many" Attendee: has
|
|
||||||
Meeting "1" -- "many" Resolution: discusses
|
|
||||||
Vote "1" -- "many" VoteOption: has
|
|
||||||
Vote "1" -- "many" Ballot: collects
|
|
||||||
Vote "1" -- "1" Resolution: decides
|
|
||||||
Resolution "1" -- "many" Approval: receives
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Testing Strategy
|
|
||||||
|
|
||||||
1. Unit tests for each model to verify:
|
|
||||||
- Basic functionality
|
|
||||||
- Serialization/deserialization
|
|
||||||
- Utility methods
|
|
||||||
- Integration with other models
|
|
||||||
2. Integration tests to verify:
|
|
||||||
- Database operations with the models
|
|
||||||
- Relationships between models
|
|
||||||
- Integration with other modules
|
|
||||||
|
|
||||||
## 6. Future Considerations
|
|
||||||
|
|
||||||
1. **Committee Model**: Add a Committee model in the future if needed
|
|
||||||
2. **Compliance Model**: Add compliance-related models in the future if needed
|
|
||||||
3. **API Integration**: Develop REST API endpoints for the governance module
|
|
||||||
4. **UI Components**: Create UI components for managing governance entities
|
|
||||||
5. **Reporting**: Implement reporting functionality for governance metrics
|
|
Loading…
Reference in New Issue
Block a user