This commit is contained in:
kristof 2025-04-04 10:59:51 +02:00
parent b37b9da8b5
commit 32f6d87454
35 changed files with 227 additions and 261 deletions

View File

@ -0,0 +1,207 @@
# Business Models
This directory contains the core business models used throughout the application for representing essential business objects like products, sales, and currency.
## Overview
The business models are implemented as Rust structs and enums with serialization/deserialization support via Serde. These models implement the `SledModel` and `Storable` traits for persistence in the application's database layer.
## Model Relationships
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Currency │◄────┤ Product │◄────┤ SaleItem │
└─────────────┘ └─────────────┘ └──────┬──────┘
▲ │
│ │
┌─────┴───────┐ │
│ProductComponent│ │
└─────────────┘ │
┌─────────────┐
│ Sale │
└─────────────┘
```
## Models
### Currency (Root Object)
Represents a monetary value with an amount and currency code.
**Properties:**
- `amount`: f64 - The monetary amount
- `currency_code`: String - The currency code (e.g., "USD", "EUR")
**Methods:**
- `new()` - Creates a new Currency instance
### Product
#### ProductType Enum
Categorizes products:
- `Product` - Physical product
- `Service` - Non-physical service
#### ProductStatus Enum
Tracks product availability:
- `Available` - Product can be purchased
- `Unavailable` - Product cannot be purchased
#### ProductComponent
Represents a component part of a product.
**Properties:**
- `id`: u32 - Unique identifier
- `name`: String - Component name
- `description`: String - Component description
- `quantity`: i32 - Number of this component
- `created_at`: DateTime<Utc> - Creation timestamp
- `updated_at`: DateTime<Utc> - Last update timestamp
**Methods:**
- `new()` - Creates a new ProductComponent with default timestamps
#### Product (Root Object)
Represents a product or service offered.
**Properties:**
- `id`: u32 - Unique identifier
- `name`: String - Product name
- `description`: String - Product description
- `price`: Currency - Product price
- `type_`: ProductType - Product or Service
- `category`: String - Product category
- `status`: ProductStatus - Available or Unavailable
- `created_at`: DateTime<Utc> - Creation timestamp
- `updated_at`: DateTime<Utc> - Last update timestamp
- `max_amount`: u16 - Maximum quantity available
- `purchase_till`: DateTime<Utc> - Deadline for purchasing
- `active_till`: DateTime<Utc> - When product/service expires
- `components`: Vec<ProductComponent> - List of product components
**Methods:**
- `new()` - Creates a new Product with default timestamps
- `add_component()` - Adds a component to this product
- `set_purchase_period()` - Updates purchase availability timeframe
- `set_active_period()` - Updates active timeframe
- `is_purchasable()` - Checks if product is available for purchase
- `is_active()` - Checks if product is still active
**Database Implementation:**
- Implements `Storable` trait for serialization
- Implements `SledModel` trait with:
- `get_id()` - Returns the ID as a string
- `db_prefix()` - Returns "product" as the database prefix
### Sale
#### SaleStatus Enum
Tracks the status of a sale:
- `Pending` - Sale is in progress
- `Completed` - Sale has been finalized
- `Cancelled` - Sale has been cancelled
#### SaleItem
Represents an item within a sale.
**Properties:**
- `id`: u32 - Unique identifier
- `sale_id`: u32 - Parent sale ID
- `product_id`: u32 - ID of the product sold
- `name`: String - Product name at time of sale
- `quantity`: i32 - Number of items purchased
- `unit_price`: Currency - Price per unit
- `subtotal`: Currency - Total price for this item (calculated)
- `active_till`: DateTime<Utc> - When item/service expires
**Methods:**
- `new()` - Creates a new SaleItem with calculated subtotal
#### Sale (Root Object)
Represents a complete sale transaction.
**Properties:**
- `id`: u32 - Unique identifier
- `company_id`: u32 - ID of the company making the sale
- `buyer_name`: String - Name of the buyer
- `buyer_email`: String - Email of the buyer
- `total_amount`: Currency - Total sale amount
- `status`: SaleStatus - Current sale status
- `sale_date`: DateTime<Utc> - When sale occurred
- `created_at`: DateTime<Utc> - Creation timestamp
- `updated_at`: DateTime<Utc> - Last update timestamp
- `items`: Vec<SaleItem> - List of items in the sale
**Methods:**
- `new()` - Creates a new Sale with default timestamps
- `add_item()` - Adds an item to the sale and updates total
- `update_status()` - Updates the status of the sale
**Database Implementation:**
- Implements `Storable` trait for serialization
- Implements `SledModel` trait with:
- `get_id()` - Returns the ID as a string
- `db_prefix()` - Returns "sale" as the database prefix
## Usage Examples
### Creating a Product
```rust
// Create a currency
let price = Currency::new(29.99, "USD".to_string());
// Create a product
let mut product = Product::new(
1, // id
"Premium Service".to_string(), // name
"Our premium service offering".to_string(), // description
price, // price
ProductType::Service, // type
"Services".to_string(), // category
ProductStatus::Available, // status
100, // max_amount
30, // validity_days (service valid for 30 days)
);
// Add a component
let component = ProductComponent::new(
1, // id
"Basic Support".to_string(), // name
"24/7 email support".to_string(), // description
1, // quantity
);
product.add_component(component);
```
### Creating a Sale
```rust
// Create a new sale
let mut sale = Sale::new(
1, // id
101, // company_id
"John Doe".to_string(), // buyer_name
"john.doe@example.com".to_string(), // buyer_email
"USD".to_string(), // currency_code
SaleStatus::Pending, // status
);
// Create a sale item
let now = Utc::now();
let item = SaleItem::new(
1, // id
1, // sale_id
1, // product_id
"Premium Service".to_string(), // name
1, // quantity
Currency::new(29.99, "USD".to_string()), // unit_price
now + Duration::days(30), // active_till
);
// Add the item to the sale
sale.add_item(item);
// Complete the sale
sale.update_status(SaleStatus::Completed);

