1131 lines
39 KiB
PHP
1131 lines
39 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 App\Models\UserModel;
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
class SalesController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected $leadModel;
|
|
protected $contactModel;
|
|
protected $activityModel;
|
|
protected $noteModel;
|
|
protected $userModel;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->leadModel = new SalesActualLeadModel();
|
|
$this->contactModel = new SalesContactPersonModel();
|
|
$this->activityModel = new SalesActivityModel();
|
|
$this->noteModel = new SalesLeadNoteModel();
|
|
$this->userModel = new UserModel();
|
|
}
|
|
|
|
|
|
public function index() {
|
|
$data = $this->getSalesStaffData();
|
|
|
|
$data['tab_name'] = 'Leads';
|
|
$data['page_name'] = 'Leads';
|
|
return $this->loadLayout('sales/tracker_view', $data);
|
|
}
|
|
|
|
public function loadactivities(){
|
|
$data = $this->getSalesStaffData();
|
|
|
|
$data['tab_name'] = 'Activities';
|
|
$data['page_name'] = 'Activities';
|
|
|
|
$data['leads'] = $this->leadModel->select('lead_id, company_name')
|
|
->orderBy('lead_id', 'DESC')
|
|
->findAll();
|
|
|
|
|
|
return $this->loadLayout('sales/activity_view', $data);
|
|
}
|
|
|
|
/**
|
|
* HELPER: Fetches Sales Managers based on the logged-in user's role and branch
|
|
*/
|
|
private function getSalesStaffData(): array
|
|
{
|
|
$db = \Config\Database::connect();
|
|
$logged_user_id = get_session_userid();
|
|
$role = get_role_id();
|
|
$team_id = user_team();
|
|
|
|
$data = [
|
|
'users' => [],
|
|
'sales_manager_ids' => []
|
|
];
|
|
|
|
$row = $db->table('user_profiles')->select('*')
|
|
->where('is_active', 1)->where('id', $logged_user_id)
|
|
->get()->getRow();
|
|
|
|
$nhance_branch_id = $row ? $row->nhance_branch_id : null;
|
|
|
|
// Is the logged-in user a Sales Manager? (Role 4, Team 5)
|
|
if ($role == 4 && in_array(5, $team_id)) {
|
|
$data['sales_manager_ids'] = [$logged_user_id];
|
|
|
|
$data['users'] = [
|
|
[
|
|
'id' => $row->id,
|
|
'first_name' => $row->first_name,
|
|
'nhance_branch_id' => $nhance_branch_id
|
|
]
|
|
];
|
|
|
|
}
|
|
// Otherwise fetch ALL sales managers in this branch
|
|
elseif (in_array($role,[1,5])) {
|
|
|
|
$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();
|
|
|
|
$data['sales_manager_ids'] = array_column($data['users'], 'id');
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function noPage($type = null)
|
|
{
|
|
|
|
$page_type = $type ?? 'Page';
|
|
|
|
$data['tab_name'] = 'Sales ' . $page_type;
|
|
$data['page_name'] = 'Sales ' . $page_type;
|
|
|
|
echo view('layout/header', $data);
|
|
echo '<div class="col-12">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h3>Oops! ' . esc($page_type) . ' is under construction.</h3>
|
|
<img src="https://omjsblog.files.wordpress.com/2023/07/errorimg.png" alt="Error Image" style="max-width: 100%; height: auto;">
|
|
</div>
|
|
</div>
|
|
</div>';
|
|
echo view('layout/footer', $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
|
|
$google_calender_response = [];
|
|
if (!empty($data['schedule_followup']) && $data['schedule_followup'] === 'yes') {
|
|
|
|
$activity = $this->activityModel->find($id);
|
|
$activity_data = [
|
|
'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()
|
|
];
|
|
$this->activityModel->insert($activity_data);
|
|
|
|
// add google calender event
|
|
$google_calender_response = $this->addCalenderEvent($activity_data);
|
|
}
|
|
|
|
return $this->respond(['status' => 'success', 'message' => 'Activity updated', 'google_calender_response' => $google_calender_response]);
|
|
} 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
|
|
], 200);
|
|
|
|
} 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);
|
|
}
|
|
|
|
// add google calender event
|
|
$response = $this->addCalenderEvent($data);
|
|
|
|
$activityId = $this->activityModel->getInsertID();
|
|
$activity = $this->activityModel->find((int)$activityId);
|
|
|
|
return $this->respondCreated([
|
|
'status' => 'success',
|
|
'message' => 'Activity created successfully',
|
|
'data' => $activity,
|
|
'google_calender_response' => $response
|
|
]);
|
|
} 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 dashboard(){
|
|
|
|
$logged_user_id = get_session_userid();
|
|
$role = get_role_id();
|
|
$team_id = user_team();
|
|
$payload = $this->request->getGet();
|
|
|
|
$db = \Config\Database::connect();
|
|
|
|
$row = $db->table('user_profiles')
|
|
->select('*')
|
|
->where('is_active', 1)
|
|
->where('id', $logged_user_id)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
$nhance_branch_id = $row ? $row['nhance_branch_id'] : null;
|
|
|
|
// dd($logged_user_id, $nhance_branch_id, $role, $team_id );
|
|
|
|
if (in_array($role,[1,5])) {
|
|
|
|
$sales_manager_ids = array_column(
|
|
$db->table('user_profiles up')
|
|
->select('up.id')
|
|
->join('user_teams ut', 'ut.user_id = up.id')
|
|
->where([
|
|
'up.is_active' => 1,
|
|
'ut.is_active' => 1,
|
|
'up.role' => 4,
|
|
'ut.team_id' => 5,
|
|
'up.nhance_branch_id' => $nhance_branch_id
|
|
])
|
|
->get()
|
|
->getResultArray(),
|
|
'id'
|
|
);
|
|
$this->branchLevelDashboard($nhance_branch_id,$sales_manager_ids);
|
|
|
|
}
|
|
elseif ($role == 4 && in_array(5, $team_id)) {
|
|
$sales_manager_ids = [$logged_user_id];
|
|
$this->salesManagerLevelDashboard($logged_user_id,$sales_manager_ids, $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]
|
|
'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' => $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());
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
try {
|
|
|
|
log_message('error', '[GOOGLE_CALENDER] Calendar Event Input: ' . json_encode($input));
|
|
|
|
/* ---------------- VALIDATION ---------------- */
|
|
|
|
if (empty($input['lead_id']) || empty($input['assigned_to']) || empty($input['scheduled_date'])) {
|
|
log_message('error', '[GOOGLE_CALENDER] Calendar Event Missing Required Data');
|
|
return [
|
|
'status' => false,
|
|
'message' => 'Required data missing'
|
|
];
|
|
}
|
|
|
|
/* ---------------- FETCH DATA ---------------- */
|
|
|
|
$lead_data = $this->leadModel
|
|
->where('lead_id', $input['lead_id'])
|
|
->first();
|
|
|
|
if (!$lead_data) {
|
|
log_message('error', '[GOOGLE_CALENDER] Lead Not Found: ' . $input['lead_id']);
|
|
return [
|
|
'status' => false,
|
|
'message' => 'Lead not found'
|
|
];
|
|
}
|
|
|
|
$user_data = $this->userModel
|
|
->where('id', $input['assigned_to'])
|
|
->first();
|
|
|
|
if (!$user_data) {
|
|
log_message('error', '[GOOGLE_CALENDER] User Not Found: ' . $input['assigned_to']);
|
|
return [
|
|
'status' => false,
|
|
'message' => 'Assigned user not found'
|
|
];
|
|
}
|
|
|
|
/* ---------------- SUMMARY ---------------- */
|
|
|
|
$summary = ucfirst($input['activity_type']) .
|
|
' with ' .
|
|
$lead_data['company_name'];
|
|
|
|
/* ---------------- GOOGLE PAYLOAD ---------------- */
|
|
|
|
$eventData = [
|
|
'summary' => $summary,
|
|
'meeting_date' => $input['scheduled_date'],
|
|
'description' => $input['notes'] ?? '',
|
|
'emails' => [$user_data['email']],
|
|
];
|
|
|
|
log_message('error', '[GOOGLE_CALENDER] Google Calendar Payload: ' . json_encode($eventData));
|
|
|
|
/* ---------------- API CALL ---------------- */
|
|
|
|
$response = add_google_calender_event($eventData);
|
|
|
|
log_message('error', '[GOOGLE_CALENDER] Google Calendar Response: ' . json_encode($response));
|
|
|
|
return $response;
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
log_message('error', '[GOOGLE_CALENDER] Calendar Event Exception: ' . $e->getMessage());
|
|
log_message('error', '[GOOGLE_CALENDER] Calendar Event Trace: ' . $e->getTraceAsString());
|
|
|
|
return [
|
|
'status' => false,
|
|
'message' => 'Calendar event failed',
|
|
'error' => $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
} |