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-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-08-30 12:13:56 +00:00
|
|
|
pub async fn is_expired(&self) -> bool {
|
|
|
|
let current_time = chrono::Utc::now().timestamp();
|
|
|
|
|
|
|
|
if let Some(expiry) = Item::get(&self.item)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.variant(&self.variant)
|
|
|
|
.unwrap()
|
|
|
|
.expiry
|
|
|
|
{
|
|
|
|
let date_added = self.timestamp;
|
|
|
|
|
|
|
|
let expiration_ts = expiry * 24 * 60 * 60;
|
|
|
|
|
2024-09-12 09:54:52 +00:00
|
|
|
return (date_added + expiration_ts) < current_time;
|
2024-08-30 12:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
2024-09-12 08:17:14 +00:00
|
|
|
|
|
|
|
pub async fn in_location(l: &str) -> Vec<Self> {
|
|
|
|
Self::find(doc! { "location": l}, None, None).await.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn in_location_recursive(l: &str) -> Vec<Self> {
|
2024-09-12 08:34:14 +00:00
|
|
|
let locations = Location::find(doc! { "parent": l}, None, None)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let mut transactions = Self::find(doc! { "location": l}, None, None).await.unwrap();
|
|
|
|
|
|
|
|
for loc in locations {
|
|
|
|
transactions.extend(
|
|
|
|
Self::find(doc! { "location": loc.id() }, None, None)
|
|
|
|
.await
|
|
|
|
.unwrap(),
|
2024-09-12 09:54:52 +00:00
|
|
|
);
|
2024-09-12 08:34:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
pub async fn active_expired() -> Vec<Self> {
|
|
|
|
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 {
|
|
|
|
if item.is_expired().await {
|
|
|
|
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-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-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(())
|
|
|
|
}
|
|
|
|
}
|