Update MyKad reader implementation

- prevent teller from overriding name, religion, address, and other
  sensitive fields if using MyKad reader
- add support for Google Chrome browser ver. 64 backward compatible with
  branches PC
- enhance error message handling when scanning if there is no card
  reader connected, no card is inserted in card reader, and if card
  reader is entered incorrectly
This commit is contained in:
afiq.hamzah
2023-02-23 14:08:42 +08:00
parent 5c4ca1adb9
commit 3bd2fc1395
2 changed files with 352 additions and 21 deletions
+77 -18
View File
@@ -1,7 +1,6 @@
let button_read_mykad = document.getElementById("button_read_mykad");
let button_close_modal = document.querySelector("#daftar_pelanggan button.btn.btn-danger");
let button_close_modal = document.querySelector("#daftar_pelanggan_mykad_reader button.btn.btn-danger");
let original_photo_loader = document.querySelector("#photo-mykad").src;
let showLoader = () => {
@@ -25,10 +24,21 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
function callbackDoneGetListCardReader(data, respcode)
{
console.log('done get list card reader');
if(respcode === -1){
console.log({'location': 'callbackDoneGetListCardReader', 'data' : data} );
}
let isDevicesConnected = Object.keys(data).length > 0;
if(! isDevicesConnected){
hideLoader();
Swal.fire({ icon: 'error', title: 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan', });
return;
}
let slots = data.toString().split(',');
WebSocketManager.open(doneOpenReaderConnection, slots[0]);
}
@@ -38,18 +48,44 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
WebSocketManager.readAllText(callBackAfterReadMykad);
}
function handleMyKadReaderError(sw12AndApiName)
{
hideLoader();
let error_handler_dictionary = {
'0x80100069' : 'Tiada MyKad dikesan, sila masukkan Mykad untuk dibaca',
'0x80100066' : 'MyKad mungkin dimasukkan terbalik, sila masukkan dengan betul',
'default' : sw12AndApiName,
};
let error_data = sw12AndApiName.split(" ");
let error_code = error_data.filter(x => x.startsWith('0x')).splice(-1)
let error_message = error_handler_dictionary['default'];
let hasDefinedError = error_handler_dictionary.hasOwnProperty(error_code);
if(hasDefinedError){
error_message = error_handler_dictionary[error_code];
}
Swal.fire({ icon: 'error', title: error_message, });
}
function callBackAfterReadMykad(data, respcode, sw12AndApiName)
{
if(respcode !== 0){
hideLoader();
Swal.fire({icon: 'error', title: sw12AndApiName,});
return;
handleMyKadReaderError(sw12AndApiName);
return
}
checkIfMyKadExists(data.IDNum)
.then(response => response[0])
.then(response => {
if (response === 'no-data') {
let isResponseArray = Array.isArray(response);
resolved_response = isResponseArray ? response.pop() : response;
if (resolved_response === 'no-data') {
populateDaftarPelangganModal(data, handleReadPhoto);
} else {
let input_search_noic = document.getElementById("noic");
@@ -70,16 +106,28 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
hideLoader();
}
function checkIfMyKadExists(myKadNumber){
function checkIfMyKadExists(myKadNumber) {
let myKadNumberWithoutHyphens = myKadNumber.replace(/-/g, '');
const url = wakil_caricustomer_endpoint + '/?icno=' + myKadNumberWithoutHyphens;
return fetch(url).then((response) => response.json());
const connectionConfig = {
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": document.querySelector('input[name="_token"]').value
},
credentials: "same-origin",
};
return fetch(url, connectionConfig)
.then(response => response.json())
}
let populateDaftarPelangganModal = (data, callbackAfterPopulateDaftarPelanggan) => {
$('#daftar_pelanggan').modal('show');
let modal_form = document.querySelector(".modal-body .row.form-group");
$('#daftar_pelanggan_mykad_reader').modal('show');
let modal_form = document.querySelector("#daftar_pelanggan_mykad_reader .modal-body .row.form-group");
const religions = [
['01', 'Islam'],
@@ -146,7 +194,16 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
return isBumiputera;
};
let photo_container = document.getElementById("photo-mykad-container");
let disablePointerEventsOnClickableInputs = () => {
modal_form.querySelector("input[name='bumiputera']").style.pointerEvents = 'none';
modal_form.querySelector("input[name='warganegara']").style.pointerEvents = 'none';
modal_form.querySelector("select[name='bangsa']").style.pointerEvents = 'none';
modal_form.querySelector("select[name='agama']").style.pointerEvents = 'none';
}
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
let photo_container = mykad_modal.querySelector("#photo-mykad-container");
modal_form.querySelector("input[name='name']").value = data.OrgName;
modal_form.querySelector("input[name='noic']").value = getMyKadNumberWithoutHypens(data.IDNum);
@@ -158,11 +215,11 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
modal_form.querySelector("select[name='agama']").value = getCodeFromValue(data.Religion, religions);
modal_form.querySelector("textarea[name='alamat']").value = [data.Address1, data.Address2, data.Address3].join(", ");
let getPostCode = (postCodeCity) => postCodeCity.replace(/\D/g, '');
let getPostCode = (postCodeCity) => postCodeCity.replace(/\D/g, '');
modal_form.querySelector("input[name='poskod']").value = getPostCode(data.PostcodeCity);
let getCityName = (postCodeCity) => postCodeCity.replace(/\d/g, '');
modal_form.querySelector("input[name='daerah']").value = getCityName(data.PostcodeCity);
let getCityName = (postCodeCity) => postCodeCity.replace(/\d/g, '');
modal_form.querySelector("input[name='daerah']").value = getCityName(data.PostcodeCity).trim();
modal_form.querySelector("input[name='negeri']").value = data.State;
modal_form.querySelector("input[name='age']").value = getAgeFromDateOfBirth(data.BirthDate);
@@ -173,9 +230,10 @@ let original_photo_loader = document.querySelector("#photo-mykad").src;
photo_container.classList.remove('d-none');
photo_container.classList.add('d-flex');
$('#daftar_pelanggan').modal('show');
$('#daftar_pelanggan_mykad_reader').modal('show');
disablePointerEventsOnClickableInputs();
callbackAfterPopulateDaftarPelanggan();
}
@@ -203,9 +261,10 @@ function doneOpenReaderConnection2()
function callbackAfterReadPhoto2(data, RespCode, RespSW12AndApiName)
{
let photo = document.getElementById("photo-mykad");
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
let photo = mykad_modal.querySelector("#photo-mykad");
let isDataEmpty = data
let isDataEmpty = data
&& Object.keys(data).length === 0
&& Object.getPrototypeOf(data) === Object.prototype;
+275 -3
View File
@@ -37,7 +37,7 @@
<button class="btn btn-outline-primary" type="submit" id="button-addon2">Cari</button>
</div>
</div>
<span class="btn btn-outline-primary d-none" id="button_read_mykad">Baca MyKad</span>
<span class="btn btn-outline-primary" id="button_read_mykad">Baca MyKad</span>
</div>
</div>
</form>
@@ -181,6 +181,140 @@
<button type="button" class="btn btn-danger" data-dismiss="modal">Batal</button>
<input type="submit" name="submit" class="btn btn-primary" id="terima_ic" value="Terima">
</div>
</form>
</div>
</div>
</div>
<!-- Modal MyKad Reader -->
<div class="modal fade" id="daftar_pelanggan_mykad_reader" tabindex="-1" data-backdrop="static" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Daftar Pelanggan</h5>
</div>
<div class="modal-body">
<form action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
@csrf
<div class="row form-group">
<div class="col-md-12 d-flex justify-content-center pb-3 d-none" id="photo-mykad-container">
<img src="{{ asset('img/spinner-loader.gif') }}" alt="" id="photo-mykad" width="150" height="200" object-fit="cover">
</div>
<div class="col-md-6">
<div class="row form-group">
<div class="col-md-4">Nama</div>
<div class="col-md-8"><input class="form-control" type="text" name="name" id="name" autocomplete="off" placeholder="Nama" readonly></div>
<div class="col-md-4">No KP</div>
<div class="col-md-8"><input class="form-control" type="text" name="noic" id="noic" value="{{$noic ?? ''}}" readonly></div>
<div class="col-md-4">Tarikh Lahir</div>
<div class="col-md-8"><input class="form-control" type="text" name="tarikh_lahir" id="tarikh_lahir" readonly></div>
<div class="col-md-4">Negeri Lahir</div>
<div class="col-md-8"><input class="form-control" type="text" name="negeri_lahir" id="negeri_lahir" readonly></div>
<div class="col-md-4">Jantina</div>
<div class="col-md-8"><input class="form-control" type="text" name="jantina" id="jantina" readonly></div>
<div class="col-md-4">No Telefon 1</div>
<div class="col-md-8"><input class="form-control" type="text" name="main_phone" id="main_phone" autocomplete="off" placeholder="No Telefon"></div>
<div class="col-md-4">No Telefon 2</div>
<div class="col-md-8"><input class="form-control" type="text" name="alternate_phone" id="alternate_phone" autocomplete="off" placeholder="No Telefon"></div>
<div class="col-md-12 ml-4">
<div class="form-group row">
<div class="form-check form-check-inline">
<input checked class="form-check-input" type="checkbox" id="warganegara" name="warganegara" value="warganegara" readonly>
<label class="form-check-label" for="warganegara">Warganegara</label>
</div>
<div class="form-check form-check-inline">
<input checked class="form-check-input" type="checkbox" id="bumiputera" name="bumiputera" value="bumiputera" readonly>
<label class="form-check-label" for="bumiputera">Bumiputera</label>
</div>
</div>
</div>
<div class="col-md-4">Agama</div>
<div class="col-md-8">
<select class="form-control" name="agama" id="agama" readonly>
<option selected value="01">Islam</option>
<option value="02">Hindu</option>
<option value="03">Buddha</option>
<option value="04">Kristian</option>
<option value="05">Lain-lain</option>
</select>
</div>
<div class="col-md-4">Ahli</div>
<div class="col-md-8">
<select class="form-control" name="kumpulan_sahabat" id="kumpulan_sahabat">
<option value="0">Tidak</option>
<option value="1">Kumpulan PKINK</option>
<option value="2">Sahabat</option>
</select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row form-group">
<div class="col-md-4">Alamat</div>
<div class="col-md-8"><textarea class="form-control" style="resize:none" name="alamat" id="alamat" cols="30" rows="3" placeholder="Alamat Pelanggan" readonly></textarea></div>
<div class="col-md-4">Poskod</div>
<div class="col-md-8"><input class="form-control" type="text" name="poskod" id="poskod" autocomplete="off" placeholder="Poskod" readonly></div>
<div class="col-md-4">Daerah</div>
<div class="col-md-8"><input class="form-control" type="text" name="daerah" id="daerah" readonly autocomplete="off" readonly></div>
<div class="col-md-4">Negeri</div>
<div class="col-md-8"><input class="form-control" type="text" name="negeri" id="negeri" readonly autocomplete="off" readonly></div>
<div class="col-md-4">Umur</div>
<div class="col-md-8"><input class="form-control" type="text" name="age" id="age" readonly></div>
<div class="col-md-4">Bangsa</div>
<div class="col-md-8">
<select class="form-control" name="bangsa" id="bangsa" readonly>
<option selected value="M">Melayu</option>
<option value="C">Cina</option>
<option value="I">India</option>
<option value="L">Lain-lain</option>
</select>
</div>
<div class="col-md-4">Bank</div>
<div class="col-md-8">
<select class="form-control" name="bank_name" data-placeholder="Pilih Bank" id="bank_name">
<option disabled selected value> -- Pilih Bank -- </option>
@foreach($bank as $banks)
<option value="{{ $banks['abbreviation'] }}">{{ $banks['name'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">No Akaun</div>
<div class="col-md-8"><input class="form-control" type="text" name="no_akaun" id="no_akaun" placeholder="No Akaun Bank"></div>
<div class="col-md-4">Tujuan Gadai</div>
@php
$tujuangadai = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/tujuangadai')->json();
@endphp
<div class="col-md-8">
<select class="form-control" name="tujuan_gadai" data-placeholder="Pilih Tujuan Gadai" id="tujuan_gadai">
<option disabled selected value> -- Pilih Tujuan Gadai -- </option>
@foreach($tujuangadai as $tujuan)
<option value="{{ $tujuan['keterangan'] }}">{{ $tujuan['keterangan'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">Pekerjaan</div>
@php
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/pekerjaan')->json();
@endphp
<div class="col-md-8">
<select class="form-control" name="pekerjaan" data-placeholder="Pilih Pekerjaan" id="pekerjaan">
<option disabled selected value> -- Pilih Pekerjaan -- </option>
@foreach($pekerjaan as $kerja)
<option value="{{ $kerja['kodkerja'] }}">{{ $kerja['keterangan'] }}</option>
@endforeach
</select>
</div>
<div class="col-md-4">Nota</div>
<div class="col-md-8"><input class="form-control" type="text" name="nota" id="nota" placeholder="Nota"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="text" hidden name="idtype" value="nokp">
<button type="button" class="btn btn-danger" data-dismiss="modal">Batal</button>
<input type="submit" name="submit" class="btn btn-primary" id="terima_ic" value="Terima">
</div>
</form>
</div>
</div>
@@ -453,7 +587,6 @@
function verifycustomer(){
// Construct URLSearchParams object instance from current URL querystring.
var queryParams = new URLSearchParams(window.location.search);
@@ -590,6 +723,146 @@
return true;
}
function verifycustomerMyKadReader(){
let myKadModal = $('#daftar_pelanggan_mykad_reader');
// Construct URLSearchParams object instance from current URL querystring.
var queryParams = new URLSearchParams(window.location.search);
let updated_icno = document.querySelector('#daftar_pelanggan_mykad_reader input[name="noic"]').value
queryParams.set("noic", updated_icno);
history.replaceState(null, null, "?"+queryParams.toString());
$("#terima_ic", myKadModal).prop('disabled', true);
var customername = $('#name', myKadModal).val();
var customeralamat = $('#alamat', myKadModal).val();
var customerposkod = $('#poskod', myKadModal).val();
var customerphone = $('#main_phone', myKadModal).val();
var customerphone_alternate = $('#alternate_phone', myKadModal).val();
var age = $('#age', myKadModal).val();
if(age < 18){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Umur Pelanggan Tidak Mencukupi 18 Tahun'
});
event.preventDefault();
return false;
}
if(customername == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nama Pelanggan'
});
event.preventDefault();
return false;
}
if(customeralamat == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Alamat Pelanggan'
});
event.preventDefault();
return false;
}
if(customerposkod == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Poskod'
});
event.preventDefault();
return false;
}else{
if(customerposkod.length != 5){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa Poskod'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerposkod) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa Poskod'
});
event.preventDefault();
return false;
}
}
}
if(customerphone == ''){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if((customerphone.length < 9 || customerphone.length >= 12)){
$("#terima_ic", myKadModal), myKadModal.prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerphone) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nombor Sahaja Pada No Telefon Pelanggan'
});
event.preventDefault();
return false;
}
}
}
if(customerphone_alternate != ''){
if(customerphone_alternate.length < 8 && customerphone_alternate.length > 12){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Periksa No Telefon Pelanggan'
});
event.preventDefault();
return false;
}else{
if($.isNumeric(customerphone_alternate) == false){
$("#terima_ic", myKadModal).prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Sila Masukkan Nombor Sahaja Pada No Telefon Pelanggan'
});
event.preventDefault();
return false;
}
}
}
return true;
}
function verifycustomerpassport(){
$("#terima_passport").prop('disabled', true);
var customername = $('#passport_name').val();
@@ -857,7 +1130,6 @@
var regex = /^[a-zA-Z0-9]+$/g;
function verifynokp(){
var id_type = $('#customerIDType').val();
var nokp = $('#noic').val();
if(id_type=='nokp'){