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