work
This commit is contained in:
parent
9da4ef072c
commit
ba61580adb
7 changed files with 177 additions and 179 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -484,6 +484,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
"uuid",
|
||||||
"web-base",
|
"web-base",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2649,9 +2650,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.6.1"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
|
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -2661,9 +2662,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid-macro-internal"
|
name = "uuid-macro-internal"
|
||||||
version = "1.6.1"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f49e7f3f3db8040a100710a11932239fd30697115e2ba4107080d8252939845e"
|
checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -15,4 +15,5 @@ serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.111"
|
serde_json = "1.0.111"
|
||||||
tokio = { version = "1.35.1", features = ["full"] }
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
web-base = "0.2.2"
|
web-base = "0.2.2"
|
||||||
|
|
114
src/item.rs
114
src/item.rs
|
@ -3,6 +3,33 @@ use std::collections::HashSet;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use mongodb::{bson::doc, Collection};
|
use mongodb::{bson::doc, Collection};
|
||||||
|
|
||||||
|
// todo : api key auth
|
||||||
|
|
||||||
|
// case: itemset
|
||||||
|
// items describe generic top level structure
|
||||||
|
|
||||||
|
// case: item variants
|
||||||
|
// specific instance of an item (real world)
|
||||||
|
// possibly deviating attrs
|
||||||
|
|
||||||
|
// case: import
|
||||||
|
// made / bought an item -> supply
|
||||||
|
// check in with attrs -> uuid (transaction)
|
||||||
|
// commited to db
|
||||||
|
|
||||||
|
// case: export
|
||||||
|
// used an item -> demand
|
||||||
|
// mark as used
|
||||||
|
// update db
|
||||||
|
|
||||||
|
// ITEM
|
||||||
|
// VARIANTS
|
||||||
|
// QUANTIZATION
|
||||||
|
// IMPORT / EXPORT
|
||||||
|
// DEPENDENCIES
|
||||||
|
// DEMAND STATS
|
||||||
|
// SEASONAL REVIEWS
|
||||||
|
|
||||||
macro_rules! collect_results {
|
macro_rules! collect_results {
|
||||||
($res:expr) => {{
|
($res:expr) => {{
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
|
@ -15,6 +42,14 @@ macro_rules! collect_results {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! get_mongo {
|
||||||
|
() => {
|
||||||
|
mongodb::Client::with_uri_str(std::env::var("DB_URI").unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
pub struct ItemVariantEntry {
|
pub struct ItemVariantEntry {
|
||||||
pub item_name: String,
|
pub item_name: String,
|
||||||
|
@ -70,8 +105,7 @@ impl Item {
|
||||||
let quantities: Collection<ItemInventoryEntry> =
|
let quantities: Collection<ItemInventoryEntry> =
|
||||||
self.db.database("cdb").collection("item_quantities");
|
self.db.database("cdb").collection("item_quantities");
|
||||||
|
|
||||||
let mut found =
|
let mut found = quantities
|
||||||
quantities
|
|
||||||
.find(doc! { "item_name": self.item.name.clone()}, None)
|
.find(doc! { "item_name": self.item.name.clone()}, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -83,8 +117,7 @@ impl Item {
|
||||||
let quantities: Collection<ItemInventoryEntry> =
|
let quantities: Collection<ItemInventoryEntry> =
|
||||||
self.db.database("cdb").collection("item_quantities");
|
self.db.database("cdb").collection("item_quantities");
|
||||||
|
|
||||||
let mut found =
|
let mut found = quantities
|
||||||
quantities
|
|
||||||
.find(doc! { "item_name": self.item.name.clone()}, None)
|
.find(doc! { "item_name": self.item.name.clone()}, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -139,6 +172,14 @@ impl Item {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn variant(&self, variant: &str) -> Option<Variant> {
|
||||||
|
// todo : check if avail
|
||||||
|
Some(Variant {
|
||||||
|
item: self.item.name.clone(),
|
||||||
|
variant: variant.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn add_variant(&self, name: &str) {
|
pub async fn add_variant(&self, name: &str) {
|
||||||
let variants: Collection<ItemVariantEntry> =
|
let variants: Collection<ItemVariantEntry> =
|
||||||
self.db.database("cdb").collection("item_variants");
|
self.db.database("cdb").collection("item_variants");
|
||||||
|
@ -155,6 +196,66 @@ impl Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Price(String);
|
||||||
|
|
||||||
|
impl Price {
|
||||||
|
fn new(price: &str) -> Self {
|
||||||
|
// todo : implement
|
||||||
|
|
||||||
|
Self(price.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Price {
|
||||||
|
fn from(val: std::string::String) -> Self {
|
||||||
|
Self::new(&val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Variant {
|
||||||
|
pub item: String,
|
||||||
|
pub variant: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Transaction {}
|
||||||
|
|
||||||
|
impl Transaction {
|
||||||
|
pub fn new(item: &str, variant: &str, price: Price, origin: &str) -> Self {
|
||||||
|
// todo : implement
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_doc(self) -> mongodb::bson::Document {
|
||||||
|
mongodb::bson::doc! {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variant {
|
||||||
|
pub async fn supply(&self, amount: usize, price: Price, origin: &str) -> String {
|
||||||
|
// todo : implement db transactions
|
||||||
|
let db = get_mongo!();
|
||||||
|
let col: mongodb::Collection<mongodb::bson::Document> =
|
||||||
|
db.database("cdb").collection("transactions");
|
||||||
|
|
||||||
|
let mut transactions = vec![];
|
||||||
|
for _ in 0..amount {
|
||||||
|
transactions.push(Transaction::new(
|
||||||
|
&self.item,
|
||||||
|
&self.variant,
|
||||||
|
price.clone(),
|
||||||
|
origin,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for transaction in transactions {
|
||||||
|
let r = col.insert_one(transaction.as_doc(), None).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ItemDB {
|
pub struct ItemDB {
|
||||||
index: mdq::Index,
|
index: mdq::Index,
|
||||||
mongodb: mongodb::Client,
|
mongodb: mongodb::Client,
|
||||||
|
@ -165,7 +266,7 @@ impl ItemDB {
|
||||||
// scan for markdown item entries
|
// scan for markdown item entries
|
||||||
let index = mdq::Index::new(dir, true);
|
let index = mdq::Index::new(dir, true);
|
||||||
let mongodb = mongodb::Client::with_uri_str(mongodb).await.unwrap();
|
let mongodb = mongodb::Client::with_uri_str(mongodb).await.unwrap();
|
||||||
|
/*
|
||||||
for item in &index.documents {
|
for item in &index.documents {
|
||||||
let item = ItemEntry::new(item.clone());
|
let item = ItemEntry::new(item.clone());
|
||||||
log::info!("Adding item {} to DB", item.name);
|
log::info!("Adding item {} to DB", item.name);
|
||||||
|
@ -180,11 +281,12 @@ impl ItemDB {
|
||||||
} else {
|
} else {
|
||||||
// todo : update values
|
// todo : update values
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
Self { index, mongodb }
|
Self { index, mongodb }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves an item by name
|
||||||
pub fn get_item(&self, item: &str) -> Option<Item> {
|
pub fn get_item(&self, item: &str) -> Option<Item> {
|
||||||
Some(Item::new(
|
Some(Item::new(
|
||||||
self.mongodb.clone(),
|
self.mongodb.clone(),
|
||||||
|
|
|
@ -36,11 +36,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
web_base::map!(web_base::Site::new(), |app: actix_web::App<_>| {
|
web_base::map!(web_base::Site::new(), |app: actix_web::App<_>| {
|
||||||
app.app_data(itemdb.clone())
|
app.app_data(itemdb.clone())
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(routes::item::item_page)
|
.service(routes::item::supply_route)
|
||||||
.service(routes::item::variant_pages::add_item_variant_page)
|
.service(routes::item::item_variants_page)
|
||||||
.service(routes::item::variant_pages::add_item_variant_page_post)
|
|
||||||
.service(routes::item::inventory_page::add_item_inventory_page)
|
|
||||||
.service(routes::item::inventory_page::add_item_inventory_page_post)
|
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0".to_string(), 8080))?
|
.bind(("0.0.0.0".to_string(), 8080))?
|
||||||
.run()
|
.run()
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
use actix_web::{get, post, HttpRequest, Responder};
|
|
||||||
use maud::html;
|
|
||||||
|
|
||||||
use crate::item;
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
|
||||||
struct AddinventoryForm {
|
|
||||||
variant: String,
|
|
||||||
origin: String,
|
|
||||||
price: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/item/{item_id}/inventory/add")]
|
|
||||||
pub async fn add_item_inventory_page_post(
|
|
||||||
r: HttpRequest,
|
|
||||||
f: actix_web::web::Form<AddinventoryForm>,
|
|
||||||
) -> impl Responder {
|
|
||||||
let id = r.match_info().query("item_id");
|
|
||||||
let itemdb: &actix_web::web::Data<item::ItemDB> = r.app_data().unwrap();
|
|
||||||
let item = itemdb.get_item(id).unwrap();
|
|
||||||
let rec_id = item.add_inventory(&f.variant, &f.origin, f.price).await;
|
|
||||||
|
|
||||||
// todo : generate receipts
|
|
||||||
|
|
||||||
web_base::redirect(&format!("/receipt/{}", rec_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/item/{item_id}/inventory/add")]
|
|
||||||
pub async fn add_item_inventory_page(r: HttpRequest) -> impl Responder {
|
|
||||||
let id = r.match_info().query("item_id");
|
|
||||||
|
|
||||||
let itemdb: &actix_web::web::Data<item::ItemDB> = r.app_data().unwrap();
|
|
||||||
|
|
||||||
let item = itemdb.get_item(id).unwrap();
|
|
||||||
let item_variants = item.get_variants().await;
|
|
||||||
|
|
||||||
// todo : get previous origins
|
|
||||||
let item_origins = item.get_origins().await;
|
|
||||||
|
|
||||||
let content = html!(
|
|
||||||
div class="bg-white p-8 rounded shadow-md max-w-md grid items-center justify-center m-auto mt-5" {
|
|
||||||
h1 class="text-2xl font-bold mb-4" { "Add Item inventory" };
|
|
||||||
form action=(format!("/item/{}/inventory/add", id)) method="post" {
|
|
||||||
div {
|
|
||||||
label class="text-sm font-medium text-gray-600 mr-3" for="variant" { "Variant:" };
|
|
||||||
select class="mt-1 p-2 border border-gray-300 rounded focus:outline-none focus:border-blue-500" id="variant" name="variant" {
|
|
||||||
@for variant in item_variants {
|
|
||||||
option value=(variant) { (variant) };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
br;
|
|
||||||
label class="text-sm font-medium text-gray-600" for="origin" { "Origin:" };
|
|
||||||
input class="mt-1 p-2 border border-gray-300 rounded focus:outline-none focus:border-blue-500" type="text" name="origin" list="origin_options" placeholder="Origin";
|
|
||||||
datalist id="origin_options" {
|
|
||||||
@for origin in item_origins {
|
|
||||||
option value=(origin);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
br;
|
|
||||||
label class="text-sm font-medium text-gray-600" for="price" { "Price: "};
|
|
||||||
input class="mt-1 p-2 border border-gray-300 rounded focus:outline-none focus:border-blue-500" type="number" step="0.01" name="price" placeholder="0.00";
|
|
||||||
br;
|
|
||||||
input class="hover:cursor-pointer bg-blue-500 text-white rounded px-4 py-2 mt-5" type="submit" name="submit" value="Add";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
).into_string();
|
|
||||||
|
|
||||||
web_base::build_site(&r, "Add Item inventory", &content)
|
|
||||||
}
|
|
|
@ -1,50 +1,60 @@
|
||||||
use actix_web::{get, HttpRequest, Responder};
|
use actix_web::post;
|
||||||
|
use actix_web::{get, HttpRequest, HttpResponse, Responder};
|
||||||
use maud::html;
|
use maud::html;
|
||||||
pub mod inventory_page;
|
use serde::Deserialize;
|
||||||
pub mod variant_pages;
|
|
||||||
|
|
||||||
use crate::item;
|
use crate::item;
|
||||||
|
|
||||||
#[get("/item/{item_id}")]
|
macro_rules! get_itemdb {
|
||||||
pub async fn item_page(r: HttpRequest) -> impl Responder {
|
($req:expr) => {{
|
||||||
let id = r.match_info().query("item_id");
|
let itemdb: &actix_web::web::Data<item::ItemDB> = $req.app_data().unwrap();
|
||||||
println!("{}", id);
|
itemdb
|
||||||
|
}};
|
||||||
let itemdb: &actix_web::web::Data<item::ItemDB> = r.app_data().unwrap();
|
|
||||||
|
|
||||||
let item = itemdb.get_item(id).unwrap();
|
|
||||||
|
|
||||||
let content =
|
|
||||||
html!(
|
|
||||||
div class="bg-gray-100 min-h-screen min-w-screen items-center justify-center py-5" {
|
|
||||||
div class="w-screen px-5" {
|
|
||||||
div {
|
|
||||||
p class="mb-2 font-bold text-2xl" { (format!("Item : {}", item.item.name)) };
|
|
||||||
p class="font-bold" { (format!("Category: {}", item.item.category))};
|
|
||||||
};
|
|
||||||
div class="mt-5" {
|
|
||||||
// (button("Add Variant", &format!("/item/{}/variant/add", item.item.name)))
|
|
||||||
// (button("Add inventory", &format!("/item/{}/inventory/add", item.item.name)));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
div class="mt-5 px-5" {
|
|
||||||
h1 class="text-2xl font-bold mb-4" { "Variants" };
|
|
||||||
@for variant in item.get_variants().await {
|
|
||||||
p class="mb-2" { (variant) };
|
|
||||||
}
|
}
|
||||||
@for q in item.get_inventory_entries().await {
|
|
||||||
div class="p-2" {
|
#[derive(Deserialize, Debug)]
|
||||||
p { (format!("Variant {}", q.variant_name))};
|
pub struct SupplyForm {
|
||||||
p { (format!("Origin {}", q.origin))};
|
item: String,
|
||||||
p { (format!("Price {}", q.price))};
|
variant: String,
|
||||||
};
|
amount: Option<usize>,
|
||||||
|
price: String,
|
||||||
|
origin: String,
|
||||||
}
|
}
|
||||||
};
|
|
||||||
};
|
#[post("/supply")]
|
||||||
|
pub async fn supply_route(
|
||||||
|
req: HttpRequest,
|
||||||
|
form: actix_web::web::Form<SupplyForm>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let itemdb = get_itemdb!(req);
|
||||||
|
println!("{form:?}");
|
||||||
|
let variant = itemdb
|
||||||
|
.get_item(&form.item)
|
||||||
|
.unwrap()
|
||||||
|
.variant(&form.variant)
|
||||||
|
.unwrap();
|
||||||
|
let transaction_id = variant
|
||||||
|
.supply(
|
||||||
|
form.amount.unwrap_or(1),
|
||||||
|
form.price.clone().into(),
|
||||||
|
&form.origin,
|
||||||
)
|
)
|
||||||
.into_string();
|
.await;
|
||||||
|
actix_web::HttpResponse::Ok().json(serde_json::json!({"uuid": transaction_id}))
|
||||||
web_base::build_site(&r, "Item", &content)
|
}
|
||||||
|
|
||||||
|
#[get("/item/{item_id}/variants")]
|
||||||
|
pub async fn item_variants_page(r: HttpRequest) -> impl Responder {
|
||||||
|
let id = r.match_info().query("item_id");
|
||||||
|
|
||||||
|
let itemdb = get_itemdb!(r);
|
||||||
|
|
||||||
|
let item = itemdb.get_item(id);
|
||||||
|
|
||||||
|
let variants = item.unwrap().get_variants().await;
|
||||||
|
|
||||||
|
HttpResponse::Ok().json(serde_json::json!({
|
||||||
|
"item": id,
|
||||||
|
"variants": variants
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
use actix_web::{get, post, HttpRequest, Responder};
|
|
||||||
use maud::html;
|
|
||||||
|
|
||||||
use crate::item;
|
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
|
||||||
struct AddVariantForm {
|
|
||||||
variant_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/item/{item_id}/variant/add")]
|
|
||||||
pub async fn add_item_variant_page_post(
|
|
||||||
r: HttpRequest,
|
|
||||||
f: actix_web::web::Form<AddVariantForm>,
|
|
||||||
) -> impl Responder {
|
|
||||||
let id = r.match_info().query("item_id");
|
|
||||||
let itemdb: &actix_web::web::Data<item::ItemDB> = r.app_data().unwrap();
|
|
||||||
let item = itemdb.get_item(id).unwrap();
|
|
||||||
item.add_variant(&f.variant_name).await;
|
|
||||||
|
|
||||||
web_base::redirect(&format!("/item/{}", id))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/item/{item_id}/variant/add")]
|
|
||||||
pub async fn add_item_variant_page(r: HttpRequest) -> impl Responder {
|
|
||||||
let id = r.match_info().query("item_id");
|
|
||||||
|
|
||||||
let itemdb: &actix_web::web::Data<item::ItemDB> = r.app_data().unwrap();
|
|
||||||
|
|
||||||
let item = itemdb.get_item(id).unwrap();
|
|
||||||
|
|
||||||
let content = html!(
|
|
||||||
div class="bg-white p-8 rounded shadow-md max-w-md grid items-center justify-center m-auto mt-5" {
|
|
||||||
h1 class="text-2xl font-bold mb-4" { "Add Variant for " (item.item.name) };
|
|
||||||
form action=(format!("/item/{}/variant/add", id)) method="post" {
|
|
||||||
input class="mt-1 p-2 border border-gray-300 rounded focus:outline-none focus:border-blue-500" type="text" name="variant_name" placeholder="Name" { };
|
|
||||||
input class="ml-5 hover:cursor-pointer bg-blue-500 text-white rounded px-4 py-2 mt-5" type="submit" name="submit" value="Add";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
).into_string();
|
|
||||||
|
|
||||||
web_base::build_site(&r, "Add Item Variant", &content)
|
|
||||||
}
|
|
Loading…
Reference in a new issue