Compare commits

..

2 Commits

Author SHA1 Message Date
Алексей Алексей 97d73cf027 платежи - готово(+отчёты)
Отчёты по остальным категориям сделаю потом, когда доделаю рецепты
2026-06-20 16:44:15 +10:00
Алексей Алексей 359e254c0a платежи почти всё 2026-06-20 12:36:48 +10:00
9 changed files with 365 additions and 55 deletions

50
code/Cargo.lock generated
View File

@ -770,6 +770,7 @@ dependencies = [
"egui_extras", "egui_extras",
"hex", "hex",
"rust_decimal", "rust_decimal",
"rust_xlsxwriter",
"sha2 0.11.0", "sha2 0.11.0",
"sqlx", "sqlx",
"tokio", "tokio",
@ -1424,6 +1425,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
"zlib-rs",
] ]
[[package]] [[package]]
@ -3472,6 +3474,16 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "rust_xlsxwriter"
version = "0.95.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f281b687352597d29efaad39701d1167d5c48aa76fb973e392bc13e9d44e7f36"
dependencies = [
"chrono",
"zip",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -4392,6 +4404,12 @@ dependencies = [
"rustc-hash 2.1.2", "rustc-hash 2.1.2",
] ]
[[package]]
name = "typed-path"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.20.1" version = "1.20.1"
@ -5805,12 +5823,44 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "zip"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0"
dependencies = [
"crc32fast",
"flate2",
"indexmap",
"memchr",
"typed-path",
"zopfli",
]
[[package]]
name = "zlib-rs"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zopfli"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
dependencies = [
"bumpalo",
"crc32fast",
"log",
"simd-adler32",
]
[[package]] [[package]]
name = "zune-core" name = "zune-core"
version = "0.5.1" version = "0.5.1"

View File

@ -14,6 +14,7 @@ egui_dock = "0.19.1"
egui_extras = {version = "0.34.2", features = ["datepicker", "image"]} egui_extras = {version = "0.34.2", features = ["datepicker", "image"]}
hex = "0.4.3" hex = "0.4.3"
rust_decimal = {version = "1.42.0", features = ["macros"]} rust_decimal = {version = "1.42.0", features = ["macros"]}
rust_xlsxwriter = {version="0.95.0",features=["chrono"]}
sha2 = "0.11.0" sha2 = "0.11.0"
sqlx = {version="0.8.6", features = ["runtime-tokio", "mysql","bigdecimal","chrono"]} sqlx = {version="0.8.6", features = ["runtime-tokio", "mysql","bigdecimal","chrono"]}
tokio={version = "1.52.3", features = ["full"]} tokio={version = "1.52.3", features = ["full"]}

View File

