diff --git a/code/src/app.rs b/code/src/app.rs index a017244..03513f5 100644 --- a/code/src/app.rs +++ b/code/src/app.rs @@ -174,7 +174,7 @@ impl eframe::App for App<'_> { struct MainTabViewer { workers: Arc>>, - equipment: Arc>>>, + equipment: Arc>>, salaries: Arc>>, db_oper: DBOperator, @@ -199,9 +199,9 @@ impl Default for MainTabViewer { workers: Arc::new(RwLock::new( rt.block_on(async { db_oper.get_workers().await.unwrap() }), )), - equipment: Arc::new(RwLock::new(Option::Some( + equipment: Arc::new(RwLock::new( rt.block_on(async { db_oper.get_equipment().await.unwrap() }), - ))), + )), salaries: Arc::new(RwLock::new( rt.block_on(async { db_oper.get_salaries().await.unwrap() }), )), @@ -268,6 +268,12 @@ impl MainTabViewer { .block_on(async { self.db_oper.get_salaries().await.unwrap() }), )); } + fn update_equipment(&mut self) { + self.equipment = Arc::new(RwLock::new( + self.rt + .block_on(async { self.db_oper.get_equipment().await.unwrap() }), + )); + } fn get_salary(&self) -> Salary { Salary { id: self @@ -358,7 +364,7 @@ impl MainTabViewer { }) .body(|mut body| { body.ui_mut().style_mut().interaction.selectable_labels = false; - for eq in self.equipment.read().clone().unwrap().iter() { + for eq in self.equipment.read().clone().iter() { body.row(20.0, |mut row| { row.col(|ui| { ui.label(&eq.name); @@ -452,7 +458,7 @@ impl MainTabViewer { .unwrap() .clone() }) - .unwrap() + .unwrap_or(&Worker::default()) .full_name .clone(); egui::ComboBox::new("process_equipment_combobox", "") @@ -610,6 +616,8 @@ impl MainTabViewer { }); } } + self.equipment_modal_state.is_open = false; + self.update_equipment(); } }); } diff --git a/code/src/database.rs b/code/src/database.rs index 90a5db7..e225cf5 100644 --- a/code/src/database.rs +++ b/code/src/database.rs @@ -6,6 +6,7 @@ use sqlx::mysql::MySqlPoolOptions; use dotenvy::dotenv_override; use std::collections::HashMap; +use std::collections::HashSet; use std::env; // use crate::schema::equipment::dsl::*; // use crate::schema::worker::dsl::*; @@ -448,11 +449,47 @@ impl DBOperator { ret.push(Recipe { id: pr.id, name: pr.name.clone(), - elements: els, + elements: els.into_iter().collect(), }); } Ok(ret) } + pub async fn add_recipe(&self, rec: Recipe) -> Result { + let mut tx = self.pool.begin().await?; + sqlx::query("INSERT INTO `recipe` (id,name) VALUES (?,?)") + .bind(rec.id) + .bind(rec.name) + .execute(&mut *tx) + .await?; + let ret = sqlx::query("SELECT LAST_INSERT_ID();") + .fetch_one(&mut *tx) + .await?; + match tx.commit().await { + Ok(_) => Ok(ret.get(0)), + Err(_) => Ok(0), + } + } + pub async fn remove_recipe(&self, rec_id: i32) -> Result { + match sqlx::query("DELETE FROM `recipe` WHERE id=?") + .bind(rec_id) + .execute(&self.pool) + .await + { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } + pub async fn update_recipe(&self, recipe: Recipe) -> Result { + match sqlx::query("UPDATE Brewery.recipe SET name=? WHERE id=?;") + .bind(recipe.name) + .bind(recipe.id) + .execute(&self.pool) + .await + { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } pub async fn get_recipe_elements(&self) -> Result, sqlx::Error> { let pre_ret = sqlx::query_as::<_, RecipeElementRow>("SELECT id, recipe_id, material_id, equipment_id, quantity FROM Brewery.recipe_element;").fetch_all(&self.pool).await; @@ -477,6 +514,36 @@ impl DBOperator { } Ok(ret) } + pub async fn add_recipe_elems( + &self, + elems: HashSet, + rec_id: i32, + ) -> Result { + let mut tx = self.pool.begin().await?; + for el in elems.iter() { + sqlx::query("INSERT INTO `recipe_element` (id, recipe_id, material_id, equipment_id, quantity) VALUES(0, ?, ?, ?, ?);") + .bind(rec_id) + .bind(el.material.id) + .bind(el.equipment.id) + .bind(el.quantity) + .execute(&mut *tx) + .await?; + } + match tx.commit().await { + Ok(_) => Ok(true), + Err(e) => Err(e), + } + } + pub async fn remove_recipe_elements(&self, rec_id: i32) -> Result { + match sqlx::query("DELETE FROM `recipe_element` WHERE recipe_id=?") + .bind(rec_id) + .execute(&self.pool) + .await + { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } } pub async fn estabilish_connection() -> Result<(), sqlx::Error> { diff --git a/code/src/main.rs b/code/src/main.rs index 36400d8..03d8aec 100644 --- a/code/src/main.rs +++ b/code/src/main.rs @@ -6,6 +6,7 @@ mod database; mod material_tab; mod models; mod payment_tab; +mod production_tab; mod recipe_tab; mod reports; mod tab; diff --git a/code/src/models.rs b/code/src/models.rs index 3e85dd4..6f8f264 100644 --- a/code/src/models.rs +++ b/code/src/models.rs @@ -111,7 +111,7 @@ pub struct RecipeElementRow { pub struct Recipe { pub id: i32, pub name: String, - pub elements: std::vec::Vec, + pub elements: HashSet, } #[derive(FromRow)] pub struct RecipeRow { diff --git a/code/src/production_tab.rs b/code/src/production_tab.rs index e69de29..e435206 100644 --- a/code/src/production_tab.rs +++ b/code/src/production_tab.rs @@ -0,0 +1,3 @@ +pub struct ProductionTabViewer { + is_admin: bool, +} diff --git a/code/src/recipe_tab.rs b/code/src/recipe_tab.rs index 2a3832c..02a9d52 100644 --- a/code/src/recipe_tab.rs +++ b/code/src/recipe_tab.rs @@ -1,3 +1,4 @@ +use bigdecimal::ToPrimitive; use egui_extras::{Column, TableBuilder}; use crate::{ @@ -37,6 +38,12 @@ impl RecipeTabViewer { ..Default::default() } } + fn update_recipes(&mut self) { + self.recipes = + Arc::new(RwLock::new(self.rt.block_on(async { + self.db_oper.get_recipes().await.unwrap_or(Vec::new()) + }))); + } fn new_element_modal(&mut self, ui: &mut egui::Ui) { egui::Modal::new(egui::Id::new("new_element_modal")).show(ui.ctx(), |ui| { if ui.button("Закрыть").clicked() { @@ -78,8 +85,12 @@ impl RecipeTabViewer { } }); }); - if ui.button("Добавить").clicked(){ - + if ui.button("Добавить").clicked() { + self.process_recipe_status + .recipe + .elements + .insert(self.process_element_status.element.clone()); + self.process_element_status.is_open = false; } }); } @@ -100,29 +111,99 @@ impl RecipeTabViewer { ui.end_row(); ui.label("Составляющие:"); + let text = match self.process_recipe_status.recipe.elements.len() { + 0 => "Пусто", + 1 => "1 элемент", + 2..=4 => &format!( + "{} элемента", + self.process_recipe_status.recipe.elements.len() + ), + _ => &format!( + "{} элементов", + self.process_recipe_status.recipe.elements.len() + ), + }; egui::ComboBox::new("recipe_element_combobox", "") .close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside) + .selected_text(text) .show_ui(ui, |ui| { if ui.button("Новый элемент").clicked() { self.process_element_status.is_open = true; self.process_element_status.status = ModalStatus::Add; + self.process_element_status.element = RecipeElement::default(); } - for el in self.recipe_elements.read().clone().iter() { - let resp = ui.checkbox( - &mut self.process_recipe_status.elements.contains(el), - &el.material.name, - ); - if resp.clicked() { - if self.process_recipe_status.elements.contains(el) { - self.process_recipe_status.elements.remove(el); - } else { - self.process_recipe_status.elements.insert(el.clone()); + for el in self.process_recipe_status.recipe.elements.clone().iter() { + ui.horizontal(|ui| { + if ui.button("x").clicked() { + self.process_recipe_status.recipe.elements.remove(el); } - } + ui.label(format!( + "{} ({})\n{}", + el.material.name, el.material.category.name, el.equipment.name + )); + }); } }); }); + let btext = match self.process_recipe_status.status { + ModalStatus::Add => "Добавить", + ModalStatus::Edit => "Сохранить", + ModalStatus::Remove => "Удалить", + }; + if ui.button(btext).clicked() { + match self.process_recipe_status.status { + ModalStatus::Add => { + self.rt.block_on(async { + let a = self + .db_oper + .add_recipe(self.process_recipe_status.recipe.clone()) + .await + .ok(); + self.db_oper + .add_recipe_elems( + self.process_recipe_status.recipe.elements.clone(), + a.unwrap().to_i32().unwrap(), + ) + .await + .ok(); + }); + } + ModalStatus::Edit => { + self.rt.block_on(async { + self.db_oper + .update_recipe(self.process_recipe_status.recipe.clone()) + .await + .ok(); + self.db_oper + .remove_recipe_elements(self.process_recipe_status.recipe.id) + .await + .ok(); + self.db_oper + .add_recipe_elems( + self.process_recipe_status.recipe.elements.clone(), + self.process_recipe_status.recipe.id, + ) + .await + .ok(); + }); + } + ModalStatus::Remove => { + self.rt.block_on(async { + self.db_oper + .remove_recipe_elements(self.process_recipe_status.recipe.id) + .await + .ok(); + self.db_oper + .remove_recipe(self.process_recipe_status.recipe.id) + .await + .ok(); + }); + } + } + self.update_recipes(); + self.process_recipe_status.is_open = false; + } }); if self.process_element_status.is_open { self.new_element_modal(ui);