using Ophelias.Models; using System.Data.SQLite; namespace Ophelias.Managers { internal static class Hotel { /* * 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()) // Open a new database connection { 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 " + "INNER JOIN rooms ON reservations.RoomNum = rooms.ID " + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status IN (@Status1,@Status2) AND CheckIn IS NOT NULL " + "ORDER BY RoomNum 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()) { currentOccupancies.Add((reader.GetDateTime(3), reader.GetInt32(0), reader.GetString(1), reader.GetString(2))); } } 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 " + "INNER JOIN rooms ON reservations.RoomNum = rooms.ID " + "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(); // Create a new reader to read the SQL data while (reader.Read()) { foreach (int item in reader.GetValues()) { previousOccupancies.Add(item); } } } 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()) // Open a new database connection { 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()) { /* * 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)) { 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 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); } } } return reservations; } 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()) // Create a new database connection { for (int i = 0; i < days; i++) { 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 " + "INNER JOIN transactions ON reservations.TransactionID = transactions.ID " + "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(); // Create a new reader to read the SQL data reader.Read(); if (reader.HasRows && !reader.IsDBNull(0)) { totalLosses += reader.GetDouble(0); losses = reader.GetDouble(0); } else // No losses found { losses = 0; } } dailyLosses.Add((dt.AddDays(i), losses)); } } return (totalLosses, totalLosses / 30, dailyLosses); } 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()) // Create a new database connection { for (int i = 0; i < days; i++) { 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 " + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status IN (@Status1,@Status2);"; 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(); // Create a new reader to read the SQL data reader.Read(); if (reader.HasRows && !reader.IsDBNull(0)) { totalIncome += reader.GetDouble(0); income = reader.GetDouble(0); } else { income = 0; } } dailyIncomes.Add((dt.AddDays(i), income)); } } return (dailyIncomes, totalIncome, totalIncome / 30); } 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()) // Create a new database connection { for (int i = 0; i < days; i++) { int rooms, conventional, prepaid, sixty, incentive; using (SQLiteTransaction Transaction = Manager.con.BeginTransaction()) // Create a new transaction { using (SQLiteCommand cmd = Manager.con.CreateCommand()) // Create a new command to execute { cmd.CommandText = "SELECT COUNT(*) " + "FROM reservations " + "WHERE (DATE(@Date) BETWEEN StartDate AND EndDate) AND Status in (@Status1,@Status2);"; 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(); // Create a new reader to read the SQL data reader.Read(); if (reader.HasRows && !reader.IsDBNull(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 = @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(); reader.Read(); if (reader.HasRows && !reader.IsDBNull(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 = @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(); reader.Read(); if (reader.HasRows && !reader.IsDBNull(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 = @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(); reader.Read(); if (reader.HasRows && !reader.IsDBNull(0)) { incentive = reader.GetInt32(0); } else { incentive = 0; } } Transaction.Commit(); // Commit transaction to database } occData.Add((dt.AddDays(i), rooms, conventional, prepaid, sixty, incentive)); } } return (thirtyDayOcc / days, occData); } 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()) // Create a new database connection { for (int i = 0; i < days; i++) { 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);"; cmd.Parameters.AddWithValue("@Date", Start.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(); // Create a new SQL data reader reader.Read(); if (reader.HasRows && !reader.IsDBNull(0)) { thirtyDayOcc += reader.GetInt32(0); if (reader.GetInt32(0) == 45) { maxCapacityInRange = true; } } } } return (thirtyDayOcc / days, maxCapacityInRange); } 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()) // Creates a new database connection { 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(); // Creates a new SQL data reader reader.Read(); if (reader.HasRows) { string? CreditCard = null, Expiration = null, CCV = null; if (reader[4].GetType() != typeof(DBNull)) { CreditCard = reader[4].ToString(); } if (reader[5].GetType() != typeof(DBNull)) { Expiration = reader[5].ToString(); } if (reader[6].GetType() != typeof(DBNull)) { CCV = reader[6].ToString(); } 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()) // Opens a database connection { 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 INNER JOIN transactions ON reservations.TransactionID = transactions.ID 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(); // 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); } 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), // 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); } 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), // 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); 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")); cmd.Parameters.AddWithValue("@DateChanged", DateTime.Now.Date.ToString("yyyy-MM-dd")); cmd.Parameters.AddWithValue("@Status1", (int)ReservationStatus.Active); cmd.Parameters.AddWithValue("@Status2", (int)ReservationStatus.Changed); cmd.ExecuteNonQuery(); } Transaction.Commit(); // Commits the transaction } return list; } } internal enum TimeRefs // Numerical values represented as words { OnTime, Early, Late } }