From 5fd8e249b1f9ec3d99a7bc266fe3935fe723f38c Mon Sep 17 00:00:00 2001 From: Wes Holland Date: Tue, 3 Dec 2024 10:31:57 -0600 Subject: [PATCH] Merge adjustment tables into one --- migrations/20241107225934_initial.sql | 25 +--- src/app/history.rs | 133 +++++++++++++++-- src/auth.rs | 2 +- src/db/adjustment.rs | 206 ++++++++++++++++++++++++++ src/db/bootstrap_data.rs | 7 +- src/db/inventory_item.rs | 13 +- src/db/mod.rs | 3 +- src/db/negative_adjustment.rs | 115 -------------- src/db/positive_adjustment.rs | 53 ------- src/db/user.rs | 17 ++- src/ingest.rs | 6 +- templates/history.html | 8 +- templates/history_item_fragment.html | 15 +- 13 files changed, 371 insertions(+), 232 deletions(-) create mode 100644 src/db/adjustment.rs delete mode 100644 src/db/negative_adjustment.rs delete mode 100644 src/db/positive_adjustment.rs diff --git a/migrations/20241107225934_initial.sql b/migrations/20241107225934_initial.sql index 2bb9f17..2c466cb 100644 --- a/migrations/20241107225934_initial.sql +++ b/migrations/20241107225934_initial.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS User ( id INTEGER PRIMARY KEY NOT NULL, + email TEXT NOT NULL, name TEXT NOT NULL, role INTEGER NOT NULL, tz_offset INTEGER NOT NULL, @@ -27,19 +28,7 @@ CREATE TABLE IF NOT EXISTS InventoryItem ( FOREIGN KEY(display_unit) REFERENCES DisplayUnit(id) ); -CREATE TABLE IF NOT EXISTS PositiveAdjustment ( - id INTEGER PRIMARY KEY NOT NULL, - item INTEGER NOT NULL, - user INTEGER NOT NULL, - create_date DATETIME NOT NULL, - target_date DATETIME NOT NULL, - amount REAL NOT NULL, - unit_price INTEGER NOT NULL, - FOREIGN KEY(user) REFERENCES User(id), - FOREIGN KEY(item) REFERENCES InventoryItem(id) -); - -CREATE TABLE IF NOT EXISTS NegativeAdjustment ( +CREATE TABLE IF NOT EXISTS Adjustment ( id INTEGER PRIMARY KEY NOT NULL, item INTEGER NOT NULL, user INTEGER NOT NULL, @@ -47,22 +36,24 @@ CREATE TABLE IF NOT EXISTS NegativeAdjustment ( target_date INTEGER NOT NULL, amount REAL NOT NULL, reason INTEGER NOT NULL, + unit_price INTEGER, FOREIGN KEY(user) REFERENCES User(id), FOREIGN KEY(item) REFERENCES InventoryItem(id), - FOREIGN KEY(reason) REFERENCES NegativeAdjustmentReason(id) + FOREIGN KEY(reason) REFERENCES AdjustmentReason(id) ); -CREATE TABLE IF NOT EXISTS NegativeAdjustmentReason ( +CREATE TABLE IF NOT EXISTS AdjustmentReason ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL ); -INSERT INTO NegativeAdjustmentReason (id, name) VALUES +INSERT INTO AdjustmentReason (id, name) VALUES (0,'unknown'), (10,'sale'), (20,'destruction'), (25,'expiration'), - (30,'theft'); + (30,'theft'), + (50,'new-stock'); CREATE TABLE IF NOT EXISTS DisplayUnit ( id INTEGER PRIMARY KEY NOT NULL, diff --git a/src/app/history.rs b/src/app/history.rs index 4912ddc..f003570 100644 --- a/src/app/history.rs +++ b/src/app/history.rs @@ -1,20 +1,21 @@ +use crate::db::adjustment::{get_adjustments_target_date_range, DbAdjustment, DbAdjustmentWithUserAndItem}; +use crate::error::{AppError, QueryExtractor}; +use crate::session::SessionUser; +use crate::util::time::tz_offset_to_string; +use anyhow::Result; use askama::Template; use askama_axum::{IntoResponse, Response}; use axum::extract::State; use axum_htmx::HxRequest; +use chrono::prelude::*; use serde::Deserialize; use sqlx::SqlitePool; use tracing::info; -use chrono::prelude::*; -use crate::db::positive_adjustment::{get_positive_adjustments_target_date_range, DbPositiveAdjustment}; -use crate::error::{AppError, QueryExtractor}; -use crate::session::SessionUser; -use crate::util::time::tz_offset_to_string; #[derive(Template)] #[template(path = "history.html")] struct HistoryLogTemplate { - items: Vec, + items: Vec, start_date: String, start_time: String, end_date: String, @@ -25,9 +26,94 @@ struct HistoryLogTemplate { #[derive(Template)] #[template(path = "history_item_fragment.html")] struct HistoryLogItemFragmentTemplate { - items: Vec + items: Vec +} + +#[derive(Clone, Debug)] +struct HistoryDisplayItem { + pub create_date: String, + pub target_date: String, + pub user_name: String, + pub item_name: String, + pub item_unit: String, + pub item_unit_abbreviation: String, + pub item_type: HistoryItemEntry, +} + +#[derive(Clone, Debug)] +struct PositiveAdjustmentDisplayItem { + pub amount: String, + pub unit_value: String, +} + +pub fn int_cents_to_currency_string(i: i64) -> String { + let whole = i / 100; + let cents = i % 100; + + format!("${}.{}", whole, cents) +} + +impl From for PositiveAdjustmentDisplayItem { + fn from(adjustment: DbAdjustment) -> Self { + Self { + amount: format!("{}", adjustment.amount), + unit_value: int_cents_to_currency_string(adjustment.unit_price.unwrap_or_default()), + } + } } +#[derive(Clone, Debug)] +struct NegativeAdjustmentDisplayItem { + pub amount: String, + pub reason: String, +} + +impl From for NegativeAdjustmentDisplayItem { + fn from(adjustment: DbAdjustment) -> Self { + Self { + amount: format!("{}", adjustment.amount), + reason: adjustment.reason.into(), + } + } +} + +#[derive(Clone, Debug)] +enum HistoryItemEntry { + PositiveAdjustment(PositiveAdjustmentDisplayItem), + NegativeAdjustment(NegativeAdjustmentDisplayItem), +} + +impl HistoryDisplayItem { + pub fn from_adjustment(db_entry: DbAdjustmentWithUserAndItem, tz_offset: i32) + -> Result { + let simple_entry: DbAdjustment = db_entry.clone().into(); + + let timezone = FixedOffset::east_opt(tz_offset) + .ok_or(anyhow::anyhow!("Invalid timezone"))?; + let create_date = db_entry.create_date.with_timezone(&timezone) + .format("%Y-%m-%d %l:%M:%S %p").to_string(); + let target_date = db_entry.target_date.with_timezone(&timezone) + .format("%Y-%m-%d %l:%M:%S %p").to_string(); + + let item_type = if simple_entry.amount > 0.0 { + HistoryItemEntry::PositiveAdjustment(simple_entry.into()) + } else { + HistoryItemEntry::NegativeAdjustment(simple_entry.into()) + }; + + Ok(Self { + create_date, + target_date, + user_name: db_entry.user_name, + item_name: db_entry.item_name, + item_unit: db_entry.item_unit, + item_unit_abbreviation: db_entry.item_unit_abbreviation, + item_type + }) + } +} + + /// Common query args for datetime ranges #[derive(Debug, Deserialize)] pub struct DatetimeRangeQueryArgs { @@ -50,12 +136,14 @@ pub async fn history_log_handler( user: SessionUser ) -> Result { + info!("Query: {:?}", query); + let today = Local::now().naive_local().date(); let start_date = query.start_date.unwrap_or("2000-01-01".to_string()); let start_time = query.start_time.unwrap_or("00:00:00".to_string()); let end_date = query.end_date.unwrap_or(today.to_string()); - let end_time = query.end_time.unwrap_or("11:59:59".to_string()); + let end_time = query.end_time.unwrap_or("23:59:59".to_string()); let tz_offset = query.time_zone_offset.unwrap_or(user.tz_offset); let timezone = FixedOffset::east_opt(tz_offset) .ok_or(anyhow::anyhow!("Invalid timezone"))?; @@ -65,25 +153,38 @@ pub async fn history_log_handler( let naive_end_date = end_date.parse::()?; let naive_end_time = end_time.parse::()?; - let combined_start = naive_start_date + + let local_start = naive_start_date .and_time(naive_start_time) .and_local_timezone(timezone) .earliest() - .ok_or(anyhow::anyhow!("Invalid start"))? - .to_utc(); + .ok_or(anyhow::anyhow!("Invalid start"))?; - let combined_end = naive_end_date + let combined_start = local_start.to_utc(); + + let local_end = naive_end_date .and_time(naive_end_time) .and_local_timezone(timezone) .latest() - .ok_or(anyhow::anyhow!("Invalid start"))? - .to_utc(); + .ok_or(anyhow::anyhow!("Invalid start"))?; + + let combined_end = local_end.to_utc(); + let start_date = local_start.naive_local().date().to_string(); + let start_time = local_start.naive_local().time().to_string(); + let end_date = local_end.naive_local().date().to_string(); + let end_time = local_end.naive_local().time().to_string(); info!("Get items from: {} to {}", combined_start, combined_end); - let items = get_positive_adjustments_target_date_range(&db, - combined_start, combined_end).await?; + let items = DbAdjustmentWithUserAndItem::query_by_date_range( + &db, combined_start, combined_end, 1000, 0) + .await? + .into_iter() + .map(|x| { + HistoryDisplayItem::from_adjustment(x, tz_offset) + }) + .collect::>>()?; info!("Item count: {}", items.len()); diff --git a/src/auth.rs b/src/auth.rs index 6e74dbf..60e6767 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -135,7 +135,7 @@ pub async fn auth_authorized( .context("failed to deserialize response as JSON")?; // STEP 7 - Authorize the user at the application level - let db_user = match db::user::get_user_by_name(&db, &oauth_user_data.email).await? { + let db_user = match db::user::get_user_by_email(&db, &oauth_user_data.email).await? { Some(user) => user, None => { return Ok(AppForbiddenResponse::new(&oauth_user_data.email, "application").into_response()) diff --git a/src/db/adjustment.rs b/src/db/adjustment.rs new file mode 100644 index 0000000..4378aa0 --- /dev/null +++ b/src/db/adjustment.rs @@ -0,0 +1,206 @@ +use serde::Serialize; +use anyhow::Result; +use chrono::{DateTime, Utc}; +use sqlx::SqlitePool; +use tracing::error; + +#[derive(Clone, Debug, Serialize)] +#[derive(sqlx::FromRow)] +pub struct DbAdjustment { + pub id: i64, + pub item: i64, + pub user: i64, + pub create_date: DateTime, + pub target_date: DateTime, + pub amount: f64, + pub unit_price: Option, + #[sqlx(try_from = "i64")] + pub reason: DbAdjustmentReason, +} + + +pub async fn add_new_stock_adjustment(db: &SqlitePool, item: i64, user: i64, + create_date: DateTime, target_date: DateTime, + amount: f64, unit_price: i64) + -> Result { + let reason: i64 = DbAdjustmentReason::NewStock.into(); + let res = sqlx::query!( + r#" + INSERT INTO Adjustment ( + item, + user, + create_date, + target_date, + amount, + reason, + unit_price) + VALUES (?, ?, ?, ?, ?, ?, ?) + "#, + item, user, create_date, target_date, amount, reason, unit_price + ).execute(db).await?; + + let new_id = res.last_insert_rowid(); + + Ok(new_id) +} + +pub async fn get_adjustments_target_date_range( + db: &SqlitePool, start_date: DateTime, end_date: DateTime +) -> Result> { + + sqlx::query_as::<_, DbAdjustment>(r#" + SELECT id, item, user, + create_date, + target_date, + amount, + reason, + unit_price + FROM Adjustment + WHERE target_date >= ? AND target_date <= ? + "#,) + .bind(start_date) + .bind(end_date) + .fetch_all(db) + .await.map_err(Into::into) +} + + +#[derive(Clone, Debug, Serialize)] +#[derive(sqlx::FromRow)] +pub struct DbAdjustmentWithUserAndItem { + pub id: i64, + pub item: i64, + pub item_name: String, + pub item_unit: String, + pub item_unit_abbreviation: String, + pub user: i64, + pub user_name: String, + pub create_date: DateTime, + pub target_date: DateTime, + pub amount: f64, + pub unit_price: Option, + #[sqlx(try_from = "i64")] + pub reason: DbAdjustmentReason, +} + +impl DbAdjustmentWithUserAndItem { + pub async fn query_by_date_range(db: &SqlitePool, + start_date: DateTime, + end_date: DateTime, + page_size: i64, + page_num: i64) -> Result> { + let offset = page_size * page_num; + + sqlx::query_as::<_, DbAdjustmentWithUserAndItem>(r#" + SELECT + adj.id as id, + adj.item as item, + item.name as item_name, + unit.unit as item_unit, + unit.abbreviation as item_unit_abbreviation, + adj.user as user, + user.name as user_name, + adj.create_date as create_date, + adj.target_date as target_date, + adj.amount as amount, + adj.unit_price as unit_price, + adj.reason as reason + FROM Adjustment as adj + JOIN User as user on adj.user = user.id + JOIN InventoryItem as item on adj.item = item.id + JOIN DisplayUnit as unit on item.display_unit = unit.id + WHERE adj.target_date >= ? AND adj.target_date <= ? + LIMIT ? OFFSET ? + "#,) + .bind(start_date) + .bind(end_date) + .bind(page_size) + .bind(offset) + .fetch_all(db) + .await.map_err(Into::into) + } +} + +impl Into for DbAdjustmentWithUserAndItem { + fn into(self) -> DbAdjustment { + DbAdjustment { + id: self.id, + item: self.item, + user: self.user, + create_date: self.create_date, + target_date: self.target_date, + amount: self.amount, + unit_price: self.unit_price, + reason: self.reason, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize)] +pub enum DbAdjustmentReason { + Unknown, + Sale, + Destruction, + Expiration, + Theft, + NewStock, +} + +impl Into for DbAdjustmentReason { + fn into(self) -> i64 { + match self { + Self::Unknown => 0, + Self::Sale => 10, + Self::Destruction => 20, + Self::Expiration => 25, + Self::Theft => 30, + Self::NewStock => 50, + } + } +} + +impl From for DbAdjustmentReason { + fn from(item: i64) -> Self { + match item { + 0 => Self::Unknown, + 10 => Self::Sale, + 20 => Self::Destruction, + 25 => Self::Expiration, + 30 => Self::Theft, + 50 => Self::NewStock, + _ => { + error!("unknown negative adjustment reason value: {}", item); + Self::Unknown + } + } + } +} + +impl TryFrom<&str> for DbAdjustmentReason { + type Error = anyhow::Error; + + fn try_from(value: &str) -> std::result::Result { + match value { + "unknown" => Ok(Self::Unknown), + "sale" => Ok(Self::Sale), + "destruction" => Ok(Self::Destruction), + "expiration" => Ok(Self::Expiration), + "theft" => Ok(Self::Theft), + "new-stock" => Ok(Self::NewStock), + _ => Err(anyhow::anyhow!("unknown negative adjustment reason")) + } + } +} + +impl Into for DbAdjustmentReason { + fn into(self) -> String { + match self { + Self::Unknown => { String::from("unknown") } + Self::Sale => { String::from("sale") } + Self::Destruction => { String::from("destruction") } + Self::Expiration => { String::from("expiration") } + Self::Theft => { String::from("theft") } + Self::NewStock => { String::from("new-stock") } + } + } +} \ No newline at end of file diff --git a/src/db/bootstrap_data.rs b/src/db/bootstrap_data.rs index 95af225..3cbae1f 100644 --- a/src/db/bootstrap_data.rs +++ b/src/db/bootstrap_data.rs @@ -1,5 +1,5 @@ use std::str::FromStr; -use crate::db::user::{add_user, get_user_by_name, get_user_count, DbUserRole}; +use crate::db::user::{add_user, get_user_by_email, get_user_count, DbUserRole}; use anyhow::{anyhow, Result}; use serde::Deserialize; use sqlx::SqlitePool; @@ -14,6 +14,7 @@ struct BootstrapData { #[derive(Debug, Deserialize)] struct BootstrapUser { + email: String, name: String, role: String, tz_offset: Option, @@ -40,8 +41,8 @@ pub async fn bootstrap_database(db: &SqlitePool) -> Result<()> { let tz = if let Some(tz) = &user.tz_offset { i64::from_str(&tz).unwrap_or(DEFAULT_TIMEZONE) } else { DEFAULT_TIMEZONE }; - if get_user_by_name(db, &user.name).await?.is_none() { - let new_id = add_user(db, &user.name, role, tz).await?; + if get_user_by_email(db, &user.name).await?.is_none() { + let new_id = add_user(db, &user.email, &user.name, role, tz).await?; info!("bootstrap new user {}:{} ({})", new_id, user.name, user.role); } } diff --git a/src/db/inventory_item.rs b/src/db/inventory_item.rs index af7c143..568fbbc 100644 --- a/src/db/inventory_item.rs +++ b/src/db/inventory_item.rs @@ -85,18 +85,15 @@ pub async fn inventory_item_get_by_id(db: &SqlitePool, id: i64) -> Result Result { let res = sqlx::query!( r#" - SELECT - (SELECT TOTAL(amount) FROM PositiveAdjustment WHERE item = ?) AS plus, - (SELECT TOTAL(amount) FROM NegativeAdjustment WHERE item = ?) AS minus + SELECT TOTAL(amount) as amt FROM Adjustment WHERE item = ? "#, - id, id + id ) .fetch_one(db).await?; - let plus: f64 = res.plus.unwrap_or_default(); - let minus: f64 = res.minus.unwrap_or_default(); - - Ok(plus - minus) + let amt: f64 = res.amt.unwrap_or_default(); + + Ok(amt) } pub async fn add_inventory_item(db: &SqlitePool, name: &str, reorder_point: f64, diff --git a/src/db/mod.rs b/src/db/mod.rs index b8455bb..dd63085 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,8 +1,7 @@ pub mod inventory_item; -pub mod positive_adjustment; +pub mod adjustment; mod bootstrap_data; pub mod user; -pub mod negative_adjustment; use crate::db::bootstrap_data::bootstrap_database; use anyhow::Context; diff --git a/src/db/negative_adjustment.rs b/src/db/negative_adjustment.rs deleted file mode 100644 index 0271a3c..0000000 --- a/src/db/negative_adjustment.rs +++ /dev/null @@ -1,115 +0,0 @@ -use serde::Serialize; -use sqlx::SqlitePool; -use anyhow::Result; -use chrono::{DateTime, Utc}; -use tracing::error; - -#[derive(Debug, Serialize)] -#[derive(sqlx::FromRow)] -pub struct DbNegativeAdjustment { - pub id: i64, - pub item: i64, - pub user: i64, - pub create_date: i64, - pub target_date: i64, - pub amount: f64, - pub reason: DbNegativeAdjustmentReason, -} - -pub async fn add_negative_adjustment(db: &SqlitePool, item: i64, user: i64, - create_date: DateTime, target_date: DateTime, - amount: f64, reason: DbNegativeAdjustmentReason) -> Result { - let reason: i64 = reason.into(); - let res = sqlx::query!( - r#" - INSERT INTO NegativeAdjustment (item, user, create_date, target_date, amount, reason) - VALUES (?, ?, ?, ?, ?, ?) - "#, - item, user, create_date, target_date, amount, reason - ).execute(db).await?; - - let new_id = res.last_insert_rowid(); - - Ok(new_id) -} - -pub async fn get_negative_adjustments_target_date_range( - db: &SqlitePool, start_date: DateTime, end_date: DateTime -) -> Result> { - sqlx::query_as!( - DbNegativeAdjustment, - r#" - SELECT id, item, user, create_date, target_date, amount, reason - FROM NegativeAdjustment - WHERE target_date >= ? AND target_date <= ? - "#, - start_date, end_date - ) - .fetch_all(db) - .await.map_err(Into::into) -} - -#[derive(Debug, Clone, Copy, Serialize)] -pub enum DbNegativeAdjustmentReason { - Unknown, - Sale, - Destruction, - Expiration, - Theft, -} - -impl Into for DbNegativeAdjustmentReason { - fn into(self) -> i64 { - match self { - Self::Unknown => 0, - Self::Sale => 10, - Self::Destruction => 20, - Self::Expiration => 25, - Self::Theft => 30, - } - } -} - -impl From for DbNegativeAdjustmentReason { - fn from(item: i64) -> Self { - match item { - 0 => Self::Unknown, - 10 => Self::Sale, - 20 => Self::Destruction, - 25 => Self::Expiration, - 30 => Self::Theft, - _ => { - error!("unknown negative adjustment reason value: {}", item); - Self::Unknown - } - } - } -} - -impl TryFrom<&str> for DbNegativeAdjustmentReason { - type Error = anyhow::Error; - - fn try_from(value: &str) -> std::result::Result { - match value { - "unknown" => Ok(Self::Unknown), - "sale" => Ok(Self::Sale), - "destruction" => Ok(Self::Destruction), - "expiration" => Ok(Self::Expiration), - "theft" => Ok(Self::Theft), - _ => Err(anyhow::anyhow!("unknown negative adjustment reason")) - } - } -} - -impl Into for DbNegativeAdjustmentReason { - fn into(self) -> String { - match self { - Self::Unknown => { String::from("unknown") } - Self::Sale => { String::from("sale") } - Self::Destruction => { String::from("destruction") } - Self::Expiration => { String::from("expiration") } - Self::Theft => { String::from("theft") } - } - } -} - diff --git a/src/db/positive_adjustment.rs b/src/db/positive_adjustment.rs deleted file mode 100644 index 9667f65..0000000 --- a/src/db/positive_adjustment.rs +++ /dev/null @@ -1,53 +0,0 @@ -use serde::Serialize; -use anyhow::Result; -use chrono::{DateTime, Utc}; -use sqlx::SqlitePool; - -#[derive(Debug, Serialize)] -#[derive(sqlx::FromRow)] -pub struct DbPositiveAdjustment { - pub id: i64, - pub item: i64, - pub user: i64, - pub create_date: DateTime, - pub target_date: DateTime, - pub amount: f64, - pub unit_price: i64, -} - -pub async fn add_positive_adjustment(db: &SqlitePool, item: i64, user: i64, - create_date: DateTime, target_date: DateTime, - amount: f64, unit_price: i64) - -> Result { - let res = sqlx::query!( - r#" - INSERT INTO PositiveAdjustment (item, user, create_date, target_date, amount, unit_price) - VALUES (?, ?, ?, ?, ?, ?) - "#, - item, user, create_date, target_date, amount, unit_price - ).execute(db).await?; - - let new_id = res.last_insert_rowid(); - - Ok(new_id) -} - -pub async fn get_positive_adjustments_target_date_range( - db: &SqlitePool, start_date: DateTime, end_date: DateTime -) -> Result> { - - sqlx::query_as::<_, DbPositiveAdjustment>(r#" - SELECT id, item, user, - create_date, - target_date, - amount, unit_price - FROM PositiveAdjustment - WHERE target_date >= ? AND target_date <= ? - "#,) - .bind(start_date) - .bind(end_date) - .fetch_all(db) - .await.map_err(Into::into) -} - - diff --git a/src/db/user.rs b/src/db/user.rs index 6ae9801..796a8c2 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -6,36 +6,39 @@ use sqlx::SqlitePool; #[derive(sqlx::FromRow)] pub struct DbUser { pub id: i64, + pub email: String, pub name: String, pub role: i64, pub tz_offset: i64, } -pub async fn get_user_by_name(db: &SqlitePool, user: &str) -> Result> { +pub async fn get_user_by_email(db: &SqlitePool, email: &str) -> Result> { sqlx::query_as::<_, DbUser>( r#" SELECT id, - name, + email, + name, role, tz_offset FROM User WHERE - name = ? + email = ? "#) - .bind(user) + .bind(email) .fetch_optional(db).await .map_err(From::from) } -pub async fn add_user(db: &SqlitePool, name: &str, role: DbUserRole, tz_offset: i64) -> Result { +pub async fn add_user(db: &SqlitePool, email: &str, name: &str, role: DbUserRole, tz_offset: i64) -> Result { let role = role.value(); let res = sqlx::query( r#" - INSERT INTO User(name, role, tz_offset) - VALUES (?, ?, ?) + INSERT INTO User(email, name, role, tz_offset) + VALUES (?, ?, ?, ?) "#) + .bind(email) .bind(name) .bind(role) .bind(tz_offset) diff --git a/src/ingest.rs b/src/ingest.rs index 0529437..437ba14 100644 --- a/src/ingest.rs +++ b/src/ingest.rs @@ -4,7 +4,7 @@ use axum::body::Bytes; use sqlx::SqlitePool; use tracing::info; use crate::db::inventory_item::add_inventory_item; -use crate::db::positive_adjustment::add_positive_adjustment; +use crate::db::adjustment::add_new_stock_adjustment; #[derive(Debug, serde::Deserialize)] struct CatalogRecord { @@ -38,8 +38,8 @@ pub async fn ingest_catalog(mut reader: csv::Reader, db: Sq record.fractional, &record.unit).await?; - let new_positive_adjustment = add_positive_adjustment(&db, new_entry_id, - user_id, timestamp, timestamp, record.quantity, record.unit_price).await?; + let new_positive_adjustment = add_new_stock_adjustment(&db, new_entry_id, + user_id, timestamp, timestamp, record.quantity, record.unit_price).await?; info!("Added new item: {}/{} - {}", new_entry_id, new_positive_adjustment, record.name); } diff --git a/templates/history.html b/templates/history.html index 92e77ba..ed89ce7 100644 --- a/templates/history.html +++ b/templates/history.html @@ -10,11 +10,11 @@
- +
- + Timezone {{ time_zone }}
@@ -22,11 +22,11 @@
- +
- + Timezone {{ time_zone }}
diff --git a/templates/history_item_fragment.html b/templates/history_item_fragment.html index 4ad584c..0eded54 100644 --- a/templates/history_item_fragment.html +++ b/templates/history_item_fragment.html @@ -1,9 +1,18 @@ {% for item in items %} -
+
-

{{ item.item }}

-

{{ item.amount }}

+

{{ item.create_date }}

+

{{ item.item_name }}

+

{{ item.user_name }}

+

+ {% match item.item_type %} + {% when HistoryItemEntry::PositiveAdjustment with (entry) %} + {{ entry.amount }} @ {{ entry.unit_value }} + {% when HistoryItemEntry::NegativeAdjustment with (entry) %} + {{ entry.amount }} {{ entry.reason }} + {% endmatch %} +

{% endfor %} \ No newline at end of file