diff --git a/flask/src/main.py b/flask/src/main.py index e736679c..4d5b14e8 100644 --- a/flask/src/main.py +++ b/flask/src/main.py @@ -121,14 +121,40 @@ def checkout(): print("> /checkout inventory", inventory) print("> validate_inventory", validate_inventory) + # Check for empty cart or inventory + if len(inventory) == 0 or len(quantities) == 0: + sentry_sdk.metrics.incr(key="checkout.failed") + response = make_response(json.dumps({ + "error": "Invalid cart state - no items found" + }), 400) + return response + with sentry_sdk.start_span(op="process_order", description="function"): - quantities = cart['quantities'] - for cartItem in quantities: - for inventoryItem in inventory: - print("> inventoryItem.count", inventoryItem['count']) - if (validate_inventory and (inventoryItem.count < quantities[cartItem] or quantities[cartItem] >= inventoryItem.count)): + # Create inventory map for O(1) lookups + inventory_map = {item['id']: item['count'] for item in inventory} + + # Validate each item in cart + for product_id, requested_quantity in quantities.items(): + # Check if product exists in inventory + if product_id not in inventory_map: + sentry_sdk.metrics.incr(key="checkout.failed") + response = make_response(json.dumps({ + "error": f"Product {product_id} not found in inventory", + "product_id": product_id + }), 404) + return response + + if validate_inventory: + available_quantity = inventory_map[product_id] + if requested_quantity > available_quantity: sentry_sdk.metrics.incr(key="checkout.failed") - raise Exception("Not enough inventory for product") + response = make_response(json.dumps({ + "error": "Insufficient inventory", + "product_id": product_id, + "requested": requested_quantity, + "available": available_quantity + }), 400) + return response if len(inventory) == 0 or len(quantities) == 0: raise Exception("Not enough inventory for product") diff --git a/vue/src/views/CheckoutView.vue b/vue/src/views/CheckoutView.vue index d5a702b1..14b45bc4 100644 --- a/vue/src/views/CheckoutView.vue +++ b/vue/src/views/CheckoutView.vue @@ -148,23 +148,42 @@ import { useCounterStore } from "../stores/cart"; var requestOptions = { method: "POST", headers: { - "Content-Type": "text/plain", + "Content-Type": "application/json", }, body: JSON.stringify(payload), redirect: "follow", }; try { - success = await this.makeCheckoutRequest(requestOptions); + checkoutResult = await this.makeCheckoutRequest(requestOptions); } catch (error) { Sentry.withActiveSpan(span, async () => { Sentry.captureException(error); + Sentry.setContext("checkout_attempt", { + cartItems: this.cartItems.length, + error_details: error.details || {}, + status: error.status + }); }) - this.$router.push("/error"); - + + // Handle specific error cases + if (error.details?.error === "Insufficient inventory") { + const productId = error.details.product_id; + const item = this.cartItems.find(i => i.id === productId); + this.$store.dispatch('showError', { + message: `Sorry, only ${error.details.available} units available for ${item?.name || 'this item'}`, + type: 'inventory' + }); + } else { + this.$store.dispatch('showError', { + message: error.message || 'An error occurred during checkout', + type: 'general' + }); + } + return false; } - return success; + return checkoutResult; }) })