This project queries orders with wc_get_orders(), aggregates the numbers in plain
PHP, and only then hands a small, pre-summarized dataset to wp_ai_client_prompt() to
turn into a narrative. That ordering matters: the model narrates a report your code
already computed correctly, it never does the arithmetic itself.
WordPress 7.0+ with an AI provider configured. WooCommerce with completed order history
covering more than a few days, a single day of test data won’t produce a meaningful
trend to summarize. This lesson doesn’t depend on WooCommerce 10.9’s abilities, it uses
wc_get_orders() directly.
Step 1: Aggregating orders in PHP, not in the prompt
// File: my-plugin.php
function my_plugin_build_sales_report( $days ) {
$orders = wc_get_orders( array(
'status' => array( 'completed', 'processing' ),
'date_created' => '>' . ( time() - $days * DAY_IN_SECONDS ),
'limit' => -1,
) );
$total_revenue = 0;
$order_count = count( $orders );
$product_counts = array();
foreach ( $orders as $order ) {
$total_revenue += (float) $order->get_total();
foreach ( $order->get_items() as $item ) {
$name = $item->get_name();
$product_counts[ $name ] = ( $product_counts[ $name ] ?? 0 ) + $item->get_quantity();
}
}
arsort( $product_counts );
return array(
'days' => $days,
'order_count' => $order_count,
'total_revenue' => round( $total_revenue, 2 ),
'top_products' => array_slice( $product_counts, 0, 5, true ),
);
}
Every number in this report is computed with plain PHP arithmetic over real order data, nothing here depends on the AI model getting math right.
Step 2: Registering the ability, redacting before the AI call
// File: my-plugin.php
add_action( 'wp_abilities_api_init', function () {
wp_register_ability(
'my-plugin/summarize-sales-trends',
array(
'label' => __( 'Summarize sales trends', 'my-plugin' ),
'description' => __( 'Aggregates recent order data into totals and top products, then generates a plain-language summary. No customer names or emails are included.', 'my-plugin' ),
'category' => 'store-inventory',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'days' => array( 'type' => 'integer', 'default' => 7 ),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'report' => array( 'type' => 'object' ),
'summary' => array( 'type' => 'string' ),
),
),
'permission_callback' => function () {
return current_user_can( 'view_woocommerce_reports' );
},
'execute_callback' => function ( $input ) {
$report = my_plugin_build_sales_report( $input['days'] ?? 7 );
$prompt = sprintf(
"Summarize this WooCommerce sales data in three plain-language sentences, " .
"no customer names or emails are present, do not invent any: %s",
wp_json_encode( $report )
);
$summary = wp_ai_client_prompt( $prompt )->generate_text();
return array( 'report' => $report, 'summary' => trim( $summary ) );
},
'meta' => array(
'annotations' => array( 'readonly' => true ),
'mcp' => array( 'public' => false ),
),
)
);
} );
wc_get_orders() results carry billing names, emails, and addresses. This ability
never passes an order object itself to wp_ai_client_prompt(), only the aggregated
$report array, which by construction contains product names and numbers, nothing
customer-identifiable. Treat “did I aggregate first” as a mandatory check any time an
ability sends order-derived data to a third-party AI provider.
Note also that view_woocommerce_reports rather than manage_woocommerce is used
here, it’s the more precisely scoped capability for a read-only reporting feature,
letting you grant this to roles that shouldn’t necessarily be able to edit orders or
products.
Step 3: Returning both the numbers and the narrative
The output schema intentionally includes report alongside summary. Anyone reading
the AI-generated sentence can check it against the actual numbers in the same
response, rather than trusting the narrative blindly.
Test it
wp-env run cli wp eval '
$ability = wp_get_ability( "my-plugin/summarize-sales-trends" );
$result = $ability->execute( array( "days" => 30 ) );
echo $result["summary"] . "\n";
print_r( $result["report"] );
'
Cross-check the printed summary against the printed report, they should agree exactly.
On a busy store, thirty days of raw order data can be thousands of line items, well past what’s reasonable to hand a model even after light aggregation. Cap the date range sensibly, and if you need longer-range trends, aggregate into weekly or monthly buckets in PHP first rather than widening the raw dataset the prompt sees.
Recap
The sales-reporting assistant computes real totals and top products with
wc_get_orders() and plain PHP arithmetic, redacts customer-identifiable fields before
anything reaches an AI provider, and uses wp_ai_client_prompt() purely to phrase an
already-correct aggregate as a readable summary, returned alongside the raw numbers so
the narrative stays checkable.
Resources & further reading
- wc_get_orders() function reference, woocommerce.github.io
- WC_Order::get_items() reference, woocommerce.github.io
- PHP AI Client SDK repository, GitHub