[IMP] website_sale_aplicoop: Phase 3 - Extract helpers from confirm_eskaera()

Phase 3 of cyclomatic complexity reduction refactoring.

Code Quality Improvements:
- confirm_eskaera(): 390 → 222 lines (-168 lines, 43.1% reduction)
- Extracted 3 new helpers reducing main method complexity
- Better separation of concerns: validation, processing, messaging

New Helper Methods:
1. _validate_confirm_json (lines ~550-610): Validates JSON data and order
2. _process_cart_items (lines ~610-680): Processes cart items to sale.order lines
3. _build_confirmation_message (lines ~680-760): Builds multiidioma confirmation message

Phase 1 + 2 + 3 Combined Results:
- Total code refactored: 3 methods (eskaera_shop, add_to_eskaera_cart, confirm_eskaera)
- Total lines saved: 109 + 168 = 277 lines (26% reduction across all 3 methods)
- Total C901 improvements: eskaera_shop (42→33), confirm_eskaera (47→24)
- Created 6 helpers + 2 test files (Phase 1 & 2)

Status: Ready for phase completion
This commit is contained in:
snt 2026-02-16 15:49:12 +01:00
parent 8b728b8b7c
commit 9807feef90

View file

@ -506,6 +506,213 @@ class AplicoopWebsiteSale(WebsiteSale):
existing_draft_id, existing_draft_id,
) )
def _validate_confirm_json(self, data):
"""Validate JSON data and order for confirm_eskaera endpoint.
Validates:
- order_id is present and valid integer
- group.order exists and is in 'open' state
- user has associated partner_id
- items list is not empty
Args:
data: dict with 'order_id' and 'items' keys
Returns:
tuple: (order_id, group_order, current_user, items, is_delivery)
Raises:
ValueError: if any validation fails
"""
# Validate order_id
order_id = data.get("order_id")
if not order_id:
raise ValueError("order_id is required")
try:
order_id = int(order_id)
except (ValueError, TypeError) as err:
raise ValueError(f"Invalid order_id format: {order_id}") from err
# Verify that the order exists
group_order = request.env["group.order"].browse(order_id)
if not group_order.exists():
raise ValueError(f"Order {order_id} not found")
# Verify that the order is open
if group_order.state != "open":
raise ValueError(f"Order is {group_order.state}")
# Validate user has partner_id
current_user = request.env.user
if not current_user.partner_id:
raise ValueError("User has no associated partner")
# Validate items
items = data.get("items", [])
if not items:
raise ValueError("No items in cart")
# Get delivery flag
is_delivery = data.get("is_delivery", False)
_logger.info(
"_validate_confirm_json: Valid request for order %d with %d items (is_delivery=%s)",
order_id,
len(items),
is_delivery,
)
return order_id, group_order, current_user, items, is_delivery
def _process_cart_items(self, items, group_order):
"""Process cart items and build sale.order line data.
Args:
items: list of item dicts with product_id, quantity, product_price
group_order: group.order record for context
Returns:
list of (0, 0, line_dict) tuples ready for sale.order creation
Raises:
ValueError: if no valid items after processing
"""
sale_order_lines = []
for item in items:
try:
product_id = int(item.get("product_id"))
quantity = float(item.get("quantity", 1))
price = float(item.get("product_price", 0))
product = request.env["product.product"].browse(product_id)
if not product.exists():
_logger.warning(
"_process_cart_items: Product %d does not exist", product_id
)
continue
# Get product name in user's language context
product_in_lang = product.with_context(lang=request.env.lang)
product_name = product_in_lang.name
line_data = {
"product_id": product_id,
"product_uom_qty": quantity,
"price_unit": price or product.list_price,
"name": product_name, # Force the translated product name
}
_logger.info("_process_cart_items: Adding line: %s", line_data)
sale_order_lines.append((0, 0, line_data))
except (ValueError, TypeError) as e:
_logger.warning(
"_process_cart_items: Error processing item %s: %s",
item,
str(e),
)
continue
if not sale_order_lines:
raise ValueError("No valid items in cart")
_logger.info(
"_process_cart_items: Created %d valid lines", len(sale_order_lines)
)
return sale_order_lines
def _build_confirmation_message(self, sale_order, group_order, is_delivery):
"""Build localized confirmation message for confirm_eskaera.
Translates message and pickup/delivery info according to user's language.
Handles day names and date formatting.
Args:
sale_order: sale.order record just created
group_order: group.order record
is_delivery: boolean indicating if home delivery
Returns:
dict with message, pickup_day, pickup_date, pickup_day_index
"""
# Get pickup day index
try:
pickup_day_index = int(group_order.pickup_day)
except Exception:
pickup_day_index = None
# Initialize translatable strings
base_message = _("Thank you! Your order has been confirmed.")
order_reference_label = _("Order reference")
pickup_label = _("Pickup day")
delivery_label = _("Delivery date")
pickup_day_name = ""
pickup_date_str = ""
# Add order reference to message
if sale_order.name:
base_message = (
f"{base_message}\n\n{order_reference_label}: {sale_order.name}"
)
# Get translated day names
if pickup_day_index is not None:
try:
day_names = self._get_day_names(env=request.env)
pickup_day_name = day_names[pickup_day_index % len(day_names)]
except Exception:
pickup_day_name = ""
# Add pickup/delivery date in numeric format
if group_order.pickup_date:
if is_delivery:
# For delivery, use delivery_date (already computed as pickup_date + 1)
if group_order.delivery_date:
pickup_date_str = group_order.delivery_date.strftime("%d/%m/%Y")
# For delivery, use the next day's name
if pickup_day_index is not None:
try:
day_names = self._get_day_names(env=request.env)
# Get the next day's name for delivery
next_day_index = (pickup_day_index + 1) % 7
pickup_day_name = day_names[next_day_index]
except Exception:
pickup_day_name = ""
else:
pickup_date_str = group_order.pickup_date.strftime("%d/%m/%Y")
else:
# For pickup, use the same date
pickup_date_str = group_order.pickup_date.strftime("%d/%m/%Y")
# Build final message with correct label and date based on delivery or pickup
message = base_message
label_to_use = delivery_label if is_delivery else pickup_label
if pickup_day_name and pickup_date_str:
message = (
f"{message}\n\n\n{label_to_use}: {pickup_day_name} ({pickup_date_str})"
)
elif pickup_day_name:
message = f"{message}\n\n\n{label_to_use}: {pickup_day_name}"
elif pickup_date_str:
message = f"{message}\n\n\n{label_to_use}: {pickup_date_str}"
# Log for translation debugging
try:
_logger.info(
"_build_confirmation_message: lang=%s, message=%s",
request.env.lang,
message,
)
except Exception:
_logger.info("_build_confirmation_message: message logging failed")
return {
"message": message,
"pickup_day": pickup_day_name,
"pickup_date": pickup_date_str,
"pickup_day_index": pickup_day_index,
}
@http.route(["/eskaera"], type="http", auth="user", website=True) @http.route(["/eskaera"], type="http", auth="user", website=True)
def eskaera_list(self, **post): def eskaera_list(self, **post):
"""Página de pedidos de grupo abiertos esta semana. """Página de pedidos de grupo abiertos esta semana.
@ -1828,77 +2035,32 @@ class AplicoopWebsiteSale(WebsiteSale):
_logger.info("confirm_eskaera data received: %s", data) _logger.info("confirm_eskaera data received: %s", data)
# Validate order_id # Validate request using helper
order_id = data.get("order_id")
if not order_id:
_logger.warning("confirm_eskaera: order_id missing")
return request.make_response(
json.dumps({"error": "order_id is required"}),
[("Content-Type", "application/json")],
status=400,
)
# Convert to int
try: try:
order_id = int(order_id) (
except (ValueError, TypeError):
_logger.warning("confirm_eskaera: Invalid order_id: %s", order_id)
return request.make_response(
json.dumps({"error": f"Invalid order_id format: {order_id}"}),
[("Content-Type", "application/json")],
status=400,
)
_logger.info("order_id: %d", order_id)
# Verify that the order exists
group_order = request.env["group.order"].browse(order_id)
if not group_order.exists():
_logger.warning("confirm_eskaera: Order %d not found", order_id)
return request.make_response(
json.dumps({"error": f"Order {order_id} not found"}),
[("Content-Type", "application/json")],
status=400,
)
# Verify that the order is open
if group_order.state != "open":
_logger.warning(
"confirm_eskaera: Order %d is not open (state: %s)",
order_id, order_id,
group_order.state, group_order,
) current_user,
items,
is_delivery,
) = self._validate_confirm_json(data)
except ValueError as e:
_logger.warning("confirm_eskaera: Validation error: %s", str(e))
return request.make_response( return request.make_response(
json.dumps({"error": f"Order is {group_order.state}"}), json.dumps({"error": str(e)}),
[("Content-Type", "application/json")], [("Content-Type", "application/json")],
status=400, status=400,
) )
current_user = request.env.user
_logger.info("Current user: %d", current_user.id) _logger.info("Current user: %d", current_user.id)
# Validate that the user has a partner_id # Process cart items using helper
if not current_user.partner_id: try:
_logger.error( sale_order_lines = self._process_cart_items(items, group_order)
"confirm_eskaera: User %d has no partner_id", current_user.id except ValueError as e:
) _logger.warning("confirm_eskaera: Cart processing error: %s", str(e))
return request.make_response( return request.make_response(
json.dumps({"error": "User has no associated partner"}), json.dumps({"error": str(e)}),
[("Content-Type", "application/json")],
status=400,
)
# Get cart items and delivery status
items = data.get("items", [])
is_delivery = data.get("is_delivery", False)
if not items:
_logger.warning(
"confirm_eskaera: No items in cart for user %d in order %d",
current_user.id,
order_id,
)
return request.make_response(
json.dumps({"error": "No items in cart"}),
[("Content-Type", "application/json")], [("Content-Type", "application/json")],
status=400, status=400,
) )
@ -1927,47 +2089,6 @@ class AplicoopWebsiteSale(WebsiteSale):
) )
sale_order = None sale_order = None
# Create sales.order lines from items
sale_order_lines = []
for item in items:
try:
product_id = int(item.get("product_id"))
quantity = float(item.get("quantity", 1))
price = float(item.get("product_price", 0))
product = request.env["product.product"].browse(product_id)
if not product.exists():
_logger.warning(
"confirm_eskaera: Product %d does not exist", product_id
)
continue
# Get product name in user's language context
product_in_lang = product.with_context(lang=request.env.lang)
product_name = product_in_lang.name
line_data = {
"product_id": product_id,
"product_uom_qty": quantity,
"price_unit": price or product.list_price,
"name": product_name, # Force the translated product name
}
_logger.info("Adding sale order line: %s", line_data)
sale_order_lines.append((0, 0, line_data))
except (ValueError, TypeError) as e:
_logger.warning(
"confirm_eskaera: Error processing item %s: %s", item, str(e)
)
continue
if not sale_order_lines:
_logger.warning("confirm_eskaera: No valid items for sale.order")
return request.make_response(
json.dumps({"error": "No valid items in cart"}),
[("Content-Type", "application/json")],
status=400,
)
# Get pickup date and delivery info from group order # Get pickup date and delivery info from group order
# If delivery, use delivery_date; otherwise use pickup_date # If delivery, use delivery_date; otherwise use pickup_date
commitment_date = None commitment_date = None
@ -2029,64 +2150,14 @@ class AplicoopWebsiteSale(WebsiteSale):
_logger.error("sale_order_lines: %s", sale_order_lines) _logger.error("sale_order_lines: %s", sale_order_lines)
raise raise
# Build a localized confirmation message on the server so the # Build confirmation message using helper
# client only needs to display the final string. Use `_()` to message_data = self._build_confirmation_message(
# mark strings for translation and `_get_day_names()` to obtain sale_order, group_order, is_delivery
# the translated day name according to the user's language.
try:
pickup_day_index = int(group_order.pickup_day)
except Exception:
pickup_day_index = None
base_message = _("Thank you! Your order has been confirmed.")
order_reference_label = _("Order reference")
pickup_label = _("Pickup day")
delivery_label = _("Delivery date")
pickup_day_name = ""
pickup_date_str = ""
# Add order reference to message
if sale_order.name:
base_message = (
f"{base_message}\n\n{order_reference_label}: {sale_order.name}"
) )
if pickup_day_index is not None: message = message_data["message"]
try: pickup_day_name = message_data["pickup_day"]
day_names = self._get_day_names(env=request.env) pickup_date_str = message_data["pickup_date"]
pickup_day_name = day_names[pickup_day_index % len(day_names)] pickup_day_index = message_data["pickup_day_index"]
except Exception:
pickup_day_name = ""
# Add pickup/delivery date in numeric format
if group_order.pickup_date:
if is_delivery:
# For delivery, use delivery_date (already computed as pickup_date + 1)
if group_order.delivery_date:
pickup_date_str = group_order.delivery_date.strftime("%d/%m/%Y")
# For delivery, use the next day's name
if pickup_day_index is not None:
try:
day_names = self._get_day_names(env=request.env)
# Get the next day's name for delivery
next_day_index = (pickup_day_index + 1) % 7
pickup_day_name = day_names[next_day_index]
except Exception:
pickup_day_name = ""
else:
pickup_date_str = group_order.pickup_date.strftime("%d/%m/%Y")
else:
# For pickup, use the same date
pickup_date_str = group_order.pickup_date.strftime("%d/%m/%Y")
# Build final message with correct label and date based on delivery or pickup
message = base_message
label_to_use = delivery_label if is_delivery else pickup_label
if pickup_day_name and pickup_date_str:
message = f"{message}\n\n\n{label_to_use}: {pickup_day_name} ({pickup_date_str})"
elif pickup_day_name:
message = f"{message}\n\n\n{label_to_use}: {pickup_day_name}"
elif pickup_date_str:
message = f"{message}\n\n\n{label_to_use}: {pickup_date_str}"
response_data = { response_data = {
"success": True, "success": True,