Compare commits
No commits in common. "44451de6d9fbc97f3f9bb3347fc38ff3ad12c96b" and "41e4bcb2d60859117109ff505914639d54718b18" have entirely different histories.
44451de6d9
...
41e4bcb2d6
202
code/src/app.rs
202
code/src/app.rs
|
|
@ -8,13 +8,12 @@ use std::{
|
||||||
use crate::{
|
use crate::{
|
||||||
database::DBOperator,
|
database::DBOperator,
|
||||||
material_tab::MaterialTabViewer,
|
material_tab::MaterialTabViewer,
|
||||||
misc::{self, ELEMENTS_PER_PAGE},
|
misc,
|
||||||
models::{
|
models::{
|
||||||
Equipment, EquipmentFilter, EquipmentModalState, LoginModalState, LoginStatus, Material,
|
Equipment, EquipmentFilter, EquipmentModalState, LoginModalState, LoginStatus, Material,
|
||||||
MaterialCategory, MaterialRow, ModalStatus, ModalWinState, Position, Salary,
|
MaterialCategory, MaterialRow, ModalStatus, ModalWinState, Position, Salary,
|
||||||
SalaryModalState, Worker,
|
SalaryModalState, Worker,
|
||||||
},
|
},
|
||||||
order_tab::OrderTabViewer,
|
|
||||||
recipe_tab::RecipeTabViewer,
|
recipe_tab::RecipeTabViewer,
|
||||||
reports::Reporter,
|
reports::Reporter,
|
||||||
tab::*,
|
tab::*,
|
||||||
|
|
@ -23,7 +22,7 @@ use crate::{
|
||||||
use bigdecimal::ToPrimitive;
|
use bigdecimal::ToPrimitive;
|
||||||
use chrono::{Datelike, NaiveDate};
|
use chrono::{Datelike, NaiveDate};
|
||||||
use egui::{TextBuffer, Vec2, mutex::RwLock};
|
use egui::{TextBuffer, Vec2, mutex::RwLock};
|
||||||
use egui_dock::{DockArea, DockState, Style, TabViewer};
|
use egui_dock::{DockArea, Style, TabViewer};
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use sqlx::types::BigDecimal;
|
use sqlx::types::BigDecimal;
|
||||||
|
|
@ -193,21 +192,17 @@ struct MainTabViewer {
|
||||||
material_tabs: MaterialTabViewer,
|
material_tabs: MaterialTabViewer,
|
||||||
material_tree: egui_dock::DockState<Tab>,
|
material_tree: egui_dock::DockState<Tab>,
|
||||||
recipe_tab: RecipeTabViewer,
|
recipe_tab: RecipeTabViewer,
|
||||||
order_tabs: OrderTabViewer,
|
|
||||||
order_tree: egui_dock::DockState<Tab>,
|
|
||||||
|
|
||||||
equipment_modal_state: EquipmentModalState,
|
equipment_modal_state: EquipmentModalState,
|
||||||
salary_modal_state: SalaryModalState,
|
salary_modal_state: SalaryModalState,
|
||||||
|
|
||||||
is_dark_theme: bool,
|
is_dark_theme: bool,
|
||||||
interface_scale_ratio: f32,
|
interface_scale_ratio: f32,
|
||||||
equipment_page: i32,
|
page: i32,
|
||||||
salaries_page: i32,
|
elements_per_page: i32,
|
||||||
workers_page: i32,
|
|
||||||
|
|
||||||
is_admin: bool,
|
is_admin: bool,
|
||||||
reporter: Reporter,
|
reporter: Reporter,
|
||||||
only_hired_workers: bool,
|
|
||||||
}
|
}
|
||||||
impl Default for MainTabViewer {
|
impl Default for MainTabViewer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
@ -215,21 +210,15 @@ impl Default for MainTabViewer {
|
||||||
let db_oper = rt.block_on(async { DBOperator::new().await });
|
let db_oper = rt.block_on(async { DBOperator::new().await });
|
||||||
let elements_per_page = 5;
|
let elements_per_page = 5;
|
||||||
Self {
|
Self {
|
||||||
workers: Arc::new(RwLock::new(rt.block_on(async {
|
workers: Arc::new(RwLock::new(
|
||||||
db_oper
|
rt.block_on(async { db_oper.get_workers().await.unwrap() }),
|
||||||
.get_workers(0, elements_per_page, false)
|
)),
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
}))),
|
|
||||||
equipment: Arc::new(RwLock::new(rt.block_on(async {
|
equipment: Arc::new(RwLock::new(rt.block_on(async {
|
||||||
db_oper
|
db_oper.get_equipment(0, elements_per_page).await.unwrap()
|
||||||
.get_equipment(0, elements_per_page, false)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
}))),
|
|
||||||
salaries: Arc::new(RwLock::new(rt.block_on(async {
|
|
||||||
db_oper.get_salaries(0, elements_per_page).await.unwrap()
|
|
||||||
}))),
|
}))),
|
||||||
|
salaries: Arc::new(RwLock::new(
|
||||||
|
rt.block_on(async { db_oper.get_salaries().await.unwrap() }),
|
||||||
|
)),
|
||||||
db_oper,
|
db_oper,
|
||||||
worker_tabs: WorkerTabViewer::default(),
|
worker_tabs: WorkerTabViewer::default(),
|
||||||
worker_tree: egui_dock::DockState::new(vec![
|
worker_tree: egui_dock::DockState::new(vec![
|
||||||
|
|
@ -258,18 +247,6 @@ impl Default for MainTabViewer {
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
recipe_tab: RecipeTabViewer::default(),
|
recipe_tab: RecipeTabViewer::default(),
|
||||||
|
|
||||||
order_tabs: OrderTabViewer::default(),
|
|
||||||
order_tree: DockState::new(vec![
|
|
||||||
Tab {
|
|
||||||
title: "Входящие".to_owned(),
|
|
||||||
tab_type: TabTypes::IncomingOrderList,
|
|
||||||
},
|
|
||||||
Tab {
|
|
||||||
title: "Исходящие".to_owned(),
|
|
||||||
tab_type: TabTypes::OutcomingOrderList,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
rt,
|
rt,
|
||||||
is_dark_theme: true,
|
is_dark_theme: true,
|
||||||
interface_scale_ratio: 1.2,
|
interface_scale_ratio: 1.2,
|
||||||
|
|
@ -296,45 +273,22 @@ impl Default for MainTabViewer {
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
reporter: Reporter {},
|
reporter: Reporter {},
|
||||||
|
|
||||||
equipment_page: 0,
|
elements_per_page,
|
||||||
salaries_page: 0,
|
page: 0,
|
||||||
workers_page: 0,
|
|
||||||
only_hired_workers: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MainTabViewer {
|
impl MainTabViewer {
|
||||||
fn update_salary(&mut self) {
|
fn update_salary(&mut self) {
|
||||||
self.salaries = Arc::new(RwLock::new(self.rt.block_on(async {
|
self.salaries = Arc::new(RwLock::new(
|
||||||
self.db_oper
|
self.rt
|
||||||
.get_salaries(
|
.block_on(async { self.db_oper.get_salaries().await.unwrap() }),
|
||||||
self.salaries_page * misc::ELEMENTS_PER_PAGE,
|
));
|
||||||
misc::ELEMENTS_PER_PAGE,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
fn update_equipment(&mut self) {
|
fn update_equipment(&mut self) {
|
||||||
self.equipment = Arc::new(RwLock::new(self.rt.block_on(async {
|
self.equipment = Arc::new(RwLock::new(self.rt.block_on(async {
|
||||||
self.db_oper
|
self.db_oper
|
||||||
.get_equipment(
|
.get_equipment(self.page, self.elements_per_page)
|
||||||
self.equipment_page * misc::ELEMENTS_PER_PAGE,
|
|
||||||
misc::ELEMENTS_PER_PAGE,
|
|
||||||
self.equipment_modal_state.filters.without_written_offs,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
fn update_workers(&mut self) {
|
|
||||||
self.workers = Arc::new(RwLock::new(self.rt.block_on(async {
|
|
||||||
self.db_oper
|
|
||||||
.get_workers(
|
|
||||||
self.workers_page * misc::ELEMENTS_PER_PAGE,
|
|
||||||
ELEMENTS_PER_PAGE,
|
|
||||||
self.only_hired_workers,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})));
|
})));
|
||||||
|
|
@ -391,31 +345,10 @@ impl MainTabViewer {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.add(egui::Checkbox::new(
|
||||||
if ui
|
&mut self.equipment_modal_state.filters.without_written_offs,
|
||||||
.add(egui::Checkbox::new(
|
"Без списанных",
|
||||||
&mut self.equipment_modal_state.filters.without_written_offs,
|
));
|
||||||
"Без списанных",
|
|
||||||
))
|
|
||||||
.changed()
|
|
||||||
{
|
|
||||||
self.update_equipment();
|
|
||||||
};
|
|
||||||
if ui.button("<-").clicked() {
|
|
||||||
if self.equipment_page > 0 {
|
|
||||||
self.equipment_page -= 1;
|
|
||||||
}
|
|
||||||
self.update_equipment();
|
|
||||||
}
|
|
||||||
ui.monospace(self.equipment_page.to_string());
|
|
||||||
if ui.button("->").clicked() {
|
|
||||||
if self.equipment.read().len() == 5 {
|
|
||||||
self.equipment_page += 1;
|
|
||||||
self.update_equipment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
TableBuilder::new(ui)
|
TableBuilder::new(ui)
|
||||||
.striped(true)
|
.striped(true)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
|
|
@ -448,42 +381,47 @@ 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().iter() {
|
for eq in self.equipment.read().clone().iter() {
|
||||||
body.row(20.0, |mut row| {
|
if (self.equipment_modal_state.filters.without_written_offs
|
||||||
row.col(|ui| {
|
&& !eq.is_written_off)
|
||||||
ui.label(&eq.name);
|
|| !self.equipment_modal_state.filters.without_written_offs
|
||||||
});
|
{
|
||||||
row.col(|ui| {
|
body.row(20.0, |mut row| {
|
||||||
ui.label(&eq.inv_number);
|
row.col(|ui| {
|
||||||
});
|
ui.label(&eq.name);
|
||||||
row.col(|ui| {
|
});
|
||||||
ui.label(&eq.worker.full_name);
|
row.col(|ui| {
|
||||||
});
|
ui.label(&eq.inv_number);
|
||||||
row.col(|ui| {
|
});
|
||||||
ui.label(eq.maintenance_date.date_naive().to_string());
|
row.col(|ui| {
|
||||||
});
|
ui.label(&eq.worker.full_name);
|
||||||
row.col(|ui| {
|
});
|
||||||
ui.label(if eq.is_written_off { "Да" } else { "Нет" });
|
row.col(|ui| {
|
||||||
});
|
ui.label(eq.maintenance_date.date_naive().to_string());
|
||||||
row.col(|ui| {
|
});
|
||||||
if self.is_admin {
|
row.col(|ui| {
|
||||||
if ui.button("Списать").clicked() {
|
ui.label(if eq.is_written_off { "Да" } else { "Нет" });
|
||||||
self.equipment_modal_state.is_open = true;
|
});
|
||||||
self.equipment_modal_state.status = ModalStatus::Remove;
|
row.col(|ui| {
|
||||||
self.equipment_modal_state.equipment = eq.clone();
|
if self.is_admin {
|
||||||
|
if ui.button("Списать").clicked() {
|
||||||
|
self.equipment_modal_state.is_open = true;
|
||||||
|
self.equipment_modal_state.status = ModalStatus::Remove;
|
||||||
|
self.equipment_modal_state.equipment = eq.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if row.response().double_clicked() {
|
||||||
|
self.equipment_modal_state.is_open = true;
|
||||||
|
self.equipment_modal_state.status = ModalStatus::Edit;
|
||||||
|
self.equipment_modal_state.equipment = eq.clone();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if row.response().double_clicked() {
|
}
|
||||||
self.equipment_modal_state.is_open = true;
|
|
||||||
self.equipment_modal_state.status = ModalStatus::Edit;
|
|
||||||
self.equipment_modal_state.equipment = eq.clone();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.equipment_modal_state.is_open {
|
if self.equipment_modal_state.is_open {
|
||||||
egui::Modal::new(egui::Id::new("process_equipment_modal")).show(ui.ctx(), |ui| {
|
egui::Modal::new(egui::Id::new("process_equipment_modal")).show(ui.ctx(), |ui| {
|
||||||
|
let mut date_okay = false;
|
||||||
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape))
|
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape))
|
||||||
{
|
{
|
||||||
self.equipment_modal_state.is_open = false;
|
self.equipment_modal_state.is_open = false;
|
||||||
|
|
@ -511,8 +449,6 @@ impl MainTabViewer {
|
||||||
|
|
||||||
ui.label("Ответственный");
|
ui.label("Ответственный");
|
||||||
egui::ComboBox::new("process_equipment_combobox", "")
|
egui::ComboBox::new("process_equipment_combobox", "")
|
||||||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
|
||||||
.height(100.0)
|
|
||||||
.selected_text(
|
.selected_text(
|
||||||
self.equipment_modal_state
|
self.equipment_modal_state
|
||||||
.equipment
|
.equipment
|
||||||
|
|
@ -528,23 +464,6 @@ impl MainTabViewer {
|
||||||
&wkrk.full_name,
|
&wkrk.full_name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("<-").clicked() {
|
|
||||||
if self.workers_page > 0 {
|
|
||||||
self.workers_page -= 1;
|
|
||||||
self.update_workers();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ui.monospace(self.workers_page.to_string());
|
|
||||||
if ui.button("->").clicked() {
|
|
||||||
if self.workers.read().len().to_i32().unwrap()
|
|
||||||
== misc::ELEMENTS_PER_PAGE
|
|
||||||
{
|
|
||||||
self.workers_page += 1;
|
|
||||||
self.update_workers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
|
@ -642,12 +561,6 @@ impl MainTabViewer {
|
||||||
.id(id)
|
.id(id)
|
||||||
.show_inside(ui, &mut self.material_tabs);
|
.show_inside(ui, &mut self.material_tabs);
|
||||||
}
|
}
|
||||||
fn show_order(&mut self, ui: &mut egui::Ui) {
|
|
||||||
let id = ui.make_persistent_id("OrderMenu");
|
|
||||||
DockArea::new(&mut self.order_tree)
|
|
||||||
.id(id)
|
|
||||||
.show_inside(ui, &mut self.order_tabs);
|
|
||||||
}
|
|
||||||
fn show_salary(&mut self, ui: &mut egui::Ui) {
|
fn show_salary(&mut self, ui: &mut egui::Ui) {
|
||||||
/*
|
/*
|
||||||
* Чё надо:
|
* Чё надо:
|
||||||
|
|
@ -1007,9 +920,6 @@ impl egui_dock::TabViewer for MainTabViewer {
|
||||||
TabTypes::Recipe => {
|
TabTypes::Recipe => {
|
||||||
&self.show_recipe(ui);
|
&self.show_recipe(ui);
|
||||||
}
|
}
|
||||||
TabTypes::Order => {
|
|
||||||
&self.show_order(ui);
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
ui.label("Welcome to Avalonia!");
|
ui.label("Welcome to Avalonia!");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,22 +35,10 @@ impl DBOperator {
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&self.pool)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
pub async fn get_workers(
|
pub async fn get_workers(&self) -> Result<Vec<Worker>, sqlx::Error> {
|
||||||
&self,
|
let pre_rets: Vec<WorkerRow> = sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker`")
|
||||||
start: i32,
|
.fetch_all(&self.pool)
|
||||||
length: i32,
|
.await?;
|
||||||
only_not_fired: bool,
|
|
||||||
) -> Result<Vec<Worker>, sqlx::Error> {
|
|
||||||
let pre_rets: Vec<WorkerRow> = match only_not_fired {
|
|
||||||
true => sqlx::query_as::<_, WorkerRow>(
|
|
||||||
"SELECT * FROM `worker` WHERE is_fired=0 LIMIT ? OFFSET ?",
|
|
||||||
),
|
|
||||||
false => sqlx::query_as::<_, WorkerRow>("SELECT * FROM `worker` LIMIT ? OFFSET ?"),
|
|
||||||
}
|
|
||||||
.bind(length)
|
|
||||||
.bind(start)
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await?;
|
|
||||||
let mut rets: Vec<Worker> = Vec::new();
|
let mut rets: Vec<Worker> = Vec::new();
|
||||||
let pos = self.get_position().await.unwrap();
|
let pos = self.get_position().await.unwrap();
|
||||||
for worker in pre_rets {
|
for worker in pre_rets {
|
||||||
|
|
@ -96,33 +84,23 @@ impl DBOperator {
|
||||||
&self,
|
&self,
|
||||||
start: i32,
|
start: i32,
|
||||||
count: i32,
|
count: i32,
|
||||||
without_written_offs: bool,
|
|
||||||
) -> Result<Vec<Equipment>, sqlx::Error> {
|
) -> Result<Vec<Equipment>, sqlx::Error> {
|
||||||
let pre_rets = match without_written_offs {
|
let pre_rets =
|
||||||
true => {
|
sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment` LIMIT ? OFFSET ?")
|
||||||
sqlx::query_as::<_, EquipmentRow>(
|
|
||||||
"SELECT * FROM `equipment` WHERE is_written_off=0 LIMIT ? OFFSET ? ",
|
|
||||||
)
|
|
||||||
.bind(count)
|
.bind(count)
|
||||||
.bind(start)
|
.bind(start)
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
.await?
|
.await?;
|
||||||
}
|
|
||||||
false => {
|
|
||||||
sqlx::query_as::<_, EquipmentRow>("SELECT * FROM `equipment` LIMIT ? OFFSET ?")
|
|
||||||
.bind(count)
|
|
||||||
.bind(start)
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rets: Vec<Equipment> = Vec::new();
|
let mut rets: Vec<Equipment> = Vec::new();
|
||||||
|
let workers = self.get_workers().await?;
|
||||||
for eq in pre_rets {
|
for eq in pre_rets {
|
||||||
let pworker = self
|
let mut pworker = Worker::default();
|
||||||
.get_worker_by_id(eq.worker_id)
|
for worker in workers.clone() {
|
||||||
.await
|
if worker.id == eq.worker_id {
|
||||||
.unwrap_or(Worker::default());
|
pworker = worker.clone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
rets.push(Equipment {
|
rets.push(Equipment {
|
||||||
id: eq.id,
|
id: eq.id,
|
||||||
name: eq.name,
|
name: eq.name,
|
||||||
|
|
@ -208,57 +186,26 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
Ok(rets)
|
Ok(rets)
|
||||||
}
|
}
|
||||||
pub async fn get_promotions(
|
|
||||||
&self,
|
|
||||||
start: i32,
|
|
||||||
length: i32,
|
|
||||||
) -> Result<Vec<Promotion>, sqlx::Error> {
|
|
||||||
let prets = sqlx::query_as::<_, PromotionRow>("SELECT * FROM `promotion` LIMIT ? OFFSET ?")
|
|
||||||
.bind(length)
|
|
||||||
.bind(start)
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await?;
|
|
||||||
let mut rets: Vec<Promotion> = Vec::new();
|
|
||||||
for p in prets {
|
|
||||||
rets.push(Promotion {
|
|
||||||
id: p.id,
|
|
||||||
worker: self
|
|
||||||
.get_worker_by_id(p.worker_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(Worker::default()),
|
|
||||||
from_position: self
|
|
||||||
.get_position_by_id(p.from_position_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(Position::default()),
|
|
||||||
to_position: self
|
|
||||||
.get_position_by_id(p.to_position_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or(Position::default()),
|
|
||||||
date: p.date,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(rets)
|
|
||||||
}
|
|
||||||
pub async fn promote_worker(
|
pub async fn promote_worker(
|
||||||
&self,
|
&self,
|
||||||
worker: Worker,
|
worker: Worker,
|
||||||
position_from: Position,
|
position_to: Position,
|
||||||
date: DateTime<chrono::Local>,
|
date: DateTime<chrono::Local>,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
let mut tx = self.pool.begin().await?;
|
let mut tx = self.pool.begin().await?;
|
||||||
sqlx::query("INSERT INTO promotion (worker_id, from_position_id, to_position_id, `date`) VALUES(?, ?, ?, ?);").bind(worker.id).bind(position_from.id).bind(worker.position.id).bind(date).execute(&mut *tx).await?;
|
sqlx::query("INSERT INTO promotion (worker_id, from_position_id, to_position_id, `date`) VALUES(?, ?, ?, ?);").bind(worker.id).bind(worker.position.id).bind(position_to.id).bind(date).execute(&mut *tx).await?;
|
||||||
sqlx::query("UPDATE worker SET position_id=? WHERE id=?;")
|
sqlx::query("UPDATE worker SET position_id=? WHERE id=?;")
|
||||||
.bind(worker.position.id)
|
.bind(position_to.id)
|
||||||
.bind(worker.id)
|
.bind(worker.id)
|
||||||
.execute(&mut *tx)
|
.execute(&mut *tx)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
tx.commit().await
|
tx.commit().await
|
||||||
}
|
}
|
||||||
pub async fn add_worker(&self, worker: Worker) -> Result<bool, sqlx::Error> {
|
pub async fn add_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
||||||
// sqlx::query!("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);",worker.position.id, worker.hire_date ,worker.is_fired,worker.full_name)
|
// sqlx::query!("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);",worker.position.id, worker.hire_date ,worker.is_fired,worker.full_name)
|
||||||
match sqlx::query("INSERT INTO `worker` (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);")
|
match sqlx::query("INSERT INTO `worker` (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);")
|
||||||
.bind(worker.position.id)
|
.bind(worker.position_id)
|
||||||
.bind(worker.hire_date)
|
.bind(worker.hire_date)
|
||||||
.bind(worker.is_fired)
|
.bind(worker.is_fired)
|
||||||
.bind(worker.full_name)
|
.bind(worker.full_name)
|
||||||
|
|
@ -344,14 +291,17 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_worker(&self, worker: Worker) -> Result<bool, sqlx::Error> {
|
pub async fn update_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `worker` SET hire_date=?, is_fired=?, full_name=? WHERE id=?;")
|
match sqlx::query(
|
||||||
.bind(worker.hire_date)
|
"UPDATE `worker` SET position_id=?, hire_date=?, is_fired=?, full_name=? WHERE id=?;",
|
||||||
.bind(worker.is_fired)
|
)
|
||||||
.bind(worker.full_name)
|
.bind(worker.position_id)
|
||||||
.bind(worker.id)
|
.bind(worker.hire_date)
|
||||||
.execute(&self.pool)
|
.bind(worker.is_fired)
|
||||||
.await
|
.bind(worker.full_name)
|
||||||
|
.bind(worker.id)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => Ok(true),
|
Ok(_) => Ok(true),
|
||||||
Err(_) => Ok(false),
|
Err(_) => Ok(false),
|
||||||
|
|
@ -451,15 +401,14 @@ impl DBOperator {
|
||||||
Err(_) => Ok(false),
|
Err(_) => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_salaries(&self, start: i32, length: i32) -> Result<Vec<Salary>, sqlx::Error> {
|
pub async fn get_salaries(&self) -> Result<Vec<Salary>, sqlx::Error> {
|
||||||
let sals = sqlx::query_as::<_, SalaryRow>("SELECT * FROM `salary` LIMIT ? OFFSET ?")
|
let sals = sqlx::query_as::<_, SalaryRow>("SELECT * FROM `salary`")
|
||||||
.bind(length)
|
|
||||||
.bind(start)
|
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
let mut rets: Vec<Salary> = Vec::new();
|
let mut rets: Vec<Salary> = Vec::new();
|
||||||
|
let workers = self.get_workers().await.unwrap();
|
||||||
for sal in sals {
|
for sal in sals {
|
||||||
match self.get_worker_by_id(sal.worker_id).await.ok() {
|
match workers.iter().find(|w| w.id == sal.worker_id) {
|
||||||
Some(w) => rets.push(Salary {
|
Some(w) => rets.push(Salary {
|
||||||
id: sal.id,
|
id: sal.id,
|
||||||
worker: w.clone(),
|
worker: w.clone(),
|
||||||
|
|
@ -517,25 +466,6 @@ impl DBOperator {
|
||||||
Err(_) => Ok(false),
|
Err(_) => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_recipe_by_id(&self, id: i32) -> Recipe {
|
|
||||||
let pret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM `recipe` WHERE id = ?")
|
|
||||||
.bind(id)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let pelems = self.get_recipe_elements().await.unwrap_or(Vec::new());
|
|
||||||
let mut elems: HashSet<RecipeElement> = HashSet::new();
|
|
||||||
for pr in pelems {
|
|
||||||
if pr.recipe_id == pret.id {
|
|
||||||
elems.insert(pr.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Recipe {
|
|
||||||
id: pret.id,
|
|
||||||
name: pret.name.clone(),
|
|
||||||
elements: elems,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub async fn get_recipes(&self) -> Result<Vec<Recipe>, sqlx::Error> {
|
pub async fn get_recipes(&self) -> Result<Vec<Recipe>, sqlx::Error> {
|
||||||
let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM `recipe`")
|
let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM `recipe`")
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
|
|
@ -652,78 +582,6 @@ impl DBOperator {
|
||||||
Err(_) => Ok(false),
|
Err(_) => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_orders(
|
|
||||||
&self,
|
|
||||||
start: i32,
|
|
||||||
length: i32,
|
|
||||||
is_incoming: bool,
|
|
||||||
) -> Vec<Order>{
|
|
||||||
let pret = sqlx::query_as::<_, OrderRow>(
|
|
||||||
"SELECT * FROM `order` WHERE is_incoming=? LIMIT ? OFFSET ?",
|
|
||||||
)
|
|
||||||
.bind(is_incoming)
|
|
||||||
.bind(length)
|
|
||||||
.bind(start)
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut ret: Vec<Order> = Vec::new();
|
|
||||||
for or in pret {
|
|
||||||
ret.push(Order {
|
|
||||||
id: or.id,
|
|
||||||
order_date: or.order_date,
|
|
||||||
client: self.get_client_by_id(or.client_id).await.unwrap(),
|
|
||||||
elements: self.get_order_elements_by_order_id(or.id).await,
|
|
||||||
total_price: or.total_price,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
pub async fn get_order_elements_by_order_id(&self, order_id: i32) -> Vec<OrderElement> {
|
|
||||||
let pret = sqlx::query_as::<_, OrderElementRow>(
|
|
||||||
"SELECT * FROM `order_element` WHERE order_id = ?",
|
|
||||||
)
|
|
||||||
.bind(order_id)
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let mut ret: Vec<OrderElement> = Vec::new();
|
|
||||||
for or in pret {
|
|
||||||
ret.push(OrderElement {
|
|
||||||
id: or.id,
|
|
||||||
total_price: or.total_price,
|
|
||||||
product: self.get_product_by_id(or.product_id).await,
|
|
||||||
quantity: or.quantity,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
pub async fn get_client_by_id(&self, id: i32) -> Result<Client, sqlx::Error> {
|
|
||||||
sqlx::query_as::<_, Client>("SELECT FROM `client` WHERE id=?")
|
|
||||||
.bind(id)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
pub async fn get_product_by_id(&self, id: i32) -> Product {
|
|
||||||
let pret = sqlx::query_as::<_, ProductRow>("SELECT * FROM `product` WHERE id = ?")
|
|
||||||
.bind(id)
|
|
||||||
.fetch_one(&self.pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let mut recipe: Recipe = Recipe::default();
|
|
||||||
for rec in self.get_recipes().await.unwrap() {
|
|
||||||
if rec.id == pret.recipe_id {
|
|
||||||
recipe = rec.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Product {
|
|
||||||
id: pret.id,
|
|
||||||
recipe,
|
|
||||||
price_per_unit: pret.price_per_unit,
|
|
||||||
volume: pret.volume,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn estabilish_connection() -> Result<(), sqlx::Error> {
|
pub async fn estabilish_connection() -> Result<(), sqlx::Error> {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ mod app;
|
||||||
mod database;
|
mod database;
|
||||||
mod material_tab;
|
mod material_tab;
|
||||||
mod models;
|
mod models;
|
||||||
mod order_tab;
|
mod production_tab;
|
||||||
mod recipe_tab;
|
mod recipe_tab;
|
||||||
mod reports;
|
mod reports;
|
||||||
mod tab;
|
mod tab;
|
||||||
|
|
@ -16,8 +16,8 @@ fn main() {
|
||||||
database::estabilish_connection();
|
database::estabilish_connection();
|
||||||
let mut native_opts = eframe::NativeOptions {
|
let mut native_opts = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
.with_inner_size([815.0, 400.0])
|
.with_inner_size([815.0, 300.0])
|
||||||
.with_min_inner_size([750.0, 350.0]),
|
.with_min_inner_size([750.0, 200.0]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
native_opts.renderer = eframe::Renderer::Glow;
|
native_opts.renderer = eframe::Renderer::Glow;
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ use std::str::FromStr;
|
||||||
use bigdecimal::ToPrimitive;
|
use bigdecimal::ToPrimitive;
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
|
|
||||||
pub const ELEMENTS_PER_PAGE: i32 = 5;
|
|
||||||
|
|
||||||
pub fn naivedate_to_jiff(date: chrono::NaiveDate) -> jiff::civil::Date {
|
pub fn naivedate_to_jiff(date: chrono::NaiveDate) -> jiff::civil::Date {
|
||||||
jiff::civil::Date::new(
|
jiff::civil::Date::new(
|
||||||
date.year().to_i16().unwrap(),
|
date.year().to_i16().unwrap(),
|
||||||
|
|
|
||||||
|
|
@ -121,53 +121,29 @@ pub struct RecipeRow {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub id: i32,
|
id: i32,
|
||||||
pub name: String,
|
name: String,
|
||||||
pub address: String,
|
address: String,
|
||||||
}
|
}
|
||||||
pub struct Order {
|
pub struct Order<'a> {
|
||||||
pub id: i32,
|
id: i32,
|
||||||
pub client: Client,
|
client: Client,
|
||||||
pub order_date: chrono::DateTime<chrono::Local>,
|
order_date: chrono::DateTime<chrono::Local>,
|
||||||
pub total_price: BigDecimal,
|
total_amount: rust_decimal::Decimal,
|
||||||
pub elements: std::vec::Vec<OrderElement>,
|
elements: std::vec::Vec<OrderElement<'a>>,
|
||||||
}
|
}
|
||||||
#[derive(FromRow)]
|
pub struct OrderElement<'a> {
|
||||||
pub struct OrderRow {
|
id: i32,
|
||||||
pub id: i32,
|
product: Product<'a>,
|
||||||
pub client_id: i32,
|
quantity: i32,
|
||||||
pub order_date: chrono::DateTime<chrono::Local>,
|
total_amount: rust_decimal::Decimal,
|
||||||
pub total_price: BigDecimal,
|
|
||||||
}
|
}
|
||||||
pub struct OrderElement {
|
pub struct Product<'a> {
|
||||||
pub id: i32,
|
id: i32,
|
||||||
pub product: Product,
|
recipe: &'a Recipe,
|
||||||
pub quantity: i32,
|
volume: f64,
|
||||||
pub total_price: BigDecimal,
|
price_per_unit: rust_decimal::Decimal,
|
||||||
}
|
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct OrderElementRow {
|
|
||||||
pub id: i32,
|
|
||||||
pub product_id: i32,
|
|
||||||
pub quantity: i32,
|
|
||||||
pub total_price: BigDecimal,
|
|
||||||
pub is_incoming: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Product {
|
|
||||||
pub id: i32,
|
|
||||||
pub recipe: Recipe,
|
|
||||||
pub volume: f64,
|
|
||||||
pub price_per_unit: BigDecimal,
|
|
||||||
}
|
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct ProductRow {
|
|
||||||
pub id: i32,
|
|
||||||
pub recipe_id: i32,
|
|
||||||
pub volume: f64,
|
|
||||||
pub price_per_unit: BigDecimal,
|
|
||||||
}
|
}
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
|
@ -175,22 +151,11 @@ pub struct User {
|
||||||
is_admin: bool,
|
is_admin: bool,
|
||||||
worker: Worker,
|
worker: Worker,
|
||||||
}
|
}
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Promotion {
|
pub struct Promotion {
|
||||||
pub id: i32,
|
|
||||||
pub worker: Worker,
|
pub worker: Worker,
|
||||||
pub from_position: Position,
|
|
||||||
pub to_position: Position,
|
pub to_position: Position,
|
||||||
pub date: DateTime<Local>,
|
pub date: DateTime<Local>,
|
||||||
}
|
}
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct PromotionRow {
|
|
||||||
pub id: i32,
|
|
||||||
pub worker_id: i32,
|
|
||||||
pub from_position_id: i32,
|
|
||||||
pub to_position_id: i32,
|
|
||||||
pub date: DateTime<Local>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct EquipmentModalState {
|
pub struct EquipmentModalState {
|
||||||
|
|
@ -200,14 +165,6 @@ pub struct EquipmentModalState {
|
||||||
pub status: ModalStatus,
|
pub status: ModalStatus,
|
||||||
pub filters: EquipmentFilter,
|
pub filters: EquipmentFilter,
|
||||||
}
|
}
|
||||||
#[derive(Default)]
|
|
||||||
pub struct WorkerModalState {
|
|
||||||
pub is_open: bool,
|
|
||||||
pub can_finish: bool,
|
|
||||||
pub worker: Worker,
|
|
||||||
pub status: ModalStatus,
|
|
||||||
pub old_pos: Position,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ModalWinState {
|
pub struct ModalWinState {
|
||||||
|
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use egui::mutex::RwLock;
|
|
||||||
use egui_extras::Column;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
database::DBOperator,
|
|
||||||
misc,
|
|
||||||
models::Order,
|
|
||||||
tab::{TABS_CAN_BE_WINDOWS, Tab, TabTypes},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct OrderTabViewer {
|
|
||||||
db_oper: DBOperator,
|
|
||||||
rt: tokio::runtime::Runtime,
|
|
||||||
|
|
||||||
incoming_orders: Arc<RwLock<Vec<Order>>>,
|
|
||||||
outcoming_orders: Arc<RwLock<Vec<Order>>>,
|
|
||||||
incoming_order_page: i32,
|
|
||||||
outcoming_order_page: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OrderTabViewer {
|
|
||||||
pub fn show_orders(&mut self, ui: &mut egui::Ui, incoming_orders: bool) {
|
|
||||||
let orders = if incoming_orders {
|
|
||||||
&self.incoming_orders
|
|
||||||
} else {
|
|
||||||
&self.outcoming_orders
|
|
||||||
};
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Добавить").clicked() {}
|
|
||||||
if ui.button("<-").clicked() {}
|
|
||||||
ui.monospace(if incoming_orders {
|
|
||||||
self.incoming_order_page.to_string()
|
|
||||||
} else {
|
|
||||||
self.outcoming_order_page.to_string()
|
|
||||||
});
|
|
||||||
if ui.button("->").clicked() {}
|
|
||||||
});
|
|
||||||
|
|
||||||
egui_extras::TableBuilder::new(ui)
|
|
||||||
.columns(Column::auto(), 5)
|
|
||||||
.striped(true)
|
|
||||||
.vscroll(true)
|
|
||||||
.header(25.0, |mut heading| {
|
|
||||||
heading.col(|ui| {
|
|
||||||
ui.heading(if incoming_orders {
|
|
||||||
"Заказчик"
|
|
||||||
} else {
|
|
||||||
"Клиент"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
heading.col(|ui| {
|
|
||||||
ui.heading("Состав");
|
|
||||||
});
|
|
||||||
heading.col(|ui| {
|
|
||||||
ui.heading("Стоимость");
|
|
||||||
});
|
|
||||||
heading.col(|ui| {
|
|
||||||
ui.heading("Дата создания");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl egui_dock::TabViewer for OrderTabViewer {
|
|
||||||
type Tab = Tab;
|
|
||||||
|
|
||||||
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
|
|
||||||
tab.title.clone().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
|
|
||||||
match tab.tab_type {
|
|
||||||
TabTypes::IncomingOrderList => {
|
|
||||||
self.show_orders(ui, true);
|
|
||||||
}
|
|
||||||
TabTypes::OutcomingOrderList => {
|
|
||||||
self.show_orders(ui, false);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
ui.label("Nope");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn allowed_in_windows(&self, _tab: &mut Self::Tab) -> bool {
|
|
||||||
TABS_CAN_BE_WINDOWS
|
|
||||||
}
|
|
||||||
fn is_closeable(&self, _tab: &Self::Tab) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for OrderTabViewer {
|
|
||||||
fn default() -> Self {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let db_oper = rt.block_on(async { DBOperator::new().await });
|
|
||||||
|
|
||||||
Self {
|
|
||||||
incoming_orders: Arc::new(RwLock::new(
|
|
||||||
rt.block_on(async { db_oper.get_orders(0, misc::ELEMENTS_PER_PAGE, true).await }),
|
|
||||||
)),
|
|
||||||
outcoming_orders: Arc::new(RwLock::new(
|
|
||||||
rt.block_on(async { db_oper.get_orders(0, misc::ELEMENTS_PER_PAGE, false).await }),
|
|
||||||
)),
|
|
||||||
|
|
||||||
incoming_order_page: 0,
|
|
||||||
outcoming_order_page: 0,
|
|
||||||
db_oper,
|
|
||||||
rt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
struct ProductionTabViewer {}
|
||||||
|
|
@ -302,7 +302,7 @@ impl Default for RecipeTabViewer {
|
||||||
rt.block_on(async { db_oper.get_materials().await.unwrap_or(Vec::new()) }),
|
rt.block_on(async { db_oper.get_materials().await.unwrap_or(Vec::new()) }),
|
||||||
)),
|
)),
|
||||||
equipment: Arc::new(RwLock::new(
|
equipment: Arc::new(RwLock::new(
|
||||||
rt.block_on(async { db_oper.get_equipment(0,5, false).await.unwrap_or(Vec::new()) }),
|
rt.block_on(async { db_oper.get_equipment(0,5).await.unwrap_or(Vec::new()) }),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
process_recipe_status: RecipeModalState {
|
process_recipe_status: RecipeModalState {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ pub enum TabTypes {
|
||||||
RecipeList,
|
RecipeList,
|
||||||
RecipeDetail,
|
RecipeDetail,
|
||||||
ProductList,
|
ProductList,
|
||||||
IncomingOrderList,
|
|
||||||
OutcomingOrderList,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,20 @@ use std::default::Default;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::misc::ELEMENTS_PER_PAGE;
|
|
||||||
use crate::models::{
|
use crate::database::*;
|
||||||
ModalStatus, ModalWinState, Position, Promotion, Worker, WorkerModalState, WorkerRow,
|
use crate::models::{ModalStatus, ModalWinState, Position, Worker, WorkerRow};
|
||||||
};
|
|
||||||
use crate::tab::*;
|
use crate::tab::*;
|
||||||
use crate::{database::*, misc};
|
|
||||||
pub struct WorkerTabViewer {
|
pub struct WorkerTabViewer {
|
||||||
db_oper: DBOperator,
|
db_oper: DBOperator,
|
||||||
workers: Arc<RwLock<Vec<Worker>>>,
|
workers: Arc<RwLock<Vec<Worker>>>,
|
||||||
positions: Arc<RwLock<Vec<Position>>>,
|
positions: Arc<RwLock<Vec<Position>>>,
|
||||||
promotions: Arc<RwLock<Vec<Promotion>>>,
|
|
||||||
|
|
||||||
rt: tokio::runtime::Runtime,
|
rt: tokio::runtime::Runtime,
|
||||||
|
|
||||||
process_worker_state: WorkerModalState,
|
process_worker_state: ModalWinState,
|
||||||
process_position_state: ModalWinState,
|
process_position_state: ModalWinState,
|
||||||
is_admin: bool,
|
is_admin: bool,
|
||||||
|
|
||||||
only_not_fired_workers: bool,
|
|
||||||
workers_page: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerTabViewer {
|
impl WorkerTabViewer {
|
||||||
|
|
@ -36,16 +30,10 @@ impl WorkerTabViewer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn update_workers(&mut self) {
|
fn update_workers(&mut self) {
|
||||||
self.workers = Arc::new(RwLock::new(self.rt.block_on(async {
|
self.workers = Arc::new(RwLock::new(
|
||||||
self.db_oper
|
self.rt
|
||||||
.get_workers(
|
.block_on(async { self.db_oper.get_workers().await.unwrap() }),
|
||||||
self.workers_page,
|
));
|
||||||
ELEMENTS_PER_PAGE,
|
|
||||||
self.only_not_fired_workers,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
fn update_positions(&mut self) {
|
fn update_positions(&mut self) {
|
||||||
self.positions = Arc::new(RwLock::new(
|
self.positions = Arc::new(RwLock::new(
|
||||||
|
|
@ -58,14 +46,28 @@ impl WorkerTabViewer {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Добавить").clicked() {
|
if ui.button("Добавить").clicked() {
|
||||||
self.process_worker_state.is_open = true;
|
self.process_worker_state.is_open = true;
|
||||||
self.process_worker_state.worker = Worker::default();
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("id".to_string(), "0".to_string());
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("full_name".to_string(), "".to_string());
|
||||||
|
self.process_worker_state.data.insert(
|
||||||
|
"position_id".to_string(),
|
||||||
|
self.positions.read()[0].name.clone(),
|
||||||
|
);
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("is_fired".to_string(), false.to_string());
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("hire_date".to_string(), "".to_string());
|
||||||
};
|
};
|
||||||
if ui.button("Обновить").clicked() {
|
if ui.button("Обновить").clicked() {
|
||||||
self.update_workers();
|
self.update_workers();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if self.process_worker_state.is_open {
|
if self.process_worker_state.is_open {
|
||||||
let pos = self.process_worker_state.worker.position.clone();
|
|
||||||
egui::Modal::new(egui::Id::new("process_worker_modal")).show(ui.ctx(), |ui| {
|
egui::Modal::new(egui::Id::new("process_worker_modal")).show(ui.ctx(), |ui| {
|
||||||
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape))
|
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape))
|
||||||
{
|
{
|
||||||
|
|
@ -76,25 +78,34 @@ impl WorkerTabViewer {
|
||||||
ui.label("Имя:");
|
ui.label("Имя:");
|
||||||
ui.add(
|
ui.add(
|
||||||
egui::TextEdit::singleline(
|
egui::TextEdit::singleline(
|
||||||
&mut self.process_worker_state.worker.full_name,
|
self.process_worker_state.data.get_mut("full_name").unwrap(),
|
||||||
)
|
)
|
||||||
.hint_text("Иванов Иван Иванович"),
|
.hint_text("Иванов Иван Иванович"),
|
||||||
);
|
);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
ui.label("Должность:");
|
ui.label("Должность:");
|
||||||
egui::ComboBox::new("process_worker_combobox", "")
|
let posname = self
|
||||||
.selected_text(if self.process_worker_state.worker.position.id == 0 {
|
.positions
|
||||||
"Выберите:"
|
.read()
|
||||||
} else {
|
.clone()
|
||||||
&self.process_worker_state.worker.position.name
|
.iter()
|
||||||
|
.find(|p| {
|
||||||
|
p.id.to_string()
|
||||||
|
== *self.process_worker_state.data.get("position_id").unwrap()
|
||||||
})
|
})
|
||||||
|
.map(|m| m.name.clone());
|
||||||
|
egui::ComboBox::new("process_worker_combobox", "")
|
||||||
|
.selected_text(posname.unwrap_or("Выберите".to_string()))
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for pos in self.positions.read().clone().iter() {
|
for pos in self.positions.read().clone().iter() {
|
||||||
ui.selectable_value(
|
ui.selectable_value(
|
||||||
&mut self.process_worker_state.worker.position,
|
self.process_worker_state
|
||||||
pos.clone(),
|
.data
|
||||||
&pos.name,
|
.get_mut("position_id")
|
||||||
|
.unwrap(),
|
||||||
|
pos.id.to_string(),
|
||||||
|
pos.name.clone(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -103,31 +114,34 @@ impl WorkerTabViewer {
|
||||||
let mut size = ui.spacing().interact_size;
|
let mut size = ui.spacing().interact_size;
|
||||||
size.x = 200.0;
|
size.x = 200.0;
|
||||||
ui.label("Дата найма:");
|
ui.label("Дата найма:");
|
||||||
let ndate = self.process_worker_state.worker.hire_date.date_naive();
|
ui.add_sized(
|
||||||
let mut date = misc::naivedate_to_jiff(ndate);
|
size,
|
||||||
ui.add(egui_extras::DatePickerButton::new(&mut date));
|
egui::TextEdit::singleline(
|
||||||
if misc::naivedate_to_jiff(
|
self.process_worker_state.data.get_mut("hire_date").unwrap(),
|
||||||
self.process_worker_state.worker.hire_date.date_naive(),
|
)
|
||||||
) != date
|
.hint_text("1970-01-23"),
|
||||||
{
|
);
|
||||||
self.process_worker_state.worker.hire_date =
|
|
||||||
misc::jiff_to_naivedate(date)
|
|
||||||
.and_hms_opt(0, 0, 0)
|
|
||||||
.unwrap()
|
|
||||||
.and_local_timezone(chrono::Local)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
if self.process_worker_state.status == ModalStatus::Edit {
|
if self.process_worker_state.status == ModalStatus::Edit {
|
||||||
ui.label("Уволен:");
|
ui.label("Уволен:");
|
||||||
ui.checkbox(&mut self.process_worker_state.worker.is_fired, "");
|
let data = self.process_worker_state.data.get("is_fired").unwrap();
|
||||||
|
let mut checked = data.parse::<bool>().unwrap();
|
||||||
|
ui.checkbox(&mut checked, "");
|
||||||
|
if checked.to_string() != data.clone() {
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("is_fired".to_string(), checked.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.process_worker_state.can_finish =
|
self.process_worker_state.can_finish = self.check_date(
|
||||||
self.process_worker_state.worker.full_name.len() > 0
|
self.process_worker_state
|
||||||
&& self.process_worker_state.worker.position.id != 0;
|
.data
|
||||||
|
.get("hire_date")
|
||||||
|
.unwrap()
|
||||||
|
.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let btext = match self.process_worker_state.status {
|
let btext = match self.process_worker_state.status {
|
||||||
ModalStatus::Add => "Добавить",
|
ModalStatus::Add => "Добавить",
|
||||||
|
|
@ -146,27 +160,91 @@ impl WorkerTabViewer {
|
||||||
ModalStatus::Add => {
|
ModalStatus::Add => {
|
||||||
self.rt.block_on(async {
|
self.rt.block_on(async {
|
||||||
self.db_oper
|
self.db_oper
|
||||||
.add_worker(self.process_worker_state.worker.clone())
|
.add_worker(WorkerRow {
|
||||||
|
id: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("id")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap(),
|
||||||
|
full_name: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("full_name")
|
||||||
|
.unwrap()
|
||||||
|
.clone(),
|
||||||
|
position_id: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("position_id")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap(),
|
||||||
|
hire_date: chrono::NaiveDate::parse_from_str(
|
||||||
|
&self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("hire_date")
|
||||||
|
.unwrap(),
|
||||||
|
"%Y-%m-%d",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.and_hms_opt(0, 0, 0)
|
||||||
|
.unwrap()
|
||||||
|
.and_local_timezone(chrono::Local)
|
||||||
|
.unwrap(),
|
||||||
|
is_fired: false,
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ModalStatus::Edit => {
|
ModalStatus::Edit => {
|
||||||
self.rt.block_on(async {
|
self.rt.block_on(async {
|
||||||
if self.process_worker_state.worker.position.id
|
|
||||||
!= self.process_worker_state.old_pos.id
|
|
||||||
{
|
|
||||||
self.db_oper
|
|
||||||
.promote_worker(
|
|
||||||
self.process_worker_state.worker.clone(),
|
|
||||||
self.process_worker_state.old_pos.clone(),
|
|
||||||
chrono::Local::now(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
self.db_oper
|
self.db_oper
|
||||||
.update_worker(self.process_worker_state.worker.clone())
|
.update_worker(WorkerRow {
|
||||||
|
id: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("id")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap(),
|
||||||
|
full_name: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("full_name")
|
||||||
|
.unwrap()
|
||||||
|
.clone(),
|
||||||
|
position_id: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("position_id")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap(),
|
||||||
|
is_fired: self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("is_fired")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<bool>()
|
||||||
|
.unwrap(),
|
||||||
|
hire_date: chrono::NaiveDate::parse_from_str(
|
||||||
|
&self
|
||||||
|
.process_worker_state
|
||||||
|
.data
|
||||||
|
.get("hire_date")
|
||||||
|
.unwrap(),
|
||||||
|
"%Y-%m-%d",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.and_hms_opt(0, 0, 0)
|
||||||
|
.unwrap()
|
||||||
|
.and_local_timezone(chrono::Local)
|
||||||
|
.unwrap(),
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
});
|
});
|
||||||
|
|
@ -216,11 +294,25 @@ impl WorkerTabViewer {
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
ui.label(if wk.is_fired { "Да" } else { "Нет" });
|
ui.label(if wk.is_fired { "Да" } else { "Нет" });
|
||||||
});
|
});
|
||||||
if row.response().double_clicked() {
|
if row.response().clicked() {
|
||||||
self.process_worker_state.is_open = true;
|
self.process_worker_state.is_open = true;
|
||||||
self.process_worker_state.status = ModalStatus::Edit;
|
self.process_worker_state.status = ModalStatus::Edit;
|
||||||
self.process_worker_state.worker = wk.clone();
|
self.process_worker_state
|
||||||
self.process_worker_state.old_pos = wk.position.clone();
|
.data
|
||||||
|
.insert("id".to_string(), wk.id.to_string());
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("full_name".to_string(), wk.full_name.clone());
|
||||||
|
self.process_worker_state.data.insert(
|
||||||
|
"hire_date".to_string(),
|
||||||
|
wk.hire_date.date_naive().to_string(),
|
||||||
|
);
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("is_fired".to_string(), wk.is_fired.to_string());
|
||||||
|
self.process_worker_state
|
||||||
|
.data
|
||||||
|
.insert("position_id".to_string(), wk.position.id.to_string());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -415,44 +507,6 @@ impl WorkerTabViewer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn show_promotions(&self, ui: &mut egui::Ui) {
|
|
||||||
egui_extras::TableBuilder::new(ui)
|
|
||||||
.columns(Column::auto(), 4)
|
|
||||||
.striped(true)
|
|
||||||
.vscroll(false)
|
|
||||||
.header(25.0, |mut header| {
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.heading("Работник");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.heading("Из");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.heading("В");
|
|
||||||
});
|
|
||||||
header.col(|ui| {
|
|
||||||
ui.heading("Дата");
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.body(|mut body| {
|
|
||||||
for pr in self.promotions.read().iter().clone() {
|
|
||||||
body.row(25.0, |mut row| {
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(&pr.worker.full_name);
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(&pr.from_position.name);
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(&pr.to_position.name);
|
|
||||||
});
|
|
||||||
row.col(|ui| {
|
|
||||||
ui.label(pr.date.date_naive().to_string());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_date(&self, date: String) -> bool {
|
fn check_date(&self, date: String) -> bool {
|
||||||
chrono::NaiveDate::parse_from_str(&date, "%Y-%m-%d").is_ok()
|
chrono::NaiveDate::parse_from_str(&date, "%Y-%m-%d").is_ok()
|
||||||
|
|
@ -468,7 +522,6 @@ impl egui_dock::TabViewer for WorkerTabViewer {
|
||||||
match &tab.tab_type {
|
match &tab.tab_type {
|
||||||
TabTypes::WorkerList => self.show_worker(ui),
|
TabTypes::WorkerList => self.show_worker(ui),
|
||||||
TabTypes::WorkerPosition => self.show_position(ui),
|
TabTypes::WorkerPosition => self.show_position(ui),
|
||||||
TabTypes::WorkerPromotion => self.show_promotions(ui),
|
|
||||||
_ => {
|
_ => {
|
||||||
ui.label("Nope");
|
ui.label("Nope");
|
||||||
}
|
}
|
||||||
|
|
@ -491,14 +544,8 @@ impl Default for WorkerTabViewer {
|
||||||
let positions = Arc::new(RwLock::new(
|
let positions = Arc::new(RwLock::new(
|
||||||
rt.block_on(async { db_oper.get_position().await.unwrap() }),
|
rt.block_on(async { db_oper.get_position().await.unwrap() }),
|
||||||
));
|
));
|
||||||
let workers = Arc::new(RwLock::new(rt.block_on(async {
|
let workers = Arc::new(RwLock::new(
|
||||||
db_oper
|
rt.block_on(async { db_oper.get_workers().await.unwrap() }),
|
||||||
.get_workers(0, misc::ELEMENTS_PER_PAGE, false)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
})));
|
|
||||||
let promotions = Arc::new(RwLock::new(
|
|
||||||
rt.block_on(async { db_oper.get_promotions(0, 5).await.unwrap() }),
|
|
||||||
));
|
));
|
||||||
let mut pos_map: HashMap<String, String> = HashMap::new();
|
let mut pos_map: HashMap<String, String> = HashMap::new();
|
||||||
pos_map.extend(|| -> Vec<(String, String)> {
|
pos_map.extend(|| -> Vec<(String, String)> {
|
||||||
|
|
@ -509,7 +556,20 @@ impl Default for WorkerTabViewer {
|
||||||
("wage".to_string(), pos.wage.to_string()),
|
("wage".to_string(), pos.wage.to_string()),
|
||||||
]
|
]
|
||||||
}());
|
}());
|
||||||
let worker = workers.read()[0].clone();
|
let mut worker_map: HashMap<String, String> = HashMap::new();
|
||||||
|
worker_map.extend(|| -> Vec<(String, String)> {
|
||||||
|
let worker = workers.read()[0].clone();
|
||||||
|
vec![
|
||||||
|
("id".to_string(), worker.id.to_string()),
|
||||||
|
("full_name".to_string(), worker.full_name),
|
||||||
|
("position_id".to_string(), worker.position.id.to_string()),
|
||||||
|
(
|
||||||
|
"hire_date".to_string(),
|
||||||
|
worker.hire_date.date_naive().format("%d-%m-%Y").to_string(),
|
||||||
|
),
|
||||||
|
("is_fired".to_string(), worker.is_fired.to_string()),
|
||||||
|
]
|
||||||
|
}());
|
||||||
Self {
|
Self {
|
||||||
process_position_state: ModalWinState {
|
process_position_state: ModalWinState {
|
||||||
is_open: false,
|
is_open: false,
|
||||||
|
|
@ -517,22 +577,18 @@ impl Default for WorkerTabViewer {
|
||||||
data: pos_map,
|
data: pos_map,
|
||||||
status: ModalStatus::Add,
|
status: ModalStatus::Add,
|
||||||
},
|
},
|
||||||
process_worker_state: WorkerModalState {
|
process_worker_state: ModalWinState {
|
||||||
is_open: false,
|
is_open: false,
|
||||||
can_finish: false,
|
can_finish: false,
|
||||||
worker,
|
data: worker_map,
|
||||||
status: ModalStatus::Add,
|
status: ModalStatus::Add,
|
||||||
old_pos: Position::default(),
|
|
||||||
},
|
},
|
||||||
workers,
|
workers,
|
||||||
positions,
|
positions: positions,
|
||||||
promotions,
|
|
||||||
|
|
||||||
db_oper,
|
db_oper,
|
||||||
rt,
|
rt,
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
only_not_fired_workers: false,
|
|
||||||
workers_page: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue