я допишу я допишу я тесты напишу я всё напишу и защищусь
parent
776f40cd11
commit
0c88a19a40
|
|
@ -174,7 +174,7 @@ impl eframe::App for App<'_> {
|
||||||
|
|
||||||
struct MainTabViewer {
|
struct MainTabViewer {
|
||||||
workers: Arc<RwLock<Vec<Worker>>>,
|
workers: Arc<RwLock<Vec<Worker>>>,
|
||||||
equipment: Arc<RwLock<Option<Vec<Equipment>>>>,
|
equipment: Arc<RwLock<Vec<Equipment>>>,
|
||||||
salaries: Arc<RwLock<Vec<Salary>>>,
|
salaries: Arc<RwLock<Vec<Salary>>>,
|
||||||
|
|
||||||
db_oper: DBOperator,
|
db_oper: DBOperator,
|
||||||
|
|
@ -199,9 +199,9 @@ impl Default for MainTabViewer {
|
||||||
workers: Arc::new(RwLock::new(
|
workers: Arc::new(RwLock::new(
|
||||||
rt.block_on(async { db_oper.get_workers().await.unwrap() }),
|
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() }),
|
rt.block_on(async { db_oper.get_equipment().await.unwrap() }),
|
||||||
))),
|
)),
|
||||||
salaries: Arc::new(RwLock::new(
|
salaries: Arc::new(RwLock::new(
|
||||||
rt.block_on(async { db_oper.get_salaries().await.unwrap() }),
|
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() }),
|
.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 {
|
fn get_salary(&self) -> Salary {
|
||||||
Salary {
|
Salary {
|
||||||
id: self
|
id: self
|
||||||
|
|
@ -358,7 +364,7 @@ impl MainTabViewer {
|
||||||
})
|
})
|
||||||
.body(|mut body| {
|
.body(|mut body| {
|
||||||
body.ui_mut().style_mut().interaction.selectable_labels = false;
|
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| {
|
body.row(20.0, |mut row| {
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
ui.label(&eq.name);
|
ui.label(&eq.name);
|
||||||
|
|
@ -452,7 +458,7 @@ impl MainTabViewer {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap_or(&Worker::default())
|
||||||
.full_name
|
.full_name
|
||||||
.clone();
|
.clone();
|
||||||
egui::ComboBox::new("process_equipment_combobox", "")
|
egui::ComboBox::new("process_equipment_combobox", "")
|
||||||
|
|
@ -610,6 +616,8 @@ impl MainTabViewer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.equipment_modal_state.is_open = false;
|
||||||
|
self.update_equipment();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use sqlx::mysql::MySqlPoolOptions;
|
||||||
|
|
||||||
use dotenvy::dotenv_override;
|
use dotenvy::dotenv_override;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
// use crate::schema::equipment::dsl::*;
|
// use crate::schema::equipment::dsl::*;
|
||||||
// use crate::schema::worker::dsl::*;
|
// use crate::schema::worker::dsl::*;
|
||||||
|
|
@ -448,11 +449,47 @@ impl DBOperator {
|
||||||
ret.push(Recipe {
|
ret.push(Recipe {
|
||||||
id: pr.id,
|
id: pr.id,
|
||||||
name: pr.name.clone(),
|
name: pr.name.clone(),
|
||||||
elements: els,
|
elements: els.into_iter().collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
pub async fn add_recipe(&self, rec: Recipe) -> Result<u64, sqlx::Error> {
|
||||||
|
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<bool, sqlx::Error> {
|
||||||
|
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<bool, sqlx::Error> {
|
||||||
|
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<Vec<RecipeElement>, sqlx::Error> {
|
pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, 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;
|
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)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
pub async fn add_recipe_elems(
|
||||||
|
&self,
|
||||||
|
elems: HashSet<RecipeElement>,
|
||||||
|
rec_id: i32,
|
||||||
|
) -> Result<bool, sqlx::Error> {
|
||||||
|
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<bool, sqlx::Error> {
|
||||||
|
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> {
|
pub async fn estabilish_connection() -> Result<(), sqlx::Error> {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod database;
|
||||||
mod material_tab;
|
mod material_tab;
|
||||||
mod models;
|
mod models;
|
||||||
mod payment_tab;
|
mod payment_tab;
|
||||||
|
mod production_tab;
|
||||||
mod recipe_tab;
|
mod recipe_tab;
|
||||||
mod reports;
|
mod reports;
|
||||||
mod tab;
|
mod tab;
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ pub struct RecipeElementRow {
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub elements: std::vec::Vec<RecipeElement>,
|
pub elements: HashSet<RecipeElement>,
|
||||||
}
|
}
|
||||||
#[derive(FromRow)]
|
#[derive(FromRow)]
|
||||||
pub struct RecipeRow {
|
pub struct RecipeRow {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub struct ProductionTabViewer {
|
||||||
|
is_admin: bool,
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use bigdecimal::ToPrimitive;
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -37,6 +38,12 @@ impl RecipeTabViewer {
|
||||||
..Default::default()
|
..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) {
|
fn new_element_modal(&mut self, ui: &mut egui::Ui) {
|
||||||
egui::Modal::new(egui::Id::new("new_element_modal")).show(ui.ctx(), |ui| {
|
egui::Modal::new(egui::Id::new("new_element_modal")).show(ui.ctx(), |ui| {
|
||||||
if ui.button("Закрыть").clicked() {
|
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.end_row();
|
||||||
|
|
||||||
ui.label("Составляющие:");
|
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", "")
|
egui::ComboBox::new("recipe_element_combobox", "")
|
||||||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||||||
|
.selected_text(text)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
if ui.button("Новый элемент").clicked() {
|
if ui.button("Новый элемент").clicked() {
|
||||||
self.process_element_status.is_open = true;
|
self.process_element_status.is_open = true;
|
||||||
self.process_element_status.status = ModalStatus::Add;
|
self.process_element_status.status = ModalStatus::Add;
|
||||||
|
self.process_element_status.element = RecipeElement::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
for el in self.recipe_elements.read().clone().iter() {
|
for el in self.process_recipe_status.recipe.elements.clone().iter() {
|
||||||
let resp = ui.checkbox(
|
ui.horizontal(|ui| {
|
||||||
&mut self.process_recipe_status.elements.contains(el),
|
if ui.button("x").clicked() {
|
||||||
&el.material.name,
|
self.process_recipe_status.recipe.elements.remove(el);
|
||||||
);
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
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 {
|
if self.process_element_status.is_open {
|
||||||
self.new_element_modal(ui);
|
self.new_element_modal(ui);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue