updated client module

This commit is contained in:
Kwesi Banson Jnr
2026-07-27 09:03:12 +00:00
parent 3656d2fd26
commit 143e1590e0
7 changed files with 514 additions and 5 deletions

View File

@@ -7,8 +7,17 @@ use Illuminate\Support\Facades\Auth;
class AuthController extends Controller class AuthController extends Controller
{ {
// Your login method is likely here...
// public function login(Request $request)
// {
// if (Auth::attempt($credentials)) {
// $request->session()->regenerate();
// return redirect()->intended('/');
// }
// return back()->withErrors([...]);
// }
/** /**
* Log the user out of the application. * Log the user out of the application.
*/ */

View File

@@ -382,4 +382,266 @@ class ClientsController extends Controller
Session::flash('success_message', 'Client successfully added'); Session::flash('success_message', 'Client successfully added');
return redirect('clients'); return redirect('clients');
} }
public function storeFiles(AddFilesRequest $request){
$document_arr = $request->except('document');
if ($request->hasFile('document')) {
if ($request->file('document')->isValid()) {
$filename = "erp_" . time() . "." . $request->document->extension();
$request->document->storeAs('client_files', $filename, 'public');
$document_arr['document'] = json_encode([$filename]);
}
}
$client = Models\Client::find($request->client_id);
$document_arr['file_extension'] = $request->document->extension();
$document_arr['file_reff'] = time() . uniqid();
$document_arr['last_modified_by'] = session('current_user.id');
$result = Models\ClientFile::create($document_arr);
if ($result) {
$data = ['code' => 1, 'msg' => 'Document successfully uploaded'];
}
else{
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
}
$user_id = session('current_user.id');
$username = session('current_user.name');
$content = "User ID : " . $user_id . " (" . $username . ") Document successfully uploaded for " . $client->name;
$this->logUsersActivity($type = 'staff', $content);
return response()->json($data, 200);
}
public function getClientFile($id){
$client_file = Models\ClientFile::with('client_info')->findOrFail($id);
$file = public_path('documents/client_files/') . $client_file->file_path;
$headers = [];
$filename = $client_file->client_info->name . "_" . $client_file->name;
$filename = $this->cleanStr($filename);
$filename = $filename . "." . $client_file->file_extension;
return \Response::download($file, $filename, $headers);
}
public function paymentsStore(Request $request){
$request->validate([
'client_id' => 'required',
'services' => 'required',
'invoice_number' => 'required',
'invoice_amount' => 'required|numeric',
'invoice_date' => 'required',
'invoice_status' => 'required',
]);
$auth_user = session('current_user');
if ($request->short_code !== null) {
$check = is_numeric($request->short_code);
if ($check == false) {
$data = ['code' => 3, 'msg' => 'Short Code must be a number'];
return response()->json($data, 200);
}
}
$finance_arr = [
'invoice_number' => $request->invoice_number,
'invoice_amount' => $request->invoice_amount,
'invoice_date' => $request->invoice_date,
'invoice_status' => $request->invoice_status,
'short_code' => ($request->short_code) ? $request->short_code : "",
'services' => implode(',', $request->services),
'user_id' => $auth_user['id'],
'client_id' => $request->client_id
];
if ($request->has('remarks')) {
$finance_arr['remarks'] = $request->remarks;
}
$client = Models\Client::find($request->client_id);
$result = Models\ClientPayment::updateOrCreate(['invoice_number' => $request->invoice_number, 'client_id' => $request->client_id, ], $finance_arr);
if ($request->has('short_code')) {
$short_code_list = Models\ClientShortCode::where('client_id', $request->client_id)->get();
$client = Models\Client::find($request->client_id);
$message_body = $auth_user['name'] . " has added a new Short Code ($request->short_code) Payment entry for " . $client->name;
dispatch(new SendShortCodeListToFinance($short_code_list, $message_body, $finance_arr));
}
#$payments = Models\ClientPayment::with('client_info', 'created_by_info')->find($result->id);
if ($result) {
$data = ['code' => 1, 'msg' => 'Payment Details successfully added'];
}
else{
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
}
$user_id = session('current_user.id');
$username = session('current_user.name');
$content = "User ID : " . $user_id . " (" . $username . ") Added a payment record for : " . $client->name;
$this->logUsersActivity($type = 'staff', $content);
return response()->json($data, 200);
}
public function financeUpdate(Request $request){
$request->validate([
'payment_id' => 'required',
'client_id' => 'required',
'services' => 'required',
'invoice_number' => 'required',
'invoice_amount' => 'required|numeric',
'invoice_date' => 'required',
'invoice_status' => 'required'
]);
$auth_user = session('current_user');
$payment = Models\ClientPayment::findOrFail($request->payment_id);
$payment->invoice_number = $request->invoice_number;
$payment->invoice_amount = $request->invoice_amount;
$payment->invoice_date = $request->invoice_date;
$payment->invoice_status = $request->invoice_status;
$payment->short_code = ($request->short_code) ? $request->short_code : "";
$payment->services = implode(',', $request->services);
$result = $payment->save();
$client = Models\Client::find($request->client_id);
if ($request->has('short_code')) {
$short_code_list = Models\ClientShortCode::where('client_id', $request->client_id)->get();
$finance_arr = [
'invoice_number' => $request->invoice_number,
'invoice_amount' => $request->invoice_amount,
'invoice_date' => $request->invoice_date,
'invoice_status' => $request->invoice_status,
'short_code' => ($request->short_code) ? $request->short_code : "",
'services' => implode(',', $request->services),
'user_id' => $auth_user['id'],
'client_id' => $request->client_id
];
if ($request->has('remarks')) {
$finance_arr['remarks'] = $request->remarks;
}
$message_body = $auth_user['name'] . " has updated a Short Code ($request->short_code) Payment entry for " . $client->name;
#dispatch(new SendShortCodeListToFinance($short_code_list, $message_body, $finance_arr));
}
if ($result) {
$data = ['code' => 1, 'msg' => 'Payment Details successfully updated'];
}
else{
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
}
$user_id = session('current_user.id');
$username = session('current_user.name');
$content = "User ID : " . $user_id . " (" . $username . ") updated an existing payment record for " . $client->name;
$this->logUsersActivity($type = 'staff', $content);
return response()->json($data, 200);
}
public function shortcodeStore(Request $request){
$request->validate([
'client_id' => 'required',
'network' => 'required',
'shortcode' => 'required',
'code_type' => 'required',
'toll_free' => 'required',
'status' => 'required',
'remarks' => 'required',
'launch_date' => 'required',
'expiry_date' => 'required',
'monthly_fee' => 'sometimes'
]);
$auth_user = session('current_user');
$shortcode_arr = [
'name' => $request->name,
'client_id' => $request->client_id,
'network' => $request->network,
// 'country' => $mnoCountry, //$network->country,
'shortcode' => $request->shortcode,
'code_type' => $request->code_type,
'toll_free' => $request->toll_free,
'last_updated_by' => $auth_user['id'],
'launch_date' => $request->launch_date,
'expiry_date' => $request->expiry_date,
'status' => $request->status
];
// dd($shortcode_arr);
if ($request->has('remarks')) {
$shortcode_arr['remarks'] = $request->remarks;
}
if ($request->has('monthly_fee')) {
$shortcode_arr['monthly_fee'] = $request->monthly_fee;
}
$result = Models\ClientShortCode::create($shortcode_arr);
$client = Models\Client::find($request->client_id);
if ($result) {
$data = ['code' => 1, 'msg' => 'ShortCode Details successfully added'];
}
else{
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
}
$user_id = session('current_user.id');
$username = session('current_user.name');
$content = "User ID : " . $user_id . " (" . $username . ") Added new short code for " . $client->name;
$this->logUsersActivity($type = 'staff', $content);
return response()->json($data, 200);
}
public function shortCodeUpdate(Request $request){
$request->validate([
'client_id' => 'required',
'network' => 'required',
'shortcode' => 'required',
'code_type' => 'required',
'toll_free' => 'required',
'monthly_fee' => 'sometimes',
'status' => 'required',
'remarks' => 'required',
'launch_date' => 'required',
'expiry_date' => 'required'
]);
$auth_user = session('current_user');
$mnoCountry = $this->getMnoCountry($request->network);
if ($mnoCountry == false) {
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
return response()->json($data, 200);
// code...
}
$shortcode_arr = [
'name' => $request->name,
'client_id' => $request->client_id,
'network' => $request->network,
'country' => $mnoCountry,
'shortcode' => $request->shortcode,
'code_type' => $request->code_type,
'toll_free' => $request->toll_free,
'last_updated_by' => $auth_user['id'],
'launch_date' => $request->launch_date,
'expiry_date' => $request->expiry_date,
'status' => $request->status
];
if ($request->has('remarks')) {
$shortcode_arr['remarks'] = $request->remarks;
}
if ($request->has('monthly_fee')) {
$shortcode_arr['monthly_fee'] = $request->monthly_fee;
}
$result = Models\ClientShortCode::where('id', $request->shortcode_id)->update($shortcode_arr);
if ($result) {
$data = ['code' => 1, 'msg' => 'Short Code Details successfully updated'];
}
else{
$data = ['code' => 3, 'msg' => 'Your request could not be handled at this time'];
}
$user_id = session('current_user.id');
$username = session('current_user.name');
$content = "User ID : " . $user_id . " (" . $username . ") updated short code enty for " . $request->short_code;
$this->logUsersActivity($type = 'staff', $content);
return response()->json($data, 200);
}
} }

