...
This commit is contained in:
parent
b37b9da8b5
commit
32f6d87454
207
herodb/src/models/biz/README.md
Normal file
207
herodb/src/models/biz/README.md
Normal 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);
|
20
herodb/src/models/biz/currency.rs
Normal file
20
herodb/src/models/biz/currency.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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)]
|
@ -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(())
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
//! Command line examples and utilities for the zaz module
|
||||
|
||||
pub mod examples;
|
@ -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;
|
@ -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()
|
||||
}
|
@ -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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user