FIX_retention rate, kyc download issues and kyc dropdown rechecked and fixed

This commit is contained in:
sanjeev.p 2025-12-17 18:59:13 +05:30
parent 847719fd92
commit af61f687c8
10 changed files with 273 additions and 82 deletions

View File

@ -169,6 +169,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) {
$routes->post("create_2", "ClientController::createClientKYCInfo_2");
$routes->post("edit_2", "ClientController::editClientKYCInfo_2");
$routes->post("delete_2", "ClientController::deleteClientKycDocs_2/$1");
$routes->get("download_2/(:any)", "ClientController::downloadClientKycDocs_2/$1");
});

View File

@ -1121,37 +1121,43 @@ class ClientController extends AdminController
}
}
public function fetch_dropdown($client_id){
$db = db_connect();
public function fetch_dropdown($client_id)
{
$db = db_connect();
$submitted_ids_subquery = $db->table('client_kyc_documents ckd')
->select('ckd.kyc_doc_type_id')
->where('ckd.client_id', $client_id)
->where('ckd.is_active', 1)
->getCompiledSelect();
$result = $db->table('kyc_docs kd')
->select('kd.*')
->join('clients c', 'kd.kyc_type_id = c.entity_type_id')
->where('c.id', $client_id)
->where("kd.kyc_type_id NOT IN ({$submitted_ids_subquery})")
->groupBy('kd.id')
->get()
->getResultArray();
// Already uploaded docs
$uploadedData = $db->table('client_kyc_documents')
->select('kyc_doc_type_id')
->where('client_id', $client_id)
->where('is_active', 1)
->get()
->getResultArray();
$dropdown = '<option value="">Select Document</option>';
$dropdown .= '<option value="other">Additional Document</option>';
$excludedIds = array_column($uploadedData, 'kyc_doc_type_id');
// Available docs
$builder = $db->table('kyc_docs kd');
$builder->select('kd.id, kd.file_name');
$builder->join('clients c', 'kd.kyc_type_id = c.entity_type_id');
$builder->where('c.id', $client_id);
$builder->where('kd.is_active', 1);
if (!empty($excludedIds)) {
$builder->whereNotIn('kd.id', $excludedIds);
}
$result = $builder->get()->getResultArray(); // ✅ ARRAY
// Build dropdown
$dropdown = '<option value="">Select Document</option>';
$dropdown .= '<option value="other">Additional Document</option>';
foreach ($result as $row) {
$dropdown .= '<option value="' . esc($row['id']) . '">'
. esc($row['file_name']) .
'</option>';
}
foreach ($result as $row) {
$dropdown .= '<option value="' . esc($row['kyc_type_id']) . '">'
. esc($row['file_name']) .
'</option>';
}
return $dropdown;
}
@ -2517,22 +2523,57 @@ class ClientController extends AdminController
{
$result['ckdlist'] = db_connect()->table('client_kyc_documents ckd')
->select("ckd.id,ckd.client_id,ckd.kyc_doc_type_id,ckd.file_name,kd.file_name AS kd_docs_name,ckd.other_docs_name,ckd.vehicle_id,ckd.is_active,
CASE
WHEN ckd.kyc_doc_type_id IS NULL
OR ckd.kyc_doc_type_id = 0
OR ckd.kyc_doc_type_id = ''
THEN ckd.other_docs_name
ELSE kd.file_name
END AS ui_docs_name")
->join('kyc_docs kd','ckd.kyc_doc_type_id = kd.kyc_type_id','left')
->where('ckd.client_id',$client_id)
->where('ckd.is_active',1)
->groupBy('ckd.id')
->get()
->getResultArray();
// $result['ckdlist'] = db_connect()->table('client_kyc_documents ckd')
// ->select("ckd.id,ckd.client_id,ckd.kyc_doc_type_id,ckd.file_name,kd.file_name AS kd_docs_name,ckd.other_docs_name,ckd.vehicle_id,ckd.is_active,
// CASE
// WHEN ckd.kyc_doc_type_id IS NULL
// OR ckd.kyc_doc_type_id = 0
// OR ckd.kyc_doc_type_id = ''
// THEN ckd.other_docs_name
// ELSE kd.file_name
// END AS ui_docs_name")
// ->join('kyc_docs kd','ckd.kyc_doc_type_id = kd.id','left')
// ->where('ckd.client_id',$client_id)
// ->where('ckd.is_active',1)
// ->groupBy('ckd.id')
// ->get()
// ->getResultArray();
$builder = db_connect()->table('client_kyc_documents AS ckd');
// Select the standard fields
$builder->select('
ckd.id,
ckd.client_id,
ckd.kyc_doc_type_id,
ckd.file_name,
ckd.other_docs_name,
ckd.vehicle_id,
ckd.is_active
');
// Add the CASE statement for ui_docs_name
// Using false as the second parameter tells CI4 not to escape the string
$builder->select("
CASE
WHEN ckd.kyc_doc_type_id IS NULL OR ckd.kyc_doc_type_id = 0 OR ckd.kyc_doc_type_id = ''
THEN ckd.other_docs_name
ELSE kd.file_name
END AS ui_docs_name
", false);
// Join with kyc_docs
$builder->join('kyc_docs AS kd', 'ckd.kyc_doc_type_id = kd.id', 'left');
// Where clauses
$builder->where('ckd.client_id', $client_id);
$builder->where('ckd.is_active', 1);
// Group By
$builder->groupBy('ckd.id');
$result['ckdlist'] = $builder->get()->getResultArray();
$result['client_id'] = $client_id;
$table = view('client_kyc_single_table', $result);
@ -8338,6 +8379,32 @@ class ClientController extends AdminController
return $default_templates_inserted ? true : false;
}
// public function downloadClientKycDocs_2()
// {
// $file = WRITEPATH . 'uploads/client_kyc_documents/' . $file_name; // Example file path
// if (file_exists($file)) {
// return $this->response->download($file, null)->setFileName($file_name);
// } else {
// return "File not found.";
// }
// }
public function downloadClientKycDocs_2($file_name = null)
{
if (!$file_name) {
return "No file specified.";
}
// Use absolute path to your upload folder
$file = WRITEPATH . 'uploads/client_kyc_documents/' . $file_name;
if (file_exists($file)) {
// download() takes the path as first param and null (or data) as second
return $this->response->download($file, null);
} else {
return "File not found at: " . $file;
}
}
}

View File

@ -2350,6 +2350,13 @@ class MasterController extends AdminController
return $this->respond([ 'status' => false, 'code' => 400, 'message' => $e->getMessage(), 'data' => $data ], 400);
}
foreach ($data as $k => $v) {
if ($v === '' || $v === null) {
unset($data[$k]);
}
}
// INSERT / UPDATE
if (empty($id)) {
$status = $posModel->insert($data);

View File

@ -594,35 +594,70 @@ class UserController extends AdminController
// add/update
if ($method === 'post') {
$data = $this->request->getPost();
if (!empty($data['id'])) {
$text = "update";
$data['updated_by'] = get_session_userid();
$result = $this->partnerStaffModel->update($data['id'], $data);
$updateID = $data['id'];
} else {
$text = "create";
$data['role_id'] = 1;
$data['created_by'] = get_session_userid();
$insertID = $this->partnerStaffModel->insert($data);
if($insertID){
$details['manager_id'] = $insertID;
$details['updated_by'] = get_session_userid();
$this->partnerStaffModel->update($insertID, $details);
$id = !empty($data['PrimaryKey']) ? $data['PrimaryKey'] : null;
// don't forgot same means just unset the key because partner_staff some UNIQUE KEY sets in table thats why
if ($id) {
$existing = $this->partnerStaffModel->find($id);
if ($existing) {
if ($data['email'] === $existing['email']) { unset($data['email']); }
if ($data['mobile'] === $existing['mobile']) { unset($data['mobile']); }
}
$result = true;
}
$id = isset($insertID) && !empty($insertID) ? $insertID : ($updateID ?? null);
$errors = [];
// Check Email Duplicate (if it wasn't unset)
if (isset($data['email'])) {
$count = $this->partnerStaffModel->where('email', $data['email'])->countAllResults();
if ($count > 0) $errors['email'] = "This email is already taken by another user.";
}
// Check Mobile Duplicate (if it wasn't unset)
if (isset($data['mobile'])) {
$count = $this->partnerStaffModel->where('mobile', $data['mobile'])->countAllResults();
if ($count > 0) $errors['mobile'] = "This mobile is already taken by another user.";
}
// If there are duplicates, stop and return error
if (!empty($errors)) {
return $this->response->setJSON([
'status' => 'error',
'message' => 'Duplicate detected',
'errors' => $errors
])->setStatusCode(400);
}
// --- Save/Update ---
if ($id) {
$text = "update";
$data['updated_by'] = get_session_userid();
$result = $this->partnerStaffModel->update($updateID, $data);
} else {
$text = "create";
$data['role_id'] = 1;
$data['created_by'] = get_session_userid();
$id = $this->partnerStaffModel->insert($data);
if($id){
$details['manager_id'] = $id;
$details['updated_by'] = get_session_userid();
$this->partnerStaffModel->update($id, $details);
}
$result = true;
}
return $this->response->setJSON([
'status' => $id ? 'success' : 'error',
'message' => $id ? "Staff {$text}d successfully" : "Unable to {$text} staff. Please try again.",
'id' => $id
])->setStatusCode($id ? 200 : 400);
}
}
// delete
if ($method === 'put') {
$data = $this->request->getRawInput();

View File

@ -61,7 +61,7 @@ class PartnerPosModel extends Model
'mobile',
'pos_code',
'certificate_file_name',
'pan',
'gst','pan',
'pan_file_name',
'aadhar',
'aadhar_file_name',
@ -73,6 +73,10 @@ class PartnerPosModel extends Model
'account_holder_name',
'account_number',
'ifsc_code',
'address',
'state',
'city',
'pincode',
// 'created_on' and 'updated_on' are handled by the Model,
// but need to be in $allowedFields if $useTimestamps is false.

View File

@ -16,7 +16,7 @@ class PartnerStaffModel extends Model
protected $useSoftDeletes = false;
// allowed fields for insert/update
protected $allowedFields = ['name','email','mobile','emp_id','role_id','email_otp','is_active','created_by','updated_by','manager_id'];
protected $allowedFields = ['name','email','mobile','emp_id','role_id','email_otp','is_active','created_by','updated_by','manager_id','retention_rate'];
// automatic timestamps
protected $useTimestamps = true;

View File

@ -623,6 +623,14 @@ table.dataTable tbody td {
<input type="text" class="form-control" id="email_addr" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" required>
</div>
<div class="form-group col-md-6">
<label for="retention_rate">Retention Rate<span class="text-danger">*</span></label>
<!-- <input type="text" class="form-control" id="retention_rate" placeholder="Enter Retention Rate" name="retention_rate" required> -->
<input type="text" class="form-control" id="retention_rate" name="retention_rate" placeholder="Enter Retention Rate" inputmode="decimal" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="locn">Address</label>
<textarea class="form-control" id="locn" name="address" placeholder="Enter address" rows="3" maxlength="1500"></textarea>
</div>
@ -1145,6 +1153,7 @@ table.dataTable tbody td {
$('#email_addr').val(user.email);
$('#mobile_no').val(user.mobile);
$('#locn').val(user.address);
$('#retention_rate').val(user.retention_rate);
// $('#btnPartnerSubmit').html('Update');
$('.modal-title').html('Update Nhance Partner');
let myModal = new bootstrap.Modal(document.getElementById('nhance-partner-modal'));
@ -1395,11 +1404,18 @@ table.dataTable tbody td {
</div>`
];
} else { // partner
let roleName = "";
switch(parseInt(row.role_id)) {
case 1: roleName = "Manager"; break;
case 2: roleName = "Handler"; break;
case 3: roleName = "Staff"; break;
default: roleName = "Others";
}
rowData = [
row.name,
row.email,
row.mobile,
row.role_id == 1 ? "Manager" : "Staff",
roleName,
`<span class="badge ${statusClass}">${statusText}</span>`,
`<div class="btn-group dropdown">
<a href="javascript:void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown">
@ -1871,6 +1887,25 @@ table.dataTable tbody td {
}
$('#retention_rate').on('input', function () {
let value = this.value;
// Allow only digits and dot
value = value.replace(/[^0-9.]/g, '');
// Allow only ONE dot
const parts = value.split('.');
if (parts.length > 2) {
value = parts[0] + '.' + parts.slice(1).join('');
}
// Allow max 2 decimals
if (parts[1] && parts[1].length > 2) {
value = parts[0] + '.' + parts[1].slice(0, 2);
}
this.value = value;
});
</script>

View File

@ -278,7 +278,19 @@
}
});
});
$(document).on('click', '.btn-download-kyc', function (e) {
e.preventDefault(); // Prevent default link behavior
// var kyc_id = $(this).attr('data-id');
// var client_id = $(this).attr('data-client_id');
var file = $(this).attr('data-file');
if (file) {
window.location.href = "<?= base_url('client/kyc/download_2') ?>/" + file;
} else {
toastr.warning('File is missing.');
}
});
// SOFT DELETE Functionality ---
function deleteKycDoc(kyc_id,client_id) {

View File

@ -12,8 +12,9 @@
<td><?= esc($value['ui_docs_name']) ?></td>
<td><?= esc($fileName) ?></td>
<td>
<a id="download_<?= esc($value['id']); ?>" data-id="<?= esc($value['id']); ?>" data-file="<?= $fileName ?>"
class="mdi mdi-download mr-1 btn-download-kyc" style="font-size:18px;" download></a>
<a class="mdi mdi-download mr-1 btn-download-kyc"
data-file="<?= $fileName ?>"
style="font-size:18px;"></a>
<a class="mdi mdi-pencil mr-1 btn-edit-kyc"
data-id="<?= $value['id'] ?>"

View File

@ -14,11 +14,11 @@
<thead class="bg-light">
<tr>
<th class="font-weight-medium">S.No.</th>
<th class="font-weight-medium">Manager</th>
<th class="font-weight-medium">POS Name</th>
<th class="font-weight-medium">POS Code</th>
<th class="font-weight-medium">Email</th>
<th class="font-weight-medium">Mobile</th>
<th class="font-weight-medium">Manager</th>
<th class="font-weight-medium">Status</th>
<th class="font-weight-medium">Action</th>
</tr>
@ -28,11 +28,11 @@
<?php foreach($data['pos_list'] as $index => $row) { ?>
<tr>
<td class="text-center"><?= $slno++; ?></td>
<td><?= $row['manager_name']; ?></td>
<td><?= $row['name']; ?></td>
<td><?= $row['pos_code']; ?></td>
<td><?= $row['email']; ?></td>
<td><?= $row['mobile']; ?></td>
<td><?= $row['manager_name']; ?></td>
<td>
<span class="<?php if($row['is_active'] == 1){ echo 'badge badge-primary'; }else{ echo 'badge badge-danger'; } ?>">
<?php if($row['is_active'] == 1){ echo 'Active'; }else{ echo 'In-Active'; } ?>
@ -112,44 +112,66 @@
</div>
<div class="form-group col-md-3">
<label for="mobile">Mobile<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="mobile" name="mobile" placeholder="Enter mobile" maxlength="10" inputmode="numeric" pattern="[0-9]{10}" required>
<input type="text" class="form-control" id="mobile" name="mobile" placeholder="Enter Mobile" maxlength="10" inputmode="numeric" pattern="[0-9]{10}" required>
<small id="mobile_error" class="text-danger d-none"></small>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-3">
<label for="address">Address<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="address" name="address" placeholder="Enter Address" required>
</div>
<div class="form-group col-md-3">
<label for="city">City<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="city" name="city" placeholder="Enter City" required>
</div>
<div class="form-group col-md-3">
<label for="state">State<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="state" name="state" placeholder="Enter State" required>
</div>
<div class="form-group col-md-3">
<label for="pincode">Pincode<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pincode" name="pincode" placeholder="Enter Pincode" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="aadhar">Aadhaar Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="aadhar" name="aadhar" placeholder="Enter Aadhaar Number" maxlength="12" inputmode="numeric" pattern="[0-9]{12}" required>
<small id="aadhar_error" class="text-danger d-none"></small>
</div>
<div class="form-group col-md-3">
<div class="form-group col-md-4">
<label for="pan">PAN Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pan" name="pan" placeholder="Enter PAN Number" maxlength="10" style="text-transform:uppercase" pattern="[A-Z]{5}[0-9]{4}[A-Z]{1}" required>
<small id="pan_error" class="text-danger d-none"></small>
</div>
<div class="form-group col-md-4">
<label for="gst">GST Number</label>
<input type="text" class="form-control" id="gst" name="gst" placeholder="Enter GST Number">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="aadhar_file_name">Aadhar</label>
<div class="input-icon">
<input type="file" class="form-control" name="aadhar_file_name" id="aadhar_file_name" accept="image/*,application/pdf">
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
<div class="form-group col-md-3">
<label for="pan">PAN Number<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="pan" name="pan" placeholder="Enter PAN Number" maxlength="10" style="text-transform:uppercase" pattern="[A-Z]{5}[0-9]{4}[A-Z]{1}" required>
<small id="pan_error" class="text-danger d-none"></small>
</div>
<div class="form-group col-md-3">
<div class="form-group col-md-4">
<label for="pan_file_name">PAN</label>
<div class="input-icon">
<input type="file" class="form-control" name="pan_file_name" id="pan_file_name" accept="image/*,application/pdf">
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<div class="form-group col-md-4">
<label for="certificate_file_name">Certificate</label>
<div class="input-icon">
<input type="file" class="form-control" name="certificate_file_name" id="certificate_file_name" accept="image/*,application/pdf">
<i class="mdi mdi-upload additional-icon"></i>
</div>
</div>
</div>
</div>
<div class="form-row">
<!-- <legend>Bank Details</legend> -->
@ -352,6 +374,13 @@
$('#account_holder_name').val(data.account_holder_name);
$('#account_number').val(data.account_number);
$('#ifsc_code').val(data.ifsc_code);
$('#gst').val(data.gst);
$('#state').val(data.state);
$('#city').val(data.city);
$('#pincode').val(data.pincode);
$('#address').val(data.address);
openModal();
$('#con-close-modal').one('shown.bs.modal', function () {