feat: Add new Rhai example demonstrating payment flow
- Add a new Rhai example showcasing complete payment flow for company registration. This includes company creation, payment record creation, successful payment processing, status updates, and handling failed and refunded payments. - Add new example demonstrating payment integration in Rhai scripting. This example showcases the usage of various payment status methods and verifies data from the database after processing payments. - Add new examples to Cargo.toml to facilitate building and running the examples. This makes it easier to integrate and test payment functionality using Rhai scripting.
This commit is contained in:
		@@ -1,19 +1,21 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use heromodels_core::{BaseModelData, Model, IndexKey, IndexKeyBuilder, Index};
 | 
			
		||||
use rhai::{CustomType, TypeBuilder}; // For #[derive(CustomType)]
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize}; // For #[derive(CustomType)]
 | 
			
		||||
 | 
			
		||||
// --- Enums ---
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 | 
			
		||||
pub enum CompanyStatus {
 | 
			
		||||
    Active,
 | 
			
		||||
    Inactive,
 | 
			
		||||
    Suspended,
 | 
			
		||||
    PendingPayment, // Company created but payment not completed
 | 
			
		||||
    Active,         // Payment completed, company is active
 | 
			
		||||
    Suspended,      // Company suspended (e.g., payment issues)
 | 
			
		||||
    Inactive,       // Company deactivated
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CompanyStatus {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        CompanyStatus::Inactive
 | 
			
		||||
        CompanyStatus::PendingPayment
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +36,7 @@ impl Default for BusinessType {
 | 
			
		||||
 | 
			
		||||
// --- Company Struct ---
 | 
			
		||||
 | 
			
		||||
#[model]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, CustomType)] // Added CustomType
 | 
			
		||||
pub struct Company {
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
@@ -51,55 +54,11 @@ pub struct Company {
 | 
			
		||||
    pub status: CompanyStatus,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Model Trait Implementation ---
 | 
			
		||||
 | 
			
		||||
impl Model for Company {
 | 
			
		||||
    fn db_prefix() -> &'static str {
 | 
			
		||||
        "company"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_id(&self) -> u32 {
 | 
			
		||||
        self.base_data.id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn base_data_mut(&mut self) -> &mut BaseModelData {
 | 
			
		||||
        &mut self.base_data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Override db_keys to provide custom indexes if needed
 | 
			
		||||
    fn db_keys(&self) -> Vec<IndexKey> {
 | 
			
		||||
        vec![
 | 
			
		||||
            IndexKeyBuilder::new("name").value(self.name.clone()).build(),
 | 
			
		||||
            IndexKeyBuilder::new("registration_number").value(self.registration_number.clone()).build(),
 | 
			
		||||
            // Add other relevant keys, e.g., by status or type if frequently queried
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Index Implementations (Example) ---
 | 
			
		||||
 | 
			
		||||
pub struct CompanyNameIndex;
 | 
			
		||||
impl Index for CompanyNameIndex {
 | 
			
		||||
    type Model = Company;
 | 
			
		||||
    type Key = str;
 | 
			
		||||
    fn key() -> &'static str {
 | 
			
		||||
        "name"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct CompanyRegistrationNumberIndex;
 | 
			
		||||
impl Index for CompanyRegistrationNumberIndex {
 | 
			
		||||
    type Model = Company;
 | 
			
		||||
    type Key = str;
 | 
			
		||||
    fn key() -> &'static str {
 | 
			
		||||
        "registration_number"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Builder Pattern ---
 | 
			
		||||
 | 
			
		||||
impl Company {
 | 
			
		||||
    pub fn new(name: String, registration_number: String, incorporation_date: i64) -> Self { // incorporation_date to i64
 | 
			
		||||
    pub fn new(name: String, registration_number: String, incorporation_date: i64) -> Self {
 | 
			
		||||
        // incorporation_date to i64
 | 
			
		||||
        Self {
 | 
			
		||||
            base_data: BaseModelData::new(),
 | 
			
		||||
            name,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,24 @@
 | 
			
		||||
// Sub-modules will be declared here
 | 
			
		||||
 | 
			
		||||
pub mod company;
 | 
			
		||||
pub mod payment;
 | 
			
		||||
pub mod product;
 | 
			
		||||
// pub mod sale;
 | 
			
		||||
// pub mod shareholder;
 | 
			
		||||
// pub mod user;
 | 
			
		||||
 | 
			
		||||
// Re-export main types from sub-modules
 | 
			
		||||
pub use company::{Company, CompanyStatus, BusinessType};
 | 
			
		||||
pub use company::{BusinessType, Company, CompanyStatus};
 | 
			
		||||
pub use payment::{Payment, PaymentStatus};
 | 
			
		||||
pub mod shareholder;
 | 
			
		||||
pub use product::{Product, ProductComponent, ProductStatus, ProductType};
 | 
			
		||||
pub use shareholder::{Shareholder, ShareholderType};
 | 
			
		||||
pub use product::{Product, ProductType, ProductStatus, ProductComponent};
 | 
			
		||||
 | 
			
		||||
pub mod sale;
 | 
			
		||||
pub use sale::{Sale, SaleItem, SaleStatus};
 | 
			
		||||
 | 
			
		||||
// pub use user::{User}; // Assuming a simple User model for now
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
#[cfg(feature = "rhai")]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								heromodels/src/models/biz/payment.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								heromodels/src/models/biz/payment.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
use heromodels_core::BaseModelData;
 | 
			
		||||
use heromodels_derive::model;
 | 
			
		||||
use rhai::{CustomType, TypeBuilder};
 | 
			
		||||
use serde::{Deserialize, Serialize}; // For #[derive(CustomType)]
 | 
			
		||||
 | 
			
		||||
// --- Enums ---
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 | 
			
		||||
pub enum PaymentStatus {
 | 
			
		||||
    Pending,
 | 
			
		||||
    Processing,
 | 
			
		||||
    Completed,
 | 
			
		||||
    Failed,
 | 
			
		||||
    Refunded,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for PaymentStatus {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            PaymentStatus::Pending => write!(f, "Pending"),
 | 
			
		||||
            PaymentStatus::Processing => write!(f, "Processing"),
 | 
			
		||||
            PaymentStatus::Completed => write!(f, "Completed"),
 | 
			
		||||
            PaymentStatus::Failed => write!(f, "Failed"),
 | 
			
		||||
            PaymentStatus::Refunded => write!(f, "Refunded"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PaymentStatus {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        PaymentStatus::Pending
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Payment Struct ---
 | 
			
		||||
#[model]
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
 | 
			
		||||
pub struct Payment {
 | 
			
		||||
    pub base_data: BaseModelData,
 | 
			
		||||
 | 
			
		||||
    // Stripe payment intent ID for tracking
 | 
			
		||||
    #[index]
 | 
			
		||||
    pub payment_intent_id: String,
 | 
			
		||||
 | 
			
		||||
    // Reference to the company this payment is for
 | 
			
		||||
    #[index]
 | 
			
		||||
    pub company_id: u32,
 | 
			
		||||
 | 
			
		||||
    // Payment plan details
 | 
			
		||||
    pub payment_plan: String, // "monthly", "yearly", "two_year"
 | 
			
		||||
    pub setup_fee: f64,
 | 
			
		||||
    pub monthly_fee: f64,
 | 
			
		||||
    pub total_amount: f64,
 | 
			
		||||
    pub currency: String, // "usd"
 | 
			
		||||
 | 
			
		||||
    pub status: PaymentStatus,
 | 
			
		||||
    pub stripe_customer_id: Option<String>,
 | 
			
		||||
    pub created_at: i64,           // Timestamp
 | 
			
		||||
    pub completed_at: Option<i64>, // Completion timestamp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Model trait implementation is automatically generated by #[model] attribute
 | 
			
		||||
 | 
			
		||||
// --- Builder Pattern ---
 | 
			
		||||
 | 
			
		||||
impl Payment {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        payment_intent_id: String,
 | 
			
		||||
        company_id: u32,
 | 
			
		||||
        payment_plan: String,
 | 
			
		||||
        setup_fee: f64,
 | 
			
		||||
        monthly_fee: f64,
 | 
			
		||||
        total_amount: f64,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let now = chrono::Utc::now().timestamp();
 | 
			
		||||
        Self {
 | 
			
		||||
            base_data: BaseModelData::new(),
 | 
			
		||||
            payment_intent_id,
 | 
			
		||||
            company_id,
 | 
			
		||||
            payment_plan,
 | 
			
		||||
            setup_fee,
 | 
			
		||||
            monthly_fee,
 | 
			
		||||
            total_amount,
 | 
			
		||||
            currency: "usd".to_string(), // Default to USD
 | 
			
		||||
            status: PaymentStatus::default(),
 | 
			
		||||
            stripe_customer_id: None,
 | 
			
		||||
            created_at: now,
 | 
			
		||||
            completed_at: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn payment_intent_id(mut self, payment_intent_id: String) -> Self {
 | 
			
		||||
        self.payment_intent_id = payment_intent_id;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn company_id(mut self, company_id: u32) -> Self {
 | 
			
		||||
        self.company_id = company_id;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn payment_plan(mut self, payment_plan: String) -> Self {
 | 
			
		||||
        self.payment_plan = payment_plan;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn setup_fee(mut self, setup_fee: f64) -> Self {
 | 
			
		||||
        self.setup_fee = setup_fee;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn monthly_fee(mut self, monthly_fee: f64) -> Self {
 | 
			
		||||
        self.monthly_fee = monthly_fee;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn total_amount(mut self, total_amount: f64) -> Self {
 | 
			
		||||
        self.total_amount = total_amount;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn status(mut self, status: PaymentStatus) -> Self {
 | 
			
		||||
        self.status = status;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn stripe_customer_id(mut self, stripe_customer_id: Option<String>) -> Self {
 | 
			
		||||
        self.stripe_customer_id = stripe_customer_id;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn currency(mut self, currency: String) -> Self {
 | 
			
		||||
        self.currency = currency;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn created_at(mut self, created_at: i64) -> Self {
 | 
			
		||||
        self.created_at = created_at;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn completed_at(mut self, completed_at: Option<i64>) -> Self {
 | 
			
		||||
        self.completed_at = completed_at;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // --- Business Logic Methods ---
 | 
			
		||||
 | 
			
		||||
    /// Complete the payment with optional Stripe customer ID
 | 
			
		||||
    pub fn complete_payment(mut self, stripe_customer_id: Option<String>) -> Self {
 | 
			
		||||
        self.status = PaymentStatus::Completed;
 | 
			
		||||
        self.stripe_customer_id = stripe_customer_id;
 | 
			
		||||
        self.completed_at = Some(chrono::Utc::now().timestamp());
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Mark payment as processing
 | 
			
		||||
    pub fn process_payment(mut self) -> Self {
 | 
			
		||||
        self.status = PaymentStatus::Processing;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Mark payment as failed
 | 
			
		||||
    pub fn fail_payment(mut self) -> Self {
 | 
			
		||||
        self.status = PaymentStatus::Failed;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Refund the payment
 | 
			
		||||
    pub fn refund_payment(mut self) -> Self {
 | 
			
		||||
        self.status = PaymentStatus::Refunded;
 | 
			
		||||
        self.base_data.update_modified();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if payment is completed
 | 
			
		||||
    pub fn is_completed(&self) -> bool {
 | 
			
		||||
        self.status == PaymentStatus::Completed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if payment is pending
 | 
			
		||||
    pub fn is_pending(&self) -> bool {
 | 
			
		||||
        self.status == PaymentStatus::Pending
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if payment is processing
 | 
			
		||||
    pub fn is_processing(&self) -> bool {
 | 
			
		||||
        self.status == PaymentStatus::Processing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if payment has failed
 | 
			
		||||
    pub fn has_failed(&self) -> bool {
 | 
			
		||||
        self.status == PaymentStatus::Failed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if payment is refunded
 | 
			
		||||
    pub fn is_refunded(&self) -> bool {
 | 
			
		||||
        self.status == PaymentStatus::Refunded
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Setter for base_data fields if needed directly
 | 
			
		||||
    pub fn set_base_created_at(mut self, created_at: i64) -> Self {
 | 
			
		||||
        self.base_data.created_at = created_at;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_base_modified_at(mut self, modified_at: i64) -> Self {
 | 
			
		||||
        self.base_data.modified_at = modified_at;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests for Payment model are located in tests/payment.rs
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -14,7 +14,7 @@ pub mod projects;
 | 
			
		||||
pub use core::Comment;
 | 
			
		||||
pub use userexample::User;
 | 
			
		||||
// pub use productexample::Product; // Temporarily remove
 | 
			
		||||
pub use biz::{Sale, SaleItem, SaleStatus};
 | 
			
		||||
pub use biz::{Payment, PaymentStatus, Sale, SaleItem, SaleStatus};
 | 
			
		||||
pub use calendar::{AttendanceStatus, Attendee, Calendar, Event};
 | 
			
		||||
pub use finance::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
 | 
			
		||||
pub use finance::{Account, Asset, AssetType};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user