#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace book_store { auto store::append_book(const product::book &book) -> void { if (this->books_max_size() != 0 && this->_books.size() < this->books_max_size()) { this->_books.push_back(book); } } auto store::append_customer(const customer_type &person) -> void { if (this->people_max_size() != 0 && this->_customers.size() < this->people_max_size()) { this->_customers.push_back(person); } } auto store::purchase_book(size_type person_id, std::string_view isbn, product::book_count count) -> std::optional { auto person = this->find_person_by_id(person_id); if (!person) { return purchase_error( purchase_error::purchase_error_type::person_not_found); } auto book = this->find_book_by_isbn(isbn); if (!book) { return purchase_error(purchase_error::purchase_error_type::book_not_found); } if (book->get().copies() < count) { return purchase_error( purchase_error::purchase_error_type::not_enough_stock); } auto &customer = *person; auto book_price = book.value().get().price_usd(); if (customer.is_member()) { auto discount = 1.0 - (this->_membership_discount_percent * 0.01); book_price = static_cast(book_price * discount); if (customer.books_bought() % 11 == 0) { auto transaction_count = 0; auto transaction_total = 0.0; for (auto &transaction : this->_transactions) { if (transaction.first == person_id) { transaction_total += transaction.second.price_usd(); transaction_count += 1; } } if (transaction_count >= 10) { book_price = (transaction_total / transaction_count) * discount; } customer.amount_spent(0); } } customer.books_bought(customer.books_bought() + count); // There's some fun casting going on here. It's all safe, though ... probably. customer.amount_spent(customer.amount_spent() + static_cast( (static_cast( book_price.value()) * count.value()))); book.value().get().copies(book.value().get().copies() - count); this->_transactions.emplace_back( person_id, product::book{book.value().get().title().data(), book.value().get().authors(), book.value().get().publisher().data(), book.value().get().isbn().data(), book_price, product::book_count(count)}); return std::nullopt; } auto store::tick_year() -> void { for (auto &customer : this->_customers) { customer.amount_spent(customer.amount_spent() + this->_membership_fee); } } auto store::membership_fee(product::price::usd fee) -> void { this->_membership_fee = fee; } auto store::membership_discount_percent(discount_percent_type discount_percent) -> void { this->_membership_discount_percent = discount_percent; } auto store::books_max_size() const noexcept -> size_type { return this->_books_max_size; } auto store::people_max_size() const noexcept -> size_type { return this->_customers_max_size; } auto store::books_size() const noexcept -> size_type { return this->_books.size(); } auto store::people_size() const noexcept -> size_type { return this->_customers.size(); } auto store::membership_fee() const noexcept -> product::price::usd { return this->_membership_fee; } auto store::membership_discount_percent() const noexcept -> discount_percent_type { return this->_membership_discount_percent; } auto store::find_person_by_id(size_type person_id) -> std::optional { for (auto &person : this->_customers) { if (person.id() == person_id) { return person; } } return std::nullopt; } auto store::find_book_by_isbn(std::string_view isbn) -> std::optional> { for (auto &book : this->_books) { std::string book_isbn = book.isbn().data(); std::string search_isbn = isbn.data(); std::transform( book_isbn.begin(), book_isbn.end(), book_isbn.begin(), [](unsigned char character) { return std::tolower(character); }); std::transform( search_isbn.begin(), search_isbn.end(), search_isbn.begin(), [](unsigned char character) { return std::tolower(character); }); if (book_isbn == search_isbn) { return book; } } return std::nullopt; } auto store::find_books_by_title(std::string_view title) -> std::vector { std::vector books; for (auto &book : this->_books) { std::string book_title = book.title().data(); std::string search_title = title.data(); std::transform( book_title.begin(), book_title.end(), book_title.begin(), [](unsigned char character) { return std::tolower(character); }); std::transform( search_title.begin(), search_title.end(), search_title.begin(), [](unsigned char character) { return std::tolower(character); }); if (book_title.find(search_title) != std::string::npos) { books.push_back(book); } } return books; } auto store::find_books_by_author(std::string_view name) -> std::vector { std::vector books; for (auto &book : this->_books) { for (auto &author : book.authors()) { std::string author_name = author.full_name(); std::string search_name = name.data(); std::transform( author_name.begin(), author_name.end(), author_name.begin(), [](unsigned char character) { return std::tolower(character); }); std::transform( search_name.begin(), search_name.end(), search_name.begin(), [](unsigned char character) { return std::tolower(character); }); if (author_name.find(search_name) != std::string::npos) { books.push_back(book); } } } return books; } auto store::transactions_by_id(size_type person_id) -> std::vector { std::vector books; for (auto &transaction : this->_transactions) { if (transaction.first == person_id) { books.push_back(transaction.second); } } return books; } auto store::books() -> books_type & { return this->_books; } auto store::people() -> customer_container & { return this->_customers; } } // namespace book_store