♻️ refactor + location conditions
This commit is contained in:
parent
ce9cdc4256
commit
3076ebe6a0
19 changed files with 1228 additions and 520 deletions
1429
Cargo.lock
generated
1429
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,13 @@
|
|||
[package]
|
||||
name = "cdb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.30"
|
||||
log = "0.4.20"
|
||||
mdq = { git = "https://git.hydrar.de/mdtools/mdq" }
|
||||
based = { git = "https://git.hydrar.de/jmarya/based" }
|
||||
rocket = { version = "0.5.1", features = ["json"] }
|
||||
rocket_cors = "0.6.0"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# Only these tokens are authorized to make requests
|
||||
allowed_tokens = ["password"]
|
||||
|
||||
home_assistant = "TOKEN"
|
||||
|
||||
# Webhook Notifications
|
||||
[webhook]
|
||||
# Item Variants inventory goes below the minimum
|
||||
|
|
12
docs/Item.md
12
docs/Item.md
|
@ -75,3 +75,15 @@ variants:
|
|||
name: "Regular Water"
|
||||
barcodes: [12345678]
|
||||
```
|
||||
|
||||
### Need Conditions
|
||||
Some items prefer or need to be stored under some conditions. These conditions can be configured and warn you when locations dont match the required conditions.
|
||||
|
||||
```yml
|
||||
name: "Water"
|
||||
variants:
|
||||
regular:
|
||||
name: "Regular Water"
|
||||
needs:
|
||||
temperature: [5.0, 10.0]
|
||||
```
|
||||
|
|
|
@ -50,7 +50,20 @@ Example: Freezer
|
|||
{
|
||||
"name": "Freezer",
|
||||
"conditions": {
|
||||
"temperature": -10
|
||||
"temperature": -10.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Fetch temperature from Home Assistant
|
||||
You can fetch the temperature from a sensor via Home Assistant.
|
||||
You need to setup a API Token for Home Assistant. After that you can enter the sensors API route in the location definition:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Freezer",
|
||||
"conditions": {
|
||||
"temperature_sensor": "https://homeassistant.local/api/states/sensor.freezer_temperature"
|
||||
}
|
||||
}
|
||||
```
|
|
@ -6,6 +6,8 @@ pub struct Config {
|
|||
pub allowed_tokens: Vec<String>,
|
||||
/// Webhook Config
|
||||
pub webhook: Option<Webhook>,
|
||||
/// Home Assistant Token
|
||||
pub home_assistant: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_config() -> Config {
|
||||
|
|
|
@ -2,9 +2,10 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::routes::item::{item_does_not_exist_error, variant_does_not_exist_error, SupplyForm};
|
||||
use crate::routes::{ApiError, ToAPI};
|
||||
use crate::routes::ApiError;
|
||||
use crate::routes::item::{SupplyForm, item_does_not_exist_error, variant_does_not_exist_error};
|
||||
use crate::{get_itemdb, get_pg};
|
||||
use based::request::api::ToAPI;
|
||||
use sqlx::FromRow;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{get_locations, routes::ToAPI};
|
||||
use crate::{config::get_config, get_locations};
|
||||
use based::request::api::ToAPI;
|
||||
|
||||
/// A Storage Location
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -17,10 +18,31 @@ pub struct Location {
|
|||
pub conditions: Option<StorageConditions>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct StorageConditions {
|
||||
/// Median temperature
|
||||
pub temperature: i64,
|
||||
pub temperature: f64,
|
||||
/// Accurate temperature sensor reading
|
||||
pub temperature_sensor: Option<String>,
|
||||
}
|
||||
|
||||
impl StorageConditions {
|
||||
/// Get a accurate temperature from a sensor endpoint
|
||||
pub async fn accurate_temperature(&self) -> Option<f64> {
|
||||
let conf = get_config();
|
||||
let client = reqwest::Client::new();
|
||||
let res: serde_json::Value = client
|
||||
.get(self.temperature_sensor.clone()?)
|
||||
.bearer_auth(conf.home_assistant?)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
res.as_object()?.get("state")?.as_f64()
|
||||
}
|
||||
}
|
||||
|
||||
impl Location {
|
||||
|
|
|
@ -167,6 +167,7 @@ async fn rocket() -> _ {
|
|||
routes::flow::end_flow_route,
|
||||
routes::flow::continue_flow_route,
|
||||
routes::flow::create_flow_route,
|
||||
routes::item::location_condition_warn,
|
||||
routes::item::move_transaction_route,
|
||||
routes::item::variant_price_latest_by_origin,
|
||||
routes::item::item_stat_route,
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use rocket::{get, post, serde::json::Json, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
check_auth,
|
||||
config::Config,
|
||||
flow::{Flow, FlowInfo, FlowNote},
|
||||
get_pg,
|
||||
json_store::JSONStore,
|
||||
routes::{api_error, vec_to_api, FallibleApiResponse, ToAPI, Token},
|
||||
routes::{FallibleApiResponse, Token, api_error},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use based::request::api::{ToAPI, vec_to_api};
|
||||
use rocket::{State, get, post, serde::json::Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use super::{item::SupplyForm, ApiError};
|
||||
use super::{ApiError, item::SupplyForm};
|
||||
|
||||
#[get("/flow/<id>/info")]
|
||||
pub async fn flow_info(
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{get, post, State};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::config::{Config, Webhook};
|
||||
use crate::routes::{ToAPI, Token};
|
||||
use crate::routes::Token;
|
||||
use crate::variant::Variant;
|
||||
use crate::{check_auth, get_itemdb};
|
||||
use crate::{
|
||||
db::ItemDB,
|
||||
routes::{api_error, FallibleApiResponse},
|
||||
routes::{FallibleApiResponse, api_error},
|
||||
};
|
||||
use based::request::api::ToAPI;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{State, get, post};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
||||
use super::{item_does_not_exist_error, variant_does_not_exist_error};
|
||||
|
||||
|
@ -23,8 +23,8 @@ pub struct DemandForm {
|
|||
price: f64,
|
||||
}
|
||||
|
||||
/// Consumes a Transaction with Price and Destination
|
||||
#[post("/demand", data = "<f>")]
|
||||
/// Consumes a Transaction with Price and Destination
|
||||
pub async fn demand_route(f: Json<DemandForm>, t: Token, c: &State<Config>) -> FallibleApiResponse {
|
||||
check_auth!(t, c);
|
||||
|
||||
|
@ -66,8 +66,8 @@ pub async fn demand_route(f: Json<DemandForm>, t: Token, c: &State<Config>) -> F
|
|||
Ok(json!({"ok": 1}))
|
||||
}
|
||||
|
||||
/// Returns all consumed transactions for Item Variant
|
||||
#[get("/item/<item_id>/<variant_id>/demand?<destination>")]
|
||||
/// Returns all consumed transactions for Item Variant
|
||||
pub async fn demand_log_route(
|
||||
item_id: &str,
|
||||
variant_id: &str,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::routes::{api_error, ApiError};
|
||||
use crate::routes::{ApiError, api_error};
|
||||
|
||||
pub fn item_does_not_exist_error() -> ApiError {
|
||||
api_error("The item does not exist")
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use rocket::{get, State};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
check_auth,
|
||||
config::Config,
|
||||
json_store::JSONStore,
|
||||
location::Location,
|
||||
routes::{api_error, vec_to_api, FallibleApiResponse, ToAPI, Token},
|
||||
routes::{FallibleApiResponse, Token, api_error},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use based::request::api::{ToAPI, vec_to_api};
|
||||
use rocket::{State, get};
|
||||
use serde_json::json;
|
||||
|
||||
#[get("/location/<id>")]
|
||||
pub async fn location_info(
|
||||
|
@ -27,6 +27,42 @@ pub async fn location_info(
|
|||
Ok(loc.api().await)
|
||||
}
|
||||
|
||||
#[get("/location/<id>/warn")]
|
||||
/// API route returning affected items on a condition mismatch
|
||||
pub async fn location_condition_warn(
|
||||
id: &str,
|
||||
locations: &State<JSONStore<Location>>,
|
||||
t: Token,
|
||||
c: &State<Config>,
|
||||
) -> FallibleApiResponse {
|
||||
check_auth!(t, c);
|
||||
|
||||
let loc = locations
|
||||
.get(id)
|
||||
.ok_or_else(|| api_error("No location with that ID"))?;
|
||||
|
||||
let transactions = Transaction::in_location(&loc.id).await;
|
||||
|
||||
let mut ret = Vec::new();
|
||||
|
||||
for t in transactions {
|
||||
if let Some(item) = t.item_variant().await {
|
||||
if !item
|
||||
.satisfy_condition(
|
||||
loc.conditions
|
||||
.as_ref()
|
||||
.ok_or(api_error("Location has no conditions"))?,
|
||||
)
|
||||
.await
|
||||
{
|
||||
ret.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(json!(vec_to_api(&ret).await))
|
||||
}
|
||||
|
||||
/// Resolve locations children recursively
|
||||
async fn location_api_resolve(
|
||||
id: &str,
|
||||
|
|
|
@ -6,6 +6,7 @@ mod supply;
|
|||
|
||||
use std::str::FromStr;
|
||||
|
||||
use based::request::api::ToAPI;
|
||||
pub use demand::*;
|
||||
pub use error::*;
|
||||
pub use location::*;
|
||||
|
@ -17,25 +18,24 @@ use serde::Serialize;
|
|||
pub use stat::*;
|
||||
pub use supply::*;
|
||||
|
||||
use rocket::get;
|
||||
use rocket::State;
|
||||
use rocket::get;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::check_auth;
|
||||
use crate::config::Config;
|
||||
use crate::db::get_items_without_min_satisfied;
|
||||
use crate::db::ItemDB;
|
||||
use crate::db::get_items_without_min_satisfied;
|
||||
use crate::get_locations;
|
||||
use crate::get_pg;
|
||||
use crate::routes::ToAPI;
|
||||
use crate::routes::Token;
|
||||
use crate::transaction::Transaction;
|
||||
|
||||
use super::api_error;
|
||||
use crate::routes::FallibleApiResponse;
|
||||
|
||||
/// Returns a JSON response with all items in the database.
|
||||
#[get("/items")]
|
||||
/// Returns a JSON response with all items in the database.
|
||||
pub fn get_items_route(itemdb: &State<ItemDB>, t: Token, c: &State<Config>) -> FallibleApiResponse {
|
||||
check_auth!(t, c);
|
||||
|
||||
|
@ -47,8 +47,8 @@ pub fn get_items_route(itemdb: &State<ItemDB>, t: Token, c: &State<Config>) -> F
|
|||
Ok(json!({"items": items}))
|
||||
}
|
||||
|
||||
/// Return an API Response for an `Item`
|
||||
#[get("/item/<item_id>")]
|
||||
/// Return an API Response for an `Item`
|
||||
pub fn item_route(
|
||||
item_id: &str,
|
||||
itemdb: &State<ItemDB>,
|
||||
|
@ -74,8 +74,8 @@ pub async fn item_image_route(item_id: &str, itemdb: &State<ItemDB>) -> Option<N
|
|||
None
|
||||
}
|
||||
|
||||
/// Returns all variants of an Item
|
||||
#[get("/item/<item_id>/variants")]
|
||||
/// Returns all variants of an Item
|
||||
pub fn item_variants_page(
|
||||
item_id: &str,
|
||||
itemdb: &State<ItemDB>,
|
||||
|
@ -112,8 +112,8 @@ pub async fn transaction_route(
|
|||
Ok(t.api().await)
|
||||
}
|
||||
|
||||
/// Returns unique values for a field
|
||||
#[get("/item/<item_id>/<variant_id>/unique?<field>")]
|
||||
/// Returns unique values for a field
|
||||
pub async fn unique_field_route(
|
||||
item_id: &str,
|
||||
variant_id: &str,
|
||||
|
@ -131,23 +131,28 @@ pub async fn unique_field_route(
|
|||
.ok_or_else(variant_does_not_exist_error)?;
|
||||
|
||||
match field {
|
||||
"origin" => Ok(json!(variant
|
||||
.get_unique_origins()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|x| !x.starts_with("flow::"))
|
||||
.collect::<Vec<_>>())),
|
||||
"destination" => Ok(json!(variant
|
||||
.get_unique_destinations()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|x| !x.starts_with("flow::"))
|
||||
.collect::<Vec<_>>())),
|
||||
"origin" => Ok(json!(
|
||||
variant
|
||||
.get_unique_origins()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|x| !x.starts_with("flow::"))
|
||||
.collect::<Vec<_>>()
|
||||
)),
|
||||
"destination" => Ok(json!(
|
||||
variant
|
||||
.get_unique_destinations()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|x| !x.starts_with("flow::"))
|
||||
.collect::<Vec<_>>()
|
||||
)),
|
||||
_ => Err(api_error("Unknown field")),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/items/expired?<days>")]
|
||||
/// Get all expired transactions
|
||||
pub async fn expired_items_route(
|
||||
days: Option<&str>,
|
||||
t: Token,
|
||||
|
@ -169,6 +174,7 @@ pub async fn expired_items_route(
|
|||
}
|
||||
|
||||
#[get("/items/min")]
|
||||
/// Return all items without minimum satisfied
|
||||
pub async fn min_items_route(
|
||||
itemdb: &State<ItemDB>,
|
||||
t: Token,
|
||||
|
@ -197,6 +203,7 @@ pub struct MoveTransaction {
|
|||
}
|
||||
|
||||
#[post("/transaction/<id>/move", data = "<form>")]
|
||||
/// Move a transaction to a new location
|
||||
pub async fn move_transaction_route(id: &str, form: Json<MoveTransaction>) -> FallibleApiResponse {
|
||||
let new_loc = &form.to;
|
||||
let locations = get_locations!();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use rocket::{get, State};
|
||||
use rocket::{State, get};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::check_auth;
|
||||
|
@ -6,7 +6,7 @@ use crate::config::Config;
|
|||
use crate::routes::Token;
|
||||
use crate::{
|
||||
db::ItemDB,
|
||||
routes::{api_error, FallibleApiResponse},
|
||||
routes::{FallibleApiResponse, api_error},
|
||||
};
|
||||
|
||||
use super::{item_does_not_exist_error, variant_does_not_exist_error};
|
||||
|
@ -48,11 +48,13 @@ pub async fn variant_price_latest_by_origin(
|
|||
.variant(variant_id)
|
||||
.ok_or_else(variant_does_not_exist_error)?;
|
||||
|
||||
Ok(json!(variant
|
||||
.price_history_by_origin(origin, Some(1))
|
||||
.await
|
||||
.first()
|
||||
.unwrap()))
|
||||
Ok(json!(
|
||||
variant
|
||||
.price_history_by_origin(origin, Some(1))
|
||||
.await
|
||||
.first()
|
||||
.unwrap()
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/items/stat")]
|
||||
|
@ -73,7 +75,7 @@ pub async fn item_stat_route(
|
|||
let item_var = itemdb.get_item(&item).unwrap().variant(var).unwrap();
|
||||
for t in item_var.inventory().await {
|
||||
transaction_count += 1;
|
||||
total_price += t.price;
|
||||
total_price += t.price.unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use rocket::serde::json::Json;
|
||||
use rocket::{get, post, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::check_auth;
|
||||
use crate::config::{Config, Webhook};
|
||||
use crate::routes::{vec_to_api, ToAPI, Token};
|
||||
use crate::routes::Token;
|
||||
use crate::{
|
||||
db::ItemDB,
|
||||
routes::{api_error, FallibleApiResponse},
|
||||
routes::{FallibleApiResponse, api_error},
|
||||
};
|
||||
use based::request::api::{ToAPI, vec_to_api};
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{State, get, post};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use super::{item_does_not_exist_error, variant_does_not_exist_error};
|
||||
|
||||
|
@ -23,8 +23,8 @@ pub struct SupplyForm {
|
|||
pub note: Option<String>,
|
||||
}
|
||||
|
||||
/// Route for supply action. Creates a new Transaction for the specified Item Variant.
|
||||
#[post("/supply", data = "<form>")]
|
||||
/// Route for supply action. Creates a new Transaction for the specified Item Variant.
|
||||
pub async fn supply_route(
|
||||
form: Json<SupplyForm>,
|
||||
itemdb: &State<ItemDB>,
|
||||
|
@ -57,8 +57,8 @@ pub async fn supply_route(
|
|||
Ok(json!({"uuid": transaction.id}))
|
||||
}
|
||||
|
||||
/// Returns a list of Transaction UUIDs for the Item Variant
|
||||
#[get("/item/<item_id>/<variant_id>/supply")]
|
||||
/// Returns a list of Transaction UUIDs for the Item Variant
|
||||
pub async fn supply_log_route(
|
||||
item_id: &str,
|
||||
variant_id: &str,
|
||||
|
@ -79,8 +79,8 @@ pub async fn supply_log_route(
|
|||
Ok(json!(transactions))
|
||||
}
|
||||
|
||||
/// Returns current active Transactions for Item
|
||||
#[get("/item/<item_id>/inventory?<origin>")]
|
||||
/// Returns current active Transactions for Item
|
||||
pub async fn inventory_route(
|
||||
item_id: &str,
|
||||
itemdb: &State<ItemDB>,
|
||||
|
@ -103,8 +103,8 @@ pub async fn inventory_route(
|
|||
Ok(json!(vec_to_api(&transactions).await))
|
||||
}
|
||||
|
||||
/// Returns current active Transactions for Item Variant
|
||||
#[get("/item/<item_id>/<variant_id>/inventory")]
|
||||
/// Returns current active Transactions for Item Variant
|
||||
pub async fn inventory_route_variant(
|
||||
item_id: &str,
|
||||
variant_id: &str,
|
||||
|
@ -125,8 +125,8 @@ pub async fn inventory_route_variant(
|
|||
Ok(json!(vec_to_api(&transactions).await))
|
||||
}
|
||||
|
||||
/// Returns statistics for the Item Variant
|
||||
#[get("/item/<item_id>/<variant_id>/stat?<full>")]
|
||||
/// Returns statistics for the Item Variant
|
||||
pub async fn variant_stat_route(
|
||||
item_id: &str,
|
||||
variant_id: &str,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use rocket::{
|
||||
http::Status, outcome::Outcome, request::FromRequest, response::status::BadRequest, Request,
|
||||
Request, http::Status, outcome::Outcome, request::FromRequest, response::status::BadRequest,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -37,20 +37,3 @@ impl<'r> FromRequest<'r> for Token {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to generate a Model API representation in JSON format.
|
||||
pub trait ToAPI: Sized {
|
||||
/// Generate public API JSON
|
||||
fn api(&self) -> impl std::future::Future<Output = serde_json::Value>;
|
||||
}
|
||||
|
||||
/// Converts a slice of items implementing the `ToAPI` trait into a `Vec` of JSON values.
|
||||
pub async fn vec_to_api(items: &[impl ToAPI]) -> Vec<serde_json::Value> {
|
||||
let mut ret = Vec::with_capacity(items.len());
|
||||
|
||||
for e in items {
|
||||
ret.push(e.api().await);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use based::request::api::ToAPI;
|
||||
use futures::StreamExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use sqlx::prelude::FromRow;
|
||||
|
||||
use crate::{get_itemdb, get_locations, get_pg, routes::ToAPI};
|
||||
use crate::{get_itemdb, get_locations, get_pg, item::Item, variant::Variant};
|
||||
|
||||
// todo : produced / consumed by flow field?
|
||||
|
||||
|
@ -17,7 +18,7 @@ pub struct Transaction {
|
|||
/// Associated Variant
|
||||
pub variant: String,
|
||||
/// Price of obtaining the Item
|
||||
pub price: f64,
|
||||
pub price: Option<f64>,
|
||||
/// Origin of the Item
|
||||
pub origin: Option<String>,
|
||||
/// The location of the Item
|
||||
|
@ -53,6 +54,14 @@ impl Transaction {
|
|||
.fetch_one(get_pg!()).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn item(&self) -> Option<&Item> {
|
||||
get_itemdb!().get_item(&self.item)
|
||||
}
|
||||
|
||||
pub async fn item_variant(&self) -> Option<Variant> {
|
||||
get_itemdb!().get_item(&self.item)?.variant(&self.variant)
|
||||
}
|
||||
|
||||
pub async fn get(id: &uuid::Uuid) -> Option<Self> {
|
||||
sqlx::query_as("SELECT * FROM transactions WHERE id = $1")
|
||||
.bind(id)
|
||||
|
@ -157,11 +166,7 @@ impl Transaction {
|
|||
item.is_expired().await
|
||||
};
|
||||
|
||||
if expired {
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if expired { Some(item) } else { None }
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{get_pg, transaction::Transaction};
|
||||
use crate::{get_pg, location::StorageConditions, transaction::Transaction};
|
||||
|
||||
pub fn timestamp_range(year: i32, month: u32) -> (i64, i64) {
|
||||
let d = chrono::NaiveDate::from_ymd_opt(year, month, 0).unwrap();
|
||||
|
@ -31,7 +31,7 @@ pub fn timestamp_range(year: i32, month: u32) -> (i64, i64) {
|
|||
/// in the real world. It may include attributes or properties that deviate from
|
||||
/// the standard definition of the item. For example, different colors, sizes, or
|
||||
/// configurations.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Variant {
|
||||
/// Associated Item
|
||||
pub item: String,
|
||||
|
@ -45,6 +45,14 @@ pub struct Variant {
|
|||
pub expiry: Option<i64>,
|
||||
/// Associated barcodes
|
||||
pub barcodes: Option<Vec<i64>>,
|
||||
/// Variant Need Conditions
|
||||
pub needs: Option<VariantNeedCondition>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct VariantNeedCondition {
|
||||
/// Required temperature range (min-max)
|
||||
pub temperature: [f64; 2],
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
|
@ -78,6 +86,21 @@ impl Variant {
|
|||
.map(|x| x.as_i64().unwrap())
|
||||
.collect()
|
||||
}),
|
||||
needs: json.as_mapping().unwrap().get("needs").map(|x| {
|
||||
let temp_range = x
|
||||
.as_mapping()
|
||||
.unwrap()
|
||||
.get("temperature")
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap();
|
||||
VariantNeedCondition {
|
||||
temperature: [
|
||||
temp_range.get(0).unwrap().as_f64().unwrap(),
|
||||
temp_range.get(1).unwrap().as_f64().unwrap(),
|
||||
],
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,10 +257,29 @@ impl Variant {
|
|||
(false, 0)
|
||||
}
|
||||
|
||||
pub async fn satisfy_condition(&self, conditions: &StorageConditions) -> bool {
|
||||
if let Some(needs) = &self.needs {
|
||||
if let Some(room_temp) = conditions.accurate_temperature().await {
|
||||
if needs.temperature[0] < room_temp && room_temp < needs.temperature[1] {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log::warn!("Could not get temperature for location");
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn stat(&self, full: bool) -> serde_json::Value {
|
||||
let active_transactions = self.inventory().await;
|
||||
|
||||
let total_price: f64 = active_transactions.iter().map(|x| x.price).sum();
|
||||
let total_price: f64 = active_transactions
|
||||
.iter()
|
||||
.map(|x| x.price.unwrap_or_default())
|
||||
.sum();
|
||||
|
||||
if !full {
|
||||
return json!({
|
||||
|
|
Loading…
Add table
Reference in a new issue