Initial commit: MCP Hub Community Edition v3.0.0
Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
495
plugins/wordpress/handlers/coupons.py
Normal file
495
plugins/wordpress/handlers/coupons.py
Normal file
@@ -0,0 +1,495 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user