skud/Form1.cs

1653 lines
83 KiB
C#
Raw Permalink Normal View History

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SQLite;
using System.Configuration;
using System.IO;
namespace WindowsFormsApp1
{
2025-04-14 22:09:03 +07:00
// --- Форма авторизации (без изменений) ---
public partial class Form1 : Form
{
// Путь к файлу SQLite
private string connectionString = @"Data Source=EmployeeDB.db;Version=3;";
public Form1()
{
InitializeComponent();
this.AcceptButton = buttonLogin;
this.Text = "Авторизация";
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Добавляем компоненты
InitializeLoginComponents();
// Создаем базу данных, если она не существует
CreateDatabaseIfNotExists();
2025-04-14 22:09:03 +07:00
this.Load += Form1_Load;
}
private void Form1_Load(object sender, EventArgs e)
{
// Используем BeginInvoke для гарантированной установки фокуса после загрузки формы
this.BeginInvoke(new Action(() => {
TextBox txtLogin = (TextBox)this.Controls["txtLogin"];
if (txtLogin != null) // Добавлена проверка на null
{
txtLogin.BringToFront();
txtLogin.Focus();
}
}));
}
private void CreateDatabaseIfNotExists()
{
try
{
if (!File.Exists("EmployeeDB.db"))
{
SQLiteConnection.CreateFile("EmployeeDB.db");
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
// Создаем таблицу пользователей
string createUsersTable = @"CREATE TABLE IF NOT EXISTS Users (
UserID INTEGER PRIMARY KEY AUTOINCREMENT,
2025-04-14 22:09:03 +07:00
Login TEXT NOT NULL UNIQUE,
Password TEXT NOT NULL)"; // Добавлено UNIQUE для Login
using (SQLiteCommand command = new SQLiteCommand(createUsersTable, connection))
{
command.ExecuteNonQuery();
}
2025-04-14 22:09:03 +07:00
// Создаем admin пользователя (если его еще нет)
string checkAdminExists = "SELECT COUNT(*) FROM Users WHERE Login = 'admin'";
using (SQLiteCommand checkCmd = new SQLiteCommand(checkAdminExists, connection))
{
2025-04-14 22:09:03 +07:00
int adminCount = Convert.ToInt32(checkCmd.ExecuteScalar());
if (adminCount == 0)
{
string insertAdmin = @"INSERT INTO Users (Login, Password) VALUES ('admin', 'admin')";
using (SQLiteCommand command = new SQLiteCommand(insertAdmin, connection))
{
command.ExecuteNonQuery();
}
}
}
2025-04-14 22:09:03 +07:00
// Создаем таблицу сотрудников
string createEmployeesTable = @"CREATE TABLE IF NOT EXISTS Employees (
EmployeeID INTEGER PRIMARY KEY AUTOINCREMENT,
LastName TEXT NOT NULL,
FirstName TEXT NOT NULL,
MiddleName TEXT,
Position TEXT,
Department TEXT)";
using (SQLiteCommand command = new SQLiteCommand(createEmployeesTable, connection))
{
command.ExecuteNonQuery();
}
// Создаем таблицу посещаемости
string createAttendanceTable = @"CREATE TABLE IF NOT EXISTS Attendance (
AttendanceID INTEGER PRIMARY KEY AUTOINCREMENT,
EmployeeID INTEGER NOT NULL,
ArrivalTime TEXT NOT NULL,
DepartureTime TEXT,
2025-04-14 22:09:03 +07:00
FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID) ON DELETE CASCADE)"; // Добавлено ON DELETE CASCADE
using (SQLiteCommand command = new SQLiteCommand(createAttendanceTable, connection))
{
command.ExecuteNonQuery();
}
}
2025-04-14 22:09:03 +07:00
// Включить поддержку внешних ключей для текущего соединения
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
using (SQLiteCommand command = new SQLiteCommand("PRAGMA foreign_keys = ON;", connection))
{
command.ExecuteNonQuery();
}
}
}
else
{
// Включить поддержку внешних ключей при каждом запуске, если БД уже существует
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
using (SQLiteCommand command = new SQLiteCommand("PRAGMA foreign_keys = ON;", connection))
{
command.ExecuteNonQuery();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка инициализации базы данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
2025-04-14 22:09:03 +07:00
private void InitializeLoginComponents()
{
// Заголовок
Label lblHeader = new Label
{
Text = "Вход в систему учета посещаемости",
Font = new Font("Arial", 14, FontStyle.Bold),
2025-04-14 22:09:03 +07:00
AutoSize = true,
TextAlign = ContentAlignment.MiddleCenter
};
// Поле логина
Label lblLogin = new Label
{
Text = "Логин:",
2025-04-14 22:09:03 +07:00
AutoSize = true
};
TextBox txtLogin = new TextBox
{
Name = "txtLogin",
2025-04-14 22:09:03 +07:00
Width = 250 // Увеличиваем ширину
};
2025-04-14 22:09:03 +07:00
txtLogin.KeyDown += TxtLogin_KeyDown;
// Поле пароля
Label lblPassword = new Label
{
Text = "Пароль:",
2025-04-14 22:09:03 +07:00
AutoSize = true
};
TextBox txtPassword = new TextBox
{
Name = "txtPassword",
2025-04-14 22:09:03 +07:00
Width = 250, // Увеличиваем ширину
PasswordChar = '*'
};
2025-04-14 22:09:03 +07:00
txtPassword.KeyDown += TxtPassword_KeyDown;
// Кнопка входа
2025-04-14 22:09:03 +07:00
buttonLogin.Text = "Войти";
buttonLogin.Width = 120; // Увеличиваем ширину кнопки
buttonLogin.Height = 35; // Увеличиваем высоту кнопки
buttonLogin.Click += ButtonLogin_Click;
2025-04-14 22:09:03 +07:00
// Устанавливаем размеры формы (увеличиваем ширину)
this.ClientSize = new Size(500, 250);
// Позиционирование заголовка
lblHeader.Location = new Point(
(this.ClientSize.Width - lblHeader.PreferredWidth) / 2, // Центрируем по ширине
20 // Отступ сверху
);
lblLogin.Location = new Point(50, 80); // Сдвигаем метку логина
txtLogin.Location = new Point(lblLogin.Right + 20, lblLogin.Top - 3); // Располагаем текстбокс справа от метки
lblPassword.Location = new Point(50, lblLogin.Bottom + 20); // Сдвигаем метку пароля
txtPassword.Location = new Point(lblPassword.Right + 20, lblPassword.Top - 3); // Располагаем текстбокс справа от метки
buttonLogin.Location = new Point(
(this.ClientSize.Width - buttonLogin.Width) / 2, // Центрируем кнопку по ширине
txtPassword.Bottom + 30 // Отступ от текстбокса пароля
);
// Добавляем все компоненты на форму
this.Controls.Add(lblHeader);
this.Controls.Add(lblLogin);
this.Controls.Add(txtLogin);
this.Controls.Add(lblPassword);
this.Controls.Add(txtPassword);
this.Controls.Add(buttonLogin);
2025-04-14 22:09:03 +07:00
// Устанавливаем кнопку входа как кнопку по умолчанию для формы
this.AcceptButton = buttonLogin;
}
// Обработчик нажатия клавиш для поля логина
private void TxtLogin_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// Предотвращаем звуковой сигнал при нажатии Enter
e.SuppressKeyPress = true;
// Перемещаем фокус на поле пароля
Control txtPassword = this.Controls["txtPassword"];
if (txtPassword != null)
txtPassword.Focus();
}
}
// Обработчик нажатия клавиш для поля пароля
private void TxtPassword_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// Предотвращаем звуковой сигнал при нажатии Enter
e.SuppressKeyPress = true;
// Вызываем метод входа в систему
ButtonLogin_Click(this, EventArgs.Empty);
}
}
private void ButtonLogin_Click(object sender, EventArgs e)
{
2025-04-14 22:09:03 +07:00
string login = "";
string password = "";
Control txtLoginCtrl = this.Controls["txtLogin"];
Control txtPasswordCtrl = this.Controls["txtPassword"];
if (txtLoginCtrl != null)
login = txtLoginCtrl.Text;
if (txtPasswordCtrl != null)
password = txtPasswordCtrl.Text;
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
{
MessageBox.Show("Логин и пароль не могут быть пустыми!", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
2025-04-14 22:09:03 +07:00
string query = "SELECT COUNT(*) FROM Users WHERE Login = @Login AND Password = @Password COLLATE NOCASE"; // Добавлено COLLATE NOCASE для регистронезависимого сравнения
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@Login", login);
2025-04-14 22:09:03 +07:00
command.Parameters.AddWithValue("@Password", password); // Пароль обычно чувствителен к регистру
int count = Convert.ToInt32(command.ExecuteScalar());
if (count > 0)
{
// Скрываем текущую форму
this.Hide();
// Открываем главную форму приложения
MainForm mainForm = new MainForm(connectionString);
2025-04-14 22:09:03 +07:00
mainForm.FormClosed += (s, args) => this.Close(); // Закрыть приложение при закрытии главной формы
mainForm.Show();
}
else
{
MessageBox.Show("Неверный логин или пароль!", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка входа: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
2025-04-14 22:09:03 +07:00
// --- Главная форма (с изменениями) ---
public class MainForm : Form
{
private string connectionString;
private TabControl tabControl;
private TabPage tabEmployees;
private TabPage tabAttendance;
private DataGridView dgvEmployees;
private DataGridView dgvAttendance;
2025-04-14 22:09:03 +07:00
// Кнопки для сотрудников (доступны через поля класса для легкого доступа)
private Button btnAddEmployee;
private Button btnEditEmployee;
private Button btnDeleteEmployee;
public MainForm(string connString)
{
connectionString = connString;
InitializeComponent();
LoadEmployeeData();
LoadAttendanceData();
2025-04-14 22:09:03 +07:00
// *** ИЗМЕНЕНИЕ: Установить вкладку "Посещаемость" по умолчанию ***
if (tabControl.TabPages.Contains(tabAttendance))
{
tabControl.SelectedTab = tabAttendance;
}
}
private void InitializeComponent()
{
this.Text = "Система учета посещаемости";
this.Width = 800;
this.Height = 600;
this.StartPosition = FormStartPosition.CenterScreen;
2025-04-14 22:09:03 +07:00
this.MinimumSize = new Size(600, 400); // Добавим минимальный размер
// Создаем вкладки
tabControl = new TabControl
{
Dock = DockStyle.Fill
};
// Вкладка "Сотрудники"
tabEmployees = new TabPage
{
Text = "Сотрудники"
};
// Вкладка "Посещаемость"
tabAttendance = new TabPage
{
Text = "Посещаемость"
};
2025-04-14 22:09:03 +07:00
// --- Вкладка Сотрудники ---
Panel panelEmployees = new Panel { Dock = DockStyle.Fill }; // Панель для содержимого вкладки
tabEmployees.Controls.Add(panelEmployees);
// Панель для кнопок сотрудников
Panel panelEmployeesButtons = new Panel
{
Dock = DockStyle.Top,
2025-04-14 22:09:03 +07:00
Height = 50,
Padding = new Padding(10) // Отступы для кнопок
};
2025-04-14 22:09:03 +07:00
panelEmployees.Controls.Add(panelEmployeesButtons); // Добавляем на панель содержимого
// Кнопки для управления сотрудниками
2025-04-14 22:09:03 +07:00
btnAddEmployee = new Button // Присваиваем полю класса
{
Text = "Добавить сотрудника",
Width = 150,
2025-04-14 22:09:03 +07:00
Location = new Point(10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top // Привязка к верху и левому краю
};
2025-04-14 22:09:03 +07:00
btnAddEmployee.Click += BtnAddEmployee_Click; // Обработчик будет содержать проверку пароля
2025-04-14 22:09:03 +07:00
btnEditEmployee = new Button // Присваиваем полю класса
{
Text = "Редактировать",
Width = 150,
2025-04-14 22:09:03 +07:00
Location = new Point(btnAddEmployee.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
2025-04-14 22:09:03 +07:00
btnEditEmployee.Click += BtnEditEmployee_Click; // Обработчик будет содержать проверку пароля
2025-04-14 22:09:03 +07:00
btnDeleteEmployee = new Button // Присваиваем полю класса
{
Text = "Удалить",
Width = 150,
2025-04-14 22:09:03 +07:00
Location = new Point(btnEditEmployee.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
2025-04-14 22:09:03 +07:00
btnDeleteEmployee.Click += BtnDeleteEmployee_Click; // Обработчик будет содержать проверку пароля
2025-04-14 22:09:03 +07:00
// Добавляем кнопки на панель кнопок сотрудников
panelEmployeesButtons.Controls.Add(btnAddEmployee);
panelEmployeesButtons.Controls.Add(btnEditEmployee);
panelEmployeesButtons.Controls.Add(btnDeleteEmployee);
// Таблица для сотрудников
dgvEmployees = new DataGridView
{
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = true,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
2025-04-14 22:09:03 +07:00
Dock = DockStyle.Fill, // Занимает оставшееся место на панели содержимого
MultiSelect = false // Запретим множественный выбор
};
2025-04-14 22:09:03 +07:00
panelEmployees.Controls.Add(dgvEmployees); // Добавляем на панель содержимого
dgvEmployees.BringToFront(); // Убедимся, что таблица поверх панели кнопок
2025-04-14 22:09:03 +07:00
// --- Вкладка Посещаемость ---
Panel panelAttendance = new Panel { Dock = DockStyle.Fill }; // Панель для содержимого вкладки
tabAttendance.Controls.Add(panelAttendance);
// Панель для кнопок посещаемости
Panel panelAttendanceButtons = new Panel
{
Dock = DockStyle.Top,
2025-04-14 22:09:03 +07:00
Height = 50,
Padding = new Padding(10) // Отступы для кнопок
};
2025-04-14 22:09:03 +07:00
panelAttendance.Controls.Add(panelAttendanceButtons); // Добавляем на панель содержимого
// Кнопки для управления посещаемостью
Button btnArrival = new Button
{
Text = "Отметить прибытие",
Width = 150,
2025-04-14 22:09:03 +07:00
Location = new Point(10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnArrival.Click += BtnArrival_Click;
Button btnDeparture = new Button
{
Text = "Отметить убытие",
Width = 150,
2025-04-14 22:09:03 +07:00
Location = new Point(btnArrival.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnDeparture.Click += BtnDeparture_Click;
2025-04-14 22:09:03 +07:00
// Добавляем две новые кнопки для удаления и редактирования записей
Button btnDeleteAttendance = new Button
{
Text = "Удалить запись",
Width = 150,
Location = new Point(btnDeparture.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnDeleteAttendance.Click += BtnDeleteAttendance_Click; // В этом методе уже есть проверка пароля
Button btnEditAttendance = new Button
{
Text = "Редактировать запись",
Width = 150,
Location = new Point(btnDeleteAttendance.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnEditAttendance.Click += BtnEditAttendance_Click; // В этом методе уже есть проверка пароля
// Добавляем кнопки на панель кнопок посещаемости
panelAttendanceButtons.Controls.Add(btnArrival);
panelAttendanceButtons.Controls.Add(btnDeparture);
2025-04-14 22:09:03 +07:00
panelAttendanceButtons.Controls.Add(btnDeleteAttendance);
panelAttendanceButtons.Controls.Add(btnEditAttendance);
// Таблица для посещаемости
dgvAttendance = new DataGridView
{
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = true,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
2025-04-14 22:09:03 +07:00
Dock = DockStyle.Fill, // Занимает оставшееся место на панели содержимого
MultiSelect = false // Запретим множественный выбор
};
2025-04-14 22:09:03 +07:00
panelAttendance.Controls.Add(dgvAttendance); // Добавляем на панель содержимого
dgvAttendance.BringToFront(); // Убедимся, что таблица поверх панели кнопок
// Добавляем вкладки в контрол
tabControl.TabPages.Add(tabEmployees);
tabControl.TabPages.Add(tabAttendance);
// Добавляем контрол вкладок на форму
this.Controls.Add(tabControl);
}
private void LoadEmployeeData()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
2025-04-14 22:09:03 +07:00
string query = "SELECT EmployeeID, LastName, FirstName, MiddleName, Position, Department FROM Employees ORDER BY LastName, FirstName"; // Добавил сортировку
SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
dgvEmployees.DataSource = dt;
// Переименовываем колонки для отображения
2025-04-14 22:09:03 +07:00
if (dgvEmployees.Columns.Count > 0) // Проверяем наличие колонок
{
dgvEmployees.Columns["EmployeeID"].HeaderText = "ID";
dgvEmployees.Columns["LastName"].HeaderText = "Фамилия";
dgvEmployees.Columns["FirstName"].HeaderText = "Имя";
dgvEmployees.Columns["MiddleName"].HeaderText = "Отчество";
dgvEmployees.Columns["Position"].HeaderText = "Должность";
dgvEmployees.Columns["Department"].HeaderText = "Отдел";
2025-04-14 22:09:03 +07:00
// Скрыть ID, если не нужен пользователю
dgvEmployees.Columns["EmployeeID"].Visible = false;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка загрузки данных сотрудников: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadAttendanceData()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
// Изменен синтаксис конкатенации для SQLite
2025-04-14 22:09:03 +07:00
string query = @"SELECT a.AttendanceID, e.EmployeeID,
e.LastName || ' ' || e.FirstName || ' ' || COALESCE(e.MiddleName, '') as FullName,
strftime('%Y-%m-%d %H:%M:%S', a.ArrivalTime) as ArrivalTimeFormatted, -- Форматируем дату/время
strftime('%Y-%m-%d %H:%M:%S', a.DepartureTime) as DepartureTimeFormatted -- Форматируем дату/время
FROM Attendance a
JOIN Employees e ON a.EmployeeID = e.EmployeeID
2025-04-14 22:09:03 +07:00
ORDER BY a.ArrivalTime DESC"; // Сортировка по времени прибытия
SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
dgvAttendance.DataSource = dt;
// Переименовываем колонки для отображения
2025-04-14 22:09:03 +07:00
if (dgvAttendance.Columns.Count > 0) // Проверяем наличие колонок
{
dgvAttendance.Columns["AttendanceID"].HeaderText = "ID записи";
dgvAttendance.Columns["EmployeeID"].HeaderText = "ID сотрудника";
dgvAttendance.Columns["FullName"].HeaderText = "ФИО";
2025-04-14 22:09:03 +07:00
dgvAttendance.Columns["ArrivalTimeFormatted"].HeaderText = "Время прибытия";
dgvAttendance.Columns["DepartureTimeFormatted"].HeaderText = "Время убытия";
// Скрыть ID, если не нужны пользователю
dgvAttendance.Columns["AttendanceID"].Visible = false;
dgvAttendance.Columns["EmployeeID"].Visible = false;
// Настроить ширину колонки ФИО
dgvAttendance.Columns["FullName"].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
dgvAttendance.Columns["FullName"].MinimumWidth = 150;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка загрузки данных посещаемости: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
2025-04-14 22:09:03 +07:00
// --- Обработчики событий для кнопок СОТРУДНИКОВ (с проверкой пароля) ---
private void BtnAddEmployee_Click(object sender, EventArgs e)
{
2025-04-14 22:09:03 +07:00
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
2025-04-14 22:09:03 +07:00
EmployeeForm form = new EmployeeForm(connectionString, 0);
if (form.ShowDialog() == DialogResult.OK)
{
LoadEmployeeData(); // Обновляем сотрудников
// Обновлять посещаемость не обязательно при добавлении нового сотрудника
}
}
}
private void BtnEditEmployee_Click(object sender, EventArgs e)
{
if (dgvEmployees.SelectedRows.Count > 0)
{
2025-04-14 22:09:03 +07:00
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
2025-04-14 22:09:03 +07:00
try
{
int employeeId = Convert.ToInt32(dgvEmployees.SelectedRows[0].Cells["EmployeeID"].Value);
EmployeeForm form = new EmployeeForm(connectionString, employeeId);
if (form.ShowDialog() == DialogResult.OK)
{
LoadEmployeeData(); // Обновляем сотрудников
LoadAttendanceData(); // Обновляем посещаемость (ФИО могло измениться)
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка получения ID сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
else
{
MessageBox.Show("Выберите сотрудника для редактирования", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void BtnDeleteEmployee_Click(object sender, EventArgs e)
{
if (dgvEmployees.SelectedRows.Count > 0)
{
2025-04-14 22:09:03 +07:00
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
try
{
2025-04-14 22:09:03 +07:00
int employeeId = Convert.ToInt32(dgvEmployees.SelectedRows[0].Cells["EmployeeID"].Value);
string employeeName = dgvEmployees.SelectedRows[0].Cells["LastName"].Value.ToString() + " " +
dgvEmployees.SelectedRows[0].Cells["FirstName"].Value.ToString();
DialogResult result = MessageBox.Show($"Вы уверены, что хотите удалить сотрудника {employeeName}?\nВсе связанные записи посещаемости также будут удалены!",
"Подтверждение удаления", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); // Изменил иконку на Warning
if (result == DialogResult.Yes)
{
2025-04-14 22:09:03 +07:00
try
{
2025-04-14 22:09:03 +07:00
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
2025-04-14 22:09:03 +07:00
connection.Open();
// Используем PRAGMA foreign_keys = ON; при создании/открытии БД
// и ON DELETE CASCADE в определении внешнего ключа.
// Удаление записей посещаемости произойдет автоматически.
2025-04-14 22:09:03 +07:00
// Удаляем сотрудника
string deleteEmployee = "DELETE FROM Employees WHERE EmployeeID = @EmployeeID";
2025-04-14 22:09:03 +07:00
using (SQLiteCommand command = new SQLiteCommand(deleteEmployee, connection))
{
command.Parameters.AddWithValue("@EmployeeID", employeeId);
2025-04-14 22:09:03 +07:00
int deletedRows = command.ExecuteNonQuery();
if (deletedRows > 0)
{
MessageBox.Show("Сотрудник и связанные записи посещаемости успешно удалены", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadEmployeeData();
LoadAttendanceData();
}
else
{
MessageBox.Show("Не удалось удалить сотрудника.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
2025-04-14 22:09:03 +07:00
catch (Exception ex)
{
MessageBox.Show("Ошибка при удалении сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (Exception ex)
{
2025-04-14 22:09:03 +07:00
MessageBox.Show("Ошибка получения ID сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
2025-04-14 22:09:03 +07:00
} // конец блока проверки пароля
}
else
{
MessageBox.Show("Выберите сотрудника для удаления", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
2025-04-14 22:09:03 +07:00
// --- Обработчики событий для кнопок ПОСЕЩАЕМОСТИ (без изменений, т.к. уже имели проверку пароля где надо) ---
private void BtnArrival_Click(object sender, EventArgs e)
{
AttendanceForm form = new AttendanceForm(connectionString, true);
if (form.ShowDialog() == DialogResult.OK)
{
LoadAttendanceData();
}
}
private void BtnDeparture_Click(object sender, EventArgs e)
{
AttendanceForm form = new AttendanceForm(connectionString, false);
if (form.ShowDialog() == DialogResult.OK)
{
LoadAttendanceData();
}
}
2025-04-14 22:09:03 +07:00
// Добавляем метод для проверки мастер-пароля
private bool VerifyMasterPassword()
{
using (PasswordVerificationForm passwordForm = new PasswordVerificationForm())
{
// Возвращаем true только если пользователь нажал OK и пароль верен
return passwordForm.ShowDialog() == DialogResult.OK;
}
// Если форма закрыта или нажата отмена, возвращаем false
// return false; // Это не нужно, т.к. ShowDialog вернет не OK
}
// Обработчик для кнопки "Удалить запись" (уже содержит проверку пароля)
private void BtnDeleteAttendance_Click(object sender, EventArgs e)
{
// Проверяем, выбрана ли запись для удаления
if (dgvAttendance.SelectedRows.Count == 0)
{
MessageBox.Show("Выберите запись для удаления", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// Запрашиваем мастер-пароль
if (VerifyMasterPassword())
{
try
{
int attendanceId = Convert.ToInt32(dgvAttendance.SelectedRows[0].Cells["AttendanceID"].Value);
DialogResult confirmResult = MessageBox.Show("Вы уверены, что хотите удалить выбранную запись?",
"Подтверждение удаления", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string deleteQuery = "DELETE FROM Attendance WHERE AttendanceID = @AttendanceID";
using (SQLiteCommand command = new SQLiteCommand(deleteQuery, connection))
{
command.Parameters.AddWithValue("@AttendanceID", attendanceId);
int rowsAffected = command.ExecuteNonQuery(); // Проверяем, удалилось ли что-то
if (rowsAffected > 0)
{
LoadAttendanceData(); // Обновляем таблицу
MessageBox.Show("Запись успешно удалена.", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("Не удалось удалить запись.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при удалении записи: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// Обработчик для кнопки "Редактировать запись" (уже содержит проверку пароля)
private void BtnEditAttendance_Click(object sender, EventArgs e)
{
// Проверяем, выбрана ли запись для редактирования
if (dgvAttendance.SelectedRows.Count == 0)
{
MessageBox.Show("Выберите запись для редактирования", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// Запрашиваем мастер-пароль
if (VerifyMasterPassword())
{
try
{
int attendanceId = Convert.ToInt32(dgvAttendance.SelectedRows[0].Cells["AttendanceID"].Value);
int employeeId = Convert.ToInt32(dgvAttendance.SelectedRows[0].Cells["EmployeeID"].Value);
// Открываем форму редактирования записи
AttendanceEditForm editForm = new AttendanceEditForm(connectionString, attendanceId, employeeId);
if (editForm.ShowDialog() == DialogResult.OK)
{
LoadAttendanceData(); // Обновляем таблицу после редактирования
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при получении данных для редактирования: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
2025-04-14 22:09:03 +07:00
// --- Форма EmployeeForm (без существенных изменений) ---
public class EmployeeForm : Form
{
private string connectionString;
private int employeeId;
private TextBox txtLastName;
private TextBox txtFirstName;
private TextBox txtMiddleName;
private TextBox txtPosition;
private TextBox txtDepartment;
public EmployeeForm(string connString, int id)
{
connectionString = connString;
employeeId = id;
InitializeComponent();
if (employeeId > 0)
{
this.Text = "Редактирование сотрудника";
LoadEmployeeData();
}
else
{
this.Text = "Добавление нового сотрудника";
}
}
private void InitializeComponent()
{
this.Width = 400;
this.Height = 300;
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
// Поля для ввода данных
2025-04-14 22:09:03 +07:00
Label lblLastName = new Label { Text = "Фамилия:", Location = new Point(30, 20), Width = 100, AutoSize = true };
txtLastName = new TextBox { Location = new Point(150, 20), Width = 200, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
2025-04-14 22:09:03 +07:00
Label lblFirstName = new Label { Text = "Имя:", Location = new Point(30, 50), Width = 100, AutoSize = true };
txtFirstName = new TextBox { Location = new Point(150, 50), Width = 200, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
2025-04-14 22:09:03 +07:00
Label lblMiddleName = new Label { Text = "Отчество:", Location = new Point(30, 80), Width = 100, AutoSize = true };
txtMiddleName = new TextBox { Location = new Point(150, 80), Width = 200, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
2025-04-14 22:09:03 +07:00
Label lblPosition = new Label { Text = "Должность:", Location = new Point(30, 110), Width = 100, AutoSize = true };
txtPosition = new TextBox { Location = new Point(150, 110), Width = 200, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
2025-04-14 22:09:03 +07:00
Label lblDepartment = new Label { Text = "Отдел:", Location = new Point(30, 140), Width = 100, AutoSize = true };
txtDepartment = new TextBox { Location = new Point(150, 140), Width = 200, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
2025-04-14 22:09:03 +07:00
// Позиционирование полей ввода относительно меток
Action<Label, TextBox> alignTextBox = (lbl, txt) => {
txt.Location = new Point(lbl.Right + 10, lbl.Top - (txt.Height - lbl.Height) / 2);
txt.Width = this.ClientSize.Width - txt.Left - 30; // Растягиваем до правого края с отступом
};
2025-04-14 22:09:03 +07:00
alignTextBox(lblLastName, txtLastName);
alignTextBox(lblFirstName, txtFirstName);
alignTextBox(lblMiddleName, txtMiddleName);
alignTextBox(lblPosition, txtPosition);
alignTextBox(lblDepartment, txtDepartment);
// Кнопки
Button btnSave = new Button
{
Text = "Сохранить",
2025-04-14 22:09:03 +07:00
DialogResult = DialogResult.OK, // Устанавливаем сразу, обработчик может отменить закрытие
Location = new Point(150, 180),
2025-04-14 22:09:03 +07:00
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right // Привязка к правому нижнему углу
};
btnSave.Click += BtnSave_Click;
Button btnCancel = new Button
{
Text = "Отмена",
2025-04-14 22:09:03 +07:00
DialogResult = DialogResult.Cancel, // Закроет форму с результатом Cancel
Location = new Point(btnSave.Left - 110, btnSave.Top), // Левее кнопки Сохранить
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
2025-04-14 22:09:03 +07:00
btnCancel.Location = new Point(this.ClientSize.Width - btnCancel.Width - 20, this.ClientSize.Height - btnCancel.Height - 20);
btnSave.Location = new Point(btnCancel.Left - btnSave.Width - 10, btnCancel.Top);
// Добавляем компоненты на форму
this.Controls.Add(lblLastName);
this.Controls.Add(txtLastName);
this.Controls.Add(lblFirstName);
this.Controls.Add(txtFirstName);
this.Controls.Add(lblMiddleName);
this.Controls.Add(txtMiddleName);
this.Controls.Add(lblPosition);
this.Controls.Add(txtPosition);
this.Controls.Add(lblDepartment);
this.Controls.Add(txtDepartment);
this.Controls.Add(btnSave);
this.Controls.Add(btnCancel);
2025-04-14 22:09:03 +07:00
// Устанавливаем динамическую высоту формы
this.ClientSize = new Size(400, btnSave.Bottom + 20);
this.AcceptButton = btnSave;
this.CancelButton = btnCancel;
}
2025-04-14 22:09:03 +07:00
private void LoadEmployeeData()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string query = "SELECT LastName, FirstName, MiddleName, Position, Department FROM Employees WHERE EmployeeID = @EmployeeID";
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@EmployeeID", employeeId);
using (SQLiteDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
2025-04-14 22:09:03 +07:00
txtLastName.Text = reader["LastName"]?.ToString() ?? ""; // Безопасное получение строк
txtFirstName.Text = reader["FirstName"]?.ToString() ?? "";
txtMiddleName.Text = reader["MiddleName"]?.ToString() ?? "";
txtPosition.Text = reader["Position"]?.ToString() ?? "";
txtDepartment.Text = reader["Department"]?.ToString() ?? "";
}
else
{
MessageBox.Show("Сотрудник с указанным ID не найден.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Close(); // Закрыть форму, если данные не загружены
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при загрузке данных сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
2025-04-14 22:09:03 +07:00
this.Close(); // Закрыть форму при ошибке
}
}
private void BtnSave_Click(object sender, EventArgs e)
{
2025-04-14 22:09:03 +07:00
// Валидация
if (string.IsNullOrWhiteSpace(txtLastName.Text))
{
2025-04-14 22:09:03 +07:00
MessageBox.Show("Поле 'Фамилия' обязательно для заполнения.", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None; // Отменяем закрытие формы
txtLastName.Focus();
return;
}
if (string.IsNullOrWhiteSpace(txtFirstName.Text))
{
MessageBox.Show("Поле 'Имя' обязательно для заполнения.", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None; // Отменяем закрытие формы
txtFirstName.Focus();
return;
}
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string query;
if (employeeId > 0)
{
// Обновление существующего сотрудника
2025-04-14 22:09:03 +07:00
query = @"UPDATE Employees SET
LastName = @LastName,
FirstName = @FirstName,
MiddleName = @MiddleName,
Position = @Position,
Department = @Department
WHERE EmployeeID = @EmployeeID";
}
else
{
// Добавление нового сотрудника
2025-04-14 22:09:03 +07:00
query = @"INSERT INTO Employees (LastName, FirstName, MiddleName, Position, Department)
VALUES (@LastName, @FirstName, @MiddleName, @Position, @Department)";
}
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
2025-04-14 22:09:03 +07:00
// Используем DBNull.Value для пустых необязательных полей
command.Parameters.AddWithValue("@LastName", txtLastName.Text.Trim());
command.Parameters.AddWithValue("@FirstName", txtFirstName.Text.Trim());
command.Parameters.AddWithValue("@MiddleName", string.IsNullOrWhiteSpace(txtMiddleName.Text) ? (object)DBNull.Value : txtMiddleName.Text.Trim());
command.Parameters.AddWithValue("@Position", string.IsNullOrWhiteSpace(txtPosition.Text) ? (object)DBNull.Value : txtPosition.Text.Trim());
command.Parameters.AddWithValue("@Department", string.IsNullOrWhiteSpace(txtDepartment.Text) ? (object)DBNull.Value : txtDepartment.Text.Trim());
if (employeeId > 0)
{
command.Parameters.AddWithValue("@EmployeeID", employeeId);
}
2025-04-14 22:09:03 +07:00
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected > 0)
{
// Успешно сохранено, DialogResult остается OK (установлен на кнопке)
}
else
{
MessageBox.Show("Не удалось сохранить данные. Возможно, сотрудник был удален.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.DialogResult = DialogResult.None; // Отменяем закрытие
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
2025-04-14 22:09:03 +07:00
this.DialogResult = DialogResult.None; // Отменяем закрытие формы при ошибке
}
}
}
2025-04-14 22:09:03 +07:00
// --- Форма AttendanceForm (без существенных изменений) ---
public class AttendanceForm : Form
{
private string connectionString;
private bool isArrival;
private ComboBox cmbEmployees;
public AttendanceForm(string connString, bool arrival)
{
connectionString = connString;
isArrival = arrival;
InitializeComponent();
2025-04-14 22:09:03 +07:00
if (!LoadEmployees()) // Если сотрудники не загружены, закрываем форму
{
this.Load += (s, e) => this.Close(); // Запланировать закрытие после загрузки
}
this.Text = isArrival ? "Отметка прибытия" : "Отметка убытия";
}
private void InitializeComponent()
{
this.Width = 400;
this.Height = 200;
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
// Заголовок
Label lblHeader = new Label
{
Text = isArrival ? "Отметка прибытия сотрудника" : "Отметка убытия сотрудника",
Font = new Font("Arial", 12, FontStyle.Bold),
2025-04-14 22:09:03 +07:00
Location = new Point(10, 20), // Сдвинул влево
Width = this.ClientSize.Width - 20, // На всю ширину с отступами
TextAlign = ContentAlignment.MiddleCenter,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right // Привязка
};
// Выбор сотрудника
Label lblEmployee = new Label
{
Text = "Сотрудник:",
2025-04-14 22:09:03 +07:00
Location = new Point(30, 65), // Немного ниже заголовка
AutoSize = true // Авторазмер
};
cmbEmployees = new ComboBox
{
2025-04-14 22:09:03 +07:00
Location = new Point(lblEmployee.Right + 10, lblEmployee.Top - 3), // Выравнивание по вертикали
Width = 250, // Немного шире
DropDownStyle = ComboBoxStyle.DropDownList,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right // Привязка
};
2025-04-14 22:09:03 +07:00
cmbEmployees.Width = this.ClientSize.Width - cmbEmployees.Left - 30; // Растягивание
// Кнопки
Button btnSave = new Button
{
Text = "Отметить",
2025-04-14 22:09:03 +07:00
DialogResult = DialogResult.OK, // Устанавливаем сразу
Location = new Point(130, 120), // Ниже комбобокса
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right // Привязка
};
btnSave.Click += BtnSave_Click;
Button btnCancel = new Button
{
Text = "Отмена",
2025-04-14 22:09:03 +07:00
DialogResult = DialogResult.Cancel, // Закроет форму
Location = new Point(btnSave.Left + 110, btnSave.Top),
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
2025-04-14 22:09:03 +07:00
// Позиционирование кнопок от правого нижнего угла
btnCancel.Location = new Point(this.ClientSize.Width - btnCancel.Width - 20, this.ClientSize.Height - btnCancel.Height - 20);
btnSave.Location = new Point(btnCancel.Left - btnSave.Width - 10, btnCancel.Top);
// Добавляем компоненты на форму
this.Controls.Add(lblHeader);
this.Controls.Add(lblEmployee);
this.Controls.Add(cmbEmployees);
this.Controls.Add(btnSave);
this.Controls.Add(btnCancel);
2025-04-14 22:09:03 +07:00
// Устанавливаем динамическую высоту
this.ClientSize = new Size(400, btnSave.Bottom + 20);
this.AcceptButton = btnSave;
this.CancelButton = btnCancel;
}
2025-04-14 22:09:03 +07:00
private bool LoadEmployees()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
2025-04-14 22:09:03 +07:00
string query = "SELECT EmployeeID, LastName || ' ' || FirstName || COALESCE(' ' || MiddleName, '') as FullName FROM Employees ORDER BY LastName, FirstName";
SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
2025-04-14 22:09:03 +07:00
if (dt.Rows.Count > 0)
{
cmbEmployees.DataSource = dt;
cmbEmployees.DisplayMember = "FullName";
cmbEmployees.ValueMember = "EmployeeID";
cmbEmployees.SelectedIndex = -1; // Сбросить выбор по умолчанию
return true; // Сотрудники загружены
}
else
{
MessageBox.Show("Нет сотрудников для выбора. Сначала добавьте сотрудников.", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
return false; // Сотрудников нет
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при загрузке списка сотрудников: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
2025-04-14 22:09:03 +07:00
return false; // Ошибка загрузки
}
}
2025-04-14 22:09:03 +07:00
private void BtnSave_Click(object sender, EventArgs e)
{
if (cmbEmployees.SelectedValue == null)
{
MessageBox.Show("Необходимо выбрать сотрудника", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
2025-04-14 22:09:03 +07:00
this.DialogResult = DialogResult.None; // Отменяем закрытие
cmbEmployees.Focus();
return;
}
2025-04-14 22:09:03 +07:00
object selectedEmployeeId = cmbEmployees.SelectedValue;
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
2025-04-14 22:09:03 +07:00
string currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (isArrival)
{
2025-04-14 22:09:03 +07:00
// Проверка, нет ли уже незакрытой отметки о прибытии для этого сотрудника сегодня
string checkQuery = @"SELECT COUNT(*) FROM Attendance
WHERE EmployeeID = @EmployeeID
AND date(ArrivalTime) = date('now', 'localtime')
AND DepartureTime IS NULL";
using (SQLiteCommand checkCmd = new SQLiteCommand(checkQuery, connection))
{
checkCmd.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
int openArrivals = Convert.ToInt32(checkCmd.ExecuteScalar());
if (openArrivals > 0)
{
MessageBox.Show("У этого сотрудника уже есть незавершенная отметка о прибытии за сегодня.", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None;
return;
}
}
// Отметка прибытия - создаем новую запись
string query = "INSERT INTO Attendance (EmployeeID, ArrivalTime) VALUES (@EmployeeID, @ArrivalTime)";
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
2025-04-14 22:09:03 +07:00
command.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
command.Parameters.AddWithValue("@ArrivalTime", currentTime);
command.ExecuteNonQuery();
}
}
2025-04-14 22:09:03 +07:00
else // Отметка убытия
{
2025-04-14 22:09:03 +07:00
// Отметка убытия - ищем последнюю запись с прибытием без убытия для данного сотрудника
string query = @"UPDATE Attendance SET DepartureTime = @DepartureTime
WHERE EmployeeID = @EmployeeID AND DepartureTime IS NULL
AND AttendanceID = (SELECT AttendanceID FROM Attendance
WHERE EmployeeID = @EmployeeID AND DepartureTime IS NULL
ORDER BY ArrivalTime DESC LIMIT 1)";
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
2025-04-14 22:09:03 +07:00
command.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
command.Parameters.AddWithValue("@DepartureTime", currentTime);
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected == 0)
{
2025-04-14 22:09:03 +07:00
MessageBox.Show("Для данного сотрудника нет открытых записей о прибытии, для которых можно отметить убытие.",
"Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
2025-04-14 22:09:03 +07:00
this.DialogResult = DialogResult.None; // Отменяем закрытие
return;
}
}
}
2025-04-14 22:09:03 +07:00
// Если дошли сюда, значит все ок, DialogResult остается OK
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
2025-04-14 22:09:03 +07:00
this.DialogResult = DialogResult.None; // Отменяем закрытие
}
}
}
// --- Форма PasswordVerificationForm (без изменений) ---
public class PasswordVerificationForm : Form
{
private TextBox txtPassword;
public PasswordVerificationForm()
{
InitializeComponent();
// Установить фокус на поле пароля при открытии
this.Load += (s, e) => { txtPassword.Focus(); };
}
private void InitializeComponent()
{
this.Width = 330;
this.Height = 150;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.StartPosition = FormStartPosition.CenterParent;
this.Text = "Проверка доступа";
Label lblPassword = new Label
{
Text = "Введите мастер-пароль:",
Location = new Point(20, 20),
AutoSize = true // Авторазмер
};
txtPassword = new TextBox
{
Location = new Point(20, lblPassword.Bottom + 5), // Позиция под меткой
Width = this.ClientSize.Width - 40, // Ширина с отступами
PasswordChar = '*',
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right // Привязка
};
Button btnOK = new Button
{
Text = "ОК",
DialogResult = DialogResult.OK, // Устанавливаем заранее
Location = new Point(120, 80),
Width = 80,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right // Привязка
};
btnOK.Click += BtnOK_Click; // Проверка пароля в обработчике
Button btnCancel = new Button
{
Text = "Отмена",
DialogResult = DialogResult.Cancel, // Закроет форму
Location = new Point(220, 80),
Width = 80,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
// Позиционирование кнопок
btnCancel.Location = new Point(this.ClientSize.Width - btnCancel.Width - 20, this.ClientSize.Height - btnCancel.Height - 20);
btnOK.Location = new Point(btnCancel.Left - btnOK.Width - 10, btnCancel.Top);
this.Controls.Add(lblPassword);
this.Controls.Add(txtPassword);
this.Controls.Add(btnOK);
this.Controls.Add(btnCancel);
// Устанавливаем высоту динамически
this.ClientSize = new Size(315, btnOK.Bottom + 20);
this.AcceptButton = btnOK;
this.CancelButton = btnCancel;
}
// --- ИЗМЕНЕННЫЙ мастер-пароль (для примера) ---
private const string MasterPassword = "1234321"; // Лучше вынести в конфигурацию или другое безопасное место
private void BtnOK_Click(object sender, EventArgs e)
{
if (txtPassword.Text == MasterPassword) // Сравнение с константой
{
// Пароль верный, DialogResult уже OK, форма закроется
}
else
{
MessageBox.Show("Неверный пароль!", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.DialogResult = DialogResult.None; // Отменяем закрытие формы
txtPassword.Clear();
txtPassword.Focus();
}
}
}
2025-04-14 22:09:03 +07:00
// --- Форма AttendanceEditForm (без существенных изменений) ---
public class AttendanceEditForm : Form
{
private string connectionString;
private int attendanceId;
private int initialEmployeeId; // Сохраняем ID сотрудника, который был изначально
private ComboBox cmbEmployees;
private DateTimePicker dtpArrivalDate;
private DateTimePicker dtpArrivalTime;
private DateTimePicker dtpDepartureDate;
private DateTimePicker dtpDepartureTime;
private CheckBox chkHasDeparture;
public AttendanceEditForm(string connString, int attId, int empId)
{
connectionString = connString;
attendanceId = attId;
initialEmployeeId = empId;
InitializeComponent();
if (!LoadEmployees()) // Если сотрудники не загружены, закрываем форму
{
this.Load += (s, e) => this.Close();
return; // Прерываем дальнейшую инициализацию
}
if (!LoadAttendanceData()) // Если запись не загружена, закрываем форму
{
this.Load += (s, e) => this.Close();
}
}
private void InitializeComponent()
{
this.Width = 450;
this.Height = 300;
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Text = "Редактирование записи посещаемости";
int currentY = 20;
int labelWidth = 120;
int controlLeft = labelWidth + 30;
int controlWidth = this.ClientSize.Width - controlLeft - 20;
// Сотрудник
Label lblEmployee = new Label { Text = "Сотрудник:", Location = new Point(20, currentY), Width = labelWidth, AutoSize = true };
cmbEmployees = new ComboBox { Location = new Point(controlLeft, lblEmployee.Top - 3), Width = controlWidth, DropDownStyle = ComboBoxStyle.DropDownList, Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right };
currentY = cmbEmployees.Bottom + 15;
// Время прибытия
Label lblArrival = new Label { Text = "Время прибытия:", Location = new Point(20, currentY), Width = labelWidth, AutoSize = true };
dtpArrivalDate = new DateTimePicker { Location = new Point(controlLeft, lblArrival.Top - 3), Width = 120, Format = DateTimePickerFormat.Short, Anchor = AnchorStyles.Top | AnchorStyles.Left };
dtpArrivalTime = new DateTimePicker { Location = new Point(dtpArrivalDate.Right + 10, dtpArrivalDate.Top), Width = 120, Format = DateTimePickerFormat.Time, ShowUpDown = true, Anchor = AnchorStyles.Top | AnchorStyles.Left };
currentY = dtpArrivalDate.Bottom + 15;
// Время убытия
chkHasDeparture = new CheckBox { Text = "Есть время убытия", Location = new Point(20, currentY), Width = labelWidth + 50, AutoSize = true, Anchor = AnchorStyles.Top | AnchorStyles.Left };
chkHasDeparture.CheckedChanged += ChkHasDeparture_CheckedChanged;
currentY = chkHasDeparture.Bottom + 5;
dtpDepartureDate = new DateTimePicker { Location = new Point(controlLeft, currentY), Width = 120, Format = DateTimePickerFormat.Short, Enabled = false, Anchor = AnchorStyles.Top | AnchorStyles.Left };
dtpDepartureTime = new DateTimePicker { Location = new Point(dtpDepartureDate.Right + 10, dtpDepartureDate.Top), Width = 120, Format = DateTimePickerFormat.Time, ShowUpDown = true, Enabled = false, Anchor = AnchorStyles.Top | AnchorStyles.Left };
currentY = dtpDepartureDate.Bottom + 25; // Больше отступ перед кнопками
// Кнопки
Button btnSave = new Button
{
Text = "Сохранить",
DialogResult = DialogResult.OK, // Устанавливаем сразу
Location = new Point(150, currentY),
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
btnSave.Click += BtnSave_Click;
Button btnCancel = new Button
{
Text = "Отмена",
DialogResult = DialogResult.Cancel, // Закроет форму
Location = new Point(btnSave.Left + 110, btnSave.Top),
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right
};
// Позиционирование кнопок
btnCancel.Location = new Point(this.ClientSize.Width - btnCancel.Width - 20, this.ClientSize.Height - btnCancel.Height - 20);
btnSave.Location = new Point(btnCancel.Left - btnSave.Width - 10, btnCancel.Top);
// Добавляем элементы на форму
this.Controls.Add(lblEmployee);
this.Controls.Add(cmbEmployees);
this.Controls.Add(lblArrival);
this.Controls.Add(dtpArrivalDate);
this.Controls.Add(dtpArrivalTime);
this.Controls.Add(chkHasDeparture);
this.Controls.Add(dtpDepartureDate);
this.Controls.Add(dtpDepartureTime);
this.Controls.Add(btnSave);
this.Controls.Add(btnCancel);
// Устанавливаем высоту формы динамически
this.ClientSize = new Size(435, btnSave.Bottom + 20);
this.AcceptButton = btnSave;
this.CancelButton = btnCancel;
}
private void ChkHasDeparture_CheckedChanged(object sender, EventArgs e)
{
dtpDepartureDate.Enabled = chkHasDeparture.Checked;
dtpDepartureTime.Enabled = chkHasDeparture.Checked;
// Если снимаем галочку, можно сбросить время убытия на время прибытия для удобства
if (!chkHasDeparture.Checked)
{
dtpDepartureDate.Value = dtpArrivalDate.Value;
dtpDepartureTime.Value = dtpArrivalTime.Value;
}
}
private bool LoadEmployees()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string query = "SELECT EmployeeID, LastName || ' ' || FirstName || COALESCE(' ' || MiddleName, '') as FullName FROM Employees ORDER BY LastName, FirstName";
SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
if (dt.Rows.Count > 0)
{
cmbEmployees.DataSource = dt;
cmbEmployees.DisplayMember = "FullName";
cmbEmployees.ValueMember = "EmployeeID";
// Устанавливаем текущего сотрудника
cmbEmployees.SelectedValue = initialEmployeeId;
// Проверка, что значение установилось
if (cmbEmployees.SelectedValue == null || (int)cmbEmployees.SelectedValue != initialEmployeeId)
{
MessageBox.Show("Не удалось выбрать исходного сотрудника в списке. Возможно, он был удален.", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
// Можно либо выбрать первого попавшегося, либо закрыть форму
if (dt.Rows.Count > 0) cmbEmployees.SelectedIndex = 0;
else return false; // Нет сотрудников вообще
}
return true;
}
else
{
MessageBox.Show("Нет сотрудников для выбора.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при загрузке списка сотрудников: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool LoadAttendanceData()
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string query = "SELECT ArrivalTime, DepartureTime FROM Attendance WHERE AttendanceID = @AttendanceID";
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@AttendanceID", attendanceId);
using (SQLiteDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
// Устанавливаем время прибытия
if (DateTime.TryParse(reader["ArrivalTime"]?.ToString(), out DateTime arrivalTime))
{
dtpArrivalDate.Value = arrivalTime.Date;
dtpArrivalTime.Value = arrivalTime; // Устанавливаем полное время
}
else
{
MessageBox.Show("Некорректный формат времени прибытия в базе данных.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; // Не можем загрузить данные
}
// Устанавливаем время убытия, если оно есть
if (!reader.IsDBNull(reader.GetOrdinal("DepartureTime")))
{
if (DateTime.TryParse(reader["DepartureTime"]?.ToString(), out DateTime departureTime))
{
dtpDepartureDate.Value = departureTime.Date;
dtpDepartureTime.Value = departureTime; // Устанавливаем полное время
chkHasDeparture.Checked = true; // Включаем чекбокс и связанные поля
}
else
{
// Не критично, просто не установим время убытия
chkHasDeparture.Checked = false;
}
}
else
{
// Если время убытия NULL, чекбокс не отмечен, поля деактивированы
chkHasDeparture.Checked = false;
// Установим время убытия равным времени прибытия по умолчанию
dtpDepartureDate.Value = dtpArrivalDate.Value;
dtpDepartureTime.Value = dtpArrivalTime.Value;
}
return true; // Данные загружены
}
else
{
MessageBox.Show("Запись посещаемости с указанным ID не найдена.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; // Запись не найдена
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при загрузке данных записи: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; // Ошибка загрузки
}
}
private void BtnSave_Click(object sender, EventArgs e)
{
if (cmbEmployees.SelectedValue == null)
{
MessageBox.Show("Необходимо выбрать сотрудника.", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None;
cmbEmployees.Focus();
return;
}
try
{
object selectedEmployeeId = cmbEmployees.SelectedValue;
// Комбинируем дату и время для получения полного времени прибытия
DateTime arrivalDateTime = dtpArrivalDate.Value.Date.Add(dtpArrivalTime.Value.TimeOfDay);
DateTime? departureDateTime = null; // nullable DateTime
if (chkHasDeparture.Checked)
{
// Комбинируем дату и время убытия
departureDateTime = dtpDepartureDate.Value.Date.Add(dtpDepartureTime.Value.TimeOfDay);
// Валидация: время убытия не может быть раньше времени прибытия
if (departureDateTime.Value < arrivalDateTime)
{
MessageBox.Show("Время убытия не может быть раньше времени прибытия.", "Ошибка валидации", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None; // Отменяем закрытие
dtpDepartureTime.Focus();
return;
}
}
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string query = @"UPDATE Attendance SET
EmployeeID = @EmployeeID,
ArrivalTime = @ArrivalTime,
DepartureTime = @DepartureTime
WHERE AttendanceID = @AttendanceID";
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
command.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
command.Parameters.AddWithValue("@ArrivalTime", arrivalDateTime.ToString("yyyy-MM-dd HH:mm:ss"));
// Используем DBNull.Value если время убытия не установлено
command.Parameters.AddWithValue("@DepartureTime", departureDateTime.HasValue ? (object)departureDateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : DBNull.Value);
command.Parameters.AddWithValue("@AttendanceID", attendanceId);
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected > 0)
{
// Успешно, DialogResult остается OK
}
else
{
MessageBox.Show("Не удалось обновить запись. Возможно, она была удалена.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.DialogResult = DialogResult.None; // Отменяем закрытие
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.DialogResult = DialogResult.None; // Отменяем закрытие
}
}
}
}