__('Shipment Cost Reports', 'ai-eshop-optimizer'), 'description' => __('Per-order shipping margin analysis. Catches under-priced shipping zones, sub-par carrier mixes, and lost-margin SKUs you did not know you were absorbing — the single biggest hidden cost on most WooCommerce stores.', 'ai-eshop-optimizer'), 'bullets' => array( __('Per-order P&L: shipping fees charged vs the free-shipping value you absorbed, broken down by zone, weight, and method.', 'ai-eshop-optimizer'), __('Carrier comparison — see which courier is winning on price for which zone.', 'ai-eshop-optimizer'), __('CSV export ready for the accountant.', 'ai-eshop-optimizer'), ), ); } public function register_hooks() { add_action('wp_ajax_aieo_dmm_shipping_pnl', array($this, 'ajax_shipping_pnl')); add_action('wp_ajax_aieo_dmm_shipping_pnl_export', array($this, 'ajax_shipping_pnl_export')); } public function render_subtab() { $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION); $ajax = admin_url('admin-ajax.php'); $this->render_retrospective_scan_card($nonce, $ajax); $this->render_shipping_pnl_card($nonce, $ajax); } private function render_retrospective_scan_card($nonce, $ajax) { list($aieo_scr_open, $aieo_scr_close) = AIEO_DMM_UI::card(array( 'anchor' => 'shipment-cost-reports-free-shipping-cost-retrospective-analysis', 'icon' => 'truck', 'title' => __('Cost of past free shipping', 'ai-eshop-optimizer'), 'description' => __('Scans past orders where shipping was free, re-runs WooCommerce shipping calculation (using each order\'s shipping postcode + cart weight + line subtotal — same inputs as table-rate shipping at checkout) and records the rate the customer would have been charged. That is a re-quote off your own shipping table, not the courier\'s invoice, so treat it as the value you gave away rather than a bill you paid. Lets you size historical free-shipping campaigns. The same job runs nightly via cron; this button is for ad-hoc analysis.', 'ai-eshop-optimizer'), 'footnote' => __('You can close this page once the run starts — the daily cron will pick up where you stopped at 03:30 the next morning.', 'ai-eshop-optimizer'), )); echo $aieo_scr_open; $aieo_scr_days = \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-fs-cost-days', 'label' => __('Look-back window (days):', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Ui::input('', '90', array('id' => 'aieo-fs-cost-days', 'type' => 'number', 'min' => 1, 'max' => 9999, 'step' => 1, 'xs' => true)), )); echo '
' . $aieo_scr_days . '
'; $aieo_scr_job = \Rooster\Admin\States::job(array( 'id' => 'aieo-fs-cost-job', 'state' => 'idle', 'run' => __('Run now', 'ai-eshop-optimizer'), 'run_attrs' => array('id' => 'aieo-fs-cost-run'), )); echo $aieo_scr_job; echo $aieo_scr_close; echo ''; } private function render_shipping_pnl_card($nonce, $ajax) { list($aieo_scr_open, $aieo_scr_close) = AIEO_DMM_UI::card(array( 'anchor' => 'shipment-cost-reports-shipping-p-l-by-carrier-zone-monthly', 'icon' => 'table', 'title' => __('Shipping profit and loss by carrier and zone', 'ai-eshop-optimizer'), 'description' => __('The shipping money that moved through each method per month, broken down by shipping zone (mainland / islands / EU / rest of world). "Customer revenue" = shipping fees customers paid you on paid orders. "Free shipping cost" = the carrier rate you absorbed for free-shipping orders (estimated by the resolver above). "Shipping revenue + absorbed value" = both summed. The "Free %" column tells you what fraction of that total was eaten by free-shipping promos.', 'ai-eshop-optimizer'), 'flush' => true, )); echo $aieo_scr_open; echo AIEO_DMM_UI::notice(esc_html__('This card does not show what the couriers invoiced you.', 'ai-eshop-optimizer'), 'warning'); echo \Rooster\Admin\Guidance::text(__('Its revenue side is what the customer paid and its free-shipping side is a re-computed quote, so the total tracks your shipping price list, not the courier\'s. For the invoiced figure go to Fulfillment → Delivery Statistics, which reads the reconciled per-voucher cost written by the month-end carrier invoice import.', 'ai-eshop-optimizer')); echo '
'; $aieo_scr_month_options = array( 'current' => __('Current month', 'ai-eshop-optimizer'), 'last' => __('Last month', 'ai-eshop-optimizer'), ); foreach (array(3, 6, 12, 24, 36) as $m) { $aieo_scr_month_options[(string) $m] = sprintf(_n('Last %d month', 'Last %d months', $m, 'ai-eshop-optimizer'), $m); } $aieo_scr_months = \Rooster\Admin\Ui::select('', $aieo_scr_month_options, '12', array('id' => 'aieo-sp-months')); echo \Rooster\Admin\Ui::filter(__('Period:', 'ai-eshop-optimizer'), $aieo_scr_months, 'aieo-sp-months'); $aieo_scr_group = \Rooster\Admin\Ui::select('', array( 'month_method_zone' => __('Month × Method × Zone', 'ai-eshop-optimizer'), 'month_method' => __('Month × Method', 'ai-eshop-optimizer'), 'month_zone' => __('Month × Zone', 'ai-eshop-optimizer'), 'method_zone' => __('Method × Zone (all months)', 'ai-eshop-optimizer'), 'month' => __('Month total', 'ai-eshop-optimizer'), 'method' => __('Method total', 'ai-eshop-optimizer'), 'zone' => __('Zone total', 'ai-eshop-optimizer'), ), 'month_method_zone', array('id' => 'aieo-sp-group')); echo \Rooster\Admin\Ui::filter(__('Group by:', 'ai-eshop-optimizer'), $aieo_scr_group, 'aieo-sp-group'); $aieo_scr_all = array('' => __('All', 'ai-eshop-optimizer')); echo \Rooster\Admin\Ui::filter(__('Method:', 'ai-eshop-optimizer'), \Rooster\Admin\Ui::select('', $aieo_scr_all, '', array('id' => 'aieo-sp-method')), 'aieo-sp-method'); echo \Rooster\Admin\Ui::filter(__('Zone:', 'ai-eshop-optimizer'), \Rooster\Admin\Ui::select('', $aieo_scr_all, '', array('id' => 'aieo-sp-zone')), 'aieo-sp-zone'); echo '
' . '' . '' . '' . '' . '
'; echo '
'; echo '
'; \Rooster\Admin\States::enqueue(); echo \Rooster\Admin\States::result_head('', array('id' => 'aieo-sp-result', 'attrs' => array('hidden' => true))); $aieo_scr_states = array( 'loading' => \Rooster\Admin\States::state('loading'), 'setup' => \Rooster\Admin\States::state('not-set-up', array('title' => __('HPOS (wp_wc_orders) is required for the shipping P&L view. Enable WooCommerce → Settings → Advanced → Features → "High-Performance order storage".', 'ai-eshop-optimizer'))), 'first' => \Rooster\Admin\States::state('first-use', array('title' => __('No shipping data for this window. If you just deployed, run the retrospective scan above so historical free-shipping rows have method + zone populated.', 'ai-eshop-optimizer'))), 'none' => \Rooster\Admin\States::state('no-results', array('clear' => '#')), 'failed' => \Rooster\Admin\States::state('failed', array('text' => __('Unknown error.', 'ai-eshop-optimizer'))), ); foreach ($aieo_scr_states as $aieo_scr_kind => $aieo_scr_state) { echo '
' . $aieo_scr_state . '
'; } $aieo_scr_row_one = _n('%s row', '%s rows', 1, 'ai-eshop-optimizer'); $aieo_scr_row_many = _n('%s row', '%s rows', 2, 'ai-eshop-optimizer'); AIEO_Table::table_assets(); echo ''; $aieo_money_cfg = wp_json_encode(array( 'symbol' => html_entity_decode(get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8'), 'pos' => get_option('woocommerce_currency_pos', 'left'), 'dec' => wc_get_price_decimal_separator(), 'thou' => wc_get_price_thousand_separator(), 'nd' => wc_get_price_decimals(), ), JSON_HEX_TAG); if (!is_string($aieo_money_cfg) || $aieo_money_cfg === '') { $aieo_money_cfg = '{"symbol":"","pos":"left","dec":".","thou":",","nd":2}'; } echo ''; echo $aieo_scr_close; } public function ajax_shipping_pnl() { AIEO_DMM::verify_ajax_request(); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(array('message' => __('Insufficient permissions.', 'ai-eshop-optimizer'))); } $payload = $this->compute_shipping_pnl($_POST); $payload['table'] = $this->sp_table($payload); wp_send_json_success($payload); } private function sp_table($payload) { $cols = isset($payload['columns']) && is_array($payload['columns']) ? $payload['columns'] : array(); $numeric = array('currency', 'currency_strong', 'integer', 'percent'); $columns = array(); $has = array_flip(array_map('strval', array_column($cols, 'key'))); foreach ($cols as $i => $col) { $num = in_array($col['fmt'], $numeric, true); $fig = array(); if ('currency' === $col['fmt'] || 'currency_strong' === $col['fmt']) { $fig = array('format' => 'money'); } elseif ('percent' === $col['fmt']) { $fig = array('total' => isset($has['free_orders'], $has['total_orders']) && 'pct_free' === $col['key'] ? array('ratio' => array('free_orders', 'total_orders'), 'percent' => true, 'decimals' => 1) : 'none'); } $columns[] = array( 'key' => (string) $col['key'], 'label' => (string) $col['label'], 'type' => $num ? 'num' : 'text', 'sort_type' => $num ? 'number' : 'text', 'always' => 0 === $i, ) + $fig; } $rows = array(); foreach ((array) ($payload['rows'] ?? array()) as $i => $r) { $row = array('_id' => (string) ($i + 1), '_s' => array()); foreach ($cols as $col) { $k = (string) $col['key']; $v = $r[$k] ?? ''; if ('currency' === $col['fmt'] || 'currency_strong' === $col['fmt']) { $row[$k] = AIEO_Table_Columns::money($v); if ('currency_strong' === $col['fmt']) { $row[$k]['html'] = '' . $row[$k]['html'] . ''; } $row['_s'][$k] = (float) $v; } elseif ('integer' === $col['fmt']) { $row[$k] = number_format_i18n((int) $v); $row['_s'][$k] = (int) $v; } elseif ('percent' === $col['fmt']) { $pct = (float) $v; $row[$k] = array( 'value' => number_format_i18n($pct * 100, 1) . ' %', 'tone' => $pct > 0.30 ? 'destructive' : ($pct > 0.15 ? 'warning' : 'success'), ); $row['_s'][$k] = $pct; } else { $row[$k] = (string) $v; $row['_s'][$k] = (string) $v; } } $rows[] = $row; } return AIEO_Table::local_answer('aieo-sp-table', $columns, $rows, array( 'prefix' => 'sp_', 'size' => 0, 'caption' => __('Shipping P&L', 'ai-eshop-optimizer'), 'chooser' => true, )); } public function ajax_shipping_pnl_export() { AIEO_DMM::verify_ajax_request(); if (!current_user_can('manage_woocommerce')) { wp_die(esc_html__('Insufficient permissions.', 'ai-eshop-optimizer')); } $payload = $this->compute_shipping_pnl($_GET); if (!class_exists('\Rooster\Admin\Export') && function_exists('rooster_admin_builders')) { rooster_admin_builders(); } if (!class_exists('\Rooster\Admin\Export')) { wp_die(esc_html__('XLSX writer unavailable.', 'ai-eshop-optimizer')); } $columns = array(); foreach ($payload['columns'] as $col) { $format = null; if ($col['fmt'] === 'currency' || $col['fmt'] === 'currency_strong') { $format = 'money'; } elseif ($col['fmt'] === 'integer') { $format = array('decimals' => 0); } elseif ($col['fmt'] === 'percent') { $format = array('decimals' => 2); } $columns[$col['key']] = null === $format ? array('label' => $col['label']) : array('label' => $col['label'], 'format' => $format); } $total = array(); foreach (array_keys($columns) as $i => $k) { if ($i === 0) { $total[$k] = __('TOTAL', 'ai-eshop-optimizer'); } elseif (isset($payload['totals'][$k])) { $total[$k] = $payload['totals'][$k]; } elseif ($k === 'pct_free') { $inv = (float) $payload['totals']['gross_shipping_value']; $total[$k] = $inv > 0 ? ((float) $payload['totals']['free_cost'] / $inv) : 0; } } $rows = $payload['rows']; $rows[] = $total; $months = (int) $payload['filters']['months']; \Rooster\Admin\Export::xlsx('shipping-pnl-' . $months . 'mo-' . gmdate('Y-m-d') . '.xlsx', array(array('name' => 'Shipping P&L', 'columns' => $columns, 'rows' => $rows))); exit; } private function period_window_sql($period_value, $date_col) { $period = (string) $period_value; $cur_bounds = function_exists('aieo_site_month_bounds_utc') ? aieo_site_month_bounds_utc('current') : array('start' => gmdate('Y-m-01 00:00:00'), 'end' => gmdate('Y-m-01 00:00:00', strtotime('+1 month'))); $last_bounds = function_exists('aieo_site_month_bounds_utc') ? aieo_site_month_bounds_utc('last') : array('start' => gmdate('Y-m-01 00:00:00', strtotime('-1 month')), 'end' => gmdate('Y-m-01 00:00:00')); if ($period === 'current') { return array( 'sql' => "{$date_col} >= '{$cur_bounds['start']}' AND {$date_col} < '{$cur_bounds['end']}'", 'tag' => 'current-month', 'months_for_compat' => 1, ); } if ($period === 'last') { return array( 'sql' => "{$date_col} >= '{$last_bounds['start']}' AND {$date_col} < '{$last_bounds['end']}'", 'tag' => 'last-month', 'months_for_compat' => 1, ); } $n = max(1, min(60, (int) $period)); return array( 'sql' => "{$date_col} >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL {$n} MONTH)", 'tag' => $n . 'mo', 'months_for_compat' => $n, ); } private function compute_shipping_pnl($input) { global $wpdb; $prefix = $wpdb->prefix; $period_in = isset($input['months']) ? sanitize_text_field((string) $input['months']) : '12'; $period = $this->period_window_sql($period_in, 'o.date_created_gmt'); $months = $period['months_for_compat']; $group = isset($input['group_by']) ? sanitize_key((string) $input['group_by']) : 'month_method_zone'; $valid = array('month_method_zone','month_method','month_zone','method_zone','month','method','zone'); if (!in_array($group, $valid, true)) { $group = 'month_method_zone'; } $f_method = isset($input['method']) ? sanitize_text_field((string) $input['method']) : ''; $f_zone = isset($input['zone']) ? sanitize_text_field((string) $input['zone']) : ''; $orders_tbl = $prefix . 'wc_orders'; $opdata_tbl = $prefix . 'wc_order_operational_data'; $items_tbl = $prefix . 'woocommerce_order_items'; $itemmeta_tbl = $prefix . 'woocommerce_order_itemmeta'; $zones_tbl = $prefix . 'woocommerce_shipping_zones'; $zonemeth_tbl = $prefix . 'woocommerce_shipping_zone_methods'; $attr_tbl = $prefix . 'aieo_dmm_offer_attributions'; $hpos_ok = AIEO_Order_Store::hpos_readable(); $unmatched_zone = __('Unmatched zone', 'ai-eshop-optimizer'); $unknown_method = __('Unknown method', 'ai-eshop-optimizer'); $empty = function() use ($months, $group, $f_method, $f_zone) { return array( 'rows' => array(), 'columns' => array(), 'totals' => array('paid_orders'=>0,'free_orders'=>0,'total_orders'=>0,'shipping_revenue'=>0,'free_cost'=>0,'free_pending'=>0,'gross_shipping_value'=>0,'pct_free'=>0), 'methods' => array(), 'zones' => array(), 'filters' => array('months'=>$months,'group_by'=>$group,'method'=>$f_method,'zone'=>$f_zone), 'message' => '', ); }; if (!$hpos_ok) { $r = $empty(); $r['message'] = __('HPOS (wp_wc_orders) is required for the shipping P&L view. Enable WooCommerce → Settings → Advanced → Features → "High-Performance order storage".', 'ai-eshop-optimizer'); return $r; } $paid_sql = $wpdb->prepare( "SELECT DATE_FORMAT(o.date_created_gmt, %s) AS month_key, COALESCE(z.zone_name, %s) AS zone_name, COALESCE(NULLIF(oi.order_item_name, ''), %s) AS method_label, COUNT(DISTINCT o.id) AS paid_orders, COALESCE(SUM(od.shipping_total_amount), 0) AS shipping_revenue, 0 AS free_orders, 0 AS free_cost, 0 AS free_pending FROM {$orders_tbl} o INNER JOIN {$opdata_tbl} od ON od.order_id = o.id LEFT JOIN {$items_tbl} oi ON oi.order_id = o.id AND oi.order_item_type = 'shipping' LEFT JOIN {$itemmeta_tbl} mi ON mi.order_item_id = oi.order_item_id AND mi.meta_key = 'instance_id' LEFT JOIN {$zonemeth_tbl} zm ON zm.instance_id = CAST(mi.meta_value AS UNSIGNED) LEFT JOIN {$zones_tbl} z ON z.zone_id = zm.zone_id WHERE o.status IN ('wc-processing','wc-completed','wc-on-hold','processing','completed','on-hold') AND o.type = 'shop_order' AND od.shipping_total_amount > 0 AND ({$period['sql']}) GROUP BY month_key, zone_name, method_label", '%Y-%m', $unmatched_zone, $unknown_method ); $free_sql = $wpdb->prepare( "SELECT DATE_FORMAT(o.date_created_gmt, %s) AS month_key, COALESCE(NULLIF(a.shipping_zone, ''), %s) AS zone_name, COALESCE(NULLIF(a.shipping_method, ''), %s) AS method_label, 0 AS paid_orders, 0 AS shipping_revenue, COUNT(*) AS free_orders, COALESCE(SUM(a.actual_cost), 0) AS free_cost, SUM(a.actual_cost IS NULL) AS free_pending FROM {$attr_tbl} a INNER JOIN {$orders_tbl} o ON o.id = a.order_id WHERE a.offer_type = 'free_shipping' AND o.type = 'shop_order' AND ({$period['sql']}) GROUP BY month_key, zone_name, method_label", '%Y-%m', $unmatched_zone, $unknown_method ); $select_dims = array(); $group_dims = array(); $columns = array(); if (in_array($group, array('month_method_zone','month_method','month_zone','month'), true)) { $select_dims[] = 'month_key'; $group_dims[] = 'month_key'; $columns[] = array('key'=>'month_key', 'label'=>__('Month', 'ai-eshop-optimizer'), 'fmt'=>'string'); } if (in_array($group, array('month_method_zone','month_method','method_zone','method'), true)) { $select_dims[] = 'method_label'; $group_dims[] = 'method_label'; $columns[] = array('key'=>'method_label', 'label'=>__('Shipping method', 'ai-eshop-optimizer'), 'fmt'=>'string'); } if (in_array($group, array('month_method_zone','month_zone','method_zone','zone'), true)) { $select_dims[] = 'zone_name'; $group_dims[] = 'zone_name'; $columns[] = array('key'=>'zone_name', 'label'=>__('Zone', 'ai-eshop-optimizer'), 'fmt'=>'string'); } $columns[] = array('key'=>'total_orders', 'label'=>__('Orders', 'ai-eshop-optimizer'), 'fmt'=>'integer'); $columns[] = array('key'=>'free_orders', 'label'=>__('Free orders', 'ai-eshop-optimizer'), 'fmt'=>'integer'); $columns[] = array('key'=>'shipping_revenue', 'label'=>__('Customer revenue', 'ai-eshop-optimizer'), 'fmt'=>'currency'); $columns[] = array('key'=>'free_cost', 'label'=>__('Free shipping cost', 'ai-eshop-optimizer'),'fmt'=>'currency'); $columns[] = array('key'=>'free_pending', 'label'=>__('Pending cost', 'ai-eshop-optimizer'), 'fmt'=>'integer'); $columns[] = array('key'=>'gross_shipping_value', 'label'=>__('Shipping revenue + absorbed value', 'ai-eshop-optimizer'),'fmt'=>'currency_strong'); $columns[] = array('key'=>'pct_free', 'label'=>__('Free %', 'ai-eshop-optimizer'), 'fmt'=>'percent'); $select_csv = empty($select_dims) ? "''" : implode(', ', $select_dims); $group_csv = empty($group_dims) ? "''" : implode(', ', $group_dims); $outer_where = array(); $outer_params = array(); if ($f_method !== '') { $outer_where[] = 'method_label = %s'; $outer_params[] = $f_method; } if ($f_zone !== '') { $outer_where[] = 'zone_name = %s'; $outer_params[] = $f_zone; } $outer_where_sql = $outer_where ? ('WHERE ' . implode(' AND ', $outer_where)) : ''; $order_sql = 'ORDER BY '; $order_parts = array(); if (in_array('month_key', $group_dims, true)) { $order_parts[] = 'month_key DESC'; } if (in_array('method_label', $group_dims, true)) { $order_parts[] = 'method_label ASC'; } if (in_array('zone_name', $group_dims, true)) { $order_parts[] = 'zone_name ASC'; } $order_sql .= empty($order_parts) ? 'gross_shipping_value DESC' : implode(', ', $order_parts); $final_sql = "SELECT {$select_csv}, SUM(paid_orders) AS paid_orders, SUM(free_orders) AS free_orders, SUM(paid_orders + free_orders) AS total_orders, SUM(shipping_revenue) AS shipping_revenue, SUM(free_cost) AS free_cost, SUM(free_pending) AS free_pending, SUM(shipping_revenue + free_cost) AS gross_shipping_value FROM ( {$paid_sql} UNION ALL {$free_sql} ) sub {$outer_where_sql} " . (empty($group_dims) ? '' : "GROUP BY {$group_csv}") . " {$order_sql}"; $prepared = $outer_params ? $wpdb->prepare($final_sql, $outer_params) : $final_sql; $rows_raw = $wpdb->get_results($prepared, ARRAY_A); if (!is_array($rows_raw)) { $rows_raw = array(); } $rows = array(); $totals = array( 'paid_orders' => 0, 'free_orders' => 0, 'total_orders' => 0, 'shipping_revenue' => 0.0, 'free_cost' => 0.0, 'free_pending' => 0, 'gross_shipping_value' => 0.0, ); foreach ($rows_raw as $r) { $invoice = (float) $r['gross_shipping_value']; $free = (float) $r['free_cost']; $row = array( 'paid_orders' => (int) $r['paid_orders'], 'free_orders' => (int) $r['free_orders'], 'total_orders' => (int) $r['total_orders'], 'shipping_revenue' => round((float) $r['shipping_revenue'], 2), 'free_cost' => round($free, 2), 'free_pending' => (int) $r['free_pending'], 'gross_shipping_value' => round($invoice, 2), 'pct_free' => $invoice > 0 ? round($free / $invoice, 4) : 0, ); if (isset($r['month_key'])) { $row['month_key'] = $r['month_key']; } if (isset($r['method_label'])) { $row['method_label'] = $r['method_label']; } if (isset($r['zone_name'])) { $row['zone_name'] = $r['zone_name']; } $rows[] = $row; $totals['paid_orders'] += (int) $r['paid_orders']; $totals['free_orders'] += (int) $r['free_orders']; $totals['total_orders'] += (int) $r['total_orders']; $totals['shipping_revenue'] += (float) $r['shipping_revenue']; $totals['free_cost'] += $free; $totals['free_pending'] += (int) $r['free_pending']; $totals['gross_shipping_value'] += $invoice; } foreach (array('shipping_revenue','free_cost','gross_shipping_value') as $k) { $totals[$k] = round($totals[$k], 2); } $totals['pct_free'] = $totals['gross_shipping_value'] > 0 ? round($totals['free_cost'] / $totals['gross_shipping_value'], 4) : 0; $methods = $wpdb->get_col("SELECT DISTINCT method_label FROM ( ({$paid_sql}) UNION ALL ({$free_sql}) ) m WHERE method_label <> '' ORDER BY method_label ASC"); if (!is_array($methods)) { $methods = array(); } $zones = $wpdb->get_col("SELECT DISTINCT zone_name FROM ( ({$paid_sql}) UNION ALL ({$free_sql}) ) z WHERE zone_name <> '' ORDER BY zone_name ASC"); if (!is_array($zones)) { $zones = array(); } return array( 'rows' => $rows, 'columns' => $columns, 'totals' => $totals, 'methods' => $methods, 'zones' => $zones, 'filters' => array('months'=>$months,'group_by'=>$group,'method'=>$f_method,'zone'=>$f_zone), ); } }