View File

@@ -0,0 +1,88 @@
$(document).ready(function() {
// Generic AJAX handler for standard forms (Shortcodes & Payments)
function submitAjaxForm(formId, modalId) {
$(formId).on('submit', function(e) {
e.preventDefault();
let $form = $(this);
let $submitBtn = $form.find('button[type="submit"]');
let originalBtnText = $submitBtn.text();
// Disable button and show loading state
$submitBtn.prop('disabled', true).text('Saving...');
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function(response) {
// Hide modal
$(modalId).modal('hide');
// Reset form
$form[0].reset();
// Reload page to show fresh data (or dynamically append to table)
location.reload();
},
error: function(xhr) {
let errors = xhr.responseJSON?.errors;
let errorMsg = 'Something went wrong. Please check your inputs.';
if (errors) {
errorMsg = Object.values(errors).flat().join('\n');
}
alert(errorMsg);
},
complete: function() {
$submitBtn.prop('disabled', false).text(originalBtnText);
}
});
});
}
// AJAX handler for Document Upload (Requires FormData for files)
function submitFileForm(formId, modalId) {
$(formId).on('submit', function(e) {
e.preventDefault();
let formElement = $(this)[0];
let formData = new FormData(formElement);
let $submitBtn = $(this).find('button[type="submit"]');
let originalBtnText = $submitBtn.text();
$submitBtn.prop('disabled', true).text('Uploading...');
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data: formData,
processData: false, // Essential for file uploads
contentType: false, // Essential for file uploads
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function(response) {
$(modalId).modal('hide');
formElement.reset();
location.reload();
},
error: function(xhr) {
let errors = xhr.responseJSON?.errors;
let errorMsg = 'Failed to upload document. Please check the file size and format.';
if (errors) {
errorMsg = Object.values(errors).flat().join('\n');
}
alert(errorMsg);
},
complete: function() {
$submitBtn.prop('disabled', false).text(originalBtnText);
}
});
});
}
// Initialize the handlers
submitAjaxForm('#shortcodeForm', '#addShortcodeModal');
submitAjaxForm('#paymentForm', '#addPaymentModal');
submitFileForm('#documentForm', '#uploadDocumentModal');
});

