Campaign Revenue Report: order list shows wrong/old orders and "Re-Sync Revenue" destroys cached revenue
# [Bug] Campaign Revenue Report: order list shows wrong/old orders and "Re-Sync Revenue" destroys cached revenue β `Collection::map('intval')` passes the array key as the `intval()` base
## Environment
- FluentCRM: 3.1.0
- FluentCRM Pro: 3.1.0
- WordPress: 7.0
- WooCommerce: 10.8.1 (tested with both HPOS-authoritative and WP-posts/legacy authoritative; compatibility sync on)
- PHP: 8.3.30
- Database: MariaDB 11.4.10
- Theme: Astra child
- Note: we had to downgrade to FluentCRM 2.9.87 / Pro 2.9.86 to recover; 2.9.x does not have this bug.
## Summary
In `FluentCrm\App\Http\Controllers\CampaignAnalyticsController::getAttributedOrderIds()`, each source branch ends with a point-free `intval` map:
```php
// woo + HPOS branch
->pluck('order_id')->map('intval')->all();
// woo + legacy branch
->pluck('post_id')->map('intval')->all();
```
The wpFluent `Collection::map()` (`vendor/wpfluent/framework/src/WPFluent/Support/Collection.php`) is implemented as:
```php
public function map(callable $callback)
{
Β Β $keys = array_keys($this->items);
Β Β $items = array_map($callback, $this->items, $keys); // <-- passes (value, key)
Β Β return new static(array_combine($keys, $items));
}
```
Because `array_map` receives the keys array as a second source, the callback is invoked as `$callback($value, $key)`. With `'intval'` this becomes `intval($value, $key)`, so the array index is used as the integer base of `intval()`. Only index `0` (base 0 = auto-detect) is parsed correctly; every subsequent order ID is corrupted.
## Reproduction
```php
$ids = ['225345','224377','223655','223194','222906','222262'];
collect($ids)->map('intval')->all();
// Actual: Β [225345, 0, 0, 8, 42, 312]
// Expected: [225345, 224377, 223655, 223194, 222906, 222262]
```
Why: `intval('224377', 1) === 0` (base 1 invalid), `intval('223655', 2) === 0` (`2` invalid in base 2), `intval('223194', 3) === 8` (`'22'` in base 3), `intval('222906', 4) === 42`, `intval('222262', 5) === 312`.
Real-world: open a campaign Revenue Report (or press Re-Sync Revenue) for a campaign with many attributed orders.
## Impact
-
Revenue Report order list (`getRevenueReport`): only the newest attributed order renders correctly. The rest become invalid IDs (`0` β skipped) or coincidentally-valid ancient order IDs (e.g. `8`, `42`) that are then shown as unrelated, years-old orders. The `total` counter stays correct (array length), which masks the bug.
-
"Re-Sync Revenue" (`getRevenueReSyncReport` β `reSyncSourceRevenue`) iterates these corrupted IDs through `wc_get_order()`, sums a near-random subset, and overwrites the cached `_campaign_revenue` meta with a wrong (much smaller) value. Pressing Re-Sync therefore permanently destroys a campaign's historical revenue figure. (In our case a campaign dropped from 2,350,771 to 107,903.)
The underlying `_fc_cid` attribution data is intact in both `postmeta` and `wc_orders_meta`; only the in-memory ID list is corrupted by the map call, so the damage is fully recoverable once the code is fixed.
## Suggested fix
Use an explicit single-argument closure so the key is not passed as a base:
```php
->pluck('post_id')->map(fn($v) => (int) $v)->all();
```
More broadly, consider making `Collection::map()` not forward keys to callbacks that are not key-aware (`intval`, `strval`, `floatval`, etc. are common point-free callables that silently break under a `(value, key)` invocation).
## Secondary observations (same feature; lower priority)
- `getAttributedOrderIds()` (woo + HPOS) reads `wc_orders_meta` exclusively, with no `postmeta` fallback and no sync-completeness guard. During an in-progress HPOS backfill this returns a partial set, compounding the issue above.
- `reSyncSourceRevenue()` switched to paid-net semantics (only `wc_get_is_paid_statuses()`, refunds subtracted) versus the v2.9.x all-status sum. This silently changes historical numbers after upgrade (e.g. cancelled/on-hold orders that used to count no longer do). Worth calling out in the release notes / migration guide.
Thanks!