addAutomations(); $this->addHooks(); SmartCodeRegister::push(); (new RevenueTracker())->init(); } public function addAutomations() { new OrderPaidTrigger(); new OrderShippedTrigger(); new OrderDeliveredTrigger(); new OrderRefundedTrigger(); new OrderCanceledTrigger(); // new OrderStatusChangedTrigger(); new SubscriptionExpiredTrigger(); //subscription activated new SubscriptionActivatedTrigger(); //subscription cancelled new SubscriptionCancelledTrigger(); //subscription renewed new SubscriptionRenewedTrigger(); //subscription end of term(completed) new SubscriptionEndOfTermTrigger(); // Goals new OrderSuccessBenchmark(); } public function addHooks() { add_filter('fluent_crm/get_import_driver_fluent_cart', [CartImporter::class, 'processUserDriver'], 10, 2); add_filter('fluent_crm/post_import_driver_fluent_cart', [CartImporter::class, 'importData'], 10, 3); add_filter('fluentcrm_ajax_options_fluent_cart_products', [$this, 'getProducts'], 10, 3); add_filter('fluentcrm_ajax_options_fluent_cart_product_categories', [$this, 'getProductCategories'], 10, 3); add_filter('fluentcrm_ajax_options_fluent_cart_subscription_products', [$this, 'getSubscriptionProducts'], 10, 3); add_filter('fluent_crm/funnel_icons', [$this, 'addCartIcon'], 10 , 1); add_filter('fluent_crm/purchase_history_fluent_cart', [$this, 'purchaseHistory'], 10, 2); add_filter('fluent_crm/smartcode_group_callback_cart_order', [SmartCodeParser::class, 'parseCartOrder'], 10, 4); add_filter('fluent_crm/smartcode_group_callback_cart_customer', [SmartCodeParser::class, 'parseCartCustomer'], 10, 4); // add_filter('fluent_crm/smartcode_group_callback_cart_transaction', [SmartCodeParser::class, 'parseCartTransaction'], 10, 4); add_filter('fluent_crm/smartcode_group_callback_cart_receipt', [SmartCodeParser::class, 'parseCartReceipt'], 10, 4); add_filter('fluentcrm_automation_condition_groups', array($this, 'addAutomationConditions'), 10, 2); add_filter('fluentcrm_automation_conditions_assess_fluent_cart', array($this, 'assessAutomationConditions'), 10, 3); // add_filter('fluentcrm_automation_conditions_assess_woo_order', array($this, 'assessAutomationOrderConditions'), 10, 5); } public function getProducts($items, $search, $ids) { return CartHelper::getFluentCartProducts($items, $search, $ids); } public function getProductCategories($items, $search, $ids) { return CartHelper::getFluentCartProductCategories($items, $search, $ids); } public function getSubscriptionProducts($items, $search, $ids) { return CartHelper::getFluentCartSubscriptionProducts($items, $search, $ids); } public function addCartIcon($icons) { $icons['fluentcart'] = [ 'svg' => '', ]; return $icons; } public function purchaseHistory($data, $subscriber) { $customer = Customer::where('email', $subscriber->email)->first(); if (!$customer) { return []; } $ordersQuery = Order::with('appliedCoupons')->where('customer_id', $customer->id); $totalCount = $ordersQuery->count(); if (!$totalCount) { return []; } // Pagination params (using super global to avoid dependency on request wrapper here) $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; if ($page < 1) { $page = 1; } $perPage = isset($_GET['per_page']) ? (int)$_GET['per_page'] : 10; if ($perPage < 1) { $perPage = 10; } $orders = $ordersQuery ->orderBy('id', 'DESC') ->limit($perPage) ->offset(($page - 1) * $perPage) ->get(); // Use a helper method for formatting $formattedOrders = $this->formatOrders($orders); return [ 'data' => $formattedOrders, 'total' => $totalCount, 'sidebar_html' => $this->getSidebarHtml($subscriber), 'after_html' => '', 'has_recount' => false, 'columns_config' => [ 'order' => ['label' => __('Order', 'fluent-crm'), 'width' => '100px', 'sortable' => true, 'key' => 'id'], 'date' => ['label' => __('Date', 'fluent-crm'), 'sortable' => true, 'key' => 'created_at'], // 'coupon' => ['label' => __('Coupon', 'fluent-crm'), 'sortable' => true, 'key' => 'created_at'], 'status' => ['label' => __('Status', 'fluent-crm'), 'width' => '140px', 'sortable' => false], // 'payment' => ['label' => __('Payment', 'fluent-crm'), 'width' => '140px', 'sortable' => false, 'key' => 'payment_status'], 'total' => ['label' => __('Total', 'fluent-crm'), 'width' => '120px', 'sortable' => true, 'key' => 'total'], 'action' => ['label' => __('', 'fluent-crm'), 'width' => '100px', 'sortable' => false], ], ]; } private function formatOrders($orders) { $formattedOrders = []; foreach ($orders as $order) { $orderActionHtml = ' '; $coupons = implode(', ', array_column($order['appliedCoupons']->toArray(), 'code')); $date = '#' . $order->id .''.date_i18n(get_option('date_format'), strtotime($order->created_at)).''; $status = ''. \FluentCrm\App\Services\Helper::getStatusText($order->status) .''; $formattedOrders[] = [ 'date' => $date, 'status' => $status, // 'payment' => $order->payment_status, // 'coupons' => $coupons, 'total' => Helper::toDecimal($order->total_amount), // Adjust if using a different helper 'action' => $orderActionHtml, ]; } return $formattedOrders; } private function getSidebarHtml($subscriber = null) { // We will build a similar widget like WooCommerce's purchase sidebar // Show a quick customer summary + recently purchased products (flat list of items) $customer = null; if ($subscriber && !empty($subscriber->email)) { $customer = Customer::where('email', $subscriber->email)->first(); } if (!$customer) { // Fallback: Just return the static block as before return '
'; } // Aggregate order stats $ordersQuery = Order::where('customer_id', $customer->id); $orderCount = (clone $ordersQuery)->count(); if (!$orderCount) { return ''; } $totalSpent = (clone $ordersQuery)->sum('total_amount'); $firstOrder = (clone $ordersQuery)->orderBy('id', 'ASC')->value('created_at'); $lastOrder = (clone $ordersQuery)->orderBy('id', 'DESC')->value('created_at'); // Fetch recent purchased items (latest 15 order items across latest orders) // We'll eager load order_items for performance $recentOrders = (clone $ordersQuery) ->with(['order_items' => function($q){ $q->orderBy('id', 'DESC'); }]) ->orderBy('id', 'DESC') ->limit(200) ->get(); $items = []; foreach ($recentOrders as $order) { foreach ($order->order_items as $orderItem) { $itemDisplayName = $orderItem->post_title; if($orderItem->post_title != $orderItem->title) { $itemDisplayName = $orderItem->post_title . ' - ' . $orderItem->title; } $items[] = [ 'name' => $itemDisplayName, 'price' => isset($orderItem->line_total) ? Helper::toDecimal($orderItem->line_total) : 0, 'created_at' => $order->created_at, 'order_id' => $order->id, ]; if (count($items) >= 15) { break 2; // Exit both loops once we have enough } } } $html = ''; return $html; } public function addAutomationConditions($groups) { $conditionItems = [ [ 'value' => 'commerce_exist', 'label' => __('Is a customer?', 'fluent-crm'), 'type' => 'selections', 'is_multiple' => false, 'disable_values' => true, 'value_description' => __('This filter will check if a contact has at least one shop order or not', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('Yes', 'fluent-crm'), 'not_exist' => __('No', 'fluent-crm'), ] ], [ 'value' => 'ltv', 'label' => __('Lifetime Value', 'fluent-crm'), 'type' => 'numeric' ], [ 'value' => 'aov', 'label' => __('Average Order Value', 'fluent-crm'), 'type' => 'numeric', ], [ 'value' => 'first_purchase_date', 'label' => __('First Order Date', 'fluent-crm'), 'type' => 'dates' ], [ 'value' => 'last_purchase_date', 'label' => __('Last Order Date', 'fluent-crm'), 'type' => 'dates' ], [ 'value' => 'purchased_items', 'label' => __('Products', 'fluent-crm'), 'type' => 'selections', 'component' => 'product_selector', 'is_multiple' => true, 'custom_operators' => [ 'exist' => __('purchased', 'fluent-crm'), 'not_exist' => __('not purchased', 'fluent-crm'), ], 'help' => __('Will filter the contacts who have at least one order', 'fluent-crm') ], [ 'value' => 'variation_purchased', 'label' => __('Product Variations', 'fluent-crm'), 'type' => 'cascade_selections', 'provider' => 'fct_variations', 'is_multiple' => true, 'value_description' => __('This filter will check if a contact has purchased at least one specific product variation or not', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('purchased', 'fluent-crm'), 'not_exist' => __('not purchased', 'fluent-crm'), ] ], [ 'value' => 'purchased_categories', 'label' => __('Product Categories', 'fluent-crm'), 'type' => 'selections', 'component' => 'tax_selector', 'taxonomy' => 'product-categories', 'is_multiple' => true, 'disabled' => true, 'help' => __('Will filter the contacts who have at least one order', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('purchased', 'fluent-crm'), 'not_exist' => __('not purchased', 'fluent-crm'), ] ], [ 'value' => 'commerce_coupons', 'label' => __('Used Coupons', 'fluent-crm'), 'type' => 'selections', 'component' => 'ajax_selector', 'option_key' => 'fct_coupons', 'is_multiple' => true, 'disabled' => true, 'custom_operators' => [ 'exist' => __('in', 'fluent-crm'), 'not_exist' => __('not in', 'fluent-crm'), ], 'help' => __('Will filter the contacts who have at least one order', 'fluent-crm') ] ]; if (ModuleSettings::isActive('license')) { $conditionItems[] = [ 'value' => 'active_licenses', 'label' => __('Active Licenses', 'fluent-crm'), 'type' => 'selections', 'component' => 'product_selector', 'is_multiple' => true, 'custom_operators' => [ 'exist' => __('have', 'fluent-crm'), 'not_exist' => __('do not have', 'fluent-crm'), ], 'help' => __('Will filter the contacts who have at least one active licenses or not', 'fluent-crm') ]; $conditionItems[] = [ 'value' => 'active_variation_licenses', 'label' => __('Active Variation Licenses', 'fluent-crm'), 'type' => 'cascade_selections', 'provider' => 'fct_variations', 'is_multiple' => true, 'value_description' => __('This filter will check if a contact has at least one specific variation license or not', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('have', 'fluent-crm'), 'not_exist' => __('do not have', 'fluent-crm'), ] ]; $conditionItems[] = [ 'value' => 'expired_licenses', 'label' => __('Expired Licenses', 'fluent-crm'), 'type' => 'selections', 'component' => 'product_selector', 'is_multiple' => true, 'custom_operators' => [ 'exist' => __('have', 'fluent-crm'), 'not_exist' => __('do not have', 'fluent-crm'), ], 'help' => __('Will filter the contacts who have at least one expired licenses or not', 'fluent-crm') ]; $conditionItems[] = [ 'value' => 'expired_variation_licenses', 'label' => __('Expired Variation Licenses', 'fluent-crm'), 'type' => 'cascade_selections', 'provider' => 'fct_variations', 'is_multiple' => true, 'value_description' => __('This filter will check if a contact has at least one specific variation expired license or not', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('have', 'fluent-crm'), 'not_exist' => __('do not have', 'fluent-crm'), ] ]; $conditionItems[] = [ 'value' => 'license_exist', 'label' => __('Has any active license?', 'fluent-crm'), 'type' => 'selections', 'is_multiple' => false, 'disable_values' => true, 'value_description' => __('Check if contacts has any active license from any products', 'fluent-crm'), 'custom_operators' => [ 'exist' => __('Yes', 'fluent-crm'), 'not_exist' => __('No', 'fluent-crm'), ] ]; } $groups['fluent_cart'] = [ 'label' => __('FluentCart', 'fluent-crm'), 'value' => 'fluent_cart', 'children' => $conditionItems ]; return $groups; } public function assessAutomationConditions($result, $conditions, $subscriber) { $legacyConditions = []; // if (Commerce::isEnabled('woo')) { $formattedConditions = []; $commerceProps = [ 'commerce_exist', 'ltv', // lifetime value 'aov', // average order value 'first_purchase_date', 'last_purchase_date', 'purchased_items', // products purchased 'variation_purchased', // product variations purchased 'purchased_categories', // product categories 'commerce_coupons', // used coupons ]; foreach ($conditions as $condition) { $prop = $condition['data_key']; $operator = $condition['operator']; if (in_array($prop, $commerceProps)) { $formattedConditions[] = [ 'operator' => $operator, 'value' => $condition['data_value'], 'property' => $prop, ]; } else { $legacyConditions[] = $condition; } } if ($formattedConditions) { $hasSubscriber = Subscriber::where('id', $subscriber->id)->where(function ($q) use ($formattedConditions) { do_action_ref_array('fluentcrm_contacts_filter_fluent_cart', [&$q, $formattedConditions]); })->first(); if (!$hasSubscriber) { return false; } } // } else { // $legacyConditions = $conditions; // } if ($legacyConditions) { $cartCustomer = Customer::query() ->where('email', $subscriber->email) ->when($subscriber->user_id, function ($q) use ($subscriber) { return $q->orWhere('user_id', $subscriber->user_id); }) ->first(); if (!$cartCustomer) { return false; } } return $result; } }