DONE: auto add ic photo for existing customer

This commit is contained in:
ISMAIL MASSERAN
2026-09-09 10:27:48 +08:00
parent 120c32f781
commit f6110b6651
6 changed files with 91 additions and 8 deletions
@@ -289,6 +289,29 @@ class CustomersController extends Controller
return response(["no-data"], 200); return response(["no-data"], 200);
} }
/**
* Store MyKad photo for an existing customer when gambar_mykad is still empty.
*/
public function storeMykadPhotoIfMissing(Request $request)
{
$icno = preg_replace('/[^0-9A-Za-z]/', '', (string) $request->input('icno'));
if ($icno === '') {
return response()->json(['error' => 'IC number is required.', 'stored' => false], 422);
}
if (!$request->filled('photo_base64')) {
return response()->json(['error' => 'Photo is required.', 'stored' => false], 422);
}
$response = Http::withOptions(['verify' => config('app.api_verify')])
->post(config('api.url') . '/api/customers/' . $icno . '/mykad-photo', [
'photo_base64' => $request->input('photo_base64'),
'photo_mime' => $request->input('photo_mime') ?: 'image/jpeg',
]);
return response()->json($response->json() ?? ['stored' => false], $response->status());
}
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
* *
+55 -1
View File
@@ -234,15 +234,69 @@ async function handleMyKadReader() {
} }
} }
function customerNeedsMykadPhoto(customer) {
if (!customer || typeof customer !== 'object') {
return false;
}
return !customer.gambar_mykad && !customer.photo_base64;
}
async function storeMykadPhotoForExistingCustomerIfMissing(customer, readerData) {
if (!customerNeedsMykadPhoto(customer)) {
return;
}
if (!readerData || !readerData.has_photo || !readerData.photo_base64) {
return;
}
if (typeof store_mykad_photo_endpoint === 'undefined' || !store_mykad_photo_endpoint) {
return;
}
const csrfToken = document.querySelector('input[name="_token"]');
const nric = getMyKadNumberWithoutHyphens(
customer.kpbaru || customer.kplama || readerData.nric || ''
);
if (!nric) {
return;
}
try {
await fetch(store_mykad_photo_endpoint, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': csrfToken ? csrfToken.value : '',
},
credentials: 'same-origin',
body: JSON.stringify({
icno: nric,
photo_base64: readerData.photo_base64,
photo_mime: readerData.photo_mime || 'image/jpeg',
}),
});
} catch (error) {
// Do not block customer search if photo backfill fails.
console.warn('Failed to store MyKad photo for existing customer', error);
}
}
function handleMyKadData(data) { function handleMyKadData(data) {
return checkIfMyKadExists(data.nric) return checkIfMyKadExists(data.nric)
.then(response => { .then(async response => {
let isResponseArray = Array.isArray(response); let isResponseArray = Array.isArray(response);
let resolved_response = isResponseArray ? response.pop() : response; let resolved_response = isResponseArray ? response.pop() : response;
if (!resolved_response || resolved_response === 'no-data') { if (!resolved_response || resolved_response === 'no-data') {
populateDaftarPelangganModal(data); populateDaftarPelangganModal(data);
} else { } else {
await storeMykadPhotoForExistingCustomerIfMissing(resolved_response, data);
let input_search_noic = document.querySelector("form[name='frmsearch'] input[name='noic']"); let input_search_noic = document.querySelector("form[name='frmsearch'] input[name='noic']");
let form_search_noic = document.querySelector("form[name='frmsearch']"); let form_search_noic = document.querySelector("form[name='frmsearch']");
@@ -13,9 +13,9 @@
<style> <style>
.print-header { .print-header {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); grid-template-columns: minmax(0, 1.75fr) auto minmax(0, 1fr);
align-items: start; align-items: start;
column-gap: 12px; column-gap: 10px;
border-bottom: 2px solid #111; border-bottom: 2px solid #111;
padding-bottom: 8px; padding-bottom: 8px;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
@@ -30,9 +30,12 @@
line-height: 1.35; line-height: 1.35;
} }
.print-header .org .name { .print-header .org .name {
font-size: 11px; font-size: 10px;
font-weight: bold; font-weight: bold;
} }
.print-header .org .name .reg {
white-space: nowrap;
}
.print-header .logos { .print-header .logos {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -70,8 +73,8 @@
<div {{ $attributes->merge(['class' => 'print-header']) }}> <div {{ $attributes->merge(['class' => 'print-header']) }}>
<div class="org"> <div class="org">
<p class="name">KOPERASI PERMODALAN KELANTAN BERHAD (D-5-0228)</p> <p class="name">KOPERASI PERMODALAN KELANTAN BERHAD <span class="reg">(D-5-0228)</span></p>
<p>Ar-Rahn {{ $cawangan }}</p> <p>{{ $cawangan }}</p>
<p>{{ $alamat1 }}</p> <p>{{ $alamat1 }}</p>
<p>{{ $alamat2 }}</p> <p>{{ $alamat2 }}</p>
<p>Tel: {{ $notel }}</p> <p>Tel: {{ $notel }}</p>
+2 -1
View File
@@ -1279,9 +1279,10 @@
// mycard-read endpoint initialization // mycard-read endpoint initialization
let mykad_reader_endpoint = "{{ config('mykad-reader.endpoint') }}"; let mykad_reader_endpoint = "{{ config('mykad-reader.endpoint') }}";
let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}"; let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}";
let store_mykad_photo_endpoint = "{{ route('customer.storeMykadPhoto') }}";
let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}"; let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}";
</script> </script>
<script src="{{ asset('js/mykad-reader-customer.js') }}?v=4"></script> <script src="{{ asset('js/mykad-reader-customer.js') }}?v=5"></script>
<script src="{{ asset('js/postcode-concerns.js') }}"></script> <script src="{{ asset('js/postcode-concerns.js') }}"></script>
@endpush @endpush
+2 -1
View File
@@ -1417,9 +1417,10 @@ document.getElementsByName('form-store')[0].addEventListener('keypress', functio
// mycard-read endpoint initialization // mycard-read endpoint initialization
let mykad_reader_endpoint = "{{ config('mykad-reader.endpoint') }}"; let mykad_reader_endpoint = "{{ config('mykad-reader.endpoint') }}";
let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}"; let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}";
let store_mykad_photo_endpoint = "{{ route('customer.storeMykadPhoto') }}";
let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}"; let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}";
</script> </script>
<script src="{{ asset('js/mykad-reader-customer.js') }}?v=4"></script> <script src="{{ asset('js/mykad-reader-customer.js') }}?v=5"></script>
<script src="{{ asset('js/postcode-concerns.js') }}"></script> <script src="{{ asset('js/postcode-concerns.js') }}"></script>
@endpush @endpush
+1
View File
@@ -133,6 +133,7 @@ Route::get('/gadai/batal',['as'=>'gadai.batal','uses'=>'GadaianController@batal'
Route::get('/customer/otp',['as'=>'customer.otp','uses'=>'CustomersController@getOTP'])->middleware('auth','teller'); Route::get('/customer/otp',['as'=>'customer.otp','uses'=>'CustomersController@getOTP'])->middleware('auth','teller');
Route::get('/getwakil',['as'=>'wakil.search','uses'=>'PenebusanController@getWakil'])->middleware('auth','teller'); Route::get('/getwakil',['as'=>'wakil.search','uses'=>'PenebusanController@getWakil'])->middleware('auth','teller');
Route::get('/cari',['as'=>'wakil.caricustomer','uses'=>'CustomersController@cari'])->middleware('auth','teller'); Route::get('/cari',['as'=>'wakil.caricustomer','uses'=>'CustomersController@cari'])->middleware('auth','teller');
Route::post('/customer/mykad-photo',['as'=>'customer.storeMykadPhoto','uses'=>'CustomersController@storeMykadPhotoIfMissing'])->middleware('auth','teller');
Route::post('/createwakil',['as'=>'wakil.create','uses'=>'PenebusanController@createWakil'])->middleware('auth','teller'); Route::post('/createwakil',['as'=>'wakil.create','uses'=>'PenebusanController@createWakil'])->middleware('auth','teller');
Route::get('/firstwakil',['as'=>'wakil.first','uses'=>'PenebusanController@getfirstWakil'])->middleware('auth','teller'); Route::get('/firstwakil',['as'=>'wakil.first','uses'=>'PenebusanController@getfirstWakil'])->middleware('auth','teller');
Route::get('/history',['as'=>'gadai.history','uses'=>'GadaianController@history'])->middleware('auth','teller'); Route::get('/history',['as'=>'gadai.history','uses'=>'GadaianController@history'])->middleware('auth','teller');