Katkat

Structure a Pizza-Ordering System with Four Discount Policies

AmericanSoftware Engineer · Online Assessment

Last updated

Design an extensible object-oriented order pricing system for a pizzeria that models pizzas, applies four distinct discount policies with specific composition rules, and returns an itemized, explainable receipt.

Problem Statement

Design the core pricing and order engine for a pizza-ordering service. Your system must represent individual pizzas, handle order composition, apply four distinct discount policies in a well-defined sequence, and output a detailed breakdown explaining how the final total was calculated.

Data Models

  1. Pizza:

    • size: "Small" ($8.00), "Medium" ($12.00), "Large" ($16.00).
    • toppings: List of topping names (strings). Each topping costs $1.50.
    • Base price of a pizza = size_price + (len(toppings) * 1.50).
  2. Order:

    • A collection of Pizza items and optional metadata (e.g., coupon codes).

Discount Policies

Discounts are evaluated and applied in the following strict precedence order. No discount can reduce any intermediate or final balance below $0.00.

  1. Policy A (Buy-X-Get-One-Free): For every 3 pizzas ordered, the cheapest pizza in that set of 3 is free (100% discount on that pizza's total price). If there are 6 pizzas, the 2 cheapest are free, etc.
  2. Policy B (Topping Special): If an order contains 2 or more "Large" pizzas, all toppings across the entire order receive a 50% discount (deduct total_topping_count * 0.75).
  3. Policy C (Order Tier Percentage Discount): If the subtotal after Policies A and B is at least $50.00, apply a 10% discount on that remaining balance.
  4. Policy D (Fixed Coupon Code): If the coupon code "SAVE5" is provided, deduct $5.00 from the remaining balance after Policy C.

Class Interface

Implement the following classes in Python:

class Pizza:
    def __init__(self, size: str, toppings: list[str]):
        pass

    def get_price(self) -> float:
        pass

class Order:
    def __init__(self, coupon_code: str = None):
        pass

    def add_pizza(self, pizza: Pizza) -> None:
        pass

    def calculate_receipt(self) -> dict:
        """
        Returns a dictionary with:
        - "subtotal": float (sum of original pizza prices)
        - "discounts": list of dicts, each with {"policy": str, "amount": float}
          (only include discounts that provided an amount > 0)
        - "total": float (final amount owed, rounded to 2 decimal places)
        """
        pass

Constraints

  • size is always one of "Small", "Medium", or "Large".
  • len(toppings) >= 0.
  • Order contains 0 <= n <= 100 pizzas.
  • All monetary amounts in the returned dictionary should be rounded to 2 decimal places.

Example

order = Order(coupon_code="SAVE5")
order.add_pizza(Pizza("Large", ["pepperoni", "mushrooms"]))  # 16 + 3.00 = 19.00
order.add_pizza(Pizza("Large", ["extra cheese"]))             # 16 + 1.50 = 17.50
order.add_pizza(Pizza("Small", []))                           # 8.00

receipt = order.calculate_receipt()
# Initial subtotal: 19.00 + 17.50 + 8.00 = 44.50
# Policy A: 3 pizzas -> cheapest is Small ($8.00) -> discount = 8.00 (subtotal -> 36.50)
# Policy B: 2 Large pizzas -> 3 total toppings * 0.75 = 2.25 discount (subtotal -> 34.25)
# Policy C: Subtotal (34.25)  not applied ($0.00)
# Policy D: "SAVE5" applied -> 5.00 discount (subtotal -> 29.25)
# Total: 29.25

Hints

Hint 1

Sort the pizza prices ascendingly when determining which items qualify for the free pizza under Policy A.

Hint 2

Maintain a running balance through each discount policy, ensuring you never deduct more than the current remaining total.

Hint 3

Consider implementing each discount policy as a separate strategy class or function that accepts order state and returns the deducted amount and explanation.

Follow-up questions

  • How would you refactor the pricing engine using the Strategy or Chain of Responsibility design pattern to allow dynamic configuration of discount precedence without modifying code?
  • If business rules required that a pizza discounted under Policy A cannot have its toppings discounted under Policy B, how would you track per-item discount allocations?
  • How would you handle currency rounding errors if items are billed in cents rather than floating-point numbers?