diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php
index dfd99a43..2d3988ac 100644
--- a/app/Controllers/LeadsController.php
+++ b/app/Controllers/LeadsController.php
@@ -32,6 +32,8 @@ use App\Models\OccupancyMasterModel;
use App\Models\LeadFilesModel;
use App\Models\LeadInstallmentPaymentDetails;
use App\Models\GmailSentHistoryModel;
+use App\Models\SalesActualLeadModel;
+use App\Models\SalesContactPersonModel;
use App\Helpers\MailHelper;
use App\Helpers\ExcelMergeHelper;
@@ -83,6 +85,8 @@ class LeadsController extends BaseController
protected $buisnessType;
protected $member_data_excel_columns;
protected $general_relationships;
+ protected $leadModel;
+ protected $contactModel;
public function __construct()
@@ -107,6 +111,8 @@ class LeadsController extends BaseController
$this->leadFilesModel = new LeadFilesModel();
$this->leadInstallmentPaymentDetails = new LeadInstallmentPaymentDetails();
$this->gmailSentHistoryModel = new GmailSentHistoryModel();
+ $this->leadModel = new SalesActualLeadModel();
+ $this->contactModel = new SalesContactPersonModel();
$this->issuer = [1 => 'JIBS', 2 => 'Nhance'];
$this->clientType = [1 => 'Group', 2 => 'Individual'];
@@ -627,6 +633,7 @@ class LeadsController extends BaseController
{
$request_data = $this->request->getPost();
+ print_r($request_data);die;
$data = sanitizeInputArrayAdvanced($request_data);
$data['client_type'] = 1;
@@ -825,6 +832,7 @@ class LeadsController extends BaseController
$last_3_years_claims = $data['finyear'];
$processedData[] = [
+ 'lost_reason' => $data['lost_reason'] ?? null,
'actual_lead_id' => $data['actual_lead_id'] ?? null,
'lead_type' => $data['lead_type'],
'issuer' => $data['issuer'],
@@ -4372,6 +4380,28 @@ class LeadsController extends BaseController
'actual_lead_id' => $actual_lead_id,
];
+ if ($actual_lead_id > 0) {
+
+ // πΉ Client Details (Single Row)
+ $data['actual_lead_client_details'] = $this->leadModel
+ ->select('company_name, email, phone, address, website, gst_number, status, assigned_to')
+ ->where('lead_id', $actual_lead_id)
+ ->first(); // first row only
+
+
+ // πΉ Contact Person Details (First Row Only)
+ $data['actual_lead_contact_person_details'] = $this->contactModel
+ ->select('contact_id, name, mobile, designation, email, is_primary')
+ ->where('lead_id', $actual_lead_id)
+ ->where('is_primary',1)
+ ->orderBy('is_primary', 'DESC') // optional (primary first)
+ ->first(); // only first row
+ }
+ else {
+ $data['actual_lead_client_details'] = null;
+ $data['actual_lead_contact_person_details'] = null;
+ }
+
// Fetch sales team members who are active in team 5
$data['salse_team'] = $this->userModel
->select('user_profiles.*')
diff --git a/app/Controllers/SalesController.php b/app/Controllers/SalesController.php
index 164444e9..39a0f672 100644
--- a/app/Controllers/SalesController.php
+++ b/app/Controllers/SalesController.php
@@ -53,7 +53,6 @@ class SalesController extends BaseController
->orderBy('lead_id', 'DESC')
->findAll();
-
return $this->loadLayout('sales/activity_view', $data);
}
@@ -66,9 +65,14 @@ class SalesController extends BaseController
return $this->loadLayout('sales/target_view', $data);
}
- /**
- * HELPER: Fetches Sales Managers based on the logged-in user's role and branch
- */
+ /**
+ * HELPER: Fetches Sales Managers based on the logged-in user's role and branch
+ * sales_manager Current branch | Role 4 + Team 5 | Assign To dropdown
+ * sales_manager_ids Current branch | Role 4 + Team 5 | Query filter IDs
+ * sales_manager_with_head Current branch | Role 1,4,5 | Branch reporting dropdown
+ * sales_manager_with_head_ids Current branch | Role 1,4,5 | Branch reporting filter
+ * sales_team All branches | Role 1,4,5 | Admin/global reporting
+ */
private function getSalesStaffData(): array
{
$db = \Config\Database::connect();
@@ -76,54 +80,118 @@ class SalesController extends BaseController
$role = get_role_id();
$team_id = user_team();
+ // ββ Get logged-in user's profile ββββββββββββββββββββββββββββββββ
+ $row = $db->table('user_profiles')
+ ->where('is_active', 1)
+ ->where('id', $logged_user_id)
+ ->get()->getRow();
+
+ $nhance_branch_id = $row ? $row->nhance_branch_id : null;
+
+ // ββ Base result structure ββββββββββββββββββββββββββββββββββββββββ
$data = [
- 'users' => [],
- 'sales_manager_ids'=> [],
- 'sales_role' => '',
- 'nhance_branch_id' => null,
- 'assigned_ids' => [],
+ 'sales_role' => '',
+ 'sales_manager' => [],
+ 'sales_manager_ids' => [],
+ 'sales_manager_with_head' => [],
+ 'sales_manager_with_head_ids' => [],
+ 'sales_team' => [],
+ 'nhance_branch_id' => $nhance_branch_id,
];
- $row = $db->table('user_profiles')->select('*')
- ->where('is_active', 1)->where('id', $logged_user_id)
- ->get()->getRow();
+ // ================================================================
+ // QUERY 1: Get all MANAGERS in current branch
+ // Role = 4 AND Team = 5 AND same branch
+ // ================================================================
+ $branch_managers = $db->table('user_profiles up')
+ ->select('up.id, up.first_name, up.role, up.nhance_branch_id')
+ ->join('user_teams ut', 'ut.user_id = up.id')
+ ->where('up.is_active', 1)
+ ->where('ut.is_active', 1)
+ ->where('up.role', 4) // Sales Manager role
+ ->where('ut.team_id', 5) // Sales team
+ ->where('up.nhance_branch_id', $nhance_branch_id) // same branch
+ ->groupBy('up.id')
+ ->get()->getResultArray();
- $nhance_branch_id = $row ? $row->nhance_branch_id : null;
- $data['nhance_branch_id']= $nhance_branch_id;
+ // ================================================================
+ // QUERY 2: Get all HEADS in current branch
+ // Role = 1 or 5 AND same branch
+ // ================================================================
+ $branch_heads = $db->table('user_profiles')
+ ->select('id, first_name, role, nhance_branch_id')
+ ->where('is_active', 1)
+ ->whereIn('role', [1, 5]) // Sales Head roles
+ ->where('nhance_branch_id', $nhance_branch_id) // same branch
+ ->get()->getResultArray();
- // ββ Sales Manager (Role 4, Team 5) ββββββββββββββββββββββββββ
+ // Add "(Head)" label to heads so dropdown is clear
+ foreach ($branch_heads as &$head) {
+ $head['first_name'] = $head['first_name'] . ' (Head)';
+ }
+ unset($head);
+
+ // ================================================================
+ // QUERY 3: Get ALL MANAGERS across ALL branches
+ // Role = 4 AND Team = 5 (no branch filter)
+ // ================================================================
+ $all_managers = $db->table('user_profiles up')
+ ->select('up.id, up.first_name, up.role, up.nhance_branch_id')
+ ->join('user_teams ut', 'ut.user_id = up.id')
+ ->where('up.is_active', 1)
+ ->where('ut.is_active', 1)
+ ->where('up.role', 4) // Sales Manager role
+ ->where('ut.team_id', 5) // Sales team
+ ->groupBy('up.id')
+ ->get()->getResultArray();
+
+ // ================================================================
+ // QUERY 4: Get ALL HEADS across ALL branches
+ // Role = 1 or 5 (no branch filter)
+ // ================================================================
+ $all_heads = $db->table('user_profiles')
+ ->select('id, first_name, role, nhance_branch_id')
+ ->where('is_active', 1)
+ ->whereIn('role', [1, 5]) // Sales Head roles
+ ->get()->getResultArray();
+
+ // Add "(Head)" label to all heads
+ foreach ($all_heads as &$head) {
+ $head['first_name'] = $head['first_name'] . ' (Head)';
+ }
+ unset($head);
+
+ // ================================================================
+ // BUILD: sales_manager_with_head = branch heads + branch managers
+ // ================================================================
+ $data['sales_manager_with_head'] = array_merge($branch_heads, $branch_managers);
+ $data['sales_manager_with_head_ids'] = array_column($data['sales_manager_with_head'], 'id');
+
+ // ================================================================
+ // BUILD: sales_team = all heads + all managers (every branch)
+ // ================================================================
+ $data['sales_team'] = array_merge($all_heads, $all_managers);
+
+ // ββ Sales Manager (Role 4, Team 5) ββββββββββββββββββββββββββββββ
if ($role == 4 && in_array(5, $team_id)) {
$data['sales_role'] = 'Sales Manager';
+ $data['sales_manager'] = [[ // only himself
+ 'id' => $row->id,
+ 'first_name' => $row->first_name,
+ 'role' => $role,
+ 'nhance_branch_id' => $nhance_branch_id,
+ ]];
$data['sales_manager_ids'] = [$logged_user_id];
- $data['assigned_ids'] = [$logged_user_id];
- $data['users'] = [
- [
- 'id' => $row->id,
- 'first_name' => $row->first_name,
- 'last_name' => $row->last_name ?? '',
- 'nhance_branch_id' => $nhance_branch_id,
- ]
- ];
- // ββ Sales Head (Role 1 or 5) βββββββββββββββββββββββββββββββββ
+ // ββ Sales Head (Role 1 or 5) βββββββββββββββββββββββββββββββββββββ
} elseif (in_array($role, [1, 5])) {
- $data['sales_role'] = 'Sales Head';
- $data['users'] = $db->table('user_profiles up')
- ->select('up.id, up.first_name, up.last_name, up.nhance_branch_id')
- ->join('user_teams ut', 'ut.user_id = up.id')
- ->where('up.is_active', 1)
- ->where('ut.is_active', 1)
- ->where('up.role', 4)
- ->where('ut.team_id', 5)
- ->where('up.nhance_branch_id', $nhance_branch_id)
- ->get()
- ->getResultArray();
-
- $ids = array_column($data['users'], 'id');
- $data['sales_manager_ids'] = $ids;
- $data['assigned_ids'] = $ids; // same value, both available
+ $data['sales_role'] = 'Sales Head';
+ $data['sales_manager'] = $branch_managers; // reuse QUERY 1 result
+ $data['sales_manager_ids'] = array_column($branch_managers, 'id');
+ $data['sales_manager_with_head'] = array_merge($branch_heads, $branch_managers); // reuse
+ $data['sales_manager_with_head_ids'] = array_column($data['sales_manager_with_head'], 'id');
}
return $data;
@@ -149,6 +217,9 @@ class SalesController extends BaseController
echo view('layout/footer', $data);
}
+ /**
+ * GET /api/sales/activities/(:num)/complete
+ */
public function completeActivity($id) {
try {
$data = $this->request->getJSON(true);
@@ -236,7 +307,8 @@ class SalesController extends BaseController
'data' => $result['data'],
'total' => $result['total'],
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
+ 'counts' => $result['counts'],
]);
} catch (\Exception $e) {
return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
@@ -398,6 +470,15 @@ class SalesController extends BaseController
$data = $this->request->getJSON(true);
$data['updated_by'] = $this->getUserId();
+ $contact = $this->contactModel->find($id);
+
+ if (isset($data['is_primary']) && $data['is_primary'] == 1) {
+ // Reset all contacts for this lead to 0 primary
+ $this->contactModel->where('lead_id', $contact['lead_id'])
+ ->set(['is_primary' => 0])
+ ->update();
+ }
+
if (!$this->contactModel->update((int)$id, $data)) {
return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
}
@@ -487,7 +568,8 @@ class SalesController extends BaseController
'data' => $result['data'],
'total' => $result['total'],
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
+ 'counts' => $result['counts'],
], 200);
} catch (\Exception $e) {
@@ -565,6 +647,11 @@ class SalesController extends BaseController
$data['created_by'] = $this->getUserId();
$data['updated_by'] = $this->getUserId();
+ // FIX: Convert the array to a JSON string so it fits in the VARCHAR column
+ if (isset($data['additional_assigned_ids']) && is_array($data['additional_assigned_ids'])) {
+ $data['additional_assigned_ids'] = json_encode($data['additional_assigned_ids']);
+ }
+
if (!$this->activityModel->insert($data)) {
return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
}
@@ -575,6 +662,11 @@ class SalesController extends BaseController
$activityId = $this->activityModel->getInsertID();
$activity = $this->activityModel->find((int)$activityId);
+ // OPTIONAL: Decode it back to an array for the API response so the frontend gets a clean array
+ if (isset($activity['additional_assigned_ids'])) {
+ $activity['additional_assigned_ids'] = json_decode($activity['additional_assigned_ids'], true);
+ }
+
return $this->respondCreated([
'status' => 'success',
'message' => 'Activity created successfully',
@@ -600,12 +692,22 @@ class SalesController extends BaseController
$data = $this->request->getJSON(true);
$data['updated_by'] = $this->getUserId();
+ // FIX: Convert the array to a JSON string for updating
+ if (isset($data['additional_assigned_ids']) && is_array($data['additional_assigned_ids'])) {
+ $data['additional_assigned_ids'] = json_encode($data['additional_assigned_ids']);
+ }
+
if (!$this->activityModel->update($id, $data)) {
return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
}
$activity = $this->activityModel->find((int)$id);
+ // OPTIONAL: Decode it back to an array for the API response
+ if (isset($activity['additional_assigned_ids'])) {
+ $activity['additional_assigned_ids'] = json_decode($activity['additional_assigned_ids'], true);
+ }
+
return $this->respond([
'status' => 'success',
'message' => 'Activity updated successfully',
@@ -927,252 +1029,896 @@ class SalesController extends BaseController
// ==================== Dashboard ====================
- public function dashboard()
- {
- $payload = $this->request->getGet();
- $base = $this->getSalesStaffData();
- $salesRole = $base['sales_role'];
- $salesManagerIds = $base['sales_manager_ids'];
- $userId = get_session_userid();
- // Get branch id from users array
- $nhanceBranchId = $base['users'][0]['nhance_branch_id'] ?? null;
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// HELPER: Build FY date range from fy_year string
+// e.g. "2024-2025" β ['2024-04-01 00:00:00', '2025-03-31 23:59:59']
+// βββββββββββββββββββββββββββββββββββββββββββββ
+private function getFYDateRange(string $financialYear): array
+{
+ // Format: "2025-2026" β split on last hyphen to get start=2025, end=2026
+ $pos = strrpos($financialYear, '-');
+ $startYear = substr($financialYear, 0, $pos); // "2025"
+ $endYear = substr($financialYear, $pos + 1); // "2026"
- if ($salesRole === 'Sales Head') {
- $this->branchLevelDashboard($nhanceBranchId, $salesManagerIds);
- } elseif ($salesRole === 'Sales Manager') {
- $this->salesManagerLevelDashboard($userId, $salesManagerIds,$payload);
- }
- }
+ return [
+ 'start' => $startYear . '-04-01 00:00:00', // 2025-04-01 00:00:00
+ 'end' => $endYear . '-03-31 23:59:59', // 2026-03-31 23:59:59
+ ];
+}
- public function branchLevelDashboard($branchId,$sales_manager_ids)
- {
- // Hardcoded branch ID as requested
- // $branchId = 1;
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// HELPER: FY quarters (Apr-Jun / Jul-Sep / Oct-Dec / Jan-Mar)
+// βββββββββββββββββββββββββββββββββββββββββββββ
+private function getFYQuarters(string $financialYear): array
+{
+ $pos = strrpos($financialYear, '-');
+ $sy = (int)substr($financialYear, 0, $pos); // 2025
+ $ey = (int)substr($financialYear, $pos + 1); // 2026
- try {
- $sales_manager_ids = array_values(array_map('intval', $sales_manager_ids));
-
- // Final safe check
- if (empty($sales_manager_ids)) {
- // No valid IDs β skip queries or return empty
- $total_leads = 0;
- $total_activity = 0;
- $total_completed_activity = 0;
- $total_pending_activity = 0;
- $pending_activities = [];
- $recent_activities = [];
- $teamPerformance = [];
-
- $leadsOverview = [];
- } else {
+ return [
+ ['name' => 'Q1', 'label' => "Q1 (AprβJun {$sy})", 'start' => "{$sy}-04-01", 'end' => "{$sy}-06-30"],
+ ['name' => 'Q2', 'label' => "Q2 (JulβSep {$sy})", 'start' => "{$sy}-07-01", 'end' => "{$sy}-09-30"],
+ ['name' => 'Q3', 'label' => "Q3 (OctβDec {$sy})", 'start' => "{$sy}-10-01", 'end' => "{$sy}-12-31"],
+ ['name' => 'Q4', 'label' => "Q4 (JanβMar {$ey})", 'start' => "{$ey}-01-01", 'end' => "{$ey}-03-31"],
+ ];
+}
- if (empty($sales_manager_ids) || !is_array($sales_manager_ids)) {
- $sales_manager_ids = array_filter((array) $sales_manager_ids); // removes null, "", 0
- }
-
- // 1. Lead Statistics
- $total_leads = $this->leadModel->whereIn('assigned_to', $sales_manager_ids)->countAllResults(); // Use countAllResults, NOT countAll
- // echo $this->leadModel->getLastQuery();die();
-
- // 2. Total activity
- $total_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->countAllResults();
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// dashboard() β entry point
+// βββββββββββββββββββββββββββββββββββββββββββββ
+public function dashboard()
+{
+ $payload = $this->request->getGet();
+ $base = $this->getSalesStaffData();
+ $salesRole = $base['sales_role'];
+ $salesManagerIds = $base['sales_manager_ids'];
+ $userId = get_session_userid();
- // 3. Completed activity
- $total_completed_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->where('status', 'completed')->countAllResults();
+ // Get branch id
+ $nhanceBranchId = $base['sales_manager'][0]['nhance_branch_id'] ?? null;
- // 4. Pending activity
- $total_pending_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->where('status', 'pending')->countAllResults();
-
- $db = \Config\Database::connect();
-
- // 5. Team Performance
- $teamPerformance = $db->table('user_profiles as u')
- ->select('u.first_name, u.last_name, r.role,
- (SELECT COUNT(*) FROM sales_activities WHERE assigned_to = u.id) as total_acts,
- (SELECT COUNT(*) FROM sales_activities WHERE assigned_to = u.id AND status = "completed") as done_acts')
- ->join('roles r', 'r.id = u.role')
- ->where('u.nhance_branch_id', $branchId)
- ->whereIn('u.id', $sales_manager_ids)
- ->where('u.is_active', 1)
- ->get()->getResultArray();
+ $current_fin_year = $payload['fy'] ?? getCurrentFinancialYear();
- // 6. Recent Activities (Joining for Lead Names)
- $recent_activities = $this->activityModel->select('sales_activities.*, sales_actual_leads.company_name')
- ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id')
- ->orderBy('sales_activities.scheduled_date', 'DESC')
- ->limit(6)
- ->findAll();
-
- // 7. Pending Activities (List)
- $pending_activities = $db->table('sales_activities sa')
- ->select('sa.activity_id,sa.lead_id,sa.activity_type,sa.scheduled_date,sa.status,sa.assigned_to,sal.company_name,up.first_name AS assigned_to_name,sa.notes')
- ->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
- ->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left') // β
ADD THIS
- ->orderBy('sa.scheduled_date', 'DESC')
- ->whereIn('sa.assigned_to', $sales_manager_ids)
- ->where('sa.status', 'pending')
- ->get()->getResultArray();
+ $db = \Config\Database::connect();
- // 8. All Leads Overview
- $leadsOverview = $db->table('sales_actual_leads sal')
- ->select('sal.lead_id,sal.company_name,sal.status,up.first_name AS assigned_to,
- COUNT(DISTINCT sa.activity_id) AS activities,
- COUNT(DISTINCT l.id) AS opportunities
- ')
- ->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
- ->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
- ->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
- ->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
- // ->having('COUNT(DISTINCT sa.activity_id) + COUNT(DISTINCT l.id) >', 0) // β this line
- ->orderBy('sal.created_at', 'DESC')
- ->whereIn('sal.assigned_to', $sales_manager_ids)
- ->get()
- ->getResultArray();
-
- // 9. Activity BrakDown
- $activityBreakdown = $db->table('sales_activities')
- ->select("activity_type, COUNT(*) AS total, ROUND(COUNT(*) * 100.0 / {$total_activity}, 0) AS percentage", false)
- ->whereIn('assigned_to', $sales_manager_ids)
- ->groupBy('activity_type')
- ->orderBy('total', 'DESC')
- ->get()
- ->getResultArray();
- }
+ // Available FY years for dropdown
+ $fin_years_raw = $db->table('sales_target')
+ ->select('fy_year', false) // false = no backtick escaping
+ ->distinct()
+ ->orderBy('fy_year', 'DESC')
+ ->get()
+ ->getResultArray();
+
+ $fin_years = array_column($fin_years_raw, 'fy_year');
+
+ if (empty($fin_years)) {
+ $fin_years[] = $current_fin_year;
+ }
+
+ // Ensure current FY is available in list
+ if (!in_array($current_fin_year, $fin_years)) {
+ array_unshift($fin_years, $current_fin_year);
+ }
+
+ // Route by role
+ if ($salesRole === 'Sales Head') {
+ $this->branchLevelDashboard($nhanceBranchId, $salesManagerIds, $current_fin_year, $fin_years);
+ } elseif ($salesRole === 'Sales Manager') {
+ $this->salesManagerLevelDashboard($userId, $current_fin_year, $fin_years);
+ }
+}
+
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// branchLevelDashboard()
+// βββββββββββββββββββββββββββββββββββββββββββββ
+public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin_year, $fin_years)
+{
+ try {
+ $sales_manager_ids = array_values(array_filter(array_map('intval', $sales_manager_ids)));
+
+ $db = \Config\Database::connect();
+ $fyRange = $this->getFYDateRange($current_fin_year);
+ $fyStart = $fyRange['start'];
+ $fyEnd = $fyRange['end'];
+
+ if (empty($sales_manager_ids)) {
+ // ββ No team members β return empty dashboard ββ
$data = [
- 'total_leads' => $total_leads,
- 'total_acts' => $total_activity,
- 'total_completed_acts' => $total_completed_activity,
- 'total_pending_acts'=> $total_pending_activity,
- 'pipeline_value' => '15.0L', // Hardcoded placeholder from PDF [cite: 14]
- 'team' => $teamPerformance,
- 'recent_acts' => $recent_activities,
- 'pending_acts' => $pending_activities,
- 'leads_overview' => $leadsOverview,
- 'activity_breakdown'=> $activityBreakdown,
- 'tab_name' => "Sales Dashboard",
- 'page_name' => "Sales Dashboard"
+ 'total_leads' => 0,
+ 'total_acts' => 0,
+ 'total_completed_acts' => 0,
+ 'total_pending_acts' => 0,
+ 'team' => [],
+ 'pending_acts' => [],
+ 'leads_overview' => [],
+ 'activity_breakdown' => [],
+ 'team_achievement' => [],
+ 'opp_achievement' => [],
+ 'fin_years' => $fin_years,
+ 'current_fin_year' => $current_fin_year,
+ 'tab_name' => 'Sales Dashboard',
+ 'page_name' => 'Sales Dashboard',
];
-
- // dd($data);
-
$this->loadLayout('sales/branch_level_dashboard_view', $data);
-
- // return view('sales/dashboard_view', $data);
-
- } catch (\Exception $e) {
- return $this->failServerError($e->getMessage());
+ return;
}
+
+ // 1. Lead count β FY filtered by created_at
+ $total_leads = $this->leadModel
+ ->whereIn('assigned_to', $sales_manager_ids)
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->countAllResults();
+
+ // 2. Total activities β FY filtered by scheduled_date
+ $total_activity = $this->activityModel
+ ->whereIn('assigned_to', $sales_manager_ids)
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->countAllResults();
+
+ // 3. Completed activities β FY filtered
+ $total_completed_activity = $this->activityModel
+ ->whereIn('assigned_to', $sales_manager_ids)
+ ->where('status', 'completed')
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->countAllResults();
+
+ // 4. Pending activities β FY filtered
+ $total_pending_activity = $this->activityModel
+ ->whereIn('assigned_to', $sales_manager_ids)
+ ->where('status', 'pending')
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->countAllResults();
+
+ // 5. Team Performance β subqueries FY filtered by scheduled_date
+ $teamPerformance = $db->table('user_profiles as u')
+ ->select("u.id, u.first_name, u.last_name, r.role,
+ (SELECT COUNT(*) FROM sales_activities
+ WHERE assigned_to = u.id
+ AND scheduled_date >= '{$fyStart}'
+ AND scheduled_date <= '{$fyEnd}') as total_acts,
+ (SELECT COUNT(*) FROM sales_activities
+ WHERE assigned_to = u.id AND status = 'completed'
+ AND scheduled_date >= '{$fyStart}'
+ AND scheduled_date <= '{$fyEnd}') as done_acts", false)
+ ->join('roles r', 'r.id = u.role')
+ ->where('u.nhance_branch_id', $branchId)
+ ->whereIn('u.id', $sales_manager_ids)
+ ->where('u.is_active', 1)
+ ->get()
+ ->getResultArray();
+
+ // 6. Pending Activities list β FY filtered by scheduled_date
+ $pending_activities = $db->table('sales_activities sa')
+ ->select('sa.activity_id, sa.lead_id, sa.activity_type, sa.scheduled_date,
+ sa.status, sa.assigned_to, sal.company_name,
+ up.first_name AS assigned_to_name, sa.notes')
+ ->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
+ ->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left')
+ ->whereIn('sa.assigned_to', $sales_manager_ids)
+ ->where('sa.status', 'pending')
+ ->where('sa.scheduled_date >=', $fyStart)
+ ->where('sa.scheduled_date <=', $fyEnd)
+ ->orderBy('sa.scheduled_date', 'DESC')
+ ->get()
+ ->getResultArray();
+
+ // 7. All Leads Overview β FY filtered by sal.created_at
+ // Activities & opportunities also scoped to FY via CASE WHEN
+ $leadsOverview = $db->table('sales_actual_leads sal')
+ ->select("sal.lead_id, sal.company_name, sal.status,
+ up.first_name AS assigned_to,
+ COUNT(DISTINCT CASE WHEN sa.scheduled_date >= '{$fyStart}'
+ AND sa.scheduled_date <= '{$fyEnd}' THEN sa.activity_id END) AS activities,
+ COUNT(DISTINCT CASE WHEN l.updated_at >= '{$fyStart}'
+ AND l.updated_at <= '{$fyEnd}' THEN l.id END) AS opportunities", false)
+ ->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
+ ->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
+ ->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
+ ->whereIn('sal.assigned_to', $sales_manager_ids)
+ ->where('sal.created_at >=', $fyStart)
+ ->where('sal.created_at <=', $fyEnd)
+ ->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
+ ->orderBy('sal.created_at', 'DESC')
+ ->get()
+ ->getResultArray();
+
+ // Protect against division by zero in query #8
+ $total_activity_safe = $total_activity > 0 ? $total_activity : 1;
+
+ // 8. Activity Breakdown β FY filtered by scheduled_date
+ $activityBreakdown = $db->table('sales_activities')
+ ->select("activity_type,
+ COUNT(*) AS total,
+ ROUND(COUNT(*) * 100.0 / {$total_activity_safe}, 0) AS percentage", false)
+ ->whereIn('assigned_to', $sales_manager_ids)
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->groupBy('activity_type')
+ ->orderBy('total', 'DESC')
+ ->get()
+ ->getResultArray();
+
+ // 9. Team Achievement (for achievement list + modal)
+ $teamAchievement = $this->buildTeamAchievement(
+ $db, $sales_manager_ids, $branchId, $current_fin_year, $fyStart, $fyEnd
+ );
+
+ // 10. Opportunities Achievement per member
+ $oppAchievement = $this->buildOppAchievement(
+ $db, $sales_manager_ids, $branchId, $current_fin_year, $fyStart, $fyEnd
+ );
+
+ $data = [
+ 'total_leads' => $total_leads,
+ 'total_acts' => $total_activity,
+ 'total_completed_acts' => $total_completed_activity,
+ 'total_pending_acts' => $total_pending_activity,
+ 'team' => $teamPerformance,
+ 'pending_acts' => $pending_activities,
+ 'leads_overview' => $leadsOverview,
+ 'activity_breakdown' => $activityBreakdown,
+ 'team_achievement' => $teamAchievement, // used by JS TEAM constant
+ 'opp_achievement' => $oppAchievement, // used by JS OPP_DATA constant
+ 'fin_years' => $fin_years,
+ 'current_fin_year' => $current_fin_year,
+ 'tab_name' => 'Sales Dashboard',
+ 'page_name' => 'Sales Dashboard',
+ ];
+
+ $this->loadLayout('sales/branch_level_dashboard_view', $data);
+
+ } catch (\Exception $e) {
+ return $this->failServerError($e->getMessage());
+ }
+}
+
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// buildTeamAchievement()
+// Builds the TEAM array for the achievement list
+// βββββββββββββββββββββββββββββββββββββββββββββ
+private function buildTeamAchievement($db, array $sales_manager_ids, $branchId, string $fy, string $fyStart, string $fyEnd): array
+{
+ $quarters = $this->getFYQuarters($fy);
+
+ // Gradient palette (cycles)
+ $gradients = [
+ ['grad' => 'linear-gradient(135deg,#10b981,#34d399)', 'color' => '#10b981'],
+ ['grad' => 'linear-gradient(135deg,#06b6d4,#67e8f9)', 'color' => '#06b6d4'],
+ ['grad' => 'linear-gradient(135deg,#4f46e5,#818cf8)', 'color' => '#4f46e5'],
+ ['grad' => 'linear-gradient(135deg,#ec4899,#f9a8d4)', 'color' => '#ec4899'],
+ ['grad' => 'linear-gradient(135deg,#f97316,#fbbf24)', 'color' => '#f97316'],
+ ];
+
+ $members = $db->table('user_profiles as u')
+ ->select('u.id, u.first_name, u.last_name, r.role')
+ ->join('roles r', 'r.id = u.role')
+ ->where('u.nhance_branch_id', $branchId)
+ ->whereIn('u.id', $sales_manager_ids)
+ ->where('u.is_active', 1)
+ ->get()
+ ->getResultArray();
+
+ $result = [];
+
+ foreach ($members as $idx => $m) {
+ $uid = (int)$m['id'];
+
+ // Target from sales_target
+ $targetRow = $db->table('sales_target')
+ ->where('user_id', $uid)
+ ->where('fy_year', $fy)
+ ->get()
+ ->getRowArray();
+ $targetAmt = (float)($targetRow['target_amount'] ?? 0);
+
+ // Achieved (won leads in FY)
+ $achievedAmt = (float)$this->getUserAchievedAmount($fy, $uid);
+
+ // Activities β FY filtered by scheduled_date
+ $totalActs = $db->table('sales_activities')
+ ->where('assigned_to', $uid)
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->countAllResults();
+ $doneActs = $db->table('sales_activities')
+ ->where('assigned_to', $uid)
+ ->where('status', 'completed')
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->countAllResults();
+
+ // Activity breakdown β FY filtered
+ $actRows = $db->table('sales_activities')
+ ->select('activity_type, COUNT(*) as cnt')
+ ->where('assigned_to', $uid)
+ ->where('scheduled_date >=', $fyStart)
+ ->where('scheduled_date <=', $fyEnd)
+ ->groupBy('activity_type')
+ ->get()->getResultArray();
+ $activities = [];
+ foreach ($actRows as $ar) {
+ $activities[$ar['activity_type']] = (int)$ar['cnt'];
+ }
+
+ // Quarter splits
+ $splits = [];
+ foreach ($quarters as $q) {
+ $qStart = $q['start'] . ' 00:00:00';
+ $qEnd = $q['end'] . ' 23:59:59';
+
+ // Achievement = SUM(exp_amt) for won policies in this quarter
+ $qAchievedRow = $db->query("
+ SELECT COALESCE(SUM(ptcs.exp_amt), 0) AS total
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details ptcs
+ ON ptcs.pt_id = pt.id
+ AND ptcs.is_active = 1
+ WHERE pt.sales_generated_by = ?
+ AND pt.issuer_branch = ?
+ AND pt.created_at >= ?
+ AND pt.created_at <= ?
+ ", [$uid, $branchId, $qStart, $qEnd])->getRowArray();
+ $qAchieved = (float)($qAchievedRow['total'] ?? 0);
+
+ $qTarget = $targetAmt > 0 ? round($targetAmt / 4, 2) : 0;
+
+ $qActs = $db->table('sales_activities')
+ ->where('assigned_to', $uid)
+ ->where('scheduled_date >=', $qStart)
+ ->where('scheduled_date <=', $qEnd)
+ ->countAllResults();
+
+ $qDone = $db->table('sales_activities')
+ ->where('assigned_to', $uid)
+ ->where('status', 'completed')
+ ->where('scheduled_date >=', $qStart)
+ ->where('scheduled_date <=', $qEnd)
+ ->countAllResults();
+
+ $qLeads = $db->table('sales_actual_leads')
+ ->where('assigned_to', $uid)
+ ->where('created_at >=', $qStart)
+ ->where('created_at <=', $qEnd)
+ ->countAllResults();
+
+ $splits[] = [
+ 'name' => $q['label'],
+ 'start' => date('M Y', strtotime($q['start'])),
+ 'end' => date('M Y', strtotime($q['end'])),
+ 'target' => $qTarget,
+ 'achieved' => $qAchieved,
+ 'acts' => $qActs,
+ 'done' => $qDone,
+ 'leads' => $qLeads,
+ ];
+ }
+
+ $palette = $gradients[$idx % count($gradients)];
+
+ $result[] = [
+ 'id' => $uid,
+ 'first_name' => $m['first_name'],
+ 'last_name' => $m['last_name'],
+ 'role' => $m['role'],
+ 'total_acts' => $totalActs,
+ 'done_acts' => $doneActs,
+ 'target_amt' => $targetAmt,
+ 'achieved_amt' => $achievedAmt,
+ 'grad' => $palette['grad'],
+ 'color' => $palette['color'],
+ 'splits' => $splits,
+ 'activities' => $activities,
+ ];
}
- public function salesManagerLevelDashboard($userId,$sales_manager_ids, $payload = [])
- {
- // $userId = get_session_userid();
- // $userId = 1;
- $db = \Config\Database::connect();
+ return $result;
+}
- try {
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// buildOppAchievement()
+// Opportunities via policy_transaction + pt_co_share_details
+// Returns per-member: totals + flat policy list (no quarterly grouping)
+// βββββββββββββββββββββββββββββββββββββββββββββ
+private function buildOppAchievement($db, array $sales_manager_ids, $branchId, string $fy, string $fyStart, string $fyEnd): array
+{
+ $result = [];
- // $payload = $this->request->getGet();
- $current_fin_year = $payload['fy'] ?? getCurrentFinancialYear();
+ foreach ($sales_manager_ids as $uid) {
-
- $fin_years = $db->table('sales_target')
- ->select('fy_year')
- ->where('user_id', $userId)
- ->orderBy('fy_year', 'desc')
- ->get()
- ->getResultArray();
+ // ββ Target ββ
+ $targetRow = $db->table('sales_target')
+ ->where('user_id', $uid)
+ ->where('fy_year', $fy)
+ ->get()
+ ->getRowArray();
+ $targetAmt = (float)($targetRow['target_amount'] ?? 0);
- $fin_years = array_column($fin_years, 'fy_year');
+ // ββ All policy rows for this user in FY ββ
+ // policy_no, issue_date (from pt), amount (exp_amt from child), created_at
+ // If no matching pt_co_share_details row exists, exp_amt = 0
+ // Policy list for Tab 2: policy_transaction + exp_amt from pt_co_share_details
+ $policyRows = $db->query("
+ SELECT
+ pt.id,
+ pt.policy_no,
+ pt.created_at AS issue_date,
+ COALESCE(ptcs.exp_amt, 0) AS amount,
+ pt.created_at AS created_at
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details ptcs
+ ON ptcs.pt_id = pt.id
+ AND ptcs.is_active = 1
+ WHERE pt.sales_generated_by = ?
+ AND pt.issuer_branch = ?
+ AND pt.created_at >= ?
+ AND pt.created_at <= ?
+ ORDER BY pt.created_at DESC
+ ", [$uid, $branchId, $fyStart, $fyEnd])->getResultArray();
- if(empty($fin_years)){
- $fin_years[] = $current_fin_year;
- }
+ // ββ Totals derived from policy_transaction ββ
+ $totalPolicies = count($policyRows);
+ $totalExpAmt = array_sum(array_column($policyRows, 'amount'));
- $target = $db->table('sales_target')
- ->where('user_id', $userId)
- ->where('fy_year', $current_fin_year)
- ->get()
- ->getRowArray();
-
- $targetAmount = $target['target_amount'] ?? 0.00;
-
- // get achieved amount from leads table
- $achievedAmount = $this->getUserAchievedAmount($current_fin_year, $userId);
-
- $remainingAmount = $targetAmount - $achievedAmount;
- // $achievementPercent = ($targetAmount > 0) ? round(($achievedAmount / $targetAmount) * 100) : 0;
- $achievementPercent = ($targetAmount > 0) ? min(100, round(($achievedAmount / $targetAmount) * 100)) : 0;
-
- $activitySummary = [
- 'total' => $this->activityModel->where('assigned_to', $userId)->countAllResults(),
- 'pending' => $this->activityModel->where(['assigned_to' => $userId, 'status' => 'pending'])->countAllResults(),
- 'completed' => $this->activityModel->where(['assigned_to' => $userId, 'status' => 'completed'])->countAllResults(),
+ // Clean policy list for JS
+ $policies = array_map(function($row) {
+ return [
+ 'policy_no' => $row['policy_no'],
+ 'issue_date' => $row['issue_date'],
+ 'amount' => (float)$row['amount'],
+ 'created_at' => $row['created_at'],
];
+ }, $policyRows);
- $myLeadsCount = $this->leadModel->where('assigned_to', $userId)->countAllResults();
+ // ββ Won Leads for this user in FY (Table 2 in modal) ββ
+ // leads.actual_lead_id maps to sales_actual_leads.id
+ // leads.type: 1 = EB, else = Non-EB
+ $wonLeads = $db->query("
+ SELECT
+ sal.lead_id,
+ sal.company_name AS company,
+ CASE WHEN l.lead_type = 1 THEN 'EB' ELSE 'Non-EB' END AS lead_type,
+ l.created_at AS created_at,
+ l.status
+ FROM leads l
+ INNER JOIN sales_actual_leads sal
+ ON sal.lead_id = l.actual_lead_id
+ WHERE l.status = 'won'
+ AND sal.assigned_to = ?
+ AND l.created_at >= ?
+ AND l.created_at <= ?
+ ORDER BY l.created_at DESC
+ ", [$uid, $fyStart, $fyEnd])->getResultArray();
- $upcomingActivities = $this->activityModel->select('sales_activities.*, sales_actual_leads.company_name')
- ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id')
- ->where(['sales_activities.assigned_to' => $userId, 'sales_activities.status' => 'pending'])
- ->orderBy('scheduled_date', 'ASC')
- ->limit(3)
- ->findAll();
-
- $recentLeads = $this->leadModel->where('assigned_to', $userId)
- ->orderBy('created_at', 'DESC')
- ->limit(5)
- ->findAll();
-
- $data = [
- 'target_amt' => $targetAmount,
- 'achieved' => $achievedAmount,
- 'remaining' => $remainingAmount,
- 'percent' => $achievementPercent,
- 'acts' => $activitySummary,
- 'lead_count' => $myLeadsCount,
- 'upcoming' => $upcomingActivities,
- 'recent_leads' => $recentLeads,
- 'fin_years' => $fin_years,
- 'display_fin_years' => format_financial_year($current_fin_year),
- 'user_name' => get_session_userdata()->first_namee ?? '',
- 'tab_name' => "Sales Dashboard",
- 'page_name' => "Sales Dashboard"
- ];
-
- // dd($data);
-
- // return view('sales/my_dashboard_view', $data);
- $this->loadLayout('sales/sales_manager_level_dashboard', $data);
-
- } catch (\Exception $e) {
- return $this->failServerError($e->getMessage());
- }
+ $result[$uid] = [
+ 'total_policies' => $totalPolicies,
+ 'total_exp_amt' => $totalExpAmt,
+ 'target_amt' => $targetAmt,
+ 'policies' => $policies,
+ 'won_leads' => $wonLeads, // for Table 2 in modal Tab 2
+ ];
}
- public function getUserAchievedAmount($financialYear, $userId)
- {
- // Split the string into two years
- $years = explode('-', $financialYear);
- $startYear = $years[0]; // 2025
- $endYear = $years[1]; // 2026
+ return $result;
+}
- // Create the timestamps
- $startFY = $startYear . '-04-01 00:00:00';
- $endFY = $endYear . '-03-31 23:59:59';
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// getUserAchievedAmount()
+// βββββββββββββββββββββββββββββββββββββββββββββ
+public function getUserAchievedAmount($financialYear, $userId)
+{
+ // "2025-2026" β strrpos splits correctly into 2025 / 2026
+ $pos = strrpos($financialYear, '-');
+ $startYear = substr($financialYear, 0, $pos); // "2025"
+ $endYear = substr($financialYear, $pos + 1); // "2026"
- $achievedAmountData = $this->leadModel
- ->select('SUM(leads.premium_amount) as achieved_amount')
- ->join('leads', 'sales_actual_leads.lead_id = leads.actual_lead_id')
- ->where('sales_actual_leads.assigned_to', $userId)
- ->where('leads.status', 'won')
- ->where('leads.updated_at >=', $startFY)
- ->where('leads.updated_at <=', $endFY)
+ $startFY = $startYear . '-04-01 00:00:00'; // 2025-04-01
+ $endFY = $endYear . '-03-31 23:59:59'; // 2026-03-31
+
+ // Achievement = SUM(exp_amt) from policy_transaction + pt_co_share_details
+ $db = \Config\Database::connect();
+ $row = $db->query("
+ SELECT COALESCE(SUM(ptcs.exp_amt), 0) AS achieved_amount
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details ptcs
+ ON ptcs.pt_id = pt.id
+ AND ptcs.is_active = 1
+ WHERE pt.sales_generated_by = ?
+ AND pt.created_at >= ?
+ AND pt.created_at <= ?
+ ", [$userId, $startFY, $endFY])->getRowArray();
+
+ return (float)($row['achieved_amount'] ?? 0.00);
+}
+
+// βββββββββββββββββββββββββββββββββββββββββββββ
+// salesManagerLevelDashboard()
+// βββββββββββββββββββββββββββββββββββββββββββββ
+public function salesManagerLevelDashboard($userId, $current_fin_year = null, $fin_years = [])
+{
+ $db = \Config\Database::connect();
+
+ try {
+
+ // -------------------------------
+ // Financial Year Handling
+ // -------------------------------
+ if (empty($current_fin_year)) {
+ $current_fin_year = getCurrentFinancialYear();
+ }
+
+ $fyRange = $this->getFYDateRange($current_fin_year);
+ $fyStart = $fyRange['start'];
+ $fyEnd = $fyRange['end'];
+
+ // -------------------------------
+ // Target (FY Based)
+ // -------------------------------
+ $target = $db->table('sales_target')
+ ->where('user_id', $userId)
+ ->where('fy_year', $current_fin_year)
+ ->get()
+ ->getRowArray();
+
+ $targetAmount = (float)($target['target_amount'] ?? 0);
+
+ // -------------------------------
+ // Achieved (FY Based)
+ // -------------------------------
+ $achievedAmount = (float)$this->getUserAchievedAmount($current_fin_year, $userId);
+
+ $remainingAmount = $targetAmount - $achievedAmount;
+ $achievementPercent = ($targetAmount > 0)
+ ? min(100, round(($achievedAmount / $targetAmount) * 100))
+ : 0;
+
+ // -------------------------------
+ // Activity Summary (FY Based using created_at)
+ // -------------------------------
+ $activitySummary = [
+ 'total' => $this->activityModel
+ ->where('assigned_to', $userId)
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->countAllResults(),
+
+ 'pending' => $this->activityModel
+ ->where([
+ 'assigned_to' => $userId,
+ 'status' => 'pending'
+ ])
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->countAllResults(),
+
+ 'completed' => $this->activityModel
+ ->where([
+ 'assigned_to' => $userId,
+ 'status' => 'completed'
+ ])
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->countAllResults(),
+ ];
+
+ // -------------------------------
+ // Leads Count (FY Based)
+ // -------------------------------
+ $myLeadsCount = $this->leadModel
+ ->where('assigned_to', $userId)
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->countAllResults();
+
+ // -------------------------------
+ // Upcoming Activities (FY Based)
+ // -------------------------------
+ $upcomingActivities = $this->activityModel
+ ->select('sales_activities.*, sales_actual_leads.company_name')
+ ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id')
+ ->where([
+ 'sales_activities.assigned_to' => $userId,
+ 'sales_activities.status' => 'pending'
+ ])
+ ->where('sales_activities.created_at >=', $fyStart)
+ ->where('sales_activities.created_at <=', $fyEnd)
+ ->orderBy('scheduled_date', 'ASC')
+ ->limit(3)
->findAll();
- return $achievedAmountData[0]['achieved_amount'] ?? 0.00;
+ // -------------------------------
+ // Recent Leads (FY Based)
+ // -------------------------------
+ $recentLeads = $this->leadModel
+ ->where('assigned_to', $userId)
+ ->where('created_at >=', $fyStart)
+ ->where('created_at <=', $fyEnd)
+ ->orderBy('created_at', 'DESC')
+ ->limit(5)
+ ->findAll();
+
+ // -------------------------------
+ // Final Data
+ // -------------------------------
+ $data = [
+ 'target_amt' => $targetAmount,
+ 'achieved' => $achievedAmount,
+ 'remaining' => $remainingAmount,
+ 'percent' => $achievementPercent,
+ 'acts' => $activitySummary,
+ 'lead_count' => $myLeadsCount,
+ 'upcoming' => $upcomingActivities,
+ 'recent_leads' => $recentLeads,
+ 'fin_years' => $fin_years,
+ 'current_fin_year' => $current_fin_year,
+ 'display_fin_years' => format_financial_year($current_fin_year),
+ 'user_name' => get_session_userdata()->first_name ?? '',
+ 'tab_name' => 'Sales Dashboard',
+ 'page_name' => 'Sales Dashboard',
+ ];
+
+ return $this->loadLayout('sales/sales_manager_level_dashboard', $data);
+
+ } catch (\Exception $e) {
+ return $this->failServerError($e->getMessage());
}
+}
+ // public function dashboard()
+ // {
+ // $payload = $this->request->getGet();
+ // $base = $this->getSalesStaffData();
+ // $salesRole = $base['sales_role'];
+ // $salesManagerIds = $base['sales_manager_ids'];
+ // $userId = get_session_userid();
+
+ // // Get branch id from users array
+ // $nhanceBranchId = $base['users'][0]['nhance_branch_id'] ?? null;
+
+ // if ($salesRole === 'Sales Head') {
+ // $this->branchLevelDashboard($nhanceBranchId, $salesManagerIds);
+ // // $this->salesManagerLevelDashboard($userId, $salesManagerIds,$payload);
+ // } elseif ($salesRole === 'Sales Manager') {
+ // $this->salesManagerLevelDashboard($userId, $salesManagerIds,$payload);
+ // }
+ // }
+
+ // public function branchLevelDashboard($branchId,$sales_manager_ids)
+ // {
+ // // Hardcoded branch ID as requested
+ // // $branchId = 1;
+
+ // try {
+ // $sales_manager_ids = array_values(array_map('intval', $sales_manager_ids));
+
+ // // Final safe check
+ // if (empty($sales_manager_ids)) {
+ // // No valid IDs β skip queries or return empty
+ // $total_leads = 0;
+ // $total_activity = 0;
+ // $total_completed_activity = 0;
+ // $total_pending_activity = 0;
+ // $pending_activities = [];
+ // $recent_activities = [];
+ // $teamPerformance = [];
+
+ // $leadsOverview = [];
+ // } else {
+
+ // if (empty($sales_manager_ids) || !is_array($sales_manager_ids)) {
+ // $sales_manager_ids = array_filter((array) $sales_manager_ids); // removes null, "", 0
+ // }
+
+ // // 1. Lead Statistics
+ // $total_leads = $this->leadModel->whereIn('assigned_to', $sales_manager_ids)->countAllResults(); // Use countAllResults, NOT countAll
+ // // echo $this->leadModel->getLastQuery();die();
+
+ // // 2. Total activity
+ // $total_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->countAllResults();
+
+ // // 3. Completed activity
+ // $total_completed_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->where('status', 'completed')->countAllResults();
+
+ // // 4. Pending activity
+ // $total_pending_activity = $this->activityModel->whereIn('assigned_to', $sales_manager_ids)->where('status', 'pending')->countAllResults();
+
+ // $db = \Config\Database::connect();
+
+ // // 5. Team Performance
+ // $teamPerformance = $db->table('user_profiles as u')
+ // ->select('u.first_name, u.last_name, r.role,
+ // (SELECT COUNT(*) FROM sales_activities WHERE assigned_to = u.id) as total_acts,
+ // (SELECT COUNT(*) FROM sales_activities WHERE assigned_to = u.id AND status = "completed") as done_acts')
+ // ->join('roles r', 'r.id = u.role')
+ // ->where('u.nhance_branch_id', $branchId)
+ // ->whereIn('u.id', $sales_manager_ids)
+ // ->where('u.is_active', 1)
+ // ->get()->getResultArray();
+
+ // // 6. Recent Activities (Joining for Lead Names)
+ // $recent_activities = $this->activityModel->select('sales_activities.*, sales_actual_leads.company_name')
+ // ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id')
+ // ->orderBy('sales_activities.scheduled_date', 'DESC')
+ // ->limit(6)
+ // ->findAll();
+
+ // // 7. Pending Activities (List)
+ // $pending_activities = $db->table('sales_activities sa')
+ // ->select('sa.activity_id,sa.lead_id,sa.activity_type,sa.scheduled_date,sa.status,sa.assigned_to,sal.company_name,up.first_name AS assigned_to_name,sa.notes')
+ // ->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
+ // ->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left') // β
ADD THIS
+ // ->orderBy('sa.scheduled_date', 'DESC')
+ // ->whereIn('sa.assigned_to', $sales_manager_ids)
+ // ->where('sa.status', 'pending')
+ // ->get()->getResultArray();
+
+ // // 8. All Leads Overview
+ // $leadsOverview = $db->table('sales_actual_leads sal')
+ // ->select('sal.lead_id,sal.company_name,sal.status,up.first_name AS assigned_to,
+ // COUNT(DISTINCT sa.activity_id) AS activities,
+ // COUNT(DISTINCT l.id) AS opportunities
+ // ')
+ // ->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
+ // ->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
+ // ->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
+ // ->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
+ // // ->having('COUNT(DISTINCT sa.activity_id) + COUNT(DISTINCT l.id) >', 0) // β this line
+ // ->orderBy('sal.created_at', 'DESC')
+ // ->whereIn('sal.assigned_to', $sales_manager_ids)
+ // ->get()
+ // ->getResultArray();
+
+ // // 9. Activity BrakDown
+ // $activityBreakdown = $db->table('sales_activities')
+ // ->select("activity_type, COUNT(*) AS total, ROUND(COUNT(*) * 100.0 / {$total_activity}, 0) AS percentage", false)
+ // ->whereIn('assigned_to', $sales_manager_ids)
+ // ->groupBy('activity_type')
+ // ->orderBy('total', 'DESC')
+ // ->get()
+ // ->getResultArray();
+ // }
+ // $data = [
+ // 'total_leads' => $total_leads,
+ // 'total_acts' => $total_activity,
+ // 'total_completed_acts' => $total_completed_activity,
+ // 'total_pending_acts'=> $total_pending_activity,
+ // 'pipeline_value' => '15.0L', // Hardcoded placeholder from PDF [cite: 14]
+ // 'display_fin_years' => format_financial_year($current_fin_year),
+ // 'team' => $teamPerformance,
+ // 'recent_acts' => $recent_activities,
+ // 'pending_acts' => $pending_activities,
+ // 'leads_overview' => $leadsOverview,
+ // 'activity_breakdown'=> $activityBreakdown,
+ // 'tab_name' => "Sales Dashboard",
+ // 'page_name' => "Sales Dashboard"
+ // ];
+
+ // // dd($data);
+
+ // $this->loadLayout('sales/branch_level_dashboard_view', $data);
+
+ // // return view('sales/dashboard_view', $data);
+
+ // } catch (\Exception $e) {
+ // return $this->failServerError($e->getMessage());
+ // }
+ // }
+
+ // public function salesManagerLevelDashboard($userId,$sales_manager_ids, $payload = [])
+ // {
+ // // $userId = get_session_userid();
+ // // $userId = 1;
+ // $db = \Config\Database::connect();
+
+ // try {
+
+ // // $payload = $this->request->getGet();
+ // $current_fin_year = $payload['fy'] ?? getCurrentFinancialYear();
+
+
+ // $fin_years = $db->table('sales_target')
+ // ->select('fy_year')
+ // ->where('user_id', $userId)
+ // ->orderBy('fy_year', 'desc')
+ // ->get()
+ // ->getResultArray();
+
+ // $fin_years = array_column($fin_years, 'fy_year');
+
+ // if(empty($fin_years)){
+ // $fin_years[] = $current_fin_year;
+ // }
+
+ // $target = $db->table('sales_target')
+ // ->where('user_id', $userId)
+ // ->where('fy_year', $current_fin_year)
+ // ->get()
+ // ->getRowArray();
+
+ // $targetAmount = $target['target_amount'] ?? 0.00;
+
+ // // get achieved amount from leads table
+ // $achievedAmount = $this->getUserAchievedAmount($current_fin_year, $userId);
+
+ // $remainingAmount = $targetAmount - $achievedAmount;
+ // // $achievementPercent = ($targetAmount > 0) ? round(($achievedAmount / $targetAmount) * 100) : 0;
+ // $achievementPercent = ($targetAmount > 0) ? min(100, round(($achievedAmount / $targetAmount) * 100)) : 0;
+
+ // $activitySummary = [
+ // 'total' => $this->activityModel->where('assigned_to', $userId)->countAllResults(),
+ // 'pending' => $this->activityModel->where(['assigned_to' => $userId, 'status' => 'pending'])->countAllResults(),
+ // 'completed' => $this->activityModel->where(['assigned_to' => $userId, 'status' => 'completed'])->countAllResults(),
+ // ];
+
+ // $myLeadsCount = $this->leadModel->where('assigned_to', $userId)->countAllResults();
+
+ // $upcomingActivities = $this->activityModel->select('sales_activities.*, sales_actual_leads.company_name')
+ // ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id')
+ // ->where(['sales_activities.assigned_to' => $userId, 'sales_activities.status' => 'pending'])
+ // ->orderBy('scheduled_date', 'ASC')
+ // ->limit(3)
+ // ->findAll();
+
+ // $recentLeads = $this->leadModel->where('assigned_to', $userId)
+ // ->orderBy('created_at', 'DESC')
+ // ->limit(5)
+ // ->findAll();
+
+ // $data = [
+ // 'target_amt' => $targetAmount,
+ // 'achieved' => $achievedAmount,
+ // 'remaining' => $remainingAmount,
+ // 'percent' => $achievementPercent,
+ // 'acts' => $activitySummary,
+ // 'lead_count' => $myLeadsCount,
+ // 'upcoming' => $upcomingActivities,
+ // 'recent_leads' => $recentLeads,
+ // 'fin_years' => $fin_years,
+ // 'display_fin_years' => format_financial_year($current_fin_year),
+ // 'user_name' => get_session_userdata()->first_namee ?? '',
+ // 'tab_name' => "Sales Dashboard",
+ // 'page_name' => "Sales Dashboard",
+ // 'splits' => [],
+ // 'activity_breakdown'=> [],
+ // ];
+
+
+
+ // // dd($data);
+
+ // // return view('sales/my_dashboard_view', $data);
+ // $this->loadLayout('sales/sales_manager_level_dashboard', $data);
+
+ // } catch (\Exception $e) {
+ // return $this->failServerError($e->getMessage());
+ // }
+ // }
+
+ // public function getUserAchievedAmount($financialYear, $userId)
+ // {
+ // // Split the string into two years
+ // $years = explode('-', $financialYear);
+ // $startYear = $years[0]; // 2025
+ // $endYear = $years[1]; // 2026
+
+ // // Create the timestamps
+ // $startFY = $startYear . '-04-01 00:00:00';
+ // $endFY = $endYear . '-03-31 23:59:59';
+
+ // $achievedAmountData = $this->leadModel
+ // ->select('SUM(leads.premium_amount) as achieved_amount')
+ // ->join('leads', 'sales_actual_leads.lead_id = leads.actual_lead_id')
+ // ->where('sales_actual_leads.assigned_to', $userId)
+ // ->where('leads.status', 'won')
+ // ->where('leads.updated_at >=', $startFY)
+ // ->where('leads.updated_at <=', $endFY)
+ // ->findAll();
+
+ // return $achievedAmountData[0]['achieved_amount'] ?? 0.00;
+ // }
public function addCalenderEvent($input)
{
diff --git a/app/Models/LeadsModel.php b/app/Models/LeadsModel.php
index 72ac2f59..3ba0e2bd 100644
--- a/app/Models/LeadsModel.php
+++ b/app/Models/LeadsModel.php
@@ -47,6 +47,7 @@ class LeadsModel extends Model
'proposel_data',
'status',
'notes',
+ 'lost_reason',
'created_at',
'created_by',
'updated_at',
diff --git a/app/Models/SalesActivityModel.php b/app/Models/SalesActivityModel.php
index e9a3801d..a51713dd 100644
--- a/app/Models/SalesActivityModel.php
+++ b/app/Models/SalesActivityModel.php
@@ -25,6 +25,7 @@ class SalesActivityModel extends Model
'completion_notes',
'completed_date',
'parent_activity_id',
+ 'additional_assigned_ids',
'created_by',
'updated_by'
];
@@ -64,8 +65,14 @@ class SalesActivityModel extends Model
*/
public function getActivitiesByLead($leadId, $status = null)
{
- $builder = $this->select('sales_activities.*, user_profiles.first_name as assigned_to_name')
- ->join('user_profiles', 'user_profiles.id = sales_activities.assigned_to', 'left')
+ // Convert the INT id to a string, then wrap it in JSON quotes to match ["5", "11"]
+ $subQuery = "(SELECT GROUP_CONCAT(up2.first_name SEPARATOR ', ')
+ FROM user_profiles up2
+ WHERE JSON_CONTAINS(sales_activities.additional_assigned_ids, JSON_QUOTE(CAST(up2.id AS CHAR)))
+ ) as additional_assigned_names";
+
+ $builder = $this->select("sales_activities.*, up1.first_name as assigned_to_name, $subQuery")
+ ->join('user_profiles as up1', 'up1.id = sales_activities.assigned_to', 'left')
->where('sales_activities.lead_id', $leadId);
if ($status) {
@@ -78,7 +85,7 @@ class SalesActivityModel extends Model
/**
* Get all sales_activities with filters
*/
- public function getActivitiesWithFilters($filters = [], $limit = 10, $offset = 0)
+ public function getActivitiesWithFiltersOLD($filters = [], $limit = 10, $offset = 0)
{
$this->select('sales_activities.*, sales_actual_leads.company_name, user_profiles.first_name as assigned_to_name')
->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id', 'left')
@@ -139,6 +146,90 @@ class SalesActivityModel extends Model
// ];
}
+ public function getActivitiesWithFilters($filters = [], $limit = 10, $offset = 0)
+ {
+ // 1. Initial Selection
+ $builder = $this->select("
+ sales_activities.*,
+ sales_actual_leads.company_name,
+ up1.first_name as assigned_to_name,
+ GROUP_CONCAT(DISTINCT up2.first_name SEPARATOR ', ') as additional_assigned_names
+ ")
+ ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id', 'left')
+ ->join('user_profiles as up1', 'up1.id = sales_activities.assigned_to', 'left');
+
+ // 2. The JSON Join for additional names
+ // Only attempts join if the string looks like a JSON array
+ $builder->join('user_profiles as up2', "
+ sales_activities.additional_assigned_ids IS NOT NULL
+ AND sales_activities.additional_assigned_ids != ''
+ AND sales_activities.additional_assigned_ids != '[]'
+ AND JSON_VALID(sales_activities.additional_assigned_ids)
+ AND JSON_CONTAINS(sales_activities.additional_assigned_ids, JSON_QUOTE(CAST(up2.id AS CHAR)))
+ ", 'left');
+
+ // 3. Apply Filters
+ if (!empty($filters['status'])) {
+ $builder->where('sales_activities.status', $filters['status']);
+ }
+
+ if (!empty($filters['activity_type'])) {
+ $builder->where('sales_activities.activity_type', $filters['activity_type']);
+ }
+
+ if (!empty($filters['assigned_to'])) {
+ $assignedToIds = is_array($filters['assigned_to']) ? $filters['assigned_to'] : explode(',', $filters['assigned_to']);
+ $builder->whereIn('sales_activities.assigned_to', $assignedToIds);
+ }
+
+ if (!empty($filters['search'])) {
+ $builder->groupStart()
+ ->like('sales_actual_leads.company_name', $filters['search'])
+ ->orLike('up1.first_name', $filters['search'])
+ ->groupEnd();
+ }
+
+ // 4. Grouping & Ordering
+ $builder->groupBy('sales_activities.activity_id');
+ $builder->orderBy('sales_activities.created_at', 'DESC');
+
+ // 5. Calculate Counts (using a clean builder to avoid the syntax error)
+ $counts = $this->getActivityStatusCounts($filters);
+
+ // 6. Get Data and Total
+ // Use true for countAllResults to get an accurate count of grouped rows
+ $totalCountQuery = clone $builder;
+ $total = $totalCountQuery->countAllResults(false);
+
+ $data = $builder->findAll($limit, $offset);
+
+ return [
+ 'data' => $data,
+ 'total' => $total,
+ 'counts' => $counts
+ ];
+ }
+
+ /**
+ * Helper function to get counts without breaking the main query syntax
+ */
+ private function getActivityStatusCounts($filters)
+ {
+ $validStatuses = ['pending', 'completed'];
+ $counts = ['all' => 0, 'pending' => 0, 'completed' => 0];
+
+ foreach ($validStatuses as $status) {
+ $query = $this->db->table('sales_activities')->where('status', $status);
+ if (!empty($filters['assigned_to'])) {
+ $ids = is_array($filters['assigned_to']) ? $filters['assigned_to'] : explode(',', $filters['assigned_to']);
+ $query->whereIn('assigned_to', $ids);
+ }
+ $counts[$status] = $query->countAllResults();
+ }
+ $counts['all'] = $counts['pending'] + $counts['completed'];
+ return $counts;
+ }
+
/**
* Complete an activity
*/
diff --git a/app/Models/SalesActualLeadModel.php b/app/Models/SalesActualLeadModel.php
index 22db6a2a..a5ca1026 100644
--- a/app/Models/SalesActualLeadModel.php
+++ b/app/Models/SalesActualLeadModel.php
@@ -123,7 +123,38 @@ class SalesActualLeadModel extends Model
$data = $this->findAll($limit, $offset);
- return ['data' => $data,'total' => $total];
+ $counts = $this->getLeadStatusCounts($filters);
+
+ return ['data' => $data,'total' => $total,'counts' => $counts];
+ }
+
+ /**
+ * Helper function to get counts without breaking the main query syntax
+ */
+ private function getLeadStatusCounts($filters)
+ {
+
+ $validStatuses = ['New', 'Potential', 'Prospects', 'Not a Prospects'];
+ $counts = ['all' => 0, 'New' => 0, 'Potential' => 0, 'Prospects' => 0, 'Not a Prospects' => 0];
+
+ foreach ($validStatuses as $status) {
+ $countQuery = $this->db->table('sales_actual_leads')
+ ->whereIn('status', $validStatuses);
+
+ // Apply assigned_to filter to counts too
+ if (!empty($filters['assigned_to'])) {
+ $assignedToIds = is_array($filters['assigned_to'])
+ ? $filters['assigned_to']
+ : explode(',', $filters['assigned_to']);
+ $countQuery->whereIn('assigned_to', $assignedToIds);
+ }
+
+ $counts[$status] = $countQuery->where('status', $status)->countAllResults();
+ }
+
+ $counts['all'] = array_sum($counts);
+
+ return $counts;
}
/**
diff --git a/app/Views/leads_form.php b/app/Views/leads_form.php
index eeef3d1b..089bff40 100644
--- a/app/Views/leads_form.php
+++ b/app/Views/leads_form.php
@@ -401,6 +401,12 @@
+
+
+
+
+ No Upcoming Activities for this Financial Year.
+
+
+
My Recent Leads
+
+ placeholder="Search leads..." onkeyup="fetchLeads(false)" style="width: 300px !important;">
+ Add Lead
@@ -166,7 +189,7 @@
@@ -228,7 +251,7 @@
Lead Detail
-
×
+
Γ
@@ -270,7 +293,7 @@
+
+ Additional Assigner
+
+
+ = $sm['first_name'] ?>
+
+
+