Structure a Pizza-Ordering System with Four Discount Policies
AmericanSoftware Engineer · Online AssessmentLast 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
-
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).
-
Order:
- A collection of
Pizzaitems and optional metadata (e.g., coupon codes).
- A collection of
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.
- 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.
- 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). - 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.
- 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
sizeis always one of"Small","Medium", or"Large".len(toppings) >= 0.- Order contains
0 <= n <= 100pizzas. - 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?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.