Finishes the guest part of the models and UI integration

This commit branch has been muddled with changes that are out of scope
and cover more than just the models. Specifically it includes
completion of basic guest related models, database access, command line interface
integration and preventing SQL injection.

Total summary of changes:

**IMPORTANT CHANGE: All queries are now parameterized to prevent SQL
injections. Prior versions are subject to it since the strings were
built.

- HotelManager
This class is used to provide database operations that are outside of
scope from a given model. Furthermore if there is no model to work off
of, such as creating a new model based off an existing entry, these
functions live outside of the model since it makes no sense for models
to contain functions where it would retrieve itself with no context.
Realistically this could be placed in an empty constructor and address
itself, however, for now this functionality has been moved off the model
since it is a one use and is not guaranteed to generate a guest. Some
functionality for the coming employee management functions are already
in there since they share overlap with the guest module. Specifically
GetBaseRate for checking and getting the base rate.

- DatabaseManager
This class has mostly migrated functionality out or switch to
parameterized query generation to prevent SQL injections.

- Guest, Reservation, and Transaction Classes
Migrated functionality to create the database entry and a new entry of
guest upon creation as well as update them.

- Program (Terminal app)
Fully integrated the guest module. Has not undergone extensive testing
but does work as of this commit.
This commit is contained in:
雲華
2022-04-15 02:29:55 -04:00
parent 8764c2e57c
commit 34bebca414
7 changed files with 621 additions and 296 deletions

View File

