cdb/src/transaction.rs

277 lines
7.4 KiB
Rust
Raw Normal View History

2024-09-12 08:58:49 +00:00
use futures::StreamExt;
2024-08-13 02:13:38 +00:00
use mongod::{
2024-09-02 16:40:02 +00:00
assert_reference_of,
2024-08-13 02:13:38 +00:00
derive::{Model, Referencable},
2024-09-12 08:58:49 +00:00
reference_of, Model, Referencable, Reference, Sort, Validate,
2024-08-13 02:13:38 +00:00
};
2024-06-22 00:05:22 +00:00
use mongodb::bson::doc;
2024-07-24 14:38:56 +00:00
use serde::{Deserialize, Serialize};
2024-06-22 00:05:22 +00:00
use serde_json::json;
2024-09-02 16:40:02 +00:00
use crate::{item::Item, location::Location};
2024-08-30 12:13:56 +00:00
2024-09-25 06:38:12 +00:00
// todo : produced / consumed by flow field?
2024-08-28 07:14:33 +00:00
/// A Transaction of an Item Variant
2024-07-24 14:38:56 +00:00
#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)]
pub struct Transaction {
2024-08-28 07:14:33 +00:00
/// UUID
2024-07-24 14:38:56 +00:00
pub _id: String,
2024-08-28 07:14:33 +00:00
/// Associated Item
2024-07-24 14:38:56 +00:00
pub item: String,
2024-08-28 07:14:33 +00:00
/// Associated Variant
2024-07-24 14:38:56 +00:00
pub variant: String,
2024-08-28 07:14:33 +00:00
/// Price of obtaining the Item
2024-07-24 14:38:56 +00:00
pub price: Price,
2024-08-28 07:14:33 +00:00
/// Origin of the Item
2024-07-24 14:38:56 +00:00
pub origin: Option<String>,
2024-09-02 16:40:02 +00:00
/// The location of the Item
pub location: Option<Reference>,
2024-08-28 07:14:33 +00:00
/// Info on consumption of the Item
2024-07-24 14:38:56 +00:00
pub consumed: Option<Consumed>,
2024-09-20 23:38:22 +00:00
/// Notes on Transaction
pub note: Option<String>,
2024-08-28 07:14:33 +00:00
/// Timestamp of the Transaction
2024-07-24 14:38:56 +00:00
pub timestamp: i64,
2024-05-03 16:22:59 +00:00
}
2024-07-24 14:38:56 +00:00
impl Validate for Transaction {
async fn validate(&self) -> Result<(), String> {
2024-09-02 16:40:02 +00:00
if let Some(location) = &self.location {
assert_reference_of!(location, Location);
}
2024-07-24 14:38:56 +00:00
Ok(())
2024-05-03 16:22:59 +00:00
}
}
2024-08-28 07:14:33 +00:00
/// Information about consumed Items
2024-07-24 14:38:56 +00:00
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Consumed {
2024-08-28 07:14:33 +00:00
/// Destination of the Item or who consumed it
2024-07-24 14:38:56 +00:00
pub destination: String,
2024-08-28 07:14:33 +00:00
/// Price the Item was exported or consumed at
2024-05-03 16:22:59 +00:00
pub price: Price,
2024-08-28 07:14:33 +00:00
/// Timestamp of Consumption
pub timestamp: i64,
2024-05-03 16:22:59 +00:00
}
impl Transaction {
2024-09-02 16:40:02 +00:00
pub async fn new(
item: &str,
variant: &str,
price: Price,
origin: Option<&str>,
location: Option<&str>,
2024-09-20 23:38:22 +00:00
note: Option<&str>,
2024-09-02 16:40:02 +00:00
) -> Self {
2024-05-03 16:22:59 +00:00
Self {
2024-07-24 14:38:56 +00:00
_id: uuid::Uuid::new_v4().to_string(),
2024-05-03 16:22:59 +00:00
item: item.to_string(),
variant: variant.to_string(),
price,
2024-07-24 14:38:56 +00:00
consumed: None,
2024-08-28 07:38:10 +00:00
origin: origin.map(std::string::ToString::to_string),
2024-09-02 16:40:02 +00:00
location: if let Some(location) = location {
2024-09-02 17:10:22 +00:00
reference_of!(Location, location)
2024-09-02 16:40:02 +00:00
} else {
None
},
2024-09-20 23:38:22 +00:00
note: note.map(|x| x.to_string()),
2024-05-03 16:22:59 +00:00
timestamp: chrono::Utc::now().timestamp(),
}
}
2024-08-28 07:14:33 +00:00
/// Consumes the Item with `price` and `destination`
2024-09-12 09:48:09 +00:00
pub async fn consume(self, price: Price, destination: &str) -> Self {
2024-09-12 08:17:14 +00:00
self.change()
.consumed(Some(Consumed {
destination: destination.to_string(),
price,
timestamp: chrono::Utc::now().timestamp(),
}))
.update()
.await
2024-09-12 09:48:09 +00:00
.unwrap()
2024-08-28 07:14:33 +00:00
}
2024-09-26 13:55:48 +00:00
pub async fn is_expired_at(&self, time: i64) -> bool {
2024-08-30 12:13:56 +00:00
if let Some(expiry) = Item::get(&self.item)
2024-09-27 07:19:32 +00:00
.await
.unwrap()
.variant(&self.variant)
.unwrap()
.expiry
{
let date_added = self.timestamp;
2024-08-30 12:13:56 +00:00
2024-09-27 07:19:32 +00:00
let expiration_ts = expiry * 24 * 60 * 60;
2024-08-30 12:13:56 +00:00
2024-09-27 07:19:32 +00:00
return (date_added + expiration_ts) < time;
}
2024-08-30 12:13:56 +00:00
2024-09-27 07:19:32 +00:00
false
2024-09-26 13:55:48 +00:00
}
pub async fn is_expired_in_days(&self, days: i64) -> bool {
let current_time = chrono::Utc::now().timestamp();
2024-09-27 07:19:32 +00:00
self.is_expired_at(current_time + (days * 24 * 60 * 60))
.await
2024-09-26 13:55:48 +00:00
}
pub async fn is_expired(&self) -> bool {
2024-09-27 07:19:32 +00:00
if self.consumed.is_some() {
if let Some(expiry) = Item::get(&self.item)
.await
.unwrap()
.variant(&self.variant)
.unwrap()
.expiry
{
let time_around = self.timestamp - self.consumed.as_ref().unwrap().timestamp;
let expiration_ts = expiry * 24 * 60 * 60;
return time_around > expiration_ts;
} else {
return false;
}
}
2024-09-26 13:55:48 +00:00
let current_time = chrono::Utc::now().timestamp();
self.is_expired_at(current_time).await
2024-08-30 12:13:56 +00:00
}
2024-09-12 08:17:14 +00:00
2024-09-21 17:18:08 +00:00
pub async fn in_location(l: &str) -> Option<Vec<Self>> {
let l = reference_of!(Location, l)?;
2024-09-22 00:38:03 +00:00
Some(
Self::find(
doc! { "location": l, "consumed": { "$not": { "$type": "object" } }},
None,
None,
)
.await
.unwrap(),
)
2024-09-12 08:17:14 +00:00
}
2024-09-21 17:18:08 +00:00
pub async fn in_location_recursive(l: &str) -> Option<Vec<Self>> {
2024-09-22 00:38:03 +00:00
// get the children of this location
let locations = Location::get(l).await?.children_recursive().await;
2024-09-21 17:18:08 +00:00
let l = reference_of!(Location, l)?;
2024-09-22 00:38:03 +00:00
let mut transactions = Self::find(
doc! { "location": l, "consumed": { "$not": { "$type": "object" } },},
None,
None,
)
.await
.unwrap();
2024-09-12 08:34:14 +00:00
for loc in locations {
transactions.extend(
2024-09-22 00:38:03 +00:00
Self::find(doc! { "location": loc.reference(), "consumed": { "$not": { "$type": "object" } }}, None, None)
2024-09-12 08:34:14 +00:00
.await
.unwrap(),
2024-09-12 09:54:52 +00:00
);
2024-09-12 08:34:14 +00:00
}
2024-09-21 17:18:08 +00:00
Some(transactions)
2024-09-12 08:17:14 +00:00
}
2024-09-12 08:58:49 +00:00
/// Get all Transactions which are not consumed and are expired
2024-09-26 13:55:48 +00:00
pub async fn active_expired(days: Option<i64>) -> Vec<Self> {
2024-09-12 08:58:49 +00:00
let items = Self::find(
doc! {
"consumed": { "$not": { "$type": "object" } }
},
None,
Some(doc! { "timestamp": Sort::Descending }),
)
.await
.unwrap();
let expired_items: Vec<_> = futures::stream::iter(items)
.filter_map(|item| async move {
2024-09-26 13:55:48 +00:00
let expired = if let Some(days) = days {
item.is_expired_in_days(days).await
} else {
item.is_expired().await
};
if expired {
2024-09-12 08:58:49 +00:00
Some(item)
} else {
None
}
})
.collect()
.await;
expired_items
}
2024-08-30 12:13:56 +00:00
}
impl mongod::ToAPI for Transaction {
async fn api(&self) -> serde_json::Value {
2024-09-23 18:07:08 +00:00
let location = if let Some(loc) = &self.location {
Some(loc.get::<Location>().await.api().await)
} else {
None
};
2024-06-22 00:05:22 +00:00
json!({
2024-07-24 14:38:56 +00:00
"uuid": self._id,
2024-06-22 00:05:22 +00:00
"item": self.item,
"variant": self.variant,
"price": self.price,
"origin": self.origin,
2024-09-23 18:07:08 +00:00
"location": location,
2024-08-28 07:14:33 +00:00
"timestamp": self.timestamp,
2024-08-30 12:13:56 +00:00
"consumed": self.consumed,
2024-09-20 23:38:22 +00:00
"note": self.note,
2024-08-30 12:13:56 +00:00
"expired": self.is_expired().await
2024-06-22 00:05:22 +00:00
})
}
2024-06-21 19:14:45 +00:00
}
2024-05-03 16:22:59 +00:00
2024-08-28 07:14:33 +00:00
/// Economic Price
2024-07-24 14:38:56 +00:00
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2024-05-03 16:22:59 +00:00
pub struct Price {
2024-08-28 07:14:33 +00:00
/// Value of the currency
2024-05-03 16:22:59 +00:00
pub value: f64,
2024-08-28 07:14:33 +00:00
/// Kind of currency
2024-05-03 16:22:59 +00:00
pub currency: String,
}
impl Price {
pub fn new(value: f64, currency: &str) -> Self {
Self {
value,
currency: currency.to_string(),
}
}
2024-09-12 14:55:36 +00:00
pub fn zero() -> Self {
Self {
value: 0.00,
currency: String::new(),
}
}
2024-05-03 16:22:59 +00:00
fn parse(price: &str) -> Option<Self> {
let (value, currency) = price.split_once(' ')?;
Some(Self {
value: value.parse().ok()?,
currency: currency.to_string(),
})
}
2024-06-21 19:14:45 +00:00
}
2024-05-03 16:22:59 +00:00
impl TryFrom<String> for Price {
type Error = ();
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::parse(&value).ok_or(())
}
}