nhance/app/Controllers/SalesController.php

839 lines
28 KiB
PHP

<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Models\SalesActualLeadModel;
use App\Models\SalesContactPersonModel;
use App\Models\SalesActivityModel;
use App\Models\SalesLeadNoteModel;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\API\ResponseTrait;
class SalesController extends BaseController
{
use ResponseTrait;
protected $leadModel;
protected $contactModel;
protected $activityModel;
protected $noteModel;
public function __construct()
{
$this->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;
}
}