@@ -27,30 +27,30 @@ namespace Ophelias.Managers
[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[Rate] INTEGER NOT NULL,
[Owed] INTEGER NOT NULL,
[Penalty] INTEGER NULL,
[Penalty] INTEGER NOT NULL,
[Multiplier] INTEGER NOT NULL,
[RefundAmount] INTEGER NOT NULL,
[PayBy] TEXT NOT NULL,
[LastPaid] TEXXT NULL,
[LastPaid] TEXT NULL,
[PaidOn] TEXT NULL);
CREATE TABLE IF NOT EXISTS [reservations] (
[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[RoomID] INTEGER NOT NULL,
[GuestID] INTEGER NOT NULL,
[TransactionID] INTEGER NOT NULL,
[RoomNum] INTEGER NULL UNIQUE,
[GuestID] INTEGER NOT NULL UNIQUE,
[TransactionID] INTEGER NOT NULL UNIQUE,
[IsNoShow] BOOLEAN NOT NULL CHECK ([IsNoShow] IN (0,1)),
[Type] INTEGER NOT NULL CHECK ([Type] IN (0,1,2,3)),
[Status] INTEGER NOT NULL CHECK ([Status] IN (0,1,2,3)),
[CreationDate] TEXT NOT NULL,
[StartDate] TEXT NOT NULL,
[EndDate] TEXT NOT NULL,
[CheckIn] TEXT NOT NULL,
[CheckOut] TEXT NOT NULL,
[DateChanged] TEXT NOT NULL,
FOREIGN KEY ([RoomID]) REFERENCES ROOMS(ID),
FOREIGN KEY ([GuestID]) REFERENCES GUESTS(ID),
FOREIGN KEY ([TransactionID]) REFERENCES TRANSACTIONS(ID));
[CheckIn] TEXT NULL,
[CheckOut] TEXT NULL,
[DateChanged] TEXT NULL,
FOREIGN KEY ([RoomNum]) REFERENCES rooms(ID),
FOREIGN KEY ([GuestID]) REFERENCES guests(ID),
FOREIGN KEY ([TransactionID]) REFERENCES transactions(ID));
CREATE TABLE IF NOT EXISTS [guests] (
[ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -98,10 +98,6 @@ namespace Ophelias.Managers
}
}
internal static class DatabaseFunctions
{
}
internal static class QueryBuilder
{
@@ -114,24 +110,24 @@ namespace Ophelias.Managers
string query = "UPDATE transactions SET";
if (Rate.HasValue)
queryComponents.Add($"Rate = {Rate}");
queryComponents.Add($"Rate = @Rate");
if (Owed.HasValue)
queryComponents.Add($"Owed = {Owed}");
queryComponents.Add($"Owed = @Owed");
if (Penalty.HasValue)
queryComponents.Add($"Penalty = {Penalty}");
queryComponents.Add($"Penalty = @Penalty");
if (Multiplier.HasValue)
queryComponents.Add($"Multiplier = {Multiplier}");
queryComponents.Add($"Multiplier = @Multiplier");
if (Refund.HasValue)
queryComponents.Add($"Refund = {Refund}");
queryComponents.Add($"Refund = @Refund");
if (PayBy.HasValue)
queryComponents.Add($"PayBy = {PayBy.Value.Date.ToString("yyyy-MM-dd")}");
queryComponents.Add($"PayBy = @PayBy");
if (LastPaid.HasValue)
queryComponents.Add($"LastPaid = {LastPaid.Value.Date.ToString("yyyy-MM-dd")}");
queryComponents.Add($"LastPaid = @LastPaid");
if (PaidOn.HasValue)
queryComponents.Add($"PaidOn = {PaidOn.Value.Date.ToString("yyyy-MM-dd")}");
queryComponents.Add($"PaidOn = @PaidOn");
if (queryComponents.Count > 0)
query += " " + string.Join(", ", queryComponents) + " " + $"WHERE ID = {Id};";
query += " " + string.Join(", ", queryComponents) + " " + $"WHERE ID = @ID;";
else
return null;
@@ -147,83 +143,58 @@ namespace Ophelias.Managers
string query = "UPDATE reservations SET";
if (RoomID.HasValue)
QueryParts.Add($"RoomID = {RoomID}");
QueryParts.Add($"RoomID = @RID");
if (GuestID.HasValue)
QueryParts.Add($"GuestID = {GuestID}");
QueryParts.Add($"GuestID = @GID");
if (TransactionID.HasValue)
QueryParts.Add($"TransactionID = {TransactionID}");
QueryParts.Add($"TransactionID = @TID");
if (IsNoShow.HasValue)
QueryParts.Add($"IsNoShow = {Convert.ToInt32(IsNoShow)}");
QueryParts.Add($"IsNoShow = @IsNoShow");
if (Type.HasValue)
QueryParts.Add($"Type = {(int)Type}");
QueryParts.Add($"Type = @Type");
if (Status.HasValue)
QueryParts.Add($"Status = {(int)Status}");
QueryParts.Add($"Status = @Status");
if (CreationDate.HasValue)
QueryParts.Add($"CreationDate = {CreationDate.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"CreationDate = @CreationDate");
if (StartDate.HasValue)
QueryParts.Add($"StartDate = {StartDate.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"StartDate = @StartDate");
if (EndDate.HasValue)
QueryParts.Add($"EndDate = {EndDate.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"EndDate = @EndDate");
if (CheckIn.HasValue)
QueryParts.Add($"CheckIn = {CheckIn.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"CheckIn = @CheckIn");
if (CheckOut.HasValue)
QueryParts.Add($"CheckOut = {CheckOut.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"CheckOut = @CheckOut");
if (DateChanged.HasValue)
QueryParts.Add($"DateChanged = {DateChanged.Value.Date.ToString("yyyy-MM-dd")}");
QueryParts.Add($"DateChanged = @DateChanged");
if (QueryParts.Count > 0)
query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = {Id};";
query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = @ID;";
else
return null;
return query;
}
internal static string CreateTransaction(double Rate, double Owed,
double Multiplier, DateTime PayBy, DateTime? LastPaid = null,
DateTime? PaidOn = null, double Refund = 0, double Penalty = 0)
{
return @$"INSERT INTO transactions (Rate, Owed, Penalty, Multiplier, RefundAmount, PayBy, LastPaid, PaidOn)
VALUES ({Rate}, {Owed}, {Penalty}, {Multiplier}, {Refund}, {PayBy}, {PayBy}, {PaidOn});";
}
internal static string CreateReservation(int guestid, int transactionid, ReservationType type,
ReservationStatus status, DateTime creationdate, DateTime startdate, DateTime enddate,
DateTime? checkin = null, DateTime? checkout = null, DateTime? datechanged = null, bool? isnoshow = false)
{
return @$"INSERT INTO reservations (GuestID, TransactionID, IsNoShow, Type, Status, CreationDate, StartDate, EndDate, CheckIn, CheckOut, DateChanged)
VALUES ({guestid}, {transactionid}, {isnoshow}, {status}, {creationdate}, {startdate}, {enddate}, {checkin}, {checkout}, {datechanged});";
}
internal static string CreateGuest(string FirstName, string LastName, string Email, string? CreditCard, string? Expiration, string? CCV)
{
if (CreditCard != null && Expiration != null && CCV != null)
return $@"INSERT INTO guests (Fname, Lname, Email, CreditCard, Expiration, CCV) VALUES ('{FirstName}', '{LastName}', '{Email}', '{CreditCard}', '{Expiration}', '{CCV}');";
else
return $@"INSERT INTO guests (Fname, Lname, Email) VALUES ('{FirstName}', '{LastName}', '{Email}');";
}
internal static string? UpdateGuest(int Id, string? FirstName = null, string? LastName = null, string? Email = null, string? CreditCard = null, string? Expiration = null, string? CCV = null)
{
List<string> QueryParts = new List<string>();
string query = "UPDATE guests SET";
if (!string.IsNullOrEmpty(FirstName))
QueryParts.Add($"Fname = '{FirstName}'");
QueryParts.Add($"Fname = @Fname");
if (!string.IsNullOrEmpty(LastName))
QueryParts.Add($"Lname = '{LastName}'");
QueryParts.Add($"Lname = @Lname");
if (!string.IsNullOrEmpty(LastName))
QueryParts.Add($"Email = '{Email}'");
QueryParts.Add($"Email = @Email");
if (!string.IsNullOrEmpty(CreditCard))
QueryParts.Add($"CreditCard = '{CreditCard}'");
QueryParts.Add($"CreditCard = @CC");
if (!string.IsNullOrEmpty(Expiration))
QueryParts.Add($"Expiration = '{Expiration}'");
QueryParts.Add($"Expiration = @Expiry");
if (!string.IsNullOrEmpty(CCV))
QueryParts.Add($"CCV = '{CCV}'");
QueryParts.Add($"CCV = @CCV");
if (QueryParts.Count > 0)
query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = {Id};";
query += " " + string.Join(", ", QueryParts) + " " + $"WHERE ID = @ID;";
else
return null;

View File

@@ -12,12 +12,14 @@ namespace Ophelias.Managers
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = $"SELECT SEQ FROM sqlite_sequence WHERE name='{tableName}';";
cmd.CommandText = $"SELECT SEQ FROM sqlite_sequence WHERE name=@Table;";
cmd.ExecuteNonQuery();
cmd.Parameters.AddWithValue("@Table", tableName);
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
reader.Read();
LastId = reader.GetInt32(0);
if (reader.HasRows)
LastId = reader.GetInt32(0);
}
}
}
@@ -32,13 +34,15 @@ namespace Ophelias.Managers
{
cmd.CommandText = $@"SELECT COUNT(*)
FROM reservations
WHERE DATE('{Date.Date.ToString("yyyy-MM-dd")}')
WHERE DATE(@Date)
BETWEEN StartDate AND EndDate;";
cmd.ExecuteNonQuery();
cmd.Parameters.AddWithValue("@Date", Date.Date.ToString("yyyy-MM-dd"));
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
reader.Read();
Occupancies.Add(reader.GetInt32(0));
if (reader.Read())
Occupancies.Add(reader.GetInt32(0));
}
}
}
@@ -47,7 +51,7 @@ namespace Ophelias.Managers
internal static int AvgOccupancySpan(DateTime Start, DateTime End)
{
int thirtyDayOcc = 0;
int days = (int)(Start.Date - End.Date).TotalDays;
int days = (int)(End.Date - Start.Date).TotalDays;
using (DatabaseManager Manager = new DatabaseManager())
{
for (int i = 0; i < days; i++)
@@ -56,95 +60,21 @@ namespace Ophelias.Managers
{
cmd.CommandText = $@"SELECT COUNT(*)
FROM reservations
WHERE DATE('{Start.AddDays(i).Date.ToString("yyyy-MM-dd")}')
WHERE DATE(@Date)
BETWEEN StartDate AND EndDate;";
cmd.ExecuteNonQuery();
cmd.Parameters.AddWithValue("@Date", Start.AddDays(i).Date.ToString("yyyy-MM-dd"));
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
reader.Read();
thirtyDayOcc += reader.GetInt32(0);
if(reader.HasRows)
thirtyDayOcc += reader.GetInt32(0);
}
}
}
}
return thirtyDayOcc / days;
}
internal static Reservation CreateReservation(Guest Guest, ReservationType Type,
DateTime CreationDate, DateTime StartDate, DateTime EndDate, ReservationStatus Status = ReservationStatus.Active)
{
int id; double Multiplier;
switch (Type)
{
case ReservationType.Conventional: Multiplier = TxFunctions.ConventionalFee; break;
case ReservationType.Prepaid: Multiplier = TxFunctions.PrepaidFee; break;
case ReservationType.Incentive: Multiplier = TxFunctions.IncentiveFee(StartDate, EndDate); break;
case ReservationType.SixtyDayAdvance: Multiplier = TxFunctions.SixtyDayFee; break;
default: throw new NotImplementedException();
}
Transaction t = CreateTransaction(
Rate: GetBaseRate(),
Owed: TxFunctions.CalculateOwed(GetBaseRate(), (int)(StartDate.Date - EndDate.Date).TotalDays),
Multiplier: Multiplier,
PayBy: TxFunctions.GetPayByDate(Type, StartDate, EndDate)
);
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = QueryBuilder.CreateReservation(Guest.Id, t.Id, Type,
Status, CreationDate, StartDate, EndDate);
cmd.ExecuteNonQuery();
}
id = (int)Manager.con.LastInsertRowId;
}
return new Reservation(id, Guest, t, Type, Status, CreationDate, StartDate, EndDate);
}
internal static Transaction CreateTransaction(double Rate, double Owed,
double Multiplier, DateTime PayBy, DateTime? LastPaid = null,
DateTime? PaidOn = null, double Refund = 0, double Penalty = 0)
{
int Id;
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = QueryBuilder.CreateTransaction(Rate, Owed, Multiplier, PayBy,Refund: Refund, Penalty: Penalty, LastPaid: LastPaid, PaidOn: PaidOn);
cmd.ExecuteNonQuery();
}
Id = (int)Manager.con.LastInsertRowId;
}
return new Transaction(Id, Rate, Owed, Multiplier, Penalty: Penalty, RefundAmount: Refund, PayBy: PayBy, PaidOn: PaidOn);
}
internal static Guest CreateGuest(string FirstName, string LastName, string Email, string? CreditCard = null, string? Expiration = null, string? CCV = null)
{
int Id;
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = QueryBuilder.CreateGuest(FirstName, LastName, Email, CreditCard, Expiration, CCV);
cmd.ExecuteNonQuery();
}
Id = (int)Manager.con.LastInsertRowId;
}
if (CreditCard != null && Expiration != null && CCV != null)
return new Guest(Id, FirstName, LastName, Email, CreditCard, Expiration, CCV);
else
return new Guest(Id, FirstName, LastName, Email);
}
internal static void UpdateGuest(int Id, string? FirstName = null, string? LastName = null, string? Email = null, string? CreditCard = null, string? Expiration = null, string? CCV = null)
{
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = QueryBuilder.UpdateGuest(Id, FirstName, LastName, Email, CreditCard, Expiration, CCV);
cmd.ExecuteNonQuery();
}
}
}
internal static Guest? GetGuestByEmail(string Email)
{
Guest? g = null;
@@ -152,7 +82,8 @@ namespace Ophelias.Managers
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
cmd.CommandText = $"SELECT * FROM guests WHERE email = '{Email}'";
cmd.CommandText = $"SELECT * FROM guests WHERE Email = @Email";
cmd.Parameters.AddWithValue("@Email", Email);
cmd.ExecuteNonQuery();
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
@@ -173,9 +104,57 @@ namespace Ophelias.Managers
}
return g;
}
internal static double GetBaseRate()
internal static Reservation? GetResByGuestAndDate(Guest g)
{
double rate;
Reservation? r = null;
Transaction? t;
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
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 = @Status;";
cmd.Parameters.AddWithValue("@GuestID", g.Id);
cmd.Parameters.AddWithValue("@Status", (int)ReservationStatus.Active);
cmd.ExecuteNonQuery();
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
reader.Read();
if (reader.HasRows)
{
if (reader[20].GetType() != typeof(DBNull))
LastPaid = reader.GetDateTime(20);
if (reader[21].GetType() != typeof(DBNull))
PaidOn = reader.GetDateTime(21);
t = new Transaction(reader.GetInt32(13), reader.GetDouble(14), reader.GetDouble(15), reader.GetDouble(17), reader.GetDateTime(19),
LastPaid: LastPaid, PaidOn: PaidOn, RefundAmount: reader.GetDouble(18), Penalty: reader.GetDouble(16));
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 Reservation(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);
}
}
}
}
return r;
}
internal static double? GetBaseRate()
{
double? rate;
using (DatabaseManager Manager = new DatabaseManager())
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
@@ -186,7 +165,10 @@ namespace Ophelias.Managers
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
reader.Read();
rate = reader.GetDouble(0);
if (reader.HasRows)
rate = reader.GetDouble(0);
else
rate = null;
}
}
}
@@ -199,8 +181,10 @@ namespace Ophelias.Managers
{
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
string query = $"INSERT INTO rates (Rate, DateSet) VALUES ({Rate}, {DateSet});";
string query = $"INSERT INTO rates (Rate, DateSet) VALUES (@Rate, @DateSet);";
cmd.CommandText = query;
cmd.Parameters.AddWithValue("@Rate", Rate);
cmd.Parameters.AddWithValue("@DateSet", DateSet);
cmd.ExecuteNonQuery();
}
}
@@ -224,8 +208,9 @@ namespace Ophelias.Managers
int? Id;
using (SQLiteCommand cmd = Manager.con.CreateCommand())
{
string query = $"SELECT Id FROM rates WHERE DateSet = {DateTime.Now.Date.ToString("yyyy-MM-dd")};";
string query = "SELECT Id FROM rates WHERE DateSet = @Date;";
cmd.CommandText = query;
cmd.Parameters.AddWithValue("@Date", DateTime.Now.Date.ToString("yyyy-MM-dd"));
cmd.ExecuteNonQuery();
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
@@ -237,8 +222,10 @@ namespace Ophelias.Managers
{
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.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();
}
}