leadModel = new SalesActualLeadModel(); $this->contactModel = new SalesContactPersonModel(); $this->activityModel = new SalesActivityModel(); $this->noteModel = new SalesLeadNoteModel(); } public function index() { $db = \Config\Database::connect(); // Fetch users for the assignment dropdowns $data['users'] = $db->table('user_profiles') ->select('id, first_name, last_name') ->where('is_active', 1) ->get()->getResultArray(); $this->loadLayout('sales/tracker_view', $data); } public function completeActivity($id) { try { $data = $this->request->getJSON(true); $data['updated_by'] = $this->getUserId(); // 1. Mark current activity as completed $this->activityModel->completeActivity((int)$id, [ 'completion_notes' => $data['completion_notes'], 'updated_by' => $data['updated_by'] ]); // 2. Handle follow-up if requested if (!empty($data['schedule_followup']) && $data['schedule_followup'] === 'yes') { $activity = $this->activityModel->find($id); $this->activityModel->insert([ 'lead_id' => $activity['lead_id'], 'activity_type' => $data['followup_type'], 'notes' => $data['followup_notes'], 'scheduled_date' => $data['followup_schedule'], 'assigned_to' => $activity['assigned_to'], 'status' => 'pending', 'created_by' => $this->getUserId() ]); } return $this->respond(['status' => 'success', 'message' => 'Activity updated']); } catch (\Exception $e) { return $this->failServerError($e->getMessage()); } } /** * Corrected createLead to handle assigned_to as ID */ public function createLead() { try { $data = $this->request->getJSON(true); $data['created_by'] = $this->getUserId(); // Ensure assigned_to is a valid integer from user_profiles if (empty($data['assigned_to'])) { return $this->fail('Please assign this lead to a user.'); } if (!$this->leadModel->insert($data)) { return $this->fail($this->leadModel->errors()); } return $this->respondCreated(['status' => 'success', 'id' => $this->leadModel->getInsertID()]); } catch (\Exception $e) { return $this->failServerError($e->getMessage()); } } // ==================== LEAD APIs ==================== /** * Get all leads with pagination and filters * GET /api/sales/leads */ public function getLeads() { try { $limit = $this->request->getGet('limit') ?? 10; $offset = $this->request->getGet('offset') ?? 0; $filters = [ 'status' => $this->request->getGet('status'), 'assigned_to' => $this->request->getGet('assigned_to'), 'search' => $this->request->getGet('search'), ]; $result = $this->leadModel->getLeadsWithFilters($filters, $limit, $offset); return $this->respond([ 'status' => 'success', 'data' => $result['data'], 'total' => $result['total'], 'limit' => $limit, 'offset' => $offset ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get single lead with complete details * GET /api/sales/leads/{id} */ public function getLead($id) { try { $lead = $this->leadModel->getLeadComplete($id); if (!$lead) { return $this->failNotFound('Lead not found'); } return $this->respond([ 'status' => 'success', 'data' => $lead ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Update lead * PUT /api/sales/leads/{id} */ public function updateLead($id) { try { if (!$this->leadModel->find((int)$id)) { return $this->failNotFound('Lead not found'); } $data = $this->request->getJSON(true); $data['updated_by'] = $this->getUserId(); if (!$this->leadModel->update($id, $data)) { return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $lead = $this->leadModel->getLeadComplete($id); return $this->respond([ 'status' => 'success', 'message' => 'Lead updated successfully', 'data' => $lead ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Delete lead * DELETE /api/sales/leads/{id} */ public function deleteLead($id) { try { if (!$this->leadModel->find((int)$id)) { return $this->failNotFound('Lead not found'); } $this->leadModel->delete($id); return $this->respondDeleted([ 'status' => 'success', 'message' => 'Lead deleted successfully' ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get lead statistics * GET /api/sales/leads/stats */ public function getLeadStats() { try { $userId = $this->request->getGet('user_id'); $stats = $this->leadModel->getLeadStats($userId); return $this->respond([ 'status' => 'success', 'data' => $stats ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } // ==================== CONTACT PERSON APIs ==================== /** * Get contacts for a lead * GET /api/sales/leads/{leadId}/contacts */ public function getContacts($leadId) { try { $contacts = $this->contactModel->getContactsByLead($leadId); return $this->respond([ 'status' => 'success', 'data' => $contacts ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Create contact person * POST /api/sales/contacts */ public function createContact() { try { $data = $this->request->getJSON(true); $data['created_by'] = $this->getUserId(); $data['updated_by'] = $this->getUserId(); if (!$this->contactModel->insert($data)) { return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $contactId = $this->contactModel->getInsertID(); $contact = $this->contactModel->find((int)$contactId); return $this->respondCreated([ 'status' => 'success', 'message' => 'Contact created successfully', 'data' => $contact ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Update contact person * PUT /api/sales/contacts/{id} */ public function updateContact($id) { try { if (!$this->contactModel->find((int)$id)) { return $this->failNotFound('Contact not found'); } $data = $this->request->getJSON(true); $data['updated_by'] = $this->getUserId(); if (!$this->contactModel->update((int)$id, $data)) { return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $contact = $this->contactModel->find((int)$id); return $this->respond([ 'status' => 'success', 'message' => 'Contact updated successfully', 'data' => $contact ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Delete contact person * DELETE /api/sales/contacts/{id} */ public function deleteContact($id) { try { if (!$this->contactModel->find((int)$id)) { return $this->failNotFound('Contact not found'); } $this->contactModel->delete((int)$id); return $this->respondDeleted([ 'status' => 'success', 'message' => 'Contact deleted successfully' ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Set primary contact * PUT /api/sales/contacts/{id}/set-primary */ public function setPrimaryContact($id) { try { $contact = $this->contactModel->find((int)$id); if (!$contact) { return $this->failNotFound('Contact not found'); } $this->contactModel->setPrimaryContact($id, $contact['lead_id']); return $this->respond([ 'status' => 'success', 'message' => 'Primary contact updated successfully' ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } // ==================== ACTIVITY APIs ==================== /** * Get all activities with filters * GET /api/sales/activities */ public function getActivities() { try { $limit = $this->request->getGet('limit') ?? 10; $offset = $this->request->getGet('offset') ?? 0; $filters = [ 'status' => $this->request->getGet('status'), 'activity_type' => $this->request->getGet('activity_type'), 'assigned_to' => $this->request->getGet('assigned_to'), 'date_from' => $this->request->getGet('date_from'), 'date_to' => $this->request->getGet('date_to'), ]; $result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset); return $this->respond([ 'status' => 'success', 'data' => $result['data'], 'total' => $result['total'], 'limit' => $limit, 'offset' => $offset ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get activities for a specific lead * GET /api/sales/leads/{leadId}/activities */ public function getLeadActivities($leadId) { try { $status = $this->request->getGet('status'); $activities = $this->activityModel->getActivitiesByLead($leadId, $status); return $this->respond([ 'status' => 'success', 'data' => $activities ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get activity timeline for a lead * GET /api/sales/leads/{leadId}/timeline */ public function getActivityTimeline($leadId) { try { $timeline = $this->activityModel->getActivityTimeline($leadId); return $this->respond([ 'status' => 'success', 'data' => $timeline ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get upcoming activities for user * GET /api/sales/activities/upcoming */ public function getUpcomingActivities() { try { $userId = $this->getUserId(); $days = $this->request->getGet('days') ?? 7; $limit = $this->request->getGet('limit') ?? 10; $activities = $this->activityModel->getUpcomingActivities($userId, $days, $limit); return $this->respond([ 'status' => 'success', 'data' => $activities ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Create activity * POST /api/sales/activities */ public function createActivity() { try { $data = $this->request->getJSON(true); $data['created_by'] = $this->getUserId(); $data['updated_by'] = $this->getUserId(); if (!$this->activityModel->insert($data)) { return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $activityId = $this->activityModel->getInsertID(); $activity = $this->activityModel->find((int)$activityId); return $this->respondCreated([ 'status' => 'success', 'message' => 'Activity created successfully', 'data' => $activity ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Update activity * PUT /api/sales/activities/{id} */ public function updateActivity($id) { try { if (!$this->activityModel->find((int)$id)) { return $this->failNotFound('Activity not found'); } $data = $this->request->getJSON(true); $data['updated_by'] = $this->getUserId(); if (!$this->activityModel->update($id, $data)) { return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $activity = $this->activityModel->find((int)$id); return $this->respond([ 'status' => 'success', 'message' => 'Activity updated successfully', 'data' => $activity ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Delete activity * DELETE /api/sales/activities/{id} */ public function deleteActivity($id) { try { if (!$this->activityModel->find((int)$id)) { return $this->failNotFound('Activity not found'); } $this->activityModel->delete((int)$id); return $this->respondDeleted([ 'status' => 'success', 'message' => 'Activity deleted successfully' ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Get pending activities count * GET /api/sales/activities/pending-count */ public function getPendingCount() { try { $userId = $this->getUserId(); $count = $this->activityModel->getPendingActivitiesCount($userId); return $this->respond([ 'status' => 'success', 'data' => ['count' => $count] ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } // ==================== LEAD NOTES APIs ==================== /** * Get notes for a lead * GET /api/sales/leads/{leadId}/notes */ public function getLeadNotes($leadId) { try { $notes = $this->noteModel->getNotesByLead($leadId); return $this->respond([ 'status' => 'success', 'data' => $notes ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Create note * POST /api/sales/notes */ public function createNote() { try { $data = $this->request->getJSON(true); $data['user_id'] = $this->getUserId(); $data['created_by'] = $this->getUserId(); $data['updated_by'] = $this->getUserId(); if (!$this->noteModel->insert($data)) { return $this->fail($this->noteModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $noteId = $this->noteModel->getInsertID(); $note = $this->noteModel->find((int)$noteId); return $this->respondCreated([ 'status' => 'success', 'message' => 'Note created successfully', 'data' => $note ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Update note * PUT /api/sales/notes/{id} */ public function updateNote($id) { try { $note = $this->noteModel->find((int)$id); if (!$note) { return $this->failNotFound('Note not found'); } // Check if user owns the note if ($note['user_id'] != $this->getUserId()) { return $this->failUnauthorized('You are not authorized to update this note'); } $data = $this->request->getJSON(true); $data['updated_by'] = $this->getUserId(); if (!$this->noteModel->update($id, $data)) { return $this->fail($this->noteModel->errors(), ResponseInterface::HTTP_BAD_REQUEST); } $updatedNote = $this->noteModel->find((int)$id); return $this->respond([ 'status' => 'success', 'message' => 'Note updated successfully', 'data' => $updatedNote ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } /** * Delete note * DELETE /api/sales/notes/{id} */ public function deleteNote($id) { try { $note = $this->noteModel->find((int)$id); if (!$note) { return $this->failNotFound('Note not found'); } // Check if user owns the note if ($note['user_id'] != $this->getUserId()) { return $this->failUnauthorized('You are not authorized to delete this note'); } $this->noteModel->delete((int)$id); return $this->respondDeleted([ 'status' => 'success', 'message' => 'Note deleted successfully' ]); } catch (\Exception $e) { return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR); } } // ==================== HELPER METHODS ==================== /** * Get current user ID from session or JWT token * You'll need to implement this based on your authentication system */ private function getUserId() { // If using session if (session()->has('user_id')) { return session()->get('user_id'); } // If using JWT, decode token and get user_id // Example: return $this->decodeJWT()['user_id']; // For development/testing, you can return a default value return 1; } // ==================== Dashboard ==================== public function branchLevelDashboard() { // Hardcoded branch ID as requested $branchId = 1; try { // 1. Lead Statistics $stats = $this->leadModel->getLeadStats(); // Using existing model method // 2. Activity Statistics $activityStats = [ 'total' => $this->activityModel->countAllResults(), 'completed' => $this->activityModel->where('status', 'completed')->countAllResults(), 'pending' => $this->activityModel->where('status', 'pending')->countAllResults(), ]; // 3. Team Performance (Aggregating activity counts per user) $db = \Config\Database::connect(); $teamPerformance = $db->table('user_profiles as u') ->select('u.first_name, u.last_name, u.profile as 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') ->where('u.nhance_branch_id', $branchId) ->where('u.is_active', 1) ->get()->getResultArray(); // 4. Recent Activities (Joining for Lead Names) $recentActivities = $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(); // 5. All Leads Overview $leadsOverview = $this->leadModel->select('sales_actual_leads.*, user_profiles.first_name, user_profiles.last_name') ->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left') ->findAll(); $data = [ 'total_leads' => $stats['total'], 'total_activities' => $activityStats['total'], 'completed_acts' => $activityStats['completed'], 'pipeline_value' => '15.0L', // Hardcoded placeholder from PDF [cite: 14] 'team' => $teamPerformance, 'recent_acts' => $recentActivities, 'leads_overview' => $leadsOverview ]; // 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 = 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'); $fin_years[] = '2024-2025'; $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' => $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 ?? '' ]; // 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; } }