feat: add 16 missing WooCommerce tool methods (coupons, variations, attributes, customers)
This commit is contained in:
+270
-18
@@ -10,8 +10,12 @@ 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)];
|
||||
$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'];
|
||||
$query = new \WP_Query($qArgs);
|
||||
$products = [];
|
||||
@@ -23,8 +27,8 @@ 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']}");
|
||||
$product = wc_get_product((int)$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'])];
|
||||
}
|
||||
|
||||
@@ -35,34 +39,38 @@ class WooTools {
|
||||
$product->set_status($args['status'] ?? 'draft');
|
||||
if (!empty($args['description'])) $product->set_description($args['description']);
|
||||
if (!empty($args['sku'])) $product->set_sku($args['sku']);
|
||||
if (isset($args['stock_quantity'])) { $product->set_manage_stock(true); $product->set_stock_quantity((int) $args['stock_quantity']); }
|
||||
if (isset($args['stock_quantity'])) { $product->set_manage_stock(true); $product->set_stock_quantity((int)$args['stock_quantity']); }
|
||||
$id = $product->save();
|
||||
return ['id' => $id, 'name' => $args['name']];
|
||||
}
|
||||
|
||||
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']}");
|
||||
$product = wc_get_product((int)$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']);
|
||||
if (isset($args['status'])) $product->set_status($args['status']);
|
||||
if (isset($args['description'])) $product->set_description($args['description']);
|
||||
if (isset($args['stock_quantity'])) { $product->set_manage_stock(true); $product->set_stock_quantity((int) $args['stock_quantity']); }
|
||||
if (isset($args['stock_quantity'])) { $product->set_manage_stock(true); $product->set_stock_quantity((int)$args['stock_quantity']); }
|
||||
$product->save();
|
||||
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'];
|
||||
$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'];
|
||||
$orders = wc_get_orders($qArgs);
|
||||
return ['orders' => array_map(fn($o) => ['id' => $o->get_id(), 'status' => $o->get_status(), 'total' => $o->get_total(), 'currency' => $o->get_currency(), 'customer_id' => $o->get_customer_id(), 'date' => $o->get_date_created()->date('Y-m-d H:i:s'), 'items_count' => $o->get_item_count()], $orders)];
|
||||
}
|
||||
|
||||
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']}");
|
||||
$order = wc_get_order((int)$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()];
|
||||
@@ -71,14 +79,18 @@ 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']}");
|
||||
$order = wc_get_order((int)$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)];
|
||||
$qArgs = ['role' => 'customer', 'number' => (int)($args['per_page'] ?? 20), 'paged' => (int)($args['page'] ?? 1)];
|
||||
if (!empty($args['search'])) $qArgs['search'] = '*' . $args['search'] . '*';
|
||||
$query = new \WP_User_Query($qArgs);
|
||||
$customers = [];
|
||||
@@ -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));
|
||||
|
||||
$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()];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user