View File

@ -0,0 +1,20 @@
use chrono::{DateTime, Utc, Duration};
use serde::{Deserialize, Serialize};
use crate::core::{SledModel, Storable}; // Import Sled traits from new location
/// Currency represents a monetary value with amount and currency code
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Currency {
pub amount: f64,
pub currency_code: String,
}
impl Currency {
/// Create a new currency with amount and code
pub fn new(amount: f64, currency_code: String) -> Self {
Self {
amount,
currency_code,
}
}
}

View File

@ -2,22 +2,6 @@ use chrono::{DateTime, Utc, Duration};
use serde::{Deserialize, Serialize};
use crate::core::{SledModel, Storable}; // Import Sled traits from new location
/// Currency represents a monetary value with amount and currency code
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Currency {
pub amount: f64,
pub currency_code: String,
}
impl Currency {
/// Create a new currency with amount and code
pub fn new(amount: f64, currency_code: String) -> Self {
Self {
amount,
currency_code,
}
}
}
/// ProductType represents the type of a product
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,199 +0,0 @@
//! Examples demonstrating how to use the new DB implementation
// Core DB is now imported via the factory
use crate::zaz::models::*;
use crate::zaz::models::shareholder::ShareholderType;
use crate::zaz::factory::create_zaz_db;
use crate::zaz::rhai::{initialize_rhai_engine, run_script_file, run_example_script};
use rhai::{Engine, EvalAltResult};
use std::path::PathBuf;
use std::fs;
use chrono::Utc;
/// Creates a simple temporary directory
fn create_temp_dir() -> std::io::Result<PathBuf> {
let temp_dir = std::env::temp_dir();
let random_name = format!("db-example-{}", std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis());
let path = temp_dir.join(random_name);
fs::create_dir_all(&path)?;
Ok(path)
}
/// Example demonstrating basic CRUD operations with the DB
pub fn run_db_examples() -> Result<(), String> {
println!("Running DB examples...");
// Create a temporary directory for the DB (or use a permanent one)
let db_path = create_temp_dir().map_err(|e| format!("Failed to create temp dir: {}", e))?;
println!("Using DB path: {:?}", db_path);
// Create a DB instance
let db = create_zaz_db(db_path).map_err(|e| format!("Failed to create DB: {}", e))?;
// --- User Example ---
println!("\nRunning User example:");
let user = User::new(
1,
"John Doe".to_string(),
"john@example.com".to_string(),
"password123".to_string(),
"ACME Corp".to_string(),
"Admin".to_string(),
);
// Insert user
db.set(&user).map_err(|e| format!("Failed to insert user: {}", e))?;
println!("Inserted user: {}", user.name);
// Get user
let retrieved_user: User = db.get(&user.id.to_string())
.map_err(|e| format!("Failed to get user: {}", e))?;
println!("Retrieved user: {} ({})", retrieved_user.name, retrieved_user.email);
// --- Company Example ---
println!("\nRunning Company example:");
let company = Company::new(
1,
"ACME Corporation".to_string(),
"REG12345".to_string(),
Utc::now(),
"12-31".to_string(),
"info@acme.com".to_string(),
"555-123-4567".to_string(),
"www.acme.com".to_string(),
"123 Main St, Metropolis".to_string(),
BusinessType::Global,
"Technology".to_string(),
"A leading technology company".to_string(),
CompanyStatus::Active,
);
// Insert company
db.set(&company).map_err(|e| format!("Failed to insert company: {}", e))?;
println!("Inserted company: {}", company.name);
// Get company
let retrieved_company: Company = db.get(&company.id.to_string())
.map_err(|e| format!("Failed to get company: {}", e))?;
println!("Retrieved company: {} ({})", retrieved_company.name, retrieved_company.registration_number);
// --- Shareholder Example ---
println!("\nRunning Shareholder example:");
// Create the shareholder directly
let shareholder = Shareholder {
id: 1,
company_id: company.id,
user_id: user.id,
name: "John Doe".to_string(),
shares: 1000.0,
percentage: 25.0,
type_: ShareholderType::Individual, // Use the shared enum via re-export
since: Utc::now(),
created_at: Utc::now(),
updated_at: Utc::now(),
};
// Insert shareholder
db.set(&shareholder).map_err(|e| format!("Failed to insert shareholder: {}", e))?;
println!("Inserted shareholder: {} ({}%)", shareholder.name, shareholder.percentage);
// Get shareholder
let retrieved_shareholder: Shareholder = db.get(&shareholder.id.to_string())
.map_err(|e| format!("Failed to get shareholder: {}", e))?;
println!("Retrieved shareholder: {} ({} shares)", retrieved_shareholder.name, retrieved_shareholder.shares);
// --- List Example ---
println!("\nListing all entities:");
let users: Vec<User> = db.list().map_err(|e| format!("Failed to list users: {}", e))?;
println!("Found {} users", users.len());
for user in &users {
println!("- User: {}", user.name);
}
let companies: Vec<Company> = db.list().map_err(|e| format!("Failed to list companies: {}", e))?;
println!("Found {} companies", companies.len());
for company in &companies {
println!("- Company: {}", company.name);
}
let shareholders: Vec<Shareholder> = db.list()
.map_err(|e| format!("Failed to list shareholders: {}", e))?;
println!("Found {} shareholders", shareholders.len());
for shareholder in &shareholders {
println!("- Shareholder: {} ({}%)", shareholder.name, shareholder.percentage);
}
// --- Delete Example ---
println!("\nDeleting entities:");
// Delete shareholder
db.delete::<Shareholder>(&shareholder.id.to_string())
.map_err(|e| format!("Failed to delete shareholder: {}", e))?;
println!("Deleted shareholder: {}", shareholder.name);
// Delete company
db.delete::<Company>(&company.id.to_string())
.map_err(|e| format!("Failed to delete company: {}", e))?;
println!("Deleted company: {}", company.name);
// Delete user
db.delete::<User>(&user.id.to_string())
.map_err(|e| format!("Failed to delete user: {}", e))?;
println!("Deleted user: {}", user.name);
// Verify deletion
let users_after_delete: Vec<User> = db.list()
.map_err(|e| format!("Failed to list users after delete: {}", e))?;
println!("Users remaining: {}", users_after_delete.len());
let companies_after_delete: Vec<Company> = db.list()
.map_err(|e| format!("Failed to list companies after delete: {}", e))?;
println!("Companies remaining: {}", companies_after_delete.len());
let shareholders_after_delete: Vec<Shareholder> = db.list()
.map_err(|e| format!("Failed to list shareholders after delete: {}", e))?;
println!("Shareholders remaining: {}", shareholders_after_delete.len());
println!("\nDB examples completed successfully!");
Ok(())
}
/// Example demonstrating how to use Rhai scripts with models
pub fn run_rhai_examples() -> Result<(), String> {
println!("Running Rhai examples...");
// Run the test script that demonstrates using the model wrappers
// without database operations
let script_path = "src/zaz/rhai/test.rhai";
run_script_file(script_path)?;
// Run an example that demonstrates creating model objects
let temp_dir = create_temp_dir().map_err(|e| format!("Failed to create temp dir: {}", e))?;
println!("Using DB path for Rhai example: {:?}", temp_dir);
// Run the example script that uses the DB
run_example_script(temp_dir.to_str().unwrap())?;
println!("Rhai examples completed successfully!");
Ok(())
}
/// Run a complete demonstration of Rhai integration with our models
pub fn run_rhai_demo() -> Result<(), String> {
println!("=====================================================");
println!("Starting Rhai demos for Zaz DB");
println!("=====================================================");
// Run the Rhai examples
run_rhai_examples()?;
println!("=====================================================");
println!("Rhai demo completed");
println!("=====================================================");
Ok(())
}

