1653 lines
83 KiB
C#
1653 lines
83 KiB
C#
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; // Отменяем закрытие
|
||
}
|
||
}
|
||
|
||
}
|
||
} |