Files
ophelias-oasis/OpheliasOasis/Managers/Hotel.cs
雲華 0ce34d9d23 Fixed an incorrect calulation
The calculations used in the reports were calulating based off the
owed column which mathematically did not make sense since we are looking
for the daily.
2022-04-17 17:44:41 -04:00

1148 lines
59 KiB
C#

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<int>) 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<int> 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<Reservation> 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<Reservation> 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(transactions.Rate - (transactions.Rate * transactions.Multiplier)) 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.Rate * Multiplier) 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<Reservation> GetActiveSixtyDayRes()
{
/*
* This query is used to build a list of active 60-day-in-advance reservations
* and return them all as a list.
*/
List<Reservation> 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<Reservation> 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<Reservation> 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
}
}