use serde::{Deserialize, Serialize};
use crate::model::adjustment::{AppliedAdjustment, PriceAdjustment};
use crate::model::currency::{Currency, CurrencyConverter};
use crate::model::markup::MarkupType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PricingDetail {
pub buy_price: f64,
pub sell_price: f64,
pub buy_currency: Currency,
pub sell_currency: Currency,
pub markup: Option<MarkupType>,
pub markup_value_in_buy_currency: Option<f64>,
pub markup_value_in_sell_currency: Option<f64>,
pub converted_buy_price: Option<f64>,
pub buy_currency_rate: Option<f64>,
pub sell_currency_rate: Option<f64>,
pub exchange_rate: Option<f64>,
pub applied_adjustments: Vec<AppliedAdjustment>,
}
impl PricingDetail {
pub fn new(buy_price: f64, buy_currency: Currency, sell_currency: Currency) -> Self {
Self {
buy_price,
sell_price: 0.0,
buy_currency,
sell_currency,
markup: None,
markup_value_in_buy_currency: None,
markup_value_in_sell_currency: None,
converted_buy_price: None,
buy_currency_rate: None,
sell_currency_rate: None,
exchange_rate: None,
applied_adjustments: vec![],
}
}
pub fn get_buy_price(&self) -> f64 {
self.buy_price
}
pub fn get_sell_price(&self) -> f64 {
self.sell_price
}
pub fn get_buy_currency(&self) -> &Currency {
&self.buy_currency
}
pub fn get_sell_currency(&self) -> &Currency {
&self.sell_currency
}
pub fn get_markup(&self) -> &Option<MarkupType> {
&self.markup
}
pub fn set_markup(&mut self, markup: MarkupType) {
self.markup = Some(markup);
}
pub fn get_buy_currency_rate(&self) -> Option<f64> {
self.buy_currency_rate
}
pub fn get_sell_currency_rate(&self) -> Option<f64> {
self.sell_currency_rate
}
pub fn get_exchange_rate(&self) -> Option<f64> {
match (self.buy_currency_rate, self.sell_currency_rate) {
(Some(buy), Some(sell)) => Some(sell / buy),
_ => None,
}
}
pub fn get_markup_value_in_buy_currency(&self) -> Option<f64> {
self.markup_value_in_buy_currency
}
pub fn get_markup_value_in_sell_currency(&self) -> Option<f64> {
self.markup_value_in_sell_currency
}
pub fn get_converted_buy_price(&self) -> Option<f64> {
self.converted_buy_price
}
pub fn get_markup_in_sell_currency(&self) -> Option<f64> {
self.markup_value_in_sell_currency
}
pub fn get_markup_in_buy_currency(&self) -> Option<f64> {
self.markup_value_in_buy_currency
}
pub fn apply_markup(&mut self, converter: &CurrencyConverter) {
let buy_rate = converter.get_exchange_rate(&self.buy_currency).unwrap_or(1.0);
let sell_rate = converter.get_exchange_rate(&self.sell_currency).unwrap_or(1.0);
self.buy_currency_rate = Some(buy_rate);
self.sell_currency_rate = Some(sell_rate);
self.exchange_rate = Some(sell_rate / buy_rate);
let markup_in_buy = match &self.markup {
Some(MarkupType::Amount { value, currency }) => {
converter.convert(*value, currency, &self.buy_currency)
},
Some(MarkupType::Percentage(pct)) => self.buy_price * pct / 100.0,
Some(MarkupType::Commission(pct)) => self.buy_price * pct / (100.0 - pct),
None => 0.0,
};
self.markup_value_in_buy_currency = Some(markup_in_buy);
let sell_base = self.buy_price + markup_in_buy;
self.converted_buy_price = Some(sell_base);
let converted = (sell_base / buy_rate) * sell_rate;
self.markup_value_in_sell_currency = Some((markup_in_buy / buy_rate) * sell_rate);
self.sell_price = converted;
}
pub fn apply_adjustments(
&mut self,
adjustments: &[PriceAdjustment],
converter: &CurrencyConverter,
) {
let mut final_price = self.sell_price;
self.applied_adjustments.clear();
for adj in adjustments {
let applied = match adj {
PriceAdjustment::Tax { name, percentage } => {
let amt = final_price * (percentage / 100.0);
final_price += amt;
AppliedAdjustment {
kind: "Tax".into(),
name: name.clone(),
percentage: Some(*percentage),
original_currency: Some(self.sell_currency.clone()),
original_amount: Some(amt),
applied_amount: amt,
}
}
PriceAdjustment::Discount { name, percentage } => {
let amt = final_price * (percentage / 100.0);
final_price -= amt;
AppliedAdjustment {
kind: "Discount".into(),
name: name.clone(),
percentage: Some(*percentage),
original_currency: Some(self.sell_currency.clone()),
original_amount: Some(amt),
applied_amount: -amt,
}
}
PriceAdjustment::Fixed { name, amount, currency } => {
let converted = converter.convert(*amount, currency, &self.sell_currency);
final_price += converted;
AppliedAdjustment {
kind: "Fixed".into(),
name: name.clone(),
percentage: None,
original_currency: Some(currency.clone()),
original_amount: Some(*amount),
applied_amount: converted,
}
}
};
self.applied_adjustments.push(applied);
}
self.sell_price = final_price;
}
}