View File

@ -1,3 +0,0 @@
//! Command line examples and utilities for the zaz module
pub mod examples;

View File

@ -1,22 +0,0 @@
// Declare the models submodule
#[path = "models/lib.rs"] // Tell compiler where to find models module source
pub mod models;
// Add a factory module for DB creation
mod factory;
pub use factory::create_zaz_db;
// Expose the rhai module with wrappers
pub mod rhai;
pub use rhai::initialize_rhai_engine;
// Declare the examples module for the new DB implementation
#[path = "examples.rs"] // Tell compiler where to find the examples module
pub mod examples;
// Expose the cmd module
pub mod cmd;
// Include tests module when running tests
#[cfg(test)]
pub mod tests;

View File

@ -1,13 +0,0 @@
// Export all rhai wrapper modules
mod engine;
mod user;
mod company;
mod shareholder;
// Re-export key components
pub use engine::{create_rhai_engine, run_script_file, run_example_script};
// Public API
pub fn initialize_rhai_engine() -> rhai::Engine {
create_rhai_engine()
}

View File

@ -1,8 +0,0 @@
//! Simple binary to demonstrate Rhai wrapper functionality
use crate::zaz::cmd::examples::run_rhai_demo;
fn main() -> Result<(), String> {
println!("Running Rhai wrapper examples...");
run_rhai_demo()
}