Compare commits
	
		
			4 Commits
		
	
	
		
			main
			...
			developmen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6569e819ae | ||
| 
						 | 
					130822b69b | ||
| 
						 | 
					7439980b33 | ||
| 
						 | 
					453e86edd2 | 
							
								
								
									
										48
									
								
								heromodels/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										48
									
								
								heromodels/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -60,7 +60,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -233,14 +233,6 @@ dependencies = [
 | 
				
			|||||||
 "typenum",
 | 
					 "typenum",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "derive"
 | 
					 | 
				
			||||||
version = "0.1.0"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "quote",
 | 
					 | 
				
			||||||
 "syn 1.0.109",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "digest"
 | 
					name = "digest"
 | 
				
			||||||
version = "0.10.7"
 | 
					version = "0.10.7"
 | 
				
			||||||
@@ -300,7 +292,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -387,7 +379,6 @@ version = "0.1.0"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode",
 | 
					 "bincode",
 | 
				
			||||||
 "chrono",
 | 
					 "chrono",
 | 
				
			||||||
 "derive",
 | 
					 | 
				
			||||||
 "heromodels-derive",
 | 
					 "heromodels-derive",
 | 
				
			||||||
 "heromodels_core",
 | 
					 "heromodels_core",
 | 
				
			||||||
 "jsonb",
 | 
					 "jsonb",
 | 
				
			||||||
@@ -411,7 +402,7 @@ version = "0.1.0"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -514,7 +505,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -952,7 +943,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1015,7 +1006,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1145,7 +1136,7 @@ dependencies = [
 | 
				
			|||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "rustversion",
 | 
					 "rustversion",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1154,17 +1145,6 @@ version = "2.6.1"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 | 
					checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "syn"
 | 
					 | 
				
			||||||
version = "1.0.109"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "proc-macro2",
 | 
					 | 
				
			||||||
 "quote",
 | 
					 | 
				
			||||||
 "unicode-ident",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "syn"
 | 
					name = "syn"
 | 
				
			||||||
version = "2.0.104"
 | 
					version = "2.0.104"
 | 
				
			||||||
@@ -1199,7 +1179,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1254,7 +1234,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1409,7 +1389,7 @@ dependencies = [
 | 
				
			|||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
 "wasm-bindgen-shared",
 | 
					 "wasm-bindgen-shared",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1431,7 +1411,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
 "wasm-bindgen-backend",
 | 
					 "wasm-bindgen-backend",
 | 
				
			||||||
 "wasm-bindgen-shared",
 | 
					 "wasm-bindgen-shared",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1487,7 +1467,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1498,7 +1478,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1633,5 +1613,5 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn 2.0.104",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								heromodels/src/models/tfmarketplace/activity.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								heromodels/src/models/tfmarketplace/activity.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct UserActivityBuilder {
 | 
				
			||||||
 | 
					    base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    activity_type: Option<crate::models::user::ActivityType>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    timestamp: Option<chrono::DateTime<chrono::Utc>>,
 | 
				
			||||||
 | 
					    metadata: Option<std::collections::HashMap<String, serde_json::Value>>,
 | 
				
			||||||
 | 
					    category: Option<String>,
 | 
				
			||||||
 | 
					    importance: Option<crate::models::user::ActivityImportance>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl UserActivityBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.base_data.id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn activity_type(mut self, activity_type: crate::models::user::ActivityType) -> Self {
 | 
				
			||||||
 | 
					        self.activity_type = Some(activity_type);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn description(mut self, description: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.description = Some(description.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn timestamp(mut self, timestamp: chrono::DateTime<chrono::Utc>) -> Self {
 | 
				
			||||||
 | 
					        self.timestamp = Some(timestamp);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn metadata(mut self, metadata: std::collections::HashMap<String, serde_json::Value>) -> Self {
 | 
				
			||||||
 | 
					        self.metadata = Some(metadata);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn category(mut self, category: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.category = Some(category.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn importance(mut self, importance: crate::models::user::ActivityImportance) -> Self {
 | 
				
			||||||
 | 
					        self.importance = Some(importance);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::models::user::UserActivity, String> {
 | 
				
			||||||
 | 
					        Ok(crate::models::user::UserActivity {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.base_data.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()) - moved to base_data,
 | 
				
			||||||
 | 
					            activity_type: self.activity_type.ok_or("activity_type is required")?,
 | 
				
			||||||
 | 
					            description: self.description.unwrap_or_else(|| "No description".to_string()),
 | 
				
			||||||
 | 
					            timestamp: self.timestamp.unwrap_or_else(|| chrono::Utc::now()),
 | 
				
			||||||
 | 
					            metadata: self.metadata.unwrap_or_default(),
 | 
				
			||||||
 | 
					            category: self.category.unwrap_or_else(|| "General".to_string()),
 | 
				
			||||||
 | 
					            importance: self.importance.unwrap_or(crate::models::user::ActivityImportance::Medium),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// User Activity Tracking
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct UserActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub activity_type: ActivityType,
 | 
				
			||||||
 | 
					    pub description: String,
 | 
				
			||||||
 | 
					    #[serde(deserialize_with = "deserialize_datetime")]
 | 
				
			||||||
 | 
					    pub timestamp: DateTime<Utc>,
 | 
				
			||||||
 | 
					    pub metadata: std::collections::HashMap<String, serde_json::Value>,
 | 
				
			||||||
 | 
					    pub category: String,
 | 
				
			||||||
 | 
					    pub importance: ActivityImportance,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum ActivityType {
 | 
				
			||||||
 | 
					    Login,
 | 
				
			||||||
 | 
					    Purchase,
 | 
				
			||||||
 | 
					    Deployment,
 | 
				
			||||||
 | 
					    ServiceCreated,
 | 
				
			||||||
 | 
					    AppPublished,
 | 
				
			||||||
 | 
					    NodeAdded,
 | 
				
			||||||
 | 
					    NodeUpdated,
 | 
				
			||||||
 | 
					    WalletTransaction,
 | 
				
			||||||
 | 
					    ProfileUpdate,
 | 
				
			||||||
 | 
					    SettingsChange,
 | 
				
			||||||
 | 
					    MarketplaceView,
 | 
				
			||||||
 | 
					    SliceCreated,
 | 
				
			||||||
 | 
					    SliceAllocated,
 | 
				
			||||||
 | 
					    SliceReleased,
 | 
				
			||||||
 | 
					    SliceRentalStarted,
 | 
				
			||||||
 | 
					    SliceRentalStopped,
 | 
				
			||||||
 | 
					    SliceRentalRestarted,
 | 
				
			||||||
 | 
					    SliceRentalCancelled,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum ActivityImportance {
 | 
				
			||||||
 | 
					    Low,
 | 
				
			||||||
 | 
					    Medium,
 | 
				
			||||||
 | 
					    High,
 | 
				
			||||||
 | 
					    Critical,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										361
									
								
								heromodels/src/models/tfmarketplace/app.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								heromodels/src/models/tfmarketplace/app.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,361 @@
 | 
				
			|||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Unified App struct that can represent published apps, deployments, and deployment stats
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct App {
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Core app information
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub category: Option<String>,
 | 
				
			||||||
 | 
					    pub version: Option<String>,
 | 
				
			||||||
 | 
					    pub status: String,
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Deployment information
 | 
				
			||||||
 | 
					    pub customer_name: Option<String>,
 | 
				
			||||||
 | 
					    pub customer_email: Option<String>,
 | 
				
			||||||
 | 
					    pub deployed_date: Option<String>,
 | 
				
			||||||
 | 
					    pub health_score: Option<f32>,
 | 
				
			||||||
 | 
					    pub region: Option<String>,
 | 
				
			||||||
 | 
					    pub instances: Option<i32>,
 | 
				
			||||||
 | 
					    pub resource_usage: Option<ResourceUtilization>,
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Business metrics
 | 
				
			||||||
 | 
					    pub deployments: Option<i32>,
 | 
				
			||||||
 | 
					    pub rating: Option<f32>,
 | 
				
			||||||
 | 
					    pub monthly_revenue_usd: Option<i32>,
 | 
				
			||||||
 | 
					    pub cost_per_month: Option<Decimal>,
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Metadata
 | 
				
			||||||
 | 
					    pub last_updated: Option<String>,
 | 
				
			||||||
 | 
					    pub auto_healing: Option<bool>,
 | 
				
			||||||
 | 
					    pub provider: Option<String>,
 | 
				
			||||||
 | 
					    pub deployed_at: Option<DateTime<Utc>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl App {
 | 
				
			||||||
 | 
					    /// Convenience method to get the app ID
 | 
				
			||||||
 | 
					    pub fn id(&self) -> &u32 {
 | 
				
			||||||
 | 
					        &self.base_data.id
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get category with default
 | 
				
			||||||
 | 
					    pub fn category_or_default(&self) -> String {
 | 
				
			||||||
 | 
					        self.category.clone().unwrap_or_else(|| "Application".to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get version with default
 | 
				
			||||||
 | 
					    pub fn version_or_default(&self) -> String {
 | 
				
			||||||
 | 
					        self.version.clone().unwrap_or_else(|| "1.0.0".to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get deployments count with default
 | 
				
			||||||
 | 
					    pub fn deployments_or_default(&self) -> i32 {
 | 
				
			||||||
 | 
					        self.deployments.unwrap_or(0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get rating with default
 | 
				
			||||||
 | 
					    pub fn rating_or_default(&self) -> f32 {
 | 
				
			||||||
 | 
					        self.rating.unwrap_or(4.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get monthly revenue with default
 | 
				
			||||||
 | 
					    pub fn monthly_revenue_usd_or_default(&self) -> i32 {
 | 
				
			||||||
 | 
					        self.monthly_revenue_usd.unwrap_or(0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get last updated with default
 | 
				
			||||||
 | 
					    pub fn last_updated_or_default(&self) -> String {
 | 
				
			||||||
 | 
					        self.last_updated.clone().unwrap_or_else(|| "Unknown".to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Get auto healing with default
 | 
				
			||||||
 | 
					    pub fn auto_healing_or_default(&self) -> bool {
 | 
				
			||||||
 | 
					        self.auto_healing.unwrap_or(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Deployment {
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub app_id: String,
 | 
				
			||||||
 | 
					    pub instance_id: String,
 | 
				
			||||||
 | 
					    pub status: String,
 | 
				
			||||||
 | 
					    pub region: String,
 | 
				
			||||||
 | 
					    pub health_score: Option<f32>,
 | 
				
			||||||
 | 
					    pub resource_usage: Option<ResourceUtilization>,
 | 
				
			||||||
 | 
					    pub deployed_at: Option<DateTime<Utc>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource utilization information
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, Default)]
 | 
				
			||||||
 | 
					pub struct ResourceUtilization {
 | 
				
			||||||
 | 
					    pub cpu: i32,
 | 
				
			||||||
 | 
					    pub memory: i32,
 | 
				
			||||||
 | 
					    pub storage: i32,
 | 
				
			||||||
 | 
					    pub network: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Deployment status enumeration
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, Default)]
 | 
				
			||||||
 | 
					pub enum DeploymentStatus {
 | 
				
			||||||
 | 
					    #[default]
 | 
				
			||||||
 | 
					    Running,
 | 
				
			||||||
 | 
					    Stopped,
 | 
				
			||||||
 | 
					    Failed,
 | 
				
			||||||
 | 
					    Pending,
 | 
				
			||||||
 | 
					    Maintenance,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Unified App builder
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct AppBuilder {
 | 
				
			||||||
 | 
					    base_data: BaseModelData,
 | 
				
			||||||
 | 
					    name: Option<String>,
 | 
				
			||||||
 | 
					    category: Option<String>,
 | 
				
			||||||
 | 
					    version: Option<String>,
 | 
				
			||||||
 | 
					    status: Option<String>,
 | 
				
			||||||
 | 
					    customer_name: Option<String>,
 | 
				
			||||||
 | 
					    customer_email: Option<String>,
 | 
				
			||||||
 | 
					    deployed_date: Option<String>,
 | 
				
			||||||
 | 
					    health_score: Option<f32>,
 | 
				
			||||||
 | 
					    region: Option<String>,
 | 
				
			||||||
 | 
					    instances: Option<i32>,
 | 
				
			||||||
 | 
					    resource_usage: Option<ResourceUtilization>,
 | 
				
			||||||
 | 
					    deployments: Option<i32>,
 | 
				
			||||||
 | 
					    rating: Option<f32>,
 | 
				
			||||||
 | 
					    monthly_revenue_usd: Option<i32>,
 | 
				
			||||||
 | 
					    cost_per_month: Option<Decimal>,
 | 
				
			||||||
 | 
					    last_updated: Option<String>,
 | 
				
			||||||
 | 
					    auto_healing: Option<bool>,
 | 
				
			||||||
 | 
					    provider: Option<String>,
 | 
				
			||||||
 | 
					    deployed_at: Option<DateTime<Utc>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AppBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn name(mut self, name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.name = Some(name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn category(mut self, category: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.category = Some(category.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn version(mut self, version: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.version = Some(version.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn status(mut self, status: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.status = Some(status.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn customer_name(mut self, name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.customer_name = Some(name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn customer_email(mut self, email: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.customer_email = Some(email.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deployed_date(mut self, date: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.deployed_date = Some(date.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn health_score(mut self, score: f32) -> Self {
 | 
				
			||||||
 | 
					        self.health_score = Some(score);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn region(mut self, region: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.region = Some(region.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn instances(mut self, instances: i32) -> Self {
 | 
				
			||||||
 | 
					        self.instances = Some(instances);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn resource_usage(mut self, usage: ResourceUtilization) -> Self {
 | 
				
			||||||
 | 
					        self.resource_usage = Some(usage);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deployments(mut self, deployments: i32) -> Self {
 | 
				
			||||||
 | 
					        self.deployments = Some(deployments);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn rating(mut self, rating: f32) -> Self {
 | 
				
			||||||
 | 
					        self.rating = Some(rating);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn monthly_revenue_usd(mut self, revenue: i32) -> Self {
 | 
				
			||||||
 | 
					        self.monthly_revenue_usd = Some(revenue);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn cost_per_month(mut self, cost: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.cost_per_month = Some(cost);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn last_updated(mut self, updated: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.last_updated = Some(updated.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn auto_healing(mut self, enabled: bool) -> Self {
 | 
				
			||||||
 | 
					        self.auto_healing = Some(enabled);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn provider(mut self, provider: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.provider = Some(provider.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deployed_at(mut self, date: DateTime<Utc>) -> Self {
 | 
				
			||||||
 | 
					        self.deployed_at = Some(date);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<App, String> {
 | 
				
			||||||
 | 
					        Ok(App {
 | 
				
			||||||
 | 
					            base_data: self.base_data,
 | 
				
			||||||
 | 
					            name: self.name.ok_or("name is required")?,
 | 
				
			||||||
 | 
					            category: self.category,
 | 
				
			||||||
 | 
					            version: self.version,
 | 
				
			||||||
 | 
					            status: self.status.unwrap_or_else(|| "Active".to_string()),
 | 
				
			||||||
 | 
					            customer_name: self.customer_name,
 | 
				
			||||||
 | 
					            customer_email: self.customer_email,
 | 
				
			||||||
 | 
					            deployed_date: self.deployed_date,
 | 
				
			||||||
 | 
					            health_score: self.health_score,
 | 
				
			||||||
 | 
					            region: self.region,
 | 
				
			||||||
 | 
					            instances: self.instances,
 | 
				
			||||||
 | 
					            resource_usage: self.resource_usage,
 | 
				
			||||||
 | 
					            deployments: self.deployments,
 | 
				
			||||||
 | 
					            rating: self.rating,
 | 
				
			||||||
 | 
					            monthly_revenue_usd: self.monthly_revenue_usd,
 | 
				
			||||||
 | 
					            cost_per_month: self.cost_per_month,
 | 
				
			||||||
 | 
					            last_updated: self.last_updated,
 | 
				
			||||||
 | 
					            auto_healing: self.auto_healing,
 | 
				
			||||||
 | 
					            provider: self.provider,
 | 
				
			||||||
 | 
					            deployed_at: self.deployed_at,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl App {
 | 
				
			||||||
 | 
					    pub fn builder() -> AppBuilder {
 | 
				
			||||||
 | 
					        AppBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Template methods for common app types
 | 
				
			||||||
 | 
					    pub fn analytics_template(name: &str) -> Self {
 | 
				
			||||||
 | 
					        Self::builder()
 | 
				
			||||||
 | 
					            .name(name)
 | 
				
			||||||
 | 
					            .category("Analytics")
 | 
				
			||||||
 | 
					            .version("1.0.0")
 | 
				
			||||||
 | 
					            .status("Active")
 | 
				
			||||||
 | 
					            .rating(4.5)
 | 
				
			||||||
 | 
					            .auto_healing(true)
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn database_template(name: &str) -> Self {
 | 
				
			||||||
 | 
					        Self::builder()
 | 
				
			||||||
 | 
					            .name(name)
 | 
				
			||||||
 | 
					            .category("Database")
 | 
				
			||||||
 | 
					            .version("1.0.0")
 | 
				
			||||||
 | 
					            .status("Active")
 | 
				
			||||||
 | 
					            .rating(4.2)
 | 
				
			||||||
 | 
					            .auto_healing(false) // Databases need manual intervention
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn web_template(name: &str) -> Self {
 | 
				
			||||||
 | 
					        Self::builder()
 | 
				
			||||||
 | 
					            .name(name)
 | 
				
			||||||
 | 
					            .category("Web")
 | 
				
			||||||
 | 
					            .version("1.0.0")
 | 
				
			||||||
 | 
					            .status("Active")
 | 
				
			||||||
 | 
					            .rating(4.0)
 | 
				
			||||||
 | 
					            .auto_healing(true)
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Fluent methods for chaining
 | 
				
			||||||
 | 
					    pub fn with_stats(mut self, deployments: i32, rating: f32, monthly_revenue_usd: i32) -> Self {
 | 
				
			||||||
 | 
					        self.deployments = Some(deployments);
 | 
				
			||||||
 | 
					        self.rating = Some(rating);
 | 
				
			||||||
 | 
					        self.monthly_revenue_usd = Some(monthly_revenue_usd);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_auto_healing(mut self, enabled: bool) -> Self {
 | 
				
			||||||
 | 
					        self.auto_healing = Some(enabled);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_version(mut self, version: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.version = Some(version.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_last_updated(mut self, updated: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.last_updated = Some(updated.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_deployment_info(mut self, customer_name: &str, customer_email: &str, region: &str) -> Self {
 | 
				
			||||||
 | 
					        self.customer_name = Some(customer_name.to_string());
 | 
				
			||||||
 | 
					        self.customer_email = Some(customer_email.to_string());
 | 
				
			||||||
 | 
					        self.region = Some(region.to_string());
 | 
				
			||||||
 | 
					        self.deployed_at = Some(Utc::now());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_resource_usage(mut self, cpu: i32, memory: i32, storage: i32, network: i32) -> Self {
 | 
				
			||||||
 | 
					        self.resource_usage = Some(ResourceUtilization {
 | 
				
			||||||
 | 
					            cpu,
 | 
				
			||||||
 | 
					            memory,
 | 
				
			||||||
 | 
					            storage,
 | 
				
			||||||
 | 
					            network,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type aliases for backward compatibility
 | 
				
			||||||
 | 
					pub type PublishedApp = App;
 | 
				
			||||||
 | 
					pub type AppDeployment = App;
 | 
				
			||||||
 | 
					pub type DeploymentStat = App;
 | 
				
			||||||
 | 
					pub type UserDeployment = App;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type PublishedAppBuilder = AppBuilder;
 | 
				
			||||||
 | 
					pub type AppDeploymentBuilder = AppBuilder;
 | 
				
			||||||
 | 
					pub type DeploymentStatBuilder = AppBuilder;
 | 
				
			||||||
 | 
					pub type UserDeploymentBuilder = AppBuilder;
 | 
				
			||||||
							
								
								
									
										351
									
								
								heromodels/src/models/tfmarketplace/builders.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								heromodels/src/models/tfmarketplace/builders.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
				
			|||||||
 | 
					//! Builder patterns for all marketplace models
 | 
				
			||||||
 | 
					//! This module provides a centralized, maintainable way to construct complex structs
 | 
				
			||||||
 | 
					//! with sensible defaults and validation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use rust_decimal_macros::dec;
 | 
				
			||||||
 | 
					use serde_json::Value;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::{
 | 
				
			||||||
 | 
					    user::{PublishedApp, DeploymentStat, ResourceUtilization, User, UserRole, MockUserData, ServiceBooking},
 | 
				
			||||||
 | 
					    product::{Product, ProductAttribute, ProductAvailability, ProductMetadata},
 | 
				
			||||||
 | 
					    order::{Order, OrderItem, OrderStatus, PaymentDetails, Address, PurchaseType},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use crate::services::user_persistence::AppDeployment;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					// USER MODEL BUILDERS
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct MockDataBuilder {
 | 
				
			||||||
 | 
					    user_type: Option<String>,
 | 
				
			||||||
 | 
					    include_farmer_data: Option<bool>,
 | 
				
			||||||
 | 
					    include_service_data: Option<bool>,
 | 
				
			||||||
 | 
					    include_app_data: Option<bool>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl MockDataBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user_type(mut self, user_type: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.user_type = Some(user_type.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn include_farmer_data(mut self, include: bool) -> Self {
 | 
				
			||||||
 | 
					        self.include_farmer_data = Some(include);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn include_service_data(mut self, include: bool) -> Self {
 | 
				
			||||||
 | 
					        self.include_service_data = Some(include);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn include_app_data(mut self, include: bool) -> Self {
 | 
				
			||||||
 | 
					        self.include_app_data = Some(include);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> crate::models::user::MockUserData {
 | 
				
			||||||
 | 
					        // This would create appropriate mock data based on configuration
 | 
				
			||||||
 | 
					        // For now, return a default instance
 | 
				
			||||||
 | 
					        crate::models::user::MockUserData::new_user()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					// FARMER DATA BUILDER
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct FarmerDataBuilder {
 | 
				
			||||||
 | 
					    total_nodes: Option<i32>,
 | 
				
			||||||
 | 
					    online_nodes: Option<i32>,
 | 
				
			||||||
 | 
					    total_capacity: Option<crate::models::user::NodeCapacity>,
 | 
				
			||||||
 | 
					    used_capacity: Option<crate::models::user::NodeCapacity>,
 | 
				
			||||||
 | 
					    monthly_earnings: Option<i32>,
 | 
				
			||||||
 | 
					    total_earnings: Option<i32>,
 | 
				
			||||||
 | 
					    uptime_percentage: Option<f32>,
 | 
				
			||||||
 | 
					    nodes: Option<Vec<crate::models::user::FarmNode>>,
 | 
				
			||||||
 | 
					    earnings_history: Option<Vec<crate::models::user::EarningsRecord>>,
 | 
				
			||||||
 | 
					    active_slices: Option<i32>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FarmerDataBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn total_nodes(mut self, total_nodes: i32) -> Self {
 | 
				
			||||||
 | 
					        self.total_nodes = Some(total_nodes);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn online_nodes(mut self, online_nodes: i32) -> Self {
 | 
				
			||||||
 | 
					        self.online_nodes = Some(online_nodes);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn total_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
 | 
				
			||||||
 | 
					        self.total_capacity = Some(capacity);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn used_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
 | 
				
			||||||
 | 
					        self.used_capacity = Some(capacity);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn monthly_earnings_usd(mut self, earnings: i32) -> Self {
 | 
				
			||||||
 | 
					        self.monthly_earnings = Some(earnings);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn total_earnings_usd(mut self, earnings: i32) -> Self {
 | 
				
			||||||
 | 
					        self.total_earnings = Some(earnings);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn uptime_percentage(mut self, uptime: f32) -> Self {
 | 
				
			||||||
 | 
					        self.uptime_percentage = Some(uptime);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn nodes(mut self, nodes: Vec<crate::models::user::FarmNode>) -> Self {
 | 
				
			||||||
 | 
					        self.nodes = Some(nodes);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn earnings_history(mut self, history: Vec<crate::models::user::EarningsRecord>) -> Self {
 | 
				
			||||||
 | 
					        self.earnings_history = Some(history);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn earnings(mut self, earnings: Vec<crate::models::user::EarningsRecord>) -> Self {
 | 
				
			||||||
 | 
					        self.earnings_history = Some(earnings);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn active_slices(mut self, active_slices: i32) -> Self {
 | 
				
			||||||
 | 
					        self.active_slices = Some(active_slices);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn calculate_totals(mut self) -> Self {
 | 
				
			||||||
 | 
					        // Calculate totals from existing data
 | 
				
			||||||
 | 
					        if let Some(ref nodes) = self.nodes {
 | 
				
			||||||
 | 
					            self.total_nodes = Some(nodes.len() as i32);
 | 
				
			||||||
 | 
					            self.online_nodes = Some(nodes.iter().filter(|n| matches!(n.status, crate::models::user::NodeStatus::Online)).count() as i32);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Calculate total and used capacity from all nodes
 | 
				
			||||||
 | 
					            let mut total_capacity = crate::models::user::NodeCapacity {
 | 
				
			||||||
 | 
					                cpu_cores: 0,
 | 
				
			||||||
 | 
					                memory_gb: 0,
 | 
				
			||||||
 | 
					                storage_gb: 0,
 | 
				
			||||||
 | 
					                bandwidth_mbps: 0,
 | 
				
			||||||
 | 
					                ssd_storage_gb: 0,
 | 
				
			||||||
 | 
					                hdd_storage_gb: 0,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            let mut used_capacity = crate::models::user::NodeCapacity {
 | 
				
			||||||
 | 
					                cpu_cores: 0,
 | 
				
			||||||
 | 
					                memory_gb: 0,
 | 
				
			||||||
 | 
					                storage_gb: 0,
 | 
				
			||||||
 | 
					                bandwidth_mbps: 0,
 | 
				
			||||||
 | 
					                ssd_storage_gb: 0,
 | 
				
			||||||
 | 
					                hdd_storage_gb: 0,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for node in nodes {
 | 
				
			||||||
 | 
					                total_capacity.cpu_cores += node.capacity.cpu_cores;
 | 
				
			||||||
 | 
					                total_capacity.memory_gb += node.capacity.memory_gb;
 | 
				
			||||||
 | 
					                total_capacity.storage_gb += node.capacity.storage_gb;
 | 
				
			||||||
 | 
					                total_capacity.bandwidth_mbps += node.capacity.bandwidth_mbps;
 | 
				
			||||||
 | 
					                total_capacity.ssd_storage_gb += node.capacity.ssd_storage_gb;
 | 
				
			||||||
 | 
					                total_capacity.hdd_storage_gb += node.capacity.hdd_storage_gb;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                used_capacity.cpu_cores += node.used_capacity.cpu_cores;
 | 
				
			||||||
 | 
					                used_capacity.memory_gb += node.used_capacity.memory_gb;
 | 
				
			||||||
 | 
					                used_capacity.storage_gb += node.used_capacity.storage_gb;
 | 
				
			||||||
 | 
					                used_capacity.bandwidth_mbps += node.used_capacity.bandwidth_mbps;
 | 
				
			||||||
 | 
					                used_capacity.ssd_storage_gb += node.used_capacity.ssd_storage_gb;
 | 
				
			||||||
 | 
					                used_capacity.hdd_storage_gb += node.used_capacity.hdd_storage_gb;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.total_capacity = Some(total_capacity);
 | 
				
			||||||
 | 
					            self.used_capacity = Some(used_capacity);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Calculate uptime percentage
 | 
				
			||||||
 | 
					            if !nodes.is_empty() {
 | 
				
			||||||
 | 
					                let avg_uptime = nodes.iter().map(|n| n.uptime_percentage).sum::<f32>() / nodes.len() as f32;
 | 
				
			||||||
 | 
					                self.uptime_percentage = Some(avg_uptime);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let Some(ref earnings) = self.earnings_history {
 | 
				
			||||||
 | 
					            let total: i32 = earnings.iter().map(|e| e.amount.to_string().parse::<i32>().unwrap_or(0)).sum();
 | 
				
			||||||
 | 
					            self.total_earnings = Some(total);
 | 
				
			||||||
 | 
					            self.monthly_earnings = Some(total); // Set monthly earnings as well
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::models::user::FarmerData, String> {
 | 
				
			||||||
 | 
					        Ok(crate::models::user::FarmerData {
 | 
				
			||||||
 | 
					            total_nodes: self.total_nodes.unwrap_or(0),
 | 
				
			||||||
 | 
					            online_nodes: self.online_nodes.unwrap_or(0),
 | 
				
			||||||
 | 
					            total_capacity: self.total_capacity.unwrap_or(crate::models::user::NodeCapacity {
 | 
				
			||||||
 | 
					                cpu_cores: 0,
 | 
				
			||||||
 | 
					                memory_gb: 0,
 | 
				
			||||||
 | 
					                storage_gb: 0,
 | 
				
			||||||
 | 
					                bandwidth_mbps: 0,
 | 
				
			||||||
 | 
					                ssd_storage_gb: 0,
 | 
				
			||||||
 | 
					                hdd_storage_gb: 0,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            used_capacity: self.used_capacity.unwrap_or(crate::models::user::NodeCapacity {
 | 
				
			||||||
 | 
					                cpu_cores: 0,
 | 
				
			||||||
 | 
					                memory_gb: 0,
 | 
				
			||||||
 | 
					                storage_gb: 0,
 | 
				
			||||||
 | 
					                bandwidth_mbps: 0,
 | 
				
			||||||
 | 
					                ssd_storage_gb: 0,
 | 
				
			||||||
 | 
					                hdd_storage_gb: 0,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            monthly_earnings_usd: self.monthly_earnings.unwrap_or(0),
 | 
				
			||||||
 | 
					            total_earnings_usd: self.total_earnings.unwrap_or(0),
 | 
				
			||||||
 | 
					            uptime_percentage: self.uptime_percentage.unwrap_or(0.0),
 | 
				
			||||||
 | 
					            nodes: self.nodes.unwrap_or_default(),
 | 
				
			||||||
 | 
					            earnings_history: self.earnings_history.unwrap_or_default(),
 | 
				
			||||||
 | 
					            slice_templates: Vec::default(), // Will be populated separately
 | 
				
			||||||
 | 
					            active_slices: self.active_slices.unwrap_or(0),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					// SERVICE BOOKING BUILDER
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct SpendingRecordBuilder {
 | 
				
			||||||
 | 
					    date: Option<String>,
 | 
				
			||||||
 | 
					    amount: Option<i32>,
 | 
				
			||||||
 | 
					    service_name: Option<String>,
 | 
				
			||||||
 | 
					    provider_name: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SpendingRecordBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn date(mut self, date: &str) -> Self {
 | 
				
			||||||
 | 
					        self.date = Some(date.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn amount(mut self, amount: i32) -> Self {
 | 
				
			||||||
 | 
					        self.amount = Some(amount);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn service_name(mut self, name: &str) -> Self {
 | 
				
			||||||
 | 
					        self.service_name = Some(name.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn provider_name(mut self, name: &str) -> Self {
 | 
				
			||||||
 | 
					        self.provider_name = Some(name.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::models::user::SpendingRecord, String> {
 | 
				
			||||||
 | 
					        Ok(crate::models::user::SpendingRecord {
 | 
				
			||||||
 | 
					            date: self.date.ok_or("Date is required")?,
 | 
				
			||||||
 | 
					            amount: self.amount.unwrap_or(0),
 | 
				
			||||||
 | 
					            service_name: self.service_name.ok_or("Service name is required")?,
 | 
				
			||||||
 | 
					            provider_name: self.provider_name.ok_or("Provider name is required")?,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl crate::models::user::SpendingRecord {
 | 
				
			||||||
 | 
					    pub fn builder() -> SpendingRecordBuilder {
 | 
				
			||||||
 | 
					        SpendingRecordBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					// AUTO TOP-UP BUILDERS
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct AutoTopUpSettingsBuilder {
 | 
				
			||||||
 | 
					    enabled: Option<bool>,
 | 
				
			||||||
 | 
					    threshold_amount: Option<Decimal>,
 | 
				
			||||||
 | 
					    topup_amount: Option<Decimal>,
 | 
				
			||||||
 | 
					    payment_method_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    daily_limit: Option<Decimal>,
 | 
				
			||||||
 | 
					    monthly_limit: Option<Decimal>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AutoTopUpSettingsBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn enabled(mut self, enabled: bool) -> Self {
 | 
				
			||||||
 | 
					        self.enabled = Some(enabled);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn threshold_amount(mut self, amount: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.threshold_amount = Some(amount);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn topup_amount(mut self, amount: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.topup_amount = Some(amount);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn payment_method_id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.payment_method_id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn daily_limit(mut self, limit: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.daily_limit = Some(limit);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn monthly_limit(mut self, limit: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.monthly_limit = Some(limit);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::services::user_persistence::AutoTopUpSettings, String> {
 | 
				
			||||||
 | 
					        Ok(crate::services::user_persistence::AutoTopUpSettings {
 | 
				
			||||||
 | 
					            enabled: self.enabled.unwrap_or(false),
 | 
				
			||||||
 | 
					            threshold_amount_usd: self.threshold_amount.unwrap_or(dec!(10.0)),
 | 
				
			||||||
 | 
					            topup_amount_usd: self.topup_amount.unwrap_or(dec!(25.0)),
 | 
				
			||||||
 | 
					            payment_method_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.payment_method_id.ok_or("payment_method_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            daily_limit_usd: self.daily_limit,
 | 
				
			||||||
 | 
					            monthly_limit_usd: self.monthly_limit,
 | 
				
			||||||
 | 
					            // created_at: chrono::Utc::now() - moved to base_data,
 | 
				
			||||||
 | 
					            // updated_at: chrono::Utc::now() - moved to base_data,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										105
									
								
								heromodels/src/models/tfmarketplace/cart.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								heromodels/src/models/tfmarketplace/cart.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Shopping Cart Models
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct CartItem {
 | 
				
			||||||
 | 
					    pub product_id: u32,
 | 
				
			||||||
 | 
					    pub quantity: u32,
 | 
				
			||||||
 | 
					    pub selected_specifications: HashMap<String, serde_json::Value>,
 | 
				
			||||||
 | 
					    pub added_at: DateTime<Utc>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Cart {
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub items: Vec<CartItem>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Cart {
 | 
				
			||||||
 | 
					    pub fn new() -> Self{
 | 
				
			||||||
 | 
					        let now = Utc::now();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            items: Vec::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_item(&mut self, item: CartItem) {
 | 
				
			||||||
 | 
					        // Check if item already exists and update quantity
 | 
				
			||||||
 | 
					        if let Some(existing_item) = self.items.iter_mut()
 | 
				
			||||||
 | 
					            .find(|i| i.product_id == item.product_id && i.selected_specifications == item.selected_specifications) {
 | 
				
			||||||
 | 
					            existing_item.quantity += item.quantity;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            self.items.push(item);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn remove_item(&mut self, product_id: &str, name: &str) -> bool{
 | 
				
			||||||
 | 
					        let initial_len = self.items.len();
 | 
				
			||||||
 | 
					        self.items.retain(|item| item.product_id != product_id);
 | 
				
			||||||
 | 
					        if self.items.len() != initial_len {
 | 
				
			||||||
 | 
					            self.base_data.updated_at = Utc::now();
 | 
				
			||||||
 | 
					            true
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update_item_quantity(&mut self, product_id: &str, name: &str) -> bool {
 | 
				
			||||||
 | 
					        if let Some(item) = self.items.iter_mut().find(|i| i.product_id == product_id) {
 | 
				
			||||||
 | 
					            if quantity == 0 {
 | 
				
			||||||
 | 
					                return self.remove_item(product_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            item.quantity = quantity;
 | 
				
			||||||
 | 
					            item.updated_at = Utc::now();
 | 
				
			||||||
 | 
					            self.base_data.updated_at = Utc::now();
 | 
				
			||||||
 | 
					            true
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
 | 
					        self.items.clear();
 | 
				
			||||||
 | 
					        self.base_data.updated_at = Utc::now();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn get_total_items(&self) -> u32 {
 | 
				
			||||||
 | 
					        self.items.iter().map(|item| item.quantity).sum()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
 | 
					        self.items.is_empty()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl CartItem {
 | 
				
			||||||
 | 
					    pub fn new(product_id: &str, name: &str) -> Self {
 | 
				
			||||||
 | 
					        let now = Utc::now();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            product_id,
 | 
				
			||||||
 | 
					            quantity,
 | 
				
			||||||
 | 
					            selected_specifications: HashMap::default(),
 | 
				
			||||||
 | 
					            added_at: now,
 | 
				
			||||||
 | 
					            // updated_at: now - moved to base_data,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn with_specifications(
 | 
				
			||||||
 | 
					        product_id: &str, name: &str) -> Self {
 | 
				
			||||||
 | 
					        let now = Utc::now();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            product_id,
 | 
				
			||||||
 | 
					            quantity,
 | 
				
			||||||
 | 
					            selected_specifications: specifications,
 | 
				
			||||||
 | 
					            added_at: now,
 | 
				
			||||||
 | 
					            // updated_at: now - moved to base_data,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								heromodels/src/models/tfmarketplace/currency.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								heromodels/src/models/tfmarketplace/currency.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use heromodels_derive::model;
 | 
				
			||||||
 | 
					use rhai::CustomType;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configurable currency support for any currency type
 | 
				
			||||||
 | 
					#[model]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
 | 
				
			||||||
 | 
					pub struct Currency {
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    #[index]
 | 
				
			||||||
 | 
					    pub code: String,           // USD, EUR, BTC, ETH, etc.
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub symbol: String,
 | 
				
			||||||
 | 
					    pub currency_type: CurrencyType,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum CurrencyType {
 | 
				
			||||||
 | 
					    Fiat,
 | 
				
			||||||
 | 
					    Cryptocurrency,
 | 
				
			||||||
 | 
					    Token,
 | 
				
			||||||
 | 
					    Points,                     // For loyalty/reward systems
 | 
				
			||||||
 | 
					    Custom(String),            // For marketplace-specific currencies
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Price {
 | 
				
			||||||
 | 
					    pub base_amount: Decimal,           // Amount in marketplace base currency
 | 
				
			||||||
 | 
					    pub base_currency: String,
 | 
				
			||||||
 | 
					    pub display_currency: String,
 | 
				
			||||||
 | 
					    pub display_amount: Decimal,
 | 
				
			||||||
 | 
					    pub formatted_display: String,
 | 
				
			||||||
 | 
					    pub conversion_rate: Decimal,
 | 
				
			||||||
 | 
					    pub conversion_timestamp: DateTime<Utc>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Currency {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        code: String,
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					        symbol: String,
 | 
				
			||||||
 | 
					        currency_type: CurrencyType,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            code,
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            symbol,
 | 
				
			||||||
 | 
					            currency_type,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Price {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        base_amount: Decimal,
 | 
				
			||||||
 | 
					        base_currency: String,
 | 
				
			||||||
 | 
					        display_currency: String,
 | 
				
			||||||
 | 
					        conversion_rate: Decimal,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let display_amount = base_amount * conversion_rate;
 | 
				
			||||||
 | 
					        // Use proper currency symbol formatting - this will be updated by the currency service
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_amount,
 | 
				
			||||||
 | 
					            base_currency: base_currency.clone(),
 | 
				
			||||||
 | 
					            display_currency: display_currency.clone(),
 | 
				
			||||||
 | 
					            display_amount,
 | 
				
			||||||
 | 
					            formatted_display: format!("{} {}", display_amount.round_dp(2), display_currency),
 | 
				
			||||||
 | 
					            conversion_rate,
 | 
				
			||||||
 | 
					            conversion_timestamp: Utc::now(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn format_with_symbol(&self, symbol: &str) -> String {
 | 
				
			||||||
 | 
					        format!("{} {}",
 | 
				
			||||||
 | 
					            self.display_amount.round_dp(2),
 | 
				
			||||||
 | 
					            symbol
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update_formatted_display(&mut self, formatted: String) {
 | 
				
			||||||
 | 
					        self.formatted_display = formatted;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								heromodels/src/models/tfmarketplace/farmer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								heromodels/src/models/tfmarketplace/farmer.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					/// Farmer-specific data
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct FarmerData {
 | 
				
			||||||
 | 
					    pub total_nodes: i32,
 | 
				
			||||||
 | 
					    pub online_nodes: i32,
 | 
				
			||||||
 | 
					    pub total_capacity: NodeCapacity,
 | 
				
			||||||
 | 
					    pub used_capacity: NodeCapacity,
 | 
				
			||||||
 | 
					    pub monthly_earnings_usd: i32,
 | 
				
			||||||
 | 
					    pub total_earnings_usd: i32,
 | 
				
			||||||
 | 
					    pub uptime_percentage: f32,
 | 
				
			||||||
 | 
					    pub nodes: Vec<FarmNode>,
 | 
				
			||||||
 | 
					    pub earnings_history: Vec<EarningsRecord>,
 | 
				
			||||||
 | 
					    pub slice_templates: Vec<crate::models::product::Product>,
 | 
				
			||||||
 | 
					    pub active_slices: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct FarmerSettings {
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub auto_accept_deployments: bool,
 | 
				
			||||||
 | 
					    #[serde(default = "default_maintenance_window")]
 | 
				
			||||||
 | 
					    pub maintenance_window: String,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub notification_preferences: NotificationSettings,
 | 
				
			||||||
 | 
					    pub minimum_deployment_duration: i32, // hours
 | 
				
			||||||
 | 
					    pub preferred_regions: Vec<String>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub default_slice_customizations: Option<std::collections::HashMap<String, serde_json::Value>>, // Placeholder for DefaultSliceFormat
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								heromodels/src/models/tfmarketplace/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								heromodels/src/models/tfmarketplace/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					// Export models - starting with basic models first
 | 
				
			||||||
 | 
					// pub mod user;
 | 
				
			||||||
 | 
					// pub mod product;
 | 
				
			||||||
 | 
					// pub mod currency;
 | 
				
			||||||
 | 
					// pub mod order;
 | 
				
			||||||
 | 
					// pub mod pool;
 | 
				
			||||||
 | 
					// pub mod builders; // Re-enabled with essential builders only
 | 
				
			||||||
 | 
					// pub mod cart;
 | 
				
			||||||
 | 
					// pub mod payment;
 | 
				
			||||||
 | 
					// pub mod service;
 | 
				
			||||||
 | 
					// pub mod slice;
 | 
				
			||||||
 | 
					// pub mod node;
 | 
				
			||||||
 | 
					pub mod app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Re-export commonly used types for easier access
 | 
				
			||||||
 | 
					pub use app::{App, PublishedApp, PublishedAppBuilder, ResourceUtilization, AppBuilder, DeploymentStatus};
 | 
				
			||||||
 | 
					// pub mod node; // Temporarily disabled - has many service dependencies
 | 
				
			||||||
							
								
								
									
										1660
									
								
								heromodels/src/models/tfmarketplace/node.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1660
									
								
								heromodels/src/models/tfmarketplace/node.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8
									
								
								heromodels/src/models/tfmarketplace/notes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								heromodels/src/models/tfmarketplace/notes.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					# Notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					all id's of base objects are u32
 | 
				
			||||||
 | 
					Cart is front end specific,
 | 
				
			||||||
 | 
					currency and exchange rates should be calculated by client
 | 
				
			||||||
 | 
					stuff such as decomal numbers related to presentation shouldnt be in base models
 | 
				
			||||||
 | 
					purchase doesnt need to now wether it is instant or cart
 | 
				
			||||||
 | 
					all base objects contain created_at and updated_at, so not needed to be added to every model
 | 
				
			||||||
							
								
								
									
										402
									
								
								heromodels/src/models/tfmarketplace/order.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								heromodels/src/models/tfmarketplace/order.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,402 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use heromodels_derive::model;
 | 
				
			||||||
 | 
					use rhai::CustomType;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[model]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
 | 
				
			||||||
 | 
					pub struct Order {
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    #[index]
 | 
				
			||||||
 | 
					    pub user_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub items: Vec<OrderItem>,
 | 
				
			||||||
 | 
					    pub subtotal_base: Decimal,     // In base currency
 | 
				
			||||||
 | 
					    pub total_base: Decimal,        // In base currency
 | 
				
			||||||
 | 
					    pub base_currency: String,
 | 
				
			||||||
 | 
					    pub currency_used: String,      // Currency user paid in
 | 
				
			||||||
 | 
					    pub currency_total: Decimal,    // Amount in user's currency
 | 
				
			||||||
 | 
					    pub conversion_rate: Decimal,   // Rate used for conversion
 | 
				
			||||||
 | 
					    pub status: OrderStatus,
 | 
				
			||||||
 | 
					    pub payment_method: String,
 | 
				
			||||||
 | 
					    pub payment_details: Option<PaymentDetails>,
 | 
				
			||||||
 | 
					    pub billing_address: Option<Address>,
 | 
				
			||||||
 | 
					    pub shipping_address: Option<Address>,
 | 
				
			||||||
 | 
					    pub notes: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub struct OrderItem {
 | 
				
			||||||
 | 
					    pub product_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub product_name: String,
 | 
				
			||||||
 | 
					    pub product_category: String,
 | 
				
			||||||
 | 
					    pub quantity: u32,
 | 
				
			||||||
 | 
					    pub unit_price_base: Decimal,   // In base currency
 | 
				
			||||||
 | 
					    pub total_price_base: Decimal,  // In base currency
 | 
				
			||||||
 | 
					    pub specifications: HashMap<String, serde_json::Value>,
 | 
				
			||||||
 | 
					    pub provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub provider_name: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum OrderStatus {
 | 
				
			||||||
 | 
					    Pending,
 | 
				
			||||||
 | 
					    Confirmed,
 | 
				
			||||||
 | 
					    Processing,
 | 
				
			||||||
 | 
					    Deployed,
 | 
				
			||||||
 | 
					    Completed,
 | 
				
			||||||
 | 
					    Cancelled,
 | 
				
			||||||
 | 
					    Refunded,
 | 
				
			||||||
 | 
					    Failed,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Order summary for display purposes
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct OrderSummary {
 | 
				
			||||||
 | 
					    pub subtotal: Decimal,
 | 
				
			||||||
 | 
					    pub tax: Decimal,
 | 
				
			||||||
 | 
					    pub shipping: Decimal,
 | 
				
			||||||
 | 
					    pub discount: Decimal,
 | 
				
			||||||
 | 
					    pub total: Decimal,
 | 
				
			||||||
 | 
					    pub currency: String,
 | 
				
			||||||
 | 
					    pub item_count: u32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Order {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        user_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        base_currency: String,
 | 
				
			||||||
 | 
					        currency_used: String,
 | 
				
			||||||
 | 
					        conversion_rate: Decimal,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            user_id,
 | 
				
			||||||
 | 
					            items: Vec::default(),
 | 
				
			||||||
 | 
					            subtotal_base: Decimal::from(0),
 | 
				
			||||||
 | 
					            total_base: Decimal::from(0),
 | 
				
			||||||
 | 
					            base_currency,
 | 
				
			||||||
 | 
					            currency_used,
 | 
				
			||||||
 | 
					            currency_total: Decimal::from(0),
 | 
				
			||||||
 | 
					            conversion_rate,
 | 
				
			||||||
 | 
					            status: OrderStatus::Pending,
 | 
				
			||||||
 | 
					            payment_method: String::new(),
 | 
				
			||||||
 | 
					            payment_details: None,
 | 
				
			||||||
 | 
					            billing_address: None,
 | 
				
			||||||
 | 
					            shipping_address: None,
 | 
				
			||||||
 | 
					            notes: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_item(&mut self, item: OrderItem) {
 | 
				
			||||||
 | 
					        self.items.push(item);
 | 
				
			||||||
 | 
					        self.calculate_totals();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn calculate_totals(&mut self) {
 | 
				
			||||||
 | 
					        self.subtotal_base = self.items.iter()
 | 
				
			||||||
 | 
					            .map(|item| item.total_price_base)
 | 
				
			||||||
 | 
					            .sum();
 | 
				
			||||||
 | 
					        self.total_base = self.subtotal_base; // Add taxes, fees, etc. here
 | 
				
			||||||
 | 
					        self.currency_total = self.total_base * self.conversion_rate;
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update_status(&mut self, status: OrderStatus) {
 | 
				
			||||||
 | 
					        self.status = status;
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_payment_details(&mut self, payment_details: PaymentDetails) {
 | 
				
			||||||
 | 
					        self.payment_details = Some(payment_details);
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn get_item_count(&self) -> u32 {
 | 
				
			||||||
 | 
					        self.items.iter().map(|item| item.quantity).sum()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderItem {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        product_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        product_name: String,
 | 
				
			||||||
 | 
					        product_category: String,
 | 
				
			||||||
 | 
					        quantity: u32,
 | 
				
			||||||
 | 
					        unit_price_base: Decimal,
 | 
				
			||||||
 | 
					        provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        provider_name: String,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            product_id,
 | 
				
			||||||
 | 
					            product_name,
 | 
				
			||||||
 | 
					            product_category,
 | 
				
			||||||
 | 
					            quantity,
 | 
				
			||||||
 | 
					            unit_price_base,
 | 
				
			||||||
 | 
					            total_price_base: unit_price_base * Decimal::from(quantity),
 | 
				
			||||||
 | 
					            specifications: HashMap::default(),
 | 
				
			||||||
 | 
					            provider_id,
 | 
				
			||||||
 | 
					            provider_name,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_specification(&mut self, key: String, value: serde_json::Value) {
 | 
				
			||||||
 | 
					        self.specifications.insert(key, value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update_quantity(&mut self, quantity: u32) {
 | 
				
			||||||
 | 
					        self.quantity = quantity;
 | 
				
			||||||
 | 
					        self.total_price_base = self.unit_price_base * Decimal::from(quantity);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct OrderBuilder {
 | 
				
			||||||
 | 
					    base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    user_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    items: Vec<OrderItem>,
 | 
				
			||||||
 | 
					    subtotal_base: Option<Decimal>,
 | 
				
			||||||
 | 
					    total_base: Option<Decimal>,
 | 
				
			||||||
 | 
					    base_currency: Option<String>,
 | 
				
			||||||
 | 
					    currency_used: Option<String>,
 | 
				
			||||||
 | 
					    currency_total: Option<Decimal>,
 | 
				
			||||||
 | 
					    conversion_rate: Option<Decimal>,
 | 
				
			||||||
 | 
					    status: Option<OrderStatus>,
 | 
				
			||||||
 | 
					    payment_method: Option<String>,
 | 
				
			||||||
 | 
					    payment_details: Option<PaymentDetails>,
 | 
				
			||||||
 | 
					    billing_address: Option<Address>,
 | 
				
			||||||
 | 
					    shipping_address: Option<Address>,
 | 
				
			||||||
 | 
					    notes: Option<String>,
 | 
				
			||||||
 | 
					    purchase_type: Option<PurchaseType>,
 | 
				
			||||||
 | 
					    // created_at: Option<DateTime<Utc>> - moved to base_data,
 | 
				
			||||||
 | 
					    // updated_at: Option<DateTime<Utc>> - moved to base_data,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.base_data.id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user_id(mut self, user_id: &str, name: &str) -> Self{
 | 
				
			||||||
 | 
					        self.user_id = Some(user_id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_item(mut self, item: OrderItem) -> Self {
 | 
				
			||||||
 | 
					        self.items.push(item);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn items(mut self, items: Vec<OrderItem>) -> Self {
 | 
				
			||||||
 | 
					        self.items = items;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn subtotal_base(mut self, subtotal: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.subtotal_base = Some(subtotal);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn total_base(mut self, total: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.total_base = Some(total);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.base_currency = Some(currency.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn currency_used(mut self, currency: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.currency_used = Some(currency.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn currency_total(mut self, total: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.currency_total = Some(total);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn conversion_rate(mut self, rate: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.conversion_rate = Some(rate);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn status(mut self, status: OrderStatus) -> Self {
 | 
				
			||||||
 | 
					        self.status = Some(status);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn payment_method(mut self, method: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.payment_method = Some(method.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn payment_details(mut self, details: PaymentDetails) -> Self {
 | 
				
			||||||
 | 
					        self.payment_details = Some(details);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn billing_address(mut self, address: Address) -> Self {
 | 
				
			||||||
 | 
					        self.billing_address = Some(address);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn shipping_address(mut self, address: Address) -> Self {
 | 
				
			||||||
 | 
					        self.shipping_address = Some(address);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn notes(mut self, notes: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.notes = Some(notes.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn purchase_type(mut self, purchase_type: PurchaseType) -> Self {
 | 
				
			||||||
 | 
					        self.purchase_type = Some(purchase_type);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<Order, String> {
 | 
				
			||||||
 | 
					        let now = Utc::now();
 | 
				
			||||||
 | 
					        let subtotal = self.subtotal_base.unwrap_or_else(|| {
 | 
				
			||||||
 | 
					            self.items.iter().map(|item| item.total_price_base).sum()
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Ok(Order {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.base_data.id.ok_or("id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            user_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.user_id.ok_or("user_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            items: self.items,
 | 
				
			||||||
 | 
					            subtotal_base: subtotal,
 | 
				
			||||||
 | 
					            total_base: self.total_base.unwrap_or(subtotal),
 | 
				
			||||||
 | 
					            base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
 | 
				
			||||||
 | 
					            currency_used: self.currency_used.unwrap_or_else(|| "USD".to_string()),
 | 
				
			||||||
 | 
					            currency_total: self.currency_total.unwrap_or(subtotal),
 | 
				
			||||||
 | 
					            conversion_rate: self.conversion_rate.unwrap_or_else(|| Decimal::from(1)),
 | 
				
			||||||
 | 
					            status: self.status.unwrap_or(OrderStatus::Pending),
 | 
				
			||||||
 | 
					            payment_method: self.payment_method.unwrap_or_else(|| "credit_card".to_string()),
 | 
				
			||||||
 | 
					            payment_details: self.payment_details,
 | 
				
			||||||
 | 
					            billing_address: self.billing_address,
 | 
				
			||||||
 | 
					            shipping_address: self.shipping_address,
 | 
				
			||||||
 | 
					            notes: self.notes,
 | 
				
			||||||
 | 
					            purchase_type: self.purchase_type.unwrap_or(PurchaseType::Cart),
 | 
				
			||||||
 | 
					            // created_at: self.base_data.created_at.unwrap_or(now) - moved to base_data,
 | 
				
			||||||
 | 
					            // updated_at: self.base_data.updated_at.unwrap_or(now) - moved to base_data,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Order {
 | 
				
			||||||
 | 
					    pub fn builder() -> OrderBuilder {
 | 
				
			||||||
 | 
					        OrderBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct OrderItemBuilder {
 | 
				
			||||||
 | 
					    product_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    product_name: Option<String>,
 | 
				
			||||||
 | 
					    product_category: Option<String>,
 | 
				
			||||||
 | 
					    quantity: Option<u32>,
 | 
				
			||||||
 | 
					    unit_price_base: Option<Decimal>,
 | 
				
			||||||
 | 
					    total_price_base: Option<Decimal>,
 | 
				
			||||||
 | 
					    specifications: HashMap<String, Value>,
 | 
				
			||||||
 | 
					    provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    provider_name: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderItemBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn product_id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.product_id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn product_name(mut self, name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.product_name = Some(name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn product_category(mut self, category: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.product_category = Some(category.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn quantity(mut self, quantity: u32) -> Self {
 | 
				
			||||||
 | 
					        self.quantity = Some(quantity);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn unit_price_base(mut self, price: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.unit_price_base = Some(price);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_specification(mut self, key: impl Into<String>, value: Value) -> Self {
 | 
				
			||||||
 | 
					        self.specifications.insert(key.into(), value);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn provider_id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.provider_id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn provider_name(mut self, name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.provider_name = Some(name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<OrderItem, String> {
 | 
				
			||||||
 | 
					        let quantity = self.quantity.unwrap_or(1);
 | 
				
			||||||
 | 
					        let unit_price = self.unit_price_base.ok_or("unit_price_base is required")?;
 | 
				
			||||||
 | 
					        let total_price = self.total_price_base.unwrap_or(unit_price * Decimal::from(quantity));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(OrderItem {
 | 
				
			||||||
 | 
					            product_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.product_id.ok_or("product_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            product_name: self.product_name.ok_or("product_name is required")?,
 | 
				
			||||||
 | 
					            product_category: self.product_category.ok_or("product_category is required")?,
 | 
				
			||||||
 | 
					            quantity,
 | 
				
			||||||
 | 
					            unit_price_base: unit_price,
 | 
				
			||||||
 | 
					            total_price_base: total_price,
 | 
				
			||||||
 | 
					            specifications: self.specifications,
 | 
				
			||||||
 | 
					            provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.provider_id.ok_or("provider_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            provider_name: self.provider_name.ok_or("provider_name is required")?,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OrderItem {
 | 
				
			||||||
 | 
					    pub fn builder() -> OrderItemBuilder {
 | 
				
			||||||
 | 
					        OrderItemBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								heromodels/src/models/tfmarketplace/payment.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								heromodels/src/models/tfmarketplace/payment.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct PaymentDetails {
 | 
				
			||||||
 | 
					    pub payment_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub payment_method: PaymentMethod,
 | 
				
			||||||
 | 
					    pub transaction_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    pub payment_status: PaymentStatus,
 | 
				
			||||||
 | 
					    pub payment_timestamp: Option<DateTime<Utc>>,
 | 
				
			||||||
 | 
					    pub failure_reason: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum PaymentMethod {
 | 
				
			||||||
 | 
					    CreditCard {
 | 
				
			||||||
 | 
					        last_four: String,
 | 
				
			||||||
 | 
					        card_type: String,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    BankTransfer {
 | 
				
			||||||
 | 
					        bank_name: String,
 | 
				
			||||||
 | 
					        account_last_four: String,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Cryptocurrency {
 | 
				
			||||||
 | 
					        currency: String,
 | 
				
			||||||
 | 
					        wallet_address: String,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Token {
 | 
				
			||||||
 | 
					        token_type: String,
 | 
				
			||||||
 | 
					        wallet_address: String,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Mock {
 | 
				
			||||||
 | 
					        method_name: String,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum PaymentStatus {
 | 
				
			||||||
 | 
					    Pending,
 | 
				
			||||||
 | 
					    Processing,
 | 
				
			||||||
 | 
					    Completed,
 | 
				
			||||||
 | 
					    Failed,
 | 
				
			||||||
 | 
					    Cancelled,
 | 
				
			||||||
 | 
					    Refunded,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PaymentDetails {
 | 
				
			||||||
 | 
					    pub fn new(payment_id: &str, name: &str) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            payment_id,
 | 
				
			||||||
 | 
					            payment_method,
 | 
				
			||||||
 | 
					            transaction_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: None - moved to base_data,
 | 
				
			||||||
 | 
					            payment_status: PaymentStatus::Pending,
 | 
				
			||||||
 | 
					            payment_timestamp: None,
 | 
				
			||||||
 | 
					            failure_reason: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn mark_completed(&mut self, transaction_id: String) { - moved to base_data
 | 
				
			||||||
 | 
					        self.transaction_id = Some(transaction_id);
 | 
				
			||||||
 | 
					        self.payment_status = PaymentStatus::Completed;
 | 
				
			||||||
 | 
					        self.payment_timestamp = Some(Utc::now());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn mark_failed(&mut self, reason: String) {
 | 
				
			||||||
 | 
					        self.payment_status = PaymentStatus::Failed;
 | 
				
			||||||
 | 
					        self.failure_reason = Some(reason);
 | 
				
			||||||
 | 
					        self.payment_timestamp = Some(Utc::now());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										105
									
								
								heromodels/src/models/tfmarketplace/pool.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								heromodels/src/models/tfmarketplace/pool.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct LiquidityPool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub token_a: String,
 | 
				
			||||||
 | 
					    pub token_b: String,
 | 
				
			||||||
 | 
					    pub reserve_a: Decimal,
 | 
				
			||||||
 | 
					    pub reserve_b: Decimal,
 | 
				
			||||||
 | 
					    pub exchange_rate: Decimal,
 | 
				
			||||||
 | 
					    pub liquidity: Decimal,
 | 
				
			||||||
 | 
					    pub volume_24h: Decimal,
 | 
				
			||||||
 | 
					    pub fee_percentage: Decimal,
 | 
				
			||||||
 | 
					    pub status: PoolStatus,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum PoolStatus {
 | 
				
			||||||
 | 
					    Active,
 | 
				
			||||||
 | 
					    Paused,
 | 
				
			||||||
 | 
					    Maintenance,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct ExchangeRequest {
 | 
				
			||||||
 | 
					    pub pool_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub from_token: String,
 | 
				
			||||||
 | 
					    pub to_token: String,
 | 
				
			||||||
 | 
					    pub amount: Decimal,
 | 
				
			||||||
 | 
					    pub min_receive: Option<Decimal>,
 | 
				
			||||||
 | 
					    pub slippage_tolerance: Option<Decimal>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct ExchangeResponse {
 | 
				
			||||||
 | 
					    pub success: bool,
 | 
				
			||||||
 | 
					    pub message: String,
 | 
				
			||||||
 | 
					    pub transaction_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    pub from_amount: Option<Decimal>,
 | 
				
			||||||
 | 
					    pub to_amount: Option<Decimal>,
 | 
				
			||||||
 | 
					    pub exchange_rate: Option<Decimal>,
 | 
				
			||||||
 | 
					    pub fee: Option<Decimal>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct StakeRequest {
 | 
				
			||||||
 | 
					    pub amount: Decimal,
 | 
				
			||||||
 | 
					    pub duration_months: u32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct StakePosition {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub user_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub amount: Decimal,
 | 
				
			||||||
 | 
					    pub start_date: DateTime<Utc>,
 | 
				
			||||||
 | 
					    pub end_date: DateTime<Utc>,
 | 
				
			||||||
 | 
					    pub discount_percentage: Decimal,
 | 
				
			||||||
 | 
					    pub reputation_bonus: i32,
 | 
				
			||||||
 | 
					    pub status: StakeStatus,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum StakeStatus {
 | 
				
			||||||
 | 
					    Active,
 | 
				
			||||||
 | 
					    Completed,
 | 
				
			||||||
 | 
					    Withdrawn,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Pool analytics data
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct PoolAnalytics {
 | 
				
			||||||
 | 
					    pub price_history: Vec<PricePoint>,
 | 
				
			||||||
 | 
					    pub volume_history: Vec<VolumePoint>,
 | 
				
			||||||
 | 
					    pub liquidity_distribution: HashMap<String, Decimal>,
 | 
				
			||||||
 | 
					    pub staking_distribution: HashMap<String, i32>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct PricePoint {
 | 
				
			||||||
 | 
					    pub timestamp: DateTime<Utc>,
 | 
				
			||||||
 | 
					    pub price: Decimal,
 | 
				
			||||||
 | 
					    pub volume: Decimal,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct VolumePoint {
 | 
				
			||||||
 | 
					    pub date: String,
 | 
				
			||||||
 | 
					    pub volume: Decimal,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										660
									
								
								heromodels/src/models/tfmarketplace/product.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										660
									
								
								heromodels/src/models/tfmarketplace/product.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,660 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use heromodels_derive::model;
 | 
				
			||||||
 | 
					use rhai::CustomType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Generic product structure that can represent any marketplace item
 | 
				
			||||||
 | 
					#[model]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
 | 
				
			||||||
 | 
					pub struct Product {
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    #[index]
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub category: ProductCategory,
 | 
				
			||||||
 | 
					    pub description: String,
 | 
				
			||||||
 | 
					    pub price: Price,
 | 
				
			||||||
 | 
					    pub attributes: HashMap<String, ProductAttribute>, // Generic attributes
 | 
				
			||||||
 | 
					    pub provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub provider_name: String,
 | 
				
			||||||
 | 
					    pub availability: ProductAvailability,
 | 
				
			||||||
 | 
					    pub metadata: ProductMetadata,  // Extensible metadata
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub struct Price {
 | 
				
			||||||
 | 
					    pub base_amount: Decimal,
 | 
				
			||||||
 | 
					    pub currency: u32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configurable product categories
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub struct ProductCategory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub display_name: String,
 | 
				
			||||||
 | 
					    pub description: String,
 | 
				
			||||||
 | 
					    pub attribute_schema: Vec<AttributeDefinition>, // Defines allowed attributes
 | 
				
			||||||
 | 
					    pub parent_category: Option<String>,
 | 
				
			||||||
 | 
					    pub is_active: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Generic attribute system for any product type
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub struct ProductAttribute {
 | 
				
			||||||
 | 
					    pub key: String,
 | 
				
			||||||
 | 
					    pub value: serde_json::Value,
 | 
				
			||||||
 | 
					    pub attribute_type: AttributeType,
 | 
				
			||||||
 | 
					    pub is_searchable: bool,
 | 
				
			||||||
 | 
					    pub is_filterable: bool,
 | 
				
			||||||
 | 
					    pub display_order: Option<u32>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum AttributeType {
 | 
				
			||||||
 | 
					    Text,
 | 
				
			||||||
 | 
					    Number,
 | 
				
			||||||
 | 
					    SliceConfiguration,
 | 
				
			||||||
 | 
					    Boolean,
 | 
				
			||||||
 | 
					    Select(Vec<String>), // Predefined options
 | 
				
			||||||
 | 
					    MultiSelect(Vec<String>),
 | 
				
			||||||
 | 
					    Range { min: f64, max: f64 },
 | 
				
			||||||
 | 
					    Custom(String), // For marketplace-specific types
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub struct AttributeDefinition {
 | 
				
			||||||
 | 
					    pub key: String,
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub attribute_type: AttributeType,
 | 
				
			||||||
 | 
					    pub is_required: bool,
 | 
				
			||||||
 | 
					    pub is_searchable: bool,
 | 
				
			||||||
 | 
					    pub is_filterable: bool,
 | 
				
			||||||
 | 
					    pub validation_rules: Vec<ValidationRule>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum ValidationRule {
 | 
				
			||||||
 | 
					    MinLength(usize),
 | 
				
			||||||
 | 
					    MaxLength(usize),
 | 
				
			||||||
 | 
					    MinValue(f64),
 | 
				
			||||||
 | 
					    MaxValue(f64),
 | 
				
			||||||
 | 
					    Pattern(String),
 | 
				
			||||||
 | 
					    Custom(String),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum ProductAvailability {
 | 
				
			||||||
 | 
					    Available,
 | 
				
			||||||
 | 
					    Limited,
 | 
				
			||||||
 | 
					    Unavailable,
 | 
				
			||||||
 | 
					    PreOrder,
 | 
				
			||||||
 | 
					    Custom(String), // For marketplace-specific availability states
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for ProductAvailability {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        ProductAvailability::Available
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
 | 
					pub enum ProductVisibility {
 | 
				
			||||||
 | 
					    Public,
 | 
				
			||||||
 | 
					    Private,
 | 
				
			||||||
 | 
					    Draft,
 | 
				
			||||||
 | 
					    Archived,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for ProductVisibility {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        ProductVisibility::Public
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
 | 
				
			||||||
 | 
					pub struct ProductMetadata {
 | 
				
			||||||
 | 
					    pub tags: Vec<String>,
 | 
				
			||||||
 | 
					    pub location: Option<String>,
 | 
				
			||||||
 | 
					    pub rating: Option<f32>,
 | 
				
			||||||
 | 
					    pub review_count: u32,
 | 
				
			||||||
 | 
					    pub featured: bool,
 | 
				
			||||||
 | 
					    pub last_updated: chrono::DateTime<chrono::Utc>,
 | 
				
			||||||
 | 
					    pub visibility: ProductVisibility,
 | 
				
			||||||
 | 
					    pub seo_keywords: Vec<String>,
 | 
				
			||||||
 | 
					    pub custom_fields: HashMap<String, serde_json::Value>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Support for different pricing models
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum PricingModel {
 | 
				
			||||||
 | 
					    OneTime,                    // Single purchase
 | 
				
			||||||
 | 
					    Recurring { interval: String }, // Subscription
 | 
				
			||||||
 | 
					    UsageBased { unit: String },    // Pay per use
 | 
				
			||||||
 | 
					    Tiered(Vec<PriceTier>),        // Volume discounts
 | 
				
			||||||
 | 
					    Custom(String),                // Marketplace-specific
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct PriceTier {
 | 
				
			||||||
 | 
					    pub min_quantity: u32,
 | 
				
			||||||
 | 
					    pub max_quantity: Option<u32>,
 | 
				
			||||||
 | 
					    pub price_per_unit: Decimal,
 | 
				
			||||||
 | 
					    pub discount_percentage: Option<f32>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Product {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					        category: ProductCategory,
 | 
				
			||||||
 | 
					        description: String,
 | 
				
			||||||
 | 
					        price: Price,
 | 
				
			||||||
 | 
					        provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        provider_name: String,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            category,
 | 
				
			||||||
 | 
					            description,
 | 
				
			||||||
 | 
					            price,
 | 
				
			||||||
 | 
					            attributes: HashMap::default(),
 | 
				
			||||||
 | 
					            provider_id,
 | 
				
			||||||
 | 
					            provider_name,
 | 
				
			||||||
 | 
					            availability: ProductAvailability::Available,
 | 
				
			||||||
 | 
					            metadata: ProductMetadata {
 | 
				
			||||||
 | 
					                tags: Vec::default(),
 | 
				
			||||||
 | 
					                location: None,
 | 
				
			||||||
 | 
					                rating: None,
 | 
				
			||||||
 | 
					                review_count: 0,
 | 
				
			||||||
 | 
					                featured: false,
 | 
				
			||||||
 | 
					                last_updated: chrono::Utc::now(),
 | 
				
			||||||
 | 
					                visibility: ProductVisibility::Public,
 | 
				
			||||||
 | 
					                seo_keywords: Vec::new(),
 | 
				
			||||||
 | 
					                custom_fields: HashMap::default(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_attribute(&mut self, key: String, value: serde_json::Value, attribute_type: AttributeType) {
 | 
				
			||||||
 | 
					        let attribute = ProductAttribute {
 | 
				
			||||||
 | 
					            key: key.clone(),
 | 
				
			||||||
 | 
					            value,
 | 
				
			||||||
 | 
					            attribute_type,
 | 
				
			||||||
 | 
					            is_searchable: true,
 | 
				
			||||||
 | 
					            is_filterable: true,
 | 
				
			||||||
 | 
					            display_order: None,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        self.attributes.insert(key, attribute);
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_featured(&mut self, featured: bool) {
 | 
				
			||||||
 | 
					        self.metadata.featured = featured;
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_tag(&mut self, tag: String) {
 | 
				
			||||||
 | 
					        if !self.metadata.tags.contains(&tag) {
 | 
				
			||||||
 | 
					            self.metadata.tags.push(tag);
 | 
				
			||||||
 | 
					            self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_rating(&mut self, rating: f32, review_count: u32) {
 | 
				
			||||||
 | 
					        self.metadata.rating = Some(rating);
 | 
				
			||||||
 | 
					        self.metadata.review_count = review_count;
 | 
				
			||||||
 | 
					        self.base_data.modified_at = Utc::now().timestamp();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ProductCategory {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					            // id: String - moved to base_data, name: String, display_name: String, description: String) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            display_name,
 | 
				
			||||||
 | 
					            description,
 | 
				
			||||||
 | 
					            attribute_schema: Vec::default(),
 | 
				
			||||||
 | 
					            parent_category: None,
 | 
				
			||||||
 | 
					            is_active: true,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Add attribute definition to category schema
 | 
				
			||||||
 | 
					    pub fn add_attribute_definition(&mut self, definition: AttributeDefinition) {
 | 
				
			||||||
 | 
					        self.attribute_schema.push(definition);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Product {
 | 
				
			||||||
 | 
					    /// Create a slice product from farmer configuration
 | 
				
			||||||
 | 
					    pub fn create_slice_product(
 | 
				
			||||||
 | 
					        base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					        farmer_name: String,
 | 
				
			||||||
 | 
					        slice_name: String,
 | 
				
			||||||
 | 
					        slice_config: SliceConfiguration,
 | 
				
			||||||
 | 
					        price_per_hour: Decimal,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let category = ProductCategory {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: "compute_slices".to_string() - moved to base_data,
 | 
				
			||||||
 | 
					            name: "Compute Slices".to_string(),
 | 
				
			||||||
 | 
					            display_name: "Compute Slices".to_string(),
 | 
				
			||||||
 | 
					            description: "Virtual compute resources".to_string(),
 | 
				
			||||||
 | 
					            attribute_schema: Vec::new(),
 | 
				
			||||||
 | 
					            parent_category: None,
 | 
				
			||||||
 | 
					            is_active: true,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let price = Price {
 | 
				
			||||||
 | 
					            base_amount: price_per_hour,
 | 
				
			||||||
 | 
					            currency: 1, // USD currency ID
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let mut product = Self::new(
 | 
				
			||||||
 | 
					            base_data,
 | 
				
			||||||
 | 
					            slice_name,
 | 
				
			||||||
 | 
					            category,
 | 
				
			||||||
 | 
					            format!("Compute slice with {} vCPU, {}GB RAM, {}GB storage",
 | 
				
			||||||
 | 
					                   slice_config.cpu_cores, slice_config.memory_gb, slice_config.storage_gb),
 | 
				
			||||||
 | 
					            price,
 | 
				
			||||||
 | 
					            farmer_id,
 | 
				
			||||||
 | 
					            farmer_name,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add slice-specific attributes
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "cpu_cores".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(slice_config.cpu_cores)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "memory_gb".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(slice_config.memory_gb)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "storage_gb".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(slice_config.storage_gb)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "bandwidth_mbps".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(slice_config.bandwidth_mbps)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "min_uptime_sla".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from_f64(slice_config.min_uptime_sla as f64).unwrap()),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "public_ips".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(slice_config.public_ips)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(ref node_id) = slice_config.node_id {
 | 
				
			||||||
 | 
					            product.add_attribute(
 | 
				
			||||||
 | 
					                "node_id".to_string(),
 | 
				
			||||||
 | 
					                serde_json::Value::String(node_id.clone()),
 | 
				
			||||||
 | 
					                AttributeType::Text,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "slice_type".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::String(format!("{:?}", slice_config.slice_type)),
 | 
				
			||||||
 | 
					            AttributeType::Text,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add slice configuration as a complex attribute
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "slice_configuration".to_string(),
 | 
				
			||||||
 | 
					            serde_json::to_value(&slice_config).unwrap(),
 | 
				
			||||||
 | 
					            AttributeType::SliceConfiguration,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add relevant tags
 | 
				
			||||||
 | 
					        product.add_tag("compute".to_string());
 | 
				
			||||||
 | 
					        product.add_tag("slice".to_string());
 | 
				
			||||||
 | 
					        product.add_tag(format!("{:?}", slice_config.slice_type).to_lowercase());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check if this product is a slice
 | 
				
			||||||
 | 
					    pub fn is_slice(&self) -> bool {
 | 
				
			||||||
 | 
					        self.category.id == "compute_slices" ||
 | 
				
			||||||
 | 
					        self.attributes.contains_key("slice_configuration")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get slice configuration from product attributes
 | 
				
			||||||
 | 
					    pub fn get_slice_configuration(&self) -> Option<SliceConfiguration> {
 | 
				
			||||||
 | 
					        self.attributes.get("slice_configuration")
 | 
				
			||||||
 | 
					            .and_then(|attr| serde_json::from_value(attr.value.clone()).ok())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Update slice configuration
 | 
				
			||||||
 | 
					    pub fn update_slice_configuration(&mut self, config: SliceConfiguration) {
 | 
				
			||||||
 | 
					        if self.is_slice() {
 | 
				
			||||||
 | 
					            self.add_attribute(
 | 
				
			||||||
 | 
					                "slice_configuration".to_string(),
 | 
				
			||||||
 | 
					                serde_json::to_value(&config).unwrap(),
 | 
				
			||||||
 | 
					                AttributeType::SliceConfiguration,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Update individual attributes for searchability
 | 
				
			||||||
 | 
					            self.add_attribute(
 | 
				
			||||||
 | 
					                "cpu_cores".to_string(),
 | 
				
			||||||
 | 
					                serde_json::Value::Number(serde_json::Number::from(config.cpu_cores)),
 | 
				
			||||||
 | 
					                AttributeType::Number,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.add_attribute(
 | 
				
			||||||
 | 
					                "memory_gb".to_string(),
 | 
				
			||||||
 | 
					                serde_json::Value::Number(serde_json::Number::from(config.memory_gb)),
 | 
				
			||||||
 | 
					                AttributeType::Number,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.add_attribute(
 | 
				
			||||||
 | 
					                "storage_gb".to_string(),
 | 
				
			||||||
 | 
					                serde_json::Value::Number(serde_json::Number::from(config.storage_gb)),
 | 
				
			||||||
 | 
					                AttributeType::Number,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check if slice fits within node capacity
 | 
				
			||||||
 | 
					    pub fn slice_fits_in_node(&self, node_capacity: &crate::models::user::NodeCapacity) -> bool {
 | 
				
			||||||
 | 
					        if let Some(config) = self.get_slice_configuration() {
 | 
				
			||||||
 | 
					            config.cpu_cores <= node_capacity.cpu_cores &&
 | 
				
			||||||
 | 
					            config.memory_gb <= node_capacity.memory_gb &&
 | 
				
			||||||
 | 
					            config.storage_gb <= node_capacity.storage_gb &&
 | 
				
			||||||
 | 
					            config.bandwidth_mbps <= node_capacity.bandwidth_mbps
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Create a full node product from a FarmNode
 | 
				
			||||||
 | 
					    pub fn create_full_node_product(
 | 
				
			||||||
 | 
					        node: &crate::models::user::FarmNode,
 | 
				
			||||||
 | 
					        farmer_email: &str,
 | 
				
			||||||
 | 
					        farmer_name: &str,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let category = ProductCategory {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: "3nodes".to_string() - moved to base_data,
 | 
				
			||||||
 | 
					            name: "3Nodes".to_string(),
 | 
				
			||||||
 | 
					            display_name: "3Nodes".to_string(),
 | 
				
			||||||
 | 
					            description: "Full node rentals".to_string(),
 | 
				
			||||||
 | 
					            attribute_schema: Vec::new(),
 | 
				
			||||||
 | 
					            parent_category: None,
 | 
				
			||||||
 | 
					            is_active: true,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let price = Price {
 | 
				
			||||||
 | 
					            base_amount: node.rental_options
 | 
				
			||||||
 | 
					                .as_ref()
 | 
				
			||||||
 | 
					                .and_then(|opts| opts.full_node_pricing.as_ref())
 | 
				
			||||||
 | 
					                .map(|pricing| pricing.monthly)
 | 
				
			||||||
 | 
					                .unwrap_or_else(|| Decimal::from(200)), // Default price
 | 
				
			||||||
 | 
					            currency: 1, // USD currency ID
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let mut product = Product {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            name: format!("Full Node: {}", node.name),
 | 
				
			||||||
 | 
					            category,
 | 
				
			||||||
 | 
					            description: format!(
 | 
				
			||||||
 | 
					                "Exclusive access to {} with {} CPU cores, {}GB RAM, {}GB storage in {}",
 | 
				
			||||||
 | 
					                node.name, node.capacity.cpu_cores, node.capacity.memory_gb,
 | 
				
			||||||
 | 
					                node.capacity.storage_gb, node.location
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            price,
 | 
				
			||||||
 | 
					            attributes: HashMap::new(),
 | 
				
			||||||
 | 
					            provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: farmer_email.to_string() - moved to base_data,
 | 
				
			||||||
 | 
					            provider_name: farmer_name.to_string(),
 | 
				
			||||||
 | 
					            availability: match node.availability_status {
 | 
				
			||||||
 | 
					                crate::models::user::NodeAvailabilityStatus::Available => ProductAvailability::Available,
 | 
				
			||||||
 | 
					                crate::models::user::NodeAvailabilityStatus::PartiallyRented => ProductAvailability::Limited,
 | 
				
			||||||
 | 
					                _ => ProductAvailability::Unavailable,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            metadata: ProductMetadata {
 | 
				
			||||||
 | 
					                tags: vec!["full-node".to_string(), "exclusive".to_string(), node.region.clone()],
 | 
				
			||||||
 | 
					                location: Some(node.location.clone()),
 | 
				
			||||||
 | 
					                rating: None,
 | 
				
			||||||
 | 
					                review_count: 0,
 | 
				
			||||||
 | 
					                featured: false,
 | 
				
			||||||
 | 
					                last_updated: chrono::Utc::now(),
 | 
				
			||||||
 | 
					                visibility: ProductVisibility::Public,
 | 
				
			||||||
 | 
					                seo_keywords: Vec::new(),
 | 
				
			||||||
 | 
					                custom_fields: HashMap::new(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add node-specific attributes
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "node_id".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::String(node.id.clone()),
 | 
				
			||||||
 | 
					            AttributeType::Text,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "rental_type".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::String("full_node".to_string()),
 | 
				
			||||||
 | 
					            AttributeType::Text,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "cpu_cores".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(node.capacity.cpu_cores)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "memory_gb".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(node.capacity.memory_gb)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "storage_gb".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(node.capacity.storage_gb)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "bandwidth_mbps".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from(node.capacity.bandwidth_mbps)),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "location".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::String(node.location.clone()),
 | 
				
			||||||
 | 
					            AttributeType::Text,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "uptime_percentage".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from_f64(node.uptime_percentage as f64).unwrap_or_else(|| serde_json::Number::from(0))),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product.add_attribute(
 | 
				
			||||||
 | 
					            "health_score".to_string(),
 | 
				
			||||||
 | 
					            serde_json::Value::Number(serde_json::Number::from_f64(node.health_score as f64).unwrap_or_else(|| serde_json::Number::from(0))),
 | 
				
			||||||
 | 
					            AttributeType::Number,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check if this product represents a full node
 | 
				
			||||||
 | 
					    pub fn is_full_node(&self) -> bool {
 | 
				
			||||||
 | 
					        self.attributes.get("rental_type")
 | 
				
			||||||
 | 
					            .and_then(|attr| attr.value.as_str())
 | 
				
			||||||
 | 
					            .map(|s| s == "full_node")
 | 
				
			||||||
 | 
					            .unwrap_or(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get the node ID if this is a node product
 | 
				
			||||||
 | 
					    pub fn get_node_id(&self) -> Option<String> {
 | 
				
			||||||
 | 
					        self.attributes.get("node_id")
 | 
				
			||||||
 | 
					            .and_then(|attr| attr.value.as_str())
 | 
				
			||||||
 | 
					            .map(|s| s.to_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ProductCategory {
 | 
				
			||||||
 | 
					    pub fn set_parent_category(&mut self, parent_id: String) {
 | 
				
			||||||
 | 
					        self.parent_category = Some(parent_id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AttributeDefinition {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        key: String,
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					        attribute_type: AttributeType,
 | 
				
			||||||
 | 
					        is_required: bool,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            key,
 | 
				
			||||||
 | 
					            name,
 | 
				
			||||||
 | 
					            attribute_type,
 | 
				
			||||||
 | 
					            is_required,
 | 
				
			||||||
 | 
					            is_searchable: true,
 | 
				
			||||||
 | 
					            is_filterable: true,
 | 
				
			||||||
 | 
					            validation_rules: Vec::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_validation_rule(&mut self, rule: ValidationRule) {
 | 
				
			||||||
 | 
					        self.validation_rules.push(rule);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct ProductBuilder {
 | 
				
			||||||
 | 
					    base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    name: Option<String>,
 | 
				
			||||||
 | 
					    category_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    base_price: Option<Decimal>,
 | 
				
			||||||
 | 
					    base_currency: Option<String>,
 | 
				
			||||||
 | 
					    attributes: HashMap<String, ProductAttribute>,
 | 
				
			||||||
 | 
					    provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    provider_name: Option<String>,
 | 
				
			||||||
 | 
					    availability: Option<ProductAvailability>,
 | 
				
			||||||
 | 
					    metadata: Option<ProductMetadata>,
 | 
				
			||||||
 | 
					    // created_at: Option<DateTime<Utc>> - moved to base_data,
 | 
				
			||||||
 | 
					    // updated_at: Option<DateTime<Utc>> - moved to base_data,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ProductBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn id(mut self, id: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.base_data.id = Some(id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn name(mut self, name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.name = Some(name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn category_id(mut self, category_id: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.category_id = Some(category_id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn description(mut self, description: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.description = Some(description.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn base_price(mut self, price: Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.base_price = Some(price);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.base_currency = Some(currency.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_attribute(mut self, key: impl Into<String>, attribute: ProductAttribute) -> Self {
 | 
				
			||||||
 | 
					        self.attributes.insert(key.into(), attribute);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn provider_id(mut self, provider_id: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.provider_id = Some(provider_id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn provider_name(mut self, provider_name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.provider_name = Some(provider_name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn availability(mut self, availability: ProductAvailability) -> Self {
 | 
				
			||||||
 | 
					        self.availability = Some(availability);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn metadata(mut self, metadata: ProductMetadata) -> Self {
 | 
				
			||||||
 | 
					        self.metadata = Some(metadata);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<Product, String> {
 | 
				
			||||||
 | 
					        let now = Utc::now();
 | 
				
			||||||
 | 
					        Ok(Product {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.base_data.id.ok_or("id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            name: self.name.ok_or("name is required")?,
 | 
				
			||||||
 | 
					            category_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.category_id.ok_or("category_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            description: self.description.unwrap_or_default(),
 | 
				
			||||||
 | 
					            base_price: self.base_price.ok_or("base_price is required")?,
 | 
				
			||||||
 | 
					            base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
 | 
				
			||||||
 | 
					            attributes: self.attributes,
 | 
				
			||||||
 | 
					            provider_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.provider_id.ok_or("provider_id is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            provider_name: self.provider_name.ok_or("provider_name is required")?,
 | 
				
			||||||
 | 
					            availability: self.availability.unwrap_or_default(),
 | 
				
			||||||
 | 
					            metadata: self.metadata.unwrap_or_default(),
 | 
				
			||||||
 | 
					            // created_at: self.base_data.created_at.unwrap_or(now) - moved to base_data,
 | 
				
			||||||
 | 
					            // updated_at: self.base_data.updated_at.unwrap_or(now) - moved to base_data,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Product {
 | 
				
			||||||
 | 
					    pub fn builder() -> ProductBuilder {
 | 
				
			||||||
 | 
					        ProductBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										297
									
								
								heromodels/src/models/tfmarketplace/service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								heromodels/src/models/tfmarketplace/service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
				
			|||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize, Deserializer};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Service Provider-specific data
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct ServiceProviderData {
 | 
				
			||||||
 | 
					    pub active_services: i32,
 | 
				
			||||||
 | 
					    pub total_clients: i32,
 | 
				
			||||||
 | 
					    pub monthly_revenue_usd: i32,
 | 
				
			||||||
 | 
					    pub total_revenue_usd: i32,
 | 
				
			||||||
 | 
					    pub service_rating: f32,
 | 
				
			||||||
 | 
					    pub services: Vec<Service>,
 | 
				
			||||||
 | 
					    pub client_requests: Vec<ServiceRequest>,
 | 
				
			||||||
 | 
					    pub revenue_history: Vec<RevenueRecord>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Service {
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,
 | 
				
			||||||
 | 
					    pub name: String,
 | 
				
			||||||
 | 
					    pub category: String,
 | 
				
			||||||
 | 
					    pub description: String,
 | 
				
			||||||
 | 
					    pub price_per_hour_usd: i32,
 | 
				
			||||||
 | 
					    pub status: String,
 | 
				
			||||||
 | 
					    pub clients: i32,
 | 
				
			||||||
 | 
					    pub rating: f32,
 | 
				
			||||||
 | 
					    pub total_hours: i32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct ServiceRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Base model data (includes id, created_at, updated_at)
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData,
 | 
				
			||||||
 | 
					    pub client_name: String,
 | 
				
			||||||
 | 
					    pub service_name: String,
 | 
				
			||||||
 | 
					    pub status: String,
 | 
				
			||||||
 | 
					    pub requested_date: String,
 | 
				
			||||||
 | 
					    pub estimated_hours: i32,
 | 
				
			||||||
 | 
					    pub budget: i32,
 | 
				
			||||||
 | 
					    pub priority: String,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub progress: Option<i32>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub completed_date: Option<String>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub client_email: Option<String>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub client_phone: Option<String>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub description: Option<String>,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub created_date: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Service booking record for customers who purchase services
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct ServiceBooking {
 | 
				
			||||||
 | 
					    pub base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,                    // Same as ServiceRequest.id for cross-reference
 | 
				
			||||||
 | 
					    pub service_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: String - moved to base_data,            // Reference to original service
 | 
				
			||||||
 | 
					    pub service_name: String,
 | 
				
			||||||
 | 
					    pub provider_email: String,        // Who provides the service
 | 
				
			||||||
 | 
					    pub customer_email: String,        // Who booked the service
 | 
				
			||||||
 | 
					    pub budget: i32,
 | 
				
			||||||
 | 
					    pub estimated_hours: i32,
 | 
				
			||||||
 | 
					    pub status: String,                // "Pending", "In Progress", "Completed"
 | 
				
			||||||
 | 
					    pub requested_date: String,
 | 
				
			||||||
 | 
					    pub priority: String,
 | 
				
			||||||
 | 
					    pub description: Option<String>,
 | 
				
			||||||
 | 
					    pub booking_date: String,          // When customer booked
 | 
				
			||||||
 | 
					    pub client_phone: Option<String>,
 | 
				
			||||||
 | 
					    pub progress: Option<i32>,
 | 
				
			||||||
 | 
					    pub completed_date: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Customer Service-specific data (for users who book services)
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct CustomerServiceData {
 | 
				
			||||||
 | 
					    pub active_bookings: i32,
 | 
				
			||||||
 | 
					    pub completed_bookings: i32,
 | 
				
			||||||
 | 
					    pub total_spent: i32,
 | 
				
			||||||
 | 
					    pub monthly_spending: i32,
 | 
				
			||||||
 | 
					    pub average_rating_given: f32,
 | 
				
			||||||
 | 
					    pub service_bookings: Vec<ServiceBooking>,
 | 
				
			||||||
 | 
					    pub favorite_providers: Vec<String>,
 | 
				
			||||||
 | 
					    pub spending_history: Vec<SpendingRecord>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct SpendingRecord {
 | 
				
			||||||
 | 
					    pub date: String,
 | 
				
			||||||
 | 
					    pub amount: i32,
 | 
				
			||||||
 | 
					    pub service_name: String,
 | 
				
			||||||
 | 
					    pub provider_name: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct ServiceBookingBuilder {
 | 
				
			||||||
 | 
					    base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    service_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    service_name: Option<String>,
 | 
				
			||||||
 | 
					    provider_email: Option<String>,
 | 
				
			||||||
 | 
					    customer_email: Option<String>,
 | 
				
			||||||
 | 
					    budget: Option<i32>,
 | 
				
			||||||
 | 
					    estimated_hours: Option<i32>,
 | 
				
			||||||
 | 
					    status: Option<String>,
 | 
				
			||||||
 | 
					    requested_date: Option<String>,
 | 
				
			||||||
 | 
					    priority: Option<String>,
 | 
				
			||||||
 | 
					    description: Option<String>,
 | 
				
			||||||
 | 
					    booking_date: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ServiceBookingBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn id(mut self) -> Self{
 | 
				
			||||||
 | 
					        self.base_data.id = Some(id.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn service_id(mut self, service_id: &str, name: &str) -> Self{
 | 
				
			||||||
 | 
					        self.service_id = Some(service_id.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn service_name(mut self, service_name: &str) -> Self {
 | 
				
			||||||
 | 
					        self.service_name = Some(service_name.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn provider_email(mut self, provider_email: &str) -> Self {
 | 
				
			||||||
 | 
					        self.provider_email = Some(provider_email.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn customer_email(mut self, customer_email: &str) -> Self {
 | 
				
			||||||
 | 
					        self.customer_email = Some(customer_email.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn budget(mut self, budget: i32) -> Self {
 | 
				
			||||||
 | 
					        self.budget = Some(budget);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn estimated_hours(mut self, hours: i32) -> Self {
 | 
				
			||||||
 | 
					        self.estimated_hours = Some(hours);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn status(mut self, status: &str) -> Self {
 | 
				
			||||||
 | 
					        self.status = Some(status.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn requested_date(mut self, date: &str) -> Self {
 | 
				
			||||||
 | 
					        self.requested_date = Some(date.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn priority(mut self, priority: &str) -> Self {
 | 
				
			||||||
 | 
					        self.priority = Some(priority.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn description(mut self, description: Option<String>) -> Self {
 | 
				
			||||||
 | 
					        self.description = description;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn booking_date(mut self, date: &str) -> Self {
 | 
				
			||||||
 | 
					        self.booking_date = Some(date.to_string());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<ServiceBooking, String> {
 | 
				
			||||||
 | 
					        Ok(ServiceBooking {
 | 
				
			||||||
 | 
					            base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.base_data.id.ok_or("ID is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            service_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.service_id.ok_or("Service ID is required")? - moved to base_data,
 | 
				
			||||||
 | 
					            service_name: self.service_name.ok_or("Service name is required")?,
 | 
				
			||||||
 | 
					            provider_email: self.provider_email.ok_or("Provider email is required")?,
 | 
				
			||||||
 | 
					            customer_email: self.customer_email.ok_or("Customer email is required")?,
 | 
				
			||||||
 | 
					            budget: self.budget.unwrap_or(0),
 | 
				
			||||||
 | 
					            estimated_hours: self.estimated_hours.unwrap_or(0),
 | 
				
			||||||
 | 
					            status: self.status.unwrap_or_else(|| "Pending".to_string()),
 | 
				
			||||||
 | 
					            requested_date: self.requested_date.unwrap_or_else(|| chrono::Utc::now().format("%Y-%m-%d").to_string()),
 | 
				
			||||||
 | 
					            priority: self.priority.unwrap_or_else(|| "Medium".to_string()),
 | 
				
			||||||
 | 
					            description: self.description,
 | 
				
			||||||
 | 
					            booking_date: self.booking_date.unwrap_or_else(|| chrono::Utc::now().format("%Y-%m-%d").to_string()),
 | 
				
			||||||
 | 
					            client_phone: None,
 | 
				
			||||||
 | 
					            progress: None,
 | 
				
			||||||
 | 
					            completed_date: None,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ServiceBooking {
 | 
				
			||||||
 | 
					    pub fn builder() -> ServiceBookingBuilder {
 | 
				
			||||||
 | 
					        ServiceBookingBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					// CUSTOMER SERVICE DATA BUILDER
 | 
				
			||||||
 | 
					// =============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct CustomerServiceDataBuilder {
 | 
				
			||||||
 | 
					    active_bookings: Option<i32>,
 | 
				
			||||||
 | 
					    completed_bookings: Option<i32>,
 | 
				
			||||||
 | 
					    total_spent: Option<i32>,
 | 
				
			||||||
 | 
					    monthly_spending: Option<i32>,
 | 
				
			||||||
 | 
					    average_rating_given: Option<f32>,
 | 
				
			||||||
 | 
					    service_bookings: Option<Vec<crate::models::user::ServiceBooking>>,
 | 
				
			||||||
 | 
					    favorite_providers: Option<Vec<String>>,
 | 
				
			||||||
 | 
					    spending_history: Option<Vec<crate::models::user::SpendingRecord>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl CustomerServiceDataBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn active_bookings(mut self, count: i32) -> Self {
 | 
				
			||||||
 | 
					        self.active_bookings = Some(count);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn completed_bookings(mut self, count: i32) -> Self {
 | 
				
			||||||
 | 
					        self.completed_bookings = Some(count);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn total_spent(mut self, amount: i32) -> Self {
 | 
				
			||||||
 | 
					        self.total_spent = Some(amount);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn monthly_spending(mut self, amount: i32) -> Self {
 | 
				
			||||||
 | 
					        self.monthly_spending = Some(amount);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn average_rating_given(mut self, rating: f32) -> Self {
 | 
				
			||||||
 | 
					        self.average_rating_given = Some(rating);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn service_bookings(mut self, bookings: Vec<crate::models::user::ServiceBooking>) -> Self {
 | 
				
			||||||
 | 
					        self.service_bookings = Some(bookings);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn favorite_providers(mut self, providers: Vec<String>) -> Self {
 | 
				
			||||||
 | 
					        self.favorite_providers = Some(providers);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn spending_history(mut self, history: Vec<crate::models::user::SpendingRecord>) -> Self {
 | 
				
			||||||
 | 
					        self.spending_history = Some(history);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::models::user::CustomerServiceData, String> {
 | 
				
			||||||
 | 
					        Ok(crate::models::user::CustomerServiceData {
 | 
				
			||||||
 | 
					            active_bookings: self.active_bookings.unwrap_or(0),
 | 
				
			||||||
 | 
					            completed_bookings: self.completed_bookings.unwrap_or(0),
 | 
				
			||||||
 | 
					            total_spent: self.total_spent.unwrap_or(0),
 | 
				
			||||||
 | 
					            monthly_spending: self.monthly_spending.unwrap_or(0),
 | 
				
			||||||
 | 
					            average_rating_given: self.average_rating_given.unwrap_or(0.0),
 | 
				
			||||||
 | 
					            service_bookings: self.service_bookings.unwrap_or_default(),
 | 
				
			||||||
 | 
					            favorite_providers: self.favorite_providers.unwrap_or_default(),
 | 
				
			||||||
 | 
					            spending_history: self.spending_history.unwrap_or_default(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl crate::models::user::CustomerServiceData {
 | 
				
			||||||
 | 
					    pub fn builder() -> CustomerServiceDataBuilder {
 | 
				
			||||||
 | 
					        CustomerServiceDataBuilder::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										200
									
								
								heromodels/src/models/tfmarketplace/slice.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								heromodels/src/models/tfmarketplace/slice.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
				
			|||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use chrono::{DateTime, Utc};
 | 
				
			||||||
 | 
					use rust_decimal::Decimal;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use heromodels_core::BaseModelData;
 | 
				
			||||||
 | 
					use crate::models::tfmarketplace::user::ResourceUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Slice configuration data structure for product attributes
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct SliceConfiguration {
 | 
				
			||||||
 | 
					    pub cpu_cores: i32,
 | 
				
			||||||
 | 
					    pub memory_gb: i32,
 | 
				
			||||||
 | 
					    pub storage_gb: i32,
 | 
				
			||||||
 | 
					    pub bandwidth_mbps: i32,
 | 
				
			||||||
 | 
					    pub min_uptime_sla: f32,
 | 
				
			||||||
 | 
					    pub public_ips: i32,
 | 
				
			||||||
 | 
					    pub node_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    pub slice_type: SliceType,
 | 
				
			||||||
 | 
					    #[serde(default)]
 | 
				
			||||||
 | 
					    pub pricing: SlicePricing,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Enhanced pricing structure for slices with multiple time periods
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct SlicePricing {
 | 
				
			||||||
 | 
					    pub hourly: Decimal,
 | 
				
			||||||
 | 
					    pub daily: Decimal,
 | 
				
			||||||
 | 
					    pub monthly: Decimal,
 | 
				
			||||||
 | 
					    pub yearly: Decimal,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for SlicePricing {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            hourly: Decimal::ZERO,
 | 
				
			||||||
 | 
					            daily: Decimal::ZERO,
 | 
				
			||||||
 | 
					            monthly: Decimal::ZERO,
 | 
				
			||||||
 | 
					            yearly: Decimal::ZERO,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SlicePricing {
 | 
				
			||||||
 | 
					    /// Create pricing from hourly rate with automatic calculation
 | 
				
			||||||
 | 
					    pub fn from_hourly(hourly_rate: Decimal, daily_discount: f32, monthly_discount: f32, yearly_discount: f32) -> Self {
 | 
				
			||||||
 | 
					        let base_daily = hourly_rate * Decimal::from(24);
 | 
				
			||||||
 | 
					        let base_monthly = hourly_rate * Decimal::from(24 * 30);
 | 
				
			||||||
 | 
					        let base_yearly = hourly_rate * Decimal::from(24 * 365);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            hourly: hourly_rate,
 | 
				
			||||||
 | 
					            daily: base_daily * Decimal::try_from(1.0 - daily_discount / 100.0).unwrap_or(Decimal::ONE),
 | 
				
			||||||
 | 
					            monthly: base_monthly * Decimal::try_from(1.0 - monthly_discount / 100.0).unwrap_or(Decimal::ONE),
 | 
				
			||||||
 | 
					            yearly: base_yearly * Decimal::try_from(1.0 - yearly_discount / 100.0).unwrap_or(Decimal::ONE),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Calculate savings compared to hourly rate
 | 
				
			||||||
 | 
					    pub fn calculate_savings(&self) -> (Decimal, Decimal, Decimal) {
 | 
				
			||||||
 | 
					        let hourly_equivalent_daily = self.hourly * Decimal::from(24);
 | 
				
			||||||
 | 
					        let hourly_equivalent_monthly = self.hourly * Decimal::from(24 * 30);
 | 
				
			||||||
 | 
					        let hourly_equivalent_yearly = self.hourly * Decimal::from(24 * 365);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let daily_savings = hourly_equivalent_daily - self.daily;
 | 
				
			||||||
 | 
					        let monthly_savings = hourly_equivalent_monthly - self.monthly;
 | 
				
			||||||
 | 
					        let yearly_savings = hourly_equivalent_yearly - self.yearly;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        (daily_savings, monthly_savings, yearly_savings)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum SliceType {
 | 
				
			||||||
 | 
					    Basic,
 | 
				
			||||||
 | 
					    Standard,
 | 
				
			||||||
 | 
					    Premium,
 | 
				
			||||||
 | 
					    Custom,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct SliceProductBuilder {
 | 
				
			||||||
 | 
					    farmer_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    farmer_name: Option<String>,
 | 
				
			||||||
 | 
					    slice_name: Option<String>,
 | 
				
			||||||
 | 
					    cpu_cores: Option<i32>,
 | 
				
			||||||
 | 
					    memory_gb: Option<i32>,
 | 
				
			||||||
 | 
					    storage_gb: Option<i32>,
 | 
				
			||||||
 | 
					    bandwidth_mbps: Option<i32>,
 | 
				
			||||||
 | 
					    min_uptime_sla: Option<f32>,
 | 
				
			||||||
 | 
					    public_ips: Option<i32>,
 | 
				
			||||||
 | 
					    node_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: Option<String> - moved to base_data,
 | 
				
			||||||
 | 
					    slice_type: Option<crate::models::tfmarketplace::product::SliceType>,
 | 
				
			||||||
 | 
					    price_per_hour: Option<rust_decimal::Decimal>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SliceProductBuilder {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn farmer_id(mut self, farmer_id: &str, name: &str) -> Self{
 | 
				
			||||||
 | 
					        self.farmer_id = Some(farmer_id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn farmer_name(mut self, farmer_name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.farmer_name = Some(farmer_name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn slice_name(mut self, slice_name: impl Into<String>) -> Self {
 | 
				
			||||||
 | 
					        self.slice_name = Some(slice_name.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn cpu_cores(mut self, cpu_cores: i32) -> Self {
 | 
				
			||||||
 | 
					        self.cpu_cores = Some(cpu_cores);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn memory_gb(mut self, memory_gb: i32) -> Self {
 | 
				
			||||||
 | 
					        self.memory_gb = Some(memory_gb);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn storage_gb(mut self, storage_gb: i32) -> Self {
 | 
				
			||||||
 | 
					        self.storage_gb = Some(storage_gb);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn bandwidth_mbps(mut self, bandwidth_mbps: i32) -> Self {
 | 
				
			||||||
 | 
					        self.bandwidth_mbps = Some(bandwidth_mbps);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn min_uptime_sla(mut self, min_uptime_sla: f32) -> Self {
 | 
				
			||||||
 | 
					        self.min_uptime_sla = Some(min_uptime_sla);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn public_ips(mut self, public_ips: i32) -> Self {
 | 
				
			||||||
 | 
					        self.public_ips = Some(public_ips);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn node_id(mut self, node_id: &str, name: &str) -> Self{
 | 
				
			||||||
 | 
					        self.node_id = Some(node_id.into());
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn slice_type(mut self, slice_type: crate::models::tfmarketplace::product::SliceType) -> Self {
 | 
				
			||||||
 | 
					        self.slice_type = Some(slice_type);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn price_per_hour(mut self, price_per_hour: rust_decimal::Decimal) -> Self {
 | 
				
			||||||
 | 
					        self.price_per_hour = Some(price_per_hour);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build(self) -> Result<crate::models::tfmarketplace::product::Product, String> {
 | 
				
			||||||
 | 
					        let farmer_id = self.farmer_id.ok_or("farmer_id is required")?;
 | 
				
			||||||
 | 
					        let farmer_name = self.farmer_name.ok_or("farmer_name is required")?;
 | 
				
			||||||
 | 
					        let slice_name = self.slice_name.ok_or("slice_name is required")?;
 | 
				
			||||||
 | 
					        let cpu_cores = self.cpu_cores.ok_or("cpu_cores is required")?;
 | 
				
			||||||
 | 
					        let memory_gb = self.memory_gb.ok_or("memory_gb is required")?;
 | 
				
			||||||
 | 
					        let storage_gb = self.storage_gb.ok_or("storage_gb is required")?;
 | 
				
			||||||
 | 
					        let bandwidth_mbps = self.bandwidth_mbps.ok_or("bandwidth_mbps is required")?;
 | 
				
			||||||
 | 
					        let price_per_hour = self.price_per_hour.ok_or("price_per_hour is required")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let slice_config = crate::models::tfmarketplace::product::SliceConfiguration {
 | 
				
			||||||
 | 
					            cpu_cores,
 | 
				
			||||||
 | 
					            memory_gb,
 | 
				
			||||||
 | 
					            storage_gb,
 | 
				
			||||||
 | 
					            bandwidth_mbps,
 | 
				
			||||||
 | 
					            min_uptime_sla: self.min_uptime_sla.unwrap_or(99.0),
 | 
				
			||||||
 | 
					            public_ips: self.public_ips.unwrap_or(0),
 | 
				
			||||||
 | 
					            node_base_data: BaseModelData::new(),
 | 
				
			||||||
 | 
					            // id: self.node_id - moved to base_data,
 | 
				
			||||||
 | 
					            slice_type: self.slice_type.unwrap_or(crate::models::tfmarketplace::product::SliceType::Basic),
 | 
				
			||||||
 | 
					            pricing: crate::models::tfmarketplace::product::SlicePricing::from_hourly(
 | 
				
			||||||
 | 
					                price_per_hour,
 | 
				
			||||||
 | 
					                5.0,  // 5% daily discount
 | 
				
			||||||
 | 
					                15.0, // 15% monthly discount
 | 
				
			||||||
 | 
					                25.0  // 25% yearly discount
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(crate::models::tfmarketplace::product::Product::create_slice_product(
 | 
				
			||||||
 | 
					            farmer_id,
 | 
				
			||||||
 | 
					            farmer_name,
 | 
				
			||||||
 | 
					            slice_name,
 | 
				
			||||||
 | 
					            slice_config,
 | 
				
			||||||
 | 
					            price_per_hour,
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3509
									
								
								heromodels/src/models/tfmarketplace/user.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3509
									
								
								heromodels/src/models/tfmarketplace/user.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user