mirror of
https://git.battle-of-pip.de/root/vpr-mitarbeiterverwaltung.git
synced 2025-06-21 00:03:18 +02:00
adding clock in break and out command
This commit is contained in:
parent
2645d698d7
commit
536de50e4c
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public enum EmployeeState
|
public enum EmployeeState
|
||||||
{
|
{
|
||||||
WORKING,
|
OUT,
|
||||||
|
IN,
|
||||||
BREAK,
|
BREAK,
|
||||||
AWAY,
|
|
||||||
}
|
}
|
@ -193,7 +193,7 @@ public class Server
|
|||||||
return Task.FromException<Employee>(new Exception("Failed to get logged in employee."));
|
return Task.FromException<Employee>(new Exception("Failed to get logged in employee."));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> clockIn(Employee employee)
|
public Task<string> ClockIn(Employee? employee = null)
|
||||||
{
|
{
|
||||||
var commandResult = ExecuteCommandAsync("clock in").Result;
|
var commandResult = ExecuteCommandAsync("clock in").Result;
|
||||||
if (commandResult == "success")
|
if (commandResult == "success")
|
||||||
@ -202,7 +202,7 @@ public class Server
|
|||||||
}
|
}
|
||||||
return Task.FromException<string>(new Exception("Failed to clock in."));
|
return Task.FromException<string>(new Exception("Failed to clock in."));
|
||||||
}
|
}
|
||||||
public Task<string> clockOut(Employee employee)
|
public Task<string> ClockOut(Employee? employee = null)
|
||||||
{
|
{
|
||||||
var commandResult = ExecuteCommandAsync("clock out").Result;
|
var commandResult = ExecuteCommandAsync("clock out").Result;
|
||||||
if (commandResult == "success")
|
if (commandResult == "success")
|
||||||
@ -214,7 +214,7 @@ public class Server
|
|||||||
return Task.FromException<string>(new Exception("Failed to clock out."));
|
return Task.FromException<string>(new Exception("Failed to clock out."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task<string> clockBreak(Employee employee)
|
public Task<string> ClockBreak(Employee? employee = null)
|
||||||
{
|
{
|
||||||
var commandResult = ExecuteCommandAsync("clock break").Result;
|
var commandResult = ExecuteCommandAsync("clock break").Result;
|
||||||
if (commandResult == "success")
|
if (commandResult == "success")
|
||||||
|
@ -43,7 +43,7 @@ public class BackendServer : DX86.TcpServer
|
|||||||
JsonMessage? jsonMessage;
|
JsonMessage? jsonMessage;
|
||||||
if (message.StartsWith("{") && message.EndsWith("}"))
|
if (message.StartsWith("{") && message.EndsWith("}"))
|
||||||
{
|
{
|
||||||
this.SendMessageAsync(client, "Command received\n");
|
//this.SendMessageAsync(client, "Command received\n");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
jsonMessage = JsonSerializer.Deserialize<JsonMessage>(message);
|
jsonMessage = JsonSerializer.Deserialize<JsonMessage>(message);
|
||||||
|
@ -170,6 +170,123 @@ public class CommandLibrary
|
|||||||
return self.ToJson();
|
return self.ToJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command("clock")]
|
||||||
|
public static string ClockCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||||||
|
{
|
||||||
|
// 1) Argument check
|
||||||
|
if (args.Length < 1)
|
||||||
|
throw new CommandException("Missing argument: usage is clock <in|out|break>");
|
||||||
|
|
||||||
|
string desired = args[0].ToLowerInvariant();
|
||||||
|
int desiredState;
|
||||||
|
switch (desired)
|
||||||
|
{
|
||||||
|
case "in":
|
||||||
|
desiredState = 1;
|
||||||
|
break;
|
||||||
|
case "out":
|
||||||
|
desiredState = 0;
|
||||||
|
break;
|
||||||
|
case "break":
|
||||||
|
desiredState = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new CommandException("Invalid argument. Use in, out, or break.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Verify client/socket
|
||||||
|
if (client == null || socket == null)
|
||||||
|
throw new CommandException("No client connection detected.");
|
||||||
|
|
||||||
|
if (!socket.LoggedInClients.TryGetValue(client, out var employeeCode))
|
||||||
|
throw new CommandException("User not logged in.");
|
||||||
|
|
||||||
|
// 3) Look up the employee by Code to get Id and current state
|
||||||
|
var empParams = new Dictionary<string, object> { { "Code", employeeCode } };
|
||||||
|
string empResultJson = Program.mySql.Get("employees", empParams);
|
||||||
|
|
||||||
|
using var empDoc = JsonDocument.Parse(empResultJson);
|
||||||
|
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (empError)
|
||||||
|
{
|
||||||
|
string dbMsg = empDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||||||
|
Program.messageSender.Error($"[ClockCommand] DB error when looking up employee: {dbMsg}");
|
||||||
|
throw new CommandException("Internal error while fetching employee data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var empArray = empDoc.RootElement.GetProperty("data");
|
||||||
|
if (empArray.GetArrayLength() == 0)
|
||||||
|
throw new CommandException("Logged-in user not found in database.");
|
||||||
|
|
||||||
|
var empRow = empArray[0];
|
||||||
|
int employeeId = empRow.GetProperty("Id").GetInt32();
|
||||||
|
int currentState = empRow.GetProperty("EmployeeState").GetInt32();
|
||||||
|
|
||||||
|
// 4) Validate state transition
|
||||||
|
if (currentState == desiredState)
|
||||||
|
throw new CommandException($"Already {(desiredState == 0 ? "OUT" : desiredState == 1 ? "IN" : "on BREAK")}.");
|
||||||
|
|
||||||
|
bool valid = false;
|
||||||
|
switch (currentState)
|
||||||
|
{
|
||||||
|
case 0: // OUT → only IN
|
||||||
|
valid = (desiredState == 1);
|
||||||
|
break;
|
||||||
|
case 1: // IN → OUT or BREAK
|
||||||
|
valid = (desiredState == 0 || desiredState == 2);
|
||||||
|
break;
|
||||||
|
case 2: // BREAK → only IN
|
||||||
|
valid = (desiredState == 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
throw new CommandException("Invalid transition from current status.");
|
||||||
|
|
||||||
|
// 5) Update employees.EmployeeState to desiredState
|
||||||
|
var updateData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "EmployeeState", desiredState }
|
||||||
|
};
|
||||||
|
var whereData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "Id", employeeId }
|
||||||
|
};
|
||||||
|
string updateJson = Program.mySql.Update("employees", updateData, whereData);
|
||||||
|
|
||||||
|
using (var updateDoc = JsonDocument.Parse(updateJson))
|
||||||
|
{
|
||||||
|
bool updError = updateDoc.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (updError)
|
||||||
|
{
|
||||||
|
string dbMsg = updateDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||||||
|
Program.messageSender.Error($"[ClockCommand] DB error when updating state: {dbMsg}");
|
||||||
|
throw new CommandException("Internal error while updating status.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) Insert into employee_state_history
|
||||||
|
var historyData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "EmployeeId", employeeId },
|
||||||
|
{ "NewState", desiredState }
|
||||||
|
};
|
||||||
|
string historyJson = Program.mySql.Insert("employee_state_history", historyData);
|
||||||
|
|
||||||
|
using (var histDoc = JsonDocument.Parse(historyJson))
|
||||||
|
{
|
||||||
|
bool histError = histDoc.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (histError)
|
||||||
|
{
|
||||||
|
string dbMsg = histDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||||||
|
Program.messageSender.Error($"[ClockCommand] DB error when logging history: {dbMsg}");
|
||||||
|
throw new CommandException("Internal error while logging status change.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7) Return success
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Administration Commands
|
#region Administration Commands
|
||||||
|
@ -31,7 +31,8 @@ namespace Server;
|
|||||||
ServerLoop();
|
ServerLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MySQL LoadOrInitDatabase()
|
#region MySQL Initialization
|
||||||
|
private static MySQL LoadOrInitDatabase()
|
||||||
{
|
{
|
||||||
var cfg = DbConfig.Load();
|
var cfg = DbConfig.Load();
|
||||||
if (cfg != null)
|
if (cfg != null)
|
||||||
@ -54,107 +55,143 @@ namespace Server;
|
|||||||
return InitializeDatabase(username, password, server, port, database);
|
return InitializeDatabase(username, password, server, port, database);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Creates a new MySQL connection, runs the CREATE TABLE DDL,
|
||||||
/// Creates a new MySQL connection, runs the CREATE TABLE DDL,
|
/// saves credentials in config.json, and returns the MySQL instance.
|
||||||
/// saves credentials in config.json, and returns the MySQL instance.
|
/// </summary>
|
||||||
/// </summary>
|
private static MySQL InitializeDatabase(
|
||||||
private static MySQL InitializeDatabase(
|
string username,
|
||||||
string username,
|
string password,
|
||||||
string password,
|
string server,
|
||||||
string server,
|
string port,
|
||||||
string port,
|
string database
|
||||||
string database
|
)
|
||||||
)
|
|
||||||
{
|
|
||||||
// 1) Instantiate MySQL (will exit on failure)
|
|
||||||
var db = new MySQL(username, password, server, port, database, messageSender);
|
|
||||||
string createEmployeesTable = @"
|
|
||||||
CREATE TABLE IF NOT EXISTS `employees` (
|
|
||||||
`Id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`Code` VARCHAR(50) NOT NULL,
|
|
||||||
`Surname` VARCHAR(100) NOT NULL,
|
|
||||||
`Forename` VARCHAR(100) NOT NULL,
|
|
||||||
`Email` VARCHAR(150) NOT NULL,
|
|
||||||
`Phone` VARCHAR(50),
|
|
||||||
`Street` VARCHAR(200),
|
|
||||||
`City` VARCHAR(100),
|
|
||||||
`Postcode` VARCHAR(20),
|
|
||||||
`Country` VARCHAR(100),
|
|
||||||
`Department` VARCHAR(100),
|
|
||||||
`Position` VARCHAR(100),
|
|
||||||
`EmployeeState` INT NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
";
|
|
||||||
|
|
||||||
messageSender.Log("[Program] Creating or verifying 'employees' table…");
|
|
||||||
var employeesResult = db.ExecuteNonQuery(createEmployeesTable);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var doc = JsonDocument.Parse(employeesResult);
|
|
||||||
bool empError = doc.RootElement.GetProperty("error").GetBoolean();
|
|
||||||
if (empError)
|
|
||||||
{
|
{
|
||||||
messageSender.Error("[Program] Failed to create/verify 'employees' table: " + employeesResult);
|
// 1) Instantiate MySQL (will exit on failure)
|
||||||
Environment.Exit(1);
|
var db = new MySQL(username, password, server, port, database, messageSender);
|
||||||
|
string createEmployeesTable = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS `employees` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`Code` VARCHAR(50) NOT NULL,
|
||||||
|
`Surname` VARCHAR(100) NOT NULL,
|
||||||
|
`Forename` VARCHAR(100) NOT NULL,
|
||||||
|
`Email` VARCHAR(150) NOT NULL,
|
||||||
|
`Phone` VARCHAR(50),
|
||||||
|
`Street` VARCHAR(200),
|
||||||
|
`City` VARCHAR(100),
|
||||||
|
`Postcode` VARCHAR(20),
|
||||||
|
`Country` VARCHAR(100),
|
||||||
|
`Department` VARCHAR(100),
|
||||||
|
`Position` VARCHAR(100),
|
||||||
|
`EmployeeState` INT NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
";
|
||||||
|
|
||||||
|
messageSender.Log("[Program] Creating or verifying 'employees' table…");
|
||||||
|
var employeesResult = db.ExecuteNonQuery(createEmployeesTable);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(employeesResult);
|
||||||
|
bool empError = doc.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (empError)
|
||||||
|
{
|
||||||
|
messageSender.Error("[Program] Failed to create/verify 'employees' table: " + employeesResult);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
messageSender.Error("[Program] Could not parse employees CREATE TABLE response: " + ex.Message);
|
||||||
|
messageSender.Error("[Program] Raw response: " + employeesResult);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
messageSender.Log("[Program] 'employees' table is ready.");
|
||||||
|
|
||||||
|
// 2b) Create a new table for storing 4-digit PINs linked to EmployeeId
|
||||||
|
string createPinsTable = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS `employee_pins` (
|
||||||
|
`PinId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`EmployeeId` INT NOT NULL,
|
||||||
|
`PinCode` CHAR(4) NOT NULL,
|
||||||
|
FOREIGN KEY (`EmployeeId`) REFERENCES `employees`(`Id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
";
|
||||||
|
|
||||||
|
messageSender.Log("[Program] Creating or verifying 'employee_pins' table…");
|
||||||
|
var pinsResult = db.ExecuteNonQuery(createPinsTable);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc2 = JsonDocument.Parse(pinsResult);
|
||||||
|
bool pinError = doc2.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (pinError)
|
||||||
|
{
|
||||||
|
messageSender.Error("[Program] Failed to create/verify 'employee_pins' table: " + pinsResult);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
messageSender.Error("[Program] Could not parse employee_pins CREATE TABLE response: " + ex.Message);
|
||||||
|
messageSender.Error("[Program] Raw response: " + pinsResult);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
messageSender.Log("[Program] 'employee_pins' table is ready.");
|
||||||
|
|
||||||
|
// 3) Neue Tabelle für Status‐Historie anlegen:
|
||||||
|
string createStateHistoryTable = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS `employee_state_history` (
|
||||||
|
`HistoryId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`EmployeeId` INT NOT NULL,
|
||||||
|
`NewState` INT NOT NULL,
|
||||||
|
`ChangeTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (`EmployeeId`)
|
||||||
|
REFERENCES `employees`(`Id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
";
|
||||||
|
|
||||||
|
Program.messageSender.Log("[Program] Ensuring 'employee_state_history' table exists…");
|
||||||
|
var historyResultJson = db.ExecuteNonQuery(createStateHistoryTable);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(historyResultJson);
|
||||||
|
bool histError = doc.RootElement.GetProperty("error").GetBoolean();
|
||||||
|
if (histError)
|
||||||
|
{
|
||||||
|
string dbMsg = doc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||||||
|
Program.messageSender.Error("[Program] Failed to create 'employee_state_history' table: " + dbMsg);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.messageSender.Error("[Program] Could not parse state history CREATE TABLE response: " + ex.Message);
|
||||||
|
Program.messageSender.Error("[Program] Raw response: " + historyResultJson);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
Program.messageSender.Log("[Program] 'employee_state_history' table is ready.");
|
||||||
|
|
||||||
|
// … (any further tables) …
|
||||||
|
|
||||||
|
// 3) Save the new credentials (including port) into config.json:
|
||||||
|
var newCfg = new DbConfig
|
||||||
|
{
|
||||||
|
Username = username,
|
||||||
|
Password = password,
|
||||||
|
Server = server,
|
||||||
|
Port = port,
|
||||||
|
Database = database
|
||||||
|
};
|
||||||
|
newCfg.Save();
|
||||||
|
messageSender.Log("[Program] Saved DB configuration to config.json.");
|
||||||
|
|
||||||
|
return db;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
messageSender.Error("[Program] Could not parse employees CREATE TABLE response: " + ex.Message);
|
|
||||||
messageSender.Error("[Program] Raw response: " + employeesResult);
|
|
||||||
Environment.Exit(1);
|
|
||||||
}
|
|
||||||
messageSender.Log("[Program] 'employees' table is ready.");
|
|
||||||
|
|
||||||
// 2b) Create a new table for storing 4-digit PINs linked to EmployeeId
|
#endregion
|
||||||
string createPinsTable = @"
|
|
||||||
CREATE TABLE IF NOT EXISTS `employee_pins` (
|
|
||||||
`PinId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`EmployeeId` INT NOT NULL,
|
|
||||||
`PinCode` CHAR(4) NOT NULL,
|
|
||||||
FOREIGN KEY (`EmployeeId`) REFERENCES `employees`(`Id`)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
";
|
|
||||||
|
|
||||||
messageSender.Log("[Program] Creating or verifying 'employee_pins' table…");
|
|
||||||
var pinsResult = db.ExecuteNonQuery(createPinsTable);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var doc2 = JsonDocument.Parse(pinsResult);
|
|
||||||
bool pinError = doc2.RootElement.GetProperty("error").GetBoolean();
|
|
||||||
if (pinError)
|
|
||||||
{
|
|
||||||
messageSender.Error("[Program] Failed to create/verify 'employee_pins' table: " + pinsResult);
|
|
||||||
Environment.Exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
messageSender.Error("[Program] Could not parse employee_pins CREATE TABLE response: " + ex.Message);
|
|
||||||
messageSender.Error("[Program] Raw response: " + pinsResult);
|
|
||||||
Environment.Exit(1);
|
|
||||||
}
|
|
||||||
messageSender.Log("[Program] 'employee_pins' table is ready.");
|
|
||||||
|
|
||||||
// … (any further tables) …
|
|
||||||
|
|
||||||
// 3) Save the new credentials (including port) into config.json:
|
|
||||||
var newCfg = new DbConfig
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Password = password,
|
|
||||||
Server = server,
|
|
||||||
Port = port,
|
|
||||||
Database = database
|
|
||||||
};
|
|
||||||
newCfg.Save();
|
|
||||||
messageSender.Log("[Program] Saved DB configuration to config.json.");
|
|
||||||
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ServerLoop()
|
private static void ServerLoop()
|
||||||
{
|
{
|
||||||
|
@ -81,6 +81,27 @@ public class Tester
|
|||||||
$"Forename: {employee.Forename}",
|
$"Forename: {employee.Forename}",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (command == "clock in")
|
||||||
|
{
|
||||||
|
string response = await server.ClockIn();
|
||||||
|
// split response into string[] at \n
|
||||||
|
var responseLines = response.Split('\n').ToList();
|
||||||
|
await messageBox.ShowAsync("Command Result: clock in", responseLines);
|
||||||
|
}
|
||||||
|
else if (command == "clock out")
|
||||||
|
{
|
||||||
|
string response = await server.ClockOut();
|
||||||
|
// split response into string[] at \n
|
||||||
|
var responseLines = response.Split('\n').ToList();
|
||||||
|
await messageBox.ShowAsync("Command Result: clock out", responseLines);
|
||||||
|
}
|
||||||
|
else if (command == "clock break")
|
||||||
|
{
|
||||||
|
string response = await server.ClockBreak();
|
||||||
|
// split response into string[] at \n
|
||||||
|
var responseLines = response.Split('\n').ToList();
|
||||||
|
await messageBox.ShowAsync("Command Result: clock break", responseLines);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user