This commit is contained in:
parent
91d39a4d83
commit
16353c7683
8 changed files with 123 additions and 86 deletions
38
docs/Item.md
38
docs/Item.md
|
@ -87,3 +87,41 @@ variants:
|
||||||
needs:
|
needs:
|
||||||
temperature: [5.0, 10.0]
|
temperature: [5.0, 10.0]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Quantization
|
||||||
|
Sometimes items are homogenous and can be subdivided.
|
||||||
|
This can be defined via the `unit` of an Item Variant.
|
||||||
|
You define the base unit (the smallest possible instance) of an Item Variant. In this case `ml`.
|
||||||
|
Optionally you can define various `conversions` to bigger units by specifiying the units name and how many of the base unit this unit is.
|
||||||
|
In this case we define `liter` to be 1000 `ml`.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
name: "Water"
|
||||||
|
variants:
|
||||||
|
regular:
|
||||||
|
name: "Regular Water"
|
||||||
|
unit:
|
||||||
|
by: "ml"
|
||||||
|
conversions:
|
||||||
|
liter: 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
Some items might be more or less the same thing but come in various configurations.
|
||||||
|
To address such items you can define custom properties on Item Variants.
|
||||||
|
You can then use these properties to save and filter on metadata about your items.
|
||||||
|
This can be done by defining custom properties in a JSON schema like format in `properties`:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
name: "iPhone"
|
||||||
|
variants:
|
||||||
|
iphone:
|
||||||
|
name: "iPhone"
|
||||||
|
properties:
|
||||||
|
color:
|
||||||
|
enum: ["Black", "White", "Red"]
|
||||||
|
description: "The Phone Color"
|
||||||
|
ram:
|
||||||
|
type: integer
|
||||||
|
description: "The amount of RAM"
|
||||||
|
```
|
||||||
|
|
3
migrations/0002_quanta_properties.sql
Normal file
3
migrations/0002_quanta_properties.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE transactions
|
||||||
|
ADD COLUMN quanta BIGINT,
|
||||||
|
ADD COLUMN properties JSONB;
|
|
@ -121,6 +121,8 @@ impl Flow {
|
||||||
Some(&format!("flow::{}::{}", self.kind, self.id)),
|
Some(&format!("flow::{}::{}", self.kind, self.id)),
|
||||||
info.location.as_ref().map(|x| x.as_str()),
|
info.location.as_ref().map(|x| x.as_str()),
|
||||||
info.note.as_ref().map(|x| x.as_str()),
|
info.note.as_ref().map(|x| x.as_str()),
|
||||||
|
info.quanta,
|
||||||
|
info.properties.clone()
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
ret.entry(item.item_variant_id().clone())
|
ret.entry(item.item_variant_id().clone())
|
||||||
|
|
|
@ -96,7 +96,7 @@ impl Item {
|
||||||
for (variant_id, variant) in variants_lst {
|
for (variant_id, variant) in variants_lst {
|
||||||
variants.insert(
|
variants.insert(
|
||||||
variant_id.as_str().unwrap().to_string(),
|
variant_id.as_str().unwrap().to_string(),
|
||||||
Variant::from_yml(&variant, variant_id.as_str().unwrap(), &id),
|
Variant::from_yml(variant, variant_id.as_str().unwrap(), &id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,10 @@ pub struct Transaction {
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
/// Notes on Transaction
|
/// Notes on Transaction
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
|
/// Qantized Unit
|
||||||
|
pub quanta: Option<i64>,
|
||||||
|
/// Custom properties
|
||||||
|
pub properties: Option<serde_json::Value>,
|
||||||
/// Timestamp of the Transaction
|
/// Timestamp of the Transaction
|
||||||
pub created: chrono::DateTime<chrono::Utc>,
|
pub created: chrono::DateTime<chrono::Utc>,
|
||||||
/// Destination of the Item or who consumed it
|
/// Destination of the Item or who consumed it
|
||||||
|
@ -35,6 +39,18 @@ pub struct Transaction {
|
||||||
pub consumed_timestamp: Option<chrono::DateTime<chrono::Utc>>,
|
pub consumed_timestamp: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO : typed origins / dests
|
||||||
|
|
||||||
|
pub enum Origin {
|
||||||
|
Flow(String),
|
||||||
|
Custom(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Destination {
|
||||||
|
Flow(String),
|
||||||
|
Custom(String)
|
||||||
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
item: &str,
|
item: &str,
|
||||||
|
@ -43,14 +59,18 @@ impl Transaction {
|
||||||
origin: Option<&str>,
|
origin: Option<&str>,
|
||||||
location: Option<&str>,
|
location: Option<&str>,
|
||||||
note: Option<&str>,
|
note: Option<&str>,
|
||||||
|
quanta: Option<i64>,
|
||||||
|
properties: Option<serde_json::Value>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
sqlx::query_as("INSERT INTO transactions (item, variant, price, origin, location, note) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *")
|
sqlx::query_as("INSERT INTO transactions (item, variant, price, origin, location, note, quanta, properties) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *")
|
||||||
.bind(item)
|
.bind(item)
|
||||||
.bind(variant)
|
.bind(variant)
|
||||||
.bind(price)
|
.bind(price)
|
||||||
.bind(origin)
|
.bind(origin)
|
||||||
.bind(location)
|
.bind(location)
|
||||||
.bind(note)
|
.bind(note)
|
||||||
|
.bind(quanta)
|
||||||
|
.bind(properties)
|
||||||
.fetch_one(get_pg!()).await.unwrap()
|
.fetch_one(get_pg!()).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +227,8 @@ impl ToAPI for Transaction {
|
||||||
"consumed": consumed,
|
"consumed": consumed,
|
||||||
"note": self.note,
|
"note": self.note,
|
||||||
"expired": self.is_expired().await,
|
"expired": self.is_expired().await,
|
||||||
|
"properties": self.properties,
|
||||||
|
"quanta": self.quanta
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,16 @@ pub struct Variant {
|
||||||
pub barcodes: Option<Vec<i64>>,
|
pub barcodes: Option<Vec<i64>>,
|
||||||
/// Variant Need Conditions
|
/// Variant Need Conditions
|
||||||
pub needs: Option<VariantNeedCondition>,
|
pub needs: Option<VariantNeedCondition>,
|
||||||
|
/// Custom fields as JSON object schema
|
||||||
|
pub meta: Option<serde_json::Value>,
|
||||||
|
/// Quantifiable
|
||||||
|
pub unit: Option<Quanta>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Quanta {
|
||||||
|
pub by: String,
|
||||||
|
pub conversions: HashMap<String, f64>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
@ -57,51 +67,8 @@ pub struct VariantNeedCondition {
|
||||||
|
|
||||||
impl Variant {
|
impl Variant {
|
||||||
/// Create variant from itemdb yaml
|
/// Create variant from itemdb yaml
|
||||||
pub fn from_yml(json: &serde_yaml::Value, variant: &str, item: &str) -> Self {
|
pub fn from_yml(json: serde_yaml::Value, variant: &str, item: &str) -> Self {
|
||||||
Self {
|
serde_yaml::from_value(json).unwrap()
|
||||||
item: item.to_string(),
|
|
||||||
variant: variant.to_string(),
|
|
||||||
name: json
|
|
||||||
.as_mapping()
|
|
||||||
.unwrap()
|
|
||||||
.get("name")
|
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
min: json
|
|
||||||
.as_mapping()
|
|
||||||
.unwrap()
|
|
||||||
.get("min")
|
|
||||||
.map(|x| x.as_i64().unwrap()),
|
|
||||||
expiry: json
|
|
||||||
.as_mapping()
|
|
||||||
.unwrap()
|
|
||||||
.get("expiry")
|
|
||||||
.map(|x| x.as_i64().unwrap()),
|
|
||||||
barcodes: json.as_mapping().unwrap().get("barcodes").map(|x| {
|
|
||||||
x.as_sequence()
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.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(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a API id for this Item Variant.
|
/// Get a API id for this Item Variant.
|
||||||
|
@ -175,8 +142,10 @@ impl Variant {
|
||||||
origin: Option<&str>,
|
origin: Option<&str>,
|
||||||
location: Option<&str>,
|
location: Option<&str>,
|
||||||
note: Option<&str>,
|
note: Option<&str>,
|
||||||
|
quanta: Option<i64>,
|
||||||
|
properties: Option<serde_json::Value>
|
||||||
) -> Transaction {
|
) -> Transaction {
|
||||||
Transaction::new(&self.item, &self.variant, price, origin, location, note).await
|
Transaction::new(&self.item, &self.variant, price, origin, location, note, quanta, properties).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all Transactions of this Item Variant
|
/// Returns all Transactions of this Item Variant
|
||||||
|
|
73
src/main.rs
73
src/main.rs
|
@ -141,44 +141,41 @@ async fn rocket() -> _ {
|
||||||
integrity::verify_integrity(&config, &flows, &locations, &itemdb).await;
|
integrity::verify_integrity(&config, &flows, &locations, &itemdb).await;
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount(
|
.mount("/", route![
|
||||||
"/",
|
routes::item::get_items_route,
|
||||||
route![
|
routes::item::item_route,
|
||||||
routes::item::get_items_route,
|
routes::item::item_variants_page,
|
||||||
routes::item::item_route,
|
routes::item::supply_log_route,
|
||||||
routes::item::item_variants_page,
|
routes::item::demand_log_route,
|
||||||
routes::item::supply_log_route,
|
routes::item::supply_route,
|
||||||
routes::item::demand_log_route,
|
routes::item::demand_route,
|
||||||
routes::item::supply_route,
|
routes::item::transaction_route,
|
||||||
routes::item::demand_route,
|
routes::item::inventory_route,
|
||||||
routes::item::transaction_route,
|
routes::item::inventory_route_variant,
|
||||||
routes::item::inventory_route,
|
routes::item::variant_stat_route,
|
||||||
routes::item::inventory_route_variant,
|
routes::item::unique_field_route,
|
||||||
routes::item::variant_stat_route,
|
routes::item::location_info,
|
||||||
routes::item::unique_field_route,
|
routes::item::locations_info,
|
||||||
routes::item::location_info,
|
routes::item::locations_list,
|
||||||
routes::item::locations_info,
|
routes::item::location_inventory,
|
||||||
routes::item::locations_list,
|
routes::flow::flow_info,
|
||||||
routes::item::location_inventory,
|
routes::flow::flows_list,
|
||||||
routes::flow::flow_info,
|
routes::item::expired_items_route,
|
||||||
routes::flow::flows_list,
|
routes::item::min_items_route,
|
||||||
routes::item::expired_items_route,
|
routes::item::variant_price_history_by_origin,
|
||||||
routes::item::min_items_route,
|
routes::flow::end_flow_route,
|
||||||
routes::item::variant_price_history_by_origin,
|
routes::flow::continue_flow_route,
|
||||||
routes::flow::end_flow_route,
|
routes::flow::create_flow_route,
|
||||||
routes::flow::continue_flow_route,
|
routes::item::location_condition_warn,
|
||||||
routes::flow::create_flow_route,
|
routes::item::move_transaction_route,
|
||||||
routes::item::location_condition_warn,
|
routes::item::variant_price_latest_by_origin,
|
||||||
routes::item::move_transaction_route,
|
routes::item::item_stat_route,
|
||||||
routes::item::variant_price_latest_by_origin,
|
routes::flow::active_flows_route,
|
||||||
routes::item::item_stat_route,
|
routes::flow::flow_api_route,
|
||||||
routes::flow::active_flows_route,
|
routes::flow::create_flow_note_route,
|
||||||
routes::flow::flow_api_route,
|
routes::flow::flow_notes_route,
|
||||||
routes::flow::create_flow_note_route,
|
routes::item::item_image_route
|
||||||
routes::flow::flow_notes_route,
|
])
|
||||||
routes::item::item_image_route
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.manage(itemdb)
|
.manage(itemdb)
|
||||||
.manage(locations)
|
.manage(locations)
|
||||||
.manage(flows)
|
.manage(flows)
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub struct SupplyForm {
|
||||||
pub origin: Option<String>,
|
pub origin: Option<String>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
|
pub quanta: Option<i64>,
|
||||||
|
pub properties: Option<serde_json::Value>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/supply", data = "<form>")]
|
#[post("/supply", data = "<form>")]
|
||||||
|
@ -39,12 +41,16 @@ pub async fn supply_route(
|
||||||
.variant(&form.variant)
|
.variant(&form.variant)
|
||||||
.ok_or_else(variant_does_not_exist_error)?;
|
.ok_or_else(variant_does_not_exist_error)?;
|
||||||
|
|
||||||
|
// TODO : check properties schema
|
||||||
|
|
||||||
let transaction = variant
|
let transaction = variant
|
||||||
.supply(
|
.supply(
|
||||||
form.price,
|
form.price,
|
||||||
form.origin.as_deref(),
|
form.origin.as_deref(),
|
||||||
form.location.as_deref(),
|
form.location.as_deref(),
|
||||||
form.note.as_deref(),
|
form.note.as_deref(),
|
||||||
|
form.quanta,
|
||||||
|
form.properties.clone()
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue