skud/Form1.cs
Professional 7ad5a4d6c5 1.0.0
2025-04-14 22:09:03 +07:00

1653 lines
83 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
// --- Форма авторизации (без изменений) ---
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();
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,
Login TEXT NOT NULL UNIQUE,
Password TEXT NOT NULL)"; // Добавлено UNIQUE для Login
using (SQLiteCommand command = new SQLiteCommand(createUsersTable, connection))
{
command.ExecuteNonQuery();
}
// Создаем admin пользователя (если его еще нет)
string checkAdminExists = "SELECT COUNT(*) FROM Users WHERE Login = 'admin'";
using (SQLiteCommand checkCmd = new SQLiteCommand(checkAdminExists, connection))
{
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();
}
}
}
// Создаем таблицу сотрудников
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,
FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID) ON DELETE CASCADE)"; // Добавлено ON DELETE CASCADE
using (SQLiteCommand command = new SQLiteCommand(createAttendanceTable, connection))
{
command.ExecuteNonQuery();
}
}
// Включить поддержку внешних ключей для текущего соединения
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);
}
}
private void InitializeLoginComponents()
{
// Заголовок
Label lblHeader = new Label
{
Text = "Вход в систему учета посещаемости",
Font = new Font("Arial", 14, FontStyle.Bold),
AutoSize = true,
TextAlign = ContentAlignment.MiddleCenter
};
// Поле логина
Label lblLogin = new Label
{
Text = "Логин:",
AutoSize = true
};
TextBox txtLogin = new TextBox
{
Name = "txtLogin",
Width = 250 // Увеличиваем ширину
};
txtLogin.KeyDown += TxtLogin_KeyDown;
// Поле пароля
Label lblPassword = new Label
{
Text = "Пароль:",
AutoSize = true
};
TextBox txtPassword = new TextBox
{
Name = "txtPassword",
Width = 250, // Увеличиваем ширину
PasswordChar = '*'
};
txtPassword.KeyDown += TxtPassword_KeyDown;
// Кнопка входа
buttonLogin.Text = "Войти";
buttonLogin.Width = 120; // Увеличиваем ширину кнопки
buttonLogin.Height = 35; // Увеличиваем высоту кнопки
buttonLogin.Click += ButtonLogin_Click;
// Устанавливаем размеры формы (увеличиваем ширину)
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);
// Устанавливаем кнопку входа как кнопку по умолчанию для формы
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)
{
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();
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);
command.Parameters.AddWithValue("@Password", password); // Пароль обычно чувствителен к регистру
int count = Convert.ToInt32(command.ExecuteScalar());
if (count > 0)
{
// Скрываем текущую форму
this.Hide();
// Открываем главную форму приложения
MainForm mainForm = new MainForm(connectionString);
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);
}
}
}
// --- Главная форма (с изменениями) ---
public class MainForm : Form
{
private string connectionString;
private TabControl tabControl;
private TabPage tabEmployees;
private TabPage tabAttendance;
private DataGridView dgvEmployees;
private DataGridView dgvAttendance;
// Кнопки для сотрудников (доступны через поля класса для легкого доступа)
private Button btnAddEmployee;
private Button btnEditEmployee;
private Button btnDeleteEmployee;
public MainForm(string connString)
{
connectionString = connString;
InitializeComponent();
LoadEmployeeData();
LoadAttendanceData();
// *** ИЗМЕНЕНИЕ: Установить вкладку "Посещаемость" по умолчанию ***
if (tabControl.TabPages.Contains(tabAttendance))
{
tabControl.SelectedTab = tabAttendance;
}
}
private void InitializeComponent()
{
this.Text = "Система учета посещаемости";
this.Width = 800;
this.Height = 600;
this.StartPosition = FormStartPosition.CenterScreen;
this.MinimumSize = new Size(600, 400); // Добавим минимальный размер
// Создаем вкладки
tabControl = new TabControl
{
Dock = DockStyle.Fill
};
// Вкладка "Сотрудники"
tabEmployees = new TabPage
{
Text = "Сотрудники"
};
// Вкладка "Посещаемость"
tabAttendance = new TabPage
{
Text = "Посещаемость"
};
// --- Вкладка Сотрудники ---
Panel panelEmployees = new Panel { Dock = DockStyle.Fill }; // Панель для содержимого вкладки
tabEmployees.Controls.Add(panelEmployees);
// Панель для кнопок сотрудников
Panel panelEmployeesButtons = new Panel
{
Dock = DockStyle.Top,
Height = 50,
Padding = new Padding(10) // Отступы для кнопок
};
panelEmployees.Controls.Add(panelEmployeesButtons); // Добавляем на панель содержимого
// Кнопки для управления сотрудниками
btnAddEmployee = new Button // Присваиваем полю класса
{
Text = "Добавить сотрудника",
Width = 150,
Location = new Point(10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top // Привязка к верху и левому краю
};
btnAddEmployee.Click += BtnAddEmployee_Click; // Обработчик будет содержать проверку пароля
btnEditEmployee = new Button // Присваиваем полю класса
{
Text = "Редактировать",
Width = 150,
Location = new Point(btnAddEmployee.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnEditEmployee.Click += BtnEditEmployee_Click; // Обработчик будет содержать проверку пароля
btnDeleteEmployee = new Button // Присваиваем полю класса
{
Text = "Удалить",
Width = 150,
Location = new Point(btnEditEmployee.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnDeleteEmployee.Click += BtnDeleteEmployee_Click; // Обработчик будет содержать проверку пароля
// Добавляем кнопки на панель кнопок сотрудников
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,
Dock = DockStyle.Fill, // Занимает оставшееся место на панели содержимого
MultiSelect = false // Запретим множественный выбор
};
panelEmployees.Controls.Add(dgvEmployees); // Добавляем на панель содержимого
dgvEmployees.BringToFront(); // Убедимся, что таблица поверх панели кнопок
// --- Вкладка Посещаемость ---
Panel panelAttendance = new Panel { Dock = DockStyle.Fill }; // Панель для содержимого вкладки
tabAttendance.Controls.Add(panelAttendance);
// Панель для кнопок посещаемости
Panel panelAttendanceButtons = new Panel
{
Dock = DockStyle.Top,
Height = 50,
Padding = new Padding(10) // Отступы для кнопок
};
panelAttendance.Controls.Add(panelAttendanceButtons); // Добавляем на панель содержимого
// Кнопки для управления посещаемостью
Button btnArrival = new Button
{
Text = "Отметить прибытие",
Width = 150,
Location = new Point(10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnArrival.Click += BtnArrival_Click;
Button btnDeparture = new Button
{
Text = "Отметить убытие",
Width = 150,
Location = new Point(btnArrival.Right + 10, 10),
Anchor = AnchorStyles.Left | AnchorStyles.Top
};
btnDeparture.Click += BtnDeparture_Click;
// Добавляем две новые кнопки для удаления и редактирования записей
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);
panelAttendanceButtons.Controls.Add(btnDeleteAttendance);
panelAttendanceButtons.Controls.Add(btnEditAttendance);
// Таблица для посещаемости
dgvAttendance = new DataGridView
{
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = true,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
Dock = DockStyle.Fill, // Занимает оставшееся место на панели содержимого
MultiSelect = false // Запретим множественный выбор
};
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();
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;
// Переименовываем колонки для отображения
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 = "Отдел";
// Скрыть 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
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
ORDER BY a.ArrivalTime DESC"; // Сортировка по времени прибытия
SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
dgvAttendance.DataSource = dt;
// Переименовываем колонки для отображения
if (dgvAttendance.Columns.Count > 0) // Проверяем наличие колонок
{
dgvAttendance.Columns["AttendanceID"].HeaderText = "ID записи";
dgvAttendance.Columns["EmployeeID"].HeaderText = "ID сотрудника";
dgvAttendance.Columns["FullName"].HeaderText = "ФИО";
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);
}
}
// --- Обработчики событий для кнопок СОТРУДНИКОВ (с проверкой пароля) ---
private void BtnAddEmployee_Click(object sender, EventArgs e)
{
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
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)
{
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
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)
{
// *** ИЗМЕНЕНИЕ: Проверка мастер-пароля ***
if (VerifyMasterPassword())
{
try
{
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)
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
// Используем PRAGMA foreign_keys = ON; при создании/открытии БД
// и ON DELETE CASCADE в определении внешнего ключа.
// Удаление записей посещаемости произойдет автоматически.
// Удаляем сотрудника
string deleteEmployee = "DELETE FROM Employees WHERE EmployeeID = @EmployeeID";
using (SQLiteCommand command = new SQLiteCommand(deleteEmployee, connection))
{
command.Parameters.AddWithValue("@EmployeeID", employeeId);
int deletedRows = command.ExecuteNonQuery();
if (deletedRows > 0)
{
MessageBox.Show("Сотрудник и связанные записи посещаемости успешно удалены", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
LoadEmployeeData();
LoadAttendanceData();
}
else
{
MessageBox.Show("Не удалось удалить сотрудника.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при удалении сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка получения ID сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} // конец блока проверки пароля
}
else
{
MessageBox.Show("Выберите сотрудника для удаления", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
// --- Обработчики событий для кнопок ПОСЕЩАЕМОСТИ (без изменений, т.к. уже имели проверку пароля где надо) ---
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();
}
}
// Добавляем метод для проверки мастер-пароля
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);
}
}
}
}
// --- Форма 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;
// Поля для ввода данных
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 };
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 };
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 };
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 };
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 };
// Позиционирование полей ввода относительно меток
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; // Растягиваем до правого края с отступом
};
alignTextBox(lblLastName, txtLastName);
alignTextBox(lblFirstName, txtFirstName);
alignTextBox(lblMiddleName, txtMiddleName);
alignTextBox(lblPosition, txtPosition);
alignTextBox(lblDepartment, txtDepartment);
// Кнопки
Button btnSave = new Button
{
Text = "Сохранить",
DialogResult = DialogResult.OK, // Устанавливаем сразу, обработчик может отменить закрытие
Location = new Point(150, 180),
Width = 100,
Anchor = AnchorStyles.Bottom | AnchorStyles.Right // Привязка к правому нижнему углу
};
btnSave.Click += BtnSave_Click;
Button btnCancel = new Button
{
Text = "Отмена",
DialogResult = DialogResult.Cancel, // Закроет форму с результатом 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(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);
// Устанавливаем динамическую высоту формы
this.ClientSize = new Size(400, btnSave.Bottom + 20);
this.AcceptButton = btnSave;
this.CancelButton = btnCancel;
}
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())
{
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);
this.Close(); // Закрыть форму при ошибке
}
}
private void BtnSave_Click(object sender, EventArgs e)
{
// Валидация
if (string.IsNullOrWhiteSpace(txtLastName.Text))
{
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)
{
// Обновление существующего сотрудника
query = @"UPDATE Employees SET
LastName = @LastName,
FirstName = @FirstName,
MiddleName = @MiddleName,
Position = @Position,
Department = @Department
WHERE EmployeeID = @EmployeeID";
}
else
{
// Добавление нового сотрудника
query = @"INSERT INTO Employees (LastName, FirstName, MiddleName, Position, Department)
VALUES (@LastName, @FirstName, @MiddleName, @Position, @Department)";
}
using (SQLiteCommand command = new SQLiteCommand(query, connection))
{
// Используем 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);
}
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; // Отменяем закрытие формы при ошибке
}
}
}
// --- Форма 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();
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),
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 = "Сотрудник:",
Location = new Point(30, 65), // Немного ниже заголовка
AutoSize = true // Авторазмер
};
cmbEmployees = new ComboBox
{
Location = new Point(lblEmployee.Right + 10, lblEmployee.Top - 3), // Выравнивание по вертикали
Width = 250, // Немного шире
DropDownStyle = ComboBoxStyle.DropDownList,
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right // Привязка
};
cmbEmployees.Width = this.ClientSize.Width - cmbEmployees.Left - 30; // Растягивание
// Кнопки
Button btnSave = new Button
{
Text = "Отметить",
DialogResult = DialogResult.OK, // Устанавливаем сразу
Location = new Point(130, 120), // Ниже комбобокса
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(lblHeader);
this.Controls.Add(lblEmployee);
this.Controls.Add(cmbEmployees);
this.Controls.Add(btnSave);
this.Controls.Add(btnCancel);
// Устанавливаем динамическую высоту
this.ClientSize = new Size(400, btnSave.Bottom + 20);
this.AcceptButton = btnSave;
this.CancelButton = btnCancel;
}
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.SelectedIndex = -1; // Сбросить выбор по умолчанию
return true; // Сотрудники загружены
}
else
{
MessageBox.Show("Нет сотрудников для выбора. Сначала добавьте сотрудников.", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
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;
}
object selectedEmployeeId = cmbEmployees.SelectedValue;
try
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
string currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (isArrival)
{
// Проверка, нет ли уже незакрытой отметки о прибытии для этого сотрудника сегодня
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))
{
command.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
command.Parameters.AddWithValue("@ArrivalTime", currentTime);
command.ExecuteNonQuery();
}
}
else // Отметка убытия
{
// Отметка убытия - ищем последнюю запись с прибытием без убытия для данного сотрудника
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))
{
command.Parameters.AddWithValue("@EmployeeID", selectedEmployeeId);
command.Parameters.AddWithValue("@DepartureTime", currentTime);
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected == 0)
{
MessageBox.Show("Для данного сотрудника нет открытых записей о прибытии, для которых можно отметить убытие.",
"Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.DialogResult = DialogResult.None; // Отменяем закрытие
return;
}
}
}
// Если дошли сюда, значит все ок, DialogResult остается OK
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
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();
}
}
}
// --- Форма 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; // Отменяем закрытие
}
}
}
}