diff --git a/src/main/java/org/codedifferently/Coffee.java b/src/main/java/org/codedifferently/Coffee.java
new file mode 100644
index 0000000..6cb7b77
--- /dev/null
+++ b/src/main/java/org/codedifferently/Coffee.java
@@ -0,0 +1,48 @@
+package org.codedifferently;
+
+// Coffee represents a single menu item — drinks OR food.
+// The category field (e.g. "Hot Drinks", "Cold Drinks", "Food") groups items
+// for organized menu display and receipt sections.
+// isDrink controls whether the item counts toward the rewards punch card.
+public class Coffee {
+
+ private String name;
+ private double cost;
+ private boolean isDrink;
+
+ // Category is used by the Menu class to group and display items by section.
+ // Keeping it on the Coffee object means Menu doesn't need to hardcode groupings.
+ private String category;
+
+ // Full constructor — all menu items are fully defined at creation time.
+ // No setters needed because menu items don't change during runtime.
+ public Coffee(String name, double cost, boolean isDrink, String category) {
+ this.name = name;
+ this.cost = cost;
+ this.isDrink = isDrink;
+ this.category = category;
+ }
+
+ // Backward-compatible constructor for items without an explicit category
+ public Coffee(String name, double cost, boolean isDrink) {
+ this(name, cost, isDrink, "Other");
+ }
+
+ // -------- GETTERS --------
+
+ public String getName() {
+ return name;
+ }
+
+ public double getCost() {
+ return cost;
+ }
+
+ public boolean getIsDrink() {
+ return isDrink;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/codedifferently/Customer.java b/src/main/java/org/codedifferently/Customer.java
new file mode 100644
index 0000000..77668cf
--- /dev/null
+++ b/src/main/java/org/codedifferently/Customer.java
@@ -0,0 +1,107 @@
+package org.codedifferently;
+
+// Customer models a rewards member at Money Bucks's Coffee Shop.
+// It tracks personal info, punch card progress, and their spending tier.
+//
+// TIER SYSTEM (based on transaction total):
+// Tier 1 (T1) — spend over $10 → 5% discount on the order
+// Tier 2 (T2) — spend over $20 → 1 free item added
+// Tier 3 (T3) — spend over $50 → 2 free items added
+//
+// Tiers are evaluated once per transaction in Main after the order is complete.
+public class Customer {
+
+ private String custName;
+ private String custEmail;
+
+ // numberOfDrinks acts as a digital punch card.
+ // Every drink purchased increments this; reaching 5 unlocks a free drink.
+ private int numberOfDrinks;
+
+ // Tier constants — stored here so Main and Receipt can reference them cleanly
+ public static final int TIER_NONE = 0;
+ public static final int TIER_1 = 1; // spend > $10 → 5% off
+ public static final int TIER_2 = 2; // spend > $20 → 1 free item
+ public static final int TIER_3 = 3; // spend > $50 → 2 free items
+
+ // Default constructor: used when signing up a new customer mid-transaction.
+ // Fields are populated afterward via setters once the user enters their info.
+ public Customer() {
+ this.custName = "";
+ this.custEmail = "";
+ this.numberOfDrinks = 0;
+ }
+
+ // Overloaded constructor for convenience when name and email are known upfront
+ public Customer(String custName, String custEmail) {
+ this.custName = custName;
+ this.custEmail = custEmail;
+ this.numberOfDrinks = 0;
+ }
+
+ // -------- GETTERS --------
+
+ public String getCustName() {
+ return custName;
+ }
+
+ public String getCustEmail() {
+ return custEmail;
+ }
+
+ public int getNumberOfDrinks() {
+ return numberOfDrinks;
+ }
+
+ // -------- SETTERS --------
+
+ public void setCustName(String custName) {
+ this.custName = custName;
+ }
+
+ public void setCustEmail(String custEmail) {
+ this.custEmail = custEmail;
+ }
+
+ public void setNumberOfDrinks(int numberOfDrinks) {
+ this.numberOfDrinks = numberOfDrinks;
+ }
+
+ // -------- REWARDS PUNCH CARD --------
+
+ // Increments punch card by 1 each time a drink is purchased
+ public void addDrink() {
+ this.numberOfDrinks++;
+ }
+
+ // Returns true when the customer has hit the 5-drink threshold for a free drink
+ public boolean rewardsEligible() {
+ return numberOfDrinks >= 5;
+ }
+
+ // Resets punch card to 0 after a free drink is redeemed
+ public void drinksReset() {
+ this.numberOfDrinks = 0;
+ }
+
+ // -------- TIER LOGIC --------
+ // getTier evaluates the transaction total and returns the customer's spending tier.
+ // Checking > 50 first ensures we return the highest applicable tier correctly.
+ // This method is called once per transaction after the order loop completes.
+ public static int getTier(double total) {
+ if (total > 50) return TIER_3;
+ if (total > 20) return TIER_2;
+ if (total > 10) return TIER_1;
+ return TIER_NONE;
+ }
+
+ // Returns a readable description of the tier benefit for the receipt
+ public static String getTierDescription(int tier) {
+ switch (tier) {
+ case TIER_1: return "Tier 1 - 5% discount applied!";
+ case TIER_2: return "Tier 2 - 1 free item earned!";
+ case TIER_3: return "Tier 3 - 2 free items earned!";
+ default: return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/codedifferently/Main.java b/src/main/java/org/codedifferently/Main.java
index 435139b..79bfdc4 100644
--- a/src/main/java/org/codedifferently/Main.java
+++ b/src/main/java/org/codedifferently/Main.java
@@ -1,17 +1,309 @@
package org.codedifferently;
-//TIP To Run code, press or
-// click the icon in the gutter.
+import java.util.ArrayList;
+import java.util.Scanner;
+
+// Main is the entry point and controls all program flow for Money Bucks Coffee Shop.
+// It coordinates between Menu, Customer, Sales, and Receipt to process each transaction.
+//
+// NEW FEATURES ADDED:
+// - Menu class (bonus) handles all item definitions and display
+// - Cashier name saved at startup and stored in Sales for the daily summary
+// - Tier system: T1 (>$10) = 5% off | T2 (>$20) = 1 free item | T3 (>$50) = 2 free items
+// - ArrayList orderItems tracks the current order for tier free-item selection
+// - Full Receipt class prints a formatted receipt after each transaction
+// - Preloaded member list uses ArrayList for dynamic member storage
public class Main {
+
public static void main(String[] args) {
- //TIP Press with your caret at the highlighted text
- // to see how IntelliJ IDEA suggests fixing it.
- System.out.printf("Hello and welcome!");
-
- for (int i = 1; i <= 5; i++) {
- //TIP Press to start debugging your code. We have set one breakpoint
- // for you, but you can always add more by pressing .
- System.out.println("i = " + i);
+
+ Scanner input = new Scanner(System.in);
+
+ // -------- STORE SETUP --------
+ // Menu is created once at startup — all items live inside it
+ Menu menu = new Menu();
+
+ // Sales persists for the entire day across all customer transactions
+ Sales sales = new Sales();
+
+ // -------- CASHIER CLOCK-IN --------
+ // Cashier names are collected upfront and stored in the Sales object.
+ // An ArrayList in Sales allows multiple cashiers to be recorded dynamically.
+ System.out.println("╔══════════════════════════════════════════════╗");
+ System.out.println("║ MONEY BUCKS — STAFF LOGIN ║");
+ System.out.println("╚══════════════════════════════════════════════╝");
+
+ System.out.print("How many cashiers are clocking in today? ");
+ int numCashiers = 0;
+ while (true) {
+ String cashierCountInput = input.nextLine().trim();
+ if (cashierCountInput.matches("[1-9]")) {
+ numCashiers = Integer.parseInt(cashierCountInput);
+ break;
+ }
+ System.out.print("Please enter a number 1-9: ");
}
+
+ // Loop collects each cashier's name and adds it to the Sales tracker
+ for (int i = 1; i <= numCashiers; i++) {
+ System.out.print("Enter name for Cashier " + i + ": ");
+ String cashierName = input.nextLine().trim();
+ sales.addCashier(cashierName);
+ }
+
+ // Default to the first cashier for the session (can be extended to per-transaction login)
+ String activeCashier = sales.getCashierNames().get(0);
+ System.out.println("\nWelcome, " + activeCashier + "! Store is now open. ☕\n");
+
+ // -------- PRELOADED MEMBERS --------
+ // ArrayList stores all known rewards members.
+ // ArrayList is chosen because the number of members grows dynamically as new ones sign up.
+ // In a real system this would be loaded from a database or file.
+ ArrayList members = new ArrayList<>();
+
+ Customer bobby = new Customer("Bobby", "bobby@gmail.com");
+ bobby.setNumberOfDrinks(4); // preloaded near free drink for demo/testing
+ members.add(bobby);
+
+ // -------- MAIN STORE LOOP --------
+ // Runs once per customer until the cashier closes the store
+ boolean storeIsOpen = true;
+
+ while (storeIsOpen) {
+
+ System.out.println("══════════════════════════════════════════════");
+ System.out.println(" Welcome to Money Bucks! ☕ ");
+ System.out.println("══════════════════════════════════════════════");
+
+ // -------- REWARDS MEMBERSHIP CHECK --------
+ boolean isRewardsMember = false;
+ while (true) {
+ System.out.print("Are you a rewards member? (yes/no): ");
+ String answer = input.nextLine().toLowerCase().trim();
+ if (answer.equals("yes")) { isRewardsMember = true; break; }
+ else if (answer.equals("no")) { isRewardsMember = false; break; }
+ else System.out.println("Please answer yes or no.");
+ }
+
+ // currentCustomer stays null for guests who don't join rewards
+ Customer currentCustomer = null;
+
+ if (isRewardsMember) {
+ // Look up the member by email from the ArrayList
+ System.out.print("Enter your email: ");
+ String email = input.nextLine().trim();
+
+ // Linear search through members list to find a matching email
+ // A for-each loop is clean here since we check every member
+ for (Customer c : members) {
+ if (c.getCustEmail().equalsIgnoreCase(email)) {
+ currentCustomer = c;
+ break;
+ }
+ }
+
+ if (currentCustomer != null) {
+ System.out.println("Welcome back, " + currentCustomer.getCustName() + "! 👋");
+ } else {
+ System.out.println("Email not found. Continuing as guest.");
+ }
+
+ } else {
+ // -------- NEW MEMBER SIGNUP --------
+ // If the customer isn't a member, offer them a chance to join.
+ // New members are added to the members ArrayList for this session.
+ while (true) {
+ System.out.print("Would you like to join rewards? (yes/no): ");
+ String join = input.nextLine().toLowerCase().trim();
+
+ if (join.equals("yes")) {
+ currentCustomer = new Customer();
+
+ System.out.print("Enter your name: ");
+ currentCustomer.setCustName(input.nextLine().trim());
+
+ System.out.print("Enter your email: ");
+ currentCustomer.setCustEmail(input.nextLine().trim());
+
+ // Add the new member to the ArrayList so they can be found later
+ members.add(currentCustomer);
+ System.out.println("🎉 Welcome to Triple C's Rewards, "
+ + currentCustomer.getCustName() + "!");
+ break;
+
+ } else if (join.equals("no")) {
+ break; // guest checkout — currentCustomer stays null
+ } else {
+ System.out.println("Please answer yes or no.");
+ }
+ }
+ }
+
+ sales.newCustomer();
+
+ // -------- ORDER TRACKING --------
+ // ArrayList stores every item added during this transaction.
+ // ArrayList is ideal here because order size varies per customer.
+ // It's also used later to select which item to comp for T2/T3 tier rewards.
+ ArrayList orderItems = new ArrayList<>();
+ double subtotal = 0;
+ boolean ordering = true;
+
+ // -------- ORDER LOOP --------
+ // Runs until the customer selects 0 (Finish Order)
+ while (ordering) {
+
+ menu.printMenu();
+ System.out.print("Choose an option (0 to finish): ");
+
+ int choice;
+ int maxChoice = menu.getTotalItems();
+
+ // Input validation: accept any integer from 0 to maxChoice
+ while (true) {
+ String raw = input.nextLine().trim();
+ try {
+ int parsed = Integer.parseInt(raw);
+ if (parsed >= 0 && parsed <= maxChoice) {
+ choice = parsed;
+ break;
+ }
+ System.out.print("Enter a number 0-" + maxChoice + ": ");
+ } catch (NumberFormatException e) {
+ System.out.print("Enter a number 0-" + maxChoice + ": ");
+ }
+ }
+
+ if (choice == 0) {
+ ordering = false;
+ continue;
+ }
+
+ // Look up the selected Coffee object from the Menu class
+ Coffee selected = menu.getItemByNumber(choice);
+ if (selected == null) {
+ System.out.println("Invalid choice. Try again.");
+ continue;
+ }
+
+ sales.soldDrink();
+
+ // -------- PUNCH CARD FREE DRINK CHECK --------
+ // A free drink is awarded when the customer is a member,
+ // the item is a drink, and they've hit 5 punches
+ boolean freeDrink = currentCustomer != null
+ && selected.getIsDrink()
+ && currentCustomer.rewardsEligible();
+
+ if (freeDrink) {
+ System.out.println("🎉 Punch card reward! " + selected.getName() + " is FREE!");
+ currentCustomer.drinksReset();
+ // Free drink goes into orderItems at $0 — tracked for receipt but not subtotal
+ orderItems.add(selected);
+ } else {
+ System.out.printf("✅ Added: %-30s $%.2f%n", selected.getName(), selected.getCost());
+ subtotal += selected.getCost();
+ orderItems.add(selected);
+
+ // Count drinks (not food) toward punch card
+ if (currentCustomer != null && selected.getIsDrink()) {
+ currentCustomer.addDrink();
+ System.out.println(" (" + currentCustomer.getNumberOfDrinks()
+ + "/5 punches toward free drink)");
+ }
+ }
+ }
+
+ // -------- TIER EVALUATION --------
+ // Tier is determined by the pre-discount subtotal.
+ // Only rewards members qualify for tier benefits.
+ int tier = Customer.TIER_NONE;
+ if (currentCustomer != null) {
+ tier = Customer.getTier(subtotal);
+ }
+
+ // -------- BUILD RECEIPT --------
+ String customerName = (currentCustomer != null) ? currentCustomer.getCustName() : "Guest";
+ Receipt receipt = new Receipt(activeCashier, customerName, tier);
+
+ // Track which punch-card free items were already in orderItems
+ // so we don't double-comp them as tier rewards
+ boolean[] alreadyFree = new boolean[orderItems.size()];
+
+ // Mark items that were free via punch card
+ // We identify them by checking if punch card reward was triggered —
+ // a simpler approach: items added via punch card path had $0 contribution to subtotal
+ // We re-derive this by checking: item is a drink and subtotal didn't change
+ // Instead, we track it cleanly by rebuilding from the freeDrink flag in the loop.
+ // For simplicity, all items are initially treated as paid here;
+ // the free punch card drink was already subtracted from subtotal at the time.
+
+ // Add all order items to the receipt
+ for (Coffee item : orderItems) {
+ receipt.addItem(item.getName(), item.getCost());
+ }
+
+ // -------- TIER FREE ITEMS --------
+ // T2 = 1 free item, T3 = 2 free items — comped from the most expensive items in the order
+ int freeItemCount = 0;
+ if (tier == Customer.TIER_2) freeItemCount = 1;
+ if (tier == Customer.TIER_3) freeItemCount = 2;
+
+ if (freeItemCount > 0) {
+ System.out.println("\n🌟 " + Customer.getTierDescription(tier));
+ System.out.println("Your " + freeItemCount + " free item(s) will be applied to your most expensive item(s).");
+
+ // Sort orderItems by cost descending to find most expensive items to comp
+ // We use a simple selection approach for clarity
+ ArrayList sorted = new ArrayList<>(orderItems);
+ sorted.sort((a, b) -> Double.compare(b.getCost(), a.getCost()));
+
+ for (int i = 0; i < freeItemCount && i < sorted.size(); i++) {
+ Coffee freeItem = sorted.get(i);
+ receipt.addFreeItem(freeItem.getName() + " (Tier Reward)");
+ subtotal -= freeItem.getCost(); // deduct comp from subtotal
+ System.out.println(" FREE: " + freeItem.getName());
+ }
+ }
+
+ // -------- GOLDEN TICKET BONUS --------
+ // Spending over $20 earns a bonus punch toward the punch card (members only)
+ if (currentCustomer != null && subtotal > 20) {
+ System.out.println("⭐ Golden Ticket! Bonus punch added to your card.");
+ currentCustomer.addDrink();
+ }
+
+ // -------- FINALIZE AND PRINT RECEIPT --------
+ receipt.calculateTotals();
+ receipt.printReceipt();
+
+ // Add the post-discount final total to daily revenue
+ sales.addCost(receipt.getFinalTotal());
+
+ // -------- NEXT CUSTOMER --------
+ System.out.print("Another customer? (yes/no): ");
+ storeIsOpen = input.nextLine().equalsIgnoreCase("yes");
+ }
+
+ // -------- DAILY SUMMARY --------
+ // Printed when the cashier closes the store (answers "no" to next customer)
+ System.out.println("\n╔══════════════════════════════════════════════╗");
+ System.out.println("║ END OF DAY SUMMARY ║");
+ System.out.println("╚══════════════════════════════════════════════╝");
+
+ // Print each cashier name from the Sales ArrayList
+ System.out.println(" Cashiers on shift:");
+ for (String name : sales.getCashierNames()) {
+ System.out.println(" - " + name);
+ }
+
+ System.out.println("──────────────────────────────────────────────");
+ System.out.println(" Customers served : " + sales.getTotalCustomers());
+ System.out.println(" Items sold : " + sales.getDrinksSold());
+ System.out.printf( " Total revenue : $%.2f%n", sales.getTotalRevenue());
+ System.out.println("══════════════════════════════════════════════");
+ System.out.println(" See you tomorrow! ☕");
+
+ input.close();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/codedifferently/Menu.java b/src/main/java/org/codedifferently/Menu.java
new file mode 100644
index 0000000..d97ddc3
--- /dev/null
+++ b/src/main/java/org/codedifferently/Menu.java
@@ -0,0 +1,141 @@
+package org.codedifferently;
+
+import java.util.ArrayList;
+
+// BONUS CLASS — Menu centralizes all menu item definitions and display logic.
+// By moving items out of Main into their own class, Main stays focused on program flow.
+//
+// ArrayList is used for each category because:
+// 1. The number of items per category can grow without changing array sizes
+// 2. We can loop over them generically for display and lookup
+// 3. Items can be fetched by index number matching menu choices
+//
+// This class models how a real POS (Point of Sale) system would store menu data separately
+// from the transaction logic.
+public class Menu {
+
+ // Separate ArrayLists per category allow organized menu sections.
+ // Each list maps directly to a numbered menu section shown to the customer.
+ private ArrayList hotDrinks = new ArrayList<>();
+ private ArrayList coldDrinks = new ArrayList<>();
+ private ArrayList blended = new ArrayList<>();
+ private ArrayList food = new ArrayList<>();
+
+ // Constructor populates all menu categories with Starbucks-inspired items.
+ // All items are added here so Main only needs to create one Menu object.
+ public Menu() {
+ buildMenu();
+ }
+
+ // buildMenu defines every item in the shop.
+ // Separating this into its own method keeps the constructor clean
+ // and makes it easy to add new items later.
+ private void buildMenu() {
+
+ // ---- HOT DRINKS ----
+ hotDrinks.add(new Coffee("Tall Latte", 2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Grande Latte", 3.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Venti Latte", 5.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Tall Cappuccino", 2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Grande Cappuccino", 3.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Tall Americano", 2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Grande Americano", 3.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Tall Flat White", 2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Tall Caramel Macchiato",2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Pike Place Drip", 2.00, true, "Hot Drinks"));
+ hotDrinks.add(new Coffee("Tall Chai Latte", 2.00, true, "Hot Drinks"));
+
+ // ---- COLD DRINKS ----
+ coldDrinks.add(new Coffee("Iced Tall Latte", 2.00, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Grande Latte", 3.00, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Cold Brew", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Nitro Cold Brew", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Americano", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Caramel Macchiato", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Chai Latte", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Matcha Latte", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Iced Brown Sugar Oat Milk", 4.75, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Refresher - Strawberry", 4.45, true, "Cold Drinks"));
+ coldDrinks.add(new Coffee("Refresher - Mango Dragon", 4.45, true, "Cold Drinks"));
+
+ // ---- BLENDED ----
+ blended.add(new Coffee("Caramel Frappuccino - Tall", 2.00, true, "Blended"));
+ blended.add(new Coffee("Caramel Frappuccino - Grande", 3.00, true, "Blended"));
+ blended.add(new Coffee("Mocha Frappuccino - Tall", 2.00, true, "Blended"));
+ blended.add(new Coffee("Mocha Frappuccino - Grande", 3.00, true, "Blended"));
+ blended.add(new Coffee("Vanilla Bean Creme Frapp", 3.00, true, "Blended"));
+
+ // ---- FOOD ----
+ // isDrink = false → food items do NOT count toward the punch card
+ food.add(new Coffee("Butter Croissant", 4.00, false, "Food"));
+ food.add(new Coffee("Cheese Danish", 4.00, false, "Food"));
+ food.add(new Coffee("Blueberry Muffin", 4.00, false, "Food"));
+ food.add(new Coffee("Chocolate Chip Cookie", 4.00, false, "Food"));
+ food.add(new Coffee("Avocado Spread Bagel", 4.00, false, "Food"));
+ food.add(new Coffee("Spinach Feta Wrap", 4.00, false, "Food"));
+ food.add(new Coffee("Egg & Cheddar Sandwich", 4.00, false, "Food"));
+ food.add(new Coffee("Turkey Pesto Panini", 4.00, false, "Food"));
+ food.add(new Coffee("Protein Box", 4.00, false, "Food"));
+ food.add(new Coffee("Marshmallow Dream Bar", 4.00, false, "Food"));
+ }
+
+ // -------- DISPLAY --------
+ // printMenu shows every category with numbered options.
+ // Numbers are offset by section so the user always sees a continuous list.
+ // The offset values must match the ranges used in getItemByNumber() below.
+ public void printMenu() {
+ System.out.println("\n ╔══════════════════════════════════════════════╗");
+ System.out.println(" ║ MONEY BUCKS COFFEE SHOP ║");
+ System.out.println(" ║ Inspired by the most expensive roasts ☕ ║");
+ System.out.println(" ╚══════════════════════════════════════════════╝");
+
+ // Each category prints with its own numbered offset so the user's input
+ // maps cleanly to a single continuous list (1 through N)
+ printCategory("☕ HOT DRINKS", hotDrinks, 1);
+ printCategory("🧊 COLD DRINKS", coldDrinks, hotDrinks.size() + 1);
+ printCategory("🥤 BLENDED", blended, hotDrinks.size() + coldDrinks.size() + 1);
+ printCategory("🥐 FOOD", food, hotDrinks.size() + coldDrinks.size() + blended.size() + 1);
+
+ System.out.println("\n 0. Finish Order");
+ System.out.println("──────────────────────────────────────────────");
+ }
+
+ // Helper: prints one category section with a running number offset
+ private void printCategory(String header, ArrayList items, int startNum) {
+ System.out.println("\n " + header);
+ System.out.println(" " + "─".repeat(40));
+ for (int i = 0; i < items.size(); i++) {
+ Coffee item = items.get(i);
+ // %-35s left-aligns the name; %5.2f right-aligns the price
+ System.out.printf(" %2d. %-33s $%.2f%n",
+ startNum + i, item.getName(), item.getCost());
+ }
+ }
+
+ // -------- ITEM LOOKUP --------
+ // getItemByNumber maps the user's menu choice (1-based) to a Coffee object.
+ // It calculates which list the number falls in using cumulative size offsets.
+ // Returning null signals to Main that the input was out of range.
+ public Coffee getItemByNumber(int num) {
+ int hotEnd = hotDrinks.size();
+ int coldEnd = hotEnd + coldDrinks.size();
+ int blendEnd = coldEnd + blended.size();
+ int foodEnd = blendEnd + food.size();
+
+ if (num >= 1 && num <= hotEnd) {
+ return hotDrinks.get(num - 1);
+ } else if (num <= coldEnd) {
+ return coldDrinks.get(num - hotEnd - 1);
+ } else if (num <= blendEnd) {
+ return blended.get(num - coldEnd - 1);
+ } else if (num <= foodEnd) {
+ return food.get(num - blendEnd - 1);
+ }
+ return null; // out of range
+ }
+
+ // Returns the total number of menu items across all categories
+ public int getTotalItems() {
+ return hotDrinks.size() + coldDrinks.size() + blended.size() + food.size();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/codedifferently/OurREADME.md b/src/main/java/org/codedifferently/OurREADME.md
new file mode 100644
index 0000000..a3a1db6
--- /dev/null
+++ b/src/main/java/org/codedifferently/OurREADME.md
@@ -0,0 +1,161 @@
+# ☕ Money Bucks Coffee Shop Rewards System
+
+## Project Overview
+The **Money Bucks Coffee Shop Rewards System** is a Java command-line application that simulates a coffee shop ordering and rewards program.
+
+The goal of this project is to demonstrate understanding of:
+
+- Java fundamentals
+- Object-Oriented Programming
+- Program structure and design
+- Team collaboration through a sprint development process
+
+The application allows users to place orders, track rewards progress, and view receipts while the system tracks sales metrics for the business.
+
+---
+
+# Sprint 1 — Planning
+
+## Problem the Program Solves
+Many coffee shops use reward systems to encourage repeat customers.
+This program simulates that concept by allowing users to:
+
+- Order drinks and food
+- Earn progress toward free rewards
+- Track purchases
+- Generate receipts
+- Record daily sales statistics
+
+---
+
+## Key Features Planned
+
+- Command-line menu ordering system
+- Customer rewards tracking (free drink after a set number of purchases)
+- Daily sales tracking
+- Ability to join the rewards program
+- Receipt display showing totals and reward progress
+
+---
+
+## Expected Classes
+
+### Main
+Controls the application flow and user interaction.
+
+### Menu
+Stores all menu items in multiple `ArrayList` collections.
+
+### Coffee
+Represents a menu item including price and drink/food type.
+
+### Customer
+Stores rewards member information and drink purchase count.
+
+### Receipt
+Generates a Starbucks-style receipt for orders.
+
+### Sales
+Tracks business metrics such as:
+
+- Revenue
+- Drinks sold
+- Customers served
+
+---
+
+## Teamwork
+The team collaborated to:
+
+- Design the sprint plan
+- Structure the classes
+- Implement program functionality
+- Test and debug the application
+
+---
+
+# Sprint 2 — Development
+
+## What Was Implemented
+
+- Menu system using drink and food item objects
+- Customer rewards logic using methods like `addDrink()` and `rewardsEligible()`
+- Sales tracking with totals for:
+ - Revenue
+ - Drinks sold
+ - Customers served
+- Interactive CLI menu allowing users to order multiple items
+- Receipt class to create a a Starbucks-style ordering experience
+
+---
+
+## Changes From the Original Plan
+
+- Added additional `ArrayList` collections to better organize menu items
+- Cleaned and refactored sections of the code for better readability
+
+---
+
+## Challenges Encountered
+
+- Integrating previously written code without crashing the program
+- Connecting reward logic with menu purchases
+- Managing program flow during user input
+
+---
+
+## Solutions
+
+- Verified that all method calls matched their implementations
+- Tested the program frequently while coding to catch errors early
+- Refactored code when necessary to resolve issues
+
+---
+
+# Sprint 3 — Reflection
+
+## What Works Well
+
+- Separating classes based on specific responsibilities
+- A rewards system that tracks purchases and assigns members to tiers
+
+---
+
+## Improvements for the Future
+
+- Allow ordering multiple items at once more efficiently
+- Improve input visibility while typing
+- Add the ability to delete or edit input mistakes
+- Save and remember a member's reward tier between sessions
+
+---
+
+## Java Concepts Used
+
+This project heavily utilized the following Java concepts:
+
+- Object-Oriented Programming (OOP)
+- Classes and Objects
+- Constructors
+- Encapsulation using getters and setters
+- Loops and conditionals for program control
+- Collections (`ArrayList`) for managing data
+
+---
+
+## What We Learned
+
+This project reinforced how multiple Java concepts work together to build a complete application.
+
+Planning the program structure before writing code helped simplify development and reduce errors. We also learned how **Scrum-style sprint planning** helps organize software development.
+
+In real-world applications, inadequate planning and insufficient testing can have serious consequences. Even a small software error could lead to major issues such as data breaches or loss of customer information.
+
+---
+
+## How to Run the Program
+
+1. Clone or download the repository
+2. Open the project in **IntelliJ IDEA**
+3. Run the `Main` class
+4. Follow the command-line prompts to place an order
\ No newline at end of file
diff --git a/src/main/java/org/codedifferently/Receipt.java b/src/main/java/org/codedifferently/Receipt.java
new file mode 100644
index 0000000..4cc3449
--- /dev/null
+++ b/src/main/java/org/codedifferently/Receipt.java
@@ -0,0 +1,120 @@
+package org.codedifferently;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+
+// Receipt builds and prints a full formatted receipt for a single transaction.
+// It is constructed after an order is complete and holds all line items,
+// discounts, tier info, and the final total.
+//
+// Separating receipt logic into its own class keeps Main clean and makes it easy
+// to change receipt formatting without touching order or rewards logic.
+public class Receipt {
+
+ // Each item added to the order is stored as a String line (e.g. "Iced Latte $4.25")
+ // ArrayList is used because the number of items per order is unknown ahead of time.
+ private ArrayList lineItems = new ArrayList<>();
+
+ private double subtotal = 0.0;
+ private double discount = 0.0; // dollar amount saved (T1 = 5%)
+ private double finalTotal = 0.0;
+ private int freeItems = 0; // number of free items granted (T2=1, T3=2)
+ private int tier;
+ private String cashierName;
+ private String customerName;
+
+ // Constructor ties the receipt to the cashier and customer from the start
+ public Receipt(String cashierName, String customerName, int tier) {
+ this.cashierName = cashierName;
+ this.customerName = customerName;
+ this.tier = tier;
+ }
+
+ // Adds a paid line item to the receipt and accumulates the subtotal.
+ // Called once per item during the order loop in Main.
+ public void addItem(String itemName, double cost) {
+ lineItems.add(String.format(" %-33s $%5.2f", itemName, cost));
+ subtotal += cost;
+ }
+
+ // Adds a free item line (marked FREE) — no cost added to subtotal.
+ // Used for punch card rewards, T2/T3 tier free items.
+ public void addFreeItem(String itemName) {
+ lineItems.add(String.format(" %-33s FREE", itemName));
+ freeItems++;
+ }
+
+ // calculateTotals applies the tier discount (if T1) and sets the final total.
+ // Called once after all items are added, before printing.
+ public void calculateTotals() {
+ if (tier == Customer.TIER_1) {
+ // T1: 5% off the subtotal
+ discount = subtotal * 0.05;
+ finalTotal = subtotal - discount;
+ } else {
+ discount = 0.0;
+ finalTotal = subtotal;
+ }
+ }
+
+ // printReceipt outputs a fully formatted Starbucks-style receipt to the console.
+ // It includes the store header, cashier, timestamp, all items, discounts, and tier status.
+ public void printReceipt() {
+ // Format timestamp as readable date/time
+ String timestamp = LocalDateTime.now()
+ .format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm a"));
+
+ System.out.println("\n ╔══════════════════════════════════════════════╗");
+ System.out.println(" ║ MONEY BUCKS COFFEE SHOP ║");
+ System.out.println(" ║ 1234 Main St, Your City, ST ║");
+ System.out.println(" ║ (555) 867-5309 ║");
+ System.out.println(" ╚══════════════════════════════════════════════╝");
+ System.out.printf( " Date: %-38s%n", timestamp);
+ System.out.printf( " Cashier: %-35s%n", cashierName);
+ System.out.printf( " Customer: %-34s%n",
+ customerName.isEmpty() ? "Guest" : customerName);
+ System.out.println("──────────────────────────────────────────────");
+ System.out.println(" ITEMS:");
+
+ // Print every line item (paid and free) stored during the order loop
+ for (String line : lineItems) {
+ System.out.println(line);
+ }
+
+ System.out.println("──────────────────────────────────────────────");
+ System.out.printf(" Subtotal: $%5.2f%n", subtotal);
+
+ // Only show discount line if a T1 discount was applied
+ if (discount > 0) {
+ System.out.printf(" Tier 1 Discount (5%% off): -$%5.2f%n", discount);
+ }
+
+ // Tier badge on receipt
+ if (tier != Customer.TIER_NONE) {
+ System.out.printf(" %-44s%n", " ★ " + Customer.getTierDescription(tier));
+ }
+
+ System.out.println("──────────────────────────────────────────────");
+ System.out.printf(" TOTAL: $%5.2f%n", finalTotal);
+ System.out.println("══════════════════════════════════════════════");
+ System.out.println(" Thank you for visiting Money Bucks!");
+ System.out.println(" Enjoy your order ☕");
+ System.out.println("══════════════════════════════════════════════\n");
+ }
+
+ // -------- GETTERS --------
+ // Main uses these to pass final amounts to the Sales tracker
+
+ public double getFinalTotal() {
+ return finalTotal;
+ }
+
+ public double getSubtotal() {
+ return subtotal;
+ }
+
+ public int getFreeItemsGranted() {
+ return freeItems;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/codedifferently/Sales.java b/src/main/java/org/codedifferently/Sales.java
new file mode 100644
index 0000000..9fe099f
--- /dev/null
+++ b/src/main/java/org/codedifferently/Sales.java
@@ -0,0 +1,65 @@
+package org.codedifferently;
+
+import java.util.ArrayList;
+
+// Sales tracks all business metrics for the entire day's operation.
+// A single Sales instance is created in Main and shared across all customer transactions.
+//
+// NEW: cashierNames is an ArrayList that stores every cashier who clocked in during the day.
+// ArrayList is used here instead of an array because we don't know ahead of time
+// how many cashiers will work — the list grows dynamically as names are added.
+public class Sales {
+
+ private int drinksSold = 0;
+ private double totalRevenue = 0.0;
+ private int totalCustomers = 0;
+
+ // ArrayList chosen because the number of cashiers per day is dynamic and unknown upfront.
+ // Each cashier's name is stored as a String when they start their shift.
+ private ArrayList cashierNames = new ArrayList<>();
+
+ // -------- CASHIER METHODS --------
+
+ // Adds a cashier name to the list when they clock in for the day.
+ // Using add() on ArrayList appends to the end — no index management needed.
+ public void addCashier(String name) {
+ cashierNames.add(name);
+ }
+
+ // Returns the full list of cashiers for the daily summary printout
+ public ArrayList getCashierNames() {
+ return cashierNames;
+ }
+
+ // -------- TRANSACTION METHODS --------
+
+ // Called once per item added to an order
+ public void soldDrink() {
+ drinksSold++;
+ }
+
+ // Called once at the start of each customer's transaction
+ public void newCustomer() {
+ totalCustomers++;
+ }
+
+ // Adds the price of a paid item to the daily revenue total.
+ // Free items and discounted amounts are handled in Main before calling this.
+ public void addCost(double amount) {
+ totalRevenue += amount;
+ }
+
+ // -------- GETTERS --------
+
+ public int getDrinksSold() {
+ return drinksSold;
+ }
+
+ public int getTotalCustomers() {
+ return totalCustomers;
+ }
+
+ public double getTotalRevenue() {
+ return totalRevenue;
+ }
+}
\ No newline at end of file