Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5bffd0b34 | |||
| d60dd8bf39 | |||
| 1f8642e230 | |||
| a825b0b467 | |||
| b0792cceb0 | |||
| 82f31baa6a | |||
| 7175d0dbcf | |||
| 15575dc8c0 | |||
| 03a0142066 | |||
| 5e7fe8c3ab | |||
| 358baf3453 | |||
| 7eebd79397 | |||
| f7dd2fae7b | |||
| b8cc3f38cc | |||
| c06e33bc1c | |||
| b95f68bd3f | |||
| c989197e76 | |||
| e3424e9161 | |||
| da06ff709c | |||
| d6b4685f54 | |||
| 173ce892b9 | |||
| 6af434360d | |||
| aaf784ad79 | |||
| 3456059db4 | |||
| cd9f83e21f | |||
| 1e174a48b6 | |||
| 80b8fd3a24 | |||
| 71990e7582 | |||
| 31dc30ed60 | |||
| bb8bcae8e8 |
@@ -0,0 +1,16 @@
|
||||
.git
|
||||
.gitea
|
||||
node_modules
|
||||
vendor
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
storage/logs
|
||||
storage/framework/cache
|
||||
storage/framework/sessions
|
||||
storage/framework/views
|
||||
storage/debugbar
|
||||
npm-debug.log
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
docker-compose.staging.yml
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:O1XZDqK4H6+3GdrODgCaLr2l+OoWvgVhYr0GJ1KizW8=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
# API_URL=http://api-annurv2.test
|
||||
API_URL=http://host.docker.internal:8080
|
||||
API_URL_KOOP=http://host.docker.internal:8080
|
||||
API_URL_PKB=http://host.docker.internal:8080
|
||||
API_ARRAHNBID=http://lelong.test/api/
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=kaunter_erahn
|
||||
DB_USERNAME=kaunteruser
|
||||
DB_PASSWORD=password
|
||||
|
||||
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
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:O1XZDqK4H6+3GdrODgCaLr2l+OoWvgVhYr0GJ1KizW8=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
# API_URL=http://api-annurv2.test
|
||||
API_URL=http://host.docker.internal:8080
|
||||
# Server-side Http:: from Docker — same rules as API_URL. Defaults in config/api.php use localhost:8000 which fails inside containers.
|
||||
API_URL_KOOP=http://host.docker.internal:8080
|
||||
API_URL_PKB=http://host.docker.internal:8080
|
||||
API_ARRAHNBID=http://lelong.test/api/
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=kaunter_erahn
|
||||
DB_USERNAME=kaunteruser
|
||||
DB_PASSWORD=password
|
||||
|
||||
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
|
||||
+1
-1
@@ -46,5 +46,5 @@ AWS_BUCKET=
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
MYKAD_READER_PORT=8085
|
||||
MYKAD_READER_PORT=17321
|
||||
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=staging
|
||||
APP_VERSION=v2.2-staging
|
||||
APP_KEY=base64:O1XZDqK4H6+3GdrODgCaLr2l+OoWvgVhYr0GJ1KizW8=
|
||||
APP_DEBUG=true
|
||||
APP_URL=https://staging.erahn.com.my
|
||||
|
||||
HTTP_PORT=8091
|
||||
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_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=kaunter_erahn
|
||||
DB_USERNAME=kaunteruser
|
||||
DB_PASSWORD=password
|
||||
|
||||
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=17321
|
||||
MYKAD_READER_ENABLED_CAWANGAN='' # IF NO CAWANGAN STATED, ALL CAWANGAN IS ENABLED
|
||||
@@ -0,0 +1,50 @@
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- staging
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-frontend:
|
||||
runs-on: docker
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Docker registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||
docker login git.koppkb.com \
|
||||
-u "${{ secrets.REGISTRY_USERNAME }}" \
|
||||
--password-stdin
|
||||
|
||||
- name: Build & push frontend image
|
||||
run: |
|
||||
set -e
|
||||
|
||||
IMAGE="git.koppkb.com/kopkb/kaunter"
|
||||
TAGS="-t ${IMAGE}:${{ gitea.sha }}"
|
||||
|
||||
if [ "${{ gitea.ref_type }}" = "tag" ] && echo "${{ gitea.ref_name }}" | grep -q '^v'; then
|
||||
TAGS="${TAGS} -t ${IMAGE}:${{ gitea.ref_name }}"
|
||||
fi
|
||||
|
||||
if [ "${{ gitea.ref_name }}" = "staging" ]; then
|
||||
TAGS="${TAGS} -t ${IMAGE}:staging"
|
||||
fi
|
||||
|
||||
echo "Building and pushing frontend: ${TAGS}"
|
||||
docker buildx build \
|
||||
--target staging \
|
||||
--platform linux/amd64 \
|
||||
--cache-from type=registry,ref=${IMAGE}:buildcache,ignore-error=true \
|
||||
--cache-to type=registry,ref=${IMAGE}:buildcache,mode=max \
|
||||
-f docker/php/Dockerfile \
|
||||
${TAGS} \
|
||||
--push .
|
||||
@@ -0,0 +1,123 @@
|
||||
name: Deploy to Staging
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: "Docker image tag to deploy (commit SHA, staging, or v*)"
|
||||
required: true
|
||||
default: "staging"
|
||||
type: string
|
||||
|
||||
env:
|
||||
APP_IMAGE: git.koppkb.com/kopkb/kaunter
|
||||
DEPLOY_DIR: /home/arrahn/Project/kaunter
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: host
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.image_tag }}
|
||||
|
||||
- name: Login to Docker registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||
docker login git.koppkb.com \
|
||||
-u "${{ secrets.REGISTRY_USERNAME }}" \
|
||||
--password-stdin
|
||||
|
||||
- name: Verify image exists in registry
|
||||
run: |
|
||||
set -e
|
||||
IMAGE_TAG="${{ inputs.image_tag }}"
|
||||
IMAGE="${APP_IMAGE}:${IMAGE_TAG}"
|
||||
|
||||
echo "Checking if image exists: ${IMAGE}"
|
||||
if ! docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then
|
||||
echo "ERROR: Image ${IMAGE} does not exist in registry!"
|
||||
echo "Run the Build Docker Image workflow first."
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ ${IMAGE} found"
|
||||
|
||||
- name: Pull Docker image
|
||||
run: |
|
||||
set -e
|
||||
IMAGE_TAG="${{ inputs.image_tag }}"
|
||||
docker pull "${APP_IMAGE}:${IMAGE_TAG}"
|
||||
echo "✓ Image pulled successfully"
|
||||
|
||||
- name: Sync compose file and deploy
|
||||
run: |
|
||||
set -e
|
||||
IMAGE_TAG="${{ inputs.image_tag }}"
|
||||
|
||||
mkdir -p "${DEPLOY_DIR}/docker/nginx" "${DEPLOY_DIR}/public"
|
||||
cp docker-compose.staging.yml "${DEPLOY_DIR}/docker-compose.yml"
|
||||
cp docker/nginx/default.conf "${DEPLOY_DIR}/docker/nginx/default.conf"
|
||||
find "${DEPLOY_DIR}/public" -mindepth 1 -delete
|
||||
cp -R public/. "${DEPLOY_DIR}/public/"
|
||||
|
||||
if [ ! -f "${DEPLOY_DIR}/.env.staging" ]; then
|
||||
if [ -f .env.staging ]; then
|
||||
cp .env.staging "${DEPLOY_DIR}/.env.staging"
|
||||
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
|
||||
|
||||
cd "${DEPLOY_DIR}"
|
||||
|
||||
export APP_IMAGE
|
||||
export IMAGE_TAG
|
||||
|
||||
docker compose --env-file .env.staging -f docker-compose.yml pull app
|
||||
docker compose --env-file .env.staging -f docker-compose.yml up -d --no-build --force-recreate --remove-orphans app web
|
||||
|
||||
echo "✓ Deployed IMAGE_TAG=${IMAGE_TAG}"
|
||||
|
||||
- name: Verify deployment
|
||||
run: |
|
||||
set -e
|
||||
cd "${DEPLOY_DIR}"
|
||||
|
||||
echo "Waiting for services to start..."
|
||||
sleep 10
|
||||
|
||||
for SERVICE in app web mysql; do
|
||||
STATUS=$(docker compose --env-file .env.staging -f docker-compose.yml ps --status running --format '{{.Name}}' "$SERVICE" 2>/dev/null || true)
|
||||
if [ -z "$STATUS" ]; then
|
||||
echo "ERROR: ${SERVICE} is not running"
|
||||
docker compose --env-file .env.staging -f docker-compose.yml ps
|
||||
docker compose --env-file .env.staging -f docker-compose.yml logs --tail=50 "$SERVICE" || true
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ ${SERVICE} is running (${STATUS})"
|
||||
done
|
||||
|
||||
HTTP_PORT=$(grep -E '^HTTP_PORT=' .env.staging 2>/dev/null | cut -d= -f2- | tr -d '"' || true)
|
||||
HTTP_PORT=${HTTP_PORT:-8091}
|
||||
|
||||
check_health() {
|
||||
NAME=$1
|
||||
URL=$2
|
||||
for i in 1 2 3 4 5 6; do
|
||||
if curl -sf "$URL" > /dev/null; then
|
||||
echo "✓ ${NAME} health check passed (${URL})"
|
||||
return 0
|
||||
fi
|
||||
echo "Waiting for ${NAME}... attempt ${i}/6"
|
||||
sleep 5
|
||||
done
|
||||
echo "ERROR: ${NAME} health check failed (${URL})"
|
||||
docker compose --env-file .env.staging -f docker-compose.yml logs --tail=50 app web || true
|
||||
return 1
|
||||
}
|
||||
|
||||
check_health "kaunter" "http://127.0.0.1:${HTTP_PORT}/health"
|
||||
+1
-1
@@ -11,5 +11,5 @@ Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/.vscode
|
||||
docker-compose.yml
|
||||
docker_src/
|
||||
**/.DS_Store
|
||||
@@ -1 +1,51 @@
|
||||
If you does't know what is this you probably in wrong project
|
||||
|
||||
```sql
|
||||
--- for all ip
|
||||
CREATE USER 'kaunteruser'@'%' IDENTIFIED BY 'your_password';
|
||||
|
||||
--- for specific ip
|
||||
CREATE USER 'kaunteruser'@'localhost' IDENTIFIED BY 'your_password';
|
||||
|
||||
GRANT ALL PRIVILEGES ON kaunter_erahn.* TO 'kaunteruser'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
# ADDITIONAL NOTE
|
||||
|
||||
## customers/v2/index.blade.php is modal to register customer
|
||||
|
||||
## Min Komoditi = RM5000
|
||||
|
||||
# System Flowchart
|
||||
|
||||
## Proses Gadaian
|
||||
1) Pelanggan datang bawa marhun
|
||||
2) Teller daftar maklumat pelanggan jika pelanggan baharu
|
||||
3) Serah marhun ke penilai untuk timbang
|
||||
4) Teller daftar marhun di sistem berdasarkan maklumat dari penilai
|
||||
5) Penilai print SAG dan serah marhun ke Khazanah (PC/PPC) - __khazanah can still scan still scan though__
|
||||
6) Khazanah scan barcode dekat SAG untuk masuk ke dalam peti besi
|
||||
* table involved - gadai.semakbarcode (default is blank, after is Lulus)
|
||||
7) DONE
|
||||
|
||||
## Setup new branch command:
|
||||
1. Duplicate previous branch table - use erahn_{{name}}
|
||||
```sql
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
SET SESSION group_concat_max_len = 1000000;
|
||||
|
||||
SELECT GROUP_CONCAT(
|
||||
CONCAT('TRUNCATE TABLE `', table_name, '`')
|
||||
SEPARATOR '; '
|
||||
) INTO @sql
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE();
|
||||
|
||||
SELECT @sql; --- copy the output and paste it
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
```
|
||||
2. Add new cawangan kod on master_erahn.arrahn_master_db and and kaunter_erahn.cawangan
|
||||
3. Register new cawangan detail in erahn_{{name}}.cawangan
|
||||
@@ -0,0 +1,27 @@
|
||||
## CAWANGAN XTRA - X0301
|
||||
- WAN NUR FATIN NATASYA BINTI W M AZHARI - PENILAI
|
||||
- MOHAMAD KHAIRULNIZAN BIN JUSOF - TELLER
|
||||
- MOHAMAD AZUHAIDIE BIN AHMAD - PPC
|
||||
- WAN NURUL AMIRA BINTI WAN HARON - PC
|
||||
|
||||
## Akaun
|
||||
- admin@test.com
|
||||
- admin2@test.com
|
||||
|
||||
Module Gadai Online:
|
||||
- Langkah 1
|
||||
- Langkah 2
|
||||
[x] PC akan tekan submit, then customer akan masuk ke senarai online mengikut batch
|
||||
[x] PC boleh semak list customer yang pilih gadai online
|
||||
- Langkah 3 (Operasi)
|
||||
[x] dapat dalam bentuk list
|
||||
[x] Apa jadi kalau operasi reject? - patah balik ke pc, untuk pc buat semakan balik
|
||||
- Langkah 4
|
||||
- Langkah 5
|
||||
- Langkah 6
|
||||
- Langkah 7
|
||||
[x] Macam mana PC nak tahu pembayaran berjaya (Tally PAID page)
|
||||
[x] Kalau online, dia kena upload kat mana (Operasi: PAID + bukti)
|
||||
- Langkah 8
|
||||
[x] PC semak senarai pelanggan gadai tu tally atau tak
|
||||
[ ] PC tekan butang blast ke (tanda selesai ada; WhatsApp/SMS belum wire)
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
|
||||
class NotifikasiAnggota implements ShouldBroadcast
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public $nokp;
|
||||
public $status; // DILULUSKAN atau DITOLAK
|
||||
public $komen;
|
||||
public $tagoperasi;
|
||||
|
||||
public function __construct($nokp, $status, $komen, $tagoperasi = null)
|
||||
{
|
||||
$this->nokp = $nokp;
|
||||
$this->status = strtoupper($status); // pastikan huruf besar DILULUSKAN/DITOLAK
|
||||
$this->komen = $komen;
|
||||
$this->tagoperasi = $tagoperasi;
|
||||
}
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return ['notice-anggota'];
|
||||
}
|
||||
|
||||
public function broadcastAs()
|
||||
{
|
||||
return 'my-anggota';
|
||||
}
|
||||
|
||||
public function broadcastWith()
|
||||
{
|
||||
return [
|
||||
'noic' => $this->nokp,
|
||||
'status' => $this->status, // ikut format API updateAnggota
|
||||
'komen' => $this->komen,
|
||||
'tagoperasi' => $this->tagoperasi,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ImpersonateController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function impersonate(Request $request, $id): RedirectResponse
|
||||
{
|
||||
Gate::authorize('impersonate');
|
||||
|
||||
$impersonator = $request->user();
|
||||
$id = (int) $id;
|
||||
$target = User::findOrFail($id);
|
||||
|
||||
if ($target->id === $request->user()->id) {
|
||||
return back()->with('error', 'Tidak boleh impersonate diri sendiri.');
|
||||
}
|
||||
|
||||
if (method_exists($target, 'canBeImpersonated') && !$target->canBeImpersonated()) {
|
||||
return back()->with('error', 'Akaun ini tidak boleh di-impersonate.');
|
||||
}
|
||||
|
||||
$request->user()->impersonate($target);
|
||||
|
||||
return redirect()->route('home');
|
||||
}
|
||||
|
||||
public function leave(Request $request): RedirectResponse
|
||||
{
|
||||
if ($request->user() && method_exists($request->user(), 'isImpersonated') && $request->user()->isImpersonated()) {
|
||||
$impersonated = $request->user();
|
||||
$impersonator = method_exists($impersonated, 'impersonator') ? $impersonated->impersonator() : null;
|
||||
|
||||
$request->user()->leaveImpersonation();
|
||||
}
|
||||
|
||||
return redirect()->route('home');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,7 @@ class LoginController extends Controller
|
||||
'password' => 'required|string',
|
||||
]);
|
||||
|
||||
$credentials = $request->only('email', 'password');
|
||||
|
||||
if (Auth::guard('web')->attempt($credentials)) {
|
||||
if ($this->attemptWebLogin($request)) {
|
||||
|
||||
$auth_user = Auth::guard('web')->user();
|
||||
$api_url = config('api.url');
|
||||
@@ -103,9 +101,7 @@ class LoginController extends Controller
|
||||
'password' => 'required|string',
|
||||
]);
|
||||
|
||||
$credentials = $request->only('email', 'password');
|
||||
|
||||
if (Auth::guard('dashboard')->attempt($credentials,true)) {
|
||||
if ($this->attemptDashboardLogin($request)) {
|
||||
$auth_user = Auth::guard('dashboard')->user();
|
||||
|
||||
$user_login['nama'] = $auth_user['name'];
|
||||
@@ -187,4 +183,42 @@ class LoginController extends Controller
|
||||
|
||||
return response()->json($kakitangan,200);
|
||||
}
|
||||
|
||||
/**
|
||||
* In local environment, log in by email only (password ignored).
|
||||
*/
|
||||
private function attemptWebLogin(Request $request): bool
|
||||
{
|
||||
if (app()->environment('local')) {
|
||||
$user = User::where('email', $request->email)->first();
|
||||
if ($user) {
|
||||
Auth::guard('web')->login($user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return Auth::guard('web')->attempt($request->only('email', 'password'));
|
||||
}
|
||||
|
||||
/**
|
||||
* In local environment, log in by email only (password ignored).
|
||||
*/
|
||||
private function attemptDashboardLogin(Request $request): bool
|
||||
{
|
||||
if (app()->environment('local')) {
|
||||
$user = Dashboard::where('email', $request->email)->first();
|
||||
if ($user) {
|
||||
Auth::guard('dashboard')->login($user, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return Auth::guard('dashboard')->attempt($request->only('email', 'password'), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Twilio\Rest\Client;
|
||||
use App\Komoditi;
|
||||
use App\Events\Dashboard\PelangganNotify;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
@@ -24,7 +25,7 @@ class CustomersController extends Controller
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->customerService = New CustomerService();
|
||||
$this->customerService = new CustomerService();
|
||||
}
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
@@ -38,84 +39,241 @@ class CustomersController extends Controller
|
||||
$api_url = config('api.url');
|
||||
$auth_user = Auth::user();
|
||||
$idtype = 'nokp';
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
$bank = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/bank')->json();
|
||||
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/pekerjaan')->json();
|
||||
$blacklist_search = false;
|
||||
|
||||
return view('customers.' . $cawangan_info['version'] . '.index',compact('search','blacklist_search','cawangan_info','bank','api_url','idtype','pekerjaan'));
|
||||
return view('customers.' . $cawangan_info['version'] . '.index', compact('search', 'blacklist_search', 'cawangan_info', 'bank', 'api_url', 'idtype', 'pekerjaan'));
|
||||
}
|
||||
|
||||
public function info(Request $request){
|
||||
public function info(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/'. Session::get('noic'))->json();
|
||||
// dd($customer_info);
|
||||
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/pekerjaan')->json();
|
||||
// 🔹 Panggil maklumat cawangan
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/cawangan/' . $auth_user['cawangan'])
|
||||
->json();
|
||||
|
||||
// 🔹 Panggil maklumat pelanggan (customer)
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/customers/' . Session::get('noic'))
|
||||
->json();
|
||||
|
||||
// 🔹 Panggil maklumat pekerjaan
|
||||
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/pekerjaan')
|
||||
->json();
|
||||
|
||||
$komoditi = Komoditi::first();
|
||||
$min_komoditi = $komoditi['min_komoditi'];
|
||||
|
||||
$bakilelong = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/bakilelong/cawangan/' . $auth_user['cawangan'] . '/' . Session::get('noic') . '/baki')->json();
|
||||
$rugilelong = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/bakilelong/cawangan/' . $auth_user['cawangan'] . '/' . Session::get('noic') . '/rugi')->json();
|
||||
// 🔹 Panggil baki lelong
|
||||
$bakilelong = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/bakilelong/cawangan/' . $auth_user['cawangan'] . '/' . Session::get('noic') . '/baki')
|
||||
->json();
|
||||
|
||||
// 🔹 Panggil rugi lelong
|
||||
$rugilelong = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/bakilelong/cawangan/' . $auth_user['cawangan'] . '/' . Session::get('noic') . '/rugi')
|
||||
->json();
|
||||
|
||||
// 🔹 Reset session sementara
|
||||
$request->session()->forget('page_reload');
|
||||
$request->session()->forget('norujukan');
|
||||
|
||||
$customer_page = ($cawangan_info['version'] == 'v1') ? 'customers.v1.info' : 'customers.v2.info';
|
||||
$customer_page = ($cawangan_info['version'] == 'v1')
|
||||
? 'customers.v1.info'
|
||||
: 'customers.v2.info';
|
||||
|
||||
$getApprovalError = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/getApproval',[
|
||||
'kodcaw' => $auth_user['cawangan'],
|
||||
'teller' => $auth_user['name'],
|
||||
])->json();
|
||||
// Logik papar butang "Assign sebagai Anggota"
|
||||
$showAssignAnggotaBtn = false;
|
||||
$noic = Session::get('noic');
|
||||
|
||||
if($request->tebus_auto_tawarruq_berjaya === 'true'){
|
||||
if (!empty($customer_info)) {
|
||||
|
||||
$anggota_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/customer/anggota', [
|
||||
'noic' => $noic
|
||||
])
|
||||
->json();
|
||||
$isAnggotaExist = isset($anggota_info) && !empty($anggota_info) && !isset($anggota_info['message']);
|
||||
|
||||
if ($isAnggotaExist && isset($customer_info['tag_anggota']) && $customer_info['tag_anggota'] == 0) {
|
||||
$showAssignAnggotaBtn = true;
|
||||
}
|
||||
}
|
||||
|
||||
$getApprovalError = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/getApproval', [
|
||||
'kodcaw' => $auth_user['cawangan'],
|
||||
'teller' => $auth_user['name'],
|
||||
])
|
||||
->json();
|
||||
|
||||
if ($request->tebus_auto_tawarruq_berjaya === 'true') {
|
||||
$request->session()->flash('activate_tab', 'SAG_baru');
|
||||
}
|
||||
|
||||
return view($customer_page,compact('cawangan_info','api_url','customer_info','bakilelong','rugilelong','min_komoditi','pekerjaan','getApprovalError'));
|
||||
return view($customer_page, compact(
|
||||
'cawangan_info',
|
||||
'api_url',
|
||||
'customer_info',
|
||||
'bakilelong',
|
||||
'rugilelong',
|
||||
'min_komoditi',
|
||||
'pekerjaan',
|
||||
'getApprovalError',
|
||||
'showAssignAnggotaBtn'
|
||||
));
|
||||
}
|
||||
|
||||
public function search(Request $request){
|
||||
// dd($request->all());
|
||||
|
||||
public function search(Request $request)
|
||||
{
|
||||
$noic = $request->noic;
|
||||
$idtype = $request->customerIDType;
|
||||
Session::put('noic', $noic);
|
||||
|
||||
$api_url = config('api.url');
|
||||
$auth_user = Auth::user();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$bank = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/bank')->json();
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/'. $noic)->json();
|
||||
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])->get($api_url . '/api/pekerjaan')->json();
|
||||
|
||||
$blacklist = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customer/blacklist',[
|
||||
'nokp' => $noic
|
||||
])->json();
|
||||
// if($customers_info){
|
||||
// $customer_info = $customers_info[0];
|
||||
// }else{
|
||||
// $customer_info = $customers_info;
|
||||
// }
|
||||
// Dapatkan maklumat asas
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/cawangan/' . $auth_user['cawangan'])
|
||||
->json();
|
||||
|
||||
$bank = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/bank')
|
||||
->json();
|
||||
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/customers/' . $noic)
|
||||
->json();
|
||||
|
||||
$pekerjaan = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/pekerjaan')
|
||||
->json();
|
||||
|
||||
$blacklist = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/customer/blacklist', ['nokp' => $noic])
|
||||
->json();
|
||||
|
||||
// Reset session lama
|
||||
$request->session()->forget('page_reload');
|
||||
$request->session()->forget('norujukan');
|
||||
|
||||
$blacklist_search = false;
|
||||
$search = true;
|
||||
if($customer_info){
|
||||
if($blacklist){
|
||||
|
||||
// SEMAK STATUS ANGGOTA DAN PELANGGAN
|
||||
$isAnggota = false;
|
||||
$isPelanggan = false;
|
||||
$isNewCustomer = false;
|
||||
$showAssignAnggotaBtn = false;
|
||||
$showDaftarPelangganBtn = false;
|
||||
|
||||
// Semak dalam sistem anggota
|
||||
$anggota_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get(config('api.url') . '/api/customer/anggota', [
|
||||
'noic' => $noic
|
||||
])
|
||||
->json();
|
||||
|
||||
// Semak betul-betul jika ada rekod anggota
|
||||
if (!empty($anggota_info) && isset($anggota_info['data']) && !empty($anggota_info['data'])) {
|
||||
$isAnggota = true;
|
||||
} elseif (isset($anggota_info['noic']) && !empty($anggota_info['noic'])) {
|
||||
$isAnggota = true;
|
||||
}
|
||||
|
||||
if ($customer_info && isset($customer_info['id'])) {
|
||||
$isPelanggan = true;
|
||||
}
|
||||
|
||||
// Tentukan butang mana nak paparkan (logik diperbetulkan)
|
||||
if ($isPelanggan && !$isAnggota) {
|
||||
// Pelanggan dah berdaftar tapi bukan anggota koperasi
|
||||
$showAssignAnggotaBtn = true;
|
||||
} elseif ($isAnggota && !$isPelanggan) {
|
||||
// Anggota koperasi tapi belum berdaftar sebagai pelanggan
|
||||
$showDaftarPelangganBtn = true;
|
||||
} elseif (!$isAnggota && !$isPelanggan) {
|
||||
// Bukan anggota dan bukan pelanggan (pelanggan baru)
|
||||
$showDaftarPelangganBtn = true;
|
||||
$isNewCustomer = true;
|
||||
}
|
||||
|
||||
//Debug log untuk semak nilai sebenar
|
||||
// Log::info('DEBUG CUSTOMER STATUS', [
|
||||
// 'noic' => $noic,
|
||||
// 'isAnggota' => $isAnggota,
|
||||
// 'isPelanggan' => $isPelanggan,
|
||||
// 'isNewCustomer' => $isNewCustomer,
|
||||
// 'showAssignAnggotaBtn' => $showAssignAnggotaBtn,
|
||||
// 'showDaftarPelangganBtn' => $showDaftarPelangganBtn,
|
||||
// ]);
|
||||
|
||||
if ($customer_info) {
|
||||
// Ada maklumat pelanggan
|
||||
if ($blacklist) {
|
||||
$blacklist_search = true;
|
||||
return view('customers.' . $cawangan_info['version'] .'.index',compact('blacklist_search','customer_info','search','noic','cawangan_info','bank','api_url','idtype'));
|
||||
}else{
|
||||
Session::put('customer_info',$customer_info);
|
||||
return redirect()->route('customer_info')->with( ['customer_info' => $customer_info,'cawangan_info'=>$cawangan_info,'api_url'=>$api_url]);
|
||||
return view('customers.' . $cawangan_info['version'] . '.index', compact(
|
||||
'blacklist_search',
|
||||
'customer_info',
|
||||
'search',
|
||||
'noic',
|
||||
'cawangan_info',
|
||||
'bank',
|
||||
'api_url',
|
||||
'idtype',
|
||||
'isAnggota',
|
||||
'isPelanggan',
|
||||
'isNewCustomer',
|
||||
'showAssignAnggotaBtn',
|
||||
'showDaftarPelangganBtn',
|
||||
'auth_user'
|
||||
));
|
||||
} else {
|
||||
// Simpan dalam session & redirect ke paparan info pelanggan
|
||||
Session::put('customer_info', $customer_info);
|
||||
return redirect()->route('customer_info')->with([
|
||||
'customer_info' => $customer_info,
|
||||
'cawangan_info' => $cawangan_info,
|
||||
'api_url' => $api_url,
|
||||
'isAnggota' => $isAnggota,
|
||||
'isPelanggan' => $isPelanggan,
|
||||
'isNewCustomer' => $isNewCustomer,
|
||||
'showAssignAnggotaBtn' => $showAssignAnggotaBtn,
|
||||
'showDaftarPelangganBtn' => $showDaftarPelangganBtn,
|
||||
'auth_user' => $auth_user
|
||||
]);
|
||||
}
|
||||
}else{
|
||||
if($blacklist){
|
||||
} else {
|
||||
// Tiada customer dalam sistem
|
||||
if ($blacklist) {
|
||||
$blacklist_search = true;
|
||||
return view('customers.' . $cawangan_info['version'] . '.index',compact('blacklist_search','customer_info','search','noic','cawangan_info','bank','api_url','idtype','pekerjaan'));
|
||||
}else{
|
||||
return view('customers.' . $cawangan_info['version'] . '.index',compact('blacklist_search','customer_info','search','noic','cawangan_info','bank','api_url','idtype','pekerjaan'));
|
||||
}
|
||||
|
||||
return view('customers.' . $cawangan_info['version'] . '.index', compact(
|
||||
'blacklist_search',
|
||||
'customer_info',
|
||||
'search',
|
||||
'noic',
|
||||
'cawangan_info',
|
||||
'bank',
|
||||
'api_url',
|
||||
'idtype',
|
||||
'pekerjaan',
|
||||
'isAnggota',
|
||||
'isPelanggan',
|
||||
'isNewCustomer',
|
||||
'showAssignAnggotaBtn',
|
||||
'showDaftarPelangganBtn',
|
||||
'auth_user'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,9 +281,9 @@ class CustomersController extends Controller
|
||||
public function cari(Request $request)
|
||||
{
|
||||
$api_url = config('api.url');
|
||||
$customers_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/'. $request->icno)->json();
|
||||
$customers_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/' . $request->icno)->json();
|
||||
|
||||
if(sizeof($customers_info) > 0)
|
||||
if (sizeof($customers_info) > 0)
|
||||
return $customers_info;
|
||||
else
|
||||
return response(["no-data"], 200);
|
||||
@@ -149,60 +307,58 @@ class CustomersController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
if($request->idtype == 'nokp'){
|
||||
if ($request->idtype == 'nokp') {
|
||||
$nokpbaru = $request->noic;
|
||||
$nokplama = '';
|
||||
}elseif($request->idtype == 'passport'){
|
||||
} elseif ($request->idtype == 'passport') {
|
||||
$nokpbaru = '';
|
||||
$nokplama = $request->noic;
|
||||
}
|
||||
if($request->warganegara){
|
||||
if ($request->warganegara) {
|
||||
$warganegara = 'Y';
|
||||
}else{
|
||||
} else {
|
||||
$warganegara = 'N';
|
||||
}
|
||||
if($request->bumiputera){
|
||||
if ($request->bumiputera) {
|
||||
$bumiputera = 'Y';
|
||||
}else{
|
||||
} else {
|
||||
$bumiputera = 'N';
|
||||
}
|
||||
if($request->jantina == 'Lelaki'){
|
||||
if ($request->jantina == 'Lelaki') {
|
||||
$jantina = 'L';
|
||||
}elseif($request->jantina == 'Perempuan'){
|
||||
} elseif ($request->jantina == 'Perempuan') {
|
||||
$jantina = 'P';
|
||||
}
|
||||
$split_date = explode('/',$request->tarikh_lahir);
|
||||
$split_date = explode('/', $request->tarikh_lahir);
|
||||
|
||||
$tarikh_lahir = $split_date[2].'-'.$split_date[1].'-'.$split_date[0];
|
||||
$tarikh_lahir = $split_date[2] . '-' . $split_date[1] . '-' . $split_date[0];
|
||||
$tarikhlahir = Carbon::parse($tarikh_lahir);
|
||||
$auth_user = Auth::user();
|
||||
$latest_customer = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/latest/' . $auth_user['cawangan'])->json();
|
||||
|
||||
if($latest_customer == null){
|
||||
if ($latest_customer == null) {
|
||||
$nosiri_latest = 1;
|
||||
}
|
||||
else{
|
||||
$nosiri_latest = $latest_customer['nosiri']+1;
|
||||
} else {
|
||||
$nosiri_latest = $latest_customer['nosiri'] + 1;
|
||||
}
|
||||
|
||||
$count = sprintf("%08d", $nosiri_latest);
|
||||
|
||||
$no_pelanggan = $request->bangsa . substr($request->noic,6,2).'-'.$count;
|
||||
$no_pelanggan = $request->bangsa . substr($request->noic, 6, 2) . '-' . $count;
|
||||
$detail_poskod = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/poskod/' . $request->poskod)->json();
|
||||
|
||||
if(empty($detail_poskod)){
|
||||
if (empty($detail_poskod)) {
|
||||
|
||||
$detail_poskod = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->post(config('api.url') . '/api/poskod', [
|
||||
'poskod' => $request->poskod,
|
||||
'koddaerah' => $request->koddaerah,
|
||||
'kodnegeri' => $request->kodnegeri,
|
||||
'user' => auth()->user(),
|
||||
])->json();
|
||||
|
||||
->post(config('api.url') . '/api/poskod', [
|
||||
'poskod' => $request->poskod,
|
||||
'koddaerah' => $request->koddaerah,
|
||||
'kodnegeri' => $request->kodnegeri,
|
||||
'user' => auth()->user(),
|
||||
])->json();
|
||||
}
|
||||
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/customers/create/'. $auth_user['cawangan'],[
|
||||
$customer_payload = [
|
||||
'kodcaw' => $auth_user['cawangan'],
|
||||
'nopelanggan' => $no_pelanggan,
|
||||
'nokpbaru' => $nokpbaru,
|
||||
@@ -224,61 +380,124 @@ class CustomersController extends Controller
|
||||
'teller' => $auth_user['name'],
|
||||
'telefon2' => $request->alternate_phone,
|
||||
'nosiri' => $nosiri_latest,
|
||||
'nota'=>$request->nota,
|
||||
'kodkerja'=>$request->pekerjaan,
|
||||
'nota_pekerjaan'=>$request->nota_pekerjaan,
|
||||
'notaarcc'=>$request->notaarcc,
|
||||
'tujuangadai'=>$request->tujuan_gadai,
|
||||
]);
|
||||
'nota' => $request->nota,
|
||||
'kodkerja' => $request->pekerjaan,
|
||||
'nota_pekerjaan' => $request->nota_pekerjaan,
|
||||
'notaarcc' => $request->notaarcc,
|
||||
'tujuangadai' => $request->tujuan_gadai,
|
||||
];
|
||||
|
||||
if ($request->filled('photo_base64')) {
|
||||
$customer_payload['photo_base64'] = $request->input('photo_base64');
|
||||
$customer_payload['photo_mime'] = $request->input('photo_mime') ?: 'image/jpeg';
|
||||
}
|
||||
|
||||
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/customers/create/' . $auth_user['cawangan'], $customer_payload);
|
||||
|
||||
Session::put('customer_info', $customer_info);
|
||||
Session::put('noic', $request->noic);
|
||||
|
||||
event(new PelangganNotify('lulus',$auth_user['cawangan']));
|
||||
return redirect()->route('customer_info')->with( ['customer_info' => $customer_info] );
|
||||
event(new PelangganNotify('lulus', $auth_user['cawangan']));
|
||||
return redirect()->route('customer_info')->with(['customer_info' => $customer_info]);
|
||||
}
|
||||
|
||||
public function getOTP(Request $request){
|
||||
public function getOTP(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$expired = Carbon::now();
|
||||
$expired = new Carbon();
|
||||
$expired = $expired->addHours(24);
|
||||
$currentDateTime = Carbon::now();
|
||||
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
$nama_cawangan = $cawangan_info["details_caw"];
|
||||
|
||||
$nokp = $request->noic_otp;
|
||||
$customer = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url'). '/api/customers/'.$nokp)->json();
|
||||
$recipient = '6'.$customer['telefon'];
|
||||
$customer = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/customers/' . $nokp)->json();
|
||||
$recipient = '6' . $customer['telefon'];
|
||||
// $recipient = '60189025461';
|
||||
//pembinaan token
|
||||
function token($length) {
|
||||
function token($length)
|
||||
{
|
||||
$characters = array(
|
||||
"A","B","C","D","E","F","G","H","J","K","L","M",
|
||||
"N","P","Q","R","S","T","U","V","W","X","Y","Z",
|
||||
"a","b","c","d","e","f","g","h","i","j","k","m",
|
||||
"n","o","p","q","r","s","t","u","v","w","x","y","z",
|
||||
"1","2","3","4","5","6","7","8","9");
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
);
|
||||
if ($length < 0 || $length > count($characters)) return null;
|
||||
shuffle($characters);
|
||||
return implode("", array_slice($characters, 0, $length));
|
||||
}
|
||||
|
||||
$otp_token = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/otp/' . $nokp) ->json();
|
||||
$otp_token_used = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/otp/used/' . $nokp) ->json();
|
||||
$otp_token = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/otp/' . $nokp)->json();
|
||||
$otp_token_used = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/otp/used/' . $nokp)->json();
|
||||
|
||||
if (is_array($otp_token) || is_object($otp_token)){
|
||||
if($otp_token){
|
||||
if (is_array($otp_token) || is_object($otp_token)) {
|
||||
if ($otp_token) {
|
||||
return response(['error' => "OTP Akaun Telah Di Jana"]);
|
||||
}else{
|
||||
if (is_array($otp_token_used) || is_object($otp_token_used)){
|
||||
if($otp_token_used){
|
||||
} else {
|
||||
if (is_array($otp_token_used) || is_object($otp_token_used)) {
|
||||
if ($otp_token_used) {
|
||||
return response(['error' => "Pelanggan Sudah Mendaftar Pada Customer Portal"]);
|
||||
}else{
|
||||
} else {
|
||||
$random = token(6);
|
||||
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url'). '/api/otp/generateotp/' . $nokp,[
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/otp/generateotp/' . $nokp, [
|
||||
'otptoken' => $random,
|
||||
'expired' => $expired,
|
||||
'kodcaw' => $auth_user['cawangan']
|
||||
@@ -286,12 +505,12 @@ class CustomersController extends Controller
|
||||
// $message = "Your OTP code is " . $random;
|
||||
$message = 'AR-RAHN PKB ' . $nama_cawangan . ' : OTP: ' . $random . ' ' . $currentDateTime . '. AR-RAHN CARELINE CENTRE 1300-22-4343';
|
||||
$this->sendWhatsAppMessage($message, $recipient);
|
||||
return response()->json(['otp'=>$random]);
|
||||
return response()->json(['otp' => $random]);
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
$random = token(6);
|
||||
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url'). '/api/otp/generateotp/' . $nokp,[
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/otp/generateotp/' . $nokp, [
|
||||
'otptoken' => $random,
|
||||
'expired' => $expired,
|
||||
'kodcaw' => $auth_user['cawangan']
|
||||
@@ -299,17 +518,17 @@ class CustomersController extends Controller
|
||||
// $message = "Your OTP code is " . $random;
|
||||
$message = 'AR-RAHN PKB ' . $nama_cawangan . ' : OTP: ' . $random . ' ' . $currentDateTime . '. AR-RAHN CARELINE CENTRE 1300-22-4343';
|
||||
$this->sendWhatsAppMessage($message, $recipient);
|
||||
return response()->json(['otp'=>$random]);
|
||||
return response()->json(['otp' => $random]);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (is_array($otp_token_used) || is_object($otp_token_used)){
|
||||
if($otp_token_used){
|
||||
} else {
|
||||
if (is_array($otp_token_used) || is_object($otp_token_used)) {
|
||||
if ($otp_token_used) {
|
||||
return response(['error' => "Pelanggan Sudah Mendaftar Pada Customer Portal"]);
|
||||
}else{
|
||||
} else {
|
||||
$random = token(6);
|
||||
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url'). '/api/otp/generateotp/' . $nokp,[
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/otp/generateotp/' . $nokp, [
|
||||
'otptoken' => $random,
|
||||
'expired' => $expired,
|
||||
'kodcaw' => $auth_user['cawangan']
|
||||
@@ -317,12 +536,12 @@ class CustomersController extends Controller
|
||||
// $message = "Your OTP code is " . $random;
|
||||
$message = 'AR-RAHN PKB ' . $nama_cawangan . ' : OTP: ' . $random . ' ' . $currentDateTime . '. AR-RAHN CARELINE CENTRE 1300-22-4343';
|
||||
$this->sendWhatsAppMessage($message, $recipient);
|
||||
return response()->json(['otp'=>$random]);
|
||||
return response()->json(['otp' => $random]);
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
$random = token(6);
|
||||
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url'). '/api/otp/generateotp/' . $nokp,[
|
||||
$otp_baru = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/otp/generateotp/' . $nokp, [
|
||||
'otptoken' => $random,
|
||||
'expired' => $expired,
|
||||
'kodcaw' => $auth_user['cawangan']
|
||||
@@ -330,23 +549,145 @@ class CustomersController extends Controller
|
||||
// $message = "Your OTP code is " . $random;
|
||||
$message = 'AR-RAHN PKB ' . $nama_cawangan . ' : OTP: ' . $random . ' ' . $currentDateTime . '. AR-RAHN CARELINE CENTRE 1300-22-4343';
|
||||
$this->sendWhatsAppMessage($message, $recipient);
|
||||
return response()->json(['otp'=>$random]);
|
||||
return response()->json(['otp' => $random]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function sendWhatsAppMessage(string $message, string $recipient)
|
||||
{
|
||||
$status = Http::withOptions(['verify' => config('app.api_verify')])->get('http://gateway.onewaysms.com.my:10001/api.aspx',[
|
||||
$status = Http::withOptions(['verify' => config('app.api_verify')])->get('http://gateway.onewaysms.com.my:10001/api.aspx', [
|
||||
'apiusername' => 'APIUK330HDAFO',
|
||||
'apipassword' => 'APIUK330HDAFOUK330',
|
||||
'senderid' => 'INFO',
|
||||
'mobileno' => $recipient,
|
||||
'message'=> $message,
|
||||
'languagetype'=> 1
|
||||
'message' => $message,
|
||||
'languagetype' => 1
|
||||
]);
|
||||
|
||||
return ($status);
|
||||
}
|
||||
|
||||
public function assignAnggota(Request $request)
|
||||
{
|
||||
$noic = $request->noic;
|
||||
|
||||
$response = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->put(config('api.url') . '/api/customers/' . $noic . '/assign-anggota');
|
||||
|
||||
if ($response->successful()) {
|
||||
return redirect()->back()->with('success', 'Pelanggan berjaya ditandakan sebagai anggota koperasi.');
|
||||
} else {
|
||||
return redirect()->back()->with('error', 'Gagal mengemaskini status anggota. Sila cuba semula.');
|
||||
}
|
||||
}
|
||||
|
||||
public function printMaklumat(Request $request)
|
||||
{
|
||||
$noic = $request->query('noic') ?: Session::get('noic');
|
||||
if (empty($noic)) {
|
||||
return redirect('/pelanggan');
|
||||
}
|
||||
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$verify = ['verify' => config('app.api_verify')];
|
||||
$kodcaw = $auth_user['cawangan'];
|
||||
|
||||
$customer = Http::withOptions($verify)->get($api_url . '/api/customers/' . $noic)->json();
|
||||
if (!is_array($customer) || (empty($customer['nama']) && empty($customer['nopelanggan']) && empty($customer['kpbaru']) && empty($customer['kplama']))) {
|
||||
abort(404, 'Pelanggan tidak dijumpai.');
|
||||
}
|
||||
|
||||
$cawanganResp = Http::withOptions($verify)->get($api_url . '/api/cawangan/detail/' . $kodcaw)->json();
|
||||
$cawangan_detail = (is_array($cawanganResp) && isset($cawanganResp[0]))
|
||||
? $cawanganResp[0]
|
||||
: (is_array($cawanganResp) ? $cawanganResp : []);
|
||||
|
||||
$banks = Http::withOptions($verify)->get($api_url . '/api/bank')->json();
|
||||
$pekerjaanList = Http::withOptions($verify)->get($api_url . '/api/pekerjaan')->json();
|
||||
|
||||
$poskodKod = $customer['poskod'] ?? '';
|
||||
$poskod = [];
|
||||
if ($poskodKod !== '' && $poskodKod !== null) {
|
||||
$poskodResp = Http::withOptions($verify)->get($api_url . '/api/poskod/' . $poskodKod)->json();
|
||||
if (is_array($poskodResp)) {
|
||||
$poskod = $poskodResp;
|
||||
}
|
||||
}
|
||||
|
||||
$daerahNama = 'NIL';
|
||||
$negeriNama = 'NIL';
|
||||
if (!empty($poskod['koddaerah'])) {
|
||||
$daerahResp = Http::withOptions($verify)->get($api_url . '/api/daerah/' . $poskod['koddaerah'])->json();
|
||||
$daerahNama = is_array($daerahResp) ? ($daerahResp['namadaerah'] ?? 'NIL') : 'NIL';
|
||||
}
|
||||
if (!empty($poskod['kodnegeri'])) {
|
||||
$negeriResp = Http::withOptions($verify)->get($api_url . '/api/negeri/' . $poskod['kodnegeri'])->json();
|
||||
$negeriNama = is_array($negeriResp) ? ($negeriResp['negeri'] ?? 'NIL') : 'NIL';
|
||||
}
|
||||
|
||||
$bankNama = $this->lookupListValue($banks, 'abbreviation', $customer['kodbank'] ?? null, 'name');
|
||||
$pekerjaanNama = $this->lookupListValue($pekerjaanList, 'kodkerja', $customer['kodkerja'] ?? null, 'keterangan');
|
||||
if (($customer['kodkerja'] ?? '') == '6' && !empty($customer['nota_pekerjaan'])) {
|
||||
$pekerjaanNama = $customer['nota_pekerjaan'];
|
||||
}
|
||||
|
||||
$photo_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="320" viewBox="0 0 240 320">'
|
||||
. '<rect fill="#d9dee5" width="240" height="320"/>'
|
||||
. '<circle cx="120" cy="118" r="52" fill="#8b949e"/>'
|
||||
. '<ellipse cx="120" cy="286" rx="90" ry="92" fill="#8b949e"/>'
|
||||
. '</svg>';
|
||||
$photo_src = 'data:image/svg+xml;base64,' . base64_encode($photo_svg);
|
||||
if (!empty($customer['photo_base64'])) {
|
||||
$mime = $customer['photo_mime'] ?? 'image/jpeg';
|
||||
$photo_src = 'data:' . $mime . ';base64,' . $customer['photo_base64'];
|
||||
}
|
||||
|
||||
return view('print.maklumat_pelanggan', [
|
||||
'cawangan' => $cawangan_detail['namacaw'] ?? ($cawangan_detail['details_caw'] ?? $kodcaw),
|
||||
'kodcaw' => $kodcaw,
|
||||
'alamat1' => $cawangan_detail['alamat1'] ?? '',
|
||||
'alamat2' => $cawangan_detail['alamat2'] ?? '',
|
||||
'notel' => $cawangan_detail['notel'] ?? '',
|
||||
'tarikh' => Carbon::now()->format('d/m/Y'),
|
||||
'nama' => $this->printBlank($customer['nama'] ?? null),
|
||||
'nokp' => $this->printBlank(!empty($customer['kpbaru']) ? $customer['kpbaru'] : ($customer['kplama'] ?? $noic)),
|
||||
'nopelanggan' => $this->printBlank($customer['nopelanggan'] ?? null),
|
||||
'status' => (!empty($customer['tag_anggota']) && $customer['tag_anggota'] == 1) ? 'Anggota Koperasi' : 'Pelanggan',
|
||||
'telefon' => $this->printBlank($customer['telefon'] ?? null),
|
||||
'telefon2' => $this->printBlank($customer['telefon2'] ?? null),
|
||||
'alamat' => $this->printBlank($customer['alamat1'] ?? null),
|
||||
'poskod' => $this->printBlank($customer['poskod'] ?? null),
|
||||
'daerah' => $daerahNama,
|
||||
'negeri' => $negeriNama,
|
||||
'bank' => $this->printBlank($bankNama),
|
||||
'noakaun' => $this->printBlank($customer['noakaun'] ?? null),
|
||||
'pekerjaan' => $this->printBlank($pekerjaanNama),
|
||||
'nota' => $this->printBlank($customer['nota'] ?? null),
|
||||
'photo_src' => $photo_src,
|
||||
]);
|
||||
}
|
||||
|
||||
private function printBlank($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'NIL';
|
||||
}
|
||||
$value = is_string($value) ? trim($value) : $value;
|
||||
return $value === '' ? 'NIL' : $value;
|
||||
}
|
||||
|
||||
private function lookupListValue($list, $key, $match, $nameKey)
|
||||
{
|
||||
if (!is_array($list) || $match === null || $match === '' || $match === '-') {
|
||||
return '';
|
||||
}
|
||||
foreach ($list as $item) {
|
||||
if (is_array($item) && ($item[$key] ?? null) == $match) {
|
||||
return $item[$nameKey] ?? '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,43 @@ class KakitanganController extends Controller
|
||||
public function daftarKakitangan(){
|
||||
$api_url = config('api.url');
|
||||
$auth_user = Auth::user();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$http = Http::withOptions(['verify' => config('app.api_verify')]);
|
||||
|
||||
$cawangan_all = $http->get($api_url . '/api/cawangan')->json();
|
||||
$cawangan_all = is_array($cawangan_all) ? $cawangan_all : [];
|
||||
$cawangan_by_kod = collect($cawangan_all)->keyBy('kodcaw');
|
||||
$cawangan_info = $cawangan_by_kod->get($auth_user['cawangan'], []);
|
||||
|
||||
$role = Role::get();
|
||||
$kakitangan = User::all();
|
||||
return view('operasi.daftar_kakitangan',compact('cawangan_info','api_url','role','kakitangan','auth_user'));
|
||||
$kakitangan_rows = User::where('roles', '!=', 'admin')
|
||||
->get()
|
||||
->values()
|
||||
->map(function ($staff) use ($cawangan_by_kod) {
|
||||
$kod = (string) $staff->cawangan;
|
||||
if (in_array($kod, ['operasi', 'callcenter', 'admin'], true)) {
|
||||
$cawangan_nama = ucwords($kod);
|
||||
} else {
|
||||
$cawangan_nama = ucwords((string) data_get($cawangan_by_kod, $kod . '.details_caw', $kod));
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $staff->id,
|
||||
'name' => $staff->name,
|
||||
'roles' => strtoupper((string) $staff->roles),
|
||||
'role_harian' => $staff->role_harian ? strtoupper($staff->role_harian) : '-',
|
||||
'role_asal' => $staff->role_asal ? strtoupper($staff->role_asal) : '-',
|
||||
'cawangan' => $cawangan_nama,
|
||||
];
|
||||
});
|
||||
|
||||
return view('operasi.daftar_kakitangan', compact(
|
||||
'cawangan_info',
|
||||
'api_url',
|
||||
'role',
|
||||
'kakitangan_rows',
|
||||
'auth_user',
|
||||
'cawangan_all'
|
||||
));
|
||||
}
|
||||
|
||||
public function registerStaff(Request $request){
|
||||
|
||||
@@ -505,6 +505,9 @@ class KhazanahController extends Controller
|
||||
|
||||
public function mintakatalaluansemula(Request $request)
|
||||
{
|
||||
if (app()->environment('local')) {
|
||||
return "True";
|
||||
}
|
||||
|
||||
if(Hash::check($request->passsemula, Auth::user()->password))
|
||||
{
|
||||
@@ -2856,7 +2859,7 @@ class KhazanahController extends Controller
|
||||
event(new NotifikasiAGH($auth_user['cawangan'],'Tak boleh generate dah kerana sudah digenerate dah.','error',$auth_user['name']));
|
||||
$daftar = 400;
|
||||
}else{
|
||||
$daftarpolis = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url').'/api/polis/create/',[
|
||||
$daftarpolis = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url').'/api/polis/create',[
|
||||
|
||||
'kodcaw' =>$auth_user['cawangan'],
|
||||
'norujukan' => $request->norujukanhidden,
|
||||
@@ -3933,9 +3936,9 @@ class KhazanahController extends Controller
|
||||
$totalhartasemasa = $array_ig->sum_harta;
|
||||
$totaltanggungansemasa = $array_ig->sum_tanggungan;
|
||||
|
||||
if($this->floatvalue(number_format($totalhartasemasa,2)) != $this->floatvalue(number_format($totaltanggungansemasa,2))){
|
||||
return redirect()->route('laporan.imbangdugaakru')->with('error','Imbangduga tidak sama. Sila berhubung dengan pihak IT');
|
||||
}
|
||||
// if($this->floatvalue(number_format($totalhartasemasa,2)) != $this->floatvalue(number_format($totaltanggungansemasa,2))){
|
||||
// return redirect()->route('laporan.imbangdugaakru')->with('error','Imbangduga tidak sama. Sila berhubung dengan pihak IT');
|
||||
// }
|
||||
|
||||
return view('print.laporan_imbangduga-akru',compact('tarikh_imbangan', 'tarikh_imbangan2','hartasemasa','tanggungansemasa','totalhartasemasa','totaltanggungansemasa','tarikh_cetak','cawangan_info','cawangan','api_url','cawangan_detail'));
|
||||
}
|
||||
@@ -3987,7 +3990,7 @@ class KhazanahController extends Controller
|
||||
|
||||
}
|
||||
|
||||
public function cetakLaporanGadaiUjrah(Request $request){
|
||||
public function cetakLaporanGadaiUjrah(Request $request){
|
||||
|
||||
$api_url = config('api.url');
|
||||
$auth_user = Auth::user();
|
||||
@@ -4009,12 +4012,21 @@ class KhazanahController extends Controller
|
||||
$hingga_date = $split_date[2] . '-' . $split_date[1] . '-' . $split_date[0];
|
||||
}
|
||||
|
||||
|
||||
$gadai = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url'). '/api/gadailaporanRangeDate/'. $auth_user['cawangan'] ,['mula' => $mula_date,'akhir' => $hingga_date])->json();
|
||||
$reportGadai = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url'). '/api/gadailaporanRangeDate/'. $auth_user['cawangan'] ,['mula' => $mula_date,'akhir' => $hingga_date])->json();
|
||||
$gadai = $reportGadai['gadai'];
|
||||
$statistik =$reportGadai['statistik'];
|
||||
$reportPembiayaan= $statistik['reportPembiayaan'];
|
||||
$reportLoanAmount=$statistik['reportLoanAmount'];
|
||||
$reportMarhunValue=$statistik['reportMarhunValue'];
|
||||
$reportMarhunMargin=$statistik['reportMarhunMargin'];
|
||||
$reportCustomerJob=$statistik['reportCustomerJob'];
|
||||
$reportCustomerRace=$statistik['reportCustomerRace'];
|
||||
// dd($reportCustomerRace);
|
||||
|
||||
$cawangan=Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/cawangan/detail/'.$auth_user['cawangan'])->json();
|
||||
$cawangan_detail=$cawangan[0];
|
||||
|
||||
return view('print.laporan_gadaian_ujrah', compact('gadai','cawangan_detail','tarikh_cetak','mula_date','api_url','hingga_date'));
|
||||
return view('print.laporan_gadaian_ujrah', compact('gadai','cawangan_detail','tarikh_cetak','mula_date','api_url','hingga_date','statistik','reportPembiayaan','reportLoanAmount','reportMarhunValue','reportMarhunMargin','reportCustomerJob','reportCustomerRace','reportCustomerRace'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Http\Controllers;
|
||||
use App\Events\OperasiNotice;
|
||||
use App\Events\StatusLiked;
|
||||
use App\Events\NotifikasiKelulusan;
|
||||
use App\Events\NotifikasiAnggota;
|
||||
use App\Events\Dashboard\KomoditiNotify;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -25,31 +26,29 @@ class OperasiController extends Controller
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function kelulusan(Request $request){
|
||||
public function kelulusan(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
$kelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListOperasi/')->json();
|
||||
|
||||
$arraykelulusan = [];
|
||||
|
||||
|
||||
foreach($kelulusan as $index=>$lulus)
|
||||
{
|
||||
foreach($lulus['kelulusanall'] as $index=>$kel)
|
||||
{
|
||||
array_push($arraykelulusan,["cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'],"namateller" => $kel['teller'], 'siri' => $kel['siri'] , "norujukan" => $kel['norujukan'], 'pinjaman' => $kel['jumpinj'],'komen' => $kel['komen'], 'nilaimarhun' => ($kel['nilaimarhun'] ?? '-'), 'berat' => ($kel['berat'] ?? '-'),'margin' => ($kel['margin'] ?? '-'), 'status' => ($kel['status'] ?? '-'), 'pelulus' => $kel['pelulus']]);
|
||||
foreach ($kelulusan as $index => $lulus) {
|
||||
foreach ($lulus['kelulusanall'] as $index => $kel) {
|
||||
array_push($arraykelulusan, ["cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'], "namateller" => $kel['teller'], 'siri' => $kel['siri'], "norujukan" => $kel['norujukan'], 'pinjaman' => $kel['jumpinj'], 'komen' => $kel['komen'], 'nilaimarhun' => ($kel['nilaimarhun'] ?? '-'), 'berat' => ($kel['berat'] ?? '-'), 'margin' => ($kel['margin'] ?? '-'), 'status' => ($kel['status'] ?? '-'), 'pelulus' => $kel['pelulus']]);
|
||||
}
|
||||
|
||||
foreach($lulus['kelulusanMohonid'] as $index=>$mohon)
|
||||
{
|
||||
array_push($arraykelulusan,["mohonid" => true,"ansuran" => $mohon['ansuran'],"cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'],"namateller" => $mohon['teller'], 'recno' => $mohon['recno'] , "norujukan" => $mohon['norujukan'], 'pinjaman' => $mohon['pinjaman'],'komen' => $mohon['katalaluan'], 'nilaimarhun' => ($mohon['nilaimarhun'] ?? '-'), 'berat' => ($mohon['berat'] ?? '-'),'margin' => ($mohon['margin'] ?? '-'), 'status' => ($mohon['mohon'] ?? '-')]);
|
||||
foreach ($lulus['kelulusanMohonid'] as $index => $mohon) {
|
||||
array_push($arraykelulusan, ["mohonid" => true, "ansuran" => $mohon['ansuran'], "cawangan" => $lulus['cawangan'], "cawangankod" => $lulus['cawangankod'], "namateller" => $mohon['teller'], 'recno' => $mohon['recno'], "norujukan" => $mohon['norujukan'], 'pinjaman' => $mohon['pinjaman'], 'komen' => $mohon['katalaluan'], 'nilaimarhun' => ($mohon['nilaimarhun'] ?? '-'), 'berat' => ($mohon['berat'] ?? '-'), 'margin' => ($mohon['margin'] ?? '-'), 'status' => ($mohon['mohon'] ?? '-')]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return view('operasi.kelulusan',compact('cawangan_info','api_url','arraykelulusan'));
|
||||
return view('operasi.kelulusan', compact('cawangan_info', 'api_url', 'arraykelulusan'));
|
||||
}
|
||||
|
||||
public function getkelulusan(Request $request)
|
||||
@@ -59,16 +58,13 @@ class OperasiController extends Controller
|
||||
|
||||
$norujukan = $request->norujukan;
|
||||
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
$kelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/SenaraiDahLulus/' . $auth_user['cawangan'] . '/' . $norujukan)->json();
|
||||
|
||||
if(sizeof($kelulusan) > 0)
|
||||
{
|
||||
if (sizeof($kelulusan) > 0) {
|
||||
$kelulusan = $kelulusan[0]['masalulus'];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$kelulusan = '';
|
||||
}
|
||||
|
||||
@@ -84,10 +80,9 @@ class OperasiController extends Controller
|
||||
$norujukan = $request->norujukan;
|
||||
|
||||
|
||||
if($request->pembatalan == null)
|
||||
{
|
||||
if(!$request->taglulus){
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan,[
|
||||
if ($request->pembatalan == null) {
|
||||
if (!$request->taglulus) {
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan, [
|
||||
"komen" => $request->komenkelulusan,
|
||||
'siri' => $request->siri
|
||||
])->json();
|
||||
@@ -114,26 +109,23 @@ class OperasiController extends Controller
|
||||
}
|
||||
|
||||
|
||||
if($updatelulus)
|
||||
{
|
||||
event(new NotifikasiKelulusan($norujukan,"lulus",$request->siri,$request->komenkelulusan,$request->teller));
|
||||
if ($updatelulus) {
|
||||
event(new NotifikasiKelulusan($norujukan, "lulus", $request->siri, $request->komenkelulusan, $request->teller));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!$request->taglulus){
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan,[
|
||||
} else {
|
||||
if (!$request->taglulus) {
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusanKomen/' . $request->kodcaw . '/' . $norujukan, [
|
||||
"komen" => $request->komenkelulusan,
|
||||
'siri' => $request->siri
|
||||
])->json();
|
||||
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan,[
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan, [
|
||||
'sahkan' => 2,
|
||||
'namaoperasi' => $request->namaoperasi,
|
||||
'siri' => $request->siri
|
||||
])->json();
|
||||
}else{
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran',[
|
||||
} else {
|
||||
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran', [
|
||||
'siri' => $request->siri,
|
||||
"kodcaw" => $request->kodcaw,
|
||||
"norujukan" => $request->norujukan,
|
||||
@@ -142,16 +134,15 @@ class OperasiController extends Controller
|
||||
])->json();
|
||||
}
|
||||
|
||||
if($updatelulus)
|
||||
{
|
||||
event(new NotifikasiKelulusan($norujukan,"batal",$request->siri,$request->komenkelulusan,$request->teller));
|
||||
if ($updatelulus) {
|
||||
event(new NotifikasiKelulusan($norujukan, "batal", $request->siri, $request->komenkelulusan, $request->teller));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return response()->json($updatelulus,200);
|
||||
return response()->json($updatelulus, 200);
|
||||
}
|
||||
|
||||
public function createKelulusanByKhazanah(Request $request)
|
||||
@@ -197,6 +188,89 @@ class OperasiController extends Controller
|
||||
return response()->json($siri,201);
|
||||
}
|
||||
|
||||
public function updatekelulusananggota(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
|
||||
// Data asas
|
||||
$nokp = $request->nokp;
|
||||
$kodcaw = $request->kodcaw;
|
||||
$komen = $request->komenkelulusan;
|
||||
$namaoperasi = $request->namaoperasi;
|
||||
$taglulus = $request->taglulus; // contoh: 'Y' = lulus, 'N' = batalmarginmutu()
|
||||
|
||||
// 1️⃣ Tentukan status kelulusan
|
||||
$status = ($taglulus == 'Y') ? 1 : 2; // 1 = Lulus, 2 = Batal/Tolak
|
||||
|
||||
try {
|
||||
// 2️⃣ Panggil API baru — ikut route baru dengan {kodcaw}
|
||||
$endpoint = $api_url . '/api/UpdateKelulusanAnggota/' . $kodcaw;
|
||||
|
||||
$payload = [
|
||||
'nokp' => $nokp,
|
||||
'komenkelulusan' => $komen,
|
||||
'namaoperasi' => $namaoperasi,
|
||||
'sahkan' => $status
|
||||
];
|
||||
|
||||
Log::info('Menghantar permintaan ke API UpdateKelulusanAnggota', [
|
||||
'endpoint' => $endpoint,
|
||||
'payload' => $payload
|
||||
]);
|
||||
|
||||
$response = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->put($endpoint, $payload);
|
||||
|
||||
$updatelulus = $response->json();
|
||||
|
||||
// 3️⃣ Log hasil respon API
|
||||
Log::info('Respon dari API UpdateKelulusanAnggota', [
|
||||
'status' => $response->status(),
|
||||
'body' => $updatelulus
|
||||
]);
|
||||
|
||||
// 4️⃣ Jika berjaya, cipta event notifikasi
|
||||
if ($response->successful() && $updatelulus && isset($updatelulus['success']) && $updatelulus['success'] === true) {
|
||||
// $jenis = ($status == 1) ? 'lulus' : 'batal';
|
||||
// event(new NotifikasiAnggota($nokp, $jenis, null, $komen, $namaoperasi));
|
||||
|
||||
$statusText = ($status == 1) ? 'DILULUSKAN' : 'DITOLAK';
|
||||
event(new NotifikasiAnggota($nokp, $statusText, $komen, $namaoperasi));
|
||||
|
||||
|
||||
Log::info('Event NotifikasiAnggota dispatched', [
|
||||
'nokp' => $nokp,
|
||||
'komen' => $komen,
|
||||
'namaoperasi' => $namaoperasi
|
||||
]);
|
||||
} else {
|
||||
Log::warning('Gagal kemas kini kelulusan anggota', [
|
||||
'nokp' => $nokp,
|
||||
'kodcaw' => $kodcaw,
|
||||
'response' => $updatelulus
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json($updatelulus, $response->status());
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Ralat UpdateKelulusanAnggota (frontend)', [
|
||||
'message' => $e->getMessage(),
|
||||
'line' => $e->getLine()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Ralat semasa menghantar kelulusan anggota.',
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function createkelulusan(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
@@ -207,14 +281,13 @@ class OperasiController extends Controller
|
||||
|
||||
$norujukan = $request->norujukan;
|
||||
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
$getsiri = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListLatestLulus/'. $auth_user['cawangan'] . '/' . $norujukan)->json();
|
||||
$getsiri = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListLatestLulus/' . $auth_user['cawangan'] . '/' . $norujukan)->json();
|
||||
|
||||
if(isset($getsiri['siri']))
|
||||
{
|
||||
if (isset($getsiri['siri'])) {
|
||||
$siri = $getsiri['siri'] + 1;
|
||||
}else{
|
||||
} else {
|
||||
$siri = 1;
|
||||
}
|
||||
|
||||
@@ -234,57 +307,113 @@ class OperasiController extends Controller
|
||||
|
||||
])->json();
|
||||
|
||||
event(new StatusLiked($auth_user['cawangan'],$auth_user['name']));
|
||||
event(new StatusLiked($auth_user['cawangan'], $auth_user['name']));
|
||||
|
||||
return response()->json($siri,201);
|
||||
return response()->json($siri, 201);
|
||||
}
|
||||
|
||||
|
||||
public function createkelulusanAnggota(Request $request)
|
||||
{
|
||||
try {
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$current = Carbon::now();
|
||||
|
||||
Log::info('createkelulusanAnggota', $request->all());
|
||||
|
||||
// Dapatkan maklumat cawangan
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/cawangan/' . $auth_user['cawangan'])
|
||||
->json();
|
||||
|
||||
// Generate siri baru
|
||||
$getsiri = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/ListLatestLulus/' . $auth_user['cawangan'] . '/anggota')
|
||||
->json();
|
||||
|
||||
$siri = isset($getsiri['siri']) ? $getsiri['siri'] + 1 : 1;
|
||||
|
||||
// Hantar ke API
|
||||
$createlulus = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->post($api_url . '/api/CreateKelulusanAnggota/' . $auth_user['cawangan'] . '/roles/operasi', [
|
||||
'komen' => $request->komen,
|
||||
'date_mohon' => $current->toDateString(),
|
||||
'siri' => $siri,
|
||||
'nokp' => $request->nokp,
|
||||
'teller' => $request->teller,
|
||||
'peloperasi' => 'operasi'
|
||||
])
|
||||
->json();
|
||||
|
||||
event(new StatusLiked($auth_user['cawangan'], $auth_user['name']));
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Permohonan kelulusan anggota berjaya dihantar.',
|
||||
'siri' => $siri,
|
||||
'data' => $createlulus,
|
||||
], 201);
|
||||
|
||||
Log::info('🛰️ API CreateKelulusanAnggota Response:', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
} catch (\Throwable $th) {
|
||||
Log::error('❌ createkelulusanAnggota error: ' . $th->getMessage());
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $th->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function mintakatalaluansemula(Request $request)
|
||||
{
|
||||
|
||||
if(Hash::check($request->passsemula, Auth::user()->password))
|
||||
{
|
||||
if (app()->environment('local')) {
|
||||
return "True";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (Hash::check($request->passsemula, Auth::user()->password)) {
|
||||
return "True";
|
||||
} else {
|
||||
return "False";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function parameter()
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
return view('operasi.parameter',compact('cawangan_info','api_url'));
|
||||
return view('operasi.parameter', compact('cawangan_info', 'api_url'));
|
||||
}
|
||||
|
||||
public function hargaemas()
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
$harga_emas = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/harga_emas/')->json();
|
||||
|
||||
return view('operasi.hargaemas',compact('cawangan_info','api_url','harga_emas'));
|
||||
return view('operasi.hargaemas', compact('cawangan_info', 'api_url', 'harga_emas'));
|
||||
}
|
||||
|
||||
public function margin(){
|
||||
public function margin()
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
$margin_pembiayaan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/maxpinjaman')->json();
|
||||
return view('operasi.margin_pembiayaan',compact('cawangan_info','api_url','margin_pembiayaan'));
|
||||
return view('operasi.margin_pembiayaan', compact('cawangan_info', 'api_url', 'margin_pembiayaan'));
|
||||
}
|
||||
|
||||
public function kelulusanrekodlepas()
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
//declare array baru
|
||||
$arraykelulusan = [];
|
||||
@@ -292,30 +421,37 @@ class OperasiController extends Controller
|
||||
$kelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListOperasiKelulusan/')->json();
|
||||
|
||||
|
||||
foreach($kelulusan as $index=>$k)
|
||||
{
|
||||
foreach($k['kelulusanall'] as $index1=>$kel)
|
||||
{
|
||||
$komen = explode("|",$kel['komen']);
|
||||
foreach ($kelulusan as $index => $k) {
|
||||
foreach ($k['kelulusanall'] as $index1 => $kel) {
|
||||
$komen = explode("|", $kel['komen']);
|
||||
|
||||
$results=array_search("",$komen,true);
|
||||
if($results !== false) {
|
||||
$results = array_search("", $komen, true);
|
||||
if ($results !== false) {
|
||||
unset($komen[$results]);
|
||||
}
|
||||
|
||||
array_push($arraykelulusan,['norujukan' => $kel['norujukan'], 'pinjaman' => $kel['jumpinj'], 'nilaimarhun' => ($komen[1] ?? $kel['nilaimarhun'] ?? '-'), 'berat' => ($komen[2] ?? $kel['berat'] ?? '-'),'margin' => ($komen[3] ?? $kel['margin'] ?? '-'), 'komen' => ($komen[0] ?? $kel['komen'] ?? '-'), 'status' => ($kel['status'] ?? '-'), 'pelulus' => $kel['operasi'],]);
|
||||
|
||||
array_push($arraykelulusan, ['norujukan' => $kel['norujukan'], 'pinjaman' => $kel['jumpinj'], 'nilaimarhun' => ($komen[1] ?? $kel['nilaimarhun'] ?? '-'), 'berat' => ($komen[2] ?? $kel['berat'] ?? '-'), 'margin' => ($komen[3] ?? $kel['margin'] ?? '-'), 'komen' => ($komen[0] ?? $kel['komen'] ?? '-'), 'status' => ($kel['status'] ?? '-'), 'pelulus' => $kel['operasi'],]);
|
||||
}
|
||||
}
|
||||
|
||||
return view('operasi.rekodtelahlulus',compact('cawangan_info','api_url','arraykelulusan'));
|
||||
|
||||
return view('operasi.rekodtelahlulus', compact('cawangan_info', 'api_url', 'arraykelulusan'));
|
||||
}
|
||||
|
||||
public function notifyKomoditi()
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
event(new KomoditiNotify('lulus',$auth_user['cawangan']));
|
||||
event(new KomoditiNotify('lulus', $auth_user['cawangan']));
|
||||
}
|
||||
|
||||
public function kelulusananggota(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/' . $auth_user['cawangan'])->json();
|
||||
|
||||
$arraykelulusan = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/list/approval/anggota')->json();
|
||||
|
||||
return view('operasi.kelulusan_anggota', compact('cawangan_info', 'api_url', 'arraykelulusan'));
|
||||
}
|
||||
|
||||
public function marginmutu()
|
||||
@@ -415,4 +551,5 @@ class OperasiController extends Controller
|
||||
|
||||
return view('print.laporan_audittrail_operasi', compact('audittrail','cawangan_detail','tarikh_cetak','date','date_till','api_url'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PembiayaanOnlineController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
protected function api()
|
||||
{
|
||||
return Http::withOptions(['verify' => config('app.api_verify')]);
|
||||
}
|
||||
|
||||
protected function kodcaw()
|
||||
{
|
||||
return Auth::user()->cawangan;
|
||||
}
|
||||
|
||||
/**
|
||||
* PC senarai queue + batch history.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
|
||||
$cawangan_info = $this->api()
|
||||
->get($api_url . '/api/cawangan/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$queue = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/queue')
|
||||
->json();
|
||||
|
||||
$batches = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches')
|
||||
->json();
|
||||
|
||||
$banks = $this->api()->get($api_url . '/api/bank')->json();
|
||||
if (!is_array($banks)) {
|
||||
$banks = [];
|
||||
}
|
||||
|
||||
$modal = $this->api()
|
||||
->get($api_url . '/api/modal-online/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$lines = $queue['data'] ?? [];
|
||||
$slot = $queue['slot_cadangan'] ?? null;
|
||||
if (!is_array($batches)) {
|
||||
$batches = [];
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.pc.senarai', compact(
|
||||
'auth_user',
|
||||
'kodcaw',
|
||||
'api_url',
|
||||
'cawangan_info',
|
||||
'lines',
|
||||
'batches',
|
||||
'banks',
|
||||
'modal',
|
||||
'slot',
|
||||
'queue'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update bank/docs for a queue line.
|
||||
*/
|
||||
public function updateLine(Request $request, $id)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
|
||||
$payload = [
|
||||
'kodbank' => $request->input('kodbank'),
|
||||
'noakaun' => $request->input('noakaun'),
|
||||
'nama_bank' => $request->input('nama_bank'),
|
||||
'nama_akaun' => $request->input('nama_akaun'),
|
||||
'nota' => $request->input('nota'),
|
||||
'update_customer_bank' => true,
|
||||
];
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal kemaskini rekod.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
return redirect()->route('pembiayaan.online.pc')->with('message', 'Rekod dikemaskini.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit selected lines as a batch to Operasi.
|
||||
*/
|
||||
public function submitBatch(Request $request)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
$auth_user = Auth::user();
|
||||
|
||||
$ids = $request->input('ids', []);
|
||||
if (!is_array($ids)) {
|
||||
$ids = array_filter(explode(',', (string) $ids));
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/submit', [
|
||||
'ids' => $ids,
|
||||
'pc_submitted_by' => $auth_user['name'],
|
||||
'slot' => $request->input('slot'),
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar batch.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$payload = $res->json();
|
||||
$batchNo = is_array($payload) ? ($payload['batch']['batch_no'] ?? '') : '';
|
||||
$appended = is_array($payload) && !empty($payload['appended']);
|
||||
$message = $appended
|
||||
? ('Rekod ditambah ke batch' . ($batchNo ? ' ' . $batchNo : '') . '.')
|
||||
: ('Batch dihantar ke Operasi' . ($batchNo ? ': ' . $batchNo : '.'));
|
||||
|
||||
return redirect()->route('pembiayaan.online.pc')->with('message', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch detail.
|
||||
*/
|
||||
public function showBatch($batchId)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
|
||||
$cawangan_info = $this->api()
|
||||
->get($api_url . '/api/cawangan/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$detail = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/' . $batchId)
|
||||
->json();
|
||||
|
||||
if (!is_array($detail) || empty($detail['batch'])) {
|
||||
return redirect()->route('pembiayaan.online.pc')->with('error', 'Batch tidak dijumpai.');
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.pc.batch', [
|
||||
'batch' => $detail['batch'],
|
||||
'lines' => $detail['lines'] ?? [],
|
||||
'kodcaw' => $kodcaw,
|
||||
'api_url' => $api_url,
|
||||
'cawangan_info' => $cawangan_info,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: senarai PAID untuk tally + blast.
|
||||
*/
|
||||
public function paidIndex(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
$blastFilter = $request->input('blast_sent', '0');
|
||||
|
||||
$cawangan_info = $this->api()
|
||||
->get($api_url . '/api/cawangan/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$query = [];
|
||||
if ($blastFilter !== 'all' && $blastFilter !== '') {
|
||||
$query['blast_sent'] = $blastFilter;
|
||||
}
|
||||
|
||||
$paid = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/paid', $query)
|
||||
->json();
|
||||
|
||||
$lines = $paid['data'] ?? [];
|
||||
if (!is_array($lines)) {
|
||||
$lines = [];
|
||||
}
|
||||
|
||||
$bilangan = $paid['bilangan'] ?? count($lines);
|
||||
$jumlah = $paid['jumlah'] ?? 0;
|
||||
|
||||
return view('pembiayaan_online.pc.paid', compact(
|
||||
'auth_user',
|
||||
'kodcaw',
|
||||
'api_url',
|
||||
'cawangan_info',
|
||||
'lines',
|
||||
'bilangan',
|
||||
'jumlah',
|
||||
'blastFilter'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: view/download bukti bayaran inline.
|
||||
*/
|
||||
public function viewBukti($id)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$res = $this->api()
|
||||
->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/bukti');
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Bukti tidak dijumpai.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$payload = $res->json();
|
||||
$bukti = $payload['bukti'] ?? null;
|
||||
if (!$bukti || empty($bukti['data'])) {
|
||||
return redirect()->back()->with('error', 'Fail bukti kosong.');
|
||||
}
|
||||
|
||||
$raw = $bukti['data'];
|
||||
if (strpos($raw, 'base64,') !== false) {
|
||||
$raw = substr($raw, strpos($raw, 'base64,') + 7);
|
||||
}
|
||||
$binary = base64_decode($raw, true);
|
||||
if ($binary === false) {
|
||||
$binary = base64_decode($bukti['data']);
|
||||
}
|
||||
|
||||
$type = $bukti['type'] ?? 'application/octet-stream';
|
||||
$filename = $bukti['filename'] ?? ('bukti-' . $id);
|
||||
|
||||
return response($binary, 200, [
|
||||
'Content-Type' => $type,
|
||||
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: tanda rekod PAID sebagai selesai semak / blast.
|
||||
*/
|
||||
public function markBlast(Request $request)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$ids = $request->input('ids', []);
|
||||
if (!is_array($ids)) {
|
||||
$ids = array_filter(explode(',', (string) $ids));
|
||||
}
|
||||
|
||||
if (count($ids) === 0) {
|
||||
return redirect()->back()->with('error', 'Sila pilih sekurang-kurangnya satu rekod.');
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/paid/blast', [
|
||||
'ids' => $ids,
|
||||
'blast_by' => Auth::user()['name'],
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tanda selesai.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$n = $res->json()['dikemaskini'] ?? count($ids);
|
||||
return redirect()
|
||||
->route('pembiayaan.online.pc.paid')
|
||||
->with('message', $n . ' rekod ditanda selesai semak/blast.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Operasi: senarai batch semua cawangan.
|
||||
*/
|
||||
public function operasiIndex(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$status = $request->input('status', 'SUBMITTED');
|
||||
$tarikh = $request->input('tarikh');
|
||||
|
||||
$query = ['status' => $status];
|
||||
if ($tarikh) {
|
||||
$query['tarikh'] = $tarikh;
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/operasi/batches', $query)
|
||||
->json();
|
||||
|
||||
$batches = $res['data'] ?? [];
|
||||
if (!is_array($batches)) {
|
||||
$batches = [];
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.operasi.senarai', compact(
|
||||
'auth_user',
|
||||
'api_url',
|
||||
'batches',
|
||||
'status',
|
||||
'tarikh'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Operasi: detail batch + lines.
|
||||
*/
|
||||
public function operasiShowBatch($kodcaw, $batchId)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
|
||||
$cawangan_info = $this->api()
|
||||
->get($api_url . '/api/cawangan/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$detail = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId)
|
||||
->json();
|
||||
|
||||
if (!is_array($detail) || empty($detail['batch'])) {
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi')
|
||||
->with('error', 'Batch tidak dijumpai.');
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.operasi.batch', [
|
||||
'auth_user' => $auth_user,
|
||||
'batch' => $detail['batch'],
|
||||
'lines' => $detail['lines'] ?? [],
|
||||
'kodcaw' => $kodcaw,
|
||||
'api_url' => $api_url,
|
||||
'cawangan_info' => $cawangan_info,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operasi: printable batch kelulusan form.
|
||||
*/
|
||||
public function operasiPrintBatch($kodcaw, $batchId)
|
||||
{
|
||||
$api_url = config('api.url');
|
||||
|
||||
$detail = $this->api()
|
||||
->get($api_url . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId)
|
||||
->json();
|
||||
|
||||
if (!is_array($detail) || empty($detail['batch'])) {
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi')
|
||||
->with('error', 'Batch tidak dijumpai.');
|
||||
}
|
||||
|
||||
$batch = $detail['batch'];
|
||||
$lines = $detail['lines'] ?? [];
|
||||
if (!is_array($lines)) {
|
||||
$lines = [];
|
||||
}
|
||||
|
||||
$cawanganResp = $this->api()
|
||||
->get($api_url . '/api/cawangan/detail/' . $kodcaw)
|
||||
->json();
|
||||
$cawangan_detail = (is_array($cawanganResp) && isset($cawanganResp[0]))
|
||||
? $cawanganResp[0]
|
||||
: (is_array($cawanganResp) ? $cawanganResp : []);
|
||||
|
||||
$cawangan = $cawangan_detail['namacaw']
|
||||
?? $cawangan_detail['details_caw']
|
||||
?? $cawangan_detail['nama']
|
||||
?? $kodcaw;
|
||||
$alamat1 = $cawangan_detail['alamat1'] ?? '';
|
||||
$alamat2 = $cawangan_detail['alamat2'] ?? '';
|
||||
$notel = $cawangan_detail['notel'] ?? '';
|
||||
|
||||
$batchNo = $batch['batch_no'] ?? '-';
|
||||
$slot = $batch['slot'] ?? '-';
|
||||
$status = $batch['status'] ?? '-';
|
||||
$dihantarOleh = $batch['pc_submitted_by'] ?? '-';
|
||||
|
||||
$jumlah = (float) ($batch['jumlah_keseluruhan'] ?? 0);
|
||||
if ($jumlah <= 0 && count($lines) > 0) {
|
||||
$jumlah = (float) collect($lines)->sum(function ($line) {
|
||||
return (float) ($line['pinjaman'] ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
$opsBy = Auth::user()['name'] ?? '';
|
||||
foreach ($lines as $line) {
|
||||
if (!empty($line['ops_by'])) {
|
||||
$opsBy = $line['ops_by'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$tarikh = $this->formatPrintDate($batch['tarikh'] ?? null);
|
||||
$dihantarPada = $this->formatPrintDate($batch['submitted_at'] ?? null, true);
|
||||
|
||||
return view('print.borang_batch_pembiayaan_online', compact(
|
||||
'cawangan',
|
||||
'kodcaw',
|
||||
'alamat1',
|
||||
'alamat2',
|
||||
'notel',
|
||||
'tarikh',
|
||||
'batchNo',
|
||||
'slot',
|
||||
'status',
|
||||
'dihantarOleh',
|
||||
'dihantarPada',
|
||||
'opsBy',
|
||||
'lines',
|
||||
'jumlah'
|
||||
));
|
||||
}
|
||||
|
||||
protected function formatPrintDate($value, $withTime = false)
|
||||
{
|
||||
if ($value === null || $value === '' || $value === '-') {
|
||||
return '-';
|
||||
}
|
||||
|
||||
try {
|
||||
return Carbon::parse($value)->format($withTime ? 'd/m/Y H:i' : 'd/m/Y');
|
||||
} catch (\Exception $e) {
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function operasiApproveLine(Request $request, $kodcaw, $id)
|
||||
{
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/approve', [
|
||||
'ops_by' => Auth::user()['name'],
|
||||
]);
|
||||
|
||||
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod diluluskan.');
|
||||
}
|
||||
|
||||
public function operasiRejectLine(Request $request, $kodcaw, $id)
|
||||
{
|
||||
$reason = trim((string) $request->input('reject_reason', ''));
|
||||
if ($reason === '') {
|
||||
return redirect()->back()->with('error', 'Sebab tolak diperlukan.');
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/reject', [
|
||||
'ops_by' => Auth::user()['name'],
|
||||
'reject_reason' => $reason,
|
||||
]);
|
||||
|
||||
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditolak. Dikembalikan ke PC.');
|
||||
}
|
||||
|
||||
public function operasiApproveBatch(Request $request, $kodcaw, $batchId)
|
||||
{
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/approve', [
|
||||
'ops_by' => Auth::user()['name'],
|
||||
]);
|
||||
|
||||
return $this->opsRedirect($res, $kodcaw, $batchId, 'Semua rekod menunggu dalam batch diluluskan.');
|
||||
}
|
||||
|
||||
public function operasiMarkPaid(Request $request, $kodcaw, $id)
|
||||
{
|
||||
$payload = $this->buildPaidPayload($request);
|
||||
if (isset($payload['error'])) {
|
||||
return redirect()->back()->with('error', $payload['error']);
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/paid', $payload);
|
||||
|
||||
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditanda PAID.');
|
||||
}
|
||||
|
||||
public function operasiMarkPaidBatch(Request $request, $kodcaw, $batchId)
|
||||
{
|
||||
$payload = $this->buildPaidPayload($request);
|
||||
if (isset($payload['error'])) {
|
||||
return redirect()->back()->with('error', $payload['error']);
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/paid', $payload);
|
||||
|
||||
return $this->opsRedirect($res, $kodcaw, $batchId, 'Rekod APPROVED dalam batch ditanda PAID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function buildPaidPayload(Request $request)
|
||||
{
|
||||
$noRujukan = trim((string) $request->input('no_rujukan_bayaran', ''));
|
||||
if ($noRujukan === '') {
|
||||
return ['error' => 'No. rujukan bayaran diperlukan.'];
|
||||
}
|
||||
|
||||
if (!$request->hasFile('bukti')) {
|
||||
return ['error' => 'Fail bukti bayaran diperlukan.'];
|
||||
}
|
||||
|
||||
$file = $request->file('bukti');
|
||||
if (!$file->isValid()) {
|
||||
return ['error' => 'Fail bukti tidak sah.'];
|
||||
}
|
||||
|
||||
$raw = base64_encode(file_get_contents($file->getRealPath()));
|
||||
$mime = $file->getMimeType() ?: 'application/octet-stream';
|
||||
|
||||
return [
|
||||
'ops_by' => Auth::user()['name'],
|
||||
'no_rujukan_bayaran' => $noRujukan,
|
||||
'tarikh_bayaran' => $request->input('tarikh_bayaran') ?: date('Y-m-d'),
|
||||
'bukti_data' => $raw,
|
||||
'bukti_filename' => $file->getClientOriginalName(),
|
||||
'bukti_type' => $mime,
|
||||
];
|
||||
}
|
||||
|
||||
protected function opsRedirect($res, $kodcaw, $batchId, $successMsg)
|
||||
{
|
||||
$batchId = $batchId ?: null;
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Tindakan gagal.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
if ($batchId) {
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi.batch', ['kodcaw' => $kodcaw, 'batchId' => $batchId])
|
||||
->with('message', $successMsg);
|
||||
}
|
||||
|
||||
return redirect()->route('pembiayaan.online.operasi')->with('message', $successMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: modal online float + top-up request form/history.
|
||||
*/
|
||||
public function topupIndex(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$kodcaw = $this->kodcaw();
|
||||
$api_url = config('api.url');
|
||||
$status = $request->input('status', '');
|
||||
|
||||
$cawangan_info = $this->api()
|
||||
->get($api_url . '/api/cawangan/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$modal = $this->api()
|
||||
->get($api_url . '/api/modal-online/' . $kodcaw)
|
||||
->json();
|
||||
|
||||
$query = [];
|
||||
if ($status !== '' && $status !== 'all') {
|
||||
$query['status'] = $status;
|
||||
}
|
||||
|
||||
$reqRes = $this->api()
|
||||
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', $query)
|
||||
->json();
|
||||
|
||||
$requests = $reqRes['data'] ?? [];
|
||||
if (!is_array($requests)) {
|
||||
$requests = [];
|
||||
}
|
||||
|
||||
$pendingRes = $this->api()
|
||||
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', ['status' => 'PENDING'])
|
||||
->json();
|
||||
$pendingList = $pendingRes['data'] ?? [];
|
||||
$hasPending = is_array($pendingList) && count($pendingList) > 0;
|
||||
$isMissing = !empty($modal['missing']);
|
||||
$pendingRequest = $modal['pending_request'] ?? null;
|
||||
if (!$pendingRequest && $hasPending) {
|
||||
$pendingRequest = $pendingList[0];
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.pc.topup', compact(
|
||||
'auth_user',
|
||||
'kodcaw',
|
||||
'api_url',
|
||||
'cawangan_info',
|
||||
'modal',
|
||||
'requests',
|
||||
'status',
|
||||
'hasPending',
|
||||
'isMissing',
|
||||
'pendingRequest'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: submit top-up request.
|
||||
*/
|
||||
public function topupRequest(Request $request)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
$amaun = (float) $request->input('amaun', 0);
|
||||
|
||||
if ($amaun <= 0) {
|
||||
return redirect()->back()->with('error', 'Amaun mesti lebih daripada 0.');
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/topup/request', [
|
||||
'amaun' => $amaun,
|
||||
'dimohon_oleh' => Auth::user()['name'],
|
||||
'nota' => $request->input('nota'),
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar permohonan.';
|
||||
return redirect()->back()->with('error', $msg)->withInput();
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('pembiayaan.online.pc.topup')
|
||||
->with('message', 'Permohonan modal online dihantar. Menunggu kelulusan Operasi.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Operasi: senarai permohonan tambah modal online.
|
||||
*/
|
||||
public function operasiTopupIndex(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$status = $request->input('status', 'PENDING');
|
||||
$tarikh = $request->input('tarikh');
|
||||
|
||||
$query = [];
|
||||
if ($status === 'all') {
|
||||
$query['status'] = 'all';
|
||||
} elseif ($status !== '' && $status !== null) {
|
||||
$query['status'] = $status;
|
||||
} else {
|
||||
$query['status'] = 'PENDING';
|
||||
}
|
||||
if ($tarikh) {
|
||||
$query['tarikh'] = $tarikh;
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->get($api_url . '/api/modal-online/operasi/topup/requests', $query)
|
||||
->json();
|
||||
|
||||
$requests = $res['data'] ?? [];
|
||||
if (!is_array($requests)) {
|
||||
$requests = [];
|
||||
}
|
||||
|
||||
return view('pembiayaan_online.operasi.topup', compact(
|
||||
'auth_user',
|
||||
'api_url',
|
||||
'requests',
|
||||
'status',
|
||||
'tarikh'
|
||||
));
|
||||
}
|
||||
|
||||
public function operasiTopupApprove(Request $request, $id)
|
||||
{
|
||||
$amaun = $request->input('amaun');
|
||||
if ($amaun !== null && $amaun !== '' && (float) $amaun <= 0) {
|
||||
return redirect()->back()->with('error', 'Amaun diluluskan mesti lebih daripada 0.');
|
||||
}
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/approve', [
|
||||
'diluluskan_oleh' => Auth::user()['name'],
|
||||
'nota' => $request->input('nota'),
|
||||
'amaun' => $amaun,
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal lulus permohonan.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$amaunLulus = $res->json()['request']['amaun'] ?? $amaun;
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi.topup')
|
||||
->with('message', 'Permohonan diluluskan RM ' . number_format((float) $amaunLulus, 2) . '. Modal online cawangan dikemas kini.');
|
||||
}
|
||||
|
||||
public function operasiTopupReject(Request $request, $id)
|
||||
{
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/reject', [
|
||||
'diluluskan_oleh' => Auth::user()['name'],
|
||||
'nota' => $request->input('nota'),
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tolak permohonan.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi.topup')
|
||||
->with('message', 'Permohonan ditolak.');
|
||||
}
|
||||
|
||||
/**
|
||||
* PC: EOD close branch modal online.
|
||||
*/
|
||||
public function closeModal(Request $request)
|
||||
{
|
||||
$kodcaw = $this->kodcaw();
|
||||
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
|
||||
'ditutup_oleh' => Auth::user()['name'],
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$dikembalikan = $res->json()['dikembalikan'] ?? 0;
|
||||
return redirect()
|
||||
->route('pembiayaan.online.pc.topup')
|
||||
->with('message', 'Modal online ditutup. Baki dikembalikan RM ' . number_format((float) $dikembalikan, 2) . '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Operasi: EOD dashboard semua cawangan.
|
||||
*/
|
||||
public function operasiEodIndex(Request $request)
|
||||
{
|
||||
$auth_user = Auth::user();
|
||||
$api_url = config('api.url');
|
||||
$tarikh = $request->input('tarikh') ?: date('Y-m-d');
|
||||
|
||||
$res = $this->api()
|
||||
->get($api_url . '/api/modal-online/operasi/eod', [
|
||||
'zone' => 'all',
|
||||
'tarikh' => $tarikh,
|
||||
])
|
||||
->json();
|
||||
|
||||
$rows = $res['data'] ?? [];
|
||||
if (!is_array($rows)) {
|
||||
$rows = [];
|
||||
}
|
||||
|
||||
$summary = [
|
||||
'open' => $res['open'] ?? 0,
|
||||
'closed' => $res['closed'] ?? 0,
|
||||
'missing' => $res['missing'] ?? 0,
|
||||
'bilangan' => $res['bilangan'] ?? count($rows),
|
||||
];
|
||||
|
||||
return view('pembiayaan_online.operasi.eod', compact(
|
||||
'auth_user',
|
||||
'api_url',
|
||||
'rows',
|
||||
'tarikh',
|
||||
'summary'
|
||||
));
|
||||
}
|
||||
|
||||
public function operasiCloseModal(Request $request, $kodcaw)
|
||||
{
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
|
||||
'ditutup_oleh' => Auth::user()['name'],
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi.eod')
|
||||
->with('message', 'Modal online ' . $kodcaw . ' ditutup.');
|
||||
}
|
||||
|
||||
public function operasiCloseAllModal(Request $request)
|
||||
{
|
||||
$res = $this->api()
|
||||
->asForm()
|
||||
->post(config('api.url') . '/api/modal-online/operasi/eod/close-all', [
|
||||
'ditutup_oleh' => Auth::user()['name'],
|
||||
'zone' => 'all',
|
||||
]);
|
||||
|
||||
if (!$res->successful()) {
|
||||
$body = $res->json();
|
||||
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup semua modal online.';
|
||||
return redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
$closed = $res->json()['closed'] ?? 0;
|
||||
$skipped = $res->json()['skipped'] ?? 0;
|
||||
return redirect()
|
||||
->route('pembiayaan.online.operasi.eod')
|
||||
->with('message', 'EOD selesai: ' . $closed . ' ditutup, ' . $skipped . ' sudah ditutup.');
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class TrustProxies extends Middleware
|
||||
*
|
||||
* @var array|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
protected $proxies = '*';
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Anggota;
|
||||
use Maatwebsite\Excel\Concerns\ToModel;
|
||||
|
||||
class AnggotaImport implements ToModel
|
||||
{
|
||||
/**
|
||||
* @param array $row
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function model(array $row)
|
||||
{
|
||||
return new Anggota([
|
||||
//
|
||||
'noic' => $row['noic'],
|
||||
'nama' => $row['nama'],
|
||||
'status' => $row['status'] ?? 'aktif',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
if (strpos((string) config('app.url'), 'https://') === 0) {
|
||||
\Illuminate\Support\Facades\URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
//
|
||||
Gate::define('impersonate', function ($user) {
|
||||
return $user->canImpersonate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Lab404\Impersonate\Models\Impersonate;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use Impersonate;
|
||||
use Notifiable;
|
||||
|
||||
protected $guard = 'web';
|
||||
@@ -43,4 +45,14 @@ class User extends Authenticatable
|
||||
{
|
||||
return $this->hasOne(Cawangan::class, 'kodcaw', 'cawangan');
|
||||
}
|
||||
|
||||
public function canImpersonate(): bool
|
||||
{
|
||||
return in_array($this->roles ?? '', ['admin'], true);
|
||||
}
|
||||
|
||||
public function canBeImpersonated(): bool
|
||||
{
|
||||
return ($this->roles ?? '') !== 'admin';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('api_url_for_browser')) {
|
||||
/**
|
||||
* Base URL for JavaScript in the browser, derived from API_URL (config api.url).
|
||||
* Docker dev often uses host.docker.internal for PHP→host; browsers may not resolve it.
|
||||
* Same host-mapped port is reachable at 127.0.0.1. Production URLs are unchanged.
|
||||
*/
|
||||
function api_url_for_browser(): string
|
||||
{
|
||||
$url = rtrim((string) config('api.url'), '/');
|
||||
|
||||
return str_ireplace('host.docker.internal', '127.0.0.1', $url);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"jeroennoten/laravel-adminlte": "^3.5",
|
||||
"lab404/laravel-impersonate": "^1.7",
|
||||
"laravel/framework": "^7.24",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^2.4",
|
||||
@@ -51,6 +52,9 @@
|
||||
"classmap": [
|
||||
"database/seeds",
|
||||
"database/factories"
|
||||
],
|
||||
"files": [
|
||||
"app/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
||||
Generated
+71
-4
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bfd66ab0246126c4f2cc7bbeeacca4ec",
|
||||
"content-hash": "fa66321247398a94dc8b7f8099bb184e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "almasaeed2010/adminlte",
|
||||
@@ -1293,6 +1293,73 @@
|
||||
},
|
||||
"time": "2022-05-18T14:12:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lab404/laravel-impersonate",
|
||||
"version": "1.7.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/404labfr/laravel-impersonate.git",
|
||||
"reference": "0008a39da8914cc946b6a5ed211230708ee736b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/404labfr/laravel-impersonate/zipball/0008a39da8914cc946b6a5ed211230708ee736b3",
|
||||
"reference": "0008a39da8914cc946b6a5ed211230708ee736b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0",
|
||||
"php": "^7.2 | ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench": "^4.0 | ^5.0 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0",
|
||||
"phpunit/phpunit": "^7.5 | ^8.0 | ^9.0 | ^10.0 | ^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Lab404\\Impersonate\\ImpersonateServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Lab404\\Impersonate\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marceau Casals",
|
||||
"email": "marceau@casals.fr"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Impersonate is a plugin that allows to you to authenticate as your users.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"impersonate",
|
||||
"impersonation",
|
||||
"laravel",
|
||||
"laravel-package",
|
||||
"laravel-plugin",
|
||||
"package",
|
||||
"plugin",
|
||||
"user"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/404labfr/laravel-impersonate/issues",
|
||||
"source": "https://github.com/404labfr/laravel-impersonate/tree/1.7.8"
|
||||
},
|
||||
"time": "2026-03-17T15:24:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v7.30.6",
|
||||
@@ -9013,12 +9080,12 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.2.5"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'url' => env('API_URL'),
|
||||
'url_koop' => env('API_URL_KOOP','http://localhost:8000'),
|
||||
'url_koop' => env('API_URL_KOOP','https://apiujrah.erahn.com.my'),
|
||||
'url_pkb' => env('API_URL_PKB','http://localhost:8000'),
|
||||
];
|
||||
@@ -15,6 +15,8 @@ return [
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
'version' => env('APP_VERSION', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
$default_port = '8085';
|
||||
$getReaderEndpoint = fn($x) => 'http://localhost:' . $x . '/?MYKADJSON';
|
||||
$default_port = '17321';
|
||||
$getReaderEndpoint = fn($x) => 'http://localhost:' . $x . '/read';
|
||||
|
||||
return[
|
||||
'endpoint' => env('MYKAD_READER_ENDPOINT', $getReaderEndpoint(env('MYKAD_READER_PORT', $default_port))),
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
name: kaunter_staging
|
||||
|
||||
services:
|
||||
app:
|
||||
image: ${APP_IMAGE:-git.koppkb.com/kopkb/kaunter}:${IMAGE_TAG:-staging}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
target: staging
|
||||
container_name: kaunter_staging_app
|
||||
working_dir: /var/www/html
|
||||
volumes:
|
||||
- storage_staging:/var/www/html/storage
|
||||
- ./.env.staging:/var/www/html/.env.staging:ro
|
||||
env_file:
|
||||
- .env.staging
|
||||
environment:
|
||||
APP_ENV: staging
|
||||
USE_STAGING_ENV: "true"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
image: nginx:1.25-alpine
|
||||
container_name: kaunter_staging_web
|
||||
ports:
|
||||
- "${HTTP_PORT:-8091}:80"
|
||||
volumes:
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./public:/var/www/html/public:ro
|
||||
depends_on:
|
||||
- app
|
||||
restart: unless-stopped
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: kaunter_staging_mysql
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
ports:
|
||||
- "127.0.0.1:${MYSQL_PUBLISH_PORT:-3309}:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: kaunter_erahn
|
||||
MYSQL_USER: kaunteruser
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
volumes:
|
||||
- mysql_staging_data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-proot"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 20s
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mysql_staging_data:
|
||||
storage_staging:
|
||||
@@ -0,0 +1,43 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
target: development
|
||||
working_dir: /var/www/html
|
||||
volumes:
|
||||
- ./:/var/www/html:cached
|
||||
environment:
|
||||
APP_ENV: local
|
||||
APP_DEBUG: "true"
|
||||
# Lets PHP resolve host.docker.internal to the host machine (Linux Docker; Desktop already provides this).
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
- mysql
|
||||
|
||||
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:
|
||||
image: mysql:8.0
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
ports:
|
||||
- "3307:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: kaunter_erahn
|
||||
MYSQL_USER: kaunteruser
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
@@ -0,0 +1,39 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /var/www/html/public;
|
||||
index index.php index.html;
|
||||
|
||||
charset utf-8;
|
||||
client_max_body_size 50M;
|
||||
|
||||
location / {
|
||||
# Production Apache public/.htaccess 301-strips trailing slashes.
|
||||
# Nginx ignores .htaccess, so without this /path/ 404s while /path works.
|
||||
rewrite ^/(.*)/$ /$1 last;
|
||||
try_files $uri @php;
|
||||
}
|
||||
|
||||
location @php {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_pass app:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_pass app:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ~ /\.(?!well-known).* {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
FROM php:7.4-fpm AS base
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
pkg-config \
|
||||
unzip \
|
||||
libonig-dev \
|
||||
libzip-dev \
|
||||
libpng-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
libfreetype6-dev \
|
||||
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install -j"$(nproc)" \
|
||||
pdo_mysql \
|
||||
mbstring \
|
||||
bcmath \
|
||||
zip \
|
||||
exif \
|
||||
gd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini
|
||||
COPY docker/php/zz-clear-env.conf /usr/local/etc/php-fpm.d/zz-clear-env.conf
|
||||
COPY docker/php/entrypoint.sh /usr/local/bin/app-entrypoint.sh
|
||||
|
||||
RUN chmod +x /usr/local/bin/app-entrypoint.sh \
|
||||
&& usermod -u 1000 www-data && groupmod -g 1000 www-data
|
||||
|
||||
ENTRYPOINT ["app-entrypoint.sh"]
|
||||
CMD ["php-fpm"]
|
||||
|
||||
FROM base AS staging
|
||||
|
||||
COPY --chown=www-data:www-data . /var/www/html
|
||||
|
||||
RUN composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader --no-scripts \
|
||||
&& mkdir -p \
|
||||
storage/logs \
|
||||
storage/framework/cache/data \
|
||||
storage/framework/views \
|
||||
storage/framework/sessions \
|
||||
storage/app/public \
|
||||
bootstrap/cache \
|
||||
&& chown -R www-data:www-data storage bootstrap/cache vendor
|
||||
|
||||
FROM base AS development
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cd /var/www/html
|
||||
|
||||
if [ "${USE_STAGING_ENV:-false}" = "true" ] && [ -f .env.staging ]; then
|
||||
cp .env.staging .env
|
||||
fi
|
||||
|
||||
if [ -f composer.json ]; then
|
||||
if [ -f vendor/autoload.php ] && [ "${USE_STAGING_ENV:-false}" = "true" ]; then
|
||||
: # vendor is baked into the staging image
|
||||
elif [ "${USE_STAGING_ENV:-false}" = "true" ]; then
|
||||
composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader --no-scripts
|
||||
else
|
||||
composer install --no-interaction --prefer-dist
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p \
|
||||
storage/logs \
|
||||
storage/framework/cache/data \
|
||||
storage/framework/views \
|
||||
storage/framework/sessions \
|
||||
storage/app/public \
|
||||
bootstrap/cache
|
||||
|
||||
if [ -f artisan ]; then
|
||||
php artisan package:discover --ansi
|
||||
php artisan storage:link --force >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
chown -R www-data:www-data storage bootstrap/cache
|
||||
|
||||
exec docker-php-entrypoint "$@"
|
||||
@@ -0,0 +1,4 @@
|
||||
memory_limit=512M
|
||||
upload_max_filesize=50M
|
||||
post_max_size=50M
|
||||
max_execution_time=120
|
||||
@@ -0,0 +1,3 @@
|
||||
[www]
|
||||
; Pass container env vars (from compose env_file) through to PHP workers.
|
||||
clear_env = no
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Vendored
+386
-276
@@ -1,293 +1,403 @@
|
||||
|
||||
let button_read_mykad = document.getElementById("button_read_mykad");
|
||||
let button_close_modal = document.querySelector("#daftar_pelanggan_mykad_reader button.btn.btn-danger");
|
||||
let original_photo_loader = document.querySelector("#photo-mykad").src;
|
||||
let photo_mykad = document.querySelector("#photo-mykad");
|
||||
let original_photo_loader = photo_mykad ? photo_mykad.src : "";
|
||||
|
||||
let showLoader = () => {
|
||||
let pageLoader = document.getElementById('page_loader');
|
||||
pageLoader.removeAttribute('class');
|
||||
pageLoader.classList.add('loading');
|
||||
};
|
||||
const MYKAD_READER_TIMEOUT_MS = 45000;
|
||||
const MYKAD_READER_DEFAULT_ENDPOINT = "http://localhost:17321/read";
|
||||
|
||||
let hideLoader = () => {
|
||||
let pageLoader = document.getElementById('page_loader');
|
||||
pageLoader.removeAttribute('class');
|
||||
};
|
||||
|
||||
function handleMyKadReader()
|
||||
{
|
||||
showLoader();
|
||||
WebSocketManager.init("wss://localhost:5963/mykadwebsocket");
|
||||
WebSocketManager.listReader(callbackDoneGetListCardReader);
|
||||
}
|
||||
|
||||
function callbackDoneGetListCardReader(data, respcode)
|
||||
{
|
||||
console.log('done get list card reader');
|
||||
|
||||
if(respcode === -1){
|
||||
console.log({'location': 'callbackDoneGetListCardReader', 'data' : data} );
|
||||
}
|
||||
|
||||
let isDevicesConnected = Object.keys(data).length > 0;
|
||||
|
||||
if(! isDevicesConnected){
|
||||
hideLoader();
|
||||
|
||||
Swal.fire({ icon: 'error', title: 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan', });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let slots = data.toString().split(',');
|
||||
WebSocketManager.open(doneOpenReaderConnection, slots[0]);
|
||||
}
|
||||
|
||||
function doneOpenReaderConnection()
|
||||
{
|
||||
WebSocketManager.readAllText(callBackAfterReadMykad);
|
||||
}
|
||||
|
||||
function handleMyKadReaderError(sw12AndApiName)
|
||||
{
|
||||
hideLoader();
|
||||
|
||||
let error_handler_dictionary = {
|
||||
'0x80100069' : 'Tiada MyKad dikesan, sila masukkan Mykad untuk dibaca',
|
||||
'0x80100066' : 'MyKad mungkin dimasukkan terbalik, sila masukkan dengan betul',
|
||||
'default' : sw12AndApiName,
|
||||
};
|
||||
|
||||
let error_data = sw12AndApiName.split(" ");
|
||||
|
||||
let error_code = error_data.filter(x => x.startsWith('0x')).splice(-1)
|
||||
|
||||
let error_message = error_handler_dictionary['default'];
|
||||
|
||||
let hasDefinedError = error_handler_dictionary.hasOwnProperty(error_code);
|
||||
|
||||
if(hasDefinedError){
|
||||
error_message = error_handler_dictionary[error_code];
|
||||
}
|
||||
|
||||
Swal.fire({ icon: 'error', title: error_message, });
|
||||
}
|
||||
|
||||
function callBackAfterReadMykad(data, respcode, sw12AndApiName)
|
||||
{
|
||||
if(respcode !== 0){
|
||||
handleMyKadReaderError(sw12AndApiName);
|
||||
return
|
||||
}
|
||||
|
||||
checkIfMyKadExists(data.IDNum)
|
||||
.then(response => {
|
||||
let isResponseArray = Array.isArray(response);
|
||||
resolved_response = isResponseArray ? response.pop() : response;
|
||||
|
||||
if (resolved_response === 'no-data') {
|
||||
populateDaftarPelangganModal(data, handleReadPhoto);
|
||||
} else {
|
||||
let input_search_noic = document.getElementById("noic");
|
||||
let form_search_noic = document.querySelector("form[name='frmsearch']");
|
||||
|
||||
let getMyKadNumberWithoutHypens = myKadNumber => myKadNumber.replace(/-/g, '');
|
||||
input_search_noic.value = getMyKadNumberWithoutHypens(data.IDNum);
|
||||
form_search_noic.submit();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: error,
|
||||
});
|
||||
});
|
||||
|
||||
hideLoader();
|
||||
}
|
||||
|
||||
function checkIfMyKadExists(myKadNumber) {
|
||||
let myKadNumberWithoutHyphens = myKadNumber.replace(/-/g, '');
|
||||
|
||||
const url = wakil_caricustomer_endpoint + '/?icno=' + myKadNumberWithoutHyphens;
|
||||
|
||||
const connectionConfig = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"X-CSRF-Token": document.querySelector('input[name="_token"]').value
|
||||
},
|
||||
credentials: "same-origin",
|
||||
};
|
||||
|
||||
return fetch(url, connectionConfig)
|
||||
.then(response => response.json())
|
||||
}
|
||||
|
||||
let populateDaftarPelangganModal = (data, callbackAfterPopulateDaftarPelanggan) => {
|
||||
$('#daftar_pelanggan_mykad_reader').modal('show');
|
||||
let modal_form = document.querySelector("#daftar_pelanggan_mykad_reader .modal-body .row.form-group");
|
||||
|
||||
const religions = [
|
||||
['01', 'Islam'],
|
||||
['02', 'Hindu'],
|
||||
['03', 'Buddha'],
|
||||
['04', 'Kristian'],
|
||||
['05', 'Lain-lain'],
|
||||
];
|
||||
|
||||
const races = [
|
||||
['M', 'Melayu'],
|
||||
['C', 'Cina'],
|
||||
['I', 'India'],
|
||||
['L', 'Lain-lain'],
|
||||
];
|
||||
|
||||
let getCodeFromValue = (needle, haystack) => {
|
||||
let title_cased_text = needle.split(' ')
|
||||
.map(w => w[0].toUpperCase() + w.substring(1).toLowerCase())
|
||||
.join(' ');
|
||||
|
||||
return haystack.filter(x => x[1] == title_cased_text)[0][0];
|
||||
};
|
||||
|
||||
let getMyKadNumberWithoutHypens = (IDNum) => {
|
||||
return IDNum.replace(/-/g, '');
|
||||
}
|
||||
|
||||
let getFormattedDateOfBirth = (date_of_birth) => {
|
||||
|
||||
let date_of_birth_hyphenated = date_of_birth.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
|
||||
|
||||
const [year, month, day] = date_of_birth_hyphenated.split('-');
|
||||
return day + '/' + month + '/' + year;
|
||||
};
|
||||
|
||||
let getAgeFromDateOfBirth = (date_of_birth) => {
|
||||
|
||||
let date_of_birth_hyphenated = date_of_birth.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
|
||||
|
||||
const [birth_year, birth_month, birth_day] = date_of_birth_hyphenated.split('-');
|
||||
|
||||
let formatBirthMonth = (birth_month) => {
|
||||
let newbirthMonth = birth_month < 9 ? birth_month[1] : birth_month;
|
||||
return newbirthMonth;
|
||||
};
|
||||
|
||||
const count_leap_year = countLeapYears(birth_year, formatBirthMonth(birth_month), birth_day) - 1;
|
||||
|
||||
const start = new Date(date_of_birth_hyphenated),
|
||||
end = new Date(),
|
||||
diff = new Date(end - start),
|
||||
days = ((diff / 1000 / 60 / 60 / 24) - count_leap_year) / 365;
|
||||
|
||||
return parseInt(days);
|
||||
};
|
||||
|
||||
let getBumiputeraStatus = (race) => {
|
||||
|
||||
const non_bumiputera_ethnicity = ['INDIA', 'CINA'];
|
||||
|
||||
let isBumiputera = !non_bumiputera_ethnicity.includes(race);
|
||||
|
||||
return isBumiputera;
|
||||
};
|
||||
|
||||
let disablePointerEventsOnClickableInputs = () => {
|
||||
modal_form.querySelector("input[name='bumiputera']").style.pointerEvents = 'none';
|
||||
modal_form.querySelector("input[name='warganegara']").style.pointerEvents = 'none';
|
||||
|
||||
modal_form.querySelector("select[name='bangsa']").style.pointerEvents = 'none';
|
||||
modal_form.querySelector("select[name='agama']").style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
||||
let photo_container = mykad_modal.querySelector("#photo-mykad-container");
|
||||
|
||||
modal_form.querySelector("textarea[name='name']").value = data.OrgName;
|
||||
modal_form.querySelector("input[name='noic']").value = getMyKadNumberWithoutHypens(data.IDNum);
|
||||
modal_form.querySelector("input[name='tarikh_lahir']").value = getFormattedDateOfBirth(data.BirthDate);
|
||||
modal_form.querySelector("input[name='negeri_lahir']").value = data.BirthPlace;
|
||||
modal_form.querySelector("input[name='jantina']").value = data.Gender == 'LELAKI' ? 'Lelaki' : 'Perempuan';
|
||||
modal_form.querySelector("input[name='warganegara']").checked = data.Citizenship === 'WARGANEGARA' ? true : false;
|
||||
modal_form.querySelector("input[name='bumiputera']").checked = getBumiputeraStatus(data.Race);
|
||||
modal_form.querySelector("select[name='agama']").value = getCodeFromValue(data.Religion, religions);
|
||||
modal_form.querySelector("textarea[name='alamat']").value = [data.Address1, data.Address2, data.Address3].join(", ");
|
||||
|
||||
let getPostCode = (postCodeCity) => postCodeCity.replace(/\D/g, '');
|
||||
modal_form.querySelector("input[name='poskod']").value = getPostCode(data.PostcodeCity);
|
||||
|
||||
let getCityName = (postCodeCity) => postCodeCity.replace(/\d/g, '');
|
||||
modal_form.querySelector("input[name='daerah']").value = getCityName(data.PostcodeCity).trim();
|
||||
|
||||
modal_form.querySelector("input[name='negeri']").value = data.State;
|
||||
modal_form.querySelector("input[name='age']").value = getAgeFromDateOfBirth(data.BirthDate);
|
||||
modal_form.querySelector("select[name='bangsa']").value = getCodeFromValue(data.Race, races);
|
||||
|
||||
calculatePassportAge();
|
||||
|
||||
photo_container.classList.remove('d-none');
|
||||
photo_container.classList.add('d-flex');
|
||||
|
||||
$('#daftar_pelanggan_mykad_reader').modal('show');
|
||||
|
||||
|
||||
disablePointerEventsOnClickableInputs();
|
||||
callbackAfterPopulateDaftarPelanggan();
|
||||
}
|
||||
|
||||
|
||||
function handleReadPhoto()
|
||||
{
|
||||
WebSocketManager.init("wss://localhost:5963/mykadwebsocket");
|
||||
WebSocketManager.listReader(callbackDoneGetListCardReader2);
|
||||
}
|
||||
|
||||
function callbackDoneGetListCardReader2(data, respcode)
|
||||
{
|
||||
if(respcode === -1){
|
||||
console.log({'location': 'callbackDoneGetListCardReader', 'data' : data} );
|
||||
}
|
||||
|
||||
let slots = data.toString().split(',');
|
||||
WebSocketManager.open(doneOpenReaderConnection2, slots[0]);
|
||||
}
|
||||
|
||||
function doneOpenReaderConnection2()
|
||||
{
|
||||
WebSocketManager.readPhoto(callbackAfterReadPhoto2);
|
||||
}
|
||||
|
||||
function callbackAfterReadPhoto2(data, RespCode, RespSW12AndApiName)
|
||||
{
|
||||
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
||||
let photo = mykad_modal.querySelector("#photo-mykad");
|
||||
|
||||
let isDataEmpty = data
|
||||
&& Object.keys(data).length === 0
|
||||
&& Object.getPrototypeOf(data) === Object.prototype;
|
||||
|
||||
if(isDataEmpty){
|
||||
photo.src = original_photo_loader;
|
||||
let showLoader = () => {
|
||||
let pageLoader = document.getElementById('page_loader');
|
||||
if (!pageLoader) {
|
||||
return;
|
||||
}
|
||||
pageLoader.removeAttribute('class');
|
||||
pageLoader.classList.add('loading');
|
||||
};
|
||||
|
||||
photo.src = 'data:image/jpeg;base64, ' + data.Base64Photo;
|
||||
let hideLoader = () => {
|
||||
let pageLoader = document.getElementById('page_loader');
|
||||
if (!pageLoader) {
|
||||
return;
|
||||
}
|
||||
pageLoader.removeAttribute('class');
|
||||
};
|
||||
|
||||
function getMyKadReaderEndpoint() {
|
||||
let endpoint = (typeof mykad_reader_endpoint !== 'undefined' && mykad_reader_endpoint)
|
||||
? String(mykad_reader_endpoint).trim()
|
||||
: '';
|
||||
|
||||
if (!endpoint || endpoint.includes('MYKADJSON') || endpoint.includes(':8085')) {
|
||||
return MYKAD_READER_DEFAULT_ENDPOINT;
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
async function parseJsonResponse(response) {
|
||||
let text = await response.text();
|
||||
let trimmed = (text || '').trim();
|
||||
|
||||
if (!trimmed) {
|
||||
throw new Error('Pembaca MyKad tidak memulangkan data. Sila cuba lagi.');
|
||||
}
|
||||
|
||||
if (trimmed.charAt(0) === '<') {
|
||||
throw new Error('Pembaca MyKad memulangkan halaman web, bukan data kad. Sila pastikan perkhidmatan mykad-helper sedang berjalan di localhost:17321.');
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(trimmed);
|
||||
} catch (error) {
|
||||
throw new Error('Pembaca MyKad memulangkan data yang tidak sah. Sila cuba lagi.');
|
||||
}
|
||||
}
|
||||
|
||||
function getMyKadNumberWithoutHyphens(nric) {
|
||||
return String(nric || '').replace(/-/g, '');
|
||||
}
|
||||
|
||||
function titleCase(value) {
|
||||
return String(value || '')
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.filter(Boolean)
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
function getCodeFromValue(needle, haystack, fallback) {
|
||||
let normalised = titleCase(needle);
|
||||
let match = haystack.find(item => item[1].toLowerCase() === normalised.toLowerCase());
|
||||
|
||||
return match ? match[0] : fallback;
|
||||
}
|
||||
|
||||
function mapGender(gender) {
|
||||
let value = String(gender || '').toLowerCase();
|
||||
|
||||
if (['male', 'lelaki', 'l', 'm'].includes(value)) {
|
||||
return 'Lelaki';
|
||||
}
|
||||
|
||||
if (['female', 'perempuan', 'p', 'f'].includes(value)) {
|
||||
return 'Perempuan';
|
||||
}
|
||||
|
||||
return gender || '';
|
||||
}
|
||||
|
||||
function formatDateOfBirth(dob) {
|
||||
if (!dob) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let hyphenated = String(dob).includes('-')
|
||||
? String(dob)
|
||||
: String(dob).replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
||||
|
||||
let [year, month, day] = hyphenated.split('-');
|
||||
|
||||
if (!year || !month || !day) {
|
||||
return dob;
|
||||
}
|
||||
|
||||
return day + '/' + month + '/' + year;
|
||||
}
|
||||
|
||||
function getAgeFromDateOfBirth(dob) {
|
||||
if (!dob) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let hyphenated = String(dob).includes('-')
|
||||
? String(dob)
|
||||
: String(dob).replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
||||
|
||||
let birthDate = new Date(hyphenated);
|
||||
if (Number.isNaN(birthDate.getTime())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let today = new Date();
|
||||
let age = today.getFullYear() - birthDate.getFullYear();
|
||||
let monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age -= 1;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
||||
|
||||
function getBumiputeraStatus(race) {
|
||||
const non_bumiputera_ethnicity = ['INDIA', 'CINA', 'INDIAN', 'CHINESE'];
|
||||
return !non_bumiputera_ethnicity.includes(String(race || '').toUpperCase());
|
||||
}
|
||||
|
||||
function joinAddress(data) {
|
||||
return [data.address1, data.address2, data.address3]
|
||||
.map(line => String(line || '').trim())
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
function mapReaderErrorCode(rawMessage) {
|
||||
const error_handler_dictionary = {
|
||||
'0x80100069': 'Tiada MyKad dikesan, sila masukkan MyKad untuk dibaca',
|
||||
'0x8010000C': 'Tiada MyKad dikesan, sila masukkan MyKad untuk dibaca',
|
||||
'0x80100066': 'MyKad mungkin dimasukkan terbalik, sila masukkan dengan betul',
|
||||
'0x80100067': 'MyKad tidak dapat dikesan. Sila cabut dan masukkan semula kad',
|
||||
'0x80100016': 'Gagal membaca MyKad. Sila pastikan kad dimasukkan dengan betul dan cuba lagi',
|
||||
'0x8010002E': 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan',
|
||||
'0x80100017': 'Pembaca MyKad tidak dapat dikesan, sila periksa sambungan',
|
||||
};
|
||||
|
||||
let message = String(rawMessage || '');
|
||||
let codes = message.match(/0x[0-9a-fA-F]+/g) || [];
|
||||
let matched = codes.reverse().find(code => error_handler_dictionary[code]);
|
||||
|
||||
return matched ? error_handler_dictionary[matched] : (message || null);
|
||||
}
|
||||
|
||||
function getReaderErrorMessage(payload) {
|
||||
if (!payload) {
|
||||
return 'Pembaca MyKad tidak dapat membaca kad. Sila cuba lagi.';
|
||||
}
|
||||
|
||||
let raw = payload.message || payload.error || payload.detail || '';
|
||||
return mapReaderErrorCode(raw)
|
||||
|| 'Pembaca MyKad tidak dapat membaca kad. Sila pastikan MyKad dimasukkan dengan betul.';
|
||||
}
|
||||
|
||||
function getFetchErrorTitle(error) {
|
||||
if (error && error.name === 'AbortError') {
|
||||
return 'Masa menunggu pembaca MyKad telah tamat. Sila cuba lagi.';
|
||||
}
|
||||
|
||||
return 'Pembaca MyKad tidak dapat dikesan. Sila pastikan perkhidmatan pembaca MyKad sedang berjalan.';
|
||||
}
|
||||
|
||||
function getDisplayError(error) {
|
||||
if (!error) {
|
||||
return getFetchErrorTitle(error);
|
||||
}
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
return getFetchErrorTitle(error);
|
||||
}
|
||||
|
||||
let message = String(error.message || '');
|
||||
if (!message || message.startsWith('Failed to fetch') || message.includes('NetworkError')) {
|
||||
return getFetchErrorTitle(error);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
async function handleMyKadReader() {
|
||||
showLoader();
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), MYKAD_READER_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const response = await fetch(getMyKadReaderEndpoint(), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
const payload = await parseJsonResponse(response);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(getReaderErrorMessage(payload));
|
||||
}
|
||||
|
||||
if (!payload || payload.ok !== true || !payload.data) {
|
||||
throw new Error(getReaderErrorMessage(payload));
|
||||
}
|
||||
|
||||
await handleMyKadData(payload.data);
|
||||
} catch (error) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: getDisplayError(error),
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
hideLoader();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMyKadData(data) {
|
||||
return checkIfMyKadExists(data.nric)
|
||||
.then(response => {
|
||||
let isResponseArray = Array.isArray(response);
|
||||
let resolved_response = isResponseArray ? response.pop() : response;
|
||||
|
||||
if (!resolved_response || resolved_response === 'no-data') {
|
||||
populateDaftarPelangganModal(data);
|
||||
} else {
|
||||
let input_search_noic = document.querySelector("form[name='frmsearch'] input[name='noic']");
|
||||
let form_search_noic = document.querySelector("form[name='frmsearch']");
|
||||
|
||||
input_search_noic.value = getMyKadNumberWithoutHyphens(data.nric);
|
||||
form_search_noic.submit();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
populateDaftarPelangganModal(data);
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfMyKadExists(myKadNumber) {
|
||||
let myKadNumberWithoutHyphens = getMyKadNumberWithoutHyphens(myKadNumber);
|
||||
let lookupUrl = new URL(wakil_caricustomer_endpoint, window.location.origin);
|
||||
lookupUrl.searchParams.set('icno', myKadNumberWithoutHyphens);
|
||||
|
||||
const csrfToken = document.querySelector('input[name="_token"]');
|
||||
|
||||
return fetch(lookupUrl.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"X-CSRF-Token": csrfToken ? csrfToken.value : ""
|
||||
},
|
||||
credentials: "same-origin",
|
||||
}).then(async response => {
|
||||
let text = await response.text();
|
||||
let trimmed = (text || '').trim();
|
||||
|
||||
if (!trimmed || trimmed.charAt(0) === '<') {
|
||||
return 'no-data';
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(trimmed);
|
||||
} catch (error) {
|
||||
return 'no-data';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let populateDaftarPelangganModal = (data) => {
|
||||
$('#daftar_pelanggan_mykad_reader').modal('show');
|
||||
let modal_form = document.querySelector("#daftar_pelanggan_mykad_reader .modal-body .row.form-group");
|
||||
|
||||
const religions = [
|
||||
['01', 'Islam'],
|
||||
['02', 'Hindu'],
|
||||
['03', 'Buddha'],
|
||||
['04', 'Kristian'],
|
||||
['05', 'Lain-lain'],
|
||||
];
|
||||
|
||||
const races = [
|
||||
['M', 'Melayu'],
|
||||
['C', 'Cina'],
|
||||
['I', 'India'],
|
||||
['L', 'Lain-lain'],
|
||||
];
|
||||
|
||||
let disablePointerEventsOnClickableInputs = () => {
|
||||
modal_form.querySelector("input[name='bumiputera']").style.pointerEvents = 'none';
|
||||
modal_form.querySelector("input[name='warganegara']").style.pointerEvents = 'none';
|
||||
modal_form.querySelector("select[name='bangsa']").style.pointerEvents = 'none';
|
||||
modal_form.querySelector("select[name='agama']").style.pointerEvents = 'none';
|
||||
};
|
||||
|
||||
let mykad_modal = document.querySelector("#daftar_pelanggan_mykad_reader");
|
||||
let photo_container = mykad_modal.querySelector("#photo-mykad-container");
|
||||
let photo = mykad_modal.querySelector("#photo-mykad");
|
||||
|
||||
modal_form.querySelector("textarea[name='name']").value = data.name || data.gmpc_name || '';
|
||||
modal_form.querySelector("input[name='noic']").value = getMyKadNumberWithoutHyphens(data.nric);
|
||||
modal_form.querySelector("input[name='tarikh_lahir']").value = formatDateOfBirth(data.dob);
|
||||
modal_form.querySelector("input[name='negeri_lahir']").value = data.birth_place || '';
|
||||
modal_form.querySelector("input[name='jantina']").value = mapGender(data.gender);
|
||||
modal_form.querySelector("input[name='warganegara']").checked = String(data.citizenship || '').toUpperCase() === 'WARGANEGARA';
|
||||
modal_form.querySelector("input[name='bumiputera']").checked = getBumiputeraStatus(data.race);
|
||||
modal_form.querySelector("select[name='agama']").value = getCodeFromValue(data.religion, religions, '05');
|
||||
modal_form.querySelector("textarea[name='alamat']").value = joinAddress(data);
|
||||
modal_form.querySelector("input[name='poskod']").value = data.postcode || '';
|
||||
modal_form.querySelector("input[name='daerah']").value = data.city || '';
|
||||
modal_form.querySelector("input[name='negeri']").value = data.state || '';
|
||||
modal_form.querySelector("input[name='age']").value = data.age != null && data.age !== ''
|
||||
? data.age
|
||||
: getAgeFromDateOfBirth(data.dob);
|
||||
modal_form.querySelector("select[name='bangsa']").value = getCodeFromValue(data.race, races, 'L');
|
||||
|
||||
if (data.has_photo && data.photo_base64) {
|
||||
let mime = data.photo_mime || 'image/jpeg';
|
||||
photo.src = 'data:' + mime + ';base64,' + data.photo_base64;
|
||||
photo_container.classList.remove('d-none');
|
||||
photo_container.classList.add('d-flex');
|
||||
setMykadPhotoFields(data.photo_base64, mime);
|
||||
} else {
|
||||
photo.src = original_photo_loader;
|
||||
photo_container.classList.add('d-none');
|
||||
photo_container.classList.remove('d-flex');
|
||||
setMykadPhotoFields('', '');
|
||||
}
|
||||
|
||||
disablePointerEventsOnClickableInputs();
|
||||
$(modal_form.querySelector("input[name='poskod']")).trigger('change');
|
||||
};
|
||||
|
||||
let hideMykadPhoto = () => {
|
||||
|
||||
WebSocketManager.close(() => console.log('Websocket closed'));
|
||||
|
||||
let photo = document.getElementById("photo-mykad");
|
||||
let photo_container = document.getElementById("photo-mykad-container");
|
||||
|
||||
photo.src = original_photo_loader;
|
||||
if (photo) {
|
||||
photo.src = original_photo_loader;
|
||||
}
|
||||
|
||||
photo_container.classList.add('d-none');
|
||||
photo_container.classList.remove('d-flex');
|
||||
if (photo_container) {
|
||||
photo_container.classList.add('d-none');
|
||||
photo_container.classList.remove('d-flex');
|
||||
}
|
||||
|
||||
setMykadPhotoFields('', '');
|
||||
};
|
||||
|
||||
button_read_mykad.addEventListener("click", handleMyKadReader);
|
||||
button_close_modal.addEventListener("click", hideMykadPhoto);
|
||||
function setMykadPhotoFields(base64, mime) {
|
||||
let form = document.querySelector("#daftar_pelanggan_mykad_reader form")
|
||||
|| document.getElementById("form-mykad-reader");
|
||||
let photoInput = (form && form.querySelector("input[name='photo_base64']"))
|
||||
|| document.getElementById("photo_base64");
|
||||
let mimeInput = (form && form.querySelector("input[name='photo_mime']"))
|
||||
|| document.getElementById("photo_mime");
|
||||
|
||||
if (photoInput) {
|
||||
photoInput.value = base64 || '';
|
||||
}
|
||||
|
||||
if (mimeInput) {
|
||||
mimeInput.value = mime || '';
|
||||
}
|
||||
}
|
||||
|
||||
function copyMykadPhotoFromImage() {
|
||||
let photo = document.querySelector("#daftar_pelanggan_mykad_reader #photo-mykad");
|
||||
if (!photo || !photo.src || photo.src.indexOf("base64,") === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parts = photo.src.split("base64,");
|
||||
let mimeMatch = photo.src.match(/^data:([^;]+);/);
|
||||
setMykadPhotoFields(parts[1] || "", mimeMatch ? mimeMatch[1] : "image/jpeg");
|
||||
}
|
||||
|
||||
if (button_read_mykad) {
|
||||
button_read_mykad.addEventListener("click", handleMyKadReader);
|
||||
}
|
||||
|
||||
if (button_close_modal) {
|
||||
button_close_modal.addEventListener("click", hideMykadPhoto);
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
$(document).ready(function(){
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var ppc = "{{ Auth::user()->name }}";
|
||||
var ic_asal = "{{ $customer_info['kpbaru'] != null ? $customer_info['kpbaru'] : $customer_info['kplama'] }}"
|
||||
|
||||
|
||||
@@ -48,6 +48,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
</script>
|
||||
@endsection
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
@php $auth_user = Auth::user();@endphp
|
||||
<input type="hidden" id="api" value="{{ $api_url }}"/>
|
||||
<input type="hidden" id="api" value="{{ api_url_for_browser() }}"/>
|
||||
<div class="col-md-12">
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
</div>
|
||||
<script>
|
||||
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tableaddtunai = $('#tableaddtunai').DataTable({
|
||||
@@ -558,9 +558,8 @@
|
||||
|
||||
function cetaklaporanalirantunai()
|
||||
{
|
||||
let urlroute = '{{ route("laporan.alirantunai") }}';
|
||||
|
||||
window.location.href = urlroute;
|
||||
let urlroute = '{{ route("laporan.alirantunai") }}';
|
||||
window.location.href = api_url + urlroute;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="icon" href="{{ asset('img/icon_pkb.png') }}">
|
||||
|
||||
<title>arOS v2.2</title>
|
||||
<title>arOS {{ config('app.version') }}</title>
|
||||
|
||||
<style>
|
||||
@keyframes slide {
|
||||
@@ -82,7 +82,7 @@
|
||||
<div class="card-header pb-0 text-left">
|
||||
<div style="display: flex; justify-content:space-between; align-items:center">
|
||||
<h1 class="font-weight-bolder text-info text-gradient" style="word-break: break-word; white-space: nowrap;">
|
||||
Kaunter arOS v2.2
|
||||
Kaunter arOS {{ config('app.version') }}
|
||||
</h1>
|
||||
<img src="{{ asset('img/icon_pkb.png') }}" alt="logo-arrahn" style="width: 85px; margin-left: 30px;">
|
||||
</div>
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
|
||||
|
||||
<script>
|
||||
var api_url = "{{ $api_url }}";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
// var kodcaw = "{{ $kodcaw }}";
|
||||
// var notisid = "{{ $notisid }}";
|
||||
//var nokp = "{{ $noic }}";
|
||||
|
||||
@@ -389,7 +389,7 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
$(document).ready(function(){
|
||||
|
||||
@@ -387,7 +387,7 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
$(document).ready(function(){
|
||||
|
||||
@@ -413,7 +413,7 @@
|
||||
|
||||
<script>
|
||||
var api_arrahnbid = "<?php echo config('app.api_arrahnbid') ?>";
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "{{ $cawangan_info['kodcaw'] }}";
|
||||
var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}";
|
||||
|
||||
|
||||
@@ -1099,7 +1099,7 @@
|
||||
|
||||
|
||||
let pertambahpinjaman = "{{ $pertambahpinjaman }}";
|
||||
var api = '<?php echo $api_url ?>';
|
||||
var api = "{{ api_url_for_browser() }}";
|
||||
let nw = 0;
|
||||
let jenisAT = 'none';
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@
|
||||
|
||||
<script>
|
||||
var api_arrahnbid = "<?php echo config('app.api_arrahnbid') ?>";
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "{{ $cawangan_info['kodcaw'] }}";
|
||||
var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}";
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
@props([
|
||||
'id',
|
||||
'columns' => [],
|
||||
'exportable' => true,
|
||||
'filename' => '',
|
||||
'skipExport' => ['Bil', 'Cetak'],
|
||||
])
|
||||
|
||||
<style>
|
||||
.erahn-datatable {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
.erahn-datatable .dataTables_wrapper {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.erahn-datatable table {
|
||||
width: 100% !important;
|
||||
margin: 0;
|
||||
}
|
||||
.erahn-datatable table th,
|
||||
.erahn-datatable table td {
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.erahn-datatable .dt-buttons {
|
||||
margin: 0 0 8px 8px;
|
||||
}
|
||||
.erahn-datatable .dt-button {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
margin-left: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
background: #17a2b8;
|
||||
color: #fff;
|
||||
}
|
||||
.erahn-datatable .dt-button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.erahn-datatable .buttons-excel {
|
||||
background: #28a745;
|
||||
}
|
||||
.erahn-datatable .buttons-csv {
|
||||
background: #6c757d;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
if (typeof window.initErahnDatatable !== 'function') {
|
||||
window.initErahnDatatable = function (selector, options) {
|
||||
var $table = $(selector);
|
||||
var exportable = $table.data('exportable');
|
||||
var filename = $table.data('export-filename') || $table.attr('id') || 'export';
|
||||
var defaults = {
|
||||
scrollX: true,
|
||||
autoWidth: false,
|
||||
language: {
|
||||
emptyTable: 'Tiada rekod'
|
||||
}
|
||||
};
|
||||
|
||||
if ((exportable === undefined || exportable === 1 || exportable === true) &&
|
||||
$.fn.dataTable && $.fn.dataTable.Buttons) {
|
||||
defaults.dom = 'lBfrtip';
|
||||
defaults.buttons = [
|
||||
{
|
||||
extend: 'excelHtml5',
|
||||
text: 'Excel',
|
||||
className: 'buttons-excel',
|
||||
title: filename,
|
||||
filename: filename,
|
||||
exportOptions: {
|
||||
columns: ':not(.no-export)',
|
||||
format: {
|
||||
body: function (data) {
|
||||
return $('<div>').html(data).text().replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'csvHtml5',
|
||||
text: 'CSV',
|
||||
className: 'buttons-csv',
|
||||
bom: true,
|
||||
title: filename,
|
||||
filename: filename,
|
||||
exportOptions: {
|
||||
columns: ':not(.no-export)',
|
||||
format: {
|
||||
body: function (data) {
|
||||
return $('<div>').html(data).text().replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return $table.DataTable($.extend(true, {}, defaults, options || {}));
|
||||
};
|
||||
}
|
||||
if (typeof window.adjustErahnDatatables !== 'function') {
|
||||
window.adjustErahnDatatables = function () {
|
||||
if (!$.fn.dataTable) {
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
|
||||
}, 50);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="erahn-datatable">
|
||||
<table
|
||||
id="{{ $id }}"
|
||||
{{ $attributes->merge(['class' => 'table table-striped table-hover nowrap']) }}
|
||||
style="width:100%"
|
||||
data-exportable="{{ $exportable ? 1 : 0 }}"
|
||||
data-export-filename="{{ $filename !== '' ? $filename : $id }}"
|
||||
>
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
@foreach ($columns as $column)
|
||||
<th @if(in_array($column, $skipExport, true)) class="no-export" @endif>{{ $column }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
{{ $slot }}
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,44 @@
|
||||
@props([
|
||||
'showCopies' => true,
|
||||
])
|
||||
|
||||
<style>
|
||||
.print-footer {
|
||||
margin-top: 14px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #222;
|
||||
}
|
||||
.print-footer .copies {
|
||||
margin-top: 10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.print-footer .copies span {
|
||||
display: inline-block;
|
||||
border: 1px solid #111;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin-right: 4px;
|
||||
vertical-align: -1px;
|
||||
}
|
||||
.print-footer .notes {
|
||||
margin-top: 10px;
|
||||
font-size: 9.5px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.print-footer .notes b { font-size: 10px; }
|
||||
.print-footer .notes p { margin: 2px 0; }
|
||||
</style>
|
||||
|
||||
<div {{ $attributes->merge(['class' => 'print-footer']) }}>
|
||||
@if($showCopies)
|
||||
<div class="copies">
|
||||
Salinan: <span></span> Kad Pengenalan <span></span> Akaun Bank/ Slip/ Penyata Bank
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(trim($slot) !== '')
|
||||
<div class="notes">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,101 @@
|
||||
@props([
|
||||
'cawangan' => '',
|
||||
'kodcaw' => '',
|
||||
'alamat1' => '',
|
||||
'alamat2' => '',
|
||||
'notel' => '',
|
||||
'tarikh' => '',
|
||||
'norujukan' => '',
|
||||
'rujukanLabel' => 'No. Rujukan SAG',
|
||||
'showDummyBadge' => false,
|
||||
])
|
||||
|
||||
<style>
|
||||
.print-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
align-items: start;
|
||||
column-gap: 12px;
|
||||
border-bottom: 2px solid #111;
|
||||
padding-bottom: 8px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #111;
|
||||
}
|
||||
.print-header .org {
|
||||
min-width: 0;
|
||||
}
|
||||
.print-header .org p {
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.print-header .org .name {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.print-header .logos {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.print-header .logos img {
|
||||
display: block;
|
||||
height: 40px;
|
||||
width: auto;
|
||||
max-width: 70px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.print-header .meta {
|
||||
text-align: right;
|
||||
font-size: 10px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.print-header .meta .label { color: #444; }
|
||||
.print-header .dummy-badge {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
padding: 1px 6px;
|
||||
border: 1px solid #c0392b;
|
||||
color: #c0392b;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
@media print {
|
||||
.print-header .dummy-badge { display: none !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div {{ $attributes->merge(['class' => 'print-header']) }}>
|
||||
<div class="org">
|
||||
<p class="name">KOPERASI PERMODALAN KELANTAN BERHAD (D-5-0228)</p>
|
||||
<p>Ar-Rahn {{ $cawangan }}</p>
|
||||
<p>{{ $alamat1 }}</p>
|
||||
<p>{{ $alamat2 }}</p>
|
||||
<p>Tel: {{ $notel }}</p>
|
||||
</div>
|
||||
<div class="logos">
|
||||
<img src="{{ asset('img/kopkb.png') }}" alt="KPKB">
|
||||
<img src="{{ asset('img/logo-arrahn.jpeg') }}" alt="Ar-Rahn">
|
||||
</div>
|
||||
<div class="meta">
|
||||
@if($kodcaw || $cawangan)
|
||||
<div>
|
||||
<span class="label">Cawangan</span><br>
|
||||
<b>{{ trim(implode(' — ', array_filter([$kodcaw, $cawangan]))) }}</b>
|
||||
</div>
|
||||
@endif
|
||||
@if($tarikh)
|
||||
<div style="{{ ($kodcaw || $cawangan) ? 'margin-top:6px' : '' }}"><span class="label">Tarikh</span><br><b>{{ $tarikh }}</b></div>
|
||||
@endif
|
||||
@if($norujukan)
|
||||
<div style="margin-top:6px"><span class="label">{{ $rujukanLabel }}</span><br><b>{{ $norujukan }}</b></div>
|
||||
@endif
|
||||
{{ $slot }}
|
||||
@if($showDummyBadge)
|
||||
<div class="dummy-badge">CONTOH SAHAJA</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
<style>
|
||||
.print-watermark {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.print-watermark img {
|
||||
height: 160px;
|
||||
max-width: 240px;
|
||||
object-fit: contain;
|
||||
opacity: 0.12;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="print-watermark" aria-hidden="true">
|
||||
<img src="{{ asset('img/logo_kad.jpeg') }}" alt="">
|
||||
<img src="{{ asset('img/logo-arrahn.jpeg') }}" alt="">
|
||||
</div>
|
||||
@@ -210,15 +210,17 @@
|
||||
<div class="modal fade" id="daftar_pelanggan_mykad_reader" tabindex="-1" data-backdrop="static" aria-labelledby="ModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form id="form-mykad-reader" action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="ModalLabel">Daftar Pelanggan</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
|
||||
@csrf
|
||||
<div class="row form-group">
|
||||
<div class="col-md-12 d-flex justify-content-center pb-3 d-none" id="photo-mykad-container">
|
||||
<img src="{{ asset('img/spinner-loader.gif') }}" alt="" id="photo-mykad" width="150" height="200" object-fit="cover">
|
||||
<input type="hidden" name="photo_base64" id="photo_base64" value="">
|
||||
<input type="hidden" name="photo_mime" id="photo_mime" value="">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row form-group">
|
||||
@@ -473,7 +475,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
var blacklist_search = "<?php echo $blacklist_search ?>";
|
||||
@@ -811,6 +813,8 @@
|
||||
|
||||
function verifycustomerMyKadReader(){
|
||||
|
||||
copyMykadPhotoFromImage();
|
||||
|
||||
let myKadModal = $('#daftar_pelanggan_mykad_reader');
|
||||
|
||||
// Construct URLSearchParams object instance from current URL querystring.
|
||||
@@ -1277,8 +1281,7 @@
|
||||
let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}";
|
||||
let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}";
|
||||
</script>
|
||||
<script src="{{ asset('js/iris-mykad-websocket-client-api.js') }}"></script>
|
||||
<script src="{{ asset('js/mykad-reader-customer.js') }}"></script>
|
||||
<script src="{{ asset('js/mykad-reader-customer.js') }}?v=4"></script>
|
||||
<script src="{{ asset('js/postcode-concerns.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
|
||||
@@ -21,6 +21,22 @@
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
@if(session()->has('message'))
|
||||
<div class="alert alert-success">{{ session('message') }}</div>
|
||||
@endif
|
||||
|
||||
@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="col-md-6">
|
||||
<div class="form-group row">
|
||||
@@ -422,7 +438,7 @@
|
||||
|
||||
<script>
|
||||
var api_arrahnbid = "<?php echo config('app.api_arrahnbid') ?>";
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "{{ $cawangan_info['kodcaw'] }}";
|
||||
var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}";
|
||||
var min_komoditi = "{{ $min_komoditi }}";
|
||||
|
||||
@@ -71,12 +71,27 @@
|
||||
</form>
|
||||
</div>
|
||||
@if($search == true && $blacklist_search == false)
|
||||
<div class="text-center mt-3">
|
||||
<button type="button" style="margin:auto" class="btn btn-primary" onclick="newcustomer()">
|
||||
Daftar Pelanggan
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
<div class="text-center mt-3">
|
||||
|
||||
{{-- ✅ Jika anggota tapi belum daftar pelanggan --}}
|
||||
@if(isset($showDaftarPelangganBtn) && $showDaftarPelangganBtn)
|
||||
<button type="button" style="margin:auto" class="btn btn-success" onclick="newcustomer()">
|
||||
Daftar Pelanggan
|
||||
</button>
|
||||
@endif
|
||||
|
||||
{{-- ✅ Jika pelanggan dah ada tapi belum anggota --}}
|
||||
@if(isset($showAssignAnggotaBtn) && $showAssignAnggotaBtn)
|
||||
<form action="{{ route('customers.assignAnggota') }}" method="POST" style="display:inline-block;">
|
||||
@csrf
|
||||
<input type="hidden" name="noic" value="{{ $noic }}">
|
||||
<button type="submit" class="btn btn-primary">Assign sebagai Anggota</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal No KP -->
|
||||
@@ -214,15 +229,17 @@
|
||||
<div class="modal fade" id="daftar_pelanggan_mykad_reader" tabindex="-1" data-backdrop="static" aria-labelledby="ModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form id="form-mykad-reader" action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="ModalLabel">Daftar Pelanggan</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{{ route('customer.store') }}" onsubmit="verifycustomerMyKadReader()" method="POST">
|
||||
@csrf
|
||||
<div class="row form-group">
|
||||
<div class="col-md-12 d-flex justify-content-center pb-3 d-none" id="photo-mykad-container">
|
||||
<img src="{{ asset('img/spinner-loader.gif') }}" alt="" id="photo-mykad" width="150" height="200" object-fit="cover">
|
||||
<input type="hidden" name="photo_base64" id="photo_base64" value="">
|
||||
<input type="hidden" name="photo_mime" id="photo_mime" value="">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row form-group">
|
||||
@@ -478,42 +495,72 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var isAnggota = @json($isAnggota ?? false);
|
||||
var isPelanggan = @json($isPelanggan ?? false);
|
||||
var showAssignAnggotaBtn = @json($showAssignAnggotaBtn ?? false);
|
||||
var showDaftarPelangganBtn = @json($showDaftarPelangganBtn ?? false);
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
var blacklist_search = "<?php echo $blacklist_search ?>";
|
||||
$(document).ready(function(){
|
||||
// Datepicker
|
||||
$('#passport_tarikh_lahir').datepicker({
|
||||
format: 'dd/mm/yyyy',
|
||||
endDate: new Date()
|
||||
});
|
||||
|
||||
var noic = $('#noic').val();
|
||||
identifyIC(noic);
|
||||
|
||||
if(search == true){
|
||||
if(blacklist_search == true){
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Amaran!',
|
||||
text: 'Pelanggan telah disenarai hitamkan dari Pembiayaan'
|
||||
});
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}else{
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Pelanggan Tiada Dalam Rekod!',
|
||||
text: 'Sila Daftar Pelanggan'
|
||||
});
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof identifyIC === 'function') {
|
||||
identifyIC(noic);
|
||||
}
|
||||
|
||||
// Debug: Papar semua nilai dalam console
|
||||
console.log({
|
||||
search,
|
||||
blacklist_search,
|
||||
showAssignAnggotaBtn,
|
||||
showDaftarPelangganBtn
|
||||
});
|
||||
|
||||
// Logik amaran SweetAlert
|
||||
if (search === true) {
|
||||
if (blacklist_search === true) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Amaran!',
|
||||
text: 'Pelanggan telah disenarai hitamkan dari Pembiayaan.'
|
||||
});
|
||||
}
|
||||
else if (isAnggota && !isPelanggan) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Anggota Belum Didaftarkan!',
|
||||
text: 'Pelanggan ini adalah anggota koperasi tetapi belum berdaftar sebagai pelanggan.'
|
||||
});
|
||||
}
|
||||
else if (isPelanggan && !isAnggota) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Pelanggan Bukan Anggota!',
|
||||
text: 'Pelanggan ini berdaftar tetapi belum menjadi anggota koperasi.'
|
||||
});
|
||||
}
|
||||
else if (!isAnggota && !isPelanggan) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Pelanggan Tiada Dalam Rekod!',
|
||||
text: 'Sila Daftar Pelanggan.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
function calculatePassportAge(){
|
||||
var passport_lahir = $('#passport_tarikh_lahir').val();
|
||||
var split_date = passport_lahir.split('/');
|
||||
@@ -845,6 +892,8 @@
|
||||
|
||||
function verifycustomerMyKadReader(){
|
||||
|
||||
copyMykadPhotoFromImage();
|
||||
|
||||
let myKadModal = $('#daftar_pelanggan_mykad_reader');
|
||||
|
||||
// Construct URLSearchParams object instance from current URL querystring.
|
||||
@@ -1370,8 +1419,7 @@ document.getElementsByName('form-store')[0].addEventListener('keypress', functio
|
||||
let wakil_caricustomer_endpoint = "{{ route('wakil.caricustomer') }}";
|
||||
let mykad_reader_driver_endpoint = "{{ route('mykadreader.driver') }}";
|
||||
</script>
|
||||
<script src="{{ asset('js/iris-mykad-websocket-client-api.js') }}"></script>
|
||||
<script src="{{ asset('js/mykad-reader-customer.js') }}"></script>
|
||||
<script src="{{ asset('js/mykad-reader-customer.js') }}?v=4"></script>
|
||||
<script src="{{ asset('js/postcode-concerns.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
|
||||
+3030
-2581
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,354 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ms">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Dummy — Borang Kelulusan Batch Pembiayaan Online</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: #e8eaed;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
background: #1f3a5f;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
.toolbar b { letter-spacing: 0.04em; }
|
||||
.toolbar .hint { opacity: 0.85; }
|
||||
.toolbar button {
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #1f3a5f;
|
||||
font-weight: bold;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sheet-wrap {
|
||||
padding: 24px 0 48px;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
padding: 14mm 12mm 16mm;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,.18);
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin: 14px 0 4px;
|
||||
}
|
||||
.title h1 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.title p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
background: #eef1f4;
|
||||
border: 1px solid #111;
|
||||
border-bottom: 0;
|
||||
padding: 5px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
table.fields {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
}
|
||||
table.fields th,
|
||||
table.fields td {
|
||||
border: 1px solid #111;
|
||||
padding: 6px 8px;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
table.fields th {
|
||||
width: 22%;
|
||||
font-weight: normal;
|
||||
background: #fafbfc;
|
||||
color: #222;
|
||||
}
|
||||
table.fields td.amt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
table.lines {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 9.5px;
|
||||
}
|
||||
table.lines th,
|
||||
table.lines td {
|
||||
border: 1px solid #111;
|
||||
padding: 4px 5px;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.lines thead th {
|
||||
background: #eef1f4;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 8.5px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
table.lines td.num { text-align: center; }
|
||||
table.lines td.amt { text-align: right; white-space: nowrap; }
|
||||
table.lines tfoot td {
|
||||
font-weight: bold;
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.akuan {
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
border: 1px solid #111;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.akuan ol {
|
||||
margin: 6px 0 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.akuan li { margin-bottom: 4px; }
|
||||
|
||||
.signs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.sign-box {
|
||||
border: 1px solid #111;
|
||||
min-height: 160px;
|
||||
padding: 8px 10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.sign-box .role {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sign-line {
|
||||
margin-top: 72px;
|
||||
border-top: 1px solid #111;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.sign-line p { margin: 2px 0; }
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 10mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.toolbar { display: none !important; }
|
||||
.sheet-wrap { padding: 0; }
|
||||
.sheet {
|
||||
width: auto;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@php
|
||||
$cawangan = 'KOTA BHARU 2';
|
||||
$kodcaw = 'KB2';
|
||||
$alamat1 = 'Lot 123, Jalan Sultan Yahya Petra';
|
||||
$alamat2 = '15200 Kota Bharu, Kelantan';
|
||||
$notel = '09-123 4567';
|
||||
$tarikh = '03/09/2026';
|
||||
$batchNo = 'KB2-PO-20260903-B1-001';
|
||||
$slot = 'BATCH1';
|
||||
$status = 'SUBMITTED';
|
||||
$dihantarOleh = 'NUR AINA BINTI HASSAN';
|
||||
$dihantarPada = '03/09/2026 10:42';
|
||||
$opsBy = 'MOHD FAIZ BIN ISMAIL';
|
||||
|
||||
$lines = [
|
||||
['norujukan' => 'KB2-260903-0001', 'nama' => 'AHMAD BIN ABDULLAH', 'nokp' => '900101-03-1234', 'bank' => 'MAYBANK', 'noakaun' => '123456789012', 'nama_akaun' => 'AHMAD BIN ABDULLAH', 'pinjaman' => 15000.00],
|
||||
['norujukan' => 'KB2-260903-0004', 'nama' => 'SITI MARIAM BINTI YUSOF', 'nokp' => '850215-03-5678', 'bank' => 'CIMB', 'noakaun' => '800123456789', 'nama_akaun' => 'SITI MARIAM BINTI YUSOF', 'pinjaman' => 8200.50],
|
||||
['norujukan' => 'KB2-260903-0007', 'nama' => 'MOHD RAZAK BIN ALI', 'nokp' => '780930-03-1122', 'bank' => 'BANK ISLAM', 'noakaun' => '140234567890', 'nama_akaun' => 'MOHD RAZAK BIN ALI', 'pinjaman' => 5000.00],
|
||||
['norujukan' => 'KB2-260903-0010', 'nama' => 'FATIMAH BINTI OMAR', 'nokp' => '920408-03-3344', 'bank' => 'RHB', 'noakaun' => '212345678901', 'nama_akaun' => 'FATIMAH BINTI OMAR', 'pinjaman' => 12500.00],
|
||||
['norujukan' => 'KB2-260903-0012', 'nama' => 'HASSAN BIN IBRAHIM', 'nokp' => '700112-03-5566', 'bank' => 'MAYBANK', 'noakaun' => '156789012345', 'nama_akaun' => 'HASSAN BIN IBRAHIM', 'pinjaman' => 3400.00],
|
||||
];
|
||||
$jumlah = array_sum(array_column($lines, 'pinjaman'));
|
||||
@endphp
|
||||
|
||||
<div class="toolbar">
|
||||
<div>
|
||||
<b>DUMMY FORM</b>
|
||||
<span class="hint"> — reka bentuk borang kelulusan batch pembiayaan online (Operasi). Data ini contoh sahaja.</span>
|
||||
</div>
|
||||
<button type="button" onclick="window.print()">Cetak / Pratinjau</button>
|
||||
</div>
|
||||
|
||||
<div class="sheet-wrap">
|
||||
<div class="sheet">
|
||||
<x-print.header
|
||||
:cawangan="$cawangan"
|
||||
:kodcaw="$kodcaw"
|
||||
:alamat1="$alamat1"
|
||||
:alamat2="$alamat2"
|
||||
:notel="$notel"
|
||||
:tarikh="$tarikh"
|
||||
:norujukan="$batchNo"
|
||||
rujukan-label="No. Batch"
|
||||
:show-dummy-badge="true"
|
||||
/>
|
||||
|
||||
<div class="title">
|
||||
<h1>Borang Kelulusan Pembiayaan Online<br>Senarai Batch Pembayaran</h1>
|
||||
<p>Untuk semakan Operasi dan kelulusan Pengurus Besar. Bukan untuk tandatangan pelanggan.</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">A. Maklumat Batch</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>No. Batch</th>
|
||||
<td>{{ $batchNo }}</td>
|
||||
<th>Cawangan</th>
|
||||
<td>{{ $kodcaw }} — {{ $cawangan }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tarikh / Slot</th>
|
||||
<td>{{ $tarikh }} · {{ $slot }}</td>
|
||||
<th>Status Batch</th>
|
||||
<td>{{ $status }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Dihantar Oleh (PC)</th>
|
||||
<td>{{ $dihantarOleh }}</td>
|
||||
<th>Dihantar Pada</th>
|
||||
<td>{{ $dihantarPada }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Bilangan Rekod</th>
|
||||
<td>{{ count($lines) }}</td>
|
||||
<th>Jumlah Keseluruhan</th>
|
||||
<td class="amt">RM {{ number_format($jumlah, 2) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">B. Senarai Penerima Dalam Batch</div>
|
||||
<table class="lines">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:4%">Bil</th>
|
||||
<th style="width:14%">No. SAG</th>
|
||||
<th>Pelanggan</th>
|
||||
<th style="width:13%">No. KP</th>
|
||||
<th style="width:11%">Bank</th>
|
||||
<th style="width:13%">No. Akaun</th>
|
||||
<th>Nama Akaun</th>
|
||||
<th style="width:12%">Pinjaman (RM)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($lines as $i => $line)
|
||||
<tr>
|
||||
<td class="num">{{ $i + 1 }}</td>
|
||||
<td class="num">{{ $line['norujukan'] }}</td>
|
||||
<td>{{ $line['nama'] }}</td>
|
||||
<td class="num">{{ $line['nokp'] }}</td>
|
||||
<td>{{ $line['bank'] }}</td>
|
||||
<td class="num">{{ $line['noakaun'] }}</td>
|
||||
<td>{{ $line['nama_akaun'] }}</td>
|
||||
<td class="amt">{{ number_format($line['pinjaman'], 2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="7" style="text-align:right">JUMLAH</td>
|
||||
<td class="amt">{{ number_format($jumlah, 2) }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">C. Pengesahan Operasi</div>
|
||||
<div class="akuan">
|
||||
Dengan ini disahkan bahawa senarai di Bahagian B telah disemak dan amaun pembiayaan
|
||||
sebanyak <b>RM {{ number_format($jumlah, 2) }}</b> ({{ count($lines) }} rekod)
|
||||
bagi batch <b>{{ $batchNo }}</b> cawangan <b>{{ $cawangan }}</b> adalah lengkap
|
||||
untuk proses pembayaran ke akaun bank pelanggan.
|
||||
<ol>
|
||||
<li>Maklumat SAG, nama, no. KP, bank dan no. akaun telah disemak terhadap rekod sistem.</li>
|
||||
<li>Pembayaran akan dikreditkan ke akaun penerima seperti senarai, bukan tunai di kaunter.</li>
|
||||
<li>Sebarang ralat akaun yang dikesan selepas kelulusan hendaklah dilaporkan serta-merta.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signs">
|
||||
<div class="sign-box">
|
||||
<div class="role">Tandatangan Operasi</div>
|
||||
<div class="sign-line">
|
||||
<p>Nama: {{ $opsBy }}</p>
|
||||
<p>Jawatan: Operasi</p>
|
||||
<p>Tarikh: {{ $tarikh }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sign-box">
|
||||
<div class="role">Pengurus Besar</div>
|
||||
<div class="sign-line">
|
||||
<p>Nama:</p>
|
||||
<p>Cop / Paraf:</p>
|
||||
<p>Tarikh:</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-print.footer :show-copies="false">
|
||||
<b>Nota:</b>
|
||||
<p>1. Borang ini dicetak dari skrin Operasi — Pembiayaan Online (Batch) untuk kelulusan pembayaran.</p>
|
||||
<p>2. Hanya dua tandatangan diperlukan: Operasi dan Pengurus Besar.</p>
|
||||
<p>3. Simpan salinan bersama bukti pindahan bank selepas status PAID.</p>
|
||||
</x-print.footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,322 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ms">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Dummy — Borang Pengesahan Pembiayaan Online</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: #e8eaed;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
background: #1f3a5f;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
.toolbar b { letter-spacing: 0.04em; }
|
||||
.toolbar .hint { opacity: 0.85; }
|
||||
.toolbar button {
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #1f3a5f;
|
||||
font-weight: bold;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sheet-wrap {
|
||||
padding: 24px 0 48px;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
padding: 14mm 14mm 16mm;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,.18);
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin: 14px 0 4px;
|
||||
}
|
||||
.title h1 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.title p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
background: #eef1f4;
|
||||
border: 1px solid #111;
|
||||
border-bottom: 0;
|
||||
padding: 5px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
table.fields {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
}
|
||||
table.fields th,
|
||||
table.fields td {
|
||||
border: 1px solid #111;
|
||||
padding: 6px 8px;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
table.fields th {
|
||||
width: 28%;
|
||||
font-weight: normal;
|
||||
background: #fafbfc;
|
||||
color: #222;
|
||||
}
|
||||
table.fields td.amt {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.akuan {
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
border: 1px solid #111;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.akuan ol {
|
||||
margin: 6px 0 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.akuan li { margin-bottom: 4px; }
|
||||
|
||||
.signs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.sign-box {
|
||||
border: 1px solid #111;
|
||||
min-height: 150px;
|
||||
padding: 8px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.sign-box .role {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sign-line {
|
||||
margin-top: 62px;
|
||||
border-top: 1px solid #111;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.sign-line p { margin: 2px 0; }
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 10mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.toolbar { display: none !important; }
|
||||
.sheet-wrap { padding: 0; }
|
||||
.sheet {
|
||||
width: auto;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@php
|
||||
// Dummy data — replace later with real gadai / customer / bank values
|
||||
$cawangan = 'KOTA BHARU 2';
|
||||
$kodcaw = 'KB2';
|
||||
$alamat1 = 'Lot 123, Jalan Sultan Yahya Petra';
|
||||
$alamat2 = '15200 Kota Bharu, Kelantan';
|
||||
$notel = '09-123 4567';
|
||||
$norujukan = 'KB2-260903-0001';
|
||||
$tarikh = '03/09/2026';
|
||||
$teller = 'SITI AMINAH BINTI ALI';
|
||||
$nama = 'AHMAD BIN ABDULLAH';
|
||||
$nokp = '900101-03-1234';
|
||||
$nopelanggan = 'KB2001234';
|
||||
$telefon = '012-3456789';
|
||||
$pinjaman = '15,000.00';
|
||||
$bank = 'Malayan Banking Berhad (MAYBANK)';
|
||||
$noakaun = '123456789012';
|
||||
$nama_akaun = 'AHMAD BIN ABDULLAH';
|
||||
@endphp
|
||||
|
||||
<div class="toolbar">
|
||||
<div>
|
||||
<b>DUMMY FORM</b>
|
||||
<span class="hint"> — reka bentuk borang pengesahan pembiayaan online. Data ini contoh sahaja.</span>
|
||||
</div>
|
||||
<button type="button" onclick="window.print()">Cetak / Pratinjau</button>
|
||||
</div>
|
||||
|
||||
<div class="sheet-wrap">
|
||||
<div class="sheet">
|
||||
<x-print.header
|
||||
:cawangan="$cawangan"
|
||||
:kodcaw="$kodcaw"
|
||||
:alamat1="$alamat1"
|
||||
:alamat2="$alamat2"
|
||||
:notel="$notel"
|
||||
:tarikh="$tarikh"
|
||||
:show-dummy-badge="true"
|
||||
/>
|
||||
|
||||
<div class="title">
|
||||
<h1>Borang Pengesahan Penerimaan Pembiayaan<br>Secara Online (Akaun Bank)</h1>
|
||||
<p>Untuk diisi dan ditandatangani oleh pelanggan semasa transaksi gadai.</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">A. Maklumat Pelanggan</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>Nama Pelanggan</th>
|
||||
<td colspan="3">{{ $nama }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. MyKad / Passport</th>
|
||||
<td>{{ $nokp }}</td>
|
||||
<th style="width:22%">No. Pelanggan</th>
|
||||
<td>{{ $nopelanggan }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. Telefon</th>
|
||||
<td>{{ $telefon }}</td>
|
||||
<th>Cawangan</th>
|
||||
<td>{{ $cawangan }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">B. Maklumat Pembiayaan</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>No. Surat Akuan Gadai</th>
|
||||
<td>{{ $norujukan }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Amaun Pembiayaan</th>
|
||||
<td class="amt">RM {{ $pinjaman }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Cara Terima Pembiayaan</th>
|
||||
<td>Online (kredit ke akaun bank) — bukan tunai</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Teller</th>
|
||||
<td>{{ $teller }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">C. Maklumat Akaun Bank Penerima</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>Nama Bank</th>
|
||||
<td>{{ $bank }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. Akaun</th>
|
||||
<td>{{ $noakaun }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nama Pemegang Akaun</th>
|
||||
<td>{{ $nama_akaun }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">D. Akuan Pelanggan</div>
|
||||
<div class="akuan">
|
||||
Saya, <b>{{ $nama }}</b> (No. KP: <b>{{ $nokp }}</b>), dengan ini mengesahkan bahawa:
|
||||
<ol>
|
||||
<li>Maklumat akaun bank di Bahagian C adalah benar, tepat, dan milik saya sendiri.</li>
|
||||
<li>Saya memohon supaya amaun pembiayaan gadai sebanyak <b>RM {{ $pinjaman }}</b> dikreditkan ke akaun tersebut dan <b>bukan</b> diterima secara tunai.</li>
|
||||
<li>Saya faham kredit ke akaun tertakluk kepada semakan Pengurus Cawangan dan proses pembayaran pihak Koperasi.</li>
|
||||
<li>Saya bertanggungjawab sepenuhnya jika maklumat akaun tidak tepat, dan tidak akan menuntut ganti rugi atas kelewatan atau ralat yang berpunca daripadanya.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signs">
|
||||
<div class="sign-box">
|
||||
<div class="role">Tandatangan Pelanggan</div>
|
||||
<div class="sign-line">
|
||||
<p>Nama: {{ $nama }}</p>
|
||||
<p>No. KP: {{ $nokp }}</p>
|
||||
<p>Tarikh: {{ $tarikh }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sign-box">
|
||||
<div class="role">Tandatangan Teller</div>
|
||||
<div class="sign-line">
|
||||
<p>Nama: {{ $teller }}</p>
|
||||
<p>Cop / Paraf:</p>
|
||||
<p>Tarikh: {{ $tarikh }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sign-box">
|
||||
<div class="role">Pengurus Cawangan</div>
|
||||
<div class="sign-line">
|
||||
<p>Nama:</p>
|
||||
<p>Cop cawangan:</p>
|
||||
<p>Tarikh:</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-print.footer>
|
||||
<b>Nota:</b>
|
||||
<p>1. Borang ini dicetak semasa pelanggan memilih cara terima pembiayaan secara Online.</p>
|
||||
<p>2. Pembiayaan akan dikreditkan ke akaun selepas semakan PC. Pelanggan tidak menerima wang tunai di kaunter.</p>
|
||||
<p>3. Sila pastikan nama pemegang akaun sepadan dengan nama pelanggan pada MyKad.</p>
|
||||
</x-print.footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,292 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ms">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Dummy — Maklumat Pelanggan</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: #e8eaed;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
background: #1f3a5f;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
.toolbar b { letter-spacing: 0.04em; }
|
||||
.toolbar .hint { opacity: 0.85; }
|
||||
.toolbar button {
|
||||
border: 0;
|
||||
background: #fff;
|
||||
color: #1f3a5f;
|
||||
font-weight: bold;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sheet-wrap {
|
||||
padding: 24px 0 48px;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
padding: 14mm 14mm 16mm;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,.18);
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin: 14px 0 4px;
|
||||
}
|
||||
.title h1 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.title p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.identity {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 32mm;
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.photo-box {
|
||||
width: 32mm;
|
||||
text-align: center;
|
||||
}
|
||||
.photo-box img {
|
||||
display: block;
|
||||
width: 32mm;
|
||||
height: 42mm;
|
||||
object-fit: cover;
|
||||
border: 1px solid #111;
|
||||
background: #eef1f4;
|
||||
}
|
||||
.photo-box .caption {
|
||||
margin-top: 4px;
|
||||
font-size: 8.5px;
|
||||
color: #444;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
background: #eef1f4;
|
||||
border: 1px solid #111;
|
||||
border-bottom: 0;
|
||||
padding: 5px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
table.fields {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
}
|
||||
table.fields th,
|
||||
table.fields td {
|
||||
border: 1px solid #111;
|
||||
padding: 6px 8px;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
table.fields th {
|
||||
width: 28%;
|
||||
font-weight: normal;
|
||||
background: #fafbfc;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 10mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.toolbar { display: none !important; }
|
||||
.sheet-wrap { padding: 0; }
|
||||
.sheet {
|
||||
width: auto;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@php
|
||||
$cawangan = 'KOTA BHARU 2';
|
||||
$kodcaw = 'KB2';
|
||||
$alamat1 = 'Lot 123, Jalan Sultan Yahya Petra';
|
||||
$alamat2 = '15200 Kota Bharu, Kelantan';
|
||||
$notel = '09-123 4567';
|
||||
$tarikh = now()->format('d/m/Y');
|
||||
|
||||
$nama = 'AHMAD BIN ABDULLAH';
|
||||
$nokp = '900101-03-1234';
|
||||
$nopelanggan = 'KB2001234';
|
||||
$telefon = '012-3456789';
|
||||
$telefon2 = '09-765 4321';
|
||||
$alamat = 'Lot 45, Kampung Sireh';
|
||||
$poskod = '15050';
|
||||
$daerah = 'Kota Bharu';
|
||||
$negeri = 'Kelantan';
|
||||
$bank = 'Malayan Banking Berhad (MAYBANK)';
|
||||
$noakaun = '123456789012';
|
||||
$pekerjaan = 'Kakitangan Kerajaan';
|
||||
$nota = 'Pelanggan tetap cawangan KB2.';
|
||||
$status = 'Anggota Koperasi';
|
||||
|
||||
$photo_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="320" viewBox="0 0 240 320">'
|
||||
. '<rect fill="#d9dee5" width="240" height="320"/>'
|
||||
. '<circle cx="120" cy="118" r="52" fill="#8b949e"/>'
|
||||
. '<ellipse cx="120" cy="286" rx="90" ry="92" fill="#8b949e"/>'
|
||||
. '</svg>';
|
||||
$photo_src = 'data:image/svg+xml;base64,' . base64_encode($photo_svg);
|
||||
@endphp
|
||||
|
||||
<div class="toolbar">
|
||||
<div>
|
||||
<b>DUMMY FORM</b>
|
||||
<span class="hint"> — reka bentuk cetakan A4 maklumat pelanggan. Data ini contoh sahaja.</span>
|
||||
</div>
|
||||
<button type="button" onclick="window.print()">Cetak / Pratinjau</button>
|
||||
</div>
|
||||
|
||||
<div class="sheet-wrap">
|
||||
<div class="sheet">
|
||||
<x-print.header
|
||||
:cawangan="$cawangan"
|
||||
:kodcaw="$kodcaw"
|
||||
:alamat1="$alamat1"
|
||||
:alamat2="$alamat2"
|
||||
:notel="$notel"
|
||||
:tarikh="$tarikh"
|
||||
:norujukan="$nopelanggan"
|
||||
rujukan-label="No. Pelanggan"
|
||||
:show-dummy-badge="true"
|
||||
/>
|
||||
|
||||
<div class="title">
|
||||
<h1>Maklumat Pelanggan</h1>
|
||||
<p>Salinan maklumat pengenalan diri pelanggan untuk rujukan cawangan.</p>
|
||||
</div>
|
||||
|
||||
<div class="identity">
|
||||
<div>
|
||||
<div class="section-title">A. Pengenalan Diri</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<td>{{ $nama }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. Kad Pengenalan</th>
|
||||
<td>{{ $nokp }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. Pelanggan</th>
|
||||
<td>{{ $nopelanggan }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>{{ $status }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="photo-box">
|
||||
<img src="{{ $photo_src }}" alt="Gambar MyKad">
|
||||
<div class="caption">Gambar MyKad</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">B. Maklumat Hubungan & Alamat</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>No. Telefon 1</th>
|
||||
<td>{{ $telefon }}</td>
|
||||
<th style="width:22%">No. Telefon 2</th>
|
||||
<td>{{ $telefon2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Alamat</th>
|
||||
<td colspan="3">{{ $alamat }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Poskod</th>
|
||||
<td>{{ $poskod }}</td>
|
||||
<th>Daerah</th>
|
||||
<td>{{ $daerah }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Negeri</th>
|
||||
<td colspan="3">{{ $negeri }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">C. Bank & Pekerjaan</div>
|
||||
<table class="fields">
|
||||
<tr>
|
||||
<th>Bank</th>
|
||||
<td colspan="3">{{ $bank }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>No. Akaun</th>
|
||||
<td colspan="3">{{ $noakaun }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Pekerjaan</th>
|
||||
<td colspan="3">{{ $pekerjaan }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nota</th>
|
||||
<td colspan="3">{{ $nota }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<x-print.footer :show-copies="false">
|
||||
<b>Nota:</b>
|
||||
<p>1. Borang ini dicetak dari skrin Maklumat Pelanggan untuk rujukan cawangan.</p>
|
||||
<p>2. Gambar diambil daripada rekod MyKad pelanggan dalam sistem.</p>
|
||||
</x-print.footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -34,7 +34,7 @@
|
||||
getPusherResponse();
|
||||
|
||||
async function getPusherResponse(){
|
||||
let api_url = "<?php echo $api_url ?>";
|
||||
let api_url = "{{ api_url_for_browser() }}";
|
||||
const jsonData = {
|
||||
norujukan: "{{ $errorapproval['norujukan'] }}",
|
||||
kodcaw: "{{ Auth::user()->cawangan }}",
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
$('form input').keydown(function (e) {
|
||||
if (e.keyCode == 13) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
@endphp
|
||||
@endif
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
$('form input').keydown(function (e) {
|
||||
if (e.keyCode == 13) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -326,7 +326,7 @@
|
||||
</div>
|
||||
<script src="{{ asset('js/rounded-methods.js') }}"></script>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw_ujrah = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
$('form input').keydown(function (e) {
|
||||
@@ -768,7 +768,7 @@
|
||||
|
||||
// Call API to get specific MaxMutuEmas data by karat
|
||||
$.ajax({
|
||||
url: '{{ config("api.url") }}/api/maxmutuemas/specific',
|
||||
url: '{{ api_url_for_browser() }}/api/maxmutuemas/specific',
|
||||
type: 'GET',
|
||||
data: { karat: karat, nilaimarhun: nm },
|
||||
success: function(response) {
|
||||
@@ -1026,7 +1026,7 @@
|
||||
'harga' : harga_edit,
|
||||
'n_marhun' : nilai_marhun_edit,
|
||||
'marhun' : marhun_edit,
|
||||
// 'nota' : nota,
|
||||
'nota' : nota,
|
||||
'tag_permata' : tagpermata_edit,
|
||||
'berat_batu_permata' : beratpermata_edit,
|
||||
"teller": "{{ Auth::user()->name }}",
|
||||
@@ -1816,7 +1816,7 @@
|
||||
rowsData.forEach((rowData) => {
|
||||
// Call API to get margin data for each individual item
|
||||
$.ajax({
|
||||
url: '{{ config("api.url") }}/api/maxmutuemas/specific',
|
||||
url: '{{ api_url_for_browser() }}/api/maxmutuemas/specific',
|
||||
type: 'GET',
|
||||
data: { karat: rowData.karat, nilaimarhun: rowData.nilaiMarhun },
|
||||
success: function(response) {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<div class="form-group row col-12">
|
||||
<input type="hidden" name="kodcaw" id="kodcaw" value="{{$cawangan_info['kodcaw']}}">
|
||||
<input type="hidden" name="nokp" id="nokp" value="{{ $customer_info['kpbaru'] }}">
|
||||
<input type="hidden" name="tag_anggota" id="tag_anggota" value="{{ $customer_info['tag_anggota'] ?? 0 }}">
|
||||
|
||||
</div>
|
||||
<div class="col-12 mb-2">
|
||||
@@ -217,6 +218,68 @@
|
||||
</div>
|
||||
</diV>
|
||||
</div>
|
||||
<div class="col-12" id="cara_terima_section" style="display:none;">
|
||||
<hr>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
Cara Terima Pembiayaan
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cara_terima" id="cara_terima_tunai" value="TUNAI" checked>
|
||||
<label class="form-check-label" for="cara_terima_tunai">Tunai</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cara_terima" id="cara_terima_online" value="ONLINE">
|
||||
<label class="form-check-label" for="cara_terima_online">Online (Akaun Bank)</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">Pilihan Online untuk pembiayaan RM5,000 – RM50,000 sahaja.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bank_online_section" style="display:none;">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">Modal Online</div>
|
||||
<div class="col-md-9">
|
||||
<div id="modal_online_status" class="mb-1">Memeriksa baki...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">Bank</div>
|
||||
<div class="col-md-4">
|
||||
@php
|
||||
$banks_list = Http::withOptions(['verify' => config('app.api_verify')])
|
||||
->get($api_url . '/api/bank')
|
||||
->json();
|
||||
@endphp
|
||||
<select class="form-control" name="kodbank" id="gadai_kodbank">
|
||||
<option value="">-- Pilih Bank --</option>
|
||||
@foreach ($banks_list as $banks)
|
||||
<option value="{{ $banks['abbreviation'] }}"
|
||||
{{ ($customer_info['kodbank'] ?? '') == $banks['abbreviation'] ? 'selected' : '' }}
|
||||
data-nama="{{ $banks['name'] }}">
|
||||
{{ $banks['name'] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">No. Akaun</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" name="noakaun" id="gadai_noakaun"
|
||||
value="{{ $customer_info['noakaun'] ?? '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">Nama Akaun</div>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control" name="nama_akaun" id="gadai_nama_akaun"
|
||||
value="{{ $customer_info['nama'] ?? '' }}">
|
||||
<input type="hidden" name="nama_bank" id="gadai_nama_bank" value="">
|
||||
<input type="hidden" name="nama_pelanggan" value="{{ $customer_info['nama'] ?? '' }}">
|
||||
<input type="hidden" name="nopelanggan" id="nopelanggan" value="{{ $customer_info['nopelanggan'] ?? '' }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<input type="text" name="norujukan_gadai" id="norujukan_gadai" value="{{ $norujukan }}" hidden>
|
||||
<input type="text" name="marhun_detail" id="marhun_detail" value="" hidden>
|
||||
@@ -224,8 +287,10 @@
|
||||
<input type="text" name="kadarkeuntungan" id="kadarkeuntungan" value="" hidden>
|
||||
<input type="text" name="kuantiti_marhun" id="kuantiti_marhun" value="" hidden>
|
||||
<input type="text" name="total_nilai_marhun" id="total_nilai_marhun" value="" hidden>
|
||||
<input type="text" name="nokp" id="nokp" value="{{ !empty($customer_info['kpbaru']) ? $customer_info['kpbaru'] : ($customer_info['kplama'] ?? '') }}" hidden>
|
||||
<a href="{{ route('gadai.batal',['norujukan'=>$norujukan]) }}" class="float-right btn btn-danger mr-2">Batal</a>
|
||||
<input type="submit" name="terima_gadai" id="terima_gadai" class="float-right btn btn-primary mr-2" value="Terima">
|
||||
<button type="button" id="cetak_borang_online" class="float-right btn btn-info mr-2" style="display:none;">Cetak Borang</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -330,7 +395,7 @@
|
||||
@endif
|
||||
<script src="{{ asset('js/rounded-methods.js') }}"></script>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw_ujrah = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
$('form input').keydown(function (e) {
|
||||
if (e.keyCode == 13) {
|
||||
@@ -629,17 +694,23 @@
|
||||
|
||||
|
||||
$.ajax({
|
||||
method:'GET',
|
||||
url: api_url + '/api/kadarujrah',
|
||||
async:false,
|
||||
data:{
|
||||
'nilaimarhun' : total_nilai_marhun,
|
||||
'kodcaw' : kodcaw_ujrah
|
||||
},
|
||||
success:function(data){
|
||||
kadarupah = data;
|
||||
}
|
||||
});
|
||||
method:'GET',
|
||||
url: api_url + '/api/kadarujrah',
|
||||
async:false,
|
||||
data:{
|
||||
'nokp': $('#nokp').val(),
|
||||
'nilaimarhun' : total_nilai_marhun,
|
||||
'kodcaw' : kodcaw_ujrah
|
||||
},
|
||||
success:function(data){
|
||||
kadarupah = data;
|
||||
|
||||
// Jika anggota (tag_anggota = 1), kadar ujrah dipaksa 0.00
|
||||
if(Number($('#tag_anggota').val()) === 1){
|
||||
kadarupah = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#total_nilai_marhun').val(formatter.format(total_nilai_marhun));
|
||||
$('#marhun_detail').val(marhun_details);
|
||||
@@ -679,6 +750,10 @@
|
||||
$('#pinjaman').on('input',function(){
|
||||
var pinjaman = Number(this.value);
|
||||
var nilaimarhun = Number($('#jumlah_nilai_marhun').val());
|
||||
var nokp = $('#nokp').val();
|
||||
//console.log('DEBUG NOKP:', nokp, kadarupah);
|
||||
|
||||
toggleCaraTerima(pinjaman);
|
||||
|
||||
// var keuntungan_sebulan = pinjaman * kadarupah;
|
||||
// var keuntungan_6bln = keuntungan_sebulan * 6;
|
||||
@@ -692,6 +767,7 @@
|
||||
url: api_url + '/api/onepercent',
|
||||
async:false,
|
||||
data:{
|
||||
'nokp': nokp,
|
||||
'nilaimarhun' : total_nilai_marhun,
|
||||
'pembiayaan' : pinjaman,
|
||||
'kadarujrah' : kadarupah,
|
||||
@@ -1189,9 +1265,207 @@
|
||||
.then(response => response.json());
|
||||
}
|
||||
|
||||
function toggleCaraTerima(pinjaman) {
|
||||
if (pinjaman >= 5000 && pinjaman <= 50000) {
|
||||
$('#cara_terima_section').show();
|
||||
toggleBankOnlineSection();
|
||||
} else {
|
||||
$('#cara_terima_section').hide();
|
||||
$('#bank_online_section').hide();
|
||||
$('#cetak_borang_online').hide();
|
||||
$('#cara_terima_tunai').prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleBankOnlineSection() {
|
||||
if ($('input[name="cara_terima"]:checked').val() === 'ONLINE') {
|
||||
$('#bank_online_section').show();
|
||||
$('#cetak_borang_online').show();
|
||||
var nama = $('#gadai_kodbank option:selected').data('nama') || '';
|
||||
$('#gadai_nama_bank').val(nama);
|
||||
refreshModalOnlineStatus();
|
||||
} else {
|
||||
$('#bank_online_section').hide();
|
||||
$('#cetak_borang_online').hide();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on('change', 'input[name="cara_terima"]', function () {
|
||||
toggleBankOnlineSection();
|
||||
});
|
||||
|
||||
$(document).on('change', '#gadai_kodbank', function () {
|
||||
$('#gadai_nama_bank').val($('#gadai_kodbank option:selected').data('nama') || '');
|
||||
});
|
||||
|
||||
$(document).on('click', '#cetak_borang_online', function () {
|
||||
var kodbank = $('#gadai_kodbank').val();
|
||||
var noakaun = ($('#gadai_noakaun').val() || '').trim();
|
||||
var namaAkaun = ($('#gadai_nama_akaun').val() || '').trim();
|
||||
var namaBank = $('#gadai_kodbank option:selected').data('nama') || $('#gadai_nama_bank').val() || '';
|
||||
var pinjaman = String($('#pinjaman').val() || '').replace(/,/g, '');
|
||||
|
||||
if (!kodbank || !noakaun) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Maklumat bank tidak lengkap',
|
||||
text: 'Sila lengkapkan bank dan no. akaun sebelum cetak borang. Anda boleh betulkan dan cetak semula tanpa tekan Terima.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var form = $('<form>', {
|
||||
method: 'POST',
|
||||
action: '{{ route("gadai.print.borang.online.preview") }}',
|
||||
target: '_blank'
|
||||
});
|
||||
form.append($('<input>', { type: 'hidden', name: '_token', value: '{{ csrf_token() }}' }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'norujukan', value: $('#norujukan_gadai').val() || $('#norujukan').val() || '' }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'nokp', value: $('#nokp').val() || '' }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'nopelanggan', value: $('#nopelanggan').val() || '' }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'nama_pelanggan', value: $('input[name="nama_pelanggan"]').val() || '' }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'pinjaman', value: pinjaman }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'kodbank', value: kodbank }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'nama_bank', value: namaBank }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'noakaun', value: noakaun }));
|
||||
form.append($('<input>', { type: 'hidden', name: 'nama_akaun', value: namaAkaun }));
|
||||
form.appendTo('body').trigger('submit').remove();
|
||||
});
|
||||
|
||||
async function getModalOnline() {
|
||||
let kodcaw = $('#kodcaw').val();
|
||||
let apiUrl = api_url + '/api/modal-online/' + kodcaw;
|
||||
return $.ajax({
|
||||
method: 'GET',
|
||||
url: apiUrl,
|
||||
dataType: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshModalOnlineStatus() {
|
||||
var $el = $('#modal_online_status');
|
||||
$el.removeClass('text-success text-danger text-warning text-muted').addClass('text-muted').text('Memeriksa baki...');
|
||||
try {
|
||||
var modal = await getModalOnline();
|
||||
var pending = modal.pending_request || null;
|
||||
var closed = Number(modal.ditutup || 0) === 1;
|
||||
var missing = !!modal.missing;
|
||||
var baki = Number(modal.baki || 0);
|
||||
var had = Number(modal.had || 0) + Number(modal.tambah || 0);
|
||||
|
||||
if (closed) {
|
||||
$el.removeClass('text-muted').addClass('text-danger')
|
||||
.text('Modal online hari ini sudah ditutup (EOD). Gadai online tidak dibenarkan.');
|
||||
return modal;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
$el.removeClass('text-muted').addClass('text-warning')
|
||||
.text('Menunggu kelulusan Operasi: RM ' + formatter.format(Number(pending.amaun || 0)) +
|
||||
' (' + (pending.dimohon_oleh || '-') + ').');
|
||||
} else if (missing) {
|
||||
$el.removeClass('text-muted').addClass('text-danger')
|
||||
.text('Belum ada had hari ini. Sila hubungi PC.');
|
||||
} else {
|
||||
$el.removeClass('text-muted').addClass('text-success')
|
||||
.text('Baki RM ' + formatter.format(baki) + ' / Had RM ' + formatter.format(had));
|
||||
}
|
||||
return modal;
|
||||
} catch (e) {
|
||||
$el.removeClass('text-muted').addClass('text-danger').text('Gagal semak modal online.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function checkDisbursementFloat(pinjaman) {
|
||||
var cara = $('input[name="cara_terima"]:checked').val() || 'TUNAI';
|
||||
if (!(pinjaman >= 5000 && pinjaman <= 50000)) {
|
||||
cara = 'TUNAI';
|
||||
}
|
||||
|
||||
if (cara === 'ONLINE') {
|
||||
var kodbank = $('#gadai_kodbank').val();
|
||||
var noakaun = ($('#gadai_noakaun').val() || '').trim();
|
||||
$('#gadai_nama_bank').val($('#gadai_kodbank option:selected').data('nama') || '');
|
||||
if (!kodbank || !noakaun) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Sila lengkapkan maklumat bank untuk pembayaran online.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
var modal;
|
||||
try {
|
||||
modal = await getModalOnline();
|
||||
} catch (e) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Gagal semak modal online. Sila cuba lagi.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number(modal.ditutup || 0) === 1) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Modal online hari ini sudah ditutup (EOD). Sila pilih Tunai.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modal.pending_request) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Menunggu Operasi',
|
||||
text: 'Permohonan RM ' + formatter.format(Number(modal.pending_request.amaun || 0)) +
|
||||
' masih PENDING. Sila tunggu kelulusan Operasi atau pilih Tunai.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modal.missing) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Belum ada had modal online hari ini. Sila hubungi PC.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number(modal.baki || 0) < pinjaman) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Baki modal online tidak mencukupi. Sila hubungi PC untuk mohon tambah modal, atau pilih Tunai.'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let dataCawangan = await getDataCawangan();
|
||||
let bakiAkhir = dataCawangan.baki;
|
||||
if (bakiAkhir < pinjaman) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Baki Akhir Tunai Lebih Rendah Daripada Nilai Marhun Yang Ingin Digadaikan. Sila Tambah Pendahuluan di Laman Aliran Tunai Segera. '
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function verifyGadai(func){
|
||||
$("#terima_gadai").prop('disabled', true);
|
||||
event.preventDefault();
|
||||
if (typeof event !== 'undefined' && event && event.preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
var kodcaw = $('#kodcaw').val();
|
||||
var nokp = $('#nokp').val();
|
||||
var nopelanggan = $('#nopelanggan').val();
|
||||
@@ -1217,24 +1491,17 @@
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Pembiayaan Tidak Lengkap'
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
let floatOk = await checkDisbursementFloat(pinjaman);
|
||||
if (!floatOk) {
|
||||
$("#terima_gadai").prop('disabled', false);
|
||||
return false;
|
||||
}
|
||||
|
||||
let dataCawangan = await getDataCawangan();
|
||||
let bakiAkhir = dataCawangan.baki;
|
||||
|
||||
if(bakiAkhir < pinjaman){
|
||||
$("#terima_gadai").prop('disabled', false);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Amaran!',
|
||||
text: 'Baki Akhir Tunai Lebih Rendah Daripada Nilai Marhun Yang Ingin Digadaikan. Sila Tambah Pendahuluan di Laman Aliran Tunai Segera. '
|
||||
});
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}else{
|
||||
if(true){
|
||||
|
||||
if($.isNumeric(pinjaman) == false){
|
||||
$("#terima_gadai").prop('disabled', false);
|
||||
@@ -1975,7 +2242,7 @@
|
||||
rowsData.forEach((rowData) => {
|
||||
// Call API to get margin data for each individual item
|
||||
$.ajax({
|
||||
url: '{{ config("api.url") }}/api/maxmutuemas/specific',
|
||||
url: '{{ api_url_for_browser() }}/api/maxmutuemas/specific',
|
||||
type: 'GET',
|
||||
data: { karat: rowData.karat, nilaimarhun: rowData.nilaiMarhun },
|
||||
success: function(response) {
|
||||
|
||||
@@ -262,6 +262,10 @@
|
||||
<a href="{{ url('pendahuluanserahan') }}"
|
||||
class="btn btn-info btn-lg btn-block mb-3">Pendahuluan - Serahan</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a href="{{ route('pembiayaan.online.pc') }}"
|
||||
class="btn btn-info btn-lg btn-block mb-3">Pembiayaan Online</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a href="{{ route('khazanah.indexwakil') }}"
|
||||
class="btn btn-info btn-lg btn-block mb-3">Surat Wakil</a>
|
||||
@@ -274,11 +278,9 @@
|
||||
<div class="row">
|
||||
<a href="{{ route('lelong') }}" class="btn btn-info btn-lg btn-block mb-3">Lelong</a>
|
||||
</div>
|
||||
@if(Auth::user()->cawangan == '011237')
|
||||
<div class="row">
|
||||
<a href="{{ url('panjar') }}" class="btn btn-info btn-lg btn-block mb-3">Perbelanjaan</a>
|
||||
</div>
|
||||
@endif
|
||||
<div class="row">
|
||||
<a href="{{ url('alirantunai') }}" class="btn btn-info btn-lg btn-block mb-3">Aliran
|
||||
Tunai</a>
|
||||
@@ -322,7 +324,7 @@
|
||||
});
|
||||
|
||||
|
||||
let api = "{{ config('api.url') }}";
|
||||
let api = "{{ api_url_for_browser() }}";
|
||||
let kodcaw = "{{ Auth::user()->cawangan }}";
|
||||
|
||||
const initialambilan = [
|
||||
|
||||
@@ -14,12 +14,16 @@
|
||||
</nav>
|
||||
|
||||
<div class="card-body text-center">
|
||||
<a href="{{ route('operasi.kelulusan') }}" class="btn btn-info btn-lg">Kelulusan</a>
|
||||
<a href="{{ route('daftar.kakitangan') }}" class="btn btn-info btn-lg">Kakitangan</a>
|
||||
<a href="{{ route('parameter') }}" class="btn btn-info btn-lg">Parameter</a>
|
||||
<a href="{{ route('komuditi') }}" class="btn btn-info btn-lg">Komoditi</a>
|
||||
<a href="{{ route('perubatan') }}" class="btn btn-info btn-lg">Perubatan</a>
|
||||
<a href="{{ route('operasi.laporan.audit') }}" class="btn btn-info btn-lg">Laporan Audit</a>
|
||||
<a href="{{ route('operasi.kelulusananggota') }}" class="btn btn-info btn-lg d-inline-block m-1">Kelulusan Anggota</a>
|
||||
<a href="{{ route('operasi.kelulusan') }}" class="btn btn-info btn-lg d-inline-block m-1">Kelulusan</a>
|
||||
<a href="{{ route('pembiayaan.online.operasi') }}" class="btn btn-info btn-lg d-inline-block m-1">Pembiayaan Online</a>
|
||||
<a href="{{ route('pembiayaan.online.operasi.topup') }}" class="btn btn-info btn-lg d-inline-block m-1">Modal Online</a>
|
||||
<a href="{{ route('pembiayaan.online.operasi.eod') }}" class="btn btn-info btn-lg d-inline-block m-1">EOD Modal Online</a>
|
||||
<a href="{{ route('daftar.kakitangan') }}" class="btn btn-info btn-lg d-inline-block m-1">Kakitangan</a>
|
||||
<a href="{{ route('parameter') }}" class="btn btn-info btn-lg d-inline-block m-1">Parameter</a>
|
||||
<a href="{{ route('komuditi') }}" class="btn btn-info btn-lg d-inline-block m-1">Komoditi</a>
|
||||
<a href="{{ route('perubatan') }}" class="btn btn-info btn-lg d-inline-block m-1">Perubatan</a>
|
||||
<a href="{{ route('operasi.laporan.audit') }}" class="btn btn-info btn-lg d-inline-block m-1">Laporan Audit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -267,7 +267,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var cawangan = "{{ Auth::user()->cawangan }}";
|
||||
var teller = "{{ Auth::user()->name }}";
|
||||
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
let teller = "{{ Auth::user()->name }}";
|
||||
let kodcaw = "{{ Auth::user()->cawangan }}";
|
||||
|
||||
function pelangganclick()
|
||||
{
|
||||
// $getTunai2=Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url').'/api/GetCurrentTunai/'.$auth_user['cawangan'],['kodlaluan' => $kodlaluan])->json();
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
const d = new Date();
|
||||
let tahunsemasa = d.getFullYear();
|
||||
let pusherenv = "{{ env('PUSHER_APP_KEY') }}";
|
||||
let api_url = "{{ $api_url }}";
|
||||
let api_url = "{{ api_url_for_browser() }}";
|
||||
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
minimumFractionDigits: 2
|
||||
|
||||
@@ -230,7 +230,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var cawangan = "{{ Auth::user()->cawangan }}";
|
||||
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var search = "<?php echo $search ?>";
|
||||
var type_id = "<?php echo $idtype ?>";
|
||||
$(document).ready(function(){
|
||||
|
||||
@@ -410,7 +410,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "{{ $cawangan_info['kodcaw'] }}";
|
||||
var ppc = "{{ Auth::user()->name }}";
|
||||
var tagarrahnbid = "{{ $cawangan_info['tagarrahnbid'] }}";
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
"scrollX": true
|
||||
});
|
||||
|
||||
let api = "{{ config('api.url') }}";
|
||||
let api = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "{{ Auth::user()->cawangan }}";
|
||||
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
@@ -271,7 +271,7 @@
|
||||
|
||||
async function updatelulus(e)
|
||||
{
|
||||
let api = "{{ config('api.url') }}";
|
||||
let api = "{{ api_url_for_browser() }}";
|
||||
let kodcaw = "{{ Auth::user()->cawangan }}";
|
||||
|
||||
const { value: katalaluan } = await Swal.fire({
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
|
||||
<script>
|
||||
|
||||
let api = "{{ config('api.url') }}";
|
||||
let api = "{{ api_url_for_browser() }}";
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
$(document).ready( function () {
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tabletebus = $('#myTable').DataTable({
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablebatalgadai = $('#myTable').DataTable({
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablegadai = $('#myTable').DataTable({
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
$(document).ready( function () {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
$(document).ready( function () {
|
||||
|
||||
@@ -189,6 +189,10 @@
|
||||
<a href="{{ route('laporan.notisperingatan') }}"
|
||||
class="btn btn-dark btn-lg btn-block mb-3 custom-btn">Notis Peringatan</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a href="{{ route('laporan.imbangduga') }}"
|
||||
class="btn btn-dark btn-lg btn-block mb-3 custom-btn">Imbang Duga</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablebatalgadai = $('#myTable').DataTable({
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
// alert(btoa(api_url));
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tabletebus = $('#myTable').DataTable({
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var panjartebus = $('#myTable').DataTable({
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
$(document).ready( function () {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var api_url = "<?php echo $api_url ?>";
|
||||
var api_url = "{{ api_url_for_browser() }}";
|
||||
var kodcaw = "<?php echo $cawangan_info['kodcaw'] ?>";
|
||||
|
||||
var tablecustomers = $('#myTable').DataTable({
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user