diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php index 133d6ad1..b69fe231 100644 --- a/app/Controllers/ApiServiceController.php +++ b/app/Controllers/ApiServiceController.php @@ -518,7 +518,7 @@ class ApiServiceController extends BaseController $userParams['phone'] = $row['phone']; $userParams['email'] = $row['email']; $userParams['relationship'] = $row['relation']; - $userParams['gender'] = $row['gender']; + $userParams['gender'] = $row['gender'] == 'M' ? 'Male' : 'Female'; $userParams['dob'] = $row['dob']; $userParams['policyNumber'] = $row['policyNumber']; $userParams['policyStartDate'] = $row['policyStartDate']; diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 6019a866..dda0402e 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -692,7 +692,8 @@ class EmployeeController extends AdminController return $this->response->download($filePath, null, $mimeType); } else { // File not found, show an error message or redirect - echo view('errors/html/production'); + echo view('errors/404.php', ['message' => 'Sample file not found']); + // return redirect()->back()->with('error', 'Sample file not found'); } } @@ -3852,7 +3853,7 @@ class EmployeeController extends AdminController foreach ($families as $empCode => $members) { $familiesPayload[$empCode] = $this->buildFamilyPayload($empCode, $members); } - + log_message('error', "[WellnessOnboard] Page {$page} API calls done. Families processed: " . json_encode($familiesPayload)); // ---------------------------------------------------------------- // 5. SEND TO WELLNESS API // ---------------------------------------------------------------- diff --git a/app/Controllers/EmployeeMultiEventServiceController.php b/app/Controllers/EmployeeMultiEventServiceController.php index 4f51f1ad..fef1e2f3 100644 --- a/app/Controllers/EmployeeMultiEventServiceController.php +++ b/app/Controllers/EmployeeMultiEventServiceController.php @@ -227,7 +227,7 @@ class EmployeeMultiEventServiceController extends BaseController 'format' => 'mobile', 'allowed_values' => null, 'custom' => 'check_dup_mobileno', - 'params' => ['row', 'existing_mobilenos'] + 'params' => ['row', 'existing_mobilenos', 'excel_data', 'row_key'] ], 'email' => [ 'col_idx' => 13, @@ -238,7 +238,7 @@ class EmployeeMultiEventServiceController extends BaseController 'format' => null, 'allowed_values' => null, 'custom' => 'check_dup_email', - 'params' => ['row', 'existing_mobilenos'] + 'params' => ['row', 'existing_mobilenos', 'excel_data', 'row_key'] ], 'pre_existing_ailments' => [ 'col_idx' => 14, diff --git a/app/Controllers/EmployeeServiceController.php b/app/Controllers/EmployeeServiceController.php index 064d1dc5..ecf2d49a 100755 --- a/app/Controllers/EmployeeServiceController.php +++ b/app/Controllers/EmployeeServiceController.php @@ -230,7 +230,7 @@ class EmployeeServiceController extends AdminController 'format' => 'mobile', 'allowed_values' => null, 'custom' => 'check_dup_mobileno', - 'params' => ['row', 'existing_mobilenos'] + 'params' => ['row', 'existing_mobilenos', 'excel_data', 'row_key'] ], 'email' => [ 'col_idx' => 13, @@ -241,7 +241,7 @@ class EmployeeServiceController extends AdminController 'format' => null, 'allowed_values' => null, 'custom' => 'check_dup_email', - 'params' => ['row', 'existing_mobilenos'] + 'params' => ['row', 'existing_mobilenos', 'excel_data', 'row_key'] ], 'pre_existing_ailments' => [ 'col_idx' => 14, diff --git a/app/Controllers/SalesController.php b/app/Controllers/SalesController.php index d42bea40..100ff01a 100644 --- a/app/Controllers/SalesController.php +++ b/app/Controllers/SalesController.php @@ -40,6 +40,7 @@ class SalesController extends BaseController public function index() { $data = $this->getSalesStaffData(); + $data = array_merge($data, $this->getSalesFilterYears()); $data['tab_name'] = 'Leads'; $data['page_name'] = 'Leads'; @@ -48,6 +49,7 @@ class SalesController extends BaseController public function loadactivities(){ $data = $this->getSalesStaffData(); + $data = array_merge($data, $this->getSalesFilterYears()); $data['tab_name'] = 'Activities'; $data['page_name'] = 'Activities'; @@ -350,7 +352,9 @@ class SalesController extends BaseController 'status' => $this->request->getGet('status'), 'assigned_to' => $this->request->getGet('assigned_to'), 'search' => $this->request->getGet('search'), + 'financial_year' => $this->request->getGet('financial_year'), ]; + $filters = $this->applyFinancialYearDateRange($filters); $result = $this->leadModel->getLeadsWithFilters($filters, $limit, $offset); @@ -732,8 +736,10 @@ class SalesController extends BaseController 'assigned_to' => $this->request->getGet('assigned_to'), 'date_from' => $this->request->getGet('date_from'), 'date_to' => $this->request->getGet('date_to'), + 'financial_year' => $this->request->getGet('financial_year'), 'search' => $this->request->getGet('search'), // ← ADD THIS ]; + $filters = $this->applyFinancialYearDateRange($filters); $result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset); @@ -1205,6 +1211,46 @@ class SalesController extends BaseController return 1; } + private function getSalesFilterYears(): array + { + $currentFinYear = getCurrentFinancialYear(); + $db = \Config\Database::connect(); + + $finYearsRaw = $db->table('sales_target') + ->select('fy_year', false) + ->distinct() + ->orderBy('fy_year', 'DESC') + ->get() + ->getResultArray(); + + $finYears = array_column($finYearsRaw, 'fy_year'); + if (empty($finYears)) { + $finYears[] = $currentFinYear; + } + + if (!in_array($currentFinYear, $finYears, true)) { + array_unshift($finYears, $currentFinYear); + } + + return [ + 'fin_years' => $finYears, + 'current_fin_year' => $currentFinYear, + ]; + } + + private function applyFinancialYearDateRange(array $filters): array + { + if (empty($filters['financial_year'])) { + return $filters; + } + + $fyRange = $this->getFYDateRange((string) $filters['financial_year']); + $filters['date_from'] = $fyRange['start']; + $filters['date_to'] = $fyRange['end']; + + return $filters; + } + // ==================== Dashboard ==================== @@ -1392,6 +1438,7 @@ public function branchLevelDashboard($branchId, $branchwise_all_sales_team_ids, // 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, + sal.assigned_to AS assigned_to_id, 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, @@ -1403,7 +1450,7 @@ public function branchLevelDashboard($branchId, $branchwise_all_sales_team_ids, ->whereIn('sal.assigned_to', $branchwise_all_sales_team_ids) ->where('sal.created_at >=', $fyStart) ->where('sal.created_at <=', $fyEnd) - ->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name') + ->groupBy('sal.lead_id, sal.company_name, sal.status, sal.assigned_to, up.first_name') ->orderBy('sal.created_at', 'DESC') ->get() ->getResultArray(); @@ -1497,6 +1544,7 @@ private function buildTeamAchievement($db, array $sales_manager_ids, $branchId, ->get() ->getRowArray(); $targetAmt = (float)($targetRow['target_amount'] ?? 0); + $targetId = $targetRow['id'] ?? null; // Achieved (won leads in FY) $achievedAmt = (float)$this->getUserAchievedAmount($fy, $uid); @@ -1589,6 +1637,7 @@ private function buildTeamAchievement($db, array $sales_manager_ids, $branchId, 'role' => $m['role'], 'total_acts' => $totalActs, 'done_acts' => $doneActs, + 'target_id' => $targetId, 'target_amt' => $targetAmt, 'achieved_amt' => $achievedAmt, 'grad' => $palette['grad'], diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index 6f64777c..bcfb183a 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -1162,28 +1162,28 @@ class TicketController extends BaseController // --- EMPLOYEE DETAILS --- 'emp_code' => [ 'label' => 'Employee ID', - 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee ID is required.', - 'regex_match' => 'Employee ID cannot contain spaces or special characters (only letters, numbers, hyphens and underscores. allowed).' + 'regex_match' => 'Employee ID can only contain letters, numbers, spaces, slashes (/), hyphens (-) and underscores (_).' ] ], 'emp_name' => [ 'label' => 'Employee Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee Name is required', 'min_length' => 'Employee Name must be at least 3 characters long', - 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'insured_name' => [ 'label' => 'Insured Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Insured Name is required', 'min_length' => 'Insured Name must be at least 3 characters long', - 'regex_match' => 'Insured Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Insured Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'relationship' => ['label' => 'Relationship','rules' => 'required', @@ -1393,19 +1393,19 @@ class TicketController extends BaseController // --- EMPLOYEE DETAILS --- 'emp_code' => [ 'label' => 'Employee ID', - 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee ID is required.', - 'regex_match' => 'Employee ID cannot contain spaces or special characters (only letters, numbers, hyphens and underscores. allowed).', + 'regex_match' => 'Employee ID can only contain letters, numbers, spaces, slashes (/), hyphens (-) and underscores (_).', ] ], 'emp_name' => [ 'label' => 'Employee Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee Name is required', 'min_length' => 'Employee Name must be at least 3 characters long', - 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]', @@ -1650,28 +1650,28 @@ class TicketController extends BaseController // --- EMPLOYEE DETAILS --- 'emp_code' => [ 'label' => 'Employee ID', - 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee ID is required.', - 'regex_match' => 'Employee ID cannot contain spaces or special characters (only letters, numbers, hyphens and underscores. allowed).', + 'regex_match' => 'Employee ID can only contain letters, numbers, spaces, slashes (/), hyphens (-) and underscores (_).', ] ], 'emp_name' => [ 'label' => 'Employee Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee Name is required', 'min_length' => 'Employee Name must be at least 3 characters long', - 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'insured_name' => [ 'label' => 'Insured Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Insured Name is required', 'min_length' => 'Insured Name must be at least 3 characters long', - 'regex_match' => 'Insured Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Insured Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'relationship' => ['label' => 'Relationship','rules' => 'required', @@ -1882,19 +1882,19 @@ class TicketController extends BaseController // --- EMPLOYEE DETAILS --- 'emp_code' => [ 'label' => 'Employee ID', - 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9_-]+$/]', + 'rules' => 'required|min_length[2]|max_length[20]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee ID is required.', - 'regex_match' => 'Employee ID cannot contain spaces or special characters (only letters, numbers, hyphens and underscores. allowed).', + 'regex_match' => 'Employee ID can only contain letters, numbers, spaces, slashes (/), hyphens (-) and underscores (_).', ] ], 'emp_name' => [ 'label' => 'Employee Name', - 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', + 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s\/_-]+$/]', 'errors' => [ 'required' => 'Employee Name is required', 'min_length' => 'Employee Name must be at least 3 characters long', - 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, hyphens, and underscores.' + 'regex_match' => 'Employee Name can only contain letters, numbers, spaces, slashes (/), hyphens (-), and underscores (_).' ] ], 'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]', diff --git a/app/Helpers/excel_util_helper.php b/app/Helpers/excel_util_helper.php index 0b7d0039..5aa68990 100755 --- a/app/Helpers/excel_util_helper.php +++ b/app/Helpers/excel_util_helper.php @@ -2107,8 +2107,8 @@ if (!function_exists('remap_default_age_ratio_into_relationship')) { } } -if (!function_exists('check_dup_mobileno')) { - function check_dup_mobileno(array $row, array $existing_mobilenos) +if (!function_exists('check_dup_mobileno_old')) { + function check_dup_mobileno_old(array $row, array $existing_mobilenos) { if(!empty($row['12'])){ foreach ($existing_mobilenos as $k => $value) { @@ -2122,6 +2122,41 @@ if (!function_exists('check_dup_mobileno')) { } } +if (!function_exists('check_dup_mobileno')) { + function check_dup_mobileno(array $row, array $existing_mobilenos, $excel_data = [], $row_key = null) + { + if(!empty($row['12'])){ + $current_relation = strtolower(trim($row['5'] ?? '')); + $current_mobile = trim((string) $row['12']); + + foreach ($existing_mobilenos as $k => $value) { + if ($current_relation == 'self' && $current_mobile == trim((string) ($value['mobile'] ?? ''))) { + return array('status' => false, 'error' => "Duplicate Mobile No"); + // break; + } + } + + foreach ($excel_data as $ed_row => $value) { + if ($row_key !== null && (string) $ed_row === (string) $row_key) { + continue; + } + + $excel_relation = strtolower(trim($value['5'] ?? '')); + $excel_mobile = trim((string) ($value['12'] ?? '')); + + if ($current_relation == 'self' && $excel_relation == 'self' && $excel_mobile !== '' && $current_mobile == $excel_mobile) { + $duplicate_row_no = is_numeric($ed_row) ? $ed_row + 1 : $ed_row; + $current_row_no = is_numeric($row_key) ? $row_key + 1 : $row_key; + return array('status' => false, 'error' => "This mobile number has already been used for another self in Excel at row no. " . ($duplicate_row_no) . " and " . ($current_row_no)); + // break; + } + } + + } + return array('status' => true); + } +} + // if (!function_exists('check_dup_email')) { // function check_dup_email(array $row, array $existing_mobilenos) // { @@ -2138,8 +2173,8 @@ if (!function_exists('check_dup_mobileno')) { // } // } -if (!function_exists('check_dup_email')) { - function check_dup_email(array $row, array $existing_mobilenos) +if (!function_exists('check_dup_email_old')) { + function check_dup_email_old(array $row, array $existing_mobilenos) { $errorMessages = []; @@ -2168,6 +2203,54 @@ if (!function_exists('check_dup_email')) { } } +if (!function_exists('check_dup_email')) { + function check_dup_email(array $row, array $existing_mobilenos, $excel_data = [], $row_key = null) + { + $errorMessages = []; + $current_relation = strtolower(trim($row['5'] ?? '')); + $current_email = trim((string) ($row['13'] ?? '')); + + // Validate email + $emailCheck = is_valid_or_empty_email($current_email); + if (!$emailCheck) { + $errorMessages[] = 'Invalid email format'; + } + + // Check duplicate only if email is not empty + if (!empty($current_email)) { + foreach ($existing_mobilenos as $value) { + if ($current_relation === 'self' && strcasecmp($current_email, trim((string) ($value['email_corporate'] ?? ''))) === 0) { + $errorMessages[] = 'Duplicate Email'; + break; + } + } + + foreach ($excel_data as $ed_row => $value) { + if ($row_key !== null && (string) $ed_row === (string) $row_key) { + continue; + } + + $excel_relation = strtolower(trim($value['5'] ?? '')); + $excel_email = trim((string) ($value['13'] ?? '')); + + if ($current_relation === 'self' && $excel_relation === 'self' && $excel_email !== '' && strcasecmp($current_email, $excel_email) === 0) { + $duplicate_row_no = is_numeric($ed_row) ? $ed_row + 1 : $ed_row; + $current_row_no = is_numeric($row_key) ? $row_key + 1 : $row_key; + $errorMessages[] = "This email has already been used for another self in Excel at row no. " . ($duplicate_row_no) . " and " . ($current_row_no); + break; + } + } + } + + // Prepare final return + if (!empty($errorMessages)) { + return ['status' => false, 'error' => implode(' & ', $errorMessages)]; + } + + return ['status' => true, 'error' => '']; + } +} + if (!function_exists('generate_family_relationship_array')) { function generate_family_relationship_array($family_structure_from_policy_terms) diff --git a/app/Models/SalesActivityModel.php b/app/Models/SalesActivityModel.php index 9f8d4c7c..01c4ee3a 100644 --- a/app/Models/SalesActivityModel.php +++ b/app/Models/SalesActivityModel.php @@ -197,6 +197,14 @@ class SalesActivityModel extends Model $builder->whereIn('sales_activities.assigned_to', $assignedToIds); } + if (!empty($filters['date_from'])) { + $builder->where('sales_activities.created_at >=', $filters['date_from']); + } + + if (!empty($filters['date_to'])) { + $builder->where('sales_activities.created_at <=', $filters['date_to']); + } + if (!empty($filters['search'])) { // $builder->groupStart() $this->groupStart() @@ -243,6 +251,12 @@ class SalesActivityModel extends Model $ids = is_array($filters['assigned_to']) ? $filters['assigned_to'] : explode(',', $filters['assigned_to']); $query->whereIn('assigned_to', $ids); } + if (!empty($filters['date_from'])) { + $query->where('created_at >=', $filters['date_from']); + } + if (!empty($filters['date_to'])) { + $query->where('created_at <=', $filters['date_to']); + } $counts[$status] = $query->countAllResults(); } $counts['all'] = $counts['pending'] + $counts['completed']; diff --git a/app/Models/SalesActualLeadModel.php b/app/Models/SalesActualLeadModel.php index d52fcd23..785e8cf4 100644 --- a/app/Models/SalesActualLeadModel.php +++ b/app/Models/SalesActualLeadModel.php @@ -112,6 +112,14 @@ class SalesActualLeadModel extends Model $this->whereIn('sales_actual_leads.assigned_to', $assignedToIds); } + if (!empty($filters['date_from'])) { + $this->where('sales_actual_leads.created_at >=', $filters['date_from']); + } + + if (!empty($filters['date_to'])) { + $this->where('sales_actual_leads.created_at <=', $filters['date_to']); + } + $this->orderBy('sales_actual_leads.created_at', 'DESC'); if (!empty($filters['search'])) { @@ -153,6 +161,14 @@ class SalesActualLeadModel extends Model $countQuery->whereIn('assigned_to', $assignedToIds); } + if (!empty($filters['date_from'])) { + $countQuery->where('created_at >=', $filters['date_from']); + } + + if (!empty($filters['date_to'])) { + $countQuery->where('created_at <=', $filters['date_to']); + } + $counts[$status] = $countQuery->where('status', $status)->countAllResults(); } diff --git a/app/Views/DashBoard.php b/app/Views/DashBoard.php index 5e2b1ee1..561e8098 100755 --- a/app/Views/DashBoard.php +++ b/app/Views/DashBoard.php @@ -185,6 +185,27 @@ + +