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