diff --git a/App.config b/App.config
new file mode 100644
index 0000000..24c49e7
--- /dev/null
+++ b/App.config
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Form1.Designer.cs b/Form1.Designer.cs
new file mode 100644
index 0000000..a393283
--- /dev/null
+++ b/Form1.Designer.cs
@@ -0,0 +1,63 @@
+namespace WindowsFormsApp1
+{
+ partial class Form1
+ {
+ ///
+ /// Обязательная переменная конструктора.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Освободить все используемые ресурсы.
+ ///
+ /// истинно, если управляемый ресурс должен быть удален; иначе ложно.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Код, автоматически созданный конструктором форм Windows
+
+ ///
+ /// Требуемый метод для поддержки конструктора — не изменяйте
+ /// содержимое этого метода с помощью редактора кода.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.buttonLogin = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // buttonLogin
+ //
+ this.buttonLogin.Location = new System.Drawing.Point(778, 428);
+ this.buttonLogin.Name = "buttonLogin";
+ this.buttonLogin.Size = new System.Drawing.Size(10, 10);
+ this.buttonLogin.TabIndex = 0;
+ this.buttonLogin.Text = "button1";
+ this.buttonLogin.UseVisualStyleBackColor = true;
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 450);
+ this.Controls.Add(this.buttonLogin);
+ this.Name = "Form1";
+ this.Text = "Form1";
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Button buttonLogin;
+ }
+}
+
diff --git a/Form1.cs b/Form1.cs
new file mode 100644
index 0000000..aae0781
--- /dev/null
+++ b/Form1.cs
@@ -0,0 +1,929 @@
+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();
+ }
+
+ 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,
+ Password TEXT NOT NULL)";
+ using (SQLiteCommand command = new SQLiteCommand(createUsersTable, connection))
+ {
+ command.ExecuteNonQuery();
+ }
+
+ // Создаем admin пользователя
+ 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))";
+ using (SQLiteCommand command = new SQLiteCommand(createAttendanceTable, 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),
+ Width = 350,
+ TextAlign = ContentAlignment.MiddleCenter,
+ Location = new Point(70, 20)
+ };
+
+ // Поле логина
+ Label lblLogin = new Label
+ {
+ Text = "Логин:",
+ Location = new Point(70, 70),
+ Width = 100
+ };
+
+ TextBox txtLogin = new TextBox
+ {
+ Name = "txtLogin",
+ Location = new Point(170, 70),
+ Width = 200
+ };
+
+ // Поле пароля
+ Label lblPassword = new Label
+ {
+ Text = "Пароль:",
+ Location = new Point(70, 100),
+ Width = 100
+ };
+
+ TextBox txtPassword = new TextBox
+ {
+ Name = "txtPassword",
+ Location = new Point(170, 100),
+ Width = 200,
+ PasswordChar = '*'
+ };
+
+ // Кнопка входа
+ Button buttonLogin = new Button
+ {
+ Name = "buttonLogin",
+ Text = "Войти",
+ Location = new Point(200, 150),
+ Width = 100
+ };
+ buttonLogin.Click += ButtonLogin_Click;
+
+ // Добавляем все компоненты на форму
+ 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.Width = 480;
+ this.Height = 250;
+ }
+
+ private void ButtonLogin_Click(object sender, EventArgs e)
+ {
+ string login = this.Controls["txtLogin"].Text;
+ string password = this.Controls["txtPassword"].Text;
+
+ try
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(connectionString))
+ {
+ connection.Open();
+ string query = "SELECT COUNT(*) FROM Users WHERE Login = @Login AND Password = @Password";
+ 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)
+ {
+ MessageBox.Show("Вход выполнен успешно!", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
+
+ // Скрываем текущую форму
+ 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;
+
+ public MainForm(string connString)
+ {
+ connectionString = connString;
+
+ InitializeComponent();
+ LoadEmployeeData();
+ LoadAttendanceData();
+ }
+
+ private void InitializeComponent()
+ {
+ this.Text = "Система учета посещаемости";
+ this.Width = 800;
+ this.Height = 600;
+ this.StartPosition = FormStartPosition.CenterScreen;
+
+ // Создаем вкладки
+ tabControl = new TabControl
+ {
+ Dock = DockStyle.Fill
+ };
+
+ // Вкладка "Сотрудники"
+ tabEmployees = new TabPage
+ {
+ Text = "Сотрудники"
+ };
+
+ // Вкладка "Посещаемость"
+ tabAttendance = new TabPage
+ {
+ Text = "Посещаемость"
+ };
+
+ // Панель для кнопок сотрудников
+ Panel panelEmployeesButtons = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 50
+ };
+
+ // Кнопки для управления сотрудниками
+ Button btnAddEmployee = new Button
+ {
+ Text = "Добавить сотрудника",
+ Width = 150,
+ Location = new Point(10, 10)
+ };
+ btnAddEmployee.Click += BtnAddEmployee_Click;
+
+ Button btnEditEmployee = new Button
+ {
+ Text = "Редактировать",
+ Width = 150,
+ Location = new Point(170, 10)
+ };
+ btnEditEmployee.Click += BtnEditEmployee_Click;
+
+ Button btnDeleteEmployee = new Button
+ {
+ Text = "Удалить",
+ Width = 150,
+ Location = new Point(330, 10)
+ };
+ 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
+ };
+
+ // ВАЖНО: сначала добавляем DataGridView, затем панель кнопок
+ // Это обеспечивает правильное расположение элементов сверху вниз
+ tabEmployees.Controls.Add(dgvEmployees);
+ tabEmployees.Controls.Add(panelEmployeesButtons);
+
+ // Панель для кнопок посещаемости
+ Panel panelAttendanceButtons = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 50
+ };
+
+ // Кнопки для управления посещаемостью
+ Button btnArrival = new Button
+ {
+ Text = "Отметить прибытие",
+ Width = 150,
+ Location = new Point(10, 10)
+ };
+ btnArrival.Click += BtnArrival_Click;
+
+ Button btnDeparture = new Button
+ {
+ Text = "Отметить убытие",
+ Width = 150,
+ Location = new Point(170, 10)
+ };
+ btnDeparture.Click += BtnDeparture_Click;
+
+ // Добавляем кнопки на панель
+ panelAttendanceButtons.Controls.Add(btnArrival);
+ panelAttendanceButtons.Controls.Add(btnDeparture);
+
+ // Таблица для посещаемости
+ dgvAttendance = new DataGridView
+ {
+ AllowUserToAddRows = false,
+ AllowUserToDeleteRows = false,
+ ReadOnly = true,
+ AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
+ SelectionMode = DataGridViewSelectionMode.FullRowSelect,
+ Dock = DockStyle.Fill
+ };
+
+ // ВАЖНО: сначала добавляем DataGridView, затем панель кнопок
+ tabAttendance.Controls.Add(dgvAttendance);
+ tabAttendance.Controls.Add(panelAttendanceButtons);
+
+ // Добавляем вкладки в контрол
+ 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";
+ SQLiteDataAdapter adapter = new SQLiteDataAdapter(query, connection);
+ DataTable dt = new DataTable();
+ adapter.Fill(dt);
+ dgvEmployees.DataSource = dt;
+
+ // Переименовываем колонки для отображения
+ if (dt.Rows.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 = "Отдел";
+ }
+ }
+ }
+ 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,
+ a.ArrivalTime, a.DepartureTime
+ 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 (dt.Rows.Count > 0)
+ {
+ dgvAttendance.Columns["AttendanceID"].HeaderText = "ID записи";
+ dgvAttendance.Columns["EmployeeID"].HeaderText = "ID сотрудника";
+ dgvAttendance.Columns["FullName"].HeaderText = "ФИО";
+ dgvAttendance.Columns["ArrivalTime"].HeaderText = "Время прибытия";
+ dgvAttendance.Columns["DepartureTime"].HeaderText = "Время убытия";
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка загрузки данных посещаемости: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ // Обработчики событий для кнопок
+ private void BtnAddEmployee_Click(object sender, EventArgs e)
+ {
+ 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)
+ {
+ int employeeId = Convert.ToInt32(dgvEmployees.SelectedRows[0].Cells["EmployeeID"].Value);
+ EmployeeForm form = new EmployeeForm(connectionString, employeeId);
+ if (form.ShowDialog() == DialogResult.OK)
+ {
+ LoadEmployeeData();
+ }
+ }
+ else
+ {
+ MessageBox.Show("Выберите сотрудника для редактирования", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ }
+
+ private void BtnDeleteEmployee_Click(object sender, EventArgs e)
+ {
+ if (dgvEmployees.SelectedRows.Count > 0)
+ {
+ 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}?",
+ "Подтверждение", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+ if (result == DialogResult.Yes)
+ {
+ try
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(connectionString))
+ {
+ connection.Open();
+ // Начинаем транзакцию
+ using (SQLiteTransaction transaction = connection.BeginTransaction())
+ {
+ try
+ {
+ // Сначала удаляем записи посещаемости
+ string deleteAttendance = "DELETE FROM Attendance WHERE EmployeeID = @EmployeeID";
+ using (SQLiteCommand command = new SQLiteCommand(deleteAttendance, connection, transaction))
+ {
+ command.Parameters.AddWithValue("@EmployeeID", employeeId);
+ command.ExecuteNonQuery();
+ }
+
+ // Затем удаляем сотрудника
+ string deleteEmployee = "DELETE FROM Employees WHERE EmployeeID = @EmployeeID";
+ using (SQLiteCommand command = new SQLiteCommand(deleteEmployee, connection, transaction))
+ {
+ command.Parameters.AddWithValue("@EmployeeID", employeeId);
+ command.ExecuteNonQuery();
+ }
+
+ // Фиксируем изменения
+ transaction.Commit();
+ MessageBox.Show("Сотрудник успешно удален", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ LoadEmployeeData();
+ LoadAttendanceData();
+ }
+ catch (Exception ex)
+ {
+ // Откатываем изменения при ошибке
+ transaction.Rollback();
+ throw ex;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка при удалении сотрудника: " + 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();
+ }
+ }
+ }
+
+ // Форма для добавления/редактирования сотрудника
+ 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
+ };
+
+ txtLastName = new TextBox
+ {
+ Location = new Point(150, 20),
+ Width = 200
+ };
+
+ Label lblFirstName = new Label
+ {
+ Text = "Имя:",
+ Location = new Point(30, 50),
+ Width = 100
+ };
+
+ txtFirstName = new TextBox
+ {
+ Location = new Point(150, 50),
+ Width = 200
+ };
+
+ Label lblMiddleName = new Label
+ {
+ Text = "Отчество:",
+ Location = new Point(30, 80),
+ Width = 100
+ };
+
+ txtMiddleName = new TextBox
+ {
+ Location = new Point(150, 80),
+ Width = 200
+ };
+
+ Label lblPosition = new Label
+ {
+ Text = "Должность:",
+ Location = new Point(30, 110),
+ Width = 100
+ };
+
+ txtPosition = new TextBox
+ {
+ Location = new Point(150, 110),
+ Width = 200
+ };
+
+ Label lblDepartment = new Label
+ {
+ Text = "Отдел:",
+ Location = new Point(30, 140),
+ Width = 100
+ };
+
+ txtDepartment = new TextBox
+ {
+ Location = new Point(150, 140),
+ Width = 200
+ };
+
+ // Кнопки
+ Button btnSave = new Button
+ {
+ Text = "Сохранить",
+ DialogResult = DialogResult.OK,
+ Location = new Point(150, 180),
+ Width = 100
+ };
+ btnSave.Click += BtnSave_Click;
+
+ Button btnCancel = new Button
+ {
+ Text = "Отмена",
+ DialogResult = DialogResult.Cancel,
+ Location = new Point(260, 180),
+ Width = 100
+ };
+
+ // Добавляем компоненты на форму
+ 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.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();
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка при загрузке данных сотрудника: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void BtnSave_Click(object sender, EventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(txtLastName.Text) || string.IsNullOrWhiteSpace(txtFirstName.Text))
+ {
+ MessageBox.Show("Фамилия и имя обязательны для заполнения", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ this.DialogResult = DialogResult.None;
+ 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))
+ {
+ command.Parameters.AddWithValue("@LastName", txtLastName.Text);
+ command.Parameters.AddWithValue("@FirstName", txtFirstName.Text);
+ command.Parameters.AddWithValue("@MiddleName", txtMiddleName.Text);
+ command.Parameters.AddWithValue("@Position", txtPosition.Text);
+ command.Parameters.AddWithValue("@Department", txtDepartment.Text);
+
+ if (employeeId > 0)
+ {
+ command.Parameters.AddWithValue("@EmployeeID", employeeId);
+ }
+
+ command.ExecuteNonQuery();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ this.DialogResult = DialogResult.None;
+ }
+ }
+ }
+
+ // Форма для отметки посещаемости
+ public class AttendanceForm : Form
+ {
+ private string connectionString;
+ private bool isArrival;
+ private ComboBox cmbEmployees;
+
+ public AttendanceForm(string connString, bool arrival)
+ {
+ connectionString = connString;
+ isArrival = arrival;
+
+ InitializeComponent();
+ LoadEmployees();
+
+ 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(50, 20),
+ Width = 300,
+ TextAlign = ContentAlignment.MiddleCenter
+ };
+
+ // Выбор сотрудника
+ Label lblEmployee = new Label
+ {
+ Text = "Сотрудник:",
+ Location = new Point(30, 60),
+ Width = 100
+ };
+
+ cmbEmployees = new ComboBox
+ {
+ Location = new Point(130, 60),
+ Width = 220,
+ DropDownStyle = ComboBoxStyle.DropDownList
+ };
+
+ // Кнопки
+ Button btnSave = new Button
+ {
+ Text = "Отметить",
+ DialogResult = DialogResult.OK,
+ Location = new Point(130, 100),
+ Width = 100
+ };
+ btnSave.Click += BtnSave_Click;
+
+ Button btnCancel = new Button
+ {
+ Text = "Отмена",
+ DialogResult = DialogResult.Cancel,
+ Location = new Point(250, 100),
+ Width = 100
+ };
+
+ // Добавляем компоненты на форму
+ this.Controls.Add(lblHeader);
+ this.Controls.Add(lblEmployee);
+ this.Controls.Add(cmbEmployees);
+ this.Controls.Add(btnSave);
+ this.Controls.Add(btnCancel);
+
+ this.AcceptButton = btnSave;
+ this.CancelButton = btnCancel;
+ }
+
+ private void 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);
+
+ cmbEmployees.DataSource = dt;
+ cmbEmployees.DisplayMember = "FullName";
+ cmbEmployees.ValueMember = "EmployeeID";
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка при загрузке списка сотрудников: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void BtnSave_Click(object sender, EventArgs e)
+ {
+ if (cmbEmployees.SelectedValue == null)
+ {
+ MessageBox.Show("Необходимо выбрать сотрудника", "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ this.DialogResult = DialogResult.None;
+ return;
+ }
+
+ try
+ {
+ using (SQLiteConnection connection = new SQLiteConnection(connectionString))
+ {
+ connection.Open();
+
+ if (isArrival)
+ {
+ // Отметка прибытия - создаем новую запись
+ string query = "INSERT INTO Attendance (EmployeeID, ArrivalTime) VALUES (@EmployeeID, @ArrivalTime)";
+ using (SQLiteCommand command = new SQLiteCommand(query, connection))
+ {
+ command.Parameters.AddWithValue("@EmployeeID", cmbEmployees.SelectedValue);
+ command.Parameters.AddWithValue("@ArrivalTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
+ 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", cmbEmployees.SelectedValue);
+ command.Parameters.AddWithValue("@DepartureTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
+ int rowsAffected = command.ExecuteNonQuery();
+
+ if (rowsAffected == 0)
+ {
+ MessageBox.Show("Для данного сотрудника нет открытых записей о прибытии",
+ "Информация", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ this.DialogResult = DialogResult.None;
+ return;
+ }
+ }
+ }
+
+ MessageBox.Show("Посещаемость успешно отмечена", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Ошибка при сохранении данных: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ this.DialogResult = DialogResult.None;
+ }
+ }
+ }
+
+}
diff --git a/Form1.resx b/Form1.resx
new file mode 100644
index 0000000..1f666f2
--- /dev/null
+++ b/Form1.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..88df6bb
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace WindowsFormsApp1
+{
+ static class Program
+ {
+ ///
+ /// Главная точка входа для приложения.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new Form1());
+ }
+ }
+}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e015c6a
--- /dev/null
+++ b/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Общие сведения об этой сборке предоставляются следующим набором
+// набора атрибутов. Измените значения этих атрибутов для изменения сведений,
+// связанных со сборкой.
+[assembly: AssemblyTitle("WindowsFormsApp1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WindowsFormsApp1")]
+[assembly: AssemblyCopyright("Copyright © 2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми
+// для компонентов COM. Если необходимо обратиться к типу в этой сборке через
+// COM, следует установить атрибут ComVisible в TRUE для этого типа.
+[assembly: ComVisible(false)]
+
+// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM
+[assembly: Guid("58e43c91-f1b2-46c1-a569-552c7a6e1d05")]
+
+// Сведения о версии сборки состоят из указанных ниже четырех значений:
+//
+// Основной номер версии
+// Дополнительный номер версии
+// Номер сборки
+// Редакция
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..3ab32fe
--- /dev/null
+++ b/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// Этот код создан программным средством.
+// Версия среды выполнения: 4.0.30319.42000
+//
+// Изменения в этом файле могут привести к неправильному поведению и будут утрачены, если
+// код создан повторно.
+//
+//------------------------------------------------------------------------------
+
+namespace WindowsFormsApp1.Properties
+{
+
+
+ ///
+ /// Класс ресурсов со строгим типом для поиска локализованных строк и пр.
+ ///
+ // Этот класс был автоматически создан при помощи StronglyTypedResourceBuilder
+ // класс с помощью таких средств, как ResGen или Visual Studio.
+ // Для добавления или удаления члена измените файл .ResX, а затем перезапустите ResGen
+ // с параметром /str или заново постройте свой VS-проект.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Возврат кэшированного экземпляра ResourceManager, используемого этим классом.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowsFormsApp1.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Переопределяет свойство CurrentUICulture текущего потока для всех
+ /// подстановки ресурсов с помощью этого класса ресурсов со строгим типом.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..438df21
--- /dev/null
+++ b/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WindowsFormsApp1.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Properties/Settings.settings b/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/WindowsFormsApp1.csproj b/WindowsFormsApp1.csproj
new file mode 100644
index 0000000..5756105
--- /dev/null
+++ b/WindowsFormsApp1.csproj
@@ -0,0 +1,113 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {58E43C91-F1B2-46C1-A569-552C7A6E1D05}
+ WinExe
+ WindowsFormsApp1
+ WindowsFormsApp1
+ v4.7.2
+ 512
+ true
+ true
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll
+
+
+ packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll
+
+
+
+
+
+ packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.119.0\lib\net46\System.Data.SQLite.dll
+
+
+ packages\System.Data.SQLite.EF6.1.0.119.0\lib\net46\System.Data.SQLite.EF6.dll
+
+
+ packages\System.Data.SQLite.Linq.1.0.119.0\lib\net46\System.Data.SQLite.Linq.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Form1.cs
+
+
+
+
+ Form1.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+
+ Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WindowsFormsApp1.sln b/WindowsFormsApp1.sln
new file mode 100644
index 0000000..6e5b74c
--- /dev/null
+++ b/WindowsFormsApp1.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35828.75 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsFormsApp1", "WindowsFormsApp1.csproj", "{58E43C91-F1B2-46C1-A569-552C7A6E1D05}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {58E43C91-F1B2-46C1-A569-552C7A6E1D05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {58E43C91-F1B2-46C1-A569-552C7A6E1D05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {58E43C91-F1B2-46C1-A569-552C7A6E1D05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {58E43C91-F1B2-46C1-A569-552C7A6E1D05}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {38090D2C-ED9E-412B-8102-3DCF86037F7A}
+ EndGlobalSection
+EndGlobal
diff --git a/packages.config b/packages.config
new file mode 100644
index 0000000..1146e4e
--- /dev/null
+++ b/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file