@ -13,6 +13,8 @@ use crate::{
Equipment, LoginModalState, LoginStatus, Material, MaterialCategory, MaterialRow, Equipment, LoginModalState, LoginStatus, Material, MaterialCategory, MaterialRow,
ModalStatus, ModalWinState, Position, Salary, SalaryModalState, Worker, ModalStatus, ModalWinState, Position, Salary, SalaryModalState, Worker,
}, },
recipe_tab::RecipeTabViewer,
reports::Reporter,
tab::*, tab::*,
worker_tab::{self, *}, worker_tab::{self, *},
}; };
@ -142,10 +144,11 @@ impl eframe::App for App<'_> {
if self.rt.block_on(async { if self.rt.block_on(async {
self.db_oper.check_admin(uname.clone()).await.unwrap() self.db_oper.check_admin(uname.clone()).await.unwrap()
}) { }) {
self.main_viewer.is_admin = true self.main_viewer.is_admin = true;
} else { } else {
self.main_viewer.is_admin = false; self.main_viewer.is_admin = false;
} }
self.main_viewer = MainTabViewer::new(self.main_viewer.is_admin);
} else { } else {
self.login_modal_state.fail = true; self.login_modal_state.fail = true;
self.login_modal_state.fail_message = self.login_modal_state.fail_message =
@ -179,12 +182,14 @@ struct MainTabViewer {
worker_tree: egui_dock::DockState<Tab>, worker_tree: egui_dock::DockState<Tab>,
material_tabs: MaterialTabViewer, material_tabs: MaterialTabViewer,
material_tree: egui_dock::DockState<Tab>, material_tree: egui_dock::DockState<Tab>,
recipe_tab: RecipeTabViewer,
rt: tokio::runtime::Runtime, rt: tokio::runtime::Runtime,
equipment_modal_state: ModalWinState, equipment_modal_state: ModalWinState,
salary_modal_state: SalaryModalState, salary_modal_state: SalaryModalState,
is_dark_theme: bool, is_dark_theme: bool,
interface_scale_ratio: f32, interface_scale_ratio: f32,
is_admin: bool, is_admin: bool,
reporter: Reporter,
} }
impl Default for MainTabViewer { impl Default for MainTabViewer {
fn default() -> Self { fn default() -> Self {
@ -223,6 +228,7 @@ impl Default for MainTabViewer {
tab_type: TabTypes::MaterialTypeList, tab_type: TabTypes::MaterialTypeList,
}, },
]), ]),
recipe_tab: RecipeTabViewer::default(),
rt, rt,
is_dark_theme: true, is_dark_theme: true,
interface_scale_ratio: 1.2, interface_scale_ratio: 1.2,
@ -251,10 +257,50 @@ impl Default for MainTabViewer {
workers: HashSet::new(), workers: HashSet::new(),
}, },
is_admin: false, is_admin: false,
reporter: Reporter {},
} }
} }
} }
impl MainTabViewer { impl MainTabViewer {
fn update_salary(&mut self) {
self.salaries = Arc::new(RwLock::new(
self.rt
.block_on(async { self.db_oper.get_salaries().await.unwrap() }),
));
}
fn get_salary(&self) -> Salary {
Salary {
id: self
.salary_modal_state
.data
.get("id")
.unwrap()
.parse::<i32>()
.unwrap(),
worker: self
.workers
.read()
.iter()
.find(|w| {
&w.id.to_string() == self.salary_modal_state.data.get("worker_id").unwrap()
})
.unwrap()
.clone(),
salary_size: BigDecimal::from_str(
self.salary_modal_state.data.get("salary_size").unwrap(),
)
.unwrap(),
salary_date: chrono::NaiveDate::from_str(
self.salary_modal_state.data.get("salary_date").unwrap(),
)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(chrono::Local)
.unwrap(),
comment: self.salary_modal_state.data.get("comment").unwrap().clone(),
}
}
fn new(is_admin: bool) -> Self { fn new(is_admin: bool) -> Self {
Self { Self {
is_admin, is_admin,
@ -287,6 +333,7 @@ impl MainTabViewer {
let mut table = TableBuilder::new(ui) let mut table = TableBuilder::new(ui)
.striped(true) .striped(true)
.resizable(false) .resizable(false)
.vscroll(false)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center)) .cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.column(Column::auto()) .column(Column::auto())
.column(Column::auto()) .column(Column::auto())
@ -512,6 +559,7 @@ impl MainTabViewer {
.striped(true) .striped(true)
.columns(Column::auto(), 5) .columns(Column::auto(), 5)
.sense(egui::Sense::click()) .sense(egui::Sense::click())
.vscroll(false)
.header(30.0, |mut header| { .header(30.0, |mut header| {
header.col(|ui| { header.col(|ui| {
ui.heading("Имя сотрудника"); ui.heading("Имя сотрудника");
@ -523,13 +571,13 @@ impl MainTabViewer {
ui.heading("Дата"); ui.heading("Дата");
}); });
header.col(|ui| { header.col(|ui| {
ui.heading("Описание"); ui.heading("Примечание");
}); });
}) })
.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 sal in (self.salaries.read().iter().clone()) { for sal in (self.salaries.read().iter().clone()) {
body.row(20.0, |mut row| { body.row(25.0, |mut row| {
row.col(|ui| { row.col(|ui| {
ui.label(&sal.worker.full_name); ui.label(&sal.worker.full_name);
}); });
@ -540,7 +588,8 @@ impl MainTabViewer {
ui.label(&sal.salary_date.date_naive().to_string()); ui.label(&sal.salary_date.date_naive().to_string());
}); });
row.col(|ui| { row.col(|ui| {
ui.label(&sal.comment); let text: String = sal.comment.chars().take(15).collect();
ui.label(text);
}); });
row.col(|ui| { row.col(|ui| {
if ui.button("Удалить").clicked() { if ui.button("Удалить").clicked() {
@ -553,9 +602,10 @@ impl MainTabViewer {
self.salary_modal_state self.salary_modal_state
.data .data
.insert("salary_size".to_owned(), sal.salary_size.to_string()); .insert("salary_size".to_owned(), sal.salary_size.to_string());
self.salary_modal_state self.salary_modal_state.data.insert(
.data "salary_date".to_owned(),
.insert("salary_date".to_owned(), sal.salary_date.to_string()); sal.salary_date.date_naive().to_string(),
);
self.salary_modal_state self.salary_modal_state
.data .data
.insert("comment".to_owned(), sal.comment.clone()); .insert("comment".to_owned(), sal.comment.clone());
@ -588,10 +638,16 @@ impl MainTabViewer {
}); });
if self.salary_modal_state.is_open { if self.salary_modal_state.is_open {
egui::Modal::new(egui::Id::new("salary_modal")).show(ui.ctx(), |ui| { egui::Modal::new(egui::Id::new("salary_modal")).show(ui.ctx(), |ui| {
if ui.button("Закрыть").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape)) ui.horizontal(|ui| {
{ if ui.button("Закрыть").clicked()
self.salary_modal_state.is_open = false; || ui.input(|i| i.key_pressed(egui::Key::Escape))
} {
self.salary_modal_state.is_open = false;
}
if ui.button("Отчёт").clicked() {
self.reporter.payment_report(self.get_salary().clone());
}
});
if self.salary_modal_state.status != ModalStatus::Remove { if self.salary_modal_state.status != ModalStatus::Remove {
egui::Grid::new("process_salary_grid").show(ui, |ui| { egui::Grid::new("process_salary_grid").show(ui, |ui| {
let size = ui.available_size(); let size = ui.available_size();
@ -639,28 +695,147 @@ impl MainTabViewer {
size, size,
egui::TextEdit::singleline( egui::TextEdit::singleline(
self.salary_modal_state.data.get_mut("salary_date").unwrap(), self.salary_modal_state.data.get_mut("salary_date").unwrap(),
), )
.hint_text("1970-01-01"),
); );
ui.end_row(); ui.end_row();
ui.label("Примечание:"); ui.label("Примечание:");
ui.add_sized(size, ui.add_sized(
egui::TextEdit::singleline(self.salary_modal_state.data.get_mut("comment").unwrap())); size,
egui::TextEdit::multiline(
self.salary_modal_state.data.get_mut("comment").unwrap(),
),
);
ui.end_row(); ui.end_row();
}); });
} else {
}else{ let wname = self
let wname = self.workers.read().clone().iter().find(|w| w.id.to_string() == self.salary_modal_state.data.get("worker_id").unwrap()).unwrap().full_name; .workers
.read()
.clone()
.iter()
.find(|w| {
&w.id.to_string()
== self.salary_modal_state.data.get("worker_id").unwrap()
})
.unwrap()
.full_name
.clone();
ui.label("Вы уверены, что хотите удалить запись о выплате:"); ui.label("Вы уверены, что хотите удалить запись о выплате:");
ui.label(format!("{}: {} | ${}", self.salary_modal_state.data.get("salary_date").unwrap(), wname, self.salary_modal_state.data.get("salary_size").unwrap())); ui.label(format!(
"{}: {} | ${}",
self.salary_modal_state.data.get("salary_date").unwrap(),
wname,
self.salary_modal_state.data.get("salary_size").unwrap()
));
} }
let btext = match self.salary_modal_state.status{ let btext = match self.salary_modal_state.status {
ModalStatus::Add => "Добавить", ModalStatus::Add => "Добавить",
ModalStatus::Edit => "Сохранить", ModalStatus::Edit => "Сохранить",
ModalStatus::Remove => "Удалить" ModalStatus::Remove => "Удалить",
}; };
if ui.button(btext).clicked(){ if ui.button(btext).clicked() {
match self.salary_modal_state.status {
ModalStatus::Add => {
self.rt.block_on(async {
for w in self.salary_modal_state.workers.iter() {
self.db_oper
.add_salary(
w.id,
chrono::NaiveDate::from_str(
self.salary_modal_state
.data
.get("salary_date")
.unwrap(),
)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(chrono::Local)
.unwrap(),
BigDecimal::from_str(
self.salary_modal_state
.data
.get("salary_size")
.unwrap(),
)
.unwrap(),
Some(
self.salary_modal_state
.data
.get("comment")
.unwrap()
.clone(),
),
)
.await
.ok();
}
});
}
ModalStatus::Edit => {
let sal = Salary {
id: self
.salary_modal_state
.data
.get("id")
.unwrap()
.parse::<i32>()
.unwrap(),
worker: self
.workers
.read()
.iter()
.find(|w| {
&w.id.to_string()
== self
.salary_modal_state
.data
.get("worker_id")
.unwrap()
})
.unwrap()
.clone(),
salary_size: BigDecimal::from_str(
self.salary_modal_state.data.get("salary_size").unwrap(),
)
.unwrap(),
salary_date: chrono::NaiveDate::parse_from_str(
self.salary_modal_state.data.get("salary_date").unwrap(),
"%Y-%m-%d",
)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_local_timezone(chrono::Local)
.unwrap(),
comment: self
.salary_modal_state
.data
.get("comment")
.unwrap()
.clone(),
};
self.rt.block_on(async {
self.db_oper.update_salary(sal).await;
});
}
ModalStatus::Remove => self.rt.block_on(async {
self.db_oper
.remove_salary(
self.salary_modal_state
.data
.get("id")
.unwrap()
.parse::<i32>()
.unwrap(),
)
.await;
}),
} }
self.salary_modal_state.is_open = false;
self.update_salary();
}
}); });
} }
/* /*
@ -681,6 +856,10 @@ impl MainTabViewer {
self.interface_scale_ratio = 1.2; self.interface_scale_ratio = 1.2;
} }
} }
fn show_recipe(&mut self, ui: &mut egui::Ui) {
self.recipe_tab.show_recipe_list(ui);
}
} }
impl egui_dock::TabViewer for MainTabViewer { impl egui_dock::TabViewer for MainTabViewer {
@ -706,6 +885,9 @@ impl egui_dock::TabViewer for MainTabViewer {
TabTypes::Settings => { TabTypes::Settings => {
&self.show_settings(ui); &self.show_settings(ui);
} }
TabTypes::Recipe => {
&self.show_recipe(ui);
}
_ => { _ => {
ui.label("Welcome to Avalonia!"); ui.label("Welcome to Avalonia!");
} }

View File

@ -365,14 +365,14 @@ impl DBOperator {
} }
pub async fn add_salary( pub async fn add_salary(
&self, &self,
worker: Worker, worker_id: i32,
salary_date: DateTime<chrono::Local>, salary_date: DateTime<chrono::Local>,
salary_size: Option<BigDecimal>, salary_size: BigDecimal,
comment: Option<String>, comment: Option<String>,
) -> Result<bool, sqlx::Error> { ) -> Result<bool, sqlx::Error> {
match sqlx::query("INSERT INTO Brewery.salary (id, worker_id, salary_size, salary_date, comment) VALUES(0, ?, ?, ?, ?);") match sqlx::query("INSERT INTO Brewery.salary (id, worker_id, salary_size, salary_date, comment) VALUES(0, ?, ?, ?, ?);")
.bind(worker.id) .bind(worker_id)
.bind(salary_size.unwrap_or(worker.position.wage)) .bind(salary_size)
.bind(salary_date) .bind(salary_date)
.bind(comment.unwrap_or(String::new())) .bind(comment.unwrap_or(String::new()))
.execute(&self.pool) .execute(&self.pool)
@ -381,6 +381,29 @@ impl DBOperator {
Err(_) => Ok(false) Err(_) => Ok(false)
} }
} }
pub async fn update_salary(&self, salary: Salary) -> Result<bool, sqlx::Error> {
match sqlx::query("UPDATE Brewery.salary SET worker_id=?, salary_size=?, salary_date=?, comment=? WHERE id=?;")
.bind(salary.worker.id)
.bind(salary.salary_size)
.bind(salary.salary_date)
.bind(salary.comment)
.bind(salary.id)
.execute(&self.pool)
.await{
Ok(_) => Ok(true),
Err(_) => Ok(false)
}
}
pub async fn remove_salary(&self, id: i32) -> Result<bool, sqlx::Error> {
match sqlx::query("DELETE FROM Brewery.salary WHERE id=?;")
.bind(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> {

View File

@ -1,10 +1,13 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use crate::reports::Reporter;
mod app; mod app;
mod database; mod database;
mod material_tab; mod material_tab;
mod models; mod models;
mod payment_tab; mod payment_tab;
mod recipe_tab; mod recipe_tab;
mod reports;
mod tab; mod tab;
mod worker_database; mod worker_database;
mod worker_tab; mod worker_tab;

View File

@ -41,13 +41,15 @@ impl MaterialTabViewer {
} }
fn show_material(&mut self, ui: &mut egui::Ui) { fn show_material(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.button("Добавить").clicked() { if self.is_admin {
self.process_material_state.is_open = true; if ui.button("Добавить").clicked() {
self.process_material_state.status = ModalStatus::Add; self.process_material_state.is_open = true;
} self.process_material_state.status = ModalStatus::Add;
if ui.button("Удалить").clicked() { }
self.process_material_state.is_open = true; // if ui.button("Удалить").clicked() {
self.process_material_state.status = ModalStatus::Remove; // self.process_material_state.is_open = true;
// self.process_material_state.status = ModalStatus::Remove;
// }
} }
if ui.button("Обновить").clicked() { if ui.button("Обновить").clicked() {
self.update_materials(); self.update_materials();
@ -352,15 +354,13 @@ impl MaterialTabViewer {
} }
} }
fn show_material_type(&mut self, ui: &mut egui::Ui) { fn show_material_category(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.button("Добавить").clicked() { if self.is_admin {
self.process_mcat_state.is_open = true; if ui.button("Добавить").clicked() {
self.process_mcat_state.status = ModalStatus::Add; self.process_mcat_state.is_open = true;
} self.process_mcat_state.status = ModalStatus::Add;
if ui.button("Удалить").clicked() { }
self.process_mcat_state.is_open = true;
self.process_mcat_state.status = ModalStatus::Remove;
} }
if ui.button("Обновить").clicked() { if ui.button("Обновить").clicked() {
self.update_material_category(); self.update_material_category();
@ -526,7 +526,7 @@ impl egui_dock::TabViewer for MaterialTabViewer {
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) { fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match &tab.tab_type { match &tab.tab_type {
TabTypes::MaterialList => self.show_material(ui), TabTypes::MaterialList => self.show_material(ui),
TabTypes::MaterialTypeList => self.show_material_type(ui), TabTypes::MaterialTypeList => self.show_material_category(ui),
_ => { _ => {
ui.label("Каким образом?"); ui.label("Каким образом?");
} }

View File

@ -45,7 +45,7 @@ pub struct SalaryRow {
pub salary_date: chrono::DateTime<chrono::Local>, pub salary_date: chrono::DateTime<chrono::Local>,
pub comment: String, pub comment: String,
} }
#[derive(Clone)]
pub struct Salary { pub struct Salary {
pub id: i32, pub id: i32,
pub worker: Worker, pub worker: Worker,

View File

@ -1,19 +1,33 @@
use crate::tab::{Tab, TabTypes}; use tokio::sync::RwLock;
use std::default::Default;
struct RecipeTabViewer { use crate::{
database::DBOperator,
models::{Equipment, Material, Recipe, RecipeElement},
tab::{Tab, TabTypes},
};
use std::{default::Default, sync::Arc};
pub struct RecipeTabViewer {
is_admin: bool, is_admin: bool,
rt: tokio::runtime::Runtime,
db_oper: DBOperator,
recipes: Arc<RwLock<Vec<Recipe>>>,
recipe_elements: Arc<RwLock<Vec<RecipeElement>>>,
materials: Arc<RwLock<Vec<Material>>>,
equipment: Arc<RwLock<Vec<Equipment>>>,
} }
impl RecipeTabViewer { impl RecipeTabViewer {
fn new(is_admin: bool) -> Self { pub fn new(is_admin: bool) -> Self {
Self { Self {
is_admin, is_admin,
..Default::default() ..Default::default()
} }
} }
fn show_recipe_list(&mut self, ui: &mut egui::Ui) {} pub fn show_recipe_list(&mut self, ui: &mut egui::Ui) {
fn show_recipe_detail(&mut self, ui: &mut egui::Ui) {} ui.label("Goida");
}
} }
impl egui_dock::TabViewer for RecipeTabViewer { impl egui_dock::TabViewer for RecipeTabViewer {
type Tab = Tab; type Tab = Tab;
@ -27,9 +41,6 @@ impl egui_dock::TabViewer for RecipeTabViewer {
TabTypes::RecipeList => { TabTypes::RecipeList => {
self.show_recipe_list(ui); self.show_recipe_list(ui);
} }
TabTypes::RecipeDetail => {
self.show_recipe_detail(ui);
}
_ => { _ => {
ui.label("Помогите!!"); ui.label("Помогите!!");
} }

40
code/src/reports.rs Normal file
View File

@ -0,0 +1,40 @@
use bigdecimal::ToPrimitive;
use rust_xlsxwriter::{Format, Workbook, XlsxError};
use crate::models::Salary;
pub struct Reporter {}
impl Reporter {
pub fn test_report(&self) -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
let mut worksheet = workbook.add_worksheet();
worksheet.write_string(0, 0, "ZOV");
worksheet.write_formula(0, 1, "=RAND()");
workbook.save("test.xlsx");
Ok(())
}
pub fn payment_report(&self, sal: Salary) -> Result<(), XlsxError> {
let mut wb = Workbook::new();
let mut ws = wb.add_worksheet();
let money_format = Format::new().set_num_format("#,##0.00 ₽");
ws.write_string(0, 0, "ФИО");
ws.write_string(0, 1, "Размер оплаты");
ws.write_string(0, 2, "Дата оплаты");
ws.write_string(0, 3, "Комментарий");
ws.write_string(1, 0, &sal.worker.full_name);
ws.write_with_format(1, 1, sal.salary_size.to_f64().unwrap(), &money_format);
ws.write_datetime(1, 2, sal.salary_date.date_naive());
ws.write_string(1, 3, sal.comment);
ws.autofit();
wb.save(format!(
"report-worker-{}.xlsx",
chrono::Local::now().to_string()
));
Ok(())
}
}