"""Coupons Handler - manages WooCommerce coupons""" import json from typing import Any from plugins.wordpress.client import WordPressClient def get_tool_specifications() -> list[dict[str, Any]]: """Return tool specifications for ToolGenerator""" return [ { "name": "list_coupons", "method_name": "list_coupons", "description": "List WooCommerce coupons. Returns paginated coupon list with discount details and usage restrictions.", "schema": { "type": "object", "properties": { "per_page": { "type": "integer", "description": "Number of coupons per page (1-100)", "default": 10, "minimum": 1, "maximum": 100, }, "page": { "type": "integer", "description": "Page number", "default": 1, "minimum": 1, }, "search": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Search term to filter coupons by code", }, }, }, "scope": "read", }, { "name": "create_coupon", "method_name": "create_coupon", "description": "Create a new WooCommerce coupon. Supports percentage and fixed discounts with usage limits and restrictions.", "schema": { "type": "object", "properties": { "code": { "type": "string", "description": "Coupon code (e.g., 'SAVE20')", "minLength": 1, }, "amount": { "type": "string", "description": "Discount amount (e.g., '20' for 20% or $20)", }, "discount_type": { "type": "string", "description": "Type of discount", "enum": ["percent", "fixed_cart", "fixed_product"], "default": "percent", }, "description": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Coupon description (internal note)", }, "date_expires": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Expiration date in ISO 8601 format (e.g., '2024-12-31T23:59:59')", }, "minimum_amount": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Minimum order amount required to use coupon", }, "maximum_amount": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Maximum order amount allowed to use coupon", }, "individual_use": { "type": "boolean", "description": "If true, coupon cannot be combined with other coupons", "default": False, }, "product_ids": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "Array of product IDs coupon applies to", }, "excluded_product_ids": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "Array of product IDs coupon does NOT apply to", }, "usage_limit": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum number of times coupon can be used", }, "usage_limit_per_user": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum number of times coupon can be used per user", }, "limit_usage_to_x_items": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum number of items coupon applies to", }, "free_shipping": { "type": "boolean", "description": "If true, grants free shipping", "default": False, }, }, "required": ["code", "amount"], }, "scope": "write", }, { "name": "update_coupon", "method_name": "update_coupon", "description": "Update an existing WooCommerce coupon. Can update discount amount, restrictions, and expiration.", "schema": { "type": "object", "properties": { "coupon_id": { "type": "integer", "description": "Coupon ID to update", "minimum": 1, }, "code": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Coupon code (e.g., 'SAVE20')", }, "discount_type": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Type of discount", "enum": ["percent", "fixed_cart", "fixed_product"], }, "amount": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Discount amount", }, "description": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Coupon description (internal note)", }, "date_expires": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Expiration date in ISO 8601 format", }, "minimum_amount": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Minimum order amount", }, "maximum_amount": { "anyOf": [{"type": "string"}, {"type": "null"}], "description": "Maximum order amount", }, "individual_use": { "anyOf": [{"type": "boolean"}, {"type": "null"}], "description": "If true, coupon cannot be combined with other coupons", }, "product_ids": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "Array of product IDs coupon applies to", }, "excluded_product_ids": { "anyOf": [ {"type": "array", "items": {"type": "integer"}}, {"type": "null"}, ], "description": "Array of product IDs coupon does NOT apply to", }, "usage_limit": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum number of uses", }, "usage_limit_per_user": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum uses per user", }, "limit_usage_to_x_items": { "anyOf": [{"type": "integer"}, {"type": "null"}], "description": "Maximum number of items coupon applies to", }, "free_shipping": { "anyOf": [{"type": "boolean"}, {"type": "null"}], "description": "If true, grants free shipping", }, }, "required": ["coupon_id"], }, "scope": "write", }, { "name": "delete_coupon", "method_name": "delete_coupon", "description": "Delete a WooCommerce coupon. Can force delete or move to trash.", "schema": { "type": "object", "properties": { "coupon_id": { "type": "integer", "description": "Coupon ID to delete", "minimum": 1, }, "force": { "type": "boolean", "description": "Force permanent delete (bypass trash)", "default": False, }, }, "required": ["coupon_id"], }, "scope": "write", }, ] class CouponsHandler: """Handle coupon-related operations for WooCommerce""" def __init__(self, client: WordPressClient): """ Initialize coupons handler. Args: client: WordPress API client instance """ self.client = client async def list_coupons( self, per_page: int = 10, page: int = 1, search: str | None = None ) -> str: """ List WooCommerce coupons. Args: per_page: Number of coupons per page (1-100) page: Page number search: Search term to filter coupons by code Returns: JSON string with coupons list """ try: params = {"per_page": per_page, "page": page} if search: params["search"] = search coupons = await self.client.get("coupons", params=params, use_woocommerce=True) result = { "total": len(coupons), "page": page, "per_page": per_page, "coupons": [ { "id": coupon["id"], "code": coupon["code"], "discount_type": coupon["discount_type"], "amount": coupon["amount"], "description": coupon.get("description", ""), "date_expires": coupon.get("date_expires"), "usage_count": coupon.get("usage_count", 0), "usage_limit": coupon.get("usage_limit"), "individual_use": coupon.get("individual_use", False), "free_shipping": coupon.get("free_shipping", False), "minimum_amount": coupon.get("minimum_amount", "0"), "maximum_amount": coupon.get("maximum_amount", "0"), } for coupon in coupons ], } return json.dumps(result, indent=2) except Exception as e: return json.dumps( {"error": str(e), "message": f"Failed to list coupons: {str(e)}"}, indent=2 ) async def create_coupon( self, code: str, amount: str, discount_type: str = "percent", description: str | None = None, date_expires: str | None = None, minimum_amount: str | None = None, maximum_amount: str | None = None, individual_use: bool = False, product_ids: list[int] | None = None, excluded_product_ids: list[int] | None = None, usage_limit: int | None = None, usage_limit_per_user: int | None = None, limit_usage_to_x_items: int | None = None, free_shipping: bool = False, ) -> str: """ Create a new WooCommerce coupon. Args: code: Coupon code (e.g., 'SAVE20') amount: Discount amount (e.g., '20' for 20% or $20) discount_type: Type of discount (percent, fixed_cart, fixed_product) description: Coupon description (internal note) date_expires: Expiration date in ISO 8601 format minimum_amount: Minimum order amount required maximum_amount: Maximum order amount allowed individual_use: If true, coupon cannot be combined with others product_ids: Product IDs coupon applies to excluded_product_ids: Product IDs coupon does NOT apply to usage_limit: Maximum number of times coupon can be used usage_limit_per_user: Maximum uses per user limit_usage_to_x_items: Maximum number of items coupon applies to free_shipping: If true, grants free shipping Returns: JSON string with created coupon data """ try: data = { "code": code, "discount_type": discount_type, "amount": amount, "individual_use": individual_use, "free_shipping": free_shipping, } # Add optional fields if description: data["description"] = description if date_expires: data["date_expires"] = date_expires if minimum_amount: data["minimum_amount"] = minimum_amount if maximum_amount: data["maximum_amount"] = maximum_amount if product_ids: data["product_ids"] = product_ids if excluded_product_ids: data["excluded_product_ids"] = excluded_product_ids if usage_limit: data["usage_limit"] = usage_limit if usage_limit_per_user: data["usage_limit_per_user"] = usage_limit_per_user if limit_usage_to_x_items: data["limit_usage_to_x_items"] = limit_usage_to_x_items coupon = await self.client.post("coupons", json_data=data, use_woocommerce=True) result = { "id": coupon["id"], "code": coupon["code"], "discount_type": coupon["discount_type"], "amount": coupon["amount"], "date_expires": coupon.get("date_expires"), "message": f"Coupon '{code}' created successfully with ID {coupon['id']}", } return json.dumps(result, indent=2) except Exception as e: return json.dumps( {"error": str(e), "message": f"Failed to create coupon: {str(e)}"}, indent=2 ) async def update_coupon( self, coupon_id: int, code: str | None = None, discount_type: str | None = None, amount: str | None = None, description: str | None = None, date_expires: str | None = None, minimum_amount: str | None = None, maximum_amount: str | None = None, individual_use: bool | None = None, product_ids: list[int] | None = None, excluded_product_ids: list[int] | None = None, usage_limit: int | None = None, usage_limit_per_user: int | None = None, limit_usage_to_x_items: int | None = None, free_shipping: bool | None = None, ) -> str: """ Update an existing WooCommerce coupon. Args: coupon_id: Coupon ID to update code: Coupon code discount_type: Type of discount amount: Discount amount description: Coupon description date_expires: Expiration date in ISO 8601 format minimum_amount: Minimum order amount maximum_amount: Maximum order amount individual_use: If true, coupon cannot be combined with others product_ids: Product IDs coupon applies to excluded_product_ids: Product IDs coupon does NOT apply to usage_limit: Maximum number of uses usage_limit_per_user: Maximum uses per user limit_usage_to_x_items: Maximum number of items coupon applies to free_shipping: If true, grants free shipping Returns: JSON string with updated coupon data """ try: # Build data dict with only provided values data = {} if code is not None: data["code"] = code if discount_type is not None: data["discount_type"] = discount_type if amount is not None: data["amount"] = amount if description is not None: data["description"] = description if date_expires is not None: data["date_expires"] = date_expires if minimum_amount is not None: data["minimum_amount"] = minimum_amount if maximum_amount is not None: data["maximum_amount"] = maximum_amount if individual_use is not None: data["individual_use"] = individual_use if product_ids is not None: data["product_ids"] = product_ids if excluded_product_ids is not None: data["excluded_product_ids"] = excluded_product_ids if usage_limit is not None: data["usage_limit"] = usage_limit if usage_limit_per_user is not None: data["usage_limit_per_user"] = usage_limit_per_user if limit_usage_to_x_items is not None: data["limit_usage_to_x_items"] = limit_usage_to_x_items if free_shipping is not None: data["free_shipping"] = free_shipping if not data: raise Exception("No update data provided") coupon = await self.client.put( f"coupons/{coupon_id}", json_data=data, use_woocommerce=True ) result = { "id": coupon["id"], "code": coupon["code"], "discount_type": coupon["discount_type"], "amount": coupon["amount"], "message": f"Coupon ID {coupon_id} updated successfully", } return json.dumps(result, indent=2) except Exception as e: return json.dumps( {"error": str(e), "message": f"Failed to update coupon {coupon_id}: {str(e)}"}, indent=2, ) async def delete_coupon(self, coupon_id: int, force: bool = False) -> str: """ Delete a WooCommerce coupon. Args: coupon_id: Coupon ID to delete force: Force permanent delete (True) or move to trash (False) Returns: JSON string with deletion result """ try: params = {"force": "true" if force else "false"} result = await self.client.delete( f"coupons/{coupon_id}", params=params, use_woocommerce=True ) action = "permanently deleted" if force else "moved to trash" return json.dumps( { "success": True, "coupon_id": coupon_id, "message": f"Coupon {action} successfully", "result": result, }, indent=2, ) except Exception as e: return json.dumps( {"error": str(e), "message": f"Failed to delete coupon {coupon_id}: {str(e)}"}, indent=2, )