From e13b0f87407530ee309f12cb9a9ed1ad193941ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B2=E8=8F=AF?= <42814579+yunwah@users.noreply.github.com> Date: Sun, 17 Apr 2022 16:57:24 -0400 Subject: [PATCH] Finishes major documentation, bug fixes, and small optimizations --- OpheliasOasis/Managers/Database.cs | 49 +- OpheliasOasis/Managers/Hotel.cs | 1525 ++++++++++++++++------------ OpheliasOasis/Program.cs | 2 + 3 files changed, 897 insertions(+), 679 deletions(-) diff --git a/OpheliasOasis/Managers/Database.cs b/OpheliasOasis/Managers/Database.cs index 6839d27..ae757b4 100644 --- a/OpheliasOasis/Managers/Database.cs +++ b/OpheliasOasis/Managers/Database.cs @@ -5,6 +5,14 @@ namespace Ophelias.Managers { internal class Database : IDisposable { + /* + * This class provides some basic functions that + * mirror SQLite. The reason for this class is to include + * some functions on it that typically would be separate. + * In another world, these functions could be of another + * static class as they don't really serve a purpose other + * than a one-time use if the database needs initialization. + */ internal SQLiteConnection con = new("DataSource=database.sqlite3;Version=3;"); internal Database() @@ -22,6 +30,11 @@ namespace Ophelias.Managers } internal void InitializeTables() { + /* + * This string is used to create the tables used by the models + * and the database functions. For more details, see the formatting + * string below as it has been organized in such a way there it is readable. + */ string tableCommands = @"CREATE TABLE IF NOT EXISTS [transactions] ( [ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, @@ -79,17 +92,21 @@ namespace Ophelias.Managers internal void InitializeRoomsTable() { + /* + * This is a one off initialization function to intialize + * a list of 45 rooms. The design specification called + * for a static 45 rooms that were not configurable hence + * it being hardcoded here for now. Ideally this is moved + * out to config file. + */ using SQLiteCommand cmd = con.CreateCommand(); - for (int i = 1; i < 46; i++) + for (int i = 0; i < 45; i++) { cmd.CommandText = $"INSERT INTO ROOMS (Occupied) VALUES (0);"; cmd.ExecuteNonQuery(); } - - // Initialize Rooms - } - public void Dispose() + public void Dispose() // Needed to support "using" { Close(); } @@ -97,12 +114,20 @@ namespace Ophelias.Managers internal static class QueryBuilder { - + /* + * This class is for building update query strings. To prevent SQL injection + * on dynamically built strings, the inputs are now used as null check values, + * rather than as null check values that get set if they exist. Parameters are + * now used in their place which prevent SQL injection. + */ internal static string? UpdateTransaction(int Id, double? Rate = null, double? Owed = null, double? Penalty = null, double? Multiplier = null, double? Refund = null, DateTime? PayBy = null, DateTime? LastPaid = null, DateTime? PaidOn = null, double? AmountPaid = null) { + /* + * Builds an update query string for the transaction model + */ List queryComponents = new(); string query = "UPDATE transactions SET"; @@ -151,7 +176,7 @@ namespace Ophelias.Managers queryComponents.Add($"PaidOn = @PaidOn"); } - if (queryComponents.Count > 0) + if (queryComponents.Count >= 0) { query += " " + string.Join(", ", queryComponents) + " " + $"WHERE ID = @ID;"; } @@ -168,6 +193,9 @@ namespace Ophelias.Managers ReservationStatus? Status = null, DateTime? CreationDate = null, DateTime? StartDate = null, DateTime? EndDate = null, DateTime? CheckIn = null, DateTime? CheckOut = null, DateTime? DateChanged = null) { + /* + * Builds an update query string for the reservation model + */ List QueryParts = new(); string query = "UPDATE reservations SET"; @@ -231,7 +259,7 @@ namespace Ophelias.Managers QueryParts.Add($"DateChanged = @DateChanged"); } - if (QueryParts.Count > 0) + if (QueryParts.Count >= 0) { query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = @ID;"; } @@ -245,6 +273,9 @@ namespace Ophelias.Managers internal static string? UpdateGuest(int Id, string? FirstName = null, string? LastName = null, string? Email = null, string? CreditCard = null, string? Expiration = null, string? CCV = null) { + /* + * Builds an update query string for the guest model + */ List QueryParts = new(); string query = "UPDATE guests SET"; @@ -278,7 +309,7 @@ namespace Ophelias.Managers QueryParts.Add($"CCV = @CCV"); } - if (QueryParts.Count > 0) + if (QueryParts.Count >= 0) { query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = @ID;"; } diff --git a/OpheliasOasis/Managers/Hotel.cs b/OpheliasOasis/Managers/Hotel.cs index 795420c..9446d51 100644 --- a/OpheliasOasis/Managers/Hotel.cs +++ b/OpheliasOasis/Managers/Hotel.cs @@ -5,34 +5,26 @@ namespace Ophelias.Managers { internal static class Hotel { - internal static int GetLastId(string tableName) - { - int LastId = 0; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = $"SELECT SEQ FROM sqlite_sequence WHERE name=@Table;"; - cmd.ExecuteNonQuery(); - cmd.Parameters.AddWithValue("@Table", tableName); - using SQLiteDataReader reader = cmd.ExecuteReader(); - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - LastId = reader.GetInt32(0); - } - } - } - return LastId; - } + /* + * This class is a large collection of SQL queries that did not belong + * on a specific model. As a result they are viewed as functions of the "Hotel". + * For details on what each does, see their docstrings. All queries here are + * written using parameters to avoid SQL injection. + */ internal static (List<(DateTime, int, string, string)>, List) GetDailyOccupancy() { + /* + * This function gets the daily occupancies by joining the reservation table + * with the guests and rooms table. It looks for where the current date is between + * any active/ changed reservation's, that has been checked in, start and end date. + * The query is then put in ascending order, or, organized by room number. + */ List previousOccupancies = new(); List<(DateTime, int, string, string)> currentOccupancies = new(); - using (Database Manager = new()) + using (Database Manager = new()) // Open a new database connection { - using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); // Being a new SQL transaction + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT RoomNum, Lname, Fname, EndDate FROM reservations " + "INNER JOIN guests ON reservations.GuestID = guests.ID " + @@ -42,15 +34,13 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + while (reader.Read()) { - while (reader.Read()) - { - currentOccupancies.Add((reader.GetDateTime(3), reader.GetInt32(0), reader.GetString(1), reader.GetString(2))); - } + currentOccupancies.Add((reader.GetDateTime(3), reader.GetInt32(0), reader.GetString(1), reader.GetString(2))); } } - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT RoomNum FROM reservations " + "INNER JOIN guests ON reservations.GuestID = guests.ID " + @@ -58,89 +48,125 @@ namespace Ophelias.Managers "WHERE DATE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status IN (@Status1);"; cmd.Parameters.AddWithValue("@Date", DateTime.Now.AddDays(-1).Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Ended); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + while (reader.Read()) { - while (reader.Read()) + foreach (int item in reader.GetValues()) { - foreach (int item in reader.GetValues()) - { - previousOccupancies.Add(item); - } + previousOccupancies.Add(item); } } } - Transaction.Commit(); + Transaction.Commit(); // Commit the transaction changes if any made } return (currentOccupancies, previousOccupancies); } internal static List GetDailyArrivals() { + /* + * This function queries for daily arrivals. A daily arrival is considered to be someone + * that arrives on the current date and has an active or changed reservation status. + * Using these conditions, the transaction and guest table are joined and then the + * data is ordered by the guests last name in ascending order. + */ List reservations = new(); - using (Database Manager = new()) + using (Database Manager = new()) // Open a new database connection { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command to execute + cmd.CommandText = "SELECT * FROM reservations " + + "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + + "INNER JOIN guests ON reservations.GuestID = guests.ID " + + "WHERE DATE (@Date) = StartDate AND Status IN (@Status1,@Status2)" + + "ORDER BY Lname ASC;"; + cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); + cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + while (reader.Read()) { - cmd.CommandText = "SELECT * FROM reservations " + - "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + - "INNER JOIN guests ON reservations.GuestID = guests.ID " + - "WHERE DATE (@Date) = StartDate AND Status IN (@Status1,@Status2)" + - "ORDER BY Lname ASC;"; - cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); - cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + /* + * Since we cannot get by string keys, each column is represented by a numerical value + * like an array. The column is as follows: + * + * 0. ID (reservations.ID) + * 1. RoomNum + * 2. GuestID + * 3. TransactionID + * 4. IsNoShow + * 5. Type + * 6. Status + * 7. CreationDate + * 8. StartDate + * 9. EndDate + * 10. CheckIn + * 11. CheckOut + * 12. DateChanged + * 13. ID (rooms.ID) + * 14. Rate + * 15. Owed + * 16. Penalty + * 17. Multiplier + * 18. RefundAmount + * 19. AmountPaid + * 20. PayBy + * 21. LastPaid + * 22. PaidOn + * 23. ID (guests.ID) + * 24. Fname + * 25. Lname + * 26. Email + * 27. CreditCard + * 28. Expiration + * 29. CCV + * + */ + Reservation? r; + Transaction? t; + Guest g; + int? RoomNumber = null; + DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; + + if (reader.HasRows && !reader.IsDBNull(0)) { - while (reader.Read()) + if (reader[21].GetType() != typeof(DBNull)) { - Reservation? r; - Transaction? t; - Guest g; - int? RoomNumber = null; - DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; - - if (reader.HasRows && !reader.IsDBNull(0)) - { - if (reader[21].GetType() != typeof(DBNull)) - { - LastPaid = reader.GetDateTime(21); - } - - if (reader[22].GetType() != typeof(DBNull)) - { - PaidOn = reader.GetDateTime(22); - } - - t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), - LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); - - g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); - - if (reader[1].GetType() != typeof(DBNull)) - { - RoomNumber = reader.GetInt32(1); - } - - if (reader[10].GetType() != typeof(DBNull)) - { - CheckIn = reader.GetDateTime(10); - } - - if (reader[11].GetType() != typeof(DBNull)) - { - CheckOut = reader.GetDateTime(11); - } - - if (reader[12].GetType() != typeof(DBNull)) - { - DateChanged = reader.GetDateTime(12); - } - - r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), - reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), - CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); - reservations.Add(r); - } + LastPaid = reader.GetDateTime(21); } + + if (reader[22].GetType() != typeof(DBNull)) + { + PaidOn = reader.GetDateTime(22); + } + + t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), // Creates a new transaction implicitly + LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); + + g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); // Creates a new guest implicitly + + if (reader[1].GetType() != typeof(DBNull)) + { + RoomNumber = reader.GetInt32(1); + } + + if (reader[10].GetType() != typeof(DBNull)) + { + CheckIn = reader.GetDateTime(10); + } + + if (reader[11].GetType() != typeof(DBNull)) + { + CheckOut = reader.GetDateTime(11); + } + + if (reader[12].GetType() != typeof(DBNull)) + { + DateChanged = reader.GetDateTime(12); + } + + r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), // Creates a new reservation implicitly + reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), + CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); + reservations.Add(r); } } } @@ -148,16 +174,21 @@ namespace Ophelias.Managers } internal static (double, double, List<(DateTime, double)>) GetIncentiveTransactions() { + /* + * Creates a list of incentive transactions based on a query that gets reservations, and their associated transactions, + * if they match the Incentive type and are active and changed. The matching dates and rates and amount owed are caluclated + * to produce a new column losses. The losses are then added to a list, averaged, and totalled. These are returned. + */ double losses; double totalLosses = 0; int days = 30; DateTime dt = DateTime.Now; List<(DateTime, double)> dailyLosses = new(); - using (Database Manager = new()) + using (Database Manager = new()) // Create a new database connection { for (int i = 0; i < days; i++) { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT sum(((julianday(EndDate) - julianday(StartDate)) * transactions.Rate) - transactions.Owed) AS Loss " + "FROM reservations " + @@ -167,18 +198,16 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); cmd.Parameters.AddWithValue("@Type", (int)ReservationType.Incentive); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - totalLosses += reader.GetDouble(0); - losses = reader.GetDouble(0); - } - else - { - losses = 0; - } + totalLosses += reader.GetDouble(0); + losses = reader.GetDouble(0); + } + else // No losses found + { + losses = 0; } } dailyLosses.Add((dt.AddDays(i), losses)); @@ -188,16 +217,22 @@ namespace Ophelias.Managers } internal static (List<(DateTime, double)>, double, double) GetExpectedIncomeCount() { + /* + * This query gets the the expected income for the next thirty days. The query looks + * for and sums transactions that cover the specified date range and are active or + * changed in status. The expected income is averaged as well and returned as daily + * values. + */ double totalIncome = 0; double income; int days = 30; DateTime dt = DateTime.Now; List<(DateTime, double)> dailyIncomes = new(); - using (Database Manager = new()) + using (Database Manager = new()) // Create a new database connection { for (int i = 0; i < days; i++) { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT sum(transactions.Owed) FROM reservations " + "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + @@ -206,18 +241,16 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - totalIncome += reader.GetDouble(0); - income = reader.GetDouble(0); - } - else - { - income = 0; - } + totalIncome += reader.GetDouble(0); + income = reader.GetDouble(0); + } + else + { + income = 0; } } dailyIncomes.Add((dt.AddDays(i), income)); @@ -227,18 +260,23 @@ namespace Ophelias.Managers } internal static (double, List<(DateTime, int, int, int, int, int)>) GetExpectedOccupancyCount() { + /* + * This function gets the expected occupancy for the next thirty days. The query looks for + * active or changed reservations based on the current date between matching reservations + * start and end dates. It also runs the query for all and each reservation respectively. + */ double thirtyDayOcc = 0; int days = 30; DateTime dt = DateTime.Now; List<(DateTime, int, int, int, int, int)> occData = new(); - using (Database Manager = new()) + using (Database Manager = new()) // Create a new database connection { for (int i = 0; i < days; i++) { int rooms, conventional, prepaid, sixty, incentive; - using (SQLiteTransaction Transaction = Manager.con.BeginTransaction()) + using (SQLiteTransaction Transaction = Manager.con.BeginTransaction()) // Create a new transaction { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT COUNT(*) " + "FROM reservations " + @@ -247,109 +285,115 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - thirtyDayOcc += reader.GetInt32(0); - rooms = reader.GetInt32(0); - } - else - { - rooms = 0; - } + thirtyDayOcc += reader.GetInt32(0); + rooms = reader.GetInt32(0); + } + else + { + rooms = 0; + } + } + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute + { + /* + * Get total conventional count + */ + cmd.CommandText = "SELECT COUNT(*) " + + "FROM reservations " + + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = @Type;"; + cmd.Parameters.AddWithValue("@Date", dt.AddDays(i).Date.ToString("yyyy-MM-dd")); + cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Type", (int)ReservationType.Conventional); + + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new reader to read the SQL data + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) + { + conventional = reader.GetInt32(0); + } + else + { + conventional = 0; } } using (SQLiteCommand cmd = Manager.con.CreateCommand()) { + /* + * Get total prepaid count + */ cmd.CommandText = "SELECT COUNT(*) " + "FROM reservations " + - "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = 0;"; + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = @Type;"; cmd.Parameters.AddWithValue("@Date", dt.AddDays(i).Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Type", (int)ReservationType.Prepaid); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - conventional = reader.GetInt32(0); - } - else - { - conventional = 0; - } + prepaid = reader.GetInt32(0); + } + else + { + prepaid = 0; } } using (SQLiteCommand cmd = Manager.con.CreateCommand()) { + /* + * Get total 60-day-in-advance count + */ cmd.CommandText = "SELECT COUNT(*) " + "FROM reservations " + - "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = 1;"; + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = @Type;"; cmd.Parameters.AddWithValue("@Date", dt.AddDays(i).Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Type", (int)ReservationType.SixtyDayAdvance); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - prepaid = reader.GetInt32(0); - } - else - { - prepaid = 0; - } + sixty = reader.GetInt32(0); + } + else + { + sixty = 0; } } using (SQLiteCommand cmd = Manager.con.CreateCommand()) { + /* + * Get total incentive count + */ cmd.CommandText = "SELECT COUNT(*) " + "FROM reservations " + - "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = 2;"; + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = @Type;"; cmd.Parameters.AddWithValue("@Date", dt.AddDays(i).Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Type", (int)ReservationType.Incentive); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); + reader.Read(); + if (reader.HasRows && !reader.IsDBNull(0)) { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - sixty = reader.GetInt32(0); - } - else - { - sixty = 0; - } + incentive = reader.GetInt32(0); + } + else + { + incentive = 0; } } - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "SELECT COUNT(*) " + - "FROM reservations " + - "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2) AND Type = 3;"; - cmd.Parameters.AddWithValue("@Date", dt.AddDays(i).Date.ToString("yyyy-MM-dd")); - cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows && !reader.IsDBNull(0)) - { - incentive = reader.GetInt32(0); - } - else - { - incentive = 0; - } - } - } - Transaction.Commit(); + Transaction.Commit(); // Commit transaction to database } occData.Add((dt.AddDays(i), rooms, conventional, prepaid, sixty, incentive)); } @@ -358,14 +402,21 @@ namespace Ophelias.Managers } internal static (int, bool) AvgOccupancySpan(DateTime Start, DateTime End) { + /* + * This query gets the average occupancy and also reports if there was a + * max capacity found within those 30 days. The count is averaged and returned + * with the boolean value once all reservation queries are checked to match + * the critiera. The search criteria is any active/ changed date where the + * date specified is between the start and end date of any matching reservation. + */ int thirtyDayOcc = 0; bool maxCapacityInRange = false; int days = (int)(End.Date - Start.Date).TotalDays; - using (Database Manager = new()) + using (Database Manager = new()) // Create a new database connection { for (int i = 0; i < days; i++) { - using SQLiteCommand cmd = Manager.con.CreateCommand(); + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command cmd.CommandText = $@"SELECT COUNT(*) FROM reservations WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2);"; @@ -373,7 +424,7 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using SQLiteDataReader reader = cmd.ExecuteReader(); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new SQL data reader reader.Read(); if (reader.HasRows && !reader.IsDBNull(0)) { @@ -389,13 +440,18 @@ namespace Ophelias.Managers } internal static Guest? GetGuestByEmail(string Email) { + /* + * This function searches the database for a guest based off + * the specified email. If a match is found the guest model is created and returned. + * If no guest details are found, a null object is returned. + */ Guest? g = null; - using (Database Manager = new()) + using (Database Manager = new()) // Creates a new database connection { - using SQLiteCommand cmd = Manager.con.CreateCommand(); + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new database command cmd.CommandText = $"SELECT * FROM guests WHERE Email = @Email"; cmd.Parameters.AddWithValue("@Email", Email); - using SQLiteDataReader reader = cmd.ExecuteReader(); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader reader.Read(); if (reader.HasRows) { @@ -415,33 +471,611 @@ namespace Ophelias.Managers CCV = reader[6].ToString(); } - g = new(reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), CreditCard, Expiration, CCV); + g = new(reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), CreditCard, Expiration, CCV); // Create a guest model } } return g; } internal static Reservation? GetResByGuest(Guest g) { + /* + * This query gets a reservation by taking an existing guest as the "where" comparison. + * Since the guest model is already passed into this query, there is no need to join the + * guests table. Instead only the transaction and reservation details need to be populated + * and then the guest object is passed into the creation of a reservation object. + */ Reservation? r = null; Transaction? t; - using (Database Manager = new()) + using (Database Manager = new()) // Opens a database connection { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - int? RoomNumber = null; - DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new command + int? RoomNumber = null; + DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; - cmd.CommandText = @"SELECT * FROM reservations + cmd.CommandText = @"SELECT * FROM reservations INNER JOIN transactions ON reservations.TransactionID = transactions.ID - WHERE GuestID = @GuestID AND Status = @Status;"; - cmd.Parameters.AddWithValue("@GuestID", g.Id); - cmd.Parameters.AddWithValue("@Status", (int)ReservationStatus.Active); + WHERE GuestID = @GuestID AND Status IN (@Status1,@Status2);"; + cmd.Parameters.AddWithValue("@GuestID", g.Id); + cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + if (reader[21].GetType() != typeof(DBNull)) { + LastPaid = reader.GetDateTime(21); + } + + if (reader[22].GetType() != typeof(DBNull)) + { + PaidOn = reader.GetDateTime(22); + } + + t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), // Creates a new transaction model + LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); + + if (reader[1].GetType() != typeof(DBNull)) + { + RoomNumber = reader.GetInt32(1); + } + + if (reader[10].GetType() != typeof(DBNull)) + { + CheckIn = reader.GetDateTime(10); + } + + if (reader[11].GetType() != typeof(DBNull)) + { + CheckOut = reader.GetDateTime(11); + } + + if (reader[12].GetType() != typeof(DBNull)) + { + DateChanged = reader.GetDateTime(12); + } + + r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), // Creates a new reservation model + reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), + CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); + } + } + return r; + } + internal static TimeRefs? CanBeCheckedIn(string Email) + { + /* + * This query checks to see if a guest can be checked in. Guest are only + * permitted to check in on the day that their reservation is due to start. + * If they check in to early, the appropriate TimeRef enum is returned to. + */ + TimeRefs? status = null; + using (Database Manager = new()) // Opens a database connection + { + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new command + cmd.CommandText = $"SELECT * FROM reservation WHERE Email = @Email AND Status IN (@Status1,@Status2)"; + cmd.Parameters.AddWithValue("@Email", Email); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + DateTime dt = reader.GetDateTime(8); + if (dt.Date == DateTime.Now.Date) + { + status = TimeRefs.OnTime; + } + else if (dt.Date > DateTime.Now.Date) + { + status = TimeRefs.Late; + } + else if (dt.Date < DateTime.Now.Date) + { + status = TimeRefs.Early; + } + } + else + { + status = null; + } + } + return status; + } + internal static double? GetBaseRate() + { + /* + * This function is used to get the current base rate that is configured + * to be the default. + */ + double? rate; + using (Database Manager = new()) // Opens a database connection + { + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new command + string query = "SELECT Rate FROM rates WHERE DefaultRate = 1;"; + cmd.CommandText = query; + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + rate = reader.GetDouble(0); + } + else + { + rate = null; + } + } + return rate; + } + internal static bool GetBaseRateByDate(DateTime dt) + { + /* + * Gets the base rate by a specific date. This is typically used to check + * if there is a conflicting rate for a given day and return if a rate has + * been configured for that date. + */ + bool configured; + using (Database Manager = new()) // Opens a database connection + { + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new command + string query = "SELECT Rate FROM rates WHERE DateSet = @Date;"; + cmd.CommandText = query; + cmd.Parameters.AddWithValue("@Date", dt.ToString("yyyy-MM-dd")); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + configured = true; + } + else + { + configured = false; + } + } + return configured; + } + internal static void SetBaseRate(double Rate, DateTime DateSet, bool? DefaultRate = null) + { + + /* + * This function is used to create a new base rate in the database and have the option to make it + * the default base rate. + */ + using Database Manager = new(); // Create a new database connection + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command + string query = $"INSERT INTO rates (Rate, DateSet, DefaultRate) VALUES (@Rate, @DateSet, @DefaultRate);"; + cmd.CommandText = query; + cmd.Parameters.AddWithValue("@Rate", Rate); + cmd.Parameters.AddWithValue("@DateSet", DateSet.ToString("yyyy-MM-dd")); + if (DefaultRate != null) + { + if (DefaultRate.Value == true) + { + cmd.Parameters.AddWithValue("@DefaultRate", Convert.ToInt32(DefaultRate)); + } + else + { + cmd.Parameters.AddWithValue("@DefaultRate", null); + } + } + else + { + cmd.Parameters.AddWithValue("@DefaultRate", null); + } + cmd.ExecuteNonQuery(); + } + internal static void UpdateBaseRate(double Rate, DateTime DateSet, bool? DefaultRate = null) + { + /* + * This command is used to update the base rate for an existing base rate. + * It will replace the value of any rate, including the default rate if specified. + */ + using Database Manager = new(); // Create a new database connection + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command + if (DefaultRate != null) + { + if (DefaultRate.Value == true) + { + cmd.CommandText = $"UPDATE rates SET rate = @Rate, defaultrate = @DefaultRate WHERE DateSet = @Date;"; + cmd.Parameters.AddWithValue("@DefaultRate", Convert.ToInt32(DefaultRate)); // Replaces existing rate + } + else + { + cmd.CommandText = $"UPDATE rates SET rate = @Rate WHERE DateSet = @Date;"; + } + } + else + { + cmd.CommandText = $"UPDATE rates SET rate = @Rate WHERE DateSet = @Date;"; + } + cmd.Parameters.AddWithValue("@Rate", Rate); + cmd.Parameters.AddWithValue("@Date", DateSet.ToString("yyyy-MM-dd")); + cmd.ExecuteNonQuery(); + } + internal static bool CheckBaseRate() + { + /* + * This function is used to check and update if there is a new base rate. + * It will check to see if there is a base rate set for the current day + * and if that is the case it will set that as the new DefaultRate to use, 1, + * while setting the other one to 0. + */ + int? OldId = null; + bool Success = true; + using (Database Manager = new()) // Open a new database connection + { + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command + { + /* + * This query checks for the existing default base rate, if it does not find a rate + * and returns null, then the function will report a boolean value of false. + */ + string query = "SELECT Id FROM rates WHERE DefaultRate = 1;"; + cmd.CommandText = query; + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + OldId = reader.GetInt32(0); + } + } + int? Id; + if (OldId != null) + { + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command + { + /* + * This query selects a base rate configured to the current date + * if it is set. If there is an Id, it will be used in the + * next query for swapping. No swap occurs if it does not exist. + */ + string query = "SELECT Id FROM rates WHERE DateSet = @Date;"; + cmd.CommandText = query; + cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new SQL data reader reader.Read(); if (reader.HasRows) { + Id = reader.GetInt32(0); + } + else + { + Id = null; + } + } + if (Id != null) + { + /* + * This query swaps the default indicator of the old base rate with the new base + * rate. + */ + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command + cmd.CommandText = @"UPDATE rates SET DefaultRate = NULL WHERE Id = @OldID; + UPDATE rates SET DefaultRate = 1 WHERE Id = @ID;"; + cmd.Parameters.AddWithValue("@OldID", OldId); + cmd.Parameters.AddWithValue("@ID", Id); + cmd.ExecuteNonQuery(); + } + } + else + { + Success = false; + } + } + return Success; + } + internal static int? CheckInGuest(string Email, DateTime CheckIn) + { + /* + * This query is used to check in a guest and is a multi-part query + * with several updates to the database. Here the database tables + * reservation and room will be updated based on whichever + * room is available, not occupied, and then assigned to the reservation. + * Once this is complete, the updated information is saved to the database + * and the room number is returned. + */ + int? RoomID = null; + using (Database Manager = new()) // Opens a new database connection + { + using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); // Begin a new SQL transaction + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Creates a new command + { + /* + * This query looks at the rooms table for the first instance of an unoccupied + * room. This room is then set as the reservations associated room. + */ + cmd.CommandText = "UPDATE reservations SET RoomNum = (SELECT ID FROM rooms WHERE Occupied = 0 LIMIT 1), CheckIn = @Date " + + "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email) AND RoomNum IS NULL AND Status in (@SActive,@SChanged);"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Date", CheckIn.ToString("yyyy-MM-dd")); + cmd.ExecuteNonQuery(); + } + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Creates a new command + { + /* + * This query sets the room that is assigned to the reservation as + * occupied as a guest has been assigned it. + */ + cmd.CommandText = "UPDATE rooms SET Occupied = 1 " + + "WHERE ID = (SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged))));"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + cmd.ExecuteNonQuery(); + } + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Creates a new command + { + /* + * This query looks for the room number that was assigned as a result of the queries above. + * This room number is then stored so it can be returned. + */ + cmd.CommandText = "SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged))"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL reader + reader.Read(); + if (reader.HasRows) + { + RoomID = (int)reader.GetValue(0); + } + } + Transaction.Commit(); // Commits the transaction + } + return RoomID; + } + internal static bool GuestCurrentlyCheckedIn(string Email) + { + /* + * Checks if a guest is currently checked in. If there is a check-in date + * on their reservation, that means thay have been checked in. A boolean + * value is returned whether or not a matching reservation is found. + */ + bool EntryFound; + using (Database Manager = new()) // Creates a new database connection + { + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Creates a new command + cmd.CommandText = "SELECT * FROM reservations " + + "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged)) AND CheckIn IS NOT NULL"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Creates a new SQL data reader + reader.Read(); + if (reader.HasRows) + { + EntryFound = true; + } + else + { + EntryFound = false; + } + } + return EntryFound; + } + internal static void CheckOutGuest(string Email, DateTime CheckOut) + { + /* + * This function checks a guest out given that they have already checked in + * and have an active reservation. If this criteria is not met, a reservation + * will not be checked out. If a reservation does match this criteria, the reservation + * check-out date is set and the status of the reservation is changed to Ended. + */ + using Database Manager = new(); // Creates a new database connection + using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); // Creates a new SQL data reader + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Creates a new command + { + /* + * This query looks for the room that is checked in and marks them as unoccupied + * based on the corresponding room to reservation relation. + */ + cmd.CommandText = "UPDATE rooms SET Occupied = 0 " + + "WHERE ID = (SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email) AND Status in (@SActive,@SChanged) AND CheckIn IS NOT NULL);"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + cmd.ExecuteNonQuery(); + } + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Creates a new command + { + /* + * This query updates the reservation status as Ended adn the checkout date. + */ + cmd.CommandText = "UPDATE reservations SET CheckOut = @Date, Status = @Status " + + "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email) AND RoomNum IS NOT NULL AND Status in (@SActive,@SChanged) AND CheckIn IS NOT NULL;"; + cmd.Parameters.AddWithValue("@Email", Email); + cmd.Parameters.AddWithValue("@Status", (int)ReservationStatus.Ended); + cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); + cmd.Parameters.AddWithValue("@Date", CheckOut.ToString("yyyy-MM-dd")); + cmd.ExecuteNonQuery(); + } + Transaction.Commit(); + return; + } + internal static List GetActiveSixtyDayRes() + { + /* + * This query is used to build a list of active 60-day-in-advance reservations + * and return them all as a list. + */ + List list = new(); + using (Database Manager = new()) // Create a new database connection + { + using SQLiteCommand cmd = Manager.con.CreateCommand(); // Create a new command + /* + * This query will find any reservation with the 60-day type that is Active or Changed + * and return them all in a table + */ + cmd.CommandText = "SELECT * FROM reservations " + + "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + + "INNER JOIN guests ON reservations.GuestID = guests.ID " + + "WHERE Type = @Type AND Status IN (@Status1,@Status2);"; + cmd.Parameters.AddWithValue("@Type", (int)ReservationType.SixtyDayAdvance); + cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new SQL data reader + while (reader.Read()) + { + Reservation? r; + Transaction? t; + Guest g; + int? RoomNumber = null; + DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; + if (reader.HasRows) + { + /* + * Since we cannot get by string keys, each column is represented by a numerical value + * like an array. The column is as follows: + * + * 0. ID (reservations.ID) + * 1. RoomNum + * 2. GuestID + * 3. TransactionID + * 4. IsNoShow + * 5. Type + * 6. Status + * 7. CreationDate + * 8. StartDate + * 9. EndDate + * 10. CheckIn + * 11. CheckOut + * 12. DateChanged + * 13. ID (rooms.ID) + * 14. Rate + * 15. Owed + * 16. Penalty + * 17. Multiplier + * 18. RefundAmount + * 19. AmountPaid + * 20. PayBy + * 21. LastPaid + * 22. PaidOn + * 23. ID (guests.ID) + * 24. Fname + * 25. Lname + * 26. Email + * 27. CreditCard + * 28. Expiration + * 29. CCV + * + */ + if (reader[21].GetType() != typeof(DBNull)) + { + LastPaid = reader.GetDateTime(21); + } + + if (reader[22].GetType() != typeof(DBNull)) + { + PaidOn = reader.GetDateTime(22); + } + + t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), // Creates a new transaction model + LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); + + g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); // Creates a new guest model + + if (reader[1].GetType() != typeof(DBNull)) + { + RoomNumber = reader.GetInt32(1); + } + + if (reader[10].GetType() != typeof(DBNull)) + { + CheckIn = reader.GetDateTime(10); + } + + if (reader[11].GetType() != typeof(DBNull)) + { + CheckOut = reader.GetDateTime(11); + } + + if (reader[12].GetType() != typeof(DBNull)) + { + DateChanged = reader.GetDateTime(12); + } + + r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), + reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), + CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); + list.Add(r); + } + } + } + return list; + } + internal static List GetPastDueReservations() + { + /* + * This function gets any reservation that is past due. + * Past due is considered to be any reservation where the start date is + * older than the current date, the reservation is active or changed, and + * has not been checked in. This list is then returned. + */ + List list = new(); + using (Database Manager = new()) // Create a new database connection + { + using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); // Create a new SQL transaction + using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command + { + /* + * This query selects non-checked in active and changed reeservations where + * the start date is older than the current day. These are considered no-shows. + */ + cmd.CommandText = "SELECT * FROM reservations " + + "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + + "INNER JOIN guests ON reservations.GuestID = guests.ID " + + "WHERE DATE(@Date) > StartDate AND Status IN (@Status1,@Status2) AND CheckIn IS NULL;"; + cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); + cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); + cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); + using SQLiteDataReader reader = cmd.ExecuteReader(); // Create a new SQL data reader + while (reader.Read()) + { + Reservation? r; + Transaction? t; + Guest g; + int? RoomNumber = null; + DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; + if (reader.HasRows) + { + /* + * Since we cannot get by string keys, each column is represented by a numerical value + * like an array. The column is as follows: + * + * 0. ID (reservations.ID) + * 1. RoomNum + * 2. GuestID + * 3. TransactionID + * 4. IsNoShow + * 5. Type + * 6. Status + * 7. CreationDate + * 8. StartDate + * 9. EndDate + * 10. CheckIn + * 11. CheckOut + * 12. DateChanged + * 13. ID (rooms.ID) + * 14. Rate + * 15. Owed + * 16. Penalty + * 17. Multiplier + * 18. RefundAmount + * 19. AmountPaid + * 20. PayBy + * 21. LastPaid + * 22. PaidOn + * 23. ID (guests.ID) + * 24. Fname + * 25. Lname + * 26. Email + * 27. CreditCard + * 28. Expiration + * 29. CCV + * + */ if (reader[21].GetType() != typeof(DBNull)) { LastPaid = reader.GetDateTime(21); @@ -452,9 +1086,11 @@ namespace Ophelias.Managers PaidOn = reader.GetDateTime(22); } - t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), + t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), // Create a new transaction model LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); + g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); // Create a new guest model + if (reader[1].GetType() != typeof(DBNull)) { RoomNumber = reader.GetInt32(1); @@ -475,471 +1111,20 @@ namespace Ophelias.Managers DateChanged = reader.GetDateTime(12); } - r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), + r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), // Create a new reservation model reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); - } - } - } - } - return r; - } - internal static TimeRefs? CanBeCheckedIn(string Email) - { - TimeRefs? status = null; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = $"SELECT * FROM reservation WHERE Email = @Email AND Status IN (0,1)"; - cmd.Parameters.AddWithValue("@Email", Email); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - DateTime dt = reader.GetDateTime(8); - if (dt.Date == DateTime.Now.Date) - { - status = TimeRefs.OnTime; - } - else if (dt.Date > DateTime.Now.Date) - { - status = TimeRefs.Late; - } - else if (dt.Date < DateTime.Now.Date) - { - status = TimeRefs.Early; - } - } - else - { - status = null; - } - } - } - } - return status; - } - internal static double? GetBaseRate() - { - double? rate; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - string query = "SELECT Rate FROM rates WHERE DefaultRate = 1;"; - cmd.CommandText = query; - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - rate = reader.GetDouble(0); - } - else - { - rate = null; - } - } - } - } - return rate; - } - internal static bool GetBaseRateByDate(DateTime dt) - { - bool configured; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - string query = "SELECT Rate FROM rates WHERE DateSet = @Date;"; - cmd.CommandText = query; - cmd.Parameters.AddWithValue("@Date", dt.ToString("yyyy-MM-dd")); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - configured = true; - } - else - { - configured = false; - } - } - } - } - return configured; - } - internal static void SetBaseRate(double Rate, DateTime DateSet, bool? DefaultRate = null) - { - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - string query = $"INSERT INTO rates (Rate, DateSet, DefaultRate) VALUES (@Rate, @DateSet, @DefaultRate);"; - cmd.CommandText = query; - cmd.Parameters.AddWithValue("@Rate", Rate); - cmd.Parameters.AddWithValue("@DateSet", DateSet.ToString("yyyy-MM-dd")); - if (DefaultRate != null) - { - if (DefaultRate.Value == true) - { - cmd.Parameters.AddWithValue("@DefaultRate", Convert.ToInt32(DefaultRate)); - } - else - { - cmd.Parameters.AddWithValue("@DefaultRate", null); - } - } - else - { - cmd.Parameters.AddWithValue("@DefaultRate", null); - } - - cmd.ExecuteNonQuery(); - } - } - } - internal static void UpdateBaseRate(double Rate, DateTime DateSet, bool? DefaultRate = null) - { - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - if (DefaultRate != null) - { - if (DefaultRate.Value == true) - { - cmd.CommandText = $"UPDATE rates SET rate = @Rate, defaultrate = @DefaultRate WHERE DateSet = @Date;"; - cmd.Parameters.AddWithValue("@DefaultRate", Convert.ToInt32(DefaultRate)); - } - else - { - cmd.CommandText = $"UPDATE rates SET rate = @Rate WHERE DateSet = @Date;"; - } - } - else - { - cmd.CommandText = $"UPDATE rates SET rate = @Rate WHERE DateSet = @Date;"; - } - cmd.Parameters.AddWithValue("@Rate", Rate); - cmd.Parameters.AddWithValue("@Date", DateSet.ToString("yyyy-MM-dd")); - cmd.ExecuteNonQuery(); - } - } - } - internal static bool CheckBaseRate() - { - int? OldId = null; - bool Success = true; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - string query = "SELECT Id FROM rates WHERE DefaultRate = 1;"; - cmd.CommandText = query; - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - OldId = reader.GetInt32(0); - } - } - } - int? Id; - if (OldId != null) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - string query = "SELECT Id FROM rates WHERE DateSet = @Date;"; - cmd.CommandText = query; - cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - Id = reader.GetInt32(0); - } - else - { - Id = null; - } - } - } - if (Id != null) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = @"UPDATE rates SET DefaultRate = NULL WHERE Id = @OldID; - UPDATE rates SET DefaultRate = 1 WHERE Id = @ID"; - cmd.Parameters.AddWithValue("@OldID", OldId); - cmd.Parameters.AddWithValue("@ID", Id); - cmd.ExecuteNonQuery(); - } - } - } - else - { - Success = false; - } - } - return Success; - } - internal static int? CheckInGuest(string Email, DateTime CheckIn) - { - int? RoomID = null; - using (Database Manager = new()) - { - using (SQLiteTransaction Transaction = Manager.con.BeginTransaction()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "UPDATE reservations SET RoomNum = (SELECT ID FROM rooms WHERE Occupied = 0 LIMIT 1), CheckIn = @Date " + - "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email) AND RoomNum IS NULL AND Status in (@SActive,@SChanged);"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - cmd.Parameters.AddWithValue("@Date", CheckIn.ToString("yyyy-MM-dd")); - cmd.ExecuteNonQuery(); - } - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "UPDATE rooms SET Occupied = 1 " + - "WHERE ID = (SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged))));"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - cmd.ExecuteNonQuery(); - } - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged))"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - RoomID = (int)reader.GetValue(0); - } - } - } - Transaction.Commit(); - } - } - return RoomID; - } - internal static bool GuestCurrentlyCheckedIn(string Email) - { - bool EntryFound; - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "SELECT * FROM reservations " + - "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged)) AND CheckIn IS NOT NULL"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - reader.Read(); - if (reader.HasRows) - { - EntryFound = true; - } - else - { - EntryFound = false; - } - } - } - } - return EntryFound; - } - internal static void CheckOutGuest(string Email, DateTime CheckOut) - { - using (Database Manager = new()) - { - using (SQLiteTransaction Transaction = Manager.con.BeginTransaction()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "UPDATE rooms SET Occupied = 0 " + - "WHERE ID = (SELECT RoomNum FROM reservations WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email AND Status in (@SActive,@SChanged)));"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - cmd.ExecuteNonQuery(); - } - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "UPDATE reservations SET CheckOut = @Date, Status = @Status " + - "WHERE GuestID = (SELECT ID FROM guests WHERE Email = @Email) AND RoomNum IS NOT NULL AND Status in (@SActive,@SChanged) AND CheckIn IS NULL;"; - cmd.Parameters.AddWithValue("@Email", Email); - cmd.Parameters.AddWithValue("@Status", (int)ReservationStatus.Ended); - cmd.Parameters.AddWithValue("@SActive", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@SChanged", (int)ReservationStatus.Changed); - cmd.Parameters.AddWithValue("@Date", CheckOut.ToString("yyyy-MM-dd")); - cmd.ExecuteNonQuery(); - } - Transaction.Commit(); - } - } - return; - } - internal static List GetActiveSixtyDayRes() - { - List list = new(); - using (Database Manager = new()) - { - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "SELECT * FROM reservations " + - "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + - "INNER JOIN guests ON reservations.GuestID = guests.ID " + - "WHERE Type = @Type AND Status IN (@Status1,@Status2);"; - cmd.Parameters.AddWithValue("@Type", (int)ReservationType.SixtyDayAdvance); - cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - cmd.ExecuteNonQuery(); - - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - Reservation? r; - Transaction? t; - Guest g; - int? RoomNumber = null; - DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; - - if (reader.HasRows) - { - if (reader[21].GetType() != typeof(DBNull)) - { - LastPaid = reader.GetDateTime(21); - } - - if (reader[22].GetType() != typeof(DBNull)) - { - PaidOn = reader.GetDateTime(22); - } - - t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), - LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); - - g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); - - if (reader[1].GetType() != typeof(DBNull)) - { - RoomNumber = reader.GetInt32(1); - } - - if (reader[10].GetType() != typeof(DBNull)) - { - CheckIn = reader.GetDateTime(10); - } - - if (reader[11].GetType() != typeof(DBNull)) - { - CheckOut = reader.GetDateTime(11); - } - - if (reader[12].GetType() != typeof(DBNull)) - { - DateChanged = reader.GetDateTime(12); - } - - r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), - reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), - CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); - list.Add(r); - } - } - } - } - } - return list; - } - internal static List GetPastDueReservations() - { - List list = new(); - using (Database Manager = new()) - { - using SQLiteTransaction Transaction = Manager.con.BeginTransaction(); - using (SQLiteCommand cmd = Manager.con.CreateCommand()) - { - cmd.CommandText = "SELECT * FROM reservations " + - "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + - "INNER JOIN guests ON reservations.GuestID = guests.ID " + - "WHERE DATE(@Date) > StartDate AND Status IN (@Status1,@Status2) AND CheckIn IS NULL;"; - cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); - cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); - cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); - using (SQLiteDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - Reservation? r; - Transaction? t; - Guest g; - int? RoomNumber = null; - DateTime? CheckIn = null, CheckOut = null, DateChanged = null, LastPaid = null, PaidOn = null; - - if (reader.HasRows) - { - if (reader[21].GetType() != typeof(DBNull)) - { - LastPaid = reader.GetDateTime(21); - } - - if (reader[22].GetType() != typeof(DBNull)) - { - PaidOn = reader.GetDateTime(22); - } - - t = new(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(20), - LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16), AmountPaid: reader.GetDouble(19)); - - g = new(reader.GetInt32(23), reader.GetString(24), reader.GetString(25), reader.GetString(26), reader.GetString(27), reader.GetString(28), reader.GetString(29)); - - if (reader[1].GetType() != typeof(DBNull)) - { - RoomNumber = reader.GetInt32(1); - } - - if (reader[10].GetType() != typeof(DBNull)) - { - CheckIn = reader.GetDateTime(10); - } - - if (reader[11].GetType() != typeof(DBNull)) - { - CheckOut = reader.GetDateTime(11); - } - - if (reader[12].GetType() != typeof(DBNull)) - { - DateChanged = reader.GetDateTime(12); - } - - r = new(reader.GetInt32(0), g, t, (ReservationType)reader.GetInt32(5), (ReservationStatus)reader.GetInt32(6), - reader.GetDateTime(7), reader.GetDateTime(8), reader.GetDateTime(9), RoomNum: RoomNumber, IsNoShow: reader.GetBoolean(4), - CheckIn: CheckIn, CheckOut: CheckOut, DateChanged: DateChanged); - list.Add(r); - } + list.Add(r); } } } using (SQLiteCommand cmd = Manager.con.CreateCommand()) { + /* + * This query updates reservations where the same criteria above is matched and then + * no-show is set. At some point the order of these queries should be swapped as the first + * can be simplified if we update the table first. + */ cmd.CommandText = "UPDATE reservations SET IsNoShow = 1, DateChanged = @DateChanged " + "WHERE DATE (@Date) > StartDate AND Status IN (@Status1,@Status2) AND CheckIn IS NULL"; cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd")); @@ -948,12 +1133,12 @@ namespace Ophelias.Managers cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); cmd.ExecuteNonQuery(); } - Transaction.Commit(); + Transaction.Commit(); // Commits the transaction } return list; } } - internal enum TimeRefs + internal enum TimeRefs // Numerical values represented as words { OnTime, Early, diff --git a/OpheliasOasis/Program.cs b/OpheliasOasis/Program.cs index 5638d08..1ffda62 100644 --- a/OpheliasOasis/Program.cs +++ b/OpheliasOasis/Program.cs @@ -850,6 +850,7 @@ internal class Program * Q and q: * Returns to the main loop for choosing either the Guest or Admin flow. */ + Hotel.CheckBaseRate(); string? input = Console.ReadLine(); switch (input) { @@ -1309,6 +1310,7 @@ internal class Program * Q or Q: * Returns to the main loop for choosing either the Guest or Admin flow. */ + Hotel.CheckBaseRate(); string? input = Console.ReadLine(); switch (input) {