16 Commits

Author SHA1 Message Date
ismailmasseran cde136516d Feature/bsas integration (#31)
Reviewed-on: #31
Co-authored-by: ISMAIL MASSERAN <ismail@koppkb.com>
Co-committed-by: ISMAIL MASSERAN <ismail@koppkb.com>
2026-10-26 15:34:27 +08:00
ismailmasseran 1138e193ae Fix/gadai online details (#30)
Build Docker Image / build-frontend (push) Successful in 46s
Deploy to Staging / deploy (push) Successful in 20s
Co-authored-by: afrina <afrina@koppkb.com>
Reviewed-on: #30
2026-10-22 11:51:55 +08:00
ISMAIL MASSERAN 464d3e8dbb DONE: remove nginx for local docker compose 2026-10-01 13:45:40 +08:00
ismailmasseran abccad3219 Merge pull request 'DONE: update cicd to auto deploy on staging, clean up garbage files' (#29) from cicd/auto-deploy-staging into main
Build Docker Image / build-frontend (push) Successful in 39s
Deploy to Staging / deploy (push) Successful in 20s
Reviewed-on: #29
2026-10-01 12:01:23 +08:00
ISMAIL MASSERAN 7ad3fd4f86 DONE: update cicd to auto deploy on staging, clean up garbage files 2026-10-01 11:59:50 +08:00
ISMAIL MASSERAN 713d5a4923 DONE: update hashed password in .env.production 2026-09-28 15:58:30 +08:00
ismailmasseran b76566a2d6 Feat/logs password protection (#28)
Build Docker Image / build-frontend (push) Successful in 42s
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: nurafrinaalimi16 <nurafrinaalimi16@gmail.com>
Reviewed-on: #28
2026-09-28 15:05:56 +08:00
ismailmasseran 38f6db2e10 Merge pull request 'fix: papar cara terima pada senarai dan cetakan SAG hanya apabila pembiayaan online aktif' (#27) from fix/sag-cara-terima-feature-flag into main
Reviewed-on: #27
2026-09-28 09:44:25 +08:00
nurafrinaalimi16 f95bdf1214 fix: papar cara terima pada senarai dan cetakan SAG hanya apabila pembiayaan online aktif 2026-09-27 16:55:12 +08:00
ISMAIL MASSERAN d614677601 DONE: add proper vector logo arrahn
Build Docker Image / build-frontend (push) Successful in 40s
2026-09-27 15:58:05 +08:00
ismailmasseran 7cd21ef610 Feat/dasar privasi (#26)
Co-authored-by: nurafrinaalimi16 <nurafrinaalimi16@gmail.com>
Reviewed-on: #26
2026-09-27 15:56:09 +08:00
nurafrinaalimi16 ca2a02e354 hotfix: betulkan lajur tarikh untuk semakan ubah dan hapus tebus
Build Docker Image / build-frontend (push) Successful in 40s
2026-09-27 15:10:29 +08:00
nurafrinaalimi16 76176ed453 hotfix:ubah hapus gadai
Build Docker Image / build-frontend (push) Successful in 40s
2026-09-27 13:05:54 +08:00
ISMAIL MASSERAN 17a5bfafdd DONE: update env.staging
Build Docker Image / build-frontend (push) Successful in 41s
2026-09-26 18:51:36 +08:00
ismailmasseran 5f29f064a0 Merge pull request 'feat(khazanah): papar tarikh ansuran terkini dalam senarai gadaian' (#25) from feat/khazanah-tarikh-ansuran-terkini into main
Reviewed-on: #25
2026-09-24 16:16:16 +08:00
nurafrinaalimi16 9dae32a462 feat(khazanah): papar tarikh ansuran terkini dalam senarai gadaian 2026-09-24 16:15:20 +08:00
43 changed files with 2212 additions and 546 deletions
+2
View File
@@ -50,3 +50,5 @@ MYKAD_READER_PORT=17321
MYKAD_READER_ENABLED_CAWANGAN='' MYKAD_READER_ENABLED_CAWANGAN=''
INTERNAL_NOTIFICATION_SECRET= INTERNAL_NOTIFICATION_SECRET=
LOGVIEWER_PASSWORD=
+1
View File
@@ -7,6 +7,7 @@ APP_URL=https://ujrah.erahn.com.my
API_URL=https://apiujrah.erahn.com.my API_URL=https://apiujrah.erahn.com.my
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOGVIEWER_PASSWORD="$2y$10$k7eiUyZdm3BruwAaT1gbmelTm.PPgZptOEQT1nlBjyiR84a4KBMlm"
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=192.168.1.216 DB_HOST=192.168.1.216
+5 -6
View File
@@ -4,17 +4,14 @@ APP_VERSION=v2.2-staging
APP_KEY=base64:O1XZDqK4H6+3GdrODgCaLr2l+OoWvgVhYr0GJ1KizW8= APP_KEY=base64:O1XZDqK4H6+3GdrODgCaLr2l+OoWvgVhYr0GJ1KizW8=
APP_DEBUG=true APP_DEBUG=true
APP_URL=https://staging.erahn.com.my APP_URL=https://staging.erahn.com.my
API_URL=https://api-staging.erahn.com.my
HTTP_PORT=8091 HTTP_PORT=8091
MYSQL_PUBLISH_PORT=3309 MYSQL_PUBLISH_PORT=3309
API_URL=https://api-staging.erahn.com.my
API_URL_KOOP=https://api-staging.erahn.com.my
API_URL_PKB=https://api-staging.erahn.com.my
API_ARRAHNBID=http://lelong.test/api/
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOG_LEVEL=debug LOG_LEVEL=debug
LOGVIEWER_PASSWORD=test123
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=mysql DB_HOST=mysql
@@ -55,4 +52,6 @@ MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=17321 MYKAD_READER_PORT=17321
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED MYKAD_READER_ENABLED_CAWANGAN=''
INTERNAL_NOTIFICATION_SECRET=staging_noti_secret
-50
View File
@@ -1,50 +0,0 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:WRTHcFTq5YARKVdA8YDUzsh9Ue8vD63ivgZgXNNBiGo=
APP_DEBUG=true
APP_URL=http://ujrahkopkb.erahn.com.my
API_URL=http://apiujrah-kopkb.erahn.com.my
#API_ARRAHNBID=http://lelong.test/api/
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=kaunter_erahn
DB_USERNAME=spider
DB_PASSWORD=G0dz!ll4
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
PUSHER_APP_ID=1132718
PUSHER_APP_KEY=26b0ec2f297f94b7aa9b
PUSHER_APP_SECRET=421b61fb5a8ecce477a6
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=8085
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
+37 -18
View File
@@ -1,6 +1,9 @@
name: Deploy to Staging name: Deploy to Staging
on: on:
push:
tags:
- "v*"
workflow_dispatch: workflow_dispatch:
inputs: inputs:
image_tag: image_tag:
@@ -18,10 +21,22 @@ jobs:
runs-on: host runs-on: host
steps: steps:
- name: Resolve image tag
id: resolve
run: |
set -e
if [ "${{ gitea.event_name }}" = "workflow_dispatch" ]; then
IMAGE_TAG="${{ inputs.image_tag }}"
else
IMAGE_TAG="${{ gitea.ref_name }}"
fi
echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
echo "Deploying image tag: ${IMAGE_TAG}"
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ inputs.image_tag }} ref: ${{ steps.resolve.outputs.IMAGE_TAG }}
- name: Login to Docker registry - name: Login to Docker registry
run: | run: |
@@ -33,28 +48,35 @@ jobs:
- name: Verify image exists in registry - name: Verify image exists in registry
run: | run: |
set -e set -e
IMAGE_TAG="${{ inputs.image_tag }}" IMAGE_TAG="${{ steps.resolve.outputs.IMAGE_TAG }}"
IMAGE="${APP_IMAGE}:${IMAGE_TAG}" IMAGE="${APP_IMAGE}:${IMAGE_TAG}"
echo "Checking if image exists: ${IMAGE}" echo "Checking if image exists: ${IMAGE}"
if ! docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then # Tag pushes build in parallel — wait for the image to appear
echo "ERROR: Image ${IMAGE} does not exist in registry!" for i in $(seq 1 30); do
echo "Run the Build Docker Image workflow first." if docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then
exit 1 echo "✓ ${IMAGE} found"
fi exit 0
echo "✓ ${IMAGE} found" fi
echo "Image not ready yet (attempt ${i}/30), waiting 20s..."
sleep 20
done
echo "ERROR: Image ${IMAGE} does not exist in registry after waiting!"
echo "Run the Build Docker Image workflow first."
exit 1
- name: Pull Docker image - name: Pull Docker image
run: | run: |
set -e set -e
IMAGE_TAG="${{ inputs.image_tag }}" IMAGE_TAG="${{ steps.resolve.outputs.IMAGE_TAG }}"
docker pull "${APP_IMAGE}:${IMAGE_TAG}" docker pull "${APP_IMAGE}:${IMAGE_TAG}"
echo "✓ Image pulled successfully" echo "✓ Image pulled successfully"
- name: Sync compose file and deploy - name: Sync compose file and deploy
run: | run: |
set -e set -e
IMAGE_TAG="${{ inputs.image_tag }}" IMAGE_TAG="${{ steps.resolve.outputs.IMAGE_TAG }}"
mkdir -p "${DEPLOY_DIR}/docker/nginx" "${DEPLOY_DIR}/public" mkdir -p "${DEPLOY_DIR}/docker/nginx" "${DEPLOY_DIR}/public"
cp docker-compose.staging.yml "${DEPLOY_DIR}/docker-compose.yml" cp docker-compose.staging.yml "${DEPLOY_DIR}/docker-compose.yml"
@@ -62,15 +84,12 @@ jobs:
find "${DEPLOY_DIR}/public" -mindepth 1 -delete find "${DEPLOY_DIR}/public" -mindepth 1 -delete
cp -R public/. "${DEPLOY_DIR}/public/" cp -R public/. "${DEPLOY_DIR}/public/"
if [ ! -f "${DEPLOY_DIR}/.env.staging" ]; then if [ ! -f .env.staging ]; then
if [ -f .env.staging ]; then echo "ERROR: .env.staging is missing in the repository"
cp .env.staging "${DEPLOY_DIR}/.env.staging" exit 1
echo "Copied .env.staging from repo (first deploy). Edit ${DEPLOY_DIR}/.env.staging on the server if needed."
else
echo "ERROR: ${DEPLOY_DIR}/.env.staging is missing on the server"
exit 1
fi
fi fi
cp .env.staging "${DEPLOY_DIR}/.env.staging"
echo "✓ Synced .env.staging from repo"
cd "${DEPLOY_DIR}" cd "${DEPLOY_DIR}"
+17 -19
View File
@@ -5,15 +5,12 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider; use App\Providers\RouteServiceProvider;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use App\Dashboard; use App\Dashboard;
use App\User; use App\User;
use Carbon\Carbon;
class LoginController extends Controller class LoginController extends Controller
{ {
@@ -144,25 +141,26 @@ class LoginController extends Controller
} }
} }
public function logout(){ public function logout(Request $request)
$current = Carbon::now(); {
$current = new Carbon();
$current = $current->toDateString();
$auth_user = Auth::guard('web')->user(); $auth_user = Auth::guard('web')->user();
Auth::logout();
Session::flush();
$session = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/logoutsession/' . $auth_user['cawangan'], [
'id' => $auth_user['id'],
'nama' => $auth_user['name'],
'nokp' => $auth_user['no_kp'],
'email' => $auth_user['email'],
'taraf' => $auth_user['role_harian'],
'tarikh' => $current,
]); Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return Redirect::to('/'); if ($auth_user) {
Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/logoutsession/' . $auth_user['cawangan'], [
'id' => $auth_user['id'],
'nama' => $auth_user['name'],
'nokp' => $auth_user['no_kp'],
'email' => $auth_user['email'],
'taraf' => $auth_user['role_harian'],
'tarikh' => now()->toDateString(),
]);
}
return redirect('/');
} }
public function GetRolesHarian(Request $request, $kodcaw){ public function GetRolesHarian(Request $request, $kodcaw){
+80 -18
View File
@@ -298,7 +298,7 @@ class GadaianController extends Controller
} }
if ($caraTerima === 'ONLINE' && !$this->hasMatchingBorangOnlinePrint($request)) { if ($caraTerima === 'ONLINE' && !$this->hasMatchingBorangOnlinePrint($request)) {
return redirect()->back()->with('error', 'Sila cetak borang pengesahan pelanggan sebelum tekan Terima.'); return $this->gadaiStoreResult($request, false, 'Sila cetak borang pengesahan pelanggan sebelum tekan Terima.');
} }
//$total_nilai_marhun = $request->total_nilai_marhun; //$total_nilai_marhun = $request->total_nilai_marhun;
@@ -320,6 +320,27 @@ class GadaianController extends Controller
$keuntungan12bulan = $keuntungan_sebln * 12; $keuntungan12bulan = $keuntungan_sebln * 12;
$hargajualan = $request->pinjaman + $keuntungan12bulan; $hargajualan = $request->pinjaman + $keuntungan12bulan;
// BSAS commodity order must succeed before finalizing gadai.
$bsas = Http::withOptions(['verify' => config('app.api_verify')])
->timeout(60)
->post(config('api.url') . '/api/komuditi/bsas/order', [
'norujukan' => $request->norujukan_gadai,
'kodcaw' => $auth_user['cawangan'],
'pinjaman' => round((float) $request->pinjaman, 2),
'hargajualan' => round((float) $hargajualan, 2),
'nama_pelanggan' => $request->nama_pelanggan ?: $request->nama_akaun,
'teller' => $auth_user['name'],
]);
if (!$bsas->successful()) {
$body = $bsas->json();
$msg = is_array($body) && isset($body['message'])
? $body['message']
: 'Gagal pembelian komoditi BSAS. Sila cuba lagi.';
return $this->gadaiStoreResult($request, false, $msg);
}
//Log::info('DEBUG NOKP:' . $request->nokp); //Log::info('DEBUG NOKP:' . $request->nokp);
$gadai = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/gadai/norujukan/' . $auth_user['cawangan'] . '/' . $request->norujukan_gadai, [ $gadai = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/gadai/norujukan/' . $auth_user['cawangan'] . '/' . $request->norujukan_gadai, [
'kodcaw' => $auth_user['cawangan'], 'kodcaw' => $auth_user['cawangan'],
@@ -344,14 +365,6 @@ class GadaianController extends Controller
'rebate' => $this->parseMoney($request->rebate), 'rebate' => $this->parseMoney($request->rebate),
])->json(); ])->json();
$komuditi = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/komuditi/transaksi', [
'unit_komuditi' => round($request->pinjaman, 2),
'norujukan' => $request->norujukan_gadai,
'kodcaw' => $auth_user['cawangan'],
'teller' => $auth_user['name']
]);
if ($caraTerima === 'ONLINE') { if ($caraTerima === 'ONLINE') {
$online = Http::withOptions(['verify' => config('app.api_verify')]) $online = Http::withOptions(['verify' => config('app.api_verify')])
->asForm() ->asForm()
@@ -374,7 +387,7 @@ class GadaianController extends Controller
$msg = is_array($body) && isset($body['message']) $msg = is_array($body) && isset($body['message'])
? $body['message'] ? $body['message']
: 'Gagal daftar pembiayaan online. Sila cuba lagi.'; : 'Gagal daftar pembiayaan online. Sila cuba lagi.';
return redirect()->back()->with('error', $msg); return $this->gadaiStoreResult($request, false, $msg);
} }
$this->notifyPcPembiayaanOnlinePending($auth_user, $request, $pinjamanAmt); $this->notifyPcPembiayaanOnlinePending($auth_user, $request, $pinjamanAmt);
@@ -392,6 +405,33 @@ class GadaianController extends Controller
$request->session()->flash('message', 'Gadai berjaya. Pembiayaan online menunggu semakan PC.'); $request->session()->flash('message', 'Gadai berjaya. Pembiayaan online menunggu semakan PC.');
} }
return $this->gadaiStoreResult($request, true);
}
/**
* JSON for AJAX Terima (BSAS progress UI); normal redirect otherwise.
*/
protected function gadaiStoreResult(Request $request, bool $ok, string $message = null)
{
if ($request->ajax() || $request->wantsJson()) {
if (!$ok) {
return response()->json([
'ok' => false,
'message' => $message ?: 'Gadai tidak berjaya.',
], 422);
}
return response()->json([
'ok' => true,
'message' => $message ?: 'Gadai berjaya.',
'redirect' => route('customer_info'),
]);
}
if (!$ok) {
return redirect()->back()->with('error', $message ?: 'Gadai tidak berjaya.');
}
return redirect()->route('customer_info'); return redirect()->route('customer_info');
} }
@@ -671,16 +711,38 @@ class GadaianController extends Controller
// $pembayaran_segera = "https://spare.arrahn.com.my/pembayaransegera"; // $pembayaran_segera = "https://spare.arrahn.com.my/pembayaransegera";
$pembayaran_segera = "https://pay.arrahn.com.my"; $pembayaran_segera = "https://pay.arrahn.com.my";
$komoditi = Http::withOptions(['verify' => config('app.api_verify')]) $bsasSag = Http::withOptions(['verify' => config('app.api_verify')])
->get($api_url . '/api/komuditi/check') ->get($api_url . '/api/komuditi/bsas/order/norujukan/' . urlencode($norujukan));
->json();
$product = Http::withOptions(['verify' => config('app.api_verify')]) $komoditi = [];
->get($api_url . '/api/komuditi/description', ['kodproduk' => $komoditi['kodproduct']]) $product = [];
->json(); $kuantiti = 0;
$kuantiti = $gadai_detail['pinjaman'] / $komoditi['nilai'];
if ($bsasSag->successful() && is_array($bsasSag->json())) {
$bsasPayload = $bsasSag->json();
$komoditi = $bsasPayload['komoditi'] ?? [];
$product = $bsasPayload['product'] ?? [];
$kuantiti = (float) ($bsasPayload['kuantiti'] ?? 0);
}
if (!is_array($komoditi)) {
$komoditi = [];
}
if (!is_array($product)) {
$product = [
'product_code' => $komoditi['kodproduct'] ?? '',
'kodproduk' => $komoditi['kodproduct'] ?? '',
'description' => '',
'unit' => 'TAN',
];
}
if ($gadai_detail['tagbaru'] == 1) { if ($gadai_detail['tagbaru'] == 1) {
return view('print.sag_ujrah_3', compact('komoditi', 'product', 'kuantiti', 'gadai_detail', 'customer_detail', 'marhun_detail', 'cawangan_detail', 'api_url', 'pembayaran_segera', 'auth_username', 'mutu', 'kadar_ujrah', 'kakitangan')); $cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
->get($api_url . '/api/cawangan/' . $kodcaw)->json();
$pembiayaanOnlineEnabled = !empty(($cawangan_info['features'] ?? [])['pembiayaan_online']);
return view('print.sag_ujrah_3', compact('komoditi', 'product', 'kuantiti', 'gadai_detail', 'customer_detail', 'marhun_detail', 'cawangan_detail', 'api_url', 'pembayaran_segera', 'auth_username', 'mutu', 'kadar_ujrah', 'kakitangan'))->with('pembiayaanOnlineEnabled', $pembiayaanOnlineEnabled);
} else { } else {
return view('print.sag', compact('gadai_detail', 'customer_detail', 'marhun_detail', 'cawangan_detail', 'api_url', 'pembayaran_segera', 'auth_username', 'mutu', 'kakitangan')); return view('print.sag', compact('gadai_detail', 'customer_detail', 'marhun_detail', 'cawangan_detail', 'api_url', 'pembayaran_segera', 'auth_username', 'mutu', 'kakitangan'));
} }
+270
View File
@@ -55,4 +55,274 @@ class KomuditiController extends Controller
return redirect()->route('komuditi'); return redirect()->route('komuditi');
} }
/**
* Khazanah branch list of BSAS commodity orders.
*/
public function khazanahBsas()
{
$auth_user = Auth::user();
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])
->json();
return view('homepage.khazanah.bsas_komoditi', compact('cawangan_info'));
}
/**
* Dummy BSAS eCert layout preview (bid, OTC, STB, then one commodity schedule).
*/
public function previewBsasEcert()
{
$line = [
['SUPPLIER' => 'FGV TRADING SDN BHD', 'VOLUME' => '0.75276406'],
];
return view('print.bsas_ecert', $this->presentBsasCertificates([
'e_cert_no' => 'CPO21OCT26-0000031-000',
'serial_number' => '1',
'otc_counter_party' => 'ALI BIN ABU',
'otc_murabaha_value' => '3,814.40',
'product_code' => 'CPO-MSIA-09',
'bid_ecert' => [
'ECERTNO' => 'CPO21OCT26-0000031-000',
'BUYER' => 'KOPERASI PERMODALAN KELANTAN BERHAD',
'OWNER' => 'KOPERASI PERMODALAN KELANTAN BERHAD',
'BIDNO' => '8',
'TOTALVALUE' => '3,200.00',
'CURRENCY' => 'MYR',
'PRICE' => '4,251.00',
'PRICE_MYR_EQUIVALENT' => '4,251.00',
'PURCHASETIMEDATE' => '15:11:53.316 21 Oct 2026',
'VALUEDATE' => '21 Oct 2026',
'PNAME' => 'CPO-MSIA-09',
'PVOLUME' => '0.75276406',
'LINE' => $line,
],
'otc_ecert' => [
'ECERTNO' => 'CPO21OCT26-0000031-000',
'SELLER' => 'KOPERASI PERMODALAN KELANTAN BERHAD',
'BUYER' => 'ALI BIN ABU',
'REPORTINGTIMEDATE' => '15:11:53.320 21 Oct 2026',
'VALUEDATE' => '21 Oct 2026',
'PNAME' => 'CPO-MSIA-09',
'PVOLUME' => '0.75276406',
'CURRENCY' => 'MYR',
'PRICE' => '4,251.00',
'PRICE_MYR_EQUIVALENT' => '4,251.00',
'TOTALVALUE' => '3,200.00',
'MURABAHAVALUE' => '3,814.40',
'LINE' => $line,
],
'stb_ecert' => [
'ECERTNO' => 'CPO21OCT26-0000031-000',
'SELLER' => 'ALI BIN ABU',
'BUYER' => 'BURSA MALAYSIA ISLAMIC SERVICES',
'TOTALVALUE' => '3,200.00',
'CURRENCY' => 'MYR',
'PRICE' => '4,251.00',
'PRICE_MYR_EQUIVALENT' => '4,251.00',
'SELLINGTIMEDATE' => '15:11:53.325 21 Oct 2026',
'VALUEDATE' => '21 Oct 2026',
'PNAME' => 'CPO-MSIA-09',
'PVOLUME' => '0.75276406',
'LINE' => $line,
],
], true));
}
/**
* Live BSAS eCerts from stored bid, OTC and STB payloads.
*/
public function showBsasEcert($uuid)
{
$response = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/komuditi/bsas/order/' . urlencode($uuid) . '/ecert');
if ($response->status() === 404) {
abort(404, $response->json('message') ?: 'eCert tidak dijumpai.');
}
if (!$response->successful()) {
abort(502, 'Gagal memuatkan eCert BSAS.');
}
$payload = $response->json();
if (!is_array($payload)) {
abort(502, 'Gagal memuatkan eCert BSAS.');
}
$presented = $this->presentBsasCertificates($payload, false);
if ($presented['certificates'] === []) {
abort(404, 'eCert belum tersedia untuk order ini.');
}
return view('print.bsas_ecert', $presented);
}
/**
* Bid, OTC and STB pages, plus one shared commodity schedule.
*/
protected function presentBsasCertificates(array $payload, bool $isDummy): array
{
$certificates = [];
$bid = $payload['bid_ecert'] ?? null;
if (is_array($bid) && $bid !== []) {
$certificates[] = $this->mapBidCertificate($bid, $payload);
}
$otc = $payload['otc_ecert'] ?? null;
if (is_array($otc) && $otc !== []) {
$certificates[] = $this->mapOtcCertificate($otc, $payload);
}
$stb = $payload['stb_ecert'] ?? null;
if (is_array($stb) && $stb !== []) {
$certificates[] = $this->mapStbCertificate($stb, $payload);
}
$scheduleSource = [];
if (is_array($otc) && $otc !== []) {
$scheduleSource = $otc;
} elseif (is_array($bid) && $bid !== []) {
$scheduleSource = $bid;
} elseif (is_array($stb) && $stb !== []) {
$scheduleSource = $stb;
}
$ecertNo = $scheduleSource['ECERTNO'] ?? ($payload['e_cert_no'] ?? '-');
return [
'certificates' => $certificates,
'ecert_no' => $ecertNo,
'lines' => $this->mapBsasLines($scheduleSource),
'isDummy' => $isDummy,
];
}
protected function mapBidCertificate(array $ecert, array $orderMeta): array
{
[$time, $date] = $this->splitBsasDateTime((string) ($ecert['PURCHASETIMEDATE'] ?? ''));
return $this->certificatePage(
'Certificate of Purchase',
'This is to certify that the following bid transaction has been executed<br>through the Bursa Suq Al-Sila\' in accordance with the<br>Rules of Bursa Suq Al-Sila\'.',
$ecert,
$orderMeta,
array_merge([
['label' => 'Buyer', 'value' => $ecert['BUYER'] ?? '-'],
['label' => 'Owner', 'value' => $ecert['OWNER'] ?? '-'],
['label' => 'Bid No', 'value' => $ecert['BIDNO'] ?? ($orderMeta['serial_number'] ?? '-')],
['label' => 'Purchase Time / Date', 'value' => $time . ' / ' . $date],
], $this->commodityDetailRows($ecert, $orderMeta))
);
}
protected function mapOtcCertificate(array $ecert, array $orderMeta): array
{
[$time, $date] = $this->splitBsasDateTime((string) ($ecert['REPORTINGTIMEDATE'] ?? ''));
$page = $this->certificatePage(
'Certificate of Murabahah',
'This is to certify that the following transaction has been executed<br>through the Bursa Suq Al-Sila\' in accordance with the<br>Rules of Bursa Suq Al-Sila\'.',
$ecert,
$orderMeta,
array_merge([
['label' => 'Seller', 'value' => $ecert['SELLER'] ?? 'KOPERASI PERMODALAN KELANTAN BERHAD'],
['label' => 'Buyer', 'value' => $ecert['BUYER'] ?? ($orderMeta['otc_counter_party'] ?? '-')],
['label' => 'Bid No', 'value' => $orderMeta['serial_number'] ?? '1'],
['label' => 'Reporting Time / Date', 'value' => $time . ' / ' . $date],
], $this->commodityDetailRows($ecert, $orderMeta), [
['label' => 'Transaction Type', 'value' => '-'],
])
);
$page['show_murabaha'] = true;
$page['murabaha'] = $ecert['MURABAHAVALUE'] ?? ($orderMeta['otc_murabaha_value'] ?? '-');
return $page;
}
protected function mapStbCertificate(array $ecert, array $orderMeta): array
{
[$time, $date] = $this->splitBsasDateTime((string) ($ecert['SELLINGTIMEDATE'] ?? ''));
return $this->certificatePage(
'Certificate of Sale',
'This is to certify that the following sale transaction has been executed<br>through the Bursa Suq Al-Sila\' in accordance with the<br>Rules of Bursa Suq Al-Sila\'.',
$ecert,
$orderMeta,
array_merge([
['label' => 'Seller', 'value' => $ecert['SELLER'] ?? ($orderMeta['otc_counter_party'] ?? '-')],
['label' => 'Buyer', 'value' => $ecert['BUYER'] ?? '-'],
['label' => 'Selling Time / Date', 'value' => $time . ' / ' . $date],
], $this->commodityDetailRows($ecert, $orderMeta))
);
}
protected function certificatePage(string $title, string $statement, array $ecert, array $orderMeta, array $rows): array
{
return [
'title' => $title,
'statement' => $statement,
'ecert_no' => $ecert['ECERTNO'] ?? ($orderMeta['e_cert_no'] ?? '-'),
'currency' => $ecert['CURRENCY'] ?? 'MYR',
'rows' => $rows,
'show_murabaha' => false,
'murabaha' => null,
];
}
protected function commodityDetailRows(array $ecert, array $orderMeta): array
{
return [
['label' => 'Value Date', 'value' => isset($ecert['VALUEDATE']) ? strtoupper((string) $ecert['VALUEDATE']) : '-'],
['label' => 'Product Code / Description', 'value' => $ecert['PNAME'] ?? ($orderMeta['product_code'] ?? '-')],
['label' => 'Unit', 'value' => 'Tonnage'],
['label' => 'Volume', 'value' => $ecert['PVOLUME'] ?? '-'],
['label' => 'Currency', 'value' => $ecert['CURRENCY'] ?? 'MYR'],
['label' => 'Price (MYR)', 'value' => $ecert['PRICE'] ?? '-'],
['label' => 'Price (MYR Equivalent)', 'value' => $ecert['PRICE_MYR_EQUIVALENT'] ?? ($ecert['PRICE'] ?? '-')],
['label' => 'Value', 'value' => $ecert['TOTALVALUE'] ?? '-'],
];
}
protected function mapBsasLines(array $ecert): array
{
$lines = [];
foreach (($ecert['LINE'] ?? []) as $line) {
if (!is_array($line)) {
continue;
}
$lines[] = [
'supplier' => $line['SUPPLIER'] ?? '-',
'volume' => $line['VOLUME'] ?? ($ecert['PVOLUME'] ?? '-'),
];
}
if ($lines === []) {
$lines[] = [
'supplier' => '-',
'volume' => $ecert['PVOLUME'] ?? '-',
];
}
return $lines;
}
protected function splitBsasDateTime(string $value): array
{
$value = trim($value);
if ($value === '') {
return ['-', '-'];
}
$parts = explode(' ', $value, 2);
return [
$parts[0] ?? '-',
isset($parts[1]) ? strtoupper($parts[1]) : '-',
];
}
} }
@@ -156,7 +156,6 @@ class PembiayaanOnlineController extends Controller
'kodbank' => $request->input('kodbank'), 'kodbank' => $request->input('kodbank'),
'noakaun' => $request->input('noakaun'), 'noakaun' => $request->input('noakaun'),
'nama_bank' => $request->input('nama_bank'), 'nama_bank' => $request->input('nama_bank'),
'nama_akaun' => $request->input('nama_akaun'),
'nota' => $request->input('nota'), 'nota' => $request->input('nota'),
'update_customer_bank' => true, 'update_customer_bank' => true,
]; ];
@@ -174,6 +173,19 @@ class PembiayaanOnlineController extends Controller
$payload['bukti_akaun_type'] = $file->getMimeType() ?: 'application/octet-stream'; $payload['bukti_akaun_type'] = $file->getMimeType() ?: 'application/octet-stream';
} }
if ($request->hasFile('borang')) {
$file = $request->file('borang');
if (!$file->isValid()) {
return redirect()->back()->with('error', 'Fail borang pengesahan tidak sah.');
}
if ($file->getSize() > 4 * 1024 * 1024) {
return redirect()->back()->with('error', 'Saiz fail maksimum 4MB.');
}
$payload['borang_data'] = base64_encode(file_get_contents($file->getRealPath()));
$payload['borang_filename'] = $file->getClientOriginalName();
$payload['borang_type'] = $file->getMimeType() ?: 'application/octet-stream';
}
$res = $this->api() $res = $this->api()
->asForm() ->asForm()
->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload); ->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload);
@@ -394,6 +406,22 @@ class PembiayaanOnlineController extends Controller
return $this->streamBase64File($file, 'slip-bank-' . $id); return $this->streamBase64File($file, 'slip-bank-' . $id);
} }
/**
* PC: view uploaded Borang Pengesahan Penerimaan Pembiayaan Secara Atas Talian.
*/
public function viewBorang($id)
{
$this->abortUnlessPembiayaanOnline();
return $this->streamLineFile(
$this->kodcaw(),
$id,
'/borang',
'Borang pengesahan tidak dijumpai.',
'Fail borang pengesahan kosong.',
'borang-pengesahan-' . $id
);
}
/** /**
* PC: view SAG for a queue line (does not mark SAG as printed). * PC: view SAG for a queue line (does not mark SAG as printed).
*/ */
@@ -426,6 +454,21 @@ class PembiayaanOnlineController extends Controller
return $this->streamBase64File($file, 'slip-bank-' . $id); return $this->streamBase64File($file, 'slip-bank-' . $id);
} }
/**
* Operasi: view uploaded Borang Pengesahan Penerimaan Pembiayaan Secara Atas Talian.
*/
public function operasiViewBorang($kodcaw, $id)
{
return $this->streamLineFile(
$kodcaw,
$id,
'/borang',
'Borang pengesahan tidak dijumpai.',
'Fail borang pengesahan kosong.',
'borang-pengesahan-' . $id
);
}
/** /**
* Operasi: view SAG for a batch line. * Operasi: view SAG for a batch line.
*/ */
@@ -434,6 +477,26 @@ class PembiayaanOnlineController extends Controller
return $this->renderLineSag($kodcaw, $id); return $this->renderLineSag($kodcaw, $id);
} }
protected function streamLineFile($kodcaw, $id, $path, $missingMessage, $emptyMessage, $fallbackName)
{
$res = $this->api()
->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . $path);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : $missingMessage;
return redirect()->back()->with('error', $msg);
}
$payload = $res->json();
$file = $payload['file'] ?? null;
if (!$file || empty($file['data'])) {
return redirect()->back()->with('error', $emptyMessage);
}
return $this->streamBase64File($file, $fallbackName);
}
protected function streamBase64File(array $file, $fallbackName) protected function streamBase64File(array $file, $fallbackName)
{ {
$raw = $file['data']; $raw = $file['data'];
@@ -522,17 +585,43 @@ class PembiayaanOnlineController extends Controller
$pembayaran_segera = 'https://pay.arrahn.com.my'; $pembayaran_segera = 'https://pay.arrahn.com.my';
$komoditi = $this->api()->get($api_url . '/api/komuditi/check')->json(); // Prefer per-Terima BSAS order; fall back to legacy daily KomuditiPurchase.
$product = $this->api() $bsasSag = $this->api()
->get($api_url . '/api/komuditi/description', [ ->get($api_url . '/api/komuditi/bsas/order/norujukan/' . urlencode($norujukan));
'kodproduk' => is_array($komoditi) ? ($komoditi['kodproduct'] ?? '') : '',
])
->json();
$nilai = is_array($komoditi) ? (float) ($komoditi['nilai'] ?? 0) : 0; if ($bsasSag->successful() && is_array($bsasSag->json())) {
$kuantiti = $nilai > 0 ? ((float) ($gadai_detail['pinjaman'] ?? 0) / $nilai) : 0; $bsasPayload = $bsasSag->json();
$komoditi = $bsasPayload['komoditi'] ?? [];
$product = $bsasPayload['product'] ?? [];
$kuantiti = (float) ($bsasPayload['kuantiti'] ?? 0);
} else {
$komoditi = $this->api()->get($api_url . '/api/komuditi/check')->json();
$product = $this->api()
->get($api_url . '/api/komuditi/description', [
'kodproduk' => is_array($komoditi) ? ($komoditi['kodproduct'] ?? '') : '',
])
->json();
$nilai = is_array($komoditi) ? (float) ($komoditi['nilai'] ?? 0) : 0;
$kuantiti = $nilai > 0 ? ((float) ($gadai_detail['pinjaman'] ?? 0) / $nilai) : 0;
}
if (!is_array($komoditi)) {
$komoditi = [];
}
if (!is_array($product)) {
$product = [
'kodproduk' => $komoditi['kodproduct'] ?? '',
'description' => '',
'unit' => 'TAN',
];
}
if (($gadai_detail['tagbaru'] ?? 0) == 1) { if (($gadai_detail['tagbaru'] ?? 0) == 1) {
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)->json();
$pembiayaanOnlineEnabled = !empty(($cawangan_info['features'] ?? [])['pembiayaan_online']);
return view('print.sag_ujrah_3', compact( return view('print.sag_ujrah_3', compact(
'komoditi', 'komoditi',
'product', 'product',
@@ -547,7 +636,7 @@ class PembiayaanOnlineController extends Controller
'mutu', 'mutu',
'kadar_ujrah', 'kadar_ujrah',
'kakitangan' 'kakitangan'
)); ))->with('pembiayaanOnlineEnabled', $pembiayaanOnlineEnabled);
} }
return view('print.sag', compact( return view('print.sag', compact(
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use Closure;
class LogViewerPassword
{
public function handle($request, Closure $next)
{
$passwordHash = config('logviewer.password');
if (!is_string($passwordHash) || $passwordHash === '') {
return response('Akses log belum dikonfigurasi.', 503)
->header('Cache-Control', 'no-store');
}
$provided = $request->getPassword();
if ($request->getUser() !== 'logviewer' || !is_string($provided) ||
!password_verify($provided, $passwordHash)) {
return response('Kata laluan diperlukan untuk mengakses log.', 401, [
'WWW-Authenticate' => 'Basic realm="Log Viewer", charset="UTF-8"',
'Cache-Control' => 'no-store',
]);
}
$response = $next($request);
$response->headers->set('Cache-Control', 'no-store');
return $response;
}
}
+6
View File
@@ -44,6 +44,12 @@ class RoleAccess
return $next($request); return $next($request);
} }
break; break;
case 'masteradmin':
if ($userRole == 'masteradmin') {
return $next($request);
}
break;
case 'penilai': case 'penilai':
if ($userRole == 'penilai') { if ($userRole == 'penilai') {
+1
View File
@@ -1,6 +1,7 @@
<?php <?php
return [ return [
'password' => env('LOGVIEWER_PASSWORD'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pattern and storage path settings | Pattern and storage path settings
+3 -10
View File
@@ -5,6 +5,8 @@ services:
dockerfile: docker/php/Dockerfile dockerfile: docker/php/Dockerfile
target: development target: development
working_dir: /var/www/html working_dir: /var/www/html
ports:
- "8081:8000"
volumes: volumes:
- ./:/var/www/html:cached - ./:/var/www/html:cached
environment: environment:
@@ -15,16 +17,7 @@ services:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
depends_on: depends_on:
- mysql - mysql
command: ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
web:
image: nginx:1.25-alpine
ports:
- "8081:80"
volumes:
- ./:/var/www/html:cached
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
mysql: mysql:
image: mysql:8.0 image: mysql:8.0
+8 -1
View File
@@ -2,7 +2,14 @@ FROM php:7.4-fpm AS base
WORKDIR /var/www/html WORKDIR /var/www/html
RUN apt-get update \ # php:7.4-fpm is Debian 11 (bullseye); after LTS end, packages live on archive.debian.org
RUN sed -i \
-e 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' \
-e 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' \
-e 's|http://deb.debian.org/debian-security|http://archive.debian.org/debian-security|g' \
/etc/apt/sources.list \
&& printf 'Acquire::Check-Valid-Until "false";\n' > /etc/apt/apt.conf.d/99no-check-valid-until \
&& apt-get update \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
git \ git \
pkg-config \ pkg-config \
-50
View File
@@ -1,50 +0,0 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:FFRogE6mJj8FaDSSHl/KpXpzqi2M+P4plxVSRgVM8JI=
APP_DEBUG=true
APP_URL=http://kaunterv2.localhost
API_URL=http://apiv2.localhost
API_ARRAHNBID=http://lelong.localhost/api/
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=database_zone2
DB_PORT=3306
DB_DATABASE=kaunterpkb
DB_USERNAME=root
DB_PASSWORD=secret
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
PUSHER_APP_ID=1132718
PUSHER_APP_KEY=26b0ec2f297f94b7aa9b
PUSHER_APP_SECRET=421b61fb5a8ecce477a6
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=8085
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
-50
View File
@@ -1,50 +0,0 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:FFRogE6mJj8FaDSSHl/KpXpzqi2M+P4plxVSRgVM8JI=
APP_DEBUG=true
APP_URL=http://kaunterv2.localhost
API_URL=http://apiv2.kgmk.localhost
API_ARRAHNBID=http://lelong.localhost/api/
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=database_zone_franchise
DB_PORT=3306
DB_DATABASE=kaunter_koopguru
DB_USERNAME=root
DB_PASSWORD=secret
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
PUSHER_APP_ID=1132718
PUSHER_APP_KEY=26b0ec2f297f94b7aa9b
PUSHER_APP_SECRET=421b61fb5a8ecce477a6
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=8085
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
-50
View File
@@ -1,50 +0,0 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:FFRogE6mJj8FaDSSHl/KpXpzqi2M+P4plxVSRgVM8JI=
APP_DEBUG=true
APP_URL=http://kaunterv2.localhost
API_URL=http://apiv2.krk.localhost
API_ARRAHNBID=http://lelong.localhost/api/
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=database_zone_franchise
DB_PORT=3306
DB_DATABASE=kaunter_krk
DB_USERNAME=root
DB_PASSWORD=secret
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
PUSHER_APP_ID=1132718
PUSHER_APP_KEY=26b0ec2f297f94b7aa9b
PUSHER_APP_SECRET=421b61fb5a8ecce477a6
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MYKAD_READER_PORT=8085
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1136" viewBox="0 0 1600 1136">
<title>Vectorized Arabic calligraphy logo</title>
<path d="M 379,572 L 385,578 L 403,585 L 418,593 L 423,594 L 429,598 L 415,588 Z M 949,551 L 945,547 L 939,546 L 933,550 L 928,557 L 851,719 L 814,784 L 784,827 L 754,862 L 720,894 L 690,917 L 647,944 L 595,969 L 543,987 L 504,996 L 453,1002 L 406,1002 L 374,999 L 344,994 L 312,986 L 274,973 L 249,962 L 227,950 L 255,967 L 267,972 L 268,974 L 322,995 L 376,1008 L 417,1013 L 438,1014 L 499,1011 L 543,1003 L 572,995 L 606,983 L 660,958 L 708,928 L 748,896 L 785,858 L 815,819 L 836,787 L 864,737 L 895,674 L 930,597 L 943,571 L 950,563 Z M 292,508 L 288,515 L 247,563 L 256,571 L 260,573 L 266,572 L 303,528 L 303,519 Z M 989,450 L 969,456 L 945,459 L 917,458 L 900,451 L 894,451 L 888,454 L 880,462 L 869,476 L 842,504 L 802,536 L 765,557 L 741,567 L 722,573 L 676,582 L 630,584 L 600,582 L 573,578 L 525,566 L 523,567 L 520,573 L 520,585 L 530,614 L 538,654 L 538,687 L 535,707 L 529,729 L 516,757 L 498,782 L 472,807 L 429,836 L 393,854 L 358,867 L 312,878 L 287,881 L 235,880 L 194,871 L 162,857 L 179,868 L 214,883 L 238,889 L 273,893 L 308,892 L 347,886 L 386,875 L 418,862 L 440,851 L 465,836 L 500,809 L 523,783 L 531,771 L 541,751 L 548,730 L 552,710 L 553,666 L 545,627 L 535,596 L 535,590 L 540,581 L 543,579 L 551,579 L 578,587 L 607,592 L 638,595 L 675,595 L 697,593 L 741,584 L 777,571 L 804,557 L 827,542 L 853,521 L 881,493 L 893,478 L 907,465 L 918,463 L 935,469 L 962,470 Z M 1071,446 L 1064,445 L 1063,450 L 1057,461 L 1047,471 L 1035,480 L 1011,505 L 1001,520 L 993,536 L 987,555 L 984,576 L 984,594 L 987,613 L 991,625 L 999,640 L 1007,650 L 1018,659 L 1010,648 L 1009,643 L 1007,641 L 1000,615 L 999,598 L 1003,566 L 1010,546 L 1023,523 L 1046,497 L 1069,478 L 1076,468 L 1078,462 L 1078,454 L 1076,450 Z M 1247,419 L 1238,416 L 1233,418 L 1228,423 L 1200,481 L 1191,494 L 1179,507 L 1170,531 L 1170,536 L 1177,534 L 1189,525 L 1203,511 L 1217,492 L 1241,441 L 1245,435 L 1252,430 L 1252,426 Z M 1403,405 L 1394,406 L 1388,412 L 1383,423 L 1378,473 L 1374,491 L 1369,505 L 1367,523 L 1363,536 L 1362,545 L 1365,545 L 1375,533 L 1387,511 L 1395,475 L 1396,451 L 1399,435 L 1405,424 L 1409,420 L 1415,418 L 1411,411 Z M 1474,363 L 1486,379 L 1493,393 L 1500,416 L 1506,464 L 1506,502 L 1502,540 L 1495,575 L 1482,617 L 1470,646 L 1443,696 L 1422,727 L 1390,766 L 1331,825 L 1223,928 L 1149,1004 L 1136,1027 L 1132,1037 L 1132,1047 L 1137,1046 L 1177,1003 L 1251,929 L 1329,855 L 1401,784 L 1426,755 L 1447,727 L 1471,688 L 1492,644 L 1507,601 L 1516,561 L 1521,519 L 1520,462 L 1513,422 L 1508,407 L 1502,395 L 1492,381 Z M 1289,362 L 1286,360 L 1293,369 L 1302,386 L 1309,413 L 1309,458 L 1305,496 L 1296,538 L 1286,569 L 1269,606 L 1247,641 L 1218,676 L 1195,698 L 1159,726 L 1131,742 L 1103,752 L 1063,759 L 1044,759 L 1029,757 L 1002,748 L 1007,750 L 1014,756 L 1029,763 L 1058,770 L 1092,769 L 1114,765 L 1135,759 L 1166,744 L 1207,714 L 1241,681 L 1265,651 L 1284,620 L 1303,578 L 1316,533 L 1324,476 L 1325,437 L 1320,408 L 1307,382 Z M 896,320 L 883,320 L 875,324 L 871,328 L 866,341 L 866,357 L 869,367 L 874,375 L 885,384 L 886,383 L 883,378 L 881,368 L 881,358 L 884,347 L 890,338 L 900,332 L 910,332 L 902,323 Z M 953,302 L 952,303 L 958,320 L 958,340 L 953,364 L 940,394 L 941,398 L 945,401 L 959,400 L 969,375 L 973,355 L 973,335 L 970,323 L 959,306 L 957,306 Z M 939,267 L 940,273 L 942,276 L 946,278 L 955,278 L 971,266 L 977,266 L 982,269 L 973,262 L 956,253 L 951,254 L 947,260 Z M 1053,228 L 1072,251 L 1086,277 L 1092,295 L 1095,310 L 1095,333 L 1089,357 L 1073,385 L 1051,410 L 1034,425 L 1020,434 L 1042,428 L 1050,428 L 1056,430 L 1061,428 L 1079,410 L 1096,387 L 1105,368 L 1109,354 L 1110,323 L 1102,299 L 1100,288 L 1080,256 L 1076,253 L 1076,251 Z M 1152,174 L 1142,186 L 1141,191 L 1153,227 L 1166,255 L 1176,271 L 1189,287 L 1205,301 L 1199,296 L 1199,294 L 1189,282 L 1189,280 L 1182,271 L 1171,248 L 1157,209 L 1155,196 L 1155,176 L 1154,174 Z M 363,117 L 359,124 L 354,145 L 353,176 L 347,215 L 335,255 L 325,279 L 311,307 L 285,349 L 263,379 L 216,436 L 185,483 L 164,525 L 155,548 L 145,581 L 138,615 L 135,648 L 136,672 L 140,695 L 146,715 L 159,743 L 171,760 L 186,773 L 179,766 L 165,739 L 157,716 L 152,693 L 151,647 L 157,608 L 162,588 L 177,544 L 193,510 L 209,482 L 237,442 L 274,398 L 300,363 L 327,319 L 345,282 L 361,234 L 368,191 L 368,140 L 365,120 Z M 930,105 L 929,108 L 940,125 L 951,138 L 968,154 L 960,146 L 958,141 L 949,131 L 934,104 Z M 1363,78 L 1363,86 L 1358,98 L 1343,115 L 1332,137 L 1329,151 L 1329,168 L 1332,184 L 1337,199 L 1348,222 L 1368,254 L 1391,283 L 1423,316 L 1401,292 L 1401,290 L 1391,279 L 1370,249 L 1353,215 L 1347,197 L 1344,176 L 1347,152 L 1355,134 L 1375,108 L 1378,98 L 1378,92 L 1375,85 L 1370,80 Z" fill="#111111" fill-rule="evenodd"/>
<path d="M 240,467 L 225,482 L 198,514 L 195,520 L 199,527 L 203,529 L 237,559 L 245,563 L 252,557 L 290,510 L 290,507 L 287,502 L 283,500 L 275,492 L 272,491 L 270,488 L 267,487 L 247,470 Z M 1300,384 L 1293,371 L 1279,352 L 1258,336 L 1228,319 L 1205,303 L 1186,285 L 1177,274 L 1157,239 L 1143,203 L 1139,183 L 1139,160 L 1141,151 L 1139,149 L 1114,185 L 1105,203 L 1098,231 L 1098,272 L 1103,298 L 1111,322 L 1127,351 L 1152,384 L 1160,402 L 1165,418 L 1168,449 L 1164,478 L 1162,481 L 1162,486 L 1158,500 L 1144,531 L 1151,528 L 1174,511 L 1189,495 L 1200,479 L 1227,423 L 1237,415 L 1247,418 L 1251,422 L 1254,430 L 1254,436 L 1256,441 L 1256,475 L 1250,499 L 1237,527 L 1225,546 L 1216,557 L 1216,559 L 1212,562 L 1172,610 L 1147,635 L 1121,656 L 1106,665 L 1090,672 L 1065,676 L 1052,675 L 1037,671 L 1013,657 L 1004,648 L 996,637 L 989,623 L 985,610 L 983,596 L 983,575 L 986,555 L 994,531 L 1009,506 L 1026,487 L 1055,462 L 1061,452 L 1063,440 L 1059,432 L 1044,429 L 1026,433 L 1025,430 L 1041,418 L 1063,396 L 1081,371 L 1090,351 L 1093,338 L 1093,304 L 1084,275 L 1074,256 L 1061,238 L 1032,208 L 976,163 L 948,136 L 929,111 L 927,105 L 920,95 L 915,81 L 913,81 L 896,126 L 892,141 L 890,161 L 894,183 L 900,193 L 910,202 L 915,209 L 919,219 L 920,260 L 922,264 L 926,266 L 937,267 L 942,264 L 952,252 L 957,252 L 986,270 L 1011,293 L 1022,308 L 1031,326 L 1036,343 L 1036,352 L 1034,358 L 1024,371 L 1005,386 L 985,396 L 971,400 L 944,402 L 940,399 L 939,394 L 952,364 L 957,339 L 956,316 L 949,298 L 939,288 L 920,280 L 897,280 L 871,288 L 852,300 L 838,316 L 832,328 L 826,356 L 826,382 L 831,399 L 840,414 L 855,429 L 855,433 L 814,465 L 794,478 L 768,492 L 745,502 L 715,512 L 688,518 L 659,522 L 604,522 L 595,520 L 585,520 L 561,516 L 528,508 L 496,498 L 494,496 L 485,494 L 478,490 L 460,484 L 417,464 L 373,440 L 368,440 L 366,445 L 364,464 L 364,479 L 357,550 L 358,558 L 362,563 L 401,580 L 422,591 L 431,597 L 454,619 L 469,641 L 480,666 L 480,670 L 482,673 L 482,692 L 480,698 L 473,709 L 464,719 L 422,759 L 395,778 L 373,788 L 341,796 L 306,799 L 265,798 L 232,794 L 219,791 L 201,784 L 179,769 L 165,754 L 152,732 L 141,703 L 135,673 L 134,643 L 137,614 L 143,584 L 156,542 L 165,520 L 181,488 L 198,460 L 217,433 L 268,371 L 295,332 L 312,303 L 333,257 L 344,223 L 349,199 L 352,174 L 353,138 L 347,91 L 345,88 L 343,91 L 336,129 L 317,191 L 292,253 L 278,279 L 255,311 L 186,385 L 145,436 L 113,484 L 86,536 L 76,561 L 68,587 L 60,632 L 60,694 L 64,719 L 69,738 L 76,758 L 88,783 L 110,814 L 124,829 L 144,846 L 176,863 L 213,875 L 247,880 L 296,879 L 327,874 L 354,867 L 383,857 L 410,845 L 450,822 L 473,805 L 496,783 L 516,755 L 526,734 L 531,719 L 537,685 L 537,656 L 535,640 L 528,611 L 519,585 L 519,572 L 523,566 L 529,565 L 545,571 L 586,579 L 632,583 L 675,581 L 710,575 L 758,559 L 791,542 L 827,516 L 847,498 L 878,463 L 891,451 L 901,450 L 918,457 L 955,457 L 983,451 L 984,454 L 960,472 L 940,493 L 902,538 L 858,600 L 816,665 L 764,741 L 735,781 L 704,819 L 675,848 L 649,869 L 634,879 L 601,896 L 547,916 L 525,922 L 520,922 L 496,928 L 489,928 L 484,930 L 432,935 L 374,935 L 336,932 L 286,924 L 240,912 L 220,905 L 180,886 L 161,873 L 151,864 L 149,864 L 133,849 L 128,847 L 130,856 L 146,883 L 173,911 L 175,911 L 192,927 L 194,927 L 202,934 L 225,948 L 258,965 L 309,984 L 350,994 L 409,1001 L 451,1001 L 476,999 L 518,992 L 549,984 L 602,965 L 626,954 L 664,933 L 687,918 L 716,896 L 743,872 L 761,853 L 782,828 L 801,802 L 828,759 L 863,693 L 923,564 L 931,551 L 939,545 L 947,547 L 951,553 L 950,574 L 938,608 L 936,618 L 936,645 L 941,670 L 947,687 L 960,710 L 967,719 L 988,739 L 1009,750 L 1025,755 L 1046,758 L 1071,757 L 1106,750 L 1126,743 L 1148,732 L 1174,714 L 1214,679 L 1243,645 L 1264,613 L 1284,571 L 1299,522 L 1307,470 L 1308,414 L 1305,398 Z M 888,318 L 899,320 L 908,327 L 914,337 L 918,349 L 918,364 L 914,376 L 906,384 L 896,388 L 881,383 L 872,374 L 867,365 L 864,351 L 866,336 L 870,328 L 879,320 Z M 1349,62 L 1336,62 L 1328,65 L 1321,70 L 1306,87 L 1296,108 L 1288,138 L 1282,176 L 1281,224 L 1284,254 L 1293,296 L 1302,324 L 1318,362 L 1340,403 L 1344,413 L 1350,437 L 1352,458 L 1350,493 L 1346,517 L 1336,543 L 1335,549 L 1362,515 L 1371,497 L 1377,472 L 1380,432 L 1384,417 L 1394,405 L 1403,404 L 1413,412 L 1424,431 L 1434,464 L 1434,469 L 1438,483 L 1441,509 L 1442,536 L 1440,563 L 1434,594 L 1422,629 L 1407,660 L 1376,708 L 1336,756 L 1309,784 L 1260,829 L 1232,857 L 1196,897 L 1176,922 L 1154,952 L 1121,1004 L 1112,1024 L 1110,1037 L 1111,1042 L 1143,1010 L 1162,988 L 1230,920 L 1337,818 L 1385,770 L 1410,741 L 1445,691 L 1467,650 L 1483,611 L 1495,570 L 1503,524 L 1505,499 L 1505,465 L 1500,422 L 1491,391 L 1483,376 L 1474,364 L 1453,343 L 1409,304 L 1375,265 L 1349,226 L 1333,191 L 1328,169 L 1328,150 L 1330,140 L 1337,123 L 1344,112 L 1357,98 L 1362,85 L 1362,75 L 1359,70 Z" fill="#F2CC00" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 166 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 50 KiB

+3
View File
@@ -115,6 +115,9 @@
</form> </form>
</div> </div>
<div class="card-footer text-center pt-0 px-lg-2 px-1"> <div class="card-footer text-center pt-0 px-lg-2 px-1">
<p class="mb-3 text-sm">
<a href="{{ route('dasar.privasi') }}" class="text-info font-weight-bold">Dasar Privasi</a>
</p>
<p class="mb-4 text-sm mx-auto"> <p class="mb-4 text-sm mx-auto">
Masalah untuk log masuk? Masalah untuk log masuk?
<a class="text-info text-gradient font-weight-bold">Hubungi <a class="text-info text-gradient font-weight-bold">Hubungi
@@ -0,0 +1,50 @@
@props([
'note' => '',
'page' => null,
'total' => null,
])
<style>
.bsas-ecert-footer {
flex: 0 0 auto;
margin-top: auto;
padding-top: 12px;
border-top: 2px solid var(--accent, #0f4c5c);
font-family: Arial, Helvetica, sans-serif;
color: var(--muted, #5a6570);
}
.bsas-ecert-footer .footer-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.bsas-ecert-footer .footer-note {
margin: 0;
font-size: 10px;
text-align: left;
line-height: 1.4;
}
.bsas-ecert-footer .page-no {
flex: 0 0 auto;
font-size: 10px;
white-space: nowrap;
}
</style>
<footer {{ $attributes->merge(['class' => 'bsas-ecert-footer']) }}>
<div class="footer-row">
<p class="footer-note">
@if(trim($slot) !== '')
{{ $slot }}
@else
{{ $note }}
@endif
</p>
@if($page !== null && $total !== null)
<div class="page-no">Page {{ $page }} of {{ $total }}</div>
@elseif($page !== null)
<div class="page-no">{{ $page }}</div>
@endif
</div>
</footer>
@@ -0,0 +1,63 @@
@props([
'company' => 'Koperasi Permodalan Kelantan Berhad',
'leftLogo' => null,
'rightLogo' => null,
])
@php
$leftLogo = $leftLogo ?? asset('img/svg/logo-kopkb.svg');
$rightLogo = $rightLogo ?? asset('img/svg/logo-arrahn-new.svg');
@endphp
<style>
.bsas-ecert-header {
flex: 0 0 auto;
padding-bottom: 8px;
margin-bottom: 14px;
}
.bsas-ecert-header .brand-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.bsas-ecert-header .logos {
display: flex;
align-items: center;
gap: 12px;
}
.bsas-ecert-header .logos img {
height: 78px;
width: auto;
object-fit: contain;
}
.bsas-ecert-header .logos.is-end {
justify-content: flex-end;
}
.bsas-ecert-header .company-heading {
text-align: center;
margin: 14px 0 0;
}
.bsas-ecert-header .company-heading .name {
margin: 0;
font-size: 16px;
font-weight: 700;
letter-spacing: .06em;
color: var(--accent, #0f4c5c);
text-transform: uppercase;
}
</style>
<header {{ $attributes->merge(['class' => 'bsas-ecert-header']) }}>
<div class="brand-row">
<div class="logos">
<img src="{{ $leftLogo }}" alt="KPKB">
</div>
<div class="logos is-end">
<img src="{{ $rightLogo }}" alt="Ar-Rahn">
</div>
</div>
<div class="company-heading">
<p class="name">{{ $company }}</p>
</div>
</header>
@@ -0,0 +1,35 @@
@props([
'leftLogo' => null,
'rightLogo' => null,
])
@php
$leftLogo = $leftLogo ?? asset('img/svg/logo-kopkb.svg');
$rightLogo = $rightLogo ?? asset('img/logo-arrahn.jpeg');
@endphp
<style>
.bsas-ecert-watermark {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 28px;
pointer-events: none;
z-index: 0;
opacity: .09;
}
.bsas-ecert-watermark img {
height: 230px;
width: auto;
max-width: 300px;
object-fit: contain;
filter: grayscale(1);
}
</style>
<div {{ $attributes->merge(['class' => 'bsas-ecert-watermark']) }} aria-hidden="true">
<img src="{{ $leftLogo }}" alt="">
<img src="{{ $rightLogo }}" alt="">
</div>
+16 -40
View File
@@ -716,8 +716,6 @@
var api_url = "{{ api_url_for_browser() }}"; var api_url = "{{ api_url_for_browser() }}";
var kodcaw = "{{ $cawangan_info['kodcaw'] }}"; var kodcaw = "{{ $cawangan_info['kodcaw'] }}";
var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}"; var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}";
var min_komoditi = "{{ $min_komoditi }}";
console.log('test api_arrahnbid: ', api_arrahnbid);
$('#daftar_gadai').on('click', async function(event) { $('#daftar_gadai').on('click', async function(event) {
event.preventDefault(); event.preventDefault();
if ($(this).prop('disabled')) { if ($(this).prop('disabled')) {
@@ -756,42 +754,14 @@
event.preventDefault(); event.preventDefault();
return false; return false;
} else { } else {
$.ajax({ // Commodity is purchased via BSAS on Terima; no bulk stock gate.
method: 'get', if (icbaru.length > 0) {
url: api_url + '/api/komuditi/check', window.location.href =
success: function(data) { "{{ route('gadaian', ['noic' => $customer_info['kpbaru']]) }}";
var check_komuditi = Object.keys(data).length; } else {
if (check_komuditi == 0) { window.location.href =
Swal.fire({ "{{ route('gadaian', ['noic' => $customer_info['kplama']]) }}";
icon: 'error', }
title: 'Unit Komoditi Berbaki Minima',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komoditi'
});
$("#daftar_gadai").prop('disabled', false);
event.preventDefault();
return false;
} else {
if (Number(data['unit_komuditi']) <= min_komoditi) {
$("#daftar_gadai").prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Unit Komoditi Berbaki Minima',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komoditi'
});
event.preventDefault();
return false;
} else {
if (icbaru.length > 0) {
window.location.href =
"{{ route('gadaian', ['noic' => $customer_info['kpbaru']]) }}";
} else {
window.location.href =
"{{ route('gadaian', ['noic' => $customer_info['kplama']]) }}";
}
}
}
}
});
} }
}); });
@@ -801,9 +771,12 @@
} }
}); });
var pembiayaanOnlineEnabled = @json(!empty(($cawangan_info['features'] ?? [])['pembiayaan_online']));
var tablegadai = initErahnDatatable('#myTable', { var tablegadai = initErahnDatatable('#myTable', {
order: [[1, 'desc']], order: [[1, 'desc']],
columnDefs: [ columnDefs: [
{ targets: 2, visible: pembiayaanOnlineEnabled, className: pembiayaanOnlineEnabled ? '' : 'no-export' },
{ targets: 0, orderable: false, width: '40px' }, { targets: 0, orderable: false, width: '40px' },
{ targets: -1, orderable: false, searchable: false, width: '70px' } { targets: -1, orderable: false, searchable: false, width: '70px' }
] ]
@@ -811,6 +784,7 @@
var xtawarruq = initErahnDatatable('#xtawarruq', { var xtawarruq = initErahnDatatable('#xtawarruq', {
columnDefs: [ columnDefs: [
{ targets: 2, visible: pembiayaanOnlineEnabled, className: pembiayaanOnlineEnabled ? '' : 'no-export' },
{ targets: 0, orderable: false, width: '40px' } { targets: 0, orderable: false, width: '40px' }
] ]
}); });
@@ -2067,7 +2041,8 @@
harini.setHours(0, 0, 0, 0); harini.setHours(0, 0, 0, 0);
if ($("input[name='options']:checked").val() == 'belum_tebus') { if ($("input[name='options']:checked").val() == 'belum_tebus') {
let test1 = $('input[name="belum"]:checked').closest('tr').children('td:eq(2)').text(); let selectedRow = $('input[name="belum"]:checked').closest('tr');
let test1 = selectedRow.length ? selectedRow.closest('table').DataTable().row(selectedRow).data()[3] : '';
let tarikhgadai2 = new Date(test1); let tarikhgadai2 = new Date(test1);
tarikhgadai2.setHours(0, 0, 0, 0); tarikhgadai2.setHours(0, 0, 0, 0);
if (+harini !== +tarikhgadai2) { if (+harini !== +tarikhgadai2) {
@@ -2484,7 +2459,8 @@
} }
if ($("input[name='options']:checked").val() == 'belum_tebus') { if ($("input[name='options']:checked").val() == 'belum_tebus') {
let test1 = $('input[name="belum"]:checked').closest('tr').children('td:eq(2)').text(); let selectedRow = $('input[name="belum"]:checked').closest('tr');
let test1 = selectedRow.length ? selectedRow.closest('table').DataTable().row(selectedRow).data()[3] : '';
let tarikhgadai2 = new Date(test1); let tarikhgadai2 = new Date(test1);
tarikhgadai2.setHours(0, 0, 0, 0); tarikhgadai2.setHours(0, 0, 0, 0);
+100
View File
@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="ms">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dasar Privasi | Kaunter arOS</title>
<link rel="icon" href="{{ asset('img/icon_pkb.png') }}">
<link href="{{ asset('css/login-v2.css') }}" rel="stylesheet">
<style>
body { background: #f4f7fb; color: #344767; }
.privacy-container { max-width: 900px; margin: 0 auto; padding: 32px 20px; }
.privacy-brand { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; }
.privacy-brand img { width: 60px; height: auto; }
.privacy-card { background: #fff; border-radius: 16px; padding: 40px; box-shadow: 0 8px 32px rgba(24, 49, 83, .08); }
.privacy-card h1 { font-size: 2rem; }
.privacy-card h2 { font-size: 1.2rem; margin-top: 28px; }
.privacy-card p, .privacy-card li { line-height: 1.8; }
.privacy-card ul { padding-left: 24px; }
.privacy-back { display: inline-block; margin-top: 24px; color: #075985; text-decoration: underline; }
a:focus-visible { outline: 3px solid #075985; outline-offset: 4px; }
@media (max-width: 576px) {
.privacy-container { padding: 20px 16px; }
.privacy-card { padding: 24px 20px; }
.privacy-card h1 { font-size: 1.7rem; }
}
</style>
</head>
<body>
<main class="privacy-container">
<div class="privacy-brand">
<img src="{{ asset('img/icon_pkb.png') }}" alt="Logo ar-Rahnu">
<span class="font-weight-bold">Kaunter arOS &middot; ar-rahn Online System</span>
</div>
<article class="privacy-card" aria-labelledby="privacy-title">
<h1 id="privacy-title">Dasar Privasi</h1>
<p class="text-sm text-muted">Versi dasar: 1.0 &middot; Tarikh kemas kini: <time datetime="2026-09-27">27 September 2026</time></p>
<h2>1. Pengenalan</h2>
<p>Selamat datang ke sistem Kaunter arOS Koperasi Permodalan Kelantan Berhad (KoPKB). Kami amat menghargai kepercayaan yang anda berikan untuk mengendalikan maklumat peribadi anda. Dasar Privasi ini digubal untuk membantu anda memahami bagaimana kami mengumpul, menggunakan, mendedahkan dan melindungi data peribadi anda bagi urusan Ar-Rahn, dengan mengambil kira keperluan Akta Perlindungan Data Peribadi 2010 (PDPA) dan amalan keselamatan maklumat yang berkaitan.</p>
<p>Dasar ini terpakai kepada kakitangan, pentadbir dan mana-mana pihak yang diberi kuasa untuk mengakses atau menggunakan sistem Kaunter arOS KoPKB, serta pengendalian data pelanggan dalam perkhidmatan yang diuruskan melalui sistem ini.</p>
<h2>2. Data yang Dikumpul</h2>
<p>Kami mengumpul dan memproses maklumat yang berkaitan dengan urusan sistem, termasuk:</p>
<ul>
<li><strong>Maklumat Pengenalan Peribadi:</strong> Nama penuh, nombor pengenalan, alamat, alamat e-mel dan nombor telefon pelanggan atau pengguna.</li>
<li><strong>Maklumat Akaun:</strong> Maklumat akaun pengguna, cawangan dan peranan akses dalam sistem.</li>
<li><strong>Data Teknikal:</strong> Maklumat seperti alamat IP, jenis pelayar dan log aktiviti sistem yang direkodkan bagi tujuan operasi dan keselamatan.</li>
<li><strong>Data Transaksi:</strong> Rekod pembiayaan, gadaian, maklumat marhun, pembayaran, penebusan, lelongan dan dokumen sokongan berkaitan.</li>
</ul>
<h2>3. Tujuan Penggunaan Data</h2>
<p>Data anda digunakan untuk tujuan berikut:</p>
<ul>
<li><strong>Operasi Sistem:</strong> Menguruskan akaun pengguna dan memproses urusan pelanggan serta transaksi Ar-Rahn.</li>
<li><strong>Pengesahan:</strong> Mengesahkan identiti dan mengawal akses pengguna mengikut peranan serta cawangan yang dibenarkan.</li>
<li><strong>Komunikasi:</strong> Menyampaikan notis berkaitan urusan, makluman pentadbiran dan sokongan pelanggan.</li>
<li><strong>Pelaporan dan Penambahbaikan:</strong> Menyediakan laporan operasi, menyokong audit serta menambah baik prestasi dan kualiti sistem.</li>
</ul>
<h2>4. Perkongsian &amp; Pendedahan Data</h2>
<p>Kami tidak menjual data peribadi anda. Akses, perkongsian atau pendedahan data dihadkan kepada keperluan urusan yang berkaitan, termasuk:</p>
<ul>
<li><strong>Kakitangan yang Diberi Kuasa:</strong> Kakitangan dan pentadbir KoPKB yang memerlukan maklumat untuk menjalankan tugas.</li>
<li><strong>Pembekal Perkhidmatan:</strong> Penyedia teknologi atau perkhidmatan yang menyokong pengehosan, penyelenggaraan sistem dan pemprosesan pembayaran, jika berkaitan.</li>
<li><strong>Pematuhan Undang-undang:</strong> Pihak berkuasa atau pihak lain apabila pendedahan diperlukan oleh undang-undang atau perintah mahkamah.</li>
</ul>
<h2>5. Keselamatan Data</h2>
<p>Langkah keselamatan teknikal dan pentadbiran digunakan untuk membantu melindungi data yang dikendalikan melalui sistem, termasuk kawalan akses mengikut peranan pengguna dan pengesahan akaun.</p>
<p>Pengguna hendaklah mengakses sistem melalui sambungan HTTPS yang selamat, menjaga kerahsiaan kata laluan, tidak berkongsi akaun dan log keluar selepas selesai menggunakan sistem. Sebarang aktiviti yang mencurigakan hendaklah dilaporkan kepada pentadbir sistem.</p>
<h2>6. Hak Pengguna</h2>
<p>Anda boleh menghubungi kami untuk mengemukakan permohonan berkaitan data peribadi anda, termasuk:</p>
<ul>
<li><strong>Akses &amp; Pembetulan:</strong> Memohon semakan dan pembetulan maklumat peribadi yang tidak tepat atau tidak terkini.</li>
<li><strong>Pemadaman:</strong> Memohon pemadaman akaun atau data dalam keadaan yang sesuai, tertakluk kepada keperluan penyimpanan rekod transaksi, audit dan undang-undang.</li>
<li><strong>Sekatan:</strong> Memohon supaya pemprosesan data tertentu dihadkan, tertakluk kepada keperluan perkhidmatan dan kewajipan yang terpakai.</li>
</ul>
<p>Permohonan akan dinilai mengikut keperluan yang terpakai. Pengesahan identiti mungkin diperlukan sebelum permohonan diproses.</p>
<h2>7. Tempoh Penyimpanan</h2>
<p>Data disimpan selama diperlukan untuk pengendalian urusan Ar-Rahn, penyelenggaraan rekod pembiayaan dan transaksi, audit serta pematuhan undang-undang. Selepas keperluan penyimpanan berakhir, data akan diuruskan untuk pemadaman atau penyahkenalpastian mengikut prosedur organisasi yang terpakai.</p>
<h2>8. Kuki &amp; Teknologi Penjejakan</h2>
<p>Sistem Kaunter arOS menggunakan kuki (cookies) dan sesi untuk menyokong log masuk, mengekalkan sesi pengguna dan melindungi permintaan dalam sistem. Anda boleh menguruskan pilihan kuki melalui tetapan pelayar. Menyekat kuki boleh menyebabkan fungsi tertentu, termasuk log masuk, tidak berfungsi dengan baik.</p>
<h2>9. Perubahan Dasar</h2>
<p>Dasar ini boleh dikemas kini dari semasa ke semasa apabila terdapat perubahan pada perkhidmatan, pengendalian sistem atau keperluan organisasi. Versi terkini akan dipaparkan pada halaman ini. Sebarang perubahan besar akan dimaklumkan melalui e-mel atau notis dalam sistem sebelum tarikh kuat kuasa baharu.</p>
<h2>10. Hubungi Kami</h2>
<p>Jika anda mempunyai pertanyaan, komen atau permohonan berkaitan dasar ini dan pengendalian data peribadi dalam sistem Kaunter arOS, sila hubungi:</p>
<p><strong>AFRINA ALIMI</strong><br>
Koperasi Permodalan Kelantan Berhad (KoPKB)<br>
E-mel: <a href="mailto:afrina@koppkb.com">afrina@koppkb.com</a>
</p>
<a class="privacy-back" href="{{ route('login') }}">Kembali ke Log Masuk</a>
</article>
</main>
</body>
</html>
+1 -30
View File
@@ -817,32 +817,7 @@
event.preventDefault(); event.preventDefault();
return false; return false;
}else{ }else{
$.ajax({ // Commodity purchase is done via BSAS on Terima (server-side).
method:'get',
url: api_url + '/api/komuditi/check',
success:function(data){
var check_komuditi = Object.keys(data).length;
if(check_komuditi == 0){
$("#terima_gadai").prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Unit Komuditi Berbaki Minumum',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komuditi'
});
event.preventDefault();
return false;
}else{
if(Number(data['unit_komuditi']) < pinjaman){
$("#terima_gadai").prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Baki Unit Komuditi Tidak Mencukupi',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komuditi'
});
event.preventDefault();
return false;
}
else{
if(pinjaman >= 10000 && pinjaman <= Number(pembiyaan_max_pelulus)){ if(pinjaman >= 10000 && pinjaman <= Number(pembiyaan_max_pelulus)){
//new added //new added
@@ -1179,10 +1154,6 @@
} }
}
}
}
});
} }
} }
+124 -46
View File
@@ -1562,6 +1562,126 @@
return true; return true;
} }
function bsasProgressHtml() {
return '' +
'<div class="text-left" id="bsas-progress-box">' +
'<p class="mb-2">Sedang memproses pembelian komoditi...</p>' +
'<ol class="mb-2 pl-3" style="list-style:none;padding-left:0;">' +
'<li id="bsas-step-1" class="mb-2 small text-muted" data-state="pending">' +
'<span class="bsas-step-icon mr-1">○</span>' +
'<strong>1.</strong> Menghantar order ke BSAS' +
'<div class="bsas-step-remark text-muted" style="margin-left:1.25rem;"></div>' +
'</li>' +
'<li id="bsas-step-2" class="mb-2 small text-muted" data-state="pending">' +
'<span class="bsas-step-icon mr-1">○</span>' +
'<strong>2.</strong> Menunggu keputusan (long poll)' +
'<div class="bsas-step-remark text-muted" style="margin-left:1.25rem;"></div>' +
'</li>' +
'<li id="bsas-step-3" class="mb-2 small text-muted" data-state="pending">' +
'<span class="bsas-step-icon mr-1">○</span>' +
'<strong>3.</strong> Mengambil e-Cert bid, OTC dan STB &amp; simpan gadai' +
'<div class="bsas-step-remark text-muted" style="margin-left:1.25rem;"></div>' +
'</li>' +
'</ol>' +
'<p class="mb-0 small text-muted">Sila tunggu, proses ini boleh mengambil masa 10–30 saat.</p>' +
'</div>';
}
function setBsasStep(step, state, remark) {
var $el = $('#bsas-step-' + step);
if (!$el.length) {
return;
}
var icon = '○';
var color = 'text-muted';
if (state === 'active') {
icon = '●';
color = 'text-primary font-weight-bold';
} else if (state === 'done') {
icon = '✓';
color = 'text-success';
} else if (state === 'error') {
icon = '✕';
color = 'text-danger';
}
$el.attr('data-state', state)
.removeClass('text-muted text-primary text-success text-danger font-weight-bold')
.addClass(color);
$el.find('.bsas-step-icon').text(icon);
$el.find('.bsas-step-remark').text(remark || '');
}
function delayMs(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
async function submitGadaiWithBsasProgress(func) {
$("#terima_gadai").prop('disabled', true);
Swal.fire({
title: 'Pembelian Komoditi BSAS',
html: bsasProgressHtml(),
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
didOpen: function () {
Swal.showLoading();
}
});
try {
setBsasStep(1, 'active', 'Menyediakan & menghantar order...');
await delayMs(1200);
setBsasStep(1, 'done', 'Order dihantar.');
setBsasStep(2, 'active', 'Menunggu padanan BSAS...');
await delayMs(800);
var response = await $.ajax({
url: $(func).attr('action'),
method: 'POST',
data: $(func).serialize(),
dataType: 'json',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
timeout: 90000
});
setBsasStep(2, 'done', 'Keputusan diterima.');
setBsasStep(3, 'active', 'Mengambil e-Cert bid, OTC dan STB & menyimpan gadai...');
await delayMs(1000);
setBsasStep(3, 'done', 'Selesai.');
await delayMs(600);
window.location.href = (response && response.redirect)
? response.redirect
: "{{ route('customer_info') }}";
} catch (err) {
var msg = 'Gagal pembelian komoditi BSAS. Sila cuba lagi.';
if (err && err.responseJSON && err.responseJSON.message) {
msg = err.responseJSON.message;
} else if (err && err.statusText === 'timeout') {
msg = 'Proses BSAS tamat masa. Sila semak rekod BSAS / cuba semula.';
}
var activeStep = $('#bsas-progress-box li[data-state="active"]').attr('id');
var stepNum = activeStep ? parseInt(activeStep.replace('bsas-step-', ''), 10) : 2;
setBsasStep(stepNum, 'error', msg);
Swal.fire({
icon: 'error',
title: 'Tidak Berjaya',
text: msg
}).then(function () {
$("#terima_gadai").prop('disabled', false);
});
}
}
async function verifyGadai(func){ async function verifyGadai(func){
$("#terima_gadai").prop('disabled', true); $("#terima_gadai").prop('disabled', true);
if (typeof event !== 'undefined' && event && event.preventDefault) { if (typeof event !== 'undefined' && event && event.preventDefault) {
@@ -1653,32 +1773,7 @@
event.preventDefault(); event.preventDefault();
return false; return false;
}else{ }else{
$.ajax({ // Commodity purchase is done via BSAS on Terima (server-side).
method:'get',
url: api_url + '/api/komuditi/check',
success:function(data){
var check_komuditi = Object.keys(data).length;
if(check_komuditi == 0){
$("#terima_gadai").prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Unit Komuditi Berbaki Minumum',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komuditi'
});
event.preventDefault();
return false;
}else{
if(Number(data['unit_komuditi']) < pinjaman){
$("#terima_gadai").prop('disabled', false);
Swal.fire({
icon: 'error',
title: 'Baki Unit Komuditi Tidak Mencukupi',
text: 'Sila Hubungi Operasi Untuk Membuat Pembelian Komuditi'
});
event.preventDefault();
return false;
}
else{
if(pinjaman >= 10000 && pinjaman <= Number(pembiyaan_max_pelulus)){ if(pinjaman >= 10000 && pinjaman <= Number(pembiyaan_max_pelulus)){
//new added //new added
$.ajax({ $.ajax({
@@ -1785,9 +1880,7 @@
title: 'Telah Disahkan', title: 'Telah Disahkan',
text: `Komen : ${data.komen}` text: `Komen : ${data.komen}`
}).then(function(){ }).then(function(){
submitGadaiWithBsasProgress(func);
$(func).attr("onSubmit","return true;");
$(func).submit();
return true; return true;
}); });
@@ -1933,12 +2026,8 @@
icon: 'success', icon: 'success',
title: 'Telah Disahkan' title: 'Telah Disahkan'
}).then(function(){ }).then(function(){
submitGadaiWithBsasProgress(func);
$(func).attr("onSubmit","return true;");
$(func).submit();
return true; return true;
}); });
} }
else if(data.kelulusan == 'batal') else if(data.kelulusan == 'batal')
@@ -1999,14 +2088,7 @@
return false; return false;
} else { } else {
//insert code asal //insert code asal
Swal.fire({ submitGadaiWithBsasProgress(func);
icon: 'success',
title: 'Berjaya!',
text: 'Pembiayaan Berjaya Disimpan'
});
$(func).attr("onSubmit","return true;");
$(func).submit();
return true; return true;
//end insert code asal //end insert code asal
} }
@@ -2018,10 +2100,6 @@
} }
}
}
}
});
} }
} }
@@ -0,0 +1,168 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<div class="card">
<nav aria-label="breadcrumb">
<ol class="breadcrumb pb-3">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Komoditi BSAS</li>
</ol>
</nav>
<div class="card-header text-center">{{ __('Rekod Komoditi BSAS') }}</div>
<div class="card-body text-center">
<div class="row justify-content-between mb-4">
<div class="col text-left">
<div class="form-group d-flex align-items-center">
<label for="limit_view_bsas" style="width: 600px" class="mb-0">Had Paparan Rekod BSAS</label>
<select onchange="loadBsasRecord()" name="limit_view_bsas" id="limit_view_bsas" class="form-control">
<option selected value="20">20</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
</div>
<div class="col text-right">
<button type="button" class="btn btn-primary" id="refresh_bsas" title="Muat Semula">
<i class="fas fa-sync-alt" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="spinner-border" role="status" id="loading-spinner-bsas" style="display:none;">
<span class="sr-only">Loading...</span>
</div>
<x-datatable
id="rekod_bsas"
filename="Rekod_Komoditi_BSAS"
class="text-center"
:columns="['Bil', 'Tarikh', 'UUID', 'No Rujukan', 'Cawangan', 'Pembeli', 'Bid Value', 'Murabaha', 'eCert No', 'Status', 'Oleh', 'Tindakan']"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2
});
let api = "{{ api_url_for_browser() }}";
let kodcaw = "{{ Auth::user()->cawangan }}";
var tablebsas = initErahnDatatable('#rekod_bsas', {
pageLength: 1000,
bLengthChange: false,
deferRender: true,
order: [[1, 'desc']],
});
$('#refresh_bsas').on('click', function () {
loadBsasRecord();
});
$(function () {
loadBsasRecord();
});
function bsasStatusBadge(status) {
var label = status || '-';
var cls = 'secondary';
if (status === 'confirmed') cls = 'success';
else if (status === 'failed') cls = 'danger';
else if (status === 'submitted' || status === 'pending') cls = 'warning';
return '<span class="badge badge-' + cls + '">' + label + '</span>';
}
function formatBsasDateTime(value) {
if (!value) {
return '-';
}
var date = new Date(value);
if (isNaN(date.getTime())) {
return value;
}
var pad = function (n) { return String(n).padStart(2, '0'); };
return date.getFullYear() + '-' +
pad(date.getMonth() + 1) + '-' +
pad(date.getDate()) + ' ' +
pad(date.getHours()) + ':' +
pad(date.getMinutes()) + ':' +
pad(date.getSeconds());
}
function bsasEcertAction(row) {
var ecertNo = row['e_cert_no'] || '';
var hasEcert = !!(row['has_bid_ecert'] || row['has_otc_ecert'] || row['has_stb_ecert']);
var uuid = row['uuid'] || '';
if (!hasEcert || !uuid) {
return '<span class="text-muted">-</span>';
}
var url = "{{ url('/khazanah/komoditi/bsas/ecert') }}/" + encodeURIComponent(uuid);
return '<a class="btn btn-sm btn-outline-primary" href="' + url + '" target="_blank" title="Lihat eCert ' + ecertNo + '">' +
'<i class="fa fa-print" aria-hidden="true"></i> eCert' +
'</a>';
}
function loadBsasRecord() {
var selectedLimitView = $('#limit_view_bsas').find(':selected').val();
$('#loading-spinner-bsas').show();
tablebsas.clear().draw();
$.ajax({
method: 'get',
url: api + '/api/komuditi/bsas/orders',
data: {
'limit_view': selectedLimitView,
'kodcaw': kodcaw,
},
success: function (rekod) {
var bilcount = 0;
$.each(rekod, function (index, row) {
bilcount++;
var ecertNo = row['e_cert_no'] || '-';
if ((row['has_bid_ecert'] || row['has_otc_ecert'] || row['has_stb_ecert']) && row['uuid']) {
ecertNo = '<a href="{{ url('/khazanah/komoditi/bsas/ecert') }}/' + encodeURIComponent(row['uuid']) + '" target="_blank">' +
(row['e_cert_no'] || 'eCert') +
'</a>';
}
tablebsas.row.add([
bilcount,
formatBsasDateTime(row['created_at']),
row['uuid'] || '-',
row['norujukan'] || '-',
row['kodcaw'] || '-',
row['otc_counter_party'] || '-',
formatter.format(Number(row['bid_value'] || 0)),
formatter.format(Number(row['otc_murabaha_value'] || 0)),
ecertNo,
bsasStatusBadge(row['status']),
row['created_by'] || '-',
bsasEcertAction(row),
]).draw(false);
});
tablebsas.draw();
$('#loading-spinner-bsas').hide();
},
error: function () {
$('#loading-spinner-bsas').hide();
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Gagal memuatkan rekod BSAS'
});
}
});
}
</script>
@endsection
@@ -282,6 +282,10 @@
<a href="{{ route('khazanah.indexwakil') }}" <a href="{{ route('khazanah.indexwakil') }}"
class="btn btn-info btn-lg btn-block mb-3">Surat Wakil</a> class="btn btn-info btn-lg btn-block mb-3">Surat Wakil</a>
</div> </div>
<div class="col-4 px-2">
<a href="{{ route('khazanah.bsas') }}"
class="btn btn-info btn-lg btn-block mb-3">Komoditi BSAS</a>
</div>
</div> </div>
</div> </div>
+97 -81
View File
@@ -25,6 +25,18 @@
</ol> </ol>
</nav> </nav>
@if(!empty($customer_info['photo_base64']))
<div class="form-group row col-md-12">
<div class="col-md-12 d-flex justify-content-center pb-3">
<img src="data:{{ $customer_info['photo_mime'] ?? 'image/jpeg' }};base64,{{ $customer_info['photo_base64'] }}"
alt="Gambar MyKad"
width="120"
height="160"
style="object-fit: cover;">
</div>
</div>
@endif
<div class="form-group row col-md-12"> <div class="form-group row col-md-12">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group row"> <div class="form-group row">
@@ -154,7 +166,7 @@
<nav class="mb-3"> <nav class="mb-3">
<div class="nav nav-tabs" id="nav-tab" role="tablist"> <div class="nav nav-tabs" id="nav-tab" role="tablist">
@php @php
$activate_tab = request()->session()->pull('activate_tab', 'SAG_lama'); $activate_tab = request()->session()->pull('activate_tab', 'SAG_baru');
$activate_tab_SAG_baru = $activate_tab === 'SAG_baru' ? 'active' : ''; $activate_tab_SAG_baru = $activate_tab === 'SAG_baru' ? 'active' : '';
$aria_selected_SAG_baru = $activate_tab === 'SAG_baru' ? 'true' : 'false'; $aria_selected_SAG_baru = $activate_tab === 'SAG_baru' ? 'true' : 'false';
@@ -171,77 +183,36 @@
</nav> </nav>
<div class="tab-content" id="navtawarruqdannottawarruq"> <div class="tab-content" id="navtawarruqdannottawarruq">
<div class="tab-pane fade {{ $activate_pane_SAG_baru }}" id="nav-tawarruq" role="tabpanel" aria-labelledby="nav-tawarruq-tab"> <div class="tab-pane fade {{ $activate_pane_SAG_baru }}" id="nav-tawarruq" role="tabpanel" aria-labelledby="nav-tawarruq-tab">
<table class="table table-striped table-hover" id="myTable" style="width: 100%"> <x-datatable
<thead class="thead-dark"> id="myTable"
<tr> filename="SAG_Baru"
<th>Bil</th> :columns="['Bil', 'No Rujukan', 'Tarikh Pembiayaan', 'Tarikh Matang', 'Baki Harga Jualan', 'Baki Pembiayaan', 'Margin', 'Tempoh', 'Keuntungan', 'Lelong', 'Pembayaran Terakhir', 'Tarikh Ansuran Terkini']"
<th>No Rujukan</th> />
<th>Tarikh Pembiayaan</th>
<th>Tarikh Matang</th>
<th>Baki Harga Jualan</th>
<th>Baki Pembiayaan</th>
<th>Margin</th>
<th>Tempoh</th>
<th>Keuntungan</th>
<th>Lelong</th>
<th>Pembayaran Terakhir</th>
</tr>
</thead>
</table>
</div> </div>
<div class="tab-pane fade {{ $activate_pane_SAG_lama }}" id="nav-xtawarruq" role="tabpanel" aria-labelledby="nav-xtawarruq-tab"> <div class="tab-pane fade {{ $activate_pane_SAG_lama }}" id="nav-xtawarruq" role="tabpanel" aria-labelledby="nav-xtawarruq-tab">
<table id="xtawarruq" class="table table-striped table-hover" style="width:100%"> <x-datatable
<thead class="thead-dark"> id="xtawarruq"
<tr> filename="SAG_Lama"
<th>Bil</th> :columns="['Bil', 'No Rujukan', 'Tarikh Pembiayaan', 'Tarikh Matang', 'Baki Harga Jualan', 'Baki Pembiayaan', 'Margin', 'Tempoh', 'Keuntungan', 'Lelong', 'Pembayaran Terakhir', 'Tarikh Ansuran Terkini']"
<th>No Rujukan</th> />
<th>Tarikh Pembiayaan</th>
<th>Tarikh Matang</th>
<th>Baki Harga Jualan</th>
<th>Baki Pembiayaan</th>
<th>Margin</th>
<th>Tempoh</th>
<th>Keuntungan</th>
<th>Lelong</th>
<th>Pembayaran Terakhir</th>
</tr>
</thead>
</table>
</div> </div>
</div> </div>
{{-- Sudah Tebus --}} {{-- Sudah Tebus --}}
<table id="dahtebusTable" class="table table-striped table-hover"> <x-datatable
<thead class="thead-dark"> id="dahtebusTable"
<tr> filename="SAG_Sudah_Tebus"
<th>Bil</th> :columns="['Bil', 'No Rujukan', 'Tarikh Penyelesaian Pembiayaan', 'Baki Pembiayaan', 'Margin', 'Tempoh']"
<th>No Rujukan</th> />
<th>Tarikh Penyelesaian Pembiayaan</th>
<th>Baki Pembiayaan</th>
<th>Margin</th>
<th>Tempoh</th>
</tr>
</thead>
</table>
{{-- Lelong --}} {{-- Lelong --}}
<table id="lelongTable" class="table table-striped table-hover" style="width:100%"> <x-datatable
<thead class="thead-dark"> id="lelongTable"
<tr> filename="SAG_Lelong"
<th>Bil</th> :columns="['Bil', 'No Rujukan', 'Tarikh Lelong', 'Tarikh Tuntut', 'Pembiayaan', 'Harga Jual', 'Jum Tuntut/Bayar', 'Keuntungan', 'Caj Lelong']"
<th>No Rujukan</th> />
<th>Tarikh Lelong</th>
<th>Tarikh Tuntut</th>
<th>Pembiayaan</th>
<th>Harga Jual</th>
<th>Jum Tuntut/Bayar</th>
<th>Keuntungan</th>
<th>Caj Lelong</th>
</tr>
</thead>
</table>
<div class="text-center"> <div class="text-center">
<button class="btn btn-primary" onclick="histfunc()">Rekod Pembiayaan</button> <button class="btn btn-primary" onclick="histfunc()">Rekod Pembiayaan</button>
@@ -502,21 +473,15 @@
var tablegadai = $('#myTable').DataTable({ var tablegadai = initErahnDatatable('#myTable', {
"scrollX": true, order: [[1, 'desc']]
order: [[1,'desc']]
}); });
var xtawarruq = $('#xtawarruq').DataTable({ var xtawarruq = initErahnDatatable('#xtawarruq');
"scrollX": true,
});
var tabledahtebus = $('#dahtebusTable').DataTable({ var tabledahtebus = initErahnDatatable('#dahtebusTable');
});
var tablelelong = $('#lelongTable').DataTable({ var tablelelong = initErahnDatatable('#lelongTable');
"scrollX": true
});
var historybayarandatatable = $('#tablehistorybayaran').DataTable({ var historybayarandatatable = $('#tablehistorybayaran').DataTable({
"ordering": false "ordering": false
@@ -634,6 +599,45 @@
}; };
} }
function tarikhAnsuranTerkini(transactions)
{
if (!Array.isArray(transactions)) {
throw new Error('Invalid transaction response');
}
return transactions.reduce(function(latest, transaction) {
if (transaction.trans_type !== 'A') return latest;
var date = String(transaction.created_at || '').slice(0, 10);
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return latest;
return date > latest ? date : latest;
}, '') || 'Tiada bayaran';
}
function muatTarikhAnsuranTerkini(row, norujukan)
{
var originalData = row.data();
function update(value) {
// Ignore responses for rows removed when the customer/table changes.
if (row.data() !== originalData) return;
var updatedData = originalData.slice();
updatedData[11] = value;
row.data(updatedData).draw(false);
}
$.ajax({
method: 'get',
url: api_url + '/api/transaction/' + encodeURIComponent(norujukan),
success: function(transactions) {
try {
update(tarikhAnsuranTerkini(transactions));
} catch (error) {
update('Gagal dimuatkan');
}
},
error: function() {
update('Gagal dimuatkan');
}
});
}
function gadaiTable(data,jenistebus){ function gadaiTable(data,jenistebus){
var counter = 1; var counter = 1;
@@ -693,8 +697,8 @@
let table_norujukan = gadai_detail["norujukan"]; let table_norujukan = gadai_detail["norujukan"];
// let radio_action = "onclick=checkCanChangeGadaianDetail(" + "'" + table_norujukan + "'" + ")"; // let radio_action = "onclick=checkCanChangeGadaianDetail(" + "'" + table_norujukan + "'" + ")";
if(gadai_detail["percentpinj"] != 0) if(gadai_detail["percentpinj"] != 0) {
xtawarruq.row.add([ var ansuranRow = xtawarruq.row.add([
'<input type="radio" value='+ gadai_detail["norujukan"] + ' name="belum"/>', '<input type="radio" value='+ gadai_detail["norujukan"] + ' name="belum"/>',
// '<a href="#" onclick="checkserah(this)" data-pinjaman='+ gadai_detail['pinjaman'] +' data-hargajualan='+ gadai_detail['hargajualan'] +' data-norujukan='+ gadai_detail['norujukan'] +' data-url=' + urlroute +'>' + gadai_detail['norujukan'] + '</a>', // '<a href="#" onclick="checkserah(this)" data-pinjaman='+ gadai_detail['pinjaman'] +' data-hargajualan='+ gadai_detail['hargajualan'] +' data-norujukan='+ gadai_detail['norujukan'] +' data-url=' + urlroute +'>' + gadai_detail['norujukan'] + '</a>',
(function() { (function() {
@@ -724,8 +728,11 @@
let color = bayaranInfo.blocked ? 'red' : 'green'; let color = bayaranInfo.blocked ? 'red' : 'green';
return '<div style="white-space: nowrap; color: ' + color + '; font-weight: bold;">' + return '<div style="white-space: nowrap; color: ' + color + '; font-weight: bold;">' +
bayaranInfo.tarikh + '</div>'; bayaranInfo.tarikh + '</div>';
})() })(),
'Memuatkan...'
]).draw(); ]).draw();
muatTarikhAnsuranTerkini(ansuranRow, gadai_detail['norujukan']);
}
}else{ }else{
var taglel = ''; var taglel = '';
if(gadai_detail['taglel'] == 1){ if(gadai_detail['taglel'] == 1){
@@ -757,7 +764,7 @@
let table_norujukan = gadai_detail["norujukan"]; let table_norujukan = gadai_detail["norujukan"];
// let radio_action = "onclick=checkCanChangeGadaianDetail(" + "'" + table_norujukan + "'" + ")"; // let radio_action = "onclick=checkCanChangeGadaianDetail(" + "'" + table_norujukan + "'" + ")";
tablegadai.row.add([ var ansuranRow = tablegadai.row.add([
'<input type="radio" value='+ gadai_detail["norujukan"] + ' name="belum"/>', '<input type="radio" value='+ gadai_detail["norujukan"] + ' name="belum"/>',
// '<a href="#" onclick="checkserah(this)" data-pinjaman='+ gadai_detail['pinjaman'] +' data-hargajualan='+ gadai_detail['hargajualan'] +' data-norujukan='+ gadai_detail['norujukan'] +' data-url=' + urlroute +'>' + gadai_detail['norujukan'] + '</a>', // '<a href="#" onclick="checkserah(this)" data-pinjaman='+ gadai_detail['pinjaman'] +' data-hargajualan='+ gadai_detail['hargajualan'] +' data-norujukan='+ gadai_detail['norujukan'] +' data-url=' + urlroute +'>' + gadai_detail['norujukan'] + '</a>',
(function() { (function() {
@@ -787,8 +794,10 @@
let color = bayaranInfo.blocked ? 'red' : 'green'; let color = bayaranInfo.blocked ? 'red' : 'green';
return '<div style="white-space: nowrap; color: ' + color + '; font-weight: bold;">' + return '<div style="white-space: nowrap; color: ' + color + '; font-weight: bold;">' +
bayaranInfo.tarikh + '</div>'; bayaranInfo.tarikh + '</div>';
})() })(),
'Memuatkan...'
]).draw(); ]).draw();
muatTarikhAnsuranTerkini(ansuranRow, gadai_detail['norujukan']);
} }
}else if(jenistebus == "sudah_tebus"){ }else if(jenistebus == "sudah_tebus"){
@@ -914,6 +923,9 @@
success: function(data){ success: function(data){
$('#myTable').parents('div.dataTables_wrapper').first().show(); $('#myTable').parents('div.dataTables_wrapper').first().show();
gadaiTable(data,"belum_tebus"); gadaiTable(data,"belum_tebus");
if (typeof adjustErahnDatatables === 'function') {
adjustErahnDatatables();
}
} }
}); });
@@ -1447,6 +1459,9 @@
url: api_url + "/api/customers/"+noic+"/gadai/belum_tebus/"+kodcaw, url: api_url + "/api/customers/"+noic+"/gadai/belum_tebus/"+kodcaw,
success: function(data){ success: function(data){
gadaiTable(data,"belum_tebus"); gadaiTable(data,"belum_tebus");
if (typeof adjustErahnDatatables === 'function') {
adjustErahnDatatables();
}
} }
}); });
} }
@@ -1695,9 +1710,10 @@
}); });
document.addEventListener('DOMContentLoaded', function(){ document.addEventListener('DOMContentLoaded', function(){
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e){ $('a[data-toggle="tab"]').on('shown.bs.tab', function(){
$($.fn.dataTable.tables(true)).DataTable() if (typeof adjustErahnDatatables === 'function') {
.columns.adjust(); adjustErahnDatatables();
}
}); });
}); });
</script> </script>
+200 -38
View File
@@ -73,43 +73,81 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header">{{ __('Rekod Pembelian/Penjualan Komoditi') }}</div> <div class="card-header">{{ __('Rekod Komoditi') }}</div>
<div class="card-body text-center"> <div class="card-body text-center">
<div class="row justify-content-between mb-4"> <ul class="nav nav-tabs mb-3" id="komoditi-rekod-tab" role="tablist">
<div class="col text-left"> <li class="nav-item">
<div class="form-group d-flex align-items-center"> <a class="nav-link active" id="tab-manual-link" data-toggle="tab" href="#tab-manual" role="tab" aria-controls="tab-manual" aria-selected="true">Beli / Jual Manual</a>
<label for="limit-komoditi" style="width: 600px" class="mb-0">Had Paparan Rekod Komoditi</label> </li>
<select onchange="loadKomoditiRecord()" name="limit_view" id="limit_view" class="form-control"> <li class="nav-item">
<option selected value="20">20</option> <a class="nav-link" id="tab-bsas-link" data-toggle="tab" href="#tab-bsas" role="tab" aria-controls="tab-bsas" aria-selected="false">Pembelian BSAS</a>
<option value="100">100</option> </li>
<option value="500">500</option> </ul>
<option value="1000">1000</option> <div class="tab-content" id="komoditi-rekod-tabContent">
<option disabled>Untuk rekod keseluruhan, sila print</option> <div class="tab-pane fade show active" id="tab-manual" role="tabpanel" aria-labelledby="tab-manual-link">
</select> <div class="row justify-content-between mb-4">
</div> <div class="col text-left">
<div class="form-group d-flex align-items-center">
<label for="limit_view" style="width: 600px" class="mb-0">Had Paparan Rekod Komoditi</label>
<select onchange="loadKomoditiRecord()" name="limit_view" id="limit_view" class="form-control">
<option selected value="20">20</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
<option disabled>Untuk rekod keseluruhan, sila print</option>
</select>
</div>
</div>
<div class="col text-right">
<a href="{{ route('laporan.komoditi') }}" target="_blank" class="btn btn-primary" title="Cetak Rekod Jual/Beli Komoditi"><i class="fa fa-print" aria-hidden="true"></i></a>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="spinner-border" role="status" id="loading-spinner">
<span class="sr-only">Loading...</span>
</div>
<x-datatable
id="rekod_komuditi"
filename="Rekod_Komoditi_Manual"
class="text-center"
:columns="['Bil', 'No Sijil', 'Tarikh', 'Masa', 'Nilai Komoditi', 'Transaksi Oleh', 'Transaksi']"
/>
</div>
</div>
</div> </div>
<div class="col text-right"> <div class="tab-pane fade" id="tab-bsas" role="tabpanel" aria-labelledby="tab-bsas-link">
<a href="{{ route('laporan.komoditi') }}" target="_blank" class="btn btn-primary" title="Cetak Rekod Jual/Beli Komoditi"><i class="fa fa-print" aria-hidden="true"></i></a> <div class="row justify-content-between mb-4">
</div> <div class="col text-left">
</div> <div class="form-group d-flex align-items-center">
<div class="row"> <label for="limit_view_bsas" style="width: 600px" class="mb-0">Had Paparan Rekod BSAS</label>
<div class="col-md-12"> <select onchange="loadBsasRecord()" name="limit_view_bsas" id="limit_view_bsas" class="form-control">
<div class="spinner-border" role="status" id="loading-spinner"> <option selected value="20">20</option>
<span class="sr-only">Loading...</span> <option value="100">100</option>
</div> <option value="500">500</option>
<table id="rekod_komuditi" class="table table-striped table-hover"> <option value="1000">1000</option>
<thead class="thead-dark"> </select>
<tr> </div>
<th>Bil</th> </div>
<th>No Sijil</th> <div class="col text-right">
<th>Tarikh</th> <button type="button" class="btn btn-primary" id="refresh_bsas" title="Muat Semula">
<th>Masa</th> <i class="fas fa-sync-alt" aria-hidden="true"></i>
<th>Nilai Komoditi</th> </button>
<th>Transaksi Oleh</th> </div>
<th>Transaksi</th> </div>
</tr> <div class="row">
</thead> <div class="col-md-12">
</table> <div class="spinner-border" role="status" id="loading-spinner-bsas" style="display:none;">
<span class="sr-only">Loading...</span>
</div>
<x-datatable
id="rekod_bsas"
filename="Rekod_Komoditi_BSAS"
class="text-center"
:columns="['Bil', 'Tarikh', 'UUID', 'No Rujukan', 'Cawangan', 'Pembeli', 'Bid Value', 'Murabaha', 'eCert No', 'Status', 'Oleh', 'Tindakan']"
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -239,15 +277,44 @@
<script> <script>
let api_url = "{{ api_url_for_browser() }}"; let api_url = "{{ api_url_for_browser() }}";
var tablerekod = $('#rekod_komuditi').DataTable( { var tablerekod = initErahnDatatable('#rekod_komuditi', {
"pageLength": 1000, pageLength: 1000,
"bLengthChange": false, bLengthChange: false,
deferRender: true,
order: [[2, 'desc']],
}); });
var tablebsas = initErahnDatatable('#rekod_bsas', {
pageLength: 1000,
bLengthChange: false,
deferRender: true,
order: [[1, 'desc']],
});
var bsasLoaded = false;
$(document).ready( function () { $(document).ready( function () {
checkKomuditi(); checkKomuditi();
}); });
$('#komoditi-rekod-tab a[data-toggle="tab"]').on('shown.bs.tab', function () {
if (typeof adjustErahnDatatables === 'function') {
adjustErahnDatatables();
}
});
$('#tab-bsas-link').on('shown.bs.tab', function () {
if (!bsasLoaded) {
loadBsasRecord();
} else if (typeof adjustErahnDatatables === 'function') {
adjustErahnDatatables();
}
});
$('#refresh_bsas').on('click', function () {
loadBsasRecord();
});
const formatter = new Intl.NumberFormat('en-US', { const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2 minimumFractionDigits: 2
}); });
@@ -395,6 +462,101 @@
}) })
} }
function bsasStatusBadge(status) {
var label = status || '-';
var cls = 'secondary';
if (status === 'confirmed') cls = 'success';
else if (status === 'failed') cls = 'danger';
else if (status === 'submitted' || status === 'pending') cls = 'warning';
return '<span class="badge badge-' + cls + '">' + label + '</span>';
}
function formatBsasDateTime(value) {
if (!value) {
return '-';
}
var date = new Date(value);
if (isNaN(date.getTime())) {
return value;
}
var pad = function (n) { return String(n).padStart(2, '0'); };
return date.getFullYear() + '-' +
pad(date.getMonth() + 1) + '-' +
pad(date.getDate()) + ' ' +
pad(date.getHours()) + ':' +
pad(date.getMinutes()) + ':' +
pad(date.getSeconds());
}
function bsasEcertAction(row) {
var ecertNo = row['e_cert_no'] || '';
var hasEcert = !!(row['has_bid_ecert'] || row['has_otc_ecert'] || row['has_stb_ecert']);
var uuid = row['uuid'] || '';
if (!hasEcert || !uuid) {
return '<span class="text-muted">-</span>';
}
var url = "{{ url('/komoditi/bsas/ecert') }}/" + encodeURIComponent(uuid);
return '<a class="btn btn-sm btn-outline-primary" href="' + url + '" target="_blank" title="Lihat eCert ' + ecertNo + '">' +
'<i class="fa fa-print" aria-hidden="true"></i> eCert' +
'</a>';
}
function loadBsasRecord()
{
var selectedLimitView = $('#limit_view_bsas').find(':selected').val();
$('#loading-spinner-bsas').show();
tablebsas.clear().draw();
$.ajax({
method: 'get',
url: api_url + '/api/komuditi/bsas/orders',
data: {
'limit_view': selectedLimitView,
},
success: function (rekod) {
var bilcount = 0;
$.each(rekod, function (index, row) {
bilcount++;
var ecertNo = row['e_cert_no'] || '-';
if ((row['has_bid_ecert'] || row['has_otc_ecert'] || row['has_stb_ecert']) && row['uuid']) {
ecertNo = '<a href="{{ url('/komoditi/bsas/ecert') }}/' + encodeURIComponent(row['uuid']) + '" target="_blank">' +
(row['e_cert_no'] || 'eCert') +
'</a>';
}
tablebsas.row.add([
bilcount,
formatBsasDateTime(row['created_at']),
row['uuid'] || '-',
row['norujukan'] || '-',
row['kodcaw'] || '-',
row['otc_counter_party'] || '-',
formatter.format(Number(row['bid_value'] || 0)),
formatter.format(Number(row['otc_murabaha_value'] || 0)),
ecertNo,
bsasStatusBadge(row['status']),
row['created_by'] || '-',
bsasEcertAction(row),
]).draw(false);
});
tablebsas.draw();
bsasLoaded = true;
$('#loading-spinner-bsas').hide();
},
error: function () {
bsasLoaded = false;
$('#loading-spinner-bsas').hide();
Swal.fire({
icon: 'error',
title: 'Amaran!',
text: 'Gagal memuatkan rekod BSAS'
});
}
});
}
$('#detail_komuditi').on('show.bs.modal', function(event) { $('#detail_komuditi').on('show.bs.modal', function(event) {
var button = $(event.relatedTarget); var button = $(event.relatedTarget);
var kodproduk = button.data('kodproduk'); var kodproduk = button.data('kodproduk');
@@ -47,6 +47,7 @@
</div> </div>
</div> </div>
<script> <script>
var api_url = '{{ api_url_for_browser() }}';
var table_margin = $('#table-margin').DataTable(); var table_margin = $('#table-margin').DataTable();
$.ajax({ $.ajax({
@@ -74,7 +74,7 @@
id="batch_lines" id="batch_lines"
class="table-sm text-center" class="table-sm text-center"
:filename="'Batch_' . ($batch['batch_no'] ?? $batch['id'])" :filename="'Batch_' . ($batch['batch_no'] ?? $batch['id'])"
:columns="['Bil', 'Status', 'SAG', 'Pelanggan', 'No KP', 'Bank', 'No Akaun', 'Nama Akaun', 'Pinjaman (RM)', 'Rujukan Bayaran', 'Dokumen', 'Tindakan']" :columns="['Bil', 'Status', 'SAG', 'Pelanggan', 'No KP', 'Bank', 'No Akaun', 'Pinjaman (RM)', 'Rujukan Bayaran', 'Dokumen', 'Tindakan']"
:skipExport="['Dokumen', 'Tindakan']" :skipExport="['Dokumen', 'Tindakan']"
> >
@foreach($lines as $line) @foreach($lines as $line)
@@ -94,7 +94,6 @@
<td>{{ $line['nokp'] ?? '-' }}</td> <td>{{ $line['nokp'] ?? '-' }}</td>
<td>{{ $line['nama_bank'] ?? $line['kodbank'] ?? '-' }}</td> <td>{{ $line['nama_bank'] ?? $line['kodbank'] ?? '-' }}</td>
<td>{{ $line['noakaun'] ?? '-' }}</td> <td>{{ $line['noakaun'] ?? '-' }}</td>
<td class="text-left">{{ $line['nama_akaun'] ?? '-' }}</td>
<td class="text-right">{{ number_format((float)($line['pinjaman'] ?? 0), 2) }}</td> <td class="text-right">{{ number_format((float)($line['pinjaman'] ?? 0), 2) }}</td>
<td>{{ $line['no_rujukan_bayaran'] ?? '-' }}</td> <td>{{ $line['no_rujukan_bayaran'] ?? '-' }}</td>
<td class="text-nowrap"> <td class="text-nowrap">
@@ -113,6 +112,13 @@
@else @else
<span class="text-muted small">Tiada slip</span> <span class="text-muted small">Tiada slip</span>
@endif @endif
@if(!empty($line['borang_file_id']))
<a class="btn btn-sm btn-outline-primary"
href="{{ route('pembiayaan.online.operasi.borang', ['kodcaw' => $kodcaw, 'id' => $line['id']]) }}"
target="_blank">
Borang Pengesahan
</a>
@endif
</td> </td>
<td class="text-nowrap"> <td class="text-nowrap">
@if(in_array($st, ['IN_BATCH', 'OPS_REVIEW'], true)) @if(in_array($st, ['IN_BATCH', 'OPS_REVIEW'], true))
@@ -70,6 +70,13 @@
@else @else
<span class="text-muted small">Tiada slip</span> <span class="text-muted small">Tiada slip</span>
@endif @endif
@if(!empty($line['borang_file_id']))
<a class="btn btn-sm btn-outline-primary"
href="{{ route('pembiayaan.online.pc.borang', ['id' => $line['id']]) }}"
target="_blank">
Borang Pengesahan
</a>
@endif
</td> </td>
</tr> </tr>
@endforeach @endforeach
@@ -274,11 +274,11 @@
data-nokp="{{ $line['nokp'] ?? '' }}" data-nokp="{{ $line['nokp'] ?? '' }}"
data-namabank="{{ $line['nama_bank'] ?? $line['kodbank'] ?? '' }}" data-namabank="{{ $line['nama_bank'] ?? $line['kodbank'] ?? '' }}"
data-noakaun="{{ $line['noakaun'] ?? '' }}" data-noakaun="{{ $line['noakaun'] ?? '' }}"
data-namaakaun="{{ $line['nama_akaun'] ?? '' }}"
data-pinjaman="{{ number_format((float)($line['pinjaman'] ?? 0), 2) }}" data-pinjaman="{{ number_format((float)($line['pinjaman'] ?? 0), 2) }}"
data-nota="{{ $line['nota'] ?? '' }}" data-nota="{{ $line['nota'] ?? '' }}"
data-sebab="{{ $line['reject_reason'] ?? '' }}" data-sebab="{{ $line['reject_reason'] ?? '' }}"
data-bukti-akaun="{{ $line['bukti_akaun_file_id'] ?? '' }}"> data-bukti-akaun="{{ $line['bukti_akaun_file_id'] ?? '' }}"
data-borang-file="{{ $line['borang_file_id'] ?? '' }}">
Semak Semak
</button> </button>
@endif @endif
@@ -286,11 +286,11 @@
data-id="{{ $line['id'] }}" data-id="{{ $line['id'] }}"
data-kodbank="{{ $line['kodbank'] ?? '' }}" data-kodbank="{{ $line['kodbank'] ?? '' }}"
data-noakaun="{{ $line['noakaun'] ?? '' }}" data-noakaun="{{ $line['noakaun'] ?? '' }}"
data-namaakaun="{{ $line['nama_akaun'] ?? '' }}"
data-namabank="{{ $line['nama_bank'] ?? '' }}" data-namabank="{{ $line['nama_bank'] ?? '' }}"
data-nota="{{ $line['nota'] ?? '' }}" data-nota="{{ $line['nota'] ?? '' }}"
data-norujukan="{{ $line['norujukan'] ?? '' }}" data-norujukan="{{ $line['norujukan'] ?? '' }}"
data-bukti-akaun="{{ $line['bukti_akaun_file_id'] ?? '' }}"> data-bukti-akaun="{{ $line['bukti_akaun_file_id'] ?? '' }}"
data-borang-file="{{ $line['borang_file_id'] ?? '' }}">
Edit Edit
</button> </button>
</td> </td>
@@ -390,10 +390,6 @@
<label>No. Akaun</label> <label>No. Akaun</label>
<input type="text" name="noakaun" id="editNoakaun" class="form-control" required> <input type="text" name="noakaun" id="editNoakaun" class="form-control" required>
</div> </div>
<div class="form-group">
<label>Nama Akaun</label>
<input type="text" name="nama_akaun" id="editNamaakaun" class="form-control">
</div>
<div class="form-group"> <div class="form-group">
<label>Slip Bank / Bukti Akaun</label> <label>Slip Bank / Bukti Akaun</label>
<input type="file" name="bukti_akaun" id="editBuktiAkaun" class="form-control-file" <input type="file" name="bukti_akaun" id="editBuktiAkaun" class="form-control-file"
@@ -405,6 +401,17 @@
</div> </div>
<small class="text-muted">PDF / imej slip akaun bank pelanggan (maks 4MB)</small> <small class="text-muted">PDF / imej slip akaun bank pelanggan (maks 4MB)</small>
</div> </div>
<div class="form-group">
<label>Borang Pengesahan Penerimaan Pembiayaan Secara Atas Talian (Online)</label>
<input type="file" name="borang" id="editBorang" class="form-control-file"
accept=".pdf,.jpg,.jpeg,.png,.gif">
<div id="editBorangExisting" class="mt-2" style="display:none;">
<a href="#" id="editBorangLink" class="btn btn-sm btn-outline-primary" target="_blank">
Lihat borang sedia ada
</a>
</div>
<small class="text-muted">PDF / imej borang yang ditandatangani pelanggan (maks 4MB). Kosongkan jika tidak mahu ganti fail sedia ada.</small>
</div>
<div class="form-group"> <div class="form-group">
<label>Nota</label> <label>Nota</label>
<textarea name="nota" id="editNota" class="form-control" rows="2"></textarea> <textarea name="nota" id="editNota" class="form-control" rows="2"></textarea>
@@ -462,10 +469,6 @@
<th>No Akaun</th> <th>No Akaun</th>
<td id="verifyNoakaun"></td> <td id="verifyNoakaun"></td>
</tr> </tr>
<tr>
<th>Nama Akaun</th>
<td id="verifyNamaakaun"></td>
</tr>
<tr> <tr>
<th>Pinjaman</th> <th>Pinjaman</th>
<td>RM <span id="verifyPinjaman"></span></td> <td>RM <span id="verifyPinjaman"></span></td>
@@ -485,9 +488,12 @@
Saya sahkan maklumat pelanggan, bank dan amaun adalah betul. Saya sahkan maklumat pelanggan, bank dan amaun adalah betul.
</label> </label>
</div> </div>
<a href="#" id="verifyBuktiAkaunLink" class="btn btn-sm btn-outline-primary" target="_blank" style="display:none;"> <a href="#" id="verifyBuktiAkaunLink" class="btn btn-sm btn-outline-primary mb-2" target="_blank" style="display:none;">
Lihat Bukti Akaun Bank Lihat Bukti Akaun Bank
</a> </a>
<a href="#" id="verifyBorangLink" class="btn btn-sm btn-outline-primary" target="_blank" style="display:none;">
Lihat Borang Pengesahan
</a>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<label class="d-block">Pratonton SAG</label> <label class="d-block">Pratonton SAG</label>
@@ -576,7 +582,6 @@
$('#verifyNokp').text($(this).data('nokp') || '-'); $('#verifyNokp').text($(this).data('nokp') || '-');
$('#verifyBank').text($(this).data('namabank') || '-'); $('#verifyBank').text($(this).data('namabank') || '-');
$('#verifyNoakaun').text($(this).data('noakaun') || '-'); $('#verifyNoakaun').text($(this).data('noakaun') || '-');
$('#verifyNamaakaun').text($(this).data('namaakaun') || '-');
$('#verifyPinjaman').text($(this).data('pinjaman') || '0.00'); $('#verifyPinjaman').text($(this).data('pinjaman') || '0.00');
$('#verifyNota').text($(this).data('nota') || '-'); $('#verifyNota').text($(this).data('nota') || '-');
if (sebab) { if (sebab) {
@@ -592,6 +597,11 @@
} else { } else {
$('#verifyBuktiAkaunLink').hide(); $('#verifyBuktiAkaunLink').hide();
} }
if ($(this).data('borangFile')) {
$('#verifyBorangLink').attr('href', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/borang").show();
} else {
$('#verifyBorangLink').hide();
}
$('#verifyModal').modal('show'); $('#verifyModal').modal('show');
}); });
@@ -605,10 +615,10 @@
$('#editNorujukan').text($(this).data('norujukan')); $('#editNorujukan').text($(this).data('norujukan'));
$('#editKodbank').val($(this).data('kodbank')); $('#editKodbank').val($(this).data('kodbank'));
$('#editNoakaun').val($(this).data('noakaun')); $('#editNoakaun').val($(this).data('noakaun'));
$('#editNamaakaun').val($(this).data('namaakaun'));
$('#editNamabank').val($(this).data('namabank')); $('#editNamabank').val($(this).data('namabank'));
$('#editNota').val($(this).data('nota')); $('#editNota').val($(this).data('nota'));
$('#editBuktiAkaun').val(''); $('#editBuktiAkaun').val('');
$('#editBorang').val('');
$('#editSagFrame').attr('src', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/sag"); $('#editSagFrame').attr('src', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/sag");
if ($(this).data('buktiAkaun')) { if ($(this).data('buktiAkaun')) {
$('#editBuktiAkaunLink').attr('href', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/bukti-akaun"); $('#editBuktiAkaunLink').attr('href', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/bukti-akaun");
@@ -616,6 +626,12 @@
} else { } else {
$('#editBuktiAkaunExisting').hide(); $('#editBuktiAkaunExisting').hide();
} }
if ($(this).data('borangFile')) {
$('#editBorangLink').attr('href', "{{ url('/pembiayaan-online/pc/lines') }}/" + id + "/borang");
$('#editBorangExisting').show();
} else {
$('#editBorangExisting').hide();
}
$('#editModal').modal('show'); $('#editModal').modal('show');
}); });
@@ -258,7 +258,7 @@
<th style="width:13%">No. KP</th> <th style="width:13%">No. KP</th>
<th style="width:11%">Bank</th> <th style="width:11%">Bank</th>
<th style="width:13%">No. Akaun</th> <th style="width:13%">No. Akaun</th>
<th>Nama Akaun</th> <th>No. Telefon</th>
<th style="width:12%">Pinjaman (RM)</th> <th style="width:12%">Pinjaman (RM)</th>
</tr> </tr>
</thead> </thead>
@@ -271,7 +271,7 @@
<td class="num">{{ $line['nokp'] ?? '-' }}</td> <td class="num">{{ $line['nokp'] ?? '-' }}</td>
<td>{{ $line['nama_bank'] ?? ($line['kodbank'] ?? '-') }}</td> <td>{{ $line['nama_bank'] ?? ($line['kodbank'] ?? '-') }}</td>
<td class="num">{{ $line['noakaun'] ?? '-' }}</td> <td class="num">{{ $line['noakaun'] ?? '-' }}</td>
<td>{{ $line['nama_akaun'] ?? '-' }}</td> <td>{{ !empty($line['telefon']) ? $line['telefon'] : '-' }}</td>
<td class="amt">{{ number_format((float) ($line['pinjaman'] ?? 0), 2) }}</td> <td class="amt">{{ number_format((float) ($line['pinjaman'] ?? 0), 2) }}</td>
</tr> </tr>
@empty @empty
@@ -306,7 +306,7 @@
<div class="signs"> <div class="signs">
<div class="sign-box"> <div class="sign-box">
<div class="role">Tandatangan Operasi</div> <div class="role">Disemak oleh</div>
<div class="sign-line"> <div class="sign-line">
<p>Nama: {{ $opsBy }}</p> <p>Nama: {{ $opsBy }}</p>
<p>Jawatan: Operasi</p> <p>Jawatan: Operasi</p>
@@ -314,10 +314,10 @@
</div> </div>
</div> </div>
<div class="sign-box"> <div class="sign-box">
<div class="role">Pengurus Besar</div> <div class="role">Disahkan oleh</div>
<div class="sign-line"> <div class="sign-line">
<p>Nama:</p> <p>Nama:</p>
<p>Cop:</p> <p>Jawatan:</p>
<p>Tarikh:</p> <p>Tarikh:</p>
</div> </div>
</div> </div>
+346
View File
@@ -0,0 +1,346 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BSAS eCert — {{ $ecert_no ?? 'Preview' }}</title>
<style>
:root {
--ink: #1a2332;
--muted: #5a6570;
--line: #c5a572;
--line-soft: #e2d4bc;
--accent: #0f4c5c;
--paper: #fbf8f2;
--panel: #ffffff;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: #d9dde3;
color: var(--ink);
font-family: "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
}
.preview-bar {
position: sticky;
top: 0;
z-index: 20;
display: flex;
gap: 10px;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
background: #0f4c5c;
color: #fff;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
}
.preview-bar .badge {
display: inline-block;
padding: 2px 8px;
border: 1px solid rgba(255,255,255,.55);
border-radius: 3px;
letter-spacing: .08em;
font-size: 11px;
font-weight: 700;
}
.preview-bar button,
.preview-bar a {
border: 0;
background: #c5a572;
color: #1a2332;
font-weight: 700;
padding: 7px 14px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
}
.sheet-stack {
padding: 24px 12px 48px;
display: flex;
flex-direction: column;
align-items: center;
gap: 28px;
}
.page {
position: relative;
width: 210mm;
min-height: 297mm;
background: var(--paper);
box-shadow: 0 10px 30px rgba(0,0,0,.18);
overflow: hidden;
padding: 16mm 16mm 18mm;
}
.content {
position: relative;
z-index: 1;
min-height: calc(297mm - 34mm);
display: flex;
flex-direction: column;
}
.page-body {
flex: 1 1 auto;
}
.cert-title {
margin: 0 0 6px;
text-align: center;
font-size: 22px;
letter-spacing: .18em;
text-transform: uppercase;
color: var(--accent);
font-weight: 700;
}
.cert-no {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
margin-bottom: 18px;
}
.cert-no strong {
color: var(--accent);
letter-spacing: .04em;
}
.gold-rule {
height: 1px;
background: linear-gradient(90deg, transparent, var(--line), transparent);
margin: 0 8%;
}
.statement {
margin: 20px auto 24px;
max-width: 92%;
text-align: center;
font-size: 13.5px;
line-height: 1.65;
color: var(--ink);
}
.section-label {
margin: 0 0 10px;
font-family: Arial, Helvetica, sans-serif;
font-size: 11px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--accent);
font-weight: 700;
border-left: 3px solid var(--line);
padding-left: 8px;
}
.detail-panel {
background: var(--panel);
border: 1px solid var(--line-soft);
border-radius: 2px;
padding: 14px 16px 8px;
}
.detail-grid {
width: 100%;
border-collapse: collapse;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
.detail-grid tr td {
padding: 7px 8px;
vertical-align: top;
border-bottom: 1px solid #eee6d8;
}
.detail-grid tr:last-child td {
border-bottom: 0;
}
.detail-grid .label {
width: 38%;
color: var(--muted);
font-weight: 600;
}
.detail-grid .value {
color: var(--ink);
font-weight: 700;
}
.commodity-table {
width: 100%;
border-collapse: collapse;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
margin-top: 8px;
}
.commodity-table th,
.commodity-table td {
border: 1px solid var(--line-soft);
padding: 10px 12px;
text-align: left;
}
.commodity-table th {
background: #f3eee4;
color: var(--accent);
letter-spacing: .06em;
text-transform: uppercase;
font-size: 11px;
}
.commodity-table td.num {
text-align: right;
font-variant-numeric: tabular-nums;
}
.murabaha-box {
margin-top: 14px;
padding: 10px 12px;
border: 1px dashed var(--line);
background: rgba(197, 165, 114, .08);
font-family: Arial, Helvetica, sans-serif;
display: flex;
justify-content: space-between;
align-items: center;
}
.murabaha-box .k {
font-size: 11px;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--muted);
font-weight: 700;
}
.murabaha-box .v {
font-size: 16px;
font-weight: 700;
color: var(--accent);
}
@media print {
body { background: #fff; }
.preview-bar { display: none !important; }
.sheet-stack { padding: 0; gap: 0; }
.page {
box-shadow: none;
margin: 0;
page-break-after: always;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.page:last-child { page-break-after: auto; }
}
@page {
size: A4 portrait;
margin: 0;
}
</style>
</head>
<body>
@php
$certificates = $certificates ?? [];
$lines = $lines ?? [];
$ecertNo = $ecert_no ?? 'CPO21OCT26-0000031-000';
$isDummy = $isDummy ?? false;
$pageTotal = count($certificates) + 1;
@endphp
<div class="preview-bar">
<div>
@if($isDummy)
<span class="badge">DUMMY PREVIEW</span>
&nbsp; BSAS e-Certificate layout — {{ $pageTotal }} pages A4
@else
BSAS e-Certificate — {{ $ecertNo }}
@endif
</div>
<div>
<button type="button" onclick="window.print()">Print / PDF</button>
</div>
</div>
<div class="sheet-stack">
@foreach($certificates as $index => $certificate)
<section class="page">
<x-print.bsas.watermark />
<div class="content">
<x-print.bsas.header />
<div class="page-body">
<h1 class="cert-title">{{ $certificate['title'] }}</h1>
<div class="cert-no">Certificate Number: <strong>{{ $certificate['ecert_no'] ?? $ecertNo }}</strong></div>
<div class="gold-rule"></div>
<p class="statement">{!! $certificate['statement'] !!}</p>
<h2 class="section-label">Detail</h2>
<div class="detail-panel">
<table class="detail-grid">
@foreach($certificate['rows'] as $row)
<tr>
<td class="label">{{ $row['label'] }}</td>
<td class="value">{{ $row['value'] }}</td>
</tr>
@endforeach
</table>
@if(!empty($certificate['show_murabaha']))
<div class="murabaha-box">
<span class="k">Murabaha Value</span>
<span class="v">{{ $certificate['currency'] ?? 'MYR' }} {{ $certificate['murabaha'] }}</span>
</div>
@endif
</div>
</div>
<x-print.bsas.footer
:page="$index + 1"
:total="$pageTotal"
/>
</div>
</section>
@endforeach
<section class="page">
<x-print.bsas.watermark />
<div class="content">
<x-print.bsas.header />
<div class="page-body">
<h1 class="cert-title" style="font-size:18px;">Commodity Schedule</h1>
<div class="cert-no">Certificate Number: <strong>{{ $ecertNo }}</strong></div>
<div class="gold-rule"></div>
<h2 class="section-label" style="margin-top:28px;">Commodity Information</h2>
<div class="detail-panel">
<table class="commodity-table">
<thead>
<tr>
<th>Commodity Supplying Participant</th>
<th style="width:28%;text-align:right;">Commodity Volume</th>
</tr>
</thead>
<tbody>
@foreach($lines as $line)
<tr>
<td>{{ $line['supplier'] ?? '-' }}</td>
<td class="num">{{ $line['volume'] ?? '-' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<x-print.bsas.footer :page="$pageTotal" :total="$pageTotal">
This schedule forms part of Certificate No. {{ $ecertNo }}
</x-print.bsas.footer>
</div>
</section>
</div>
</body>
</html>
+25 -4
View File
@@ -556,9 +556,30 @@
</table> </table>
@php @php
$komuditi = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/komuditi/check')->json(); $bsasSag = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/komuditi/bsas/order/norujukan/' . urlencode($gadai_detail['norujukan']));
$product = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/komuditi/description',['kodproduk'=>$komuditi['kodproduct']])->json();
$kuantiti = $gadai_detail['pinjaman']/$komuditi['nilai']; $komuditi = [];
$product = [];
$kuantiti = 0;
if ($bsasSag->successful() && is_array($bsasSag->json())) {
$bsasPayload = $bsasSag->json();
$komuditi = $bsasPayload['komoditi'] ?? [];
$product = $bsasPayload['product'] ?? [];
$kuantiti = (float) ($bsasPayload['kuantiti'] ?? 0);
}
if (!is_array($komuditi)) {
$komuditi = [];
}
if (!is_array($product)) {
$product = [
'product_code' => $komuditi['kodproduct'] ?? '',
'kodproduk' => $komuditi['kodproduct'] ?? '',
'description' => '',
'unit' => 'TAN',
];
}
@endphp @endphp
<table style="margin-bottom:10px;" id="keuntungan"> <table style="margin-bottom:10px;" id="keuntungan">
@@ -576,7 +597,7 @@
<tr> <tr>
<td>2</td> <td>2</td>
<td>Kod Produk / Diskripsi</td> <td>Kod Produk / Diskripsi</td>
<td>{{ $product['kodproduk'] . ' / ' . $product['description'] }}</td> <td>{{ $product['product_code'] ?? '' }}</td>
<td>5</td> <td>5</td>
<td>Nilai</td> <td>Nilai</td>
<td>{{ 'RM ' . number_format($gadai_detail['pinjaman'],2) }}</td> <td>{{ 'RM ' . number_format($gadai_detail['pinjaman'],2) }}</td>
+4 -2
View File
@@ -620,7 +620,7 @@
<tr> <tr>
<td class="text-center">2</td> <td class="text-center">2</td>
<td>Kod Produk / Deskripsi</td> <td>Kod Produk / Deskripsi</td>
<td>{{ $product['kodproduk'] . ' / ' . $product['description'] }}</td> <td>{{ $product['product_code'] ?? '' }}</td>
<td class="text-center">5</td> <td class="text-center">5</td>
<td>harga kos</td> <td>harga kos</td>
<td>{{ 'RM' . number_format($gadai_detail['pinjaman'], 2) }}</td> <td>{{ 'RM' . number_format($gadai_detail['pinjaman'], 2) }}</td>
@@ -1854,7 +1854,7 @@
<tr> <tr>
<td class="text-center">2</td> <td class="text-center">2</td>
<td>Kod Produk / Deskripsi</td> <td>Kod Produk / Deskripsi</td>
<td>{{ $product['kodproduk'] . ' / ' . $product['description'] }}</td> <td>{{ $product['product_code'] ?? '' }}</td>
<td class="text-center">5</td> <td class="text-center">5</td>
<td>Harga Kos</td> <td>Harga Kos</td>
<td>{{ 'RM' . number_format($gadai_detail['pinjaman'], 2) }}</td> <td>{{ 'RM' . number_format($gadai_detail['pinjaman'], 2) }}</td>
@@ -2112,6 +2112,7 @@
</div> </div>
<div class="main-page" style="font-family: Arial, Helvetica, sans-serif;"> <div class="main-page" style="font-family: Arial, Helvetica, sans-serif;">
<div class="sub-page" style="font-size: 11px;"> <div class="sub-page" style="font-size: 11px;">
@if ($pembiayaanOnlineEnabled ?? false)
@php @php
$caraTerimaBayaran = strtoupper(trim((string) ($gadai_detail['cara_terima'] ?? ''))); $caraTerimaBayaran = strtoupper(trim((string) ($gadai_detail['cara_terima'] ?? '')));
@endphp @endphp
@@ -2128,6 +2129,7 @@
</span> </span>
</div> </div>
</div> </div>
@endif
{{-- HEADER SECTION --}} {{-- HEADER SECTION --}}
<table style="width: 100%; margin-top: 0;"> <table style="width: 100%; margin-top: 0;">
<tr style="vertical-align: top;"> <tr style="vertical-align: top;">
+15 -1
View File
@@ -17,6 +17,8 @@ use Illuminate\Support\Facades\Storage;
Route::post('/internal/notifications', ['as' => 'internal.notifications.store', 'uses' => 'InternalNotificationController@store']); Route::post('/internal/notifications', ['as' => 'internal.notifications.store', 'uses' => 'InternalNotificationController@store']);
Route::view('/dasar-privasi', 'dasar-privasi')->name('dasar.privasi');
Route::get('/health', function () { Route::get('/health', function () {
return response()->json(['status' => 'ok']); return response()->json(['status' => 'ok']);
}); });
@@ -61,7 +63,13 @@ Route::get('test', function () {
return "event test je"; return "event test je";
}); });
Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index'); Route::get('logs', '\Rap2hpoutre\LaravelLogViewer\LogViewerController@index')
->middleware([
'auth',
'role:admin,masteradmin,operasi',
'throttle:10,1',
\App\Http\Middleware\LogViewerPassword::class,
]);
Route::get('change-password', ['as'=>'user.password','uses'=>'ChangePasswordController@index']); Route::get('change-password', ['as'=>'user.password','uses'=>'ChangePasswordController@index']);
Route::post('change-password', ['as'=>'change.password','uses'=>'ChangePasswordController@store']); Route::post('change-password', ['as'=>'change.password','uses'=>'ChangePasswordController@store']);
@@ -204,6 +212,7 @@ Route::put('/pembiayaan-online/pc/lines/{id}',['as'=>'pembiayaan.online.pc.updat
Route::post('/pembiayaan-online/pc/lines/{id}/verify',['as'=>'pembiayaan.online.pc.verify','uses'=>'PembiayaanOnlineController@verifyLine'])->middleware('auth','khazanah'); Route::post('/pembiayaan-online/pc/lines/{id}/verify',['as'=>'pembiayaan.online.pc.verify','uses'=>'PembiayaanOnlineController@verifyLine'])->middleware('auth','khazanah');
Route::post('/pembiayaan-online/pc/lines/{id}/unverify',['as'=>'pembiayaan.online.pc.unverify','uses'=>'PembiayaanOnlineController@unverifyLine'])->middleware('auth','khazanah'); Route::post('/pembiayaan-online/pc/lines/{id}/unverify',['as'=>'pembiayaan.online.pc.unverify','uses'=>'PembiayaanOnlineController@unverifyLine'])->middleware('auth','khazanah');
Route::get('/pembiayaan-online/pc/lines/{id}/bukti-akaun',['as'=>'pembiayaan.online.pc.bukti.akaun','uses'=>'PembiayaanOnlineController@viewBuktiAkaun'])->middleware('auth','khazanah'); Route::get('/pembiayaan-online/pc/lines/{id}/bukti-akaun',['as'=>'pembiayaan.online.pc.bukti.akaun','uses'=>'PembiayaanOnlineController@viewBuktiAkaun'])->middleware('auth','khazanah');
Route::get('/pembiayaan-online/pc/lines/{id}/borang',['as'=>'pembiayaan.online.pc.borang','uses'=>'PembiayaanOnlineController@viewBorang'])->middleware('auth','khazanah');
Route::get('/pembiayaan-online/pc/lines/{id}/sag',['as'=>'pembiayaan.online.pc.sag','uses'=>'PembiayaanOnlineController@viewSag'])->middleware('auth','khazanah'); Route::get('/pembiayaan-online/pc/lines/{id}/sag',['as'=>'pembiayaan.online.pc.sag','uses'=>'PembiayaanOnlineController@viewSag'])->middleware('auth','khazanah');
Route::post('/pembiayaan-online/pc/batches/submit',['as'=>'pembiayaan.online.pc.submit','uses'=>'PembiayaanOnlineController@submitBatch'])->middleware('auth','khazanah'); Route::post('/pembiayaan-online/pc/batches/submit',['as'=>'pembiayaan.online.pc.submit','uses'=>'PembiayaanOnlineController@submitBatch'])->middleware('auth','khazanah');
Route::get('/pembiayaan-online/pc/batches/{batchId}',['as'=>'pembiayaan.online.pc.batch','uses'=>'PembiayaanOnlineController@showBatch'])->middleware('auth','khazanah'); Route::get('/pembiayaan-online/pc/batches/{batchId}',['as'=>'pembiayaan.online.pc.batch','uses'=>'PembiayaanOnlineController@showBatch'])->middleware('auth','khazanah');
@@ -230,6 +239,7 @@ Route::post('/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/approve',['as
Route::post('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/paid',['as'=>'pembiayaan.online.operasi.paid','uses'=>'PembiayaanOnlineController@operasiMarkPaid'])->middleware('auth','operasi'); Route::post('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/paid',['as'=>'pembiayaan.online.operasi.paid','uses'=>'PembiayaanOnlineController@operasiMarkPaid'])->middleware('auth','operasi');
Route::post('/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/paid',['as'=>'pembiayaan.online.operasi.paid.batch','uses'=>'PembiayaanOnlineController@operasiMarkPaidBatch'])->middleware('auth','operasi'); Route::post('/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/paid',['as'=>'pembiayaan.online.operasi.paid.batch','uses'=>'PembiayaanOnlineController@operasiMarkPaidBatch'])->middleware('auth','operasi');
Route::get('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/bukti-akaun',['as'=>'pembiayaan.online.operasi.bukti.akaun','uses'=>'PembiayaanOnlineController@operasiViewBuktiAkaun'])->middleware('auth','operasi'); Route::get('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/bukti-akaun',['as'=>'pembiayaan.online.operasi.bukti.akaun','uses'=>'PembiayaanOnlineController@operasiViewBuktiAkaun'])->middleware('auth','operasi');
Route::get('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/borang',['as'=>'pembiayaan.online.operasi.borang','uses'=>'PembiayaanOnlineController@operasiViewBorang'])->middleware('auth','operasi');
Route::get('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/sag',['as'=>'pembiayaan.online.operasi.sag','uses'=>'PembiayaanOnlineController@operasiViewSag'])->middleware('auth','operasi'); Route::get('/pembiayaan-online/operasi/{kodcaw}/lines/{id}/sag',['as'=>'pembiayaan.online.operasi.sag','uses'=>'PembiayaanOnlineController@operasiViewSag'])->middleware('auth','operasi');
Route::get('/permohonan/rekod',['as'=>'permohonan.rekod','uses' => 'KhazanahController@RekodPermohonan'])->middleware('auth','khazanah'); Route::get('/permohonan/rekod',['as'=>'permohonan.rekod','uses' => 'KhazanahController@RekodPermohonan'])->middleware('auth','khazanah');
Route::post('/permohonan/simpan',['as'=>'permohonan.simpan','uses' => 'KhazanahController@SimpanPermohonan'])->middleware('auth','khazanah'); Route::post('/permohonan/simpan',['as'=>'permohonan.simpan','uses' => 'KhazanahController@SimpanPermohonan'])->middleware('auth','khazanah');
@@ -243,6 +253,8 @@ Route::post('/kakitangan/harian',['as'=>'kakitangan.harian','uses'=>'KakitanganC
Route::get('/lelong',['as'=>'lelong','uses'=>'LelongController@lelong']); Route::get('/lelong',['as'=>'lelong','uses'=>'LelongController@lelong']);
Route::get('/khazanah/harga_emas',['as'=>'khazanah.harga_emas','uses'=>'HargaEmasController@index'])->middleware('auth','khazanah'); Route::get('/khazanah/harga_emas',['as'=>'khazanah.harga_emas','uses'=>'HargaEmasController@index'])->middleware('auth','khazanah');
Route::get('/khazanah/komoditi/bsas',['as'=>'khazanah.bsas','uses'=>'KomuditiController@khazanahBsas'])->middleware('auth','khazanah');
Route::get('/khazanah/komoditi/bsas/ecert/{uuid}',['as'=>'khazanah.bsas.ecert','uses'=>'KomuditiController@showBsasEcert'])->middleware('auth','khazanah');
//Request Teller Batal Route //Request Teller Batal Route
Route::get('/requestbatal',['as'=>'request.pembatalan','uses'=>'KhazanahController@RequestPembatalan'])->middleware('auth','khazanah'); Route::get('/requestbatal',['as'=>'request.pembatalan','uses'=>'KhazanahController@RequestPembatalan'])->middleware('auth','khazanah');
@@ -445,6 +457,8 @@ Route::get('/operasi/marginmutu/edit',['as'=>'operasi.marginmutu.edit','uses'=>'
Route::get('/komoditi',['as'=>'komuditi','uses'=>'KomuditiController@index']); Route::get('/komoditi',['as'=>'komuditi','uses'=>'KomuditiController@index']);
Route::get('/komoditi/rekod',['as'=>'rekod.komoditi','uses'=>'KomuditiController@rekodKomoditi']); Route::get('/komoditi/rekod',['as'=>'rekod.komoditi','uses'=>'KomuditiController@rekodKomoditi']);
Route::get('/komoditi/rekod/print',['as'=>'komoditi.rekod.print','uses'=>'KomuditiController@printRekodTransaksi']); Route::get('/komoditi/rekod/print',['as'=>'komoditi.rekod.print','uses'=>'KomuditiController@printRekodTransaksi']);
Route::get('/komoditi/bsas/ecert/preview',['as'=>'komoditi.bsas.ecert.preview','uses'=>'KomuditiController@previewBsasEcert'])->middleware('auth','operasi');
Route::get('/komoditi/bsas/ecert/{uuid}',['as'=>'komoditi.bsas.ecert','uses'=>'KomuditiController@showBsasEcert'])->middleware('auth','operasi');
Route::post('/khazanah/suratakuangadaian',['as'=>'agh.create','uses'=>'KhazanahController@SuratAkuanGadaian'])->middleware('auth','khazanah'); Route::post('/khazanah/suratakuangadaian',['as'=>'agh.create','uses'=>'KhazanahController@SuratAkuanGadaian'])->middleware('auth','khazanah');
Route::post('/khazanah/suratakuangadaianedit',['as'=>'agh.update','uses'=>'KhazanahController@SuratAkuanGadaianUpdate'])->middleware('auth','khazanah'); Route::post('/khazanah/suratakuangadaianedit',['as'=>'agh.update','uses'=>'KhazanahController@SuratAkuanGadaianUpdate'])->middleware('auth','khazanah');
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Tests\Feature;
use App\Http\Middleware\LogViewerPassword;
use Illuminate\Http\Request;
use Tests\TestCase;
class LogViewerPasswordTest extends TestCase
{
public function test_missing_configuration_denies_access()
{
config(['logviewer.password' => null]);
$response = $this->checkAccess();
$this->assertSame(503, $response->getStatusCode());
}
public function test_missing_or_incorrect_credentials_are_rejected()
{
config(['logviewer.password' => password_hash('test-only-secret', PASSWORD_DEFAULT)]);
foreach ([[], ['PHP_AUTH_USER' => 'logs', 'PHP_AUTH_PW' => 'wrong'],
['PHP_AUTH_USER' => 'other', 'PHP_AUTH_PW' => 'test-only-secret']] as $server) {
$response = $this->checkAccess($server);
$this->assertSame(401, $response->getStatusCode());
$this->assertStringContainsString('Basic realm=', $response->headers->get('WWW-Authenticate'));
}
}
public function test_correct_credentials_allow_access_without_caching()
{
config(['logviewer.password' => password_hash('test-only-secret', PASSWORD_DEFAULT)]);
$response = $this->checkAccess(['PHP_AUTH_USER' => 'logs', 'PHP_AUTH_PW' => 'test-only-secret']);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('allowed', $response->getContent());
$this->assertStringContainsString('no-store', $response->headers->get('Cache-Control'));
}
public function test_logs_route_has_auth_role_password_and_throttle_middleware()
{
$route = app('router')->getRoutes()->match(Request::create('/logs'));
$middleware = $route->gatherMiddleware();
$this->assertContains('auth', $middleware);
$this->assertContains('role:admin,masteradmin', $middleware);
$this->assertContains(LogViewerPassword::class, $middleware);
$this->assertContains('throttle:10,1', $middleware);
}
private function checkAccess(array $server = [])
{
$request = Request::create('/logs?dl=example', 'GET', [], [], [], $server);
return (new LogViewerPassword)->handle($request, function () {
return response('allowed');
});
}
}