Motorradvermietung/src/Motorradvermietung.cpp

712 lines
18 KiB
C++

#include <iostream>
#include <list>
#include <vector>
#include <ctime>
#include <string>
#include <fstream>
using namespace std;
/**
* \brief ask a question and get a answer.
* \tparam T return parameter
* \param question the question.
* \return the value.
*/
template <typename T>
T ask_question(const string& question)
{
cout << question << ": ";
T value;
cin >> value;
return value;
}
/**
* \brief Returns true if answer was 'J'. False if user answered with 'N'
* \param question The question for user input.
* \return
*/
bool ask_question(const string& question)
{
while (true)
{
const auto answer = ask_question<char>(question + " (J/N)");
if (answer == 'j' || answer == 'J')
{
return true;
}
if (answer == 'n' || answer == 'N')
{
return false;
}
cout << "Ungueltige Eigabe. Bitte noch einmal versuchen!";
}
}
/**
* \brief Converts a int to string and fill up with '0' at first position.
* \param value the integer value.
* \param digits Number of digits to fill
* \return Formatted integer as string
*/
string int_to_string(const unsigned int value, const unsigned int digits = 0)
{
auto str = to_string(value);
const auto len = digits > str.length() ? digits - str.length() : 0;
for (unsigned int i = 0; i < len; ++i)
{
str.insert(0, "0");
}
return str;
}
/**
* \brief Present a date with day, month and year
*/
class date final
{
unsigned short day_;
unsigned short month_;
unsigned short year_;
int day_num_;
/**
* \brief Get the day number from current day/month/year.
* \return Number of days
*/
int day_number_from_date() const
{
const auto month = (month_ + 9) % 12;
const auto year = year_ - month / 10;
return 365 * year + year / 4 - year / 100 + year / 400 + (month * 306 + 5) / 10 + (day_ - 1);
}
public:
date(const unsigned short day, const unsigned short month, const unsigned short year) : day_(day), month_(month),
year_(year),
day_num_(
day_number_from_date())
{
}
/**
* \brief Get day num of current day/month/year
* \return Number of days
*/
int get_day_num() const
{
return day_num_;
}
/**
* \brief Output operator of date formatted as DD.MM.YYYY
* \param ostr output stream
* \param date current date
* \return output stream
*/
friend ostream& operator<<(ostream& ostr, const date& date)
{
ostr << int_to_string(date.day_, 2) << "."
<< int_to_string(date.month_, 2) << "."
<< int_to_string(date.year_, 4);
return ostr;
}
};
/**
* \brief address containing street, no, postal code and city
*/
class address final
{
string street_;
string street_no_;
string postal_code_;
string city_;
public:
address() = default;
address(const string&& street, const string&& street_no, const string&& postal_code, const string&& city) :
street_(street),
street_no_(street_no),
postal_code_(postal_code),
city_(city)
{
}
/**
* \brief Input operator of address
* \param istr output stream
* \param address current address
* \return output stream
*/
friend istream& operator>>(istream& istr, address& address)
{
cout << "Strasse: ";
istr >> address.street_;
cout << "Hausnummer: ";
istr >> address.street_no_;
cout << "Postleitzahl: ";
istr >> address.postal_code_;
cout << "Ort: ";
istr >> address.city_;
return istr;
}
/**
* \brief Output operator of address
* \param ostr output stream
* \param address current address
* \return output stream
*/
friend ostream& operator<<(ostream& ostr, const address& address)
{
ostr << "Strasse: " << address.street_ << endl;
ostr << "Hausnummer: " << address.street_no_ << endl;
ostr << "Postleitzahl: " << address.postal_code_ << endl;
ostr << "Ort: " << address.city_ << endl;
return ostr;
}
};
/**
* \brief Incremental raised number as id for customer.
*/
int next_customer_id;
/**
* \brief Customer, represented by name, first name, address, phone and year of birth
*/
class customer final
{
int id_;
string name_;
string first_name_;
address address_;
unsigned short year_of_birth_ = 1900;
string phone_no_;
bool has_driving_license_ = false;
public:
customer() : id_(++next_customer_id)
{
}
customer(string&& name, string&& first_name, address&& address, unsigned short&& year_of_birth, string&& phone_no,
bool&& has_driving_license) : id_(++next_customer_id),
name_(name),
first_name_(first_name),
address_(address),
year_of_birth_(year_of_birth),
phone_no_(phone_no),
has_driving_license_(has_driving_license)
{
}
/**
* \brief Input operator of customer
* \param istr input stream
* \param customer current address instance
* \return output stream
*/
friend istream& operator>>(istream& istr, customer*& customer)
{
cout << "Name: ";
istr >> customer->name_;
cout << "Vorname: ";
istr >> customer->first_name_;
cout << "Geburtsjahr: ";
istr >> customer->year_of_birth_;
istr >> customer->address_;
cout << "Telefonnumer: ";
istr >> customer->phone_no_;
customer->has_driving_license_ = ask_question("Fuehrerschein der Klasse A?");
return istr;
}
/**
* \brief Output operator of customer
* \param ostr output stream
* \param customer current customer instance
* \return output stream
*/
friend ostream& operator<<(ostream& ostr, const customer& customer)
{
ostr << "Name: " << customer.name_ << endl;
ostr << "Vorname: " << customer.first_name_ << endl;
ostr << "Geburtsjahr: " << customer.year_of_birth_ << endl;
ostr << customer.address_;
ostr << "Telefonnummer: " << customer.phone_no_ << endl;
ostr << "Fuehrerschein " << (customer.has_driving_license_ ? "" : "nicht ") << "vorhanden." << endl;
return ostr;
}
/**
* \brief get name of customer
* \return name
*/
string get_name() const
{
return name_;
}
/**
* \brief get first name of customer
* \return first name
*/
string get_first_name() const
{
return first_name_;
}
/**
* \brief get the value, if customer hast driving license
* \return True if customer hast driving license. False otherwise.
*/
bool get_has_driving_license() const
{
return has_driving_license_;
}
};
/**
* \brief Incremental raised number as id for a reservation.
*/
int next_reservation_id;
/**
* \brief Reservation of a motorcycle
*/
class reservation final
{
int id_;
date start_;
date end_;
short motorcycle_index_ = -1; // => -1 means nothing reserved
customer* customer_;
bool canceled_;
public:
reservation(const date start, const date end, const short motorcycle, customer* customer) :
id_(++next_reservation_id),
start_(start),
end_(end),
motorcycle_index_(motorcycle),
customer_(customer),
canceled_(false)
{
}
/**
* \brief get start date of reservation
* \return date instance
*/
date get_start_date() const
{
return start_;
}
/**
* \brief get end date of reservation
* \return date instance
*/
date get_end_date() const
{
return end_;
}
/**
* \brief get index of motorcycle array
* \return integer value of index
*/
short get_motorcycle_index() const
{
return motorcycle_index_;
}
/**
* \brief get pointer of customer, attached to this instance
* \return pointer to customer
*/
customer* get_customer() const
{
return customer_;
}
/**
* \brief Cancel reservation -> Give Motorcycle to the customer
*/
void cancel_reservation()
{
canceled_ = true;
}
/**
* \brief Get Id of the item
* \return Auto-incremented id as integer
*/
int get_id() const
{
return id_;
}
/**
* \brief get cancellation status of the reservation
* \return True is canceled. False otherwise.
*/
bool get_cancellation() const
{
return canceled_;
}
};
/**
* \brief All available motorcycles
*/
string motorcycles[] =
{
"Suzuki Bandit",
"Honda TransAlp",
"BMW F 650 GS",
"Kawasaki ZZR1400"
};
/**
* \brief Collection of customer-pointers
*/
list<customer*> customers;
/**
* \brief Collection of reservation pointers.
*/
list<reservation*> reservations;
/**
* \brief Creates a new customer, let the user check the input and push it on the list.
*/
void create_customer();
void create_reservation();
void rent_a_motorcycle();
void export_reservations();
void main_menu();
/**
* \brief Main entry method. Starts the main menu.
* \return success code
*/
int main()
{
main_menu();
return 0;
}
/**
* \brief Let the user choice an option.
*/
void main_menu()
{
try
{
while (true)
{
system("cls");
cout << "Motorradvermietung (" << reservations.size() << " Reservierungen; " << customers.size() <<
" Kunden)"
<< endl << endl;
cout << "Bitte w\x84hlen Sie eine Option:" << endl;
cout << "1: Neuen Kunden anlegen" << endl;
cout << "2: Erstellen einer Reservierung" << endl;
cout << "3: Motorrad herausgeben" << endl;
cout << "4: Reservierungen exportieren" << endl;
cout << endl;
cout << "0: Programm beenden" << endl;
const auto input = ask_question<int>("Ihre Eingabe");
switch (input)
{
case 1:
create_customer();
break;
case 2:
create_reservation();
break;
case 3:
rent_a_motorcycle();
break;
case 4:
export_reservations();
break;
case 0:
{
return;
}
default:
cout << "Unerlaubte Eingabe" << endl;
system("pause");
}
}
}
catch (...)
{
cout << "Etwas unvorhergesehendes ist passiert. Tut mir leid." << endl;
system("pause");
}
}
/**
* \brief Creates a new customer, let the user check the input and push it on the list.
*/
void create_customer()
{
while (true)
{
system("cls");
cout << "Neuen Kunden anlegen" << endl;
cout << "------------------------------------" << endl;
auto c = new customer;
cin >> c;
cout << endl << endl;
cout << *c;
if (ask_question("Angaben korrekt?"))
{
customers.push_back(c);
return;
}
}
}
/**
* \brief Finds customers by last name
* \param name Last name
* \return Returns a vector of customers with the same last name
*/
vector<customer*> find_customers_by_name(const string& name)
{
vector<customer*> result;
for (auto customer : customers)
{
if (customer->get_name() == name)
{
result.push_back(customer);
}
}
return result;
}
/**
* \brief Find the first customer by first name.
* \param filtered_customers A vector of customers with the same last name
* \param first_name The first name
* \return The first customer with the matching first name. If no customer found, it returns a nullptr
*/
customer* find_customer_by_first_name(const vector<customer*>& filtered_customers, const string& first_name)
{
for (auto c : filtered_customers)
{
if (c->get_first_name() == first_name)
{
return c;
}
}
return nullptr;
}
/**
* \brief Gets the validated date
* \param question Question to ask the user
* \return A date object.
*/
date get_validated_date(const string&& question)
{
while (true)
{
auto date_as_string = ask_question<string>(question + " (tt.mm.jjjj)");
auto day = 0, month = 0, year = 0;
const auto res = sscanf_s(date_as_string.c_str(), "%2d.%2d.%4d",
&day,
&month,
&year);
if (res == 3 && (month >= 1 && month <= 12) && (day >= 1 && day <= 31) && (year >= 1900 && month <= 2200))
{
return date{
static_cast<const unsigned short>(day),
static_cast<const unsigned short>(month),
static_cast<const unsigned short>(year)
};
}
cout << "Fehlerhafte Eingabe." << endl;
}
}
/**
* \brief
* \return
*/
int select_motorcycle()
{
while (true)
{
cout << "Bitte w\x84hlen sie ein Motorrad aus:" << endl;
auto index = 0;
for (const auto& cycle : motorcycles)
{
cout << ++index << ": " << cycle << endl;
}
const auto answer = ask_question<int>("Auswahl");
if (answer > 0 && answer <= index)
{
return answer - 1;
}
cout << "Ung\x81ltige Eingabe" << endl << endl;
}
}
bool validate_reservation(reservation* const reservation)
{
const auto start_sum = reservation->get_start_date().get_day_num();
const auto end_sum = reservation->get_end_date().get_day_num();
for (auto r : reservations)
{
if (r->get_motorcycle_index() == reservation->get_motorcycle_index())
{
if (start_sum < r->get_end_date().get_day_num() && end_sum > r->get_start_date().get_day_num()
|| end_sum > r->get_start_date().get_day_num() && end_sum < r->get_end_date().get_day_num())
{
return false;
}
}
}
return true;
}
customer* get_customer()
{
const auto name = ask_question<string>("Name des Kunden");
auto customers = find_customers_by_name(name);
if (customers.size() > 1)
{
const auto first_name = ask_question<string>("Vorname des Kunden");
return find_customer_by_first_name(customers, first_name);
}
if (customers.empty())
{
return nullptr;
}
return customers.front();
}
void create_reservation()
{
system("cls");
const auto current = get_customer();
if (current == nullptr)
{
cout << "Kunde nicht gefunden" << endl;
system("pause");
return;
}
cout << "Kunde gefunden: " << current->get_first_name() << " " << current->get_name() << endl;
if (!current->get_has_driving_license())
{
cout << "Der Kunde hat keinen entsprechenden Fuehrerschein." << endl;
system("pause");
return;
}
const auto start_date = get_validated_date("Startdatum");
const auto end_date = get_validated_date("Enddatum");
const auto motorcycle = select_motorcycle();
const auto r = new reservation(start_date, end_date, motorcycle, current);
if (validate_reservation(r))
{
reservations.push_back(r);
}
else
{
cout << "Reservierung konnte nicht angelegt werden." << endl;
cout << "Die Zeitperiode ueberschneidet sich mit einer vorhandenen Reservierung" << endl;
system("pause");
}
}
reservation* get_reservation_for_customer(customer* const current_customer)
{
auto index = 0;
vector<reservation*> reservations_of_customer;
for (auto reservation : reservations)
{
if (reservation->get_customer() == current_customer)
{
cout << ++index << ". " << reservation->get_start_date() << " - " << reservation->get_end_date() << ": " <<
motorcycles[reservation->get_motorcycle_index()] << endl;
reservations_of_customer.push_back(reservation);
}
}
index = ask_question<int>("Waehlen Sie eine Reservierung aus");
return reservations_of_customer[index - 1];
}
void rent_a_motorcycle()
{
system("cls");
const auto current = get_customer();
if (current == nullptr)
{
cout << "Kunde nicht gefunden" << endl;
system("pause");
return;
}
auto r = get_reservation_for_customer(current);
const auto cancel_reservation = ask_question("Motorrad ausgeben?");
cout << endl;
if (cancel_reservation)
{
r->cancel_reservation();
cout << "Motorrad reserviert!" << endl;
}
else
{
cout << "Reservierung abgebrochen" << endl;
}
system("pause");
}
string current_date_time()
{
auto t = time(nullptr);
struct tm buf{};
localtime_s(&buf, &t);
const auto time = int_to_string(buf.tm_hour, 2)
+ ":" + int_to_string(buf.tm_min, 2)
+ ":" + int_to_string(buf.tm_sec, 2);
const auto date = int_to_string(buf.tm_mday, 2)
+ "." + int_to_string(1 + buf.tm_mon, 2)
+ "." + int_to_string(1900 + buf.tm_year, 4);
return date + " " + time;
}
void export_reservations()
{
ofstream f;
f.open("reservierungen.txt", ios::app);
f << "Export (" << current_date_time() << ")" << endl;
f << "Id;Motorcycle;Start;End;Name;First_Name;Cancelled" << endl;
for (auto reservation : reservations)
{
f << reservation->get_id() << ";";
f << motorcycles[reservation->get_motorcycle_index()] << ";";
f << reservation->get_start_date() << ";";
f << reservation->get_end_date() << ";";
f << reservation->get_customer()->get_name() << ";";
f << reservation->get_customer()->get_first_name() << ";";
f << (reservation->get_cancellation() ? "True" : "False") << endl;
}
f << "------------" << endl;
f.close();
}