init(); }, 90); } protected function init() { $drivers = DriverManager::getEnabled(); if (!$drivers) { return false; } // Boot only enabled drivers (available + toggled on in settings) foreach ($drivers as $driver) { $driver->register(); $driver->registerAutomationTrigger(); } // Expose abandon cart availability to the frontend via fcAdmin vars add_filter('fluent_crm/admin_vars', function ($vars) { $vars['has_abandon_carts'] = true; $vars['can_read_abandon_carts'] = PermissionManager::currentUserCan('fcrm_read_funnels'); return $vars; }); // Add Abandoned Carts as a sub-item under the Reports dropdown add_filter('fluent_crm/menu_items', function ($items) { if (!PermissionManager::currentUserCan('fcrm_read_funnels')) { return $items; } $urlBase = fluentcrm_menu_url_base(); $hasReportsMenu = false; foreach ($items as &$item) { if (!empty($item['key']) && $item['key'] === 'reports' && isset($item['sub_items'])) { $item['sub_items'][] = [ 'key' => 'reports_abandoned_carts', 'label' => __('Abandoned Carts', 'fluent-crm'), 'permalink' => $urlBase . 'reports?tab=abandoned_carts', 'icon' => '', ]; $hasReportsMenu = true; break; } } unset($item); if (!$hasReportsMenu) { $items[] = [ 'key' => 'reports', 'label' => __('Reports', 'fluent-crm'), 'permalink' => $urlBase . 'reports?tab=abandoned_carts', 'layout_class' => 'fc_1_col_menu', 'sub_items' => [ [ 'key' => 'reports_abandoned_carts', 'label' => __('Abandoned Carts', 'fluent-crm'), 'permalink' => $urlBase . 'reports?tab=abandoned_carts', 'icon' => '', ] ], ]; } return $items; }); // Run the runner add_action('fluentcrm_scheduled_five_minute_tasks', [$this, 'maybeRunAbRunner'], 999); add_action('fluentcrm_scheduled_daily_tasks', [$this, 'markOldCartsAsLost'], 10); add_filter('fluent_crm/sales_stats', function ($stats) { [$recoveredCount, $recoveredRevenue] = AbCartHelper::getCountAndSumByStatus('recovered', [], 'recovered_at'); if (!$recoveredRevenue) { return $stats; } $dateRange = [ gmdate('Y-m-01 00:00:00', current_time('timestamp')), gmdate('Y-m-t 23:59:59', current_time('timestamp')) ]; [$thisMonth, $thisMonthRevenue] = AbCartHelper::getCountAndSumByStatus('recovered', $dateRange, 'recovered_at'); $stats[] = [ 'title' => __('Cart Recovered (This Month)', 'fluent-crm'), 'content' => DriverManager::formatPrice($thisMonthRevenue) ]; $stats[] = [ 'title' => __('Cart Recovered (All Time)', 'fluent-crm'), 'content' => DriverManager::formatPrice($recoveredRevenue) ]; return $stats; }); } public function maybeRunAbRunner() { static $counter = 0; if (!$counter) { if (fluentCrmIsTimeOut(30)) { return false; } // It's the first time. Check if there has any runner or not $lastRunner = fluentCrmGetOptionCache('__fc_ab_runner'); if ($lastRunner) { $timeElapsed = time() - $lastRunner; if ($timeElapsed < 50) { return false; } fluentCrmSetOptionCache('__fc_ab_runner', null, 50); } } fluentCrmSetOptionCache('__fc_ab_runner', time(), 50); $counter = $counter + 1; // Get Draft Carts that need to be abandoned $settings = AbCartHelper::getSettings(); $cutMinutes = Arr::get($settings, 'capture_after_minutes', 5); $cutDateTime = gmdate('Y-m-d H:i:s', current_time('timestamp') - ($cutMinutes * 60)); $enabledSlugs = DriverManager::getEnabledSlugs(); if (!$enabledSlugs) { fluentCrmSetOptionCache('__fc_ab_runner', null, 50); return false; } $abCarts = AbandonCartModel::where('status', 'draft') ->whereIn('provider', $enabledSlugs) ->where('updated_at', '<=', $cutDateTime) ->orderBy('id', 'DESC') ->limit(10) ->get(); if ($abCarts->isEmpty()) { fluentCrmSetOptionCache('__fc_ab_runner', null, 50); return false; } foreach ($abCarts as $abCart) { (new AbandonCartRunner())->runAbandonCart($abCart); } fluentCrmSetOptionCache('__fc_ab_runner', null, 50); if (!fluentCrmIsTimeOut(40)) { $this->maybeRunAbRunner(); } return true; } public function markOldCartsAsLost() { $settings = AbCartHelper::getSettings(); $cutDays = Arr::get($settings, 'lost_cart_days', 15); $cutDateTime = gmdate('Y-m-d H:i:s', current_time('timestamp') - ($cutDays * 86400)); AbandonCartModel::where('status', 'processing') ->where('created_at', '<=', $cutDateTime) ->update(['status' => 'lost']); } }