feat: add 16 missing WooCommerce tool methods (coupons, variations, attributes, customers)

This commit is contained in:
2026-06-02 11:58:58 +00:00
parent 6175a836f7
commit 0501dcecd4
+259 -7
View File
@@ -10,6 +10,10 @@ defined('ABSPATH') || exit;
*/
class WooTools {
// =========================================================
// PRODUCTS
// =========================================================
public static function listProducts(array $args): array {
$qArgs = ['post_type' => 'product', 'post_status' => $args['status'] ?? 'publish', 'posts_per_page' => (int)($args['per_page'] ?? 20), 'paged' => (int)($args['page'] ?? 1)];
if (!empty($args['search'])) $qArgs['s'] = $args['search'];
@@ -24,7 +28,7 @@ class WooTools {
public static function getProduct(array $args): array {
$product = wc_get_product((int)$args['product_id']);
if (!$product) throw new \RuntimeException("Product not found: {$args['product_id']}");
if (!$product) return ['error' => "Product not found: {$args['product_id']}"];
return ['id' => $product->get_id(), 'name' => $product->get_name(), 'type' => $product->get_type(), 'status' => $product->get_status(), 'sku' => $product->get_sku(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'price' => $product->get_price(), 'stock_quantity' => $product->get_stock_quantity(), 'manage_stock' => $product->get_manage_stock(), 'description' => $product->get_description(), 'short_description' => $product->get_short_description(), 'categories' => wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'names'])];
}
@@ -42,7 +46,7 @@ class WooTools {
public static function updateProduct(array $args): array {
$product = wc_get_product((int)$args['product_id']);
if (!$product) throw new \RuntimeException("Product not found: {$args['product_id']}");
if (!$product) return ['error' => "Product not found: {$args['product_id']}"];
if (isset($args['name'])) $product->set_name($args['name']);
if (isset($args['regular_price'])) $product->set_regular_price($args['regular_price']);
if (isset($args['sale_price'])) $product->set_sale_price($args['sale_price']);
@@ -53,6 +57,10 @@ class WooTools {
return ['id' => $product->get_id(), 'updated' => true];
}
// =========================================================
// ORDERS
// =========================================================
public static function listOrders(array $args): array {
$qArgs = ['limit' => (int)($args['per_page'] ?? 20), 'paged' => (int)($args['page'] ?? 1), 'status' => $args['status'] ?? 'any'];
if (!empty($args['customer_id'])) $qArgs['customer_id'] = (int)$args['customer_id'];
@@ -62,7 +70,7 @@ class WooTools {
public static function getOrder(array $args): array {
$order = wc_get_order((int)$args['order_id']);
if (!$order) throw new \RuntimeException("Order not found: {$args['order_id']}");
if (!$order) return ['error' => "Order not found: {$args['order_id']}"];
$items = [];
foreach ($order->get_items() as $item) {
$items[] = ['product_id' => $item->get_product_id(), 'name' => $item->get_name(), 'qty' => $item->get_quantity(), 'total' => $item->get_total()];
@@ -72,11 +80,15 @@ class WooTools {
public static function updateOrderStatus(array $args): array {
$order = wc_get_order((int)$args['order_id']);
if (!$order) throw new \RuntimeException("Order not found: {$args['order_id']}");
if (!$order) return ['error' => "Order not found: {$args['order_id']}"];
$order->update_status($args['status']);
return ['id' => $order->get_id(), 'status' => $args['status'], 'updated' => true];
}
// =========================================================
// CUSTOMERS
// =========================================================
public static function listCustomers(array $args): array {
$qArgs = ['role' => 'customer', 'number' => (int)($args['per_page'] ?? 20), 'paged' => (int)($args['page'] ?? 1)];
if (!empty($args['search'])) $qArgs['search'] = '*' . $args['search'] . '*';
@@ -89,18 +101,258 @@ class WooTools {
return ['customers' => $customers, 'total' => $query->get_total()];
}
// Alias for MCP tool name wc_get_customers
public static function getCustomers(array $args): array {
return self::listCustomers($args);
}
// =========================================================
// STORE STATS
// =========================================================
public static function getStoreStats(array $args): array {
$period = $args['period'] ?? 'month';
$after = match($period) {
'day' => date('Y-m-d', strtotime('-1 day')),
'week' => date('Y-m-d', strtotime('-7 days')),
'year' => date('Y-m-d', strtotime('-1 year')),
default => date('Y-m-01'), // month
default => date('Y-m-01'),
};
$orders = wc_get_orders(['status' => ['completed', 'processing'], 'date_after' => $after, 'limit' => -1]);
$revenue = array_sum(array_map(fn($o) => (float)$o->get_total(), $orders));
return ['period' => $period, 'revenue' => $revenue, 'currency' => get_woocommerce_currency(), 'orders_count' => count($orders), 'products_count' => wp_count_posts('product')->publish ?? 0];
}
// =========================================================
// COUPONS
// =========================================================
public static function getCoupons(array $args): array {
$per_page = (int)($args['per_page'] ?? 20);
$page = (int)($args['page'] ?? 1);
$qArgs = ['post_type' => 'shop_coupon', 'post_status' => $args['status'] ?? 'publish', 'posts_per_page' => $per_page, 'paged' => $page];
if (!empty($args['code'])) $qArgs['title'] = $args['code'];
$query = new \WP_Query($qArgs);
$coupons = [];
foreach ($query->posts as $p) {
$coupon = new \WC_Coupon($p->ID);
$coupons[] = self::formatCoupon($coupon);
}
return ['coupons' => $coupons, 'total' => $query->found_posts];
}
public static function getCoupon(array $args): array {
$id = $args['coupon_id'] ?? null;
if ($id) {
$coupon = new \WC_Coupon((int)$id);
} elseif (!empty($args['code'])) {
$coupon = new \WC_Coupon($args['code']);
} else {
return ['error' => 'Provide coupon_id or code'];
}
if (!$coupon->get_id()) return ['error' => 'Coupon not found'];
return self::formatCoupon($coupon);
}
public static function createCoupon(array $args): array {
if (empty($args['code'])) return ['error' => 'code is required'];
$coupon = new \WC_Coupon();
$coupon->set_code(strtolower($args['code']));
$coupon->set_discount_type($args['discount_type'] ?? 'percent');
$coupon->set_amount($args['amount'] ?? 0);
if (!empty($args['description'])) $coupon->set_description($args['description']);
if (isset($args['usage_limit'])) $coupon->set_usage_limit((int)$args['usage_limit']);
if (isset($args['usage_limit_per_user'])) $coupon->set_usage_limit_per_user((int)$args['usage_limit_per_user']);
if (!empty($args['expiry_date'])) $coupon->set_date_expires(strtotime($args['expiry_date']));
if (isset($args['minimum_amount'])) $coupon->set_minimum_amount($args['minimum_amount']);
if (isset($args['maximum_amount'])) $coupon->set_maximum_amount($args['maximum_amount']);
if (isset($args['free_shipping'])) $coupon->set_free_shipping((bool)$args['free_shipping']);
$id = $coupon->save();
return ['coupon_id' => $id, 'code' => $coupon->get_code(), 'created' => true];
}
public static function updateCoupon(array $args): array {
if (empty($args['coupon_id'])) return ['error' => 'coupon_id is required'];
$coupon = new \WC_Coupon((int)$args['coupon_id']);
if (!$coupon->get_id()) return ['error' => 'Coupon not found'];
if (isset($args['code'])) $coupon->set_code(strtolower($args['code']));
if (isset($args['discount_type'])) $coupon->set_discount_type($args['discount_type']);
if (isset($args['amount'])) $coupon->set_amount($args['amount']);
if (isset($args['description'])) $coupon->set_description($args['description']);
if (isset($args['usage_limit'])) $coupon->set_usage_limit((int)$args['usage_limit']);
if (isset($args['expiry_date'])) $coupon->set_date_expires(strtotime($args['expiry_date']));
if (isset($args['minimum_amount'])) $coupon->set_minimum_amount($args['minimum_amount']);
if (isset($args['maximum_amount'])) $coupon->set_maximum_amount($args['maximum_amount']);
if (isset($args['free_shipping'])) $coupon->set_free_shipping((bool)$args['free_shipping']);
$coupon->save();
return ['coupon_id' => $coupon->get_id(), 'updated' => true];
}
public static function deleteCoupon(array $args): array {
if (empty($args['coupon_id'])) return ['error' => 'coupon_id is required'];
$coupon = new \WC_Coupon((int)$args['coupon_id']);
if (!$coupon->get_id()) return ['error' => 'Coupon not found'];
$force = (bool)($args['force'] ?? false);
$result = $force ? wp_delete_post($coupon->get_id(), true) : wp_trash_post($coupon->get_id());
return ['coupon_id' => $args['coupon_id'], 'deleted' => (bool)$result, 'force' => $force];
}
public static function getCouponCount(array $args): array {
$counts = wp_count_posts('shop_coupon');
return ['published' => (int)($counts->publish ?? 0), 'draft' => (int)($counts->draft ?? 0), 'trash' => (int)($counts->trash ?? 0)];
}
public static function emptyCouponTrash(array $args): array {
global $wpdb;
$ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type='shop_coupon' AND post_status='trash'");
$deleted = 0;
foreach ($ids as $id) {
if (wp_delete_post((int)$id, true)) $deleted++;
}
return ['deleted' => $deleted];
}
private static function formatCoupon(\WC_Coupon $c): array {
return ['id' => $c->get_id(), 'code' => $c->get_code(), 'discount_type' => $c->get_discount_type(), 'amount' => $c->get_amount(), 'usage_count' => $c->get_usage_count(), 'usage_limit' => $c->get_usage_limit(), 'expiry_date' => $c->get_date_expires() ? $c->get_date_expires()->date('Y-m-d') : null, 'free_shipping' => $c->get_free_shipping(), 'minimum_amount' => $c->get_minimum_amount(), 'maximum_amount' => $c->get_maximum_amount()];
}
// =========================================================
// PRODUCT ATTRIBUTES
// =========================================================
public static function getProductAttributes(array $args): array {
$taxonomies = wc_get_attribute_taxonomies();
return ['attributes' => array_map(fn($a) => ['id' => $a->attribute_id, 'name' => $a->attribute_label, 'slug' => 'pa_' . $a->attribute_name, 'type' => $a->attribute_type, 'order_by' => $a->attribute_orderby], $taxonomies)];
}
public static function createProductAttribute(array $args): array {
if (empty($args['name'])) return ['error' => 'name is required'];
$slug = $args['slug'] ?? sanitize_title($args['name']);
$data = ['attribute_label' => $args['name'], 'attribute_name' => $slug, 'attribute_type' => $args['type'] ?? 'select', 'attribute_orderby' => $args['order_by'] ?? 'menu_order', 'attribute_public' => (int)($args['is_visible'] ?? 1)];
$id = wc_create_attribute($data);
if (is_wp_error($id)) return ['error' => $id->get_error_message()];
return ['id' => $id, 'slug' => 'pa_' . $slug, 'name' => $args['name'], 'created' => true];
}
public static function getAttributeTerms(array $args): array {
if (empty($args['attribute_id']) && empty($args['attribute_slug'])) return ['error' => 'attribute_id or attribute_slug required'];
if (!empty($args['attribute_id'])) {
$attr = wc_get_attribute((int)$args['attribute_id']);
if (!$attr) return ['error' => 'Attribute not found'];
$taxonomy = 'pa_' . $attr->slug;
} else {
$taxonomy = strpos($args['attribute_slug'], 'pa_') === 0 ? $args['attribute_slug'] : 'pa_' . $args['attribute_slug'];
}
$terms = get_terms(['taxonomy' => $taxonomy, 'hide_empty' => false]);
if (is_wp_error($terms)) return ['error' => $terms->get_error_message()];
return ['terms' => array_map(fn($t) => ['id' => $t->term_id, 'name' => $t->name, 'slug' => $t->slug, 'count' => $t->count], $terms)];
}
public static function setProductAttributes(array $args): array {
if (empty($args['product_id'])) return ['error' => 'product_id is required'];
$product = wc_get_product((int)$args['product_id']);
if (!$product) return ['error' => "Product not found: {$args['product_id']}"];
$attributes = [];
foreach ((array)($args['attributes'] ?? []) as $attr_data) {
$taxonomy = 'pa_' . ltrim($attr_data['slug'] ?? $attr_data['name'] ?? '', 'pa_');
$attr = new \WC_Product_Attribute();
$attr->set_name($taxonomy);
$attr->set_options(array_map('intval', $attr_data['term_ids'] ?? []));
$attr->set_position($attr_data['position'] ?? 0);
$attr->set_visible((bool)($attr_data['is_visible'] ?? true));
$attr->set_variation((bool)($attr_data['is_variation'] ?? false));
$attributes[] = $attr;
}
$product->set_attributes($attributes);
$product->save();
return ['product_id' => $product->get_id(), 'attributes_set' => count($attributes)];
}
// =========================================================
// PRODUCT VARIATIONS
// =========================================================
public static function getProductVariations(array $args): array {
if (empty($args['product_id'])) return ['error' => 'product_id is required'];
$product = wc_get_product((int)$args['product_id']);
if (!$product || $product->get_type() !== 'variable') return ['error' => 'Variable product not found'];
$variations = [];
foreach ($product->get_children() as $var_id) {
$variation = wc_get_product($var_id);
if (!$variation) continue;
$variations[] = self::formatVariation($variation);
}
return ['product_id' => $product->get_id(), 'variations' => $variations];
}
public static function getVariation(array $args): array {
if (empty($args['variation_id'])) return ['error' => 'variation_id is required'];
$variation = wc_get_product((int)$args['variation_id']);
if (!$variation || $variation->get_type() !== 'variation') return ['error' => 'Variation not found'];
return self::formatVariation($variation);
}
public static function createVariation(array $args): array {
if (empty($args['product_id'])) return ['error' => 'product_id is required'];
$parent = wc_get_product((int)$args['product_id']);
if (!$parent) return ['error' => "Product not found: {$args['product_id']}"];
$variation = new \WC_Product_Variation();
$variation->set_parent_id($parent->get_id());
$variation->set_status($args['status'] ?? 'publish');
if (isset($args['regular_price'])) $variation->set_regular_price($args['regular_price']);
if (isset($args['sale_price'])) $variation->set_sale_price($args['sale_price']);
if (isset($args['sku'])) $variation->set_sku($args['sku']);
if (isset($args['stock_quantity'])) { $variation->set_manage_stock(true); $variation->set_stock_quantity((int)$args['stock_quantity']); }
if (!empty($args['attributes'])) {
$attrs = [];
foreach ($args['attributes'] as $key => $val) {
$attrs['attribute_pa_' . ltrim($key, 'pa_')] = sanitize_title($val);
}
$variation->set_attributes($attrs);
}
$id = $variation->save();
return ['variation_id' => $id, 'product_id' => $parent->get_id(), 'created' => true];
}
public static function updateVariation(array $args): array {
if (empty($args['variation_id'])) return ['error' => 'variation_id is required'];
$variation = wc_get_product((int)$args['variation_id']);
if (!$variation || $variation->get_type() !== 'variation') return ['error' => 'Variation not found'];
if (isset($args['regular_price'])) $variation->set_regular_price($args['regular_price']);
if (isset($args['sale_price'])) $variation->set_sale_price($args['sale_price']);
if (isset($args['status'])) $variation->set_status($args['status']);
if (isset($args['sku'])) $variation->set_sku($args['sku']);
if (isset($args['stock_quantity'])) { $variation->set_manage_stock(true); $variation->set_stock_quantity((int)$args['stock_quantity']); }
$variation->save();
return ['variation_id' => $variation->get_id(), 'updated' => true];
}
public static function deleteVariation(array $args): array {
if (empty($args['variation_id'])) return ['error' => 'variation_id is required'];
$variation = wc_get_product((int)$args['variation_id']);
if (!$variation || $variation->get_type() !== 'variation') return ['error' => 'Variation not found'];
$result = wp_delete_post($variation->get_id(), true);
return ['variation_id' => $args['variation_id'], 'deleted' => (bool)$result];
}
public static function batchUpdateVariations(array $args): array {
if (empty($args['product_id'])) return ['error' => 'product_id is required'];
$results = ['created' => [], 'updated' => [], 'deleted' => []];
foreach ((array)($args['create'] ?? []) as $data) {
$data['product_id'] = $args['product_id'];
$r = self::createVariation($data);
$results['created'][] = $r;
}
foreach ((array)($args['update'] ?? []) as $data) {
$results['updated'][] = self::updateVariation($data);
}
foreach ((array)($args['delete'] ?? []) as $vid) {
$results['deleted'][] = self::deleteVariation(['variation_id' => $vid]);
}
return $results;
}
private static function formatVariation(\WC_Product_Variation $v): array {
return ['id' => $v->get_id(), 'parent_id' => $v->get_parent_id(), 'sku' => $v->get_sku(), 'status' => $v->get_status(), 'price' => $v->get_price(), 'regular_price' => $v->get_regular_price(), 'sale_price' => $v->get_sale_price(), 'stock_quantity' => $v->get_stock_quantity(), 'attributes' => $v->get_variation_attributes()];
}
}