View File

@@ -344,7 +344,7 @@
format: format format: format
}); });
// Redirect the browser to trigger the file download
window.location.href = "{{ route('clients.export') }}?" + queryParams; window.location.href = "{{ route('clients.export') }}?" + queryParams;
}); });
// Render Table Rows // Render Table Rows

View File

@@ -0,0 +1,125 @@
<!-- ========================================== -->
<!-- 1. ADD SHORT CODE MODAL -->
<!-- ========================================== -->
<div class="modal fade" id="addShortcodeModal" tabindex="-1" aria-labelledby="addShortcodeModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form id="shortcodeForm" action="{{ route('shortcodes.store') }}" method="POST">
@csrf
<input type="hidden" name="client_id" value="{{ $showclient->id }}">
<div class="modal-header">
<h5 class="modal-title fw-bold" id="addShortcodeModalLabel"><i class="bi bi-code-square text-primary me-2"></i>Add New Short Code</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Code Type</label>
<select name="code_type" class="form-select" required>
<option value="sms">SMS</option>
<option value="ussd">USSD</option>
<option value="voice">Voice</option>
</select>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Short Code / Number</label>
<input type="text" name="shortcode" class="form-control" placeholder="e.g. 1234 or *123#" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Network</label>
<input type="text" name="network" class="form-control" placeholder="e.g. MTN, Vodafone, AirtelTigo">
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Expiry Date</label>
<input type="date" name="expiry_date" class="form-control">
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary btn-sm px-4">Save Short Code</button>
</div>
</form>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- 2. ADD PAYMENT MODAL -->
<!-- ========================================== -->
<div class="modal fade" id="addPaymentModal" tabindex="-1" aria-labelledby="addPaymentModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form id="paymentForm" action="{{ route('client-payments.store') }}" method="POST">
@csrf
<input type="hidden" name="client_id" value="{{ $showclient->id }}">
<div class="modal-header">
<h5 class="modal-title fw-bold" id="addPaymentModalLabel"><i class="bi bi-wallet2 text-primary me-2"></i>Record New Payment</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Invoice / Reference Number</label>
<input type="text" name="invoice_number" class="form-control" placeholder="e.g. INV-2026-001" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Amount</label>
<input type="number" step="0.01" name="invoice_amount" class="form-control" placeholder="0.00" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Invoice Date</label>
<input type="date" name="invoice_date" class="form-control" value="{{ date('Y-m-d') }}" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Status</label>
<select name="invoice_status" class="form-select" required>
<option value="paid">Paid</option>
<option value="pending">Pending</option>
<option value="partial">Partial</option>
</select>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary btn-sm px-4">Record Payment</button>
</div>
</form>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- 3. UPLOAD DOCUMENT MODAL -->
<!-- ========================================== -->
<div class="modal fade" id="uploadDocumentModal" tabindex="-1" aria-labelledby="uploadDocumentModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form id="documentForm" action="{{ route('client-files.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<input type="hidden" name="client_id" value="{{ $showclient->id }}">
<div class="modal-header">
<h5 class="modal-title fw-bold" id="uploadDocumentModalLabel"><i class="bi bi-file-earmark-arrow-up text-primary me-2"></i>Upload Client Document</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Document Name / Title</label>
<input type="text" name="name" class="form-control" placeholder="e.g. Signed Service Agreement" required>
</div>
<div class="mb-3">
<label class="form-label text-muted small fw-bold">Select File</label>
<input type="file" name="file" class="form-control" required>
<div class="form-text text-muted">Accepted formats: PDF, DOC, DOCX, JPG, PNG (Max size depends on server config).</div>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary btn-sm px-4">Upload Document</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -291,8 +291,10 @@
</div> </div>
<!-- Tabs Navigation for Additional Records --> <!-- Tabs Navigation for Additional Records -->
<div class="card border-light-subtle shadow-sm mt-5 mb-4"> <div class="card border-light-subtle shadow-sm mt-5 mb-4">
<div class="card-header bg-white pt-3 pb-0 border-bottom"> <div class="card-header bg-white py-3 border-bottom d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3">
<ul class="nav nav-tabs card-header-tabs" id="clientRecordTabs" role="tablist">
<!-- Tab Links -->
<ul class="nav nav-tabs card-header-tabs m-0" id="clientRecordTabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active fw-bold text-dark" id="shortcodes-tab" data-bs-toggle="tab" data-bs-target="#shortcodes-pane" type="button" role="tab"> <button class="nav-link active fw-bold text-dark" id="shortcodes-tab" data-bs-toggle="tab" data-bs-target="#shortcodes-pane" type="button" role="tab">
<i class="bi bi-code-square text-primary me-1"></i> Short Codes <i class="bi bi-code-square text-primary me-1"></i> Short Codes
@@ -312,6 +314,20 @@
</button> </button>
</li> </li>
</ul> </ul>
<!-- Action Buttons Corresponding to Tabs -->
<div class="tab-action-buttons">
<button type="button" class="btn btn-sm btn-outline-primary fw-bold" data-bs-toggle="modal" data-bs-target="#addShortcodeModal">
<i class="bi bi-plus-lg me-1"></i> Add Short Code
</button>
<button type="button" class="btn btn-sm btn-outline-primary fw-bold ms-1" data-bs-toggle="modal" data-bs-target="#addPaymentModal">
<i class="bi bi-plus-lg me-1"></i> Add Payment
</button>
<button type="button" class="btn btn-sm btn-outline-primary fw-bold ms-1" data-bs-toggle="modal" data-bs-target="#uploadDocumentModal">
<i class="bi bi-upload me-1"></i> Upload Doc
</button>
</div>
</div> </div>
<div class="card-body bg-light tab-content" id="clientRecordTabsContent"> <div class="card-body bg-light tab-content" id="clientRecordTabsContent">
@@ -471,4 +487,8 @@
</div> </div>
</div> </div>
@include('clients.partials.shortcode-payment-docs-modal')
@endsection
@section('scripts')
<script src="{{ asset('js/client-show-modals.js') }}"></script>
@endsection @endsection

View File

@@ -17,7 +17,12 @@ Route::middleware(['auth'])->group(function () {
Route::get('/clients/data', [App\Http\Controllers\ClientsController::class, 'fetchData'])->name('clients.data'); Route::get('/clients/data', [App\Http\Controllers\ClientsController::class, 'fetchData'])->name('clients.data');
Route::get('/clients/export', [App\Http\Controllers\ClientController::class, 'export'])->name('clients.export'); Route::get('/clients/export', [App\Http\Controllers\ClientController::class, 'export'])->name('clients.export');
// {{ route('shortcodes.store') }}
// {{ route('client-payments.store') }}
// {{ route('client-files.store') }}
Route::get('/clients/shortcodes-store', [App\Http\Controllers\ClientController::class, 'shortcodeStore'])->name('shortcodes.store');
Route::get('/clients/payments-store', [App\Http\Controllers\ClientController::class, 'paymentsStore'])->name('client-payments.store');
Route::get('/clients/files-store', [App\Http\Controllers\ClientController::class, 'storeFiles'])->name('client-files.store');
Route::resource('clients', App\Http\Controllers\ClientsController::class); Route::resource('clients', App\Http\Controllers\ClientsController::class);