47 Commits

Author SHA1 Message Date
afrina aac29bc199 Merge pull request 'fix: recalculate gadai totals after marhun edits' (#21) from fix/marhun-edit-gadai-totals into main
Build Docker Image / build-backend (push) Successful in 18s
Deploy to Staging / deploy (push) Successful in 17s
Reviewed-on: #21
2026-10-05 10:43:45 +08:00
nurafrinaalimi16 9e73f6ad32 fix: recalculate gadai totals after marhun edits 2026-10-05 10:39:19 +08:00
ISMAIL MASSERAN ff21d82af5 DONE: remove nginx for local docker compose 2026-10-01 13:45:34 +08:00
ismailmasseran 62c6d240ce Merge pull request 'DONE: update cicd to auto deploy on staging, clean up garbage files' (#20) from cicd/auto-deploy-staging into main
Build Docker Image / build-backend (push) Successful in 17s
Deploy to Staging / deploy (push) Successful in 17s
Reviewed-on: #20
2026-10-01 12:01:34 +08:00
ISMAIL MASSERAN 1ee7edefff DONE: update cicd to auto deploy on staging, clean up garbage files 2026-10-01 12:00:09 +08:00
ismailmasseran b52f4b6919 Merge pull request 'DONE: add password on laravel log viewer' (#19) from feature/add-pass-logviewer into main
Build Docker Image / build-backend (push) Successful in 18s
Reviewed-on: #19
2026-09-28 16:27:55 +08:00
ISMAIL MASSERAN b8e3e2b369 DONE: add password on laravel log viewer 2026-09-28 16:26:50 +08:00
ismailmasseran 3a86ba5b26 Merge pull request 'DONE: add env based checking for whatsapp before testing at staging' (#18) from feature/env-based-whatsapp-cloud into main
Build Docker Image / build-backend (push) Successful in 18s
Reviewed-on: #18
2026-09-28 10:30:11 +08:00
ISMAIL MASSERAN b1934e1627 DONE: add env based checking for whatsapp before testing at staging 2026-09-28 10:26:06 +08:00
ISMAIL MASSERAN 583d936fd2 DONE: update whatsapp message template
Build Docker Image / build-backend (push) Successful in 17s
2026-09-27 15:57:50 +08:00
ISMAIL MASSERAN 1076ecbaf2 DONE: update env.staging
Build Docker Image / build-backend (push) Successful in 26s
2026-09-26 18:51:47 +08:00
ismailmasseran 92894bad44 Merge pull request 'fix: change pembiayaan online batch 2 cutoff from 16:30 to 15:00' (#17) from fix/pembiayaan-online-batch2-cutoff into main
Reviewed-on: #17
2026-09-24 16:15:43 +08:00
nurafrinaalimi16 9916e81dba fix: change pembiayaan online batch 2 cutoff from 16:30 to 15:00 2026-09-24 16:14:44 +08:00
ismailmasseran 7fb7f75050 Feature/whatsapp blast (#16)
Build Docker Image / build-backend (push) Successful in 18s
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #16
2026-09-24 11:27:48 +08:00
ismailmasseran 9fa0172017 Experiment/merge annur (#15)
Build Docker Image / build-backend (push) Successful in 23s
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Reviewed-on: #15
2026-09-20 12:49:53 +08:00
ismailmasseran 79bc41d211 Merge pull request 'feature: include cara terima in reports' (#14) from feature/tagging-cara-terima into main
Reviewed-on: #14
2026-09-20 09:38:22 +08:00
nurafrinaalimi16 2f6f06d3e3 feature: include cara terima in reports 2026-09-17 16:23:46 +08:00
ismailmasseran 8de9d872f9 Merge pull request 'feature/modal-online-pendahuluan' (#13) from feature/modal-online-pendahuluan into main
Reviewed-on: #13
2026-09-17 07:58:26 +08:00
nurafrinaalimi16 20268cd136 feature/modal-online-pendahuluan 2026-09-14 15:26:35 +08:00
nurafrinaalimi16 aa4c8ca5ba fix laporan barang kes controller 2026-09-10 09:27:57 +08:00
ismailmasseran 7920eb9f71 Explore/ismail (#12)
Build Docker Image / build-backend (push) Successful in 10s
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Reviewed-on: #12
Co-authored-by: ismailmasseran <ismail@koppkb.com>
Co-committed-by: ismailmasseran <ismail@koppkb.com>
2026-09-09 14:47:33 +08:00
afrina 5fc212f6af Prod kopkb (#8)
Co-authored-by: nurafrinaalimi16 <nurafrinaalimi16@gmail.com>
Co-authored-by: dev_kopkb <xtrabiz.sb@gmail.com>
Co-authored-by: root <root@erahn.click>
Reviewed-on: #8
Co-authored-by: afrina <afrina@koppkb.com>
Co-committed-by: afrina <afrina@koppkb.com>
2026-09-01 15:09:35 +08:00
ismailmasseran 51860cea37 DONE: add cicd for api_arrahn (#2)
Build Docker Image / build-backend (push) Successful in 8s
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Reviewed-on: #2
2026-08-26 12:03:29 +08:00
afrina f1618c1ffd Rebate anggota (#1)
Co-authored-by: nurafrinaalimi16 <nurafrinaalimi16@gmail.com>
Reviewed-on: #1
2026-08-26 11:40:55 +08:00
nurafrinaalimi16 162f246acc function wtd baru 2026-08-03 12:43:55 +08:00
nurafrinaalimi16 95997841cf Merge remote-tracking branch 'origin/rebate-anggota' into rebate-anggota 2026-06-14 15:58:31 +08:00
nurafrinaalimi16 9ff4393313 include kplama on report marhun 2026-06-14 15:38:38 +08:00
nurafrinaalimi16 5bc5d286b4 fix figure on laporan terkumpul 2026-06-14 15:37:58 +08:00
nurafrinaalimi16 5826f92661 pdpa for koperasi 2026-06-14 15:37:25 +08:00
nurafrinaalimi16 2fa36eabf7 include kplama on report marhun 2026-06-14 15:06:24 +08:00
nurafrinaalimi16 237a5aa44b fix figure on laporan terkumpul 2026-06-14 15:05:02 +08:00
nurafrinaalimi16 e1878e31e4 pdpa for koperasi 2026-06-14 15:02:45 +08:00
nurafrinaalimi16 043c1fa83b include lahad datu on report stok audit 2025-12-23 10:21:02 +08:00
nurafrinaalimi16 b8cf2a172d fix bilangan pelanggan baru on report terkumpul 2025-12-18 10:54:32 +08:00
nurafrinaalimi16 2451969c88 disable logs kadar anggota 2025-12-14 10:37:29 +08:00
nurafrinaalimi16 bc95a60009 put excpetion if kadar upah = 0 2025-11-18 00:30:36 +08:00
nurafrinaalimi16 cb0b80428b fix kadar upah return 0 2025-11-13 23:33:11 +08:00
nurafrinaalimi16 da7883919e add column name on operasi approval 2025-11-11 22:23:37 +08:00
nurafrinaalimi16 f998d6e7dc fix error url lelong 2025-11-11 00:08:26 +08:00
nurafrinaalimi16 0aaf81f1cb fix url lelong 2025-11-11 00:05:33 +08:00
nurafrinaalimi16 42c2c80726 comment command import csv 2025-11-10 23:02:30 +08:00
nurafrinaalimi16 600cd753f0 import csv anggota koperasi 2025-11-10 19:14:29 +08:00
nurafrinaalimi16 6898a78506 route for kadar anggota 2025-11-10 19:13:29 +08:00
nurafrinaalimi16 2ebeae54ea database migration for kadar anggota koperasi 2025-11-10 19:12:26 +08:00
nurafrinaalimi16 dde4d910bb calculation for kadar anggota koperasi 2025-11-10 19:11:13 +08:00
nurafrinaalimi16 4f5edb69fb update all controllers for kadar anggota koperasi 2025-11-10 18:55:17 +08:00
nurafrinaalimi16 1075d7d585 models for kadar anggota koperasi 2025-11-10 18:51:01 +08:00
88 changed files with 7140 additions and 8799 deletions
+16
View File
@@ -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
+28
View File
@@ -0,0 +1,28 @@
APP_NAME=Lumen
APP_ENV=production
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=https://apiannurv2.erahn.com.my
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION_2=mysql
DB_HOST_2=192.168.1.216
DB_PORT_2=3306
DB_DATABASE_2=annur_tampoi
DB_USERNAME_2=spider
DB_PASSWORD_2=arrahn@123
DB_CONNECTION_7=mysql
DB_HOST_7=192.168.1.216
DB_PORT_7=3306
DB_DATABASE_7=master_annur
DB_USERNAME_7=spider
DB_PASSWORD_7=arrahn@123
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
TENNANT_MASTER_DB=master_annur
+71
View File
@@ -0,0 +1,71 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=Asia/Kuala_Lumpur
API_ARRAHNBID=http://lelong.test/api/
API_ARRAHNBID_ANNUR=http://lelong-annur.test/api/
LOG_CHANNEL=stack
LOG_LEVEL=debug
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=erahn_xtra
DB_USERNAME=root
DB_PASSWORD=root
DB_CONNECTION_7=mysql
DB_HOST_7=mysql
DB_PORT_7=3306
DB_DATABASE_7=master_erahn
DB_USERNAME_7=root
DB_PASSWORD_7=root
# DB_CONNECTION_3=mysql
# DB_HOST_3=127.0.0.1
# DB_PORT_3=3306
# DB_DATABASE_3=erahn_baling
# DB_USERNAME_3=root
# DB_PASSWORD_3=root
# DB_CONNECTION_4=mysql
# DB_HOST_4=127.0.0.1
# DB_PORT_4=3306
# DB_DATABASE_4=arrahn_ld
# DB_USERNAME_4=root
# DB_PASSWORD_4=root
# DB_CONNECTION_5=mysql
# DB_HOST_5=127.0.0.1
# DB_PORT_5=3306
# DB_DATABASE_5=erahn_segamat
# DB_USERNAME_5=root
# DB_PASSWORD_5=root
# DB_CONNECTION_6=mysql
# DB_HOST_6=127.0.0.1
# DB_PORT_6=3306
# DB_DATABASE_6=arrahn_kb2
# DB_USERNAME_6=spider
# DB_PASSWORD_6=G0dz!ll42020
# DB_CONNECTION_8=mysql
# DB_HOST_8=127.0.0.1
# DB_PORT_8=3306
# DB_DATABASE_8=arrahn_klanas
# DB_USERNAME_8=spider
# DB_PASSWORD_8=G0dz!ll42020
TENNANT_MASTER_DB=master_erahn
TENNANT_KAUNTER_DB=kaunter_erahn
KAUNTER_URL=http://host.docker.internal:8081
INTERNAL_NOTIFICATION_SECRET=erahn-local-notification-secret
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
+16 -163
View File
@@ -5,6 +5,7 @@ APP_DEBUG=true
APP_URL=http://api_arrahn.test APP_URL=http://api_arrahn.test
APP_TIMEZONE=Asia/Kuala_Lumpur APP_TIMEZONE=Asia/Kuala_Lumpur
API_ARRAHNBID=http://lelong.test/api/ API_ARRAHNBID=http://lelong.test/api/
API_ARRAHNBID_ANNUR=http://lelong-annur.test/api/
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL= LOG_SLACK_WEBHOOK_URL=
@@ -86,172 +87,24 @@ DB_DATABASE_11=arrahn_pputeh
DB_USERNAME_11=spider DB_USERNAME_11=spider
DB_PASSWORD_11=G0dz!ll42020 DB_PASSWORD_11=G0dz!ll42020
DB_CONNECTION_12=mysql
DB_HOST_12=127.0.0.1
DB_PORT_12=3306
DB_DATABASE_12=arrahn_rpanjang
DB_USERNAME_12=spider
DB_PASSWORD_12=G0dz!ll42020
DB_CONNECTION_13=mysql
DB_HOST_13=127.0.0.1
DB_PORT_13=3306
DB_DATABASE_13=arrahn_tmerah
DB_USERNAME_13=spider
DB_PASSWORD_13=G0dz!ll42020
DB_CONNECTION_14=mysql
DB_HOST_14=127.0.0.1
DB_PORT_14=3306
DB_DATABASE_14=arrahn_wb
DB_USERNAME_14=spider
DB_PASSWORD_14=G0dz!ll42020
DB_CONNECTION_15=mysql
DB_HOST_15=127.0.0.1
DB_PORT_15=3306
DB_DATABASE_15=arrahn_wcy
DB_USERNAME_15=spider
DB_PASSWORD_15=G0dz!ll42020
DB_CONNECTION_16=mysql
DB_HOST_16=127.0.0.1
DB_PORT_16=3306
DB_DATABASE_16=arrahn_bentong
DB_USERNAME_16=spider
DB_PASSWORD_16=G0dz!ll42020
DB_CONNECTION_17=mysql
DB_HOST_17=192.168.0.102
DB_PORT_17=3306
DB_DATABASE_17=arrahn_kajang
DB_USERNAME_17=spider
DB_PASSWORD_17=G0dz!ll42020
DB_CONNECTION_18=mysql
DB_HOST_18=192.168.0.102
DB_PORT_18=3306
DB_DATABASE_18=arrahn_kangar
DB_USERNAME_18=spider
DB_PASSWORD_18=G0dz!ll42020
DB_CONNECTION_19=mysql
DB_HOST_19=192.168.0.102
DB_PORT_19=3306
DB_DATABASE_19=arrahn_kuantan
DB_USERNAME_19=spider
DB_PASSWORD_19=G0dz!ll42020
DB_CONNECTION_20=mysql
DB_HOST_20=192.168.0.102
DB_PORT_20=3306
DB_DATABASE_20=arrahn_pgudang
DB_USERNAME_20=spider
DB_PASSWORD_20=G0dz!ll42020
DB_CONNECTION_21=mysql
DB_HOST_21=192.168.0.102
DB_PORT_21=3306
DB_DATABASE_21=arrahn_raub
DB_USERNAME_21=spider
DB_PASSWORD_21=G0dz!ll42020
DB_CONNECTION_22=mysql
DB_HOST_22=192.168.0.102
DB_PORT_22=3306
DB_DATABASE_22=arrahn_rlaut
DB_USERNAME_22=spider
DB_PASSWORD_22=G0dz!ll42020
DB_CONNECTION_23=mysql
DB_HOST_23=192.168.0.102
DB_PORT_23=3306
DB_DATABASE_23=arrahn_skudai
DB_USERNAME_23=spider
DB_PASSWORD_23=G0dz!ll42020
DB_CONNECTION_24=mysql
DB_HOST_24=192.168.0.102
DB_PORT_24=3306
DB_DATABASE_24=arrahn_spetani
DB_USERNAME_24=spider
DB_PASSWORD_24=G0dz!ll42020
DB_CONNECTION_25=mysql
DB_HOST_25=192.168.0.102
DB_PORT_25=3306
DB_DATABASE_25=arrahn_temerloh
DB_USERNAME_25=spider
DB_PASSWORD_25=G0dz!ll42020
DB_CONNECTION_26=mysql
DB_HOST_26=192.168.0.103
DB_PORT_26=3306
DB_DATABASE_26=arrahn_api2
DB_USERNAME_26=spider
DB_PASSWORD_26=G0dz!ll42020
DB_CONNECTION_27=mysql
DB_HOST_27=192.168.0.103
DB_PORT_27=3306
DB_DATABASE_27=arrahn_beaufort
DB_USERNAME_27=spider
DB_PASSWORD_27=G0dz!ll42020
DB_CONNECTION_28=mysql
DB_HOST_28=192.168.0.103
DB_PORT_28=3306
DB_DATABASE_28=arrahn_kbelud
DB_USERNAME_28=spider
DB_PASSWORD_28=G0dz!ll42020
DB_CONNECTION_29=mysql
DB_HOST_29=192.168.0.103
DB_PORT_29=3306
DB_DATABASE_29=arrahn_keningau
DB_USERNAME_29=spider
DB_PASSWORD_29=G0dz!ll42020
DB_CONNECTION_30=mysql
DB_HOST_30=192.168.0.103
DB_PORT_30=3306
DB_DATABASE_30=arrahn_putatan
DB_USERNAME_30=spider
DB_PASSWORD_30=G0dz!ll42020
DB_CONNECTION_31=mysql
DB_HOST_31=192.168.0.103
DB_PORT_31=3306
DB_DATABASE_31=arrahn_sandakan
DB_USERNAME_31=spider
DB_PASSWORD_31=G0dz!ll42020
DB_CONNECTION_32=mysql
DB_HOST_32=192.168.0.103
DB_PORT_32=3306
DB_DATABASE_32=arrahn_tawau
DB_USERNAME_32=spider
DB_PASSWORD_32=G0dz!ll42020
DB_CONNECTION_33=mysql
DB_HOST_33=192.168.0.103
DB_PORT_33=3306
DB_DATABASE_33=arrahn_tuaran
DB_USERNAME_33=spider
DB_PASSWORD_33=G0dz!ll42020
CACHE_DRIVER=file CACHE_DRIVER=file
QUEUE_CONNECTION=sync QUEUE_CONNECTION=sync
WABOT_API=https://md.wabot.my/api/send.php MAX_MUTUEMAS=true
WABOT_INSTANCES_ID=63F40BD120DB4
# WABOT_INSTANCES_ID=64892FD7440A6
WABOT_TOKEN_KEYS=cbee7e95cf3ffbfa0531d5fddfd44f87
TENNANT_MASTER_DB=master_erahn
TENNANT_KAUNTER_DB=kaunter_erahn
ONEWAYSMS_API=http://gateway.onewaysms.com.my:10001/api.aspx KAUNTER_URL=http://host.docker.internal:8081
ONEWAYSMS_AUTH_USER=APIUK330HDAFO INTERNAL_NOTIFICATION_SECRET=
ONEWAYSMS_AUTH_PASS=APIUK330HDAFOUK330
ADMIN_PHONE_NUMBER=60178916936 LOGVIEWER_PASSWORD=
ACTIVE_JOB_SERVER=false
WHATSAPP_TOKEN= # Paste token from Graph API explorer.
WHATSAPP_PHONE_NUMBER_ID=1323449077519027
WHATSAPP_API_VERSION=v25.0
WHATSAPP_TEMPLATE=hello_world
WHATSAPP_TEMPLATE_LANG=en_US
WHATSAPP_VERIFY_TOKEN= # webhook verify token
WHATSAPP_APP_SECRET= # X-Hub-Signature-256 checks; need to be enable first
ADMIN_PHONE_NUMBER=60193450165
+89
View File
@@ -0,0 +1,89 @@
APP_NAME=Lumen
APP_ENV=production
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=false
APP_URL=https://apiujrah.erahn.com.my
APP_TIMEZONE=Asia/Kuala_Lumpur
API_ARRAHNBID=https://adminbid-kopkb.erahn.com.my/api/
API_ARRAHNBID_ANNUR=https://adminbid-annur.erahn.com.my/api/
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
LOGVIEWER_PASSWORD="$2y$10$k7eiUyZdm3BruwAaT1gbmelTm.PPgZptOEQT1nlBjyiR84a4KBMlm"
DB_CONNECTION_2=mysql
DB_HOST_2=192.168.1.216
DB_PORT_2=3306
DB_DATABASE_2=erahn_xtra
DB_USERNAME_2=spider
DB_PASSWORD_2=arrahn@123
DB_CONNECTION_3=mysql
DB_HOST_3=192.168.1.216
DB_PORT_3=3306
DB_DATABASE_3=erahn_baling
DB_USERNAME_3=spider
DB_PASSWORD_3=arrahn@123
DB_CONNECTION_4=mysql
DB_HOST_4=192.168.1.216
DB_PORT_4=3306
DB_DATABASE_4=arrahn_ld
DB_USERNAME_4=spider
DB_PASSWORD_4=arrahn@123
DB_CONNECTION_5=mysql
DB_HOST_5=192.168.1.216
DB_PORT_5=3306
DB_DATABASE_5=erahn_segamat
DB_USERNAME_5=spider
DB_PASSWORD_5=arrahn@123
DB_CONNECTION_6=mysql
DB_HOST_6=192.168.1.216
DB_PORT_6=3306
DB_DATABASE_6=erahn_terengganu
DB_USERNAME_6=spider
DB_PASSWORD_6=arrahn@123
DB_CONNECTION_8=mysql
DB_HOST_8=192.168.1.216
DB_PORT_8=3306
DB_DATABASE_8=erahn_benoni
DB_USERNAME_8=spider
DB_PASSWORD_8=arrahn@123
DB_CONNECTION_7=mysql
DB_HOST_7=192.168.1.216
DB_PORT_7=3306
DB_DATABASE_7=master_erahn
DB_USERNAME_7=spider
DB_PASSWORD_7=arrahn@123
DB_CONNECTION_9=mysql
DB_HOST_9=192.168.1.216
DB_PORT_9=3306
DB_DATABASE_9=annur_tampoi
DB_USERNAME_9=spider
DB_PASSWORD_9=arrahn@123
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
MAX_MUTUEMAS=true
TENNANT_MASTER_DB=master_erahn
TENNANT_KAUNTER_DB=kaunter_erahn
KAUNTER_URL=https://ujrah.erahn.com.my
INTERNAL_NOTIFICATION_SECRET="nIDTe(u@6[)VLJyd"
WHATSAPP_CLOUD_ENABLED=true
WHATSAPP_TOKEN=EAAZAonT56AO0BSnZAhWLi4UXDRT6iAldbiybxSQZAnGrzIke1QiNWvQkZAbQQTjWeVZC5bL1gEBhW6ff3Shf79nowXiTQEwyxqgZAZB6ZATwFCeTs8MZA5H6ZA76IJBuVqRiAZBvZCfkeSurbUNpEWyi9W7zsT2GWiZCLX8meX0KCfDHu7cp4aBgVu1Ps5ZCRO3bP4ZCQZDZD
WHATSAPP_PHONE_NUMBER_ID=1375899418935337
WHATSAPP_API_VERSION=v25.0
WHATSAPP_TEMPLATE=hebahan_pembiayaan_online
WHATSAPP_TEMPLATE_LANG=ms
WHATSAPP_VERIFY_TOKEN=418ed86d49422071681c76bed25ae1cb7e90456c794a92f1c9ea417945a35a0a
WHATSAPP_APP_SECRET=
ADMIN_PHONE_NUMBER=
+100
View File
@@ -0,0 +1,100 @@
APP_NAME=Lumen
APP_ENV=staging
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://127.0.0.1:8090
APP_TIMEZONE=Asia/Kuala_Lumpur
API_ARRAHNBID=http://lelong.test/api/
API_ARRAHNBID_ANNUR=http://lelong-annur.test/api/
HTTP_PORT=8090
MYSQL_PUBLISH_PORT=3308
LOG_CHANNEL=stack
LOG_LEVEL=debug
LOG_SLACK_WEBHOOK_URL=
LOGVIEWER_PASSWORD=test123
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=erahn_xtra
DB_USERNAME=root
DB_PASSWORD=root
DB_CONNECTION_7=mysql
DB_HOST_7=mysql
DB_PORT_7=3306
DB_DATABASE_7=master_erahn
DB_USERNAME_7=root
DB_PASSWORD_7=root
DB_CONNECTION_2=mysql
DB_HOST_2=mysql
DB_PORT_2=3306
DB_DATABASE_2=erahn_xtra
DB_USERNAME_2=root
DB_PASSWORD_2=root
DB_CONNECTION_3=mysql
DB_HOST_3=mysql
DB_PORT_3=3306
DB_DATABASE_3=erahn_baling
DB_USERNAME_3=root
DB_PASSWORD_3=root
DB_CONNECTION_4=mysql
DB_HOST_4=mysql
DB_PORT_4=3306
DB_DATABASE_4=arrahn_ld
DB_USERNAME_4=root
DB_PASSWORD_4=root
DB_CONNECTION_5=mysql
DB_HOST_5=mysql
DB_PORT_5=3306
DB_DATABASE_5=erahn_segamat
DB_USERNAME_5=root
DB_PASSWORD_5=root
DB_CONNECTION_7=mysql
DB_HOST_7=mysql
DB_PORT_7=3306
DB_DATABASE_7=master_erahn
DB_USERNAME_7=root
DB_PASSWORD_7=root
DB_CONNECTION_6=mysql
DB_HOST_6=mysql
DB_PORT_6=3306
DB_DATABASE_6=erahn_test
DB_USERNAME_6=root
DB_PASSWORD_6=root
DB_CONNECTION_9=mysql
DB_HOST_9=mysql
DB_PORT_9=3306
DB_DATABASE_9=annur_tampoi
DB_USERNAME_9=root
DB_PASSWORD_9=root
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
MAX_MUTUEMAS=true
TENNANT_MASTER_DB=master_erahn
TENNANT_KAUNTER_DB=kaunter_erahn
KAUNTER_URL=https://staging.erahn.com.my/login
INTERNAL_NOTIFICATION_SECRET=staging_noti_secret
WHATSAPP_CLOUD_ENABLED=false
WHATSAPP_TOKEN=EAAZAonT56AO0BSnZAhWLi4UXDRT6iAldbiybxSQZAnGrzIke1QiNWvQkZAbQQTjWeVZC5bL1gEBhW6ff3Shf79nowXiTQEwyxqgZAZB6ZATwFCeTs8MZA5H6ZA76IJBuVqRiAZBvZCfkeSurbUNpEWyi9W7zsT2GWiZCLX8meX0KCfDHu7cp4aBgVu1Ps5ZCRO3bP4ZCQZDZD
WHATSAPP_PHONE_NUMBER_ID=1375899418935337
WHATSAPP_API_VERSION=v25.0
WHATSAPP_TEMPLATE=hebahan_pembiayaan_online
WHATSAPP_TEMPLATE_LANG=ms
WHATSAPP_VERIFY_TOKEN=418ed86d49422071681c76bed25ae1cb7e90456c794a92f1c9ea417945a35a0a
WHATSAPP_APP_SECRET=
ADMIN_PHONE_NUMBER=
-244
View File
@@ -1,244 +0,0 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=false
APP_URL=https://api.arrahn.com.my
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION=mysql
DB_HOST=172.16.4.102
DB_PORT=3306
DB_DATABASE=arrahn_kb2
DB_USERNAME=zone2
DB_PASSWORD=G0dz!ll4@2020
DB_CONNECTION_2=mysql
DB_HOST_2=172.16.4.102
DB_PORT_2=3306
DB_DATABASE_2=arrahn_wcy
DB_USERNAME_2=zone2
DB_PASSWORD_2=G0dz!ll4@2020
DB_CONNECTION_3=mysql
DB_HOST_3=172.16.4.102
DB_PORT_3=3306
DB_DATABASE_3=arrahn_1stop
DB_USERNAME_3=zone2
DB_PASSWORD_3=G0dz!ll4@2020
DB_CONNECTION_4=mysql
DB_HOST_4=172.16.4.102
DB_PORT_4=3306
DB_DATABASE_4=arrahn_jlwt
DB_USERNAME_4=zone2
DB_PASSWORD_4=G0dz!ll4@2020
DB_CONNECTION_5=mysql
DB_HOST_5=172.16.4.102
DB_PORT_5=3306
DB_DATABASE_5=arrahn_wb
DB_USERNAME_5=zone2
DB_PASSWORD_5=G0dz!ll4@2020
DB_CONNECTION_6=mysql
DB_HOST_6=172.16.4.102
DB_PORT_6=3306
DB_DATABASE_6=arrahn_kls
DB_USERNAME_6=zone2
DB_PASSWORD_6=G0dz!ll4@2020
DB_CONNECTION_7=mysql
DB_HOST_7=172.16.4.102
DB_PORT_7=3306
DB_DATABASE_7=online_arrahn
DB_USERNAME_7=zone2
DB_PASSWORD_7=G0dz!ll4@2020
DB_CONNECTION_8=mysql
DB_HOST_8=172.16.4.102
DB_PORT_8=3306
DB_DATABASE_8=arrahn_pp
DB_USERNAME_8=zone2
DB_PASSWORD_8=G0dz!ll4@2020
DB_CONNECTION_9=mysql
DB_HOST_9=172.16.4.102
DB_PORT_9=3306
DB_DATABASE_9=arrahn_mace
DB_USERNAME_9=zone2
DB_PASSWORD_9=G0dz!ll4@2020
DB_CONNECTION_10=mysql
DB_HOST_10=172.16.4.102
DB_PORT_10=3306
DB_DATABASE_10=arrahn_pm
DB_USERNAME_10=zone2
DB_PASSWORD_10=G0dz!ll4@2020
DB_CONNECTION_11=mysql
DB_HOST_11=172.16.4.102
DB_PORT_11=3306
DB_DATABASE_11=arrahn_gm
DB_USERNAME_11=zone2
DB_PASSWORD_11=G0dz!ll4@2020
DB_CONNECTION_12=mysql
DB_HOST_12172.16.4.102
DB_PORT_12=3306
DB_DATABASE_12=arrahn_gcl
DB_USERNAME_12=zone2
DB_PASSWORD_12=G0dz!ll4@2020
DB_CONNECTION_13=mysql
DB_HOST_13=172.16.4.102
DB_PORT_13=3306
DB_DATABASE_13=arrahn_jli
DB_USERNAME_13=zone2
DB_PASSWORD_13=G0dz!ll4@2020
DB_CONNECTION_14=mysql
DB_HOST_14=172.16.4.102
DB_PORT_14=3306
DB_DATABASE_14=arrahn_rp
DB_USERNAME_14=zone2
DB_PASSWORD_14=G0dz!ll4@2020
DB_CONNECTION_15=mysql
DB_HOST_15=172.16.4.102
DB_PORT_15=3306
DB_DATABASE_15=arrahn_ktn
DB_USERNAME_15=zone2
DB_PASSWORD_15=G0dz!ll4@2020
DB_CONNECTION_16=mysql
DB_HOST_16=172.16.4.102
DB_PORT_16=3306
DB_DATABASE_16=arrahn_pg
DB_USERNAME_16=zone2
DB_PASSWORD_16=G0dz!ll4@2020
DB_CONNECTION_17=mysql
DB_HOST_17=172.16.4.102
DB_PORT_17=3306
DB_DATABASE_17=arrahn_skudai
DB_USERNAME_17=zone2
DB_PASSWORD_17=G0dz!ll4@2020
DB_CONNECTION_18=mysql
DB_HOST_18=172.16.4.102
DB_PORT_18=3306
DB_DATABASE_18=arrahn_pttn
DB_USERNAME_18=zone2
DB_PASSWORD_18=G0dz!ll4@2020
DB_CONNECTION_19=mysql
DB_HOST_19=172.16.4.102
DB_PORT_19=3306
DB_DATABASE_19=arrahn_btg
DB_USERNAME_19=zone2
DB_PASSWORD_19=G0dz!ll4@2020
DB_CONNECTION_20=mysql
DB_HOST_20=172.16.4.102
DB_PORT_20=3306
DB_DATABASE_20=arrahn_raub
DB_USERNAME_20=zone2
DB_PASSWORD_20=G0dz!ll4@2020
DB_CONNECTION_21=mysql
DB_HOST_21=172.16.4.102
DB_PORT_21=3306
DB_DATABASE_21=arrahn_kgr
DB_USERNAME_21=zone2
DB_PASSWORD_21=G0dz!ll4@2020
DB_CONNECTION_22=mysql
DB_HOST_22=172.16.4.102
DB_PORT_22=3306
DB_DATABASE_22=arrahn_sdk
DB_USERNAME_22=zone2
DB_PASSWORD_22=G0dz!ll4@2020
DB_CONNECTION_23=mysql
DB_HOST_23=172.16.4.102
DB_PORT_23=3306
DB_DATABASE_23=arrahn_sp
DB_USERNAME_23=zone2
DB_PASSWORD_23=G0dz!ll4@2020
DB_CONNECTION_24=mysql
DB_HOST_24=172.16.4.102
DB_PORT_24=3306
DB_DATABASE_24=arrahn_twu
DB_USERNAME_24=zone2
DB_PASSWORD_24=G0dz!ll4@2020
DB_CONNECTION_25=mysql
DB_HOST_25=172.16.4.102
DB_PORT_25=3306
DB_DATABASE_25=arrahn_api2
DB_USERNAME_25=zone2
DB_PASSWORD_25=G0dz!ll4@2020
DB_CONNECTION_26=mysql
DB_HOST_26=172.16.4.102
DB_PORT_26=3306
DB_DATABASE_26=arrahn_kjg
DB_USERNAME_26=zone2
DB_PASSWORD_26=G0dz!ll4@2020
DB_CONNECTION_27=mysql
DB_HOST_27=172.16.4.102
DB_PORT_27=3306
DB_DATABASE_27=arrahn_rl
DB_USERNAME_27=zone2
DB_PASSWORD_27=G0dz!ll4@2020
DB_CONNECTION_28=mysql
DB_HOST_28=172.16.4.102
DB_PORT_28=3306
DB_DATABASE_28=arrahn_bfort
DB_USERNAME_28=zone2
DB_PASSWORD_28=G0dz!ll4@2020
DB_CONNECTION_29=mysql
DB_HOST_29=172.16.4.102
DB_PORT_29=3306
DB_DATABASE_29=arrahn_kbelud
DB_USERNAME_29=zone2
DB_PASSWORD_29=G0dz!ll4@2020
DB_CONNECTION_30=mysql
DB_HOST_30=172.16.4.102
DB_PORT_30=3306
DB_DATABASE_30=arrahn_tuaran
DB_USERNAME_30=zone2
DB_PASSWORD_30=G0dz!ll4@2020
DB_CONNECTION_31=mysql
DB_HOST_31=172.16.4.102
DB_PORT_31=3306
DB_DATABASE_31=arrahn_kngau
DB_USERNAME_31=zone2
DB_PASSWORD_31=G0dz!ll4@2020
DB_CONNECTION_32=mysql
DB_HOST_32=172.16.4.102
DB_PORT_32=3306
DB_DATABASE_32=arrahn_tm
DB_USERNAME_32=zone2
DB_PASSWORD_32=G0dz!ll4@2020
DB_CONNECTION_33=mysql
DB_HOST_33=172.16.4.102
DB_PORT_33=3306
DB_DATABASE_33=arrahn_tmlh
DB_USERNAME_33=zone2
DB_PASSWORD_33=G0dz!ll4@2020
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
+49
View File
@@ -0,0 +1,49 @@
name: Build Docker Image
on:
push:
branches:
- staging
tags:
- "v*"
workflow_dispatch:
jobs:
build-backend:
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 backend image
run: |
set -e
IMAGE="git.koppkb.com/kopkb/api_arrahn"
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 backend: ${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 .
+141
View File
@@ -0,0 +1,141 @@
name: Deploy to Staging
on:
push:
tags:
- "v*"
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/api_arrahn
DEPLOY_DIR: /home/arrahn/Project/api_arrahn
jobs:
deploy:
runs-on: host
steps:
- name: Resolve image tag
id: resolve
run: |
set -e
if [ "${{ gitea.event_name }}" = "workflow_dispatch" ]; then
IMAGE_TAG="${{ inputs.image_tag }}"
else
IMAGE_TAG="${{ gitea.ref_name }}"
fi
echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_OUTPUT"
echo "Deploying image tag: ${IMAGE_TAG}"
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ steps.resolve.outputs.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="${{ steps.resolve.outputs.IMAGE_TAG }}"
IMAGE="${APP_IMAGE}:${IMAGE_TAG}"
echo "Checking if image exists: ${IMAGE}"
# Tag pushes build in parallel — wait for the image to appear
for i in $(seq 1 30); do
if docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then
echo "✓ ${IMAGE} found"
exit 0
fi
echo "Image not ready yet (attempt ${i}/30), waiting 20s..."
sleep 20
done
echo "ERROR: Image ${IMAGE} does not exist in registry after waiting!"
echo "Run the Build Docker Image workflow first."
exit 1
- name: Pull Docker image
run: |
set -e
IMAGE_TAG="${{ steps.resolve.outputs.IMAGE_TAG }}"
docker pull "${APP_IMAGE}:${IMAGE_TAG}"
echo "✓ Image pulled successfully"
- name: Sync compose file and deploy
run: |
set -e
IMAGE_TAG="${{ steps.resolve.outputs.IMAGE_TAG }}"
mkdir -p "${DEPLOY_DIR}/docker/nginx" "${DEPLOY_DIR}/docker/mysql"
cp docker-compose.staging.yml "${DEPLOY_DIR}/docker-compose.yml"
cp docker/nginx/default.conf "${DEPLOY_DIR}/docker/nginx/default.conf"
cp -R docker/mysql/init "${DEPLOY_DIR}/docker/mysql/"
if [ ! -f .env.staging ]; then
echo "ERROR: .env.staging is missing in the repository"
exit 1
fi
cp .env.staging "${DEPLOY_DIR}/.env.staging"
echo "✓ Synced .env.staging from repo"
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:-8090}
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 "api" "http://127.0.0.1:${HTTP_PORT}/health"
+2 -1
View File
@@ -5,6 +5,7 @@ Homestead.yaml
.env .env
.phpunit.result.cache .phpunit.result.cache
ray.php ray.php
docker-compose.yml
/docker_src /docker_src
/.vscode /.vscode
**/.DS_Store
db-dump
+29
View File
@@ -1 +1,30 @@
You know what is this when you in this project You know what is this when you in this project
If you does't know what is this you probably in wrong project
```sql
--- for all ip
CREATE USER 'master_erahn'@'%' IDENTIFIED BY 'password';
--- for specific ip
CREATE USER 'kaunter_erahn'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON kaunter_erahn.* TO 'kaunter_erahn'@'%';
FLUSH PRIVILEGES;
```
Gadai=Pembiayaan
Tebus=penyelesaian penuh/ansuran
## Load db dump without using docker
```bash
docker exec -i api_arrahn_staging_mysql mysql -uroot -proot erahn_test < erahn_test.sql
```
## Disable the cawangan in arrahn_master_db using feature flag
## Feature Flag
1. Register the flag in api_arrahn/app/Feature.php
2. Block the real work in the API
3. Hide the UI in kaunter from the
4. Enable a pilot from Admin → Ciri Cawangan (or SQL):
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BatchPembiayaanOnline extends Model
{
protected $table = 'batch_pembiayaan_online';
protected $fillable = [
'batch_no',
'kodcaw',
'tarikh',
'slot',
'bilangan',
'jumlah_keseluruhan',
'status',
'pc_submitted_by',
'submitted_at',
];
}
+11
View File
@@ -11,6 +11,17 @@ class Cawangan extends Model
public $timestamps = false; public $timestamps = false;
/**
* Display name from master table (`details_caw`).
* Keep this as the single mapping so callers do not guess nama/namacaw.
*/
public function getNamaCawanganAttribute()
{
$nama = trim((string) ($this->attributes['details_caw'] ?? ''));
return $nama !== '' ? $nama : (string) ($this->attributes['kodcaw'] ?? '');
}
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CawanganFeature extends Model
{
protected $table = 'cawangan_feature';
protected $connection = 'mysql7';
protected $fillable = [
'kodcaw',
'feature',
'enabled',
];
protected $casts = [
'enabled' => 'integer',
];
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use App\Services\Whatsapp\WhatsappCloudClient;
use Illuminate\Console\Command;
class WhatsappTestSend extends Command
{
protected $signature = 'whatsapp:test {--to= : Recipient MSISDN, default ADMIN_PHONE_NUMBER}';
protected $description = 'Send the configured WhatsApp Cloud template to a test number';
public function handle()
{
$client = new WhatsappCloudClient();
if (!$client->isConfigured()) {
$this->error('Set WHATSAPP_TOKEN and WHATSAPP_PHONE_NUMBER_ID in .env first.');
return 1;
}
$to = $client->normalizeMsisdn($this->option('to') ?: env('ADMIN_PHONE_NUMBER'));
if (!$to) {
$this->error('Pass --to=60193450165 or set ADMIN_PHONE_NUMBER.');
return 1;
}
$template = config('whatsapp.template', 'hello_world');
$lang = config('whatsapp.template_lang', 'en_US');
$bodyParams = [];
if (strtolower($template) !== 'hello_world') {
// pembiayaan_online_paid: SAG, amaun, bank, 4 digit akhir, tarikh, rujukan
$bodyParams = [
'TEST-SAG',
'100.00',
'Maybank',
'1234',
date('d/m/Y'),
'TEST-REF',
];
}
$this->info("Sending {$template} ({$lang}) to {$to} …");
$response = $client->sendTemplate($to, $template, $lang, $bodyParams);
if ($response->successful()) {
$this->info('OK ' . $response->status() . ' ' . $response->body());
return 0;
}
$this->error('FAIL ' . $response->status() . ' ' . $response->body());
return 1;
}
}
+1
View File
@@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel
Commands\ClearCachesAll::class, Commands\ClearCachesAll::class,
Commands\RestartSupervisor::class, Commands\RestartSupervisor::class,
Commands\InsertSempornaData::class, Commands\InsertSempornaData::class,
Commands\WhatsappTestSend::class,
//Commands\ImportCsv::class, //Commands\ImportCsv::class,
]; ];
+2 -1
View File
@@ -62,7 +62,8 @@ class Customer extends Model
'telefon2', 'telefon2',
'nota_pekerjaan', 'nota_pekerjaan',
'tagpelanggan', 'tagpelanggan',
'tag_anggota' 'tag_anggota',
'gambar_mykad'
]; ];
/** /**
+172
View File
@@ -0,0 +1,172 @@
<?php
namespace App;
class Feature
{
const PEMBIAYAAN_ONLINE = 'pembiayaan_online';
const REBET_ANNUR = 'rebet_annur';
/**
* Known flag keys. New features: add a constant here, then INSERT rows for pilot kodcaw.
*
* @return string[]
*/
public static function keys()
{
return [
self::PEMBIAYAAN_ONLINE,
self::REBET_ANNUR,
];
}
/**
* @return array<string,int>
*/
public static function defaults()
{
$map = [];
foreach (self::keys() as $key) {
$map[$key] = 0;
}
return $map;
}
/**
* Display labels for admin UI. New features: add a label here.
*
* @return array<string,string>
*/
public static function labels()
{
return [
self::PEMBIAYAAN_ONLINE => 'Pembiayaan Online',
self::REBET_ANNUR => 'Rebet / diskaun Annur',
];
}
/**
* Lelong portal base URL for this branch. rebet_annur branches (Annur/Tampoi)
* use API_ARRAHNBID_ANNUR; everyone else uses API_ARRAHNBID.
*
* Always returned with a trailing slash so callers can append 'portalApi'.
*
* @param string $kodcaw
* @return string
*/
public static function arrahnbidUrl($kodcaw)
{
$url = config('app_parameter.api_arrahnbid');
if ($kodcaw && self::enabled($kodcaw, self::REBET_ANNUR)) {
$annur = config('app_parameter.api_arrahnbid_annur');
if ($annur) {
$url = $annur;
}
}
return rtrim((string) $url, '/') . '/';
}
/**
* Missing row is off (fail closed).
*
* @param string $kodcaw
* @param string $feature
* @return bool
*/
public static function enabled($kodcaw, $feature)
{
return CawanganFeature::on('mysql7')
->where('kodcaw', $kodcaw)
->where('feature', $feature)
->where('enabled', 1)
->exists();
}
/**
* @param string $kodcaw
* @return array<string,int>
*/
public static function mapFor($kodcaw)
{
$map = self::defaults();
$rows = CawanganFeature::on('mysql7')
->where('kodcaw', $kodcaw)
->get(['feature', 'enabled']);
foreach ($rows as $row) {
$map[$row->feature] = ((int) $row->enabled) === 1 ? 1 : 0;
}
return $map;
}
/**
* @param string[] $kodcaws
* @return array<string,array<string,int>>
*/
public static function mapForMany(array $kodcaws)
{
$defaults = self::defaults();
$result = [];
foreach ($kodcaws as $kodcaw) {
$result[$kodcaw] = $defaults;
}
if (count($kodcaws) === 0) {
return $result;
}
$rows = CawanganFeature::on('mysql7')
->whereIn('kodcaw', $kodcaws)
->get(['kodcaw', 'feature', 'enabled']);
foreach ($rows as $row) {
$result[$row->kodcaw][$row->feature] = ((int) $row->enabled) === 1 ? 1 : 0;
}
return $result;
}
/**
* @param string $kodcaw
* @param string $feature
* @param bool|int $enabled
* @return bool
*/
public static function setEnabled($kodcaw, $feature, $enabled)
{
if (!in_array($feature, self::keys(), true)) {
return false;
}
CawanganFeature::on('mysql7')->updateOrCreate(
[
'kodcaw' => $kodcaw,
'feature' => $feature,
],
[
'enabled' => $enabled ? 1 : 0,
]
);
return true;
}
/**
* @param string|null $message
* @return array{ok:bool,status:int,payload:array}
*/
public static function denied($message = null)
{
return [
'ok' => false,
'status' => 403,
'payload' => [
'message' => $message ?: 'Ciri ini tidak diaktifkan untuk cawangan ini',
],
];
}
}
+8 -1
View File
@@ -9,6 +9,10 @@ class Gadai extends Model
protected $table = 'gadai'; protected $table = 'gadai';
public $timestamps = false; public $timestamps = false;
protected $attributes = [
'cara_terima' => 'TUNAI',
];
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
@@ -25,6 +29,7 @@ class Gadai extends Model
'nilaimarhun', 'nilaimarhun',
'berat', 'berat',
'pinjaman', 'pinjaman',
'cara_terima',
'telahbayar', 'telahbayar',
'upahtelahbyr', 'upahtelahbyr',
'cajtelahbyr', 'cajtelahbyr',
@@ -73,7 +78,9 @@ class Gadai extends Model
'tagkomuditi', 'tagkomuditi',
'tempohbayar', 'tempohbayar',
'lelong_tawarruq', 'lelong_tawarruq',
'tag_tebus' 'tag_tebus',
'kadarrebet',
'diskaun'
]; ];
/** /**
+254 -264
View File
@@ -30,6 +30,7 @@ use App\Status;
use App\LookupStatus; use App\LookupStatus;
use App\ARFiles; use App\ARFiles;
use App\WTDDraft; use App\WTDDraft;
use App\WTDSimulasi;
use App\SerahBaki; use App\SerahBaki;
use App\WTD; use App\WTD;
use App\StokAudit1; use App\StokAudit1;
@@ -62,7 +63,6 @@ use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Jobs\AccountCheckJob; use App\Jobs\AccountCheckJob;
use App\Penyelarasan;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
@@ -2365,7 +2365,6 @@ class AdminPageController extends Controller
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first();
//$latestbatch = SerahBaki::on($cawangan['mysql'])->where('norujukan','baru')->get()->toArray(); //$latestbatch = SerahBaki::on($cawangan['mysql'])->where('norujukan','baru')->get()->toArray();
//new added //new added
$serahBaki = SerahBaki::on($cawangan['mysql']) $serahBaki = SerahBaki::on($cawangan['mysql'])
->where('norujukan', 'baru') ->where('norujukan', 'baru')
@@ -2374,28 +2373,28 @@ class AdminPageController extends Controller
$allBatchNo = $serahBaki->pluck('batchno')->unique()->toArray(); $allBatchNo = $serahBaki->pluck('batchno')->unique()->toArray();
$wtddraftData = WTDDraft::on('mysql7') $wtddraftData = WTDDraft::on('mysql7')
->selectRaw(" ->selectRaw("
batchno_serah, batchno_serah,
YEAR(t_jual) as tahun, YEAR(t_jual) as tahun,
GROUP_CONCAT( GROUP_CONCAT(
DISTINCT MONTH(t_jual) DISTINCT MONTH(t_jual)
ORDER BY MONTH(t_jual) ORDER BY MONTH(t_jual)
SEPARATOR ',' SEPARATOR ','
) as bulan ) as bulan
") ")
->where('kodcaw', $request->kodcaw) ->where('kodcaw', $request->kodcaw)
->whereIn('batchno_serah', $allBatchNo) ->whereIn('batchno_serah', $allBatchNo)
->groupBy('batchno_serah', DB::raw('YEAR(t_jual)')) ->groupBy('batchno_serah', DB::raw('YEAR(t_jual)'))
->get() ->get()
->groupBy('batchno_serah') ->groupBy('batchno_serah')
->map(function ($rows) { ->map(function ($rows) {
return $rows->map(function ($row) { return $rows->map(function ($row) {
return [ return [
'tahun' => $row->tahun, 'tahun' => $row->tahun,
'bulan' => $row->bulan 'bulan' => $row->bulan
]; ];
})->values(); })->values();
}); });
$finalData = $serahBaki->map(function ($item) use ($wtddraftData) { $finalData = $serahBaki->map(function ($item) use ($wtddraftData) {
$batchno = $item->batchno; $batchno = $item->batchno;
@@ -2427,29 +2426,27 @@ class AdminPageController extends Controller
$finalDataArray = $finalData->toArray(); $finalDataArray = $finalData->toArray();
//end new added //end new added
$batch_old = WTDDraft::on('mysql7')->select('kodcaw')->where('batchno_serah',0)->where('kodcaw',$request->kodcaw) $batch_old = WTDDraft::on('mysql7')->select('kodcaw')->where('batchno_serah',0)->where('kodcaw',$request->kodcaw)
->groupBy('kodcaw')->get(); ->groupBy('kodcaw')->get();
$batch_old_noruj = WTDDraft::on('mysql7')->where('batchno_serah',0)->where('kodcaw',$request->kodcaw)->pluck('norujukan'); $batch_old_noruj = WTDDraft::on('mysql7')->where('batchno_serah',0)->where('kodcaw',$request->kodcaw)->pluck('norujukan');
$lelong_data = Lelong::on($cawangan['mysql'])->whereIn('norujukan',$batch_old_noruj)->get(); $lelong_data = Lelong::on($cawangan['mysql'])->whereIn('norujukan',$batch_old_noruj)->get();
foreach($batch_old as $index=>$bo) foreach($batch_old as $index=>$bo)
{ {
//array_push($latestbatch,['recno' => 0,'norujukan' => 'lama','batchno' => 0,'baki' => $lelong_data->sum('baki'),'aktif' => 1]); //array_push($latestbatch,['recno' => 0,'norujukan' => 'lama','batchno' => 0,'baki' => $lelong_data->sum('baki'),'aktif' => 1]);
array_push($finalDataArray,['recno' => 0,'norujukan' => 'lama','batchno' => 0,'baki' => $lelong_data->sum('baki'),'aktif' => 1]); array_push($finalDataArray,['recno' => 0,'norujukan' => 'lama','batchno' => 0,'baki' => $lelong_data->sum('baki'),'aktif' => 1]);
} }
//$data = $this->paginate($latestbatch,8); //$data = $this->paginate($latestbatch,8);
$data = $this->paginate($finalDataArray,8); $data = $this->paginate($finalDataArray,8);
//dd($data);
return response()->json($data,200); return response()->json($data,200);
} }
public function rekodWTDByBatchNo($batchno,Request $request){ public function rekodWTDByBatchNo($batchno,Request $request){
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first();
@@ -2727,7 +2724,7 @@ class AdminPageController extends Controller
'prkod' => "01", 'prkod' => "01",
'teller' => "DM", 'teller' => "DM",
'bakipjm' => ($lel['bakipjm']) ? $lel['bakipjm'] : 0, 'bakipjm' => ($lel['bakipjm']) ? $lel['bakipjm'] : 0,
'bakiupah' => ($lel['bakiupah']) ? $lel['bakiupah'] : 0, 'bakiupah' => $lel['bakiupah'],
'cajlain' => '0.00', 'cajlain' => '0.00',
'cajlelong' => $lel['cajlelong'], 'cajlelong' => $lel['cajlelong'],
'kodgl' => '', 'kodgl' => '',
@@ -2749,7 +2746,7 @@ class AdminPageController extends Controller
't_jual' => $lel['t_jual'], 't_jual' => $lel['t_jual'],
'hargajual' => $lel['hargajual'], 'hargajual' => $lel['hargajual'],
'pinjaman' => $lel['pinjaman'], 'pinjaman' => $lel['pinjaman'],
'bakiupah' => ($lel['bakiupah']) ? $lel['bakiupah'] : 0, 'bakiupah' => $lel['bakiupah'],
'cajlelong' => $lel['cajlelong'], 'cajlelong' => $lel['cajlelong'],
'gst_cl' => $lel['gst_cl'], 'gst_cl' => $lel['gst_cl'],
'baki' => $lel['baki'], 'baki' => $lel['baki'],
@@ -2779,222 +2776,6 @@ class AdminPageController extends Controller
} }
public function janaSimulasiWTD(Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first();
//listing sag belum tuntut cwgn
$sag_cwgn = collect(
Lelong::on($cawangan['mysql'])
->selectRaw('norujukan')
->whereYear('t_jual', $request->tahun)
->whereNull('trkhtuntut')
->where('baki', '>', 0)
->pluck('norujukan') // lebih bersih
);
//listing sag blocked
$sag_online = collect(
WTDDraft::on('mysql7')
->selectRaw('norujukan')
->where('kodcaw', '=', $request->kodcaw)
->pluck('norujukan')
);
//dd($sag_online);
//filter sag yg tk wujud dlm wtd_draft
$filtersag = $sag_cwgn->diff($sag_online);
//query baru
$tarikh_belum_tuntut = Lelong::on($cawangan['mysql'])
->selectRaw('
MONTH(t_jual) as bulan,
SUM(pinjaman) as pembiayaan,
ABS(SUM(baki)) as baki,
COUNT(DISTINCT norujukan) as jumlah_norujukan,
kodcaw
')
->whereIn('norujukan', $filtersag)
->groupBy('bulan', 'kodcaw')
->orderBy('bulan')
->get();
$tarikh_belum_tuntut = $tarikh_belum_tuntut->map(function ($item) {
$item->bulan = \Carbon\Carbon::create()->month($item->bulan)->format('F');
return $item;
});
return response()->json($tarikh_belum_tuntut);
}
public function RekodSimulasiWTD(Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first();
$rekod_simulasi = Lelong::on($cawangan['mysql'])
->join('gadai','lelong.norujukan','=','gadai.norujukan')
->selectRaw('ABS(lelong.baki) as baki, lelong.t_lelong, lelong.pinjaman, lelong.norujukan, gadai.nokp')
->whereMonth('lelong.t_jual', $request->month)
->whereYear('lelong.t_jual', $request->year)
->whereNull('lelong.trkhtuntut')
->where('lelong.baki', '>', 0)
->get();
return response()->json($rekod_simulasi);
}
public function prosesSimulasiWTD(Request $request)
{
$proses = $request->input;
// dd($proses, $cawangan);
$baki = 0;
$jumlahPembiayaan = 0;
$simulasi = WTDSimulasi::max('simulasi')+1;
foreach ($proses as $item) {
WTDSimulasi::create([
'bulan' => $item['bulan'],
'tahun' => $item['tahun'],
'kodcaw' => $item['kodcaw'],
'jumlah_sag' => $item['jumlah_sag'],
'pembiayaan'=> $item['pembiayaan'],
'baki'=> $item['baki'],
'simulasi' => $simulasi,
]);
$baki += $item['baki'];
$jumlahPembiayaan = $item['pembiayaan'];
}
return response()->json(['status' => 'success','simulasi' => $simulasi,'baki' => $baki,'pinjaman' => $jumlahPembiayaan]);
}
public function pengesahanSimulasiWTD(Request $request)
{
// Decode the JSON records from the request
$records = json_decode($request->records, true);
// Retrieve and clean 'kodcaw' from the request
$kodcaw = $request->kodcaw;
$kodcaw_clean = str_replace('"', '', $kodcaw);
// Get the database connection for the branch (cawangan)
$cawangan = Cawangan::where('kodcaw', $kodcaw_clean)->first();
// Check if 'Cawangan' exists
if (!$cawangan) {
return response()->json(['status' => 'error', 'message' => 'Invalid branch code (kodcaw).'], 404);
}
$cawangan_connection = $cawangan->mysql;
// Get the latest batch number and increment it
$latestBatch = SerahBaki::on($cawangan_connection)->max('batchno') + 1;
// Initialize total variables
$totalPembiayaan = 0;
$totalBaki = 0;
try {
// Iterate through the records and save them to the WTDDraft table
foreach ($records as $record) {
// Check if 'bulan' and 'tahun' exist in the record
if (!isset($record['bulan']) || !isset($record['tahun'])) {
return response()->json(['status' => 'error', 'message' => 'Missing bulan or tahun in records.'], 400);
}
$month = DateTime::createFromFormat('F', $record["bulan"])->format('n');
// Query norujukan records from the database
$query_norujukan = Lelong::on($cawangan_connection)
->join('gadai', 'lelong.norujukan', '=', 'gadai.norujukan')
->selectRaw('ABS(lelong.baki) as baki, lelong.t_lelong, lelong.pinjaman, lelong.norujukan, gadai.nokp, lelong.t_jual')
->whereMonth('lelong.t_jual', $month)
->whereYear('lelong.t_jual', $record["tahun"])
->whereNull('lelong.trkhtuntut')
->where('lelong.baki', '>', 0)
->get();
// Save each record to the WTDDraft table
foreach ($query_norujukan as $query) {
$wtddraft = new WTDDraft();
$wtddraft->nokp = $query['nokp'];
$wtddraft->kodcaw = $kodcaw_clean;
$wtddraft->norujukan = $query['norujukan'];
$wtddraft->t_jual = $query['t_jual'];
$wtddraft->t_gadai = $query['t_gadai'] ?? null; // Optional: ensure 't_gadai' exists
$wtddraft->t_lelong = $query['t_lelong'];
$wtddraft->batchno_serah = $latestBatch;
$wtddraft->save();
}
// Accumulate totals
$totalPembiayaan += $record['pembiayaan'] ?? 0;
$totalBaki += $record['baki'] ?? 0;
}
// Create a new record in the SerahBaki table
SerahBaki::on($cawangan_connection)->create([
'norujukan' => 'baru',
'batchno' => $latestBatch,
'hargajual' => 0.00,
'pinjaman' => $totalPembiayaan,
'upah' => 0.00,
'cajlel' => 0.00,
'gst_cl' => 0.00,
'gst_hj' => 0.00,
'baki' => $totalBaki,
'tarikh' => Carbon::now()->toDateString(),
't_jual' => Carbon::now()->toDateString(),
'nopelanggan' => '',
'aktif' => 0,
]);
// Return a success response with the new batch number
return response()->json(['status' => 'success', 'batchno' => $latestBatch], 200);
} catch (\Throwable $e) {
// Log the error and return a failure response
Log::error($e->getMessage());
Log::error($e->getLine());
return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500);
}
}
public function simulasiToDraft(Request $request)
{
$bulan = $request->bulan;
if (!$bulan || count($bulan) === 0) {
return response()->json(['error' => 'Sila pilih sekurang-kurangnya satu bulan.'], 400);
}
$simulasi = WTDSimulasi::whereIn('bulan', $bulan)->get();
if ($simulasi->isEmpty()) {
return response()->json(['error' => 'Tiada data untuk bulan yang dipilih.'], 404);
}
// foreach ($simulasi as $data) {
// WTDDraft::create([
// 'nokp' => $data->nokp ?? null,
// 'kodcaw' => $data->kodcaw ?? null,
// 'norujukan' => $data->norujukan ?? null,
// 't_jual' => $data->t_jual ?? null,
// 't_gadai' => $data->t_gadai ?? null,
// 't_lelong' => $data->t_lelong ?? null,
// 'batchno_serah' => $data->simulasi, // Simpan sebagai rujukan
// ]);
// }
dd($bulan, $simulasi);
return response()->json(['message' => 'Data berjaya dipindahkan ke WTD Draft!']);
}
public function PenjanaanPC(Request $request){ public function PenjanaanPC(Request $request){
$timeloni = Carbon::now(); $timeloni = Carbon::now();
@@ -3445,8 +3226,6 @@ class AdminPageController extends Controller
return response()->json($customers); return response()->json($customers);
} }
public function AkruKeuntunganSebenar(Request $request){ public function AkruKeuntunganSebenar(Request $request){
@@ -4045,13 +3824,13 @@ class AdminPageController extends Controller
$get_imbangduga = imbangdugabackup::on($cawangan['mysql'])->where('tarikh', $lastDayOfYear)->first(); $get_imbangduga = imbangdugabackup::on($cawangan['mysql'])->where('tarikh', $lastDayOfYear)->first();
$get_imbangduga = optional($get_imbangduga)->toArray() ?? []; $get_imbangduga = optional($get_imbangduga)->toArray() ?? [];
if(empty($get_imbangduga)){ // if(empty($get_imbangduga)){
$requestData = new Request([ // $requestData = new Request([
'tarikh' => $request->tarikh, // 'tarikh' => $request->tarikh,
'kodcaw' => $kodcaw, // 'kodcaw' => $kodcaw,
]); // ]);
$exception_case = $this->AkruKeuntunganSebenar($requestData)->getData(); // $exception_case = $this->AkruKeuntunganSebenar($requestData);
} // }
$backdate_keun_sebenar += isset($get_imbangduga['keun_sebenar']) ? $get_imbangduga['keun_sebenar'] : 0; $backdate_keun_sebenar += isset($get_imbangduga['keun_sebenar']) ? $get_imbangduga['keun_sebenar'] : 0;
$backdate_keun_sebenar += (float) ($exception_case ?? 0); $backdate_keun_sebenar += (float) ($exception_case ?? 0);
@@ -4070,14 +3849,7 @@ class AdminPageController extends Controller
$total = ($utgrugilama + $lelong + $upahsimpantebus + $upahsimpanlelong + $upahsimpanansur + $gstcajlelong + $keuntungansebenar + $upahsimpankeuntungansebelumtawarruq + $available_akru + $total_keuntungan_sebenar + $akru_ujrah + $akru_1percent) + $parameter; $total = ($utgrugilama + $lelong + $upahsimpantebus + $upahsimpanlelong + $upahsimpanansur + $gstcajlelong + $keuntungansebenar + $upahsimpankeuntungansebelumtawarruq + $available_akru + $total_keuntungan_sebenar + $akru_ujrah + $akru_1percent) + $parameter;
if($kodcaw == '011229') if($kodcaw == '011229')
$total -= 45390.37; $total -= 45390.37;
if($kodcaw == 'KB2')
$total -= 64.8;
$total += Penyelarasan::on('mysql7')
->where('kodcaw', $kodcaw)
->sum(DB::raw('rugilelong + bakilelong + `lain-lain`'));
return response()->json($total,200); return response()->json($total,200);
} }
@@ -4250,4 +4022,222 @@ class AdminPageController extends Controller
'result' => $status === 'completed' ? $result : null, 'result' => $status === 'completed' ? $result : null,
]); ]);
} }
//function wtd baru
public function janaSimulasiWTD(Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first();
//listing sag belum tuntut cwgn
$sag_cwgn = collect(
Lelong::on($cawangan['mysql'])
->selectRaw('norujukan')
->whereYear('t_jual', $request->tahun)
->whereNull('trkhtuntut')
->where('baki', '>', 0)
->pluck('norujukan') // lebih bersih
);
//listing sag blocked
$sag_online = collect(
WTDDraft::on('mysql7')
->selectRaw('norujukan')
->where('kodcaw', '=', $request->kodcaw)
->pluck('norujukan')
);
//dd($sag_online);
//filter sag yg tk wujud dlm wtd_draft
$filtersag = $sag_cwgn->diff($sag_online);
//query baru
$tarikh_belum_tuntut = Lelong::on($cawangan['mysql'])
->selectRaw('
MONTH(t_jual) as bulan,
SUM(pinjaman) as pembiayaan,
ABS(SUM(baki)) as baki,
COUNT(DISTINCT norujukan) as jumlah_norujukan,
kodcaw
')
->whereIn('norujukan', $filtersag)
->groupBy('bulan', 'kodcaw')
->orderBy('bulan')
->get();
$tarikh_belum_tuntut = $tarikh_belum_tuntut->map(function ($item) {
$item->bulan = \Carbon\Carbon::create()->month($item->bulan)->format('F');
return $item;
});
return response()->json($tarikh_belum_tuntut);
}
public function RekodSimulasiWTD(Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first();
$rekod_simulasi = Lelong::on($cawangan['mysql'])
->join('gadai','lelong.norujukan','=','gadai.norujukan')
->selectRaw('ABS(lelong.baki) as baki, lelong.t_lelong, lelong.pinjaman, lelong.norujukan, gadai.nokp')
->whereMonth('lelong.t_jual', $request->month)
->whereYear('lelong.t_jual', $request->year)
->whereNull('lelong.trkhtuntut')
->where('lelong.baki', '>', 0)
->get();
return response()->json($rekod_simulasi);
}
public function prosesSimulasiWTD(Request $request)
{
$proses = $request->input;
// dd($proses, $cawangan);
$baki = 0;
$jumlahPembiayaan = 0;
$simulasi = WTDSimulasi::max('simulasi')+1;
foreach ($proses as $item) {
WTDSimulasi::create([
'bulan' => $item['bulan'],
'tahun' => $item['tahun'],
'kodcaw' => $item['kodcaw'],
'jumlah_sag' => $item['jumlah_sag'],
'pembiayaan'=> $item['pembiayaan'],
'baki'=> $item['baki'],
'simulasi' => $simulasi,
]);
$baki += $item['baki'];
$jumlahPembiayaan = $item['pembiayaan'];
}
return response()->json(['status' => 'success','simulasi' => $simulasi,'baki' => $baki,'pinjaman' => $jumlahPembiayaan]);
}
public function pengesahanSimulasiWTD(Request $request)
{
// Decode the JSON records from the request
$records = json_decode($request->records, true);
// Retrieve and clean 'kodcaw' from the request
$kodcaw = $request->kodcaw;
$kodcaw_clean = str_replace('"', '', $kodcaw);
// Get the database connection for the branch (cawangan)
$cawangan = Cawangan::where('kodcaw', $kodcaw_clean)->first();
// Check if 'Cawangan' exists
if (!$cawangan) {
return response()->json(['status' => 'error', 'message' => 'Invalid branch code (kodcaw).'], 404);
}
$cawangan_connection = $cawangan->mysql;
// Get the latest batch number and increment it
$latestBatch = SerahBaki::on($cawangan_connection)->max('batchno') + 1;
// Initialize total variables
$totalPembiayaan = 0;
$totalBaki = 0;
try {
// Iterate through the records and save them to the WTDDraft table
foreach ($records as $record) {
// Check if 'bulan' and 'tahun' exist in the record
if (!isset($record['bulan']) || !isset($record['tahun'])) {
return response()->json(['status' => 'error', 'message' => 'Missing bulan or tahun in records.'], 400);
}
$month = DateTime::createFromFormat('F', $record["bulan"])->format('n');
// Query norujukan records from the database
$query_norujukan = Lelong::on($cawangan_connection)
->join('gadai', 'lelong.norujukan', '=', 'gadai.norujukan')
->selectRaw('ABS(lelong.baki) as baki, lelong.t_lelong, lelong.pinjaman, lelong.norujukan, gadai.nokp, lelong.t_jual')
->whereMonth('lelong.t_jual', $month)
->whereYear('lelong.t_jual', $record["tahun"])
->whereNull('lelong.trkhtuntut')
->where('lelong.baki', '>', 0)
->get();
// Save each record to the WTDDraft table
foreach ($query_norujukan as $query) {
$wtddraft = new WTDDraft();
$wtddraft->nokp = $query['nokp'];
$wtddraft->kodcaw = $kodcaw_clean;
$wtddraft->norujukan = $query['norujukan'];
$wtddraft->t_jual = $query['t_jual'];
$wtddraft->t_gadai = $query['t_gadai'] ?? null; // Optional: ensure 't_gadai' exists
$wtddraft->t_lelong = $query['t_lelong'];
$wtddraft->batchno_serah = $latestBatch;
$wtddraft->save();
}
// Accumulate totals
$totalPembiayaan += $record['pembiayaan'] ?? 0;
$totalBaki += $record['baki'] ?? 0;
}
// Create a new record in the SerahBaki table
SerahBaki::on($cawangan_connection)->create([
'norujukan' => 'baru',
'batchno' => $latestBatch,
'hargajual' => 0.00,
'pinjaman' => $totalPembiayaan,
'upah' => 0.00,
'cajlel' => 0.00,
'gst_cl' => 0.00,
'gst_hj' => 0.00,
'baki' => $totalBaki,
'tarikh' => Carbon::now()->toDateString(),
't_jual' => Carbon::now()->toDateString(),
'nopelanggan' => '',
'aktif' => 0,
]);
// Return a success response with the new batch number
return response()->json(['status' => 'success', 'batchno' => $latestBatch], 200);
} catch (\Throwable $e) {
// Log the error and return a failure response
Log::error($e->getMessage());
Log::error($e->getLine());
return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500);
}
}
public function simulasiToDraft(Request $request)
{
$bulan = $request->bulan;
if (!$bulan || count($bulan) === 0) {
return response()->json(['error' => 'Sila pilih sekurang-kurangnya satu bulan.'], 400);
}
$simulasi = WTDSimulasi::whereIn('bulan', $bulan)->get();
if ($simulasi->isEmpty()) {
return response()->json(['error' => 'Tiada data untuk bulan yang dipilih.'], 404);
}
// foreach ($simulasi as $data) {
// WTDDraft::create([
// 'nokp' => $data->nokp ?? null,
// 'kodcaw' => $data->kodcaw ?? null,
// 'norujukan' => $data->norujukan ?? null,
// 't_jual' => $data->t_jual ?? null,
// 't_gadai' => $data->t_gadai ?? null,
// 't_lelong' => $data->t_lelong ?? null,
// 'batchno_serah' => $data->simulasi, // Simpan sebagai rujukan
// ]);
// }
//dd($bulan, $simulasi);
return response()->json(['message' => 'Data berjaya dipindahkan ke WTD Draft!']);
}
} }
+1 -1
View File
@@ -59,7 +59,7 @@ class BarangkesController extends Controller
$itemArray['gadaian_sudah_tebus'] = !is_null($item['tebus']); $itemArray['gadaian_sudah_tebus'] = !is_null($item['tebus']);
// Add nokp customer from gadai relationship // Add nokp customer from gadai relationship
$itemArray['nokp'] = $item['gadai']['nokp']; $itemArray['nokp'] = $item['gadai']['nokp'] ?? null;
// Remove the tebus relationship data to keep response clean // Remove the tebus relationship data to keep response clean
unset($itemArray['tebus']); unset($itemArray['tebus']);
+11 -1
View File
@@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Cawangan; use App\Cawangan;
use App\Feature;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class CawanganController extends Controller class CawanganController extends Controller
@@ -18,7 +19,16 @@ class CawanganController extends Controller
$cawangan = Cawangan::on('mysql7') $cawangan = Cawangan::on('mysql7')
->where('kodcaw','=',$code_cawangan) ->where('kodcaw','=',$code_cawangan)
->first(); ->first();
return response()->json($cawangan);
if (!$cawangan) {
return response()->json($cawangan);
}
$data = $cawangan->toArray();
$data['features'] = Feature::mapFor($code_cawangan);
$data['api_arrahnbid'] = Feature::arrahnbidUrl($code_cawangan);
return response()->json($data);
} }
public function create(Request $request) public function create(Request $request)
+206 -24
View File
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Audit; use App\Audit;
use App\Marhun; use App\Marhun;
use App\BankMaster; use App\BankMaster;
use App\ARFiles;
use App\Customer; use App\Customer;
use App\Gadai; use App\Gadai;
use App\Lelong; use App\Lelong;
@@ -64,7 +65,7 @@ class CustomerController extends Controller
->orWhere('kpbaru','=',$ic) ->orWhere('kpbaru','=',$ic)
->first(); ->first();
return response()->json($customer); return response()->json($this->withMykadPhoto($customer));
} }
public function showOneCustomerByICAdmin(Request $request){ public function showOneCustomerByICAdmin(Request $request){
@@ -74,7 +75,7 @@ class CustomerController extends Controller
->orWhere('kpbaru','=',$request->ic) ->orWhere('kpbaru','=',$request->ic)
->first(); ->first();
return response()->json($customer); return response()->json($this->withMykadPhoto($customer));
} }
public function showOneCustomerByICMobile($ic){ public function showOneCustomerByICMobile($ic){
@@ -368,6 +369,8 @@ class CustomerController extends Controller
'tujuangadai'=>$request->tujuangadai 'tujuangadai'=>$request->tujuangadai
]); ]);
$this->attachMykadPhoto($customer, $request);
$audit = new Audit(); $audit = new Audit();
$audit->users = $request->teller; $audit->users = $request->teller;
$audit->actions = "Daftar Pelanggan Baru"; $audit->actions = "Daftar Pelanggan Baru";
@@ -377,7 +380,7 @@ class CustomerController extends Controller
$audit->updated_at = $current; $audit->updated_at = $current;
$audit->save(); $audit->save();
return response()->json($customer, 201); return response()->json($this->withMykadPhoto($customer), 201);
} }
public function customerUpdate($nokp,Request $request){ public function customerUpdate($nokp,Request $request){
@@ -396,37 +399,38 @@ class CustomerController extends Controller
// 'nota_arcc'=>$request->nota_arcc, // 'nota_arcc'=>$request->nota_arcc,
'telefon' => $request->telefon1, 'telefon' => $request->telefon1,
'telefon2' => $request->telefon2, 'telefon2' => $request->telefon2,
'telefon2' => $request->telefon2,
)); ));
return response()->json($customer_update, 200); return response()->json($customer_update, 200);
} }
public function assignAnggota($noic, Request $request) public function assignAnggota($noic, Request $request)
{ {
// Semak sama ada pelanggan wujud // Semak sama ada pelanggan wujud
$customer = Customer::on('mysql7') $customer = Customer::on('mysql7')
->where('kpbaru', $noic) ->where('kpbaru', $noic)
->orWhere('kplama', $noic) ->orWhere('kplama', $noic)
->first(); ->first();
if (!$customer) {
return response()->json([
'success' => false,
'message' => 'Pelanggan tidak dijumpai.'
], 404);
}
// Kemaskini tag_anggota
$customer->tag_anggota = 1;
$customer->save();
if (!$customer) {
return response()->json([ return response()->json([
'success' => false, 'success' => true,
'message' => 'Pelanggan tidak dijumpai.' 'message' => 'Tag anggota berjaya dikemaskini.',
], 404); 'data' => $customer
], 200);
} }
// Kemaskini tag_anggota
$customer->tag_anggota = 1;
$customer->save();
return response()->json([
'success' => true,
'message' => 'Tag anggota berjaya dikemaskini.',
'data' => $customer
], 200);
}
public function customerUpdateKaunter($nokp,$kodcaw, Request $request){ public function customerUpdateKaunter($nokp,$kodcaw, Request $request){
$current = Carbon::now(); $current = Carbon::now();
@@ -1102,4 +1106,182 @@ public function AdminsearchCustomerBySAG(Request $request)
return response()->json($results); return response()->json($results);
} }
public function updateTagPdpa(Request $request)
{
$tarikh_cetak_pdpa = Carbon::now()->toDateString();
$no_ic = $request->input('no_ic');
$customer = Customer::where('kplama', $no_ic)
->orWhere('kpbaru', $no_ic)
->first();
if (!$customer) {
return response()->json(['error' => 'Customer not found.'], 404);
}
$customer->tag_pdpa = $request->input('tag_pdpa');
$customer->tarikh_cetak_pdpa = $tarikh_cetak_pdpa;
$customer->save();
return response()->json(['message' => 'Customer PDPA updated successfully.'], 200);
}
/**
* Store MyKad photo for an existing customer only when gambar_mykad is empty.
*/
public function storeMykadPhotoIfMissing($ic, Request $request)
{
$customer = Customer::on('mysql7')
->where('kplama', '=', $ic)
->orWhere('kpbaru', '=', $ic)
->first();
if (!$customer) {
return response()->json(['error' => 'Customer not found.', 'stored' => false], 404);
}
if (!empty($customer->gambar_mykad)) {
return response()->json([
'message' => 'MyKad photo already stored.',
'gambar_mykad' => $customer->gambar_mykad,
'stored' => false,
], 200);
}
$this->attachMykadPhoto($customer, $request);
return response()->json([
'message' => !empty($customer->gambar_mykad)
? 'MyKad photo stored.'
: 'No photo provided or store failed.',
'gambar_mykad' => $customer->gambar_mykad,
'stored' => !empty($customer->gambar_mykad),
], 200);
}
/**
* Save MyKad photo into arrahn_files and link it on customer.gambar_mykad.
*/
protected function attachMykadPhoto($customer, Request $request)
{
if (!$customer) {
return;
}
$raw = $this->photoBase64FromRequest($request);
if ($raw === '') {
return;
}
try {
$fileId = $this->storeMykadPhoto($raw, $request->input('photo_mime', $request->json('photo_mime')), $customer);
if (!$fileId) {
return;
}
Customer::on('mysql7')
->where('kpbaru', $customer->kpbaru)
->where('kplama', $customer->kplama)
->update(['gambar_mykad' => $fileId]);
$customer->gambar_mykad = $fileId;
} catch (\Throwable $e) {
Log::error('Failed to attach MyKad photo: ' . $e->getMessage());
}
}
protected function photoBase64FromRequest(Request $request)
{
$raw = $request->input('photo_base64');
if ($raw === null || $raw === '') {
$raw = $request->json('photo_base64');
}
return is_string($raw) ? $raw : '';
}
protected function storeMykadPhoto($raw, $mime, $customer)
{
$commaPos = strpos($raw, ',');
if ($commaPos !== false) {
$raw = substr($raw, $commaPos + 1);
}
$raw = preg_replace('/\s+/', '', $raw);
if ($raw === '') {
return null;
}
$decoded = base64_decode($raw, true);
if ($decoded === false) {
$decoded = base64_decode($raw);
}
if ($decoded === false || $decoded === '') {
Log::warning('MyKad photo skipped: invalid base64');
return null;
}
if (strlen($raw) > 400000) {
Log::warning('MyKad photo skipped: payload too large');
return null;
}
$mime = strtolower((string) ($mime ?: 'image/jpeg'));
if (!in_array($mime, ['image/jpeg', 'image/jpg', 'image/png'], true)) {
$mime = 'image/jpeg';
}
$nric = preg_replace('/[^0-9A-Za-z]/', '', (string) ($customer->kpbaru ?: ''));
$ext = $mime === 'image/png' ? 'png' : 'jpg';
$file = new ARFiles();
$file->data = $raw;
$file->filename = ($nric !== '' ? $nric : 'mykad') . '.' . $ext;
$file->type = $mime;
$file->tarikhmasa = Carbon::now()->toDateTimeString();
$file->save();
return $file->id;
}
protected function withMykadPhoto($customer)
{
if (!$customer) {
return $customer;
}
$payload = $customer->toArray();
$payload['photo_base64'] = null;
$payload['photo_mime'] = null;
if (empty($customer->gambar_mykad)) {
return $payload;
}
$file = ARFiles::on('mysql7')->where('id', $customer->gambar_mykad)->first();
if ($file && $file->data) {
$payload['photo_base64'] = $file->data;
$payload['photo_mime'] = $file->type ?: 'image/jpeg';
}
return $payload;
}
// public function createNotaArcc($kodcaw, Request $request)
// {
// $current = Carbon::now();
// $customer = Customer::on('mysql7')->create([
// 'kodcaw' => $kodcaw,
// 'tarikhdaftar' => $current->toDateString(),
// 'nota_arcc' => $request->nota_arcc,
// 'teller' => $request->teller,
// 'aktif' => 'A'
// ]);
// return response()->json($customer, 201);
// }
} }
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers;
use App\Cawangan;
use App\Feature;
use Illuminate\Http\Request;
class FeatureController extends Controller
{
public function index()
{
return response()->json([
'keys' => Feature::keys(),
'labels' => Feature::labels(),
]);
}
public function cawangan()
{
$cawangan = Cawangan::on('mysql7')
->orderBy('kodcaw')
->get(['kodcaw', 'details_caw']);
$maps = Feature::mapForMany($cawangan->pluck('kodcaw')->all());
$data = $cawangan->map(function ($c) use ($maps) {
return [
'kodcaw' => $c->kodcaw,
'details_caw' => $c->nama_cawangan,
'features' => $maps[$c->kodcaw] ?? Feature::defaults(),
];
});
return response()->json($data);
}
public function update($kodcaw, $feature, Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', $kodcaw)->first();
if (!$cawangan) {
return response()->json(['message' => 'Cawangan tidak dijumpai'], 404);
}
if (!in_array($feature, Feature::keys(), true)) {
return response()->json(['message' => 'Ciri tidak sah'], 422);
}
$enabled = (int) $request->input('enabled') ? 1 : 0;
Feature::setEnabled($kodcaw, $feature, $enabled);
return response()->json([
'kodcaw' => $kodcaw,
'feature' => $feature,
'enabled' => $enabled,
'features' => Feature::mapFor($kodcaw),
]);
}
}
+8 -3
View File
@@ -537,7 +537,7 @@ class GadaiController extends Controller
public function getLaporanGadai($kodcaw,$tarikh){ public function getLaporanGadai($kodcaw,$tarikh){
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$gadai_data = Gadai::on($cawangan['mysql'])->select('t_gadai','norujukan','nokp','semakbarcode','masa','berat','nilaimarhun','pinjaman','upah12','percentpinj','teller','nokp','ujrah','onepercent') $gadai_data = Gadai::on($cawangan['mysql'])->select('cara_terima','t_gadai','norujukan','nokp','semakbarcode','masa','berat','nilaimarhun','pinjaman','upah12','percentpinj','teller','nokp','ujrah','onepercent','kadarrebet','diskaun')
->where('t_gadai','=',$tarikh) ->where('t_gadai','=',$tarikh)
->orderBy('teller') ->orderBy('teller')
->orderBy('sirig') ->orderBy('sirig')
@@ -551,6 +551,7 @@ class GadaiController extends Controller
array_push($gadai,[ array_push($gadai,[
"t_gadai"=> $gadaiData['t_gadai'], "t_gadai"=> $gadaiData['t_gadai'],
"norujukan"=> $gadaiData['norujukan'], "norujukan"=> $gadaiData['norujukan'],
"cara_terima"=> $gadaiData['cara_terima'],
"nama"=> $customer_info['nama'], "nama"=> $customer_info['nama'],
"nokp"=> $gadaiData['nokp'], "nokp"=> $gadaiData['nokp'],
"semakbarcode"=> $gadaiData['semakbarcode'], "semakbarcode"=> $gadaiData['semakbarcode'],
@@ -563,7 +564,9 @@ class GadaiController extends Controller
"alamat1"=> $customer_info['alamat1'], "alamat1"=> $customer_info['alamat1'],
"teller"=> $gadaiData['teller'], "teller"=> $gadaiData['teller'],
"onepercent"=> $gadaiData['onepercent'], "onepercent"=> $gadaiData['onepercent'],
"ujrah"=> $gadaiData['ujrah'] "ujrah"=> $gadaiData['ujrah'],
"kadarrebet"=> $gadaiData['kadarrebet'],
"diskaun"=> $gadaiData['diskaun']
]); ]);
} }
} }
@@ -626,7 +629,7 @@ class GadaiController extends Controller
"alamat1" => optional($customer)->alamat1, "alamat1" => optional($customer)->alamat1,
"t_gadai" => $g->t_gadai, "t_gadai" => $g->t_gadai,
"norujukan" => $g->norujukan, "norujukan" => $g->norujukan,
"norujukan"=> $g->norujukan, "cara_terima" => strtoupper((string) ($g->cara_terima ?: 'TUNAI')),
"nokp"=> $g->nokp, "nokp"=> $g->nokp,
"semakbarcode"=> $g->semakbarcode, "semakbarcode"=> $g->semakbarcode,
@@ -640,6 +643,8 @@ class GadaiController extends Controller
"teller"=> $g->teller, "teller"=> $g->teller,
"ujrah"=> $g->ujrah, "ujrah"=> $g->ujrah,
"onepercent"=> $g->onepercent, "onepercent"=> $g->onepercent,
"kadarrebet"=> $g->kadarrebet,
"diskaun"=> $g->diskaun,
"tagbaru"=> $g->tagbaru, "tagbaru"=> $g->tagbaru,
"kodkerja"=> optional($customer)->kodkerja, "kodkerja"=> optional($customer)->kodkerja,
"bangsa"=> optional($customer)->bangsa, "bangsa"=> optional($customer)->bangsa,
+128 -13
View File
@@ -8,6 +8,7 @@ use App\Cawangan;
use App\Mohonid; use App\Mohonid;
use App\MohonAnggota; use App\MohonAnggota;
use App\Customer; use App\Customer;
use App\Services\StaffNotificationClient;
use Carbon\Carbon; use Carbon\Carbon;
@@ -444,6 +445,8 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
$audit->updated_at = $harini; $audit->updated_at = $harini;
$audit->save(); $audit->save();
$this->notifyOperasiAssignAnggota($kodcaw, $cawangan, $kelulusan, $request);
// ✅ Return JSON response ke frontend // ✅ Return JSON response ke frontend
return response()->json([ return response()->json([
'success' => true, 'success' => true,
@@ -466,6 +469,58 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
} }
} }
/**
* Inbox for all operasi staff. Failures must not block the teller request.
*
* @param string $kodcaw
* @param \App\Cawangan $cawangan
* @param \App\MohonAnggota $kelulusan
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function notifyOperasiAssignAnggota($kodcaw, $cawangan, $kelulusan, Request $request)
{
try {
$nama = trim((string) ($request->nama ?? ''));
if ($nama === '' && !empty($kelulusan->nokp)) {
$customer = Customer::on('mysql7')
->where('kpbaru', $kelulusan->nokp)
->orWhere('kplama', $kelulusan->nokp)
->first();
if ($customer && !empty($customer->nama)) {
$nama = trim((string) $customer->nama);
}
}
$namaCaw = $kodcaw;
if ($cawangan) {
$resolved = trim((string) $cawangan->nama_cawangan);
if ($resolved !== '') {
$namaCaw = $resolved;
}
}
app(StaffNotificationClient::class)->notify([
'type' => 'anggota.pending_operasi',
'cawangan' => $kodcaw,
'audience' => ['role' => 'operasi'],
'data' => [
'id' => $kelulusan->id,
'nokp' => $kelulusan->nokp,
'nama' => $nama,
'teller' => $kelulusan->teller,
'komen' => $kelulusan->komen,
'cawangan_nama' => $namaCaw,
],
]);
} catch (\Throwable $e) {
Log::warning('Assign anggota operasi notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'nokp' => isset($kelulusan->nokp) ? $kelulusan->nokp : null,
]);
}
}
//KEPOHONAN EDIT DAN HAPUS //KEPOHONAN EDIT DAN HAPUS
public function kepohonancreate(REQUEST $request,$kodcaw,$norujukan) public function kepohonancreate(REQUEST $request,$kodcaw,$norujukan)
@@ -600,26 +655,86 @@ public function create(REQUEST $request,$kodcaw,$norujukan,$roles)
return response()->json($arraykelulusan,200); return response()->json($arraykelulusan,200);
} }
public function listoperasikelulusan() public function listoperasikelulusan(Request $request)
{ {
$harini = Carbon::now(); $dateFrom = $request->query('date_from');
$harini = new Carbon; $dateTo = $request->query('date_to');
$search = trim((string) $request->query('search', ''));
// Default to last 30 days to avoid loading entire history
if (!$dateFrom && !$dateTo) {
$dateTo = Carbon::now()->toDateString();
$dateFrom = Carbon::now()->subDays(30)->toDateString();
}
$cawangan_all = Cawangan::on('mysql7')
->select('mysql', 'details_caw', 'kodcaw')
->get();
$cawangan_all = Cawangan::on('mysql7')->get();
$arraykelulusan = []; $arraykelulusan = [];
foreach($cawangan_all as $index=>$cawangan){ foreach ($cawangan_all as $cawangan) {
$kelulusan=Mohon::on($cawangan['mysql']) $query = Mohon::on($cawangan['mysql'])
->where('peloperasi','=','operasi') ->select([
->where('operasi','!=','') 'tarikh',
->get(); 'norujukan',
'komen',
'jumpinj',
'nilaimarhun',
'berat',
'margin',
'status',
'operasi',
'masalulus',
])
->where('peloperasi', '=', 'operasi')
->where('operasi', '!=', '');
if(sizeof($kelulusan) != 0){ if ($dateFrom) {
array_push($arraykelulusan,["kelulusanall" => $kelulusan,"cawangan" => $cawangan['details_caw'],"cawangankod" => $cawangan['kodcaw']]); $query->where('tarikh', '>=', $dateFrom);
}
if ($dateTo) {
$query->where('tarikh', '<=', $dateTo);
}
if ($search !== '') {
$query->where(function ($q) use ($search) {
$q->where('norujukan', 'like', "%{$search}%")
->orWhere('operasi', 'like', "%{$search}%")
->orWhere('status', 'like', "%{$search}%")
->orWhere('komen', 'like', "%{$search}%");
});
} }
}
return response()->json($arraykelulusan,200); $kelulusan = $query->orderBy('tarikh', 'desc')->get();
foreach ($kelulusan as $kel) {
$komenParts = explode('|', (string) ($kel->komen ?? ''));
$komenParts = array_values(array_filter($komenParts, function ($part) {
return $part !== '';
}));
$arraykelulusan[] = [
'tarikh' => $kel->tarikh ?? '-',
'norujukan' => $kel->norujukan ?? '-',
'status' => $kel->status ?? '-',
'komen' => $komenParts[0] ?? ($kel->komen ?: '-'),
'pinjaman' => $kel->jumpinj ?? '-',
'nilaimarhun' => $komenParts[1] ?? ($kel->nilaimarhun ?? '-'),
'berat' => $komenParts[2] ?? ($kel->berat ?? '-'),
'margin' => $komenParts[3] ?? ($kel->margin ?? '-'),
'pelulus' => $kel->operasi ?? '-',
'masalulus' => $kel->masalulus ?? '-',
'cawangan' => $cawangan['details_caw'],
'cawangankod' => $cawangan['kodcaw'],
];
}
}
usort($arraykelulusan, function ($a, $b) {
return strcmp((string) ($b['tarikh'] ?? ''), (string) ($a['tarikh'] ?? ''));
});
return response()->json($arraykelulusan, 200);
} }
public function UpdateHapusAnsuranOperasi(Request $request){ public function UpdateHapusAnsuranOperasi(Request $request){
+5 -2
View File
@@ -145,15 +145,18 @@ class KomuditiController extends Controller
$komuditi_current = KomuditiPurchase::on('mysql7')->where('date_purchase','=',$current->toDateString())->orderBy('created_at','desc')->first(); $komuditi_current = KomuditiPurchase::on('mysql7')->where('date_purchase','=',$current->toDateString())->orderBy('created_at','desc')->first();
$unit_current = $komuditi_current['unit_komuditi']; $unit_current = $komuditi_current['unit_komuditi'];
$update_unit = $unit_current - $request->unit_komuditi; $update_unit = $unit_current - $request->unit_komuditi;
$unit_current = round((float) $komuditi_current['unit_komuditi'], 2);
$unit_komuditi = round((float) $request->unit_komuditi, 2);
$update_unit = round($unit_current - $unit_komuditi, 2);
$update_komuditi = KomuditiPurchase::on('mysql7')->where('id','=',$komuditi_current['id'])->update([ $update_komuditi = KomuditiPurchase::on('mysql7')->where('id','=',$komuditi_current['id'])->update([
'unit_komuditi' => $update_unit 'unit_komuditi' => $update_unit
]); ]);
$komuditi_transaksi = KomuditiTransaksi::on('mysql7')->create([ $komuditi_transaksi = KomuditiTransaksi::on('mysql7')->create([
'unit_komuditi' => $request->unit_komuditi, 'unit_komuditi' => $unit_komuditi,
'nosijil'=>$komuditi_current['no_sijil'], 'nosijil'=>$komuditi_current['no_sijil'],
'norujukan' => $request->norujukan, 'norujukan' => $request->norujukan,
'kodcaw' => $request->kodcaw , 'kodcaw' => $request->kodcaw ,
+6 -18
View File
@@ -847,7 +847,7 @@ $date = new Carbon($request->tarikh);
$bilangan_tebus = $tebus->count(); $bilangan_tebus = $tebus->count();
// $bilangan_ansuran = $ansuran->count(); // $bilangan_ansuran = $ansuran->count();
//$bilangan_customer_baru = Customer::on($cawangan['mysql'])->where([['tarikhdaftar','>=',$request->mula],['tarikhdaftar','<=',$request->akhir]])->count(); //$bilangan_customer_baru = Customer::on($cawangan['mysql'])->where([['tarikhdaftar','>=',$request->mula],['tarikhdaftar','<=',$request->akhir]])->count();
$bilangan_customer_baru = Customer::on('mysql7')->where([['tarikhdaftar','>=',$request->mula],['tarikhdaftar','<=',$request->akhir]])->count(); $bilangan_customer_baru = Customer::on('mysql7')->whereBetween('tarikhdaftar', [$request->mula, $request->akhir])->where('kodcaw', $request->kodcaw)->count();
//f //f
$data_lelong = Lelong::on($cawangan['mysql'])->where([['t_jual','>=',$request->mula],['t_jual','<=',$request->akhir]])->get(); $data_lelong = Lelong::on($cawangan['mysql'])->where([['t_jual','>=',$request->mula],['t_jual','<=',$request->akhir]])->get();
@@ -975,23 +975,11 @@ $date = new Carbon($request->tarikh);
$gadaiDataKaunter = Gadai::on($cawangan['mysql'])->whereIn('norujukan',$total_keuntunganansuran_data_kaunter)->whereNotNull('hargajualan')->pluck('norujukan'); $gadaiDataKaunter = Gadai::on($cawangan['mysql'])->whereIn('norujukan',$total_keuntunganansuran_data_kaunter)->whereNotNull('hargajualan')->pluck('norujukan');
// $keuntunganansuran_kaunter_query = TransaksiKeuntungan::on('mysql7')
// ->whereIn('norujukan',$gadaiDataKaunter)
// ->whereDate('tarikh_bayaran','>=',$request->mula)
// ->whereDate('tarikh_bayaran','<=',$request->akhir);
// //new update
$keuntunganansuran_kaunter_query = TransaksiKeuntungan::on('mysql7') $keuntunganansuran_kaunter_query = TransaksiKeuntungan::on('mysql7')
->leftJoin('transaction as trans', function($join) ->whereIn('norujukan',$gadaiDataKaunter)
{ ->whereDate('tarikh_bayaran','>=',$request->mula)
$join->on('transaksi_keuntungan.norujukan', '=', 'trans.norujukan'); ->whereDate('tarikh_bayaran','<=',$request->akhir);
$join->on('transaksi_keuntungan.tarikh_bayaran', '=', 'trans.created_at');
})
->whereDate('trans.created_at','>=',$request->mula)
->whereDate('trans.created_at','<=',$request->akhir)
->where('trans.teller','<>','Online')
->where('trans.trans_type','=','A')
->where('trans.kodcaw',$request->kodcaw)
->pluck('transaksi_keuntungan.norujukan');
$total_keuntunganansuran_kaunter = $keuntunganansuran_kaunter_query->sum('bayaran_keuntungan'); $total_keuntunganansuran_kaunter = $keuntunganansuran_kaunter_query->sum('bayaran_keuntungan');
@@ -1257,7 +1245,7 @@ $date = new Carbon($request->tarikh);
array_push($atarray,[ array_push($atarray,[
'tarikh' => $tarikh, 'tarikh' => $tarikh,
'masa' => $masa, 'masa' => $masa,
'users' => isset($at['users']) && $at['users'] !== '' ? $at['users'] : '-', 'users' => $at['users'],
'pelulus' => isset($atnewdata['namapelulus']) ? $atnewdata['namapelulus'] : '-', 'pelulus' => isset($atnewdata['namapelulus']) ? $atnewdata['namapelulus'] : '-',
'actions' => $at['actions'], 'actions' => $at['actions'],
'norujukan' => $at['norujukan'], 'norujukan' => $at['norujukan'],
+33 -16
View File
@@ -11,6 +11,7 @@ use App\Negeri;
use App\Lelong; use App\Lelong;
use App\Transaction; use App\Transaction;
use App\Cawangan; use App\Cawangan;
use App\Feature;
use App\Gadai; use App\Gadai;
use App\TuntutBaki; use App\TuntutBaki;
use App\WTD; use App\WTD;
@@ -26,6 +27,7 @@ use App\Tunai;
use App\Audit; use App\Audit;
use App\GlDetail; use App\GlDetail;
use App\Pembeli; use App\Pembeli;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use App\Services\RingkasanHarianServices; use App\Services\RingkasanHarianServices;
@@ -604,16 +606,7 @@ class LelongController extends Controller
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first();
if($cawangan['tagarrahnbid'] == 1){ if($cawangan['tagarrahnbid'] == 1){
if(env('APP_ENV') != 'production'){ $lelong = $this->fetchPortalLelong($request->kodcaw, $request->norujukan);
$link = 'http://niklah.arrahn.com.my/api/portalApi';
}else{
$link = 'https://adminbid-kopkb.erahn.com.my/api/portalApi';
}
$lelong = Http::withOptions(['verify' => true])->get($link,[
'norujukan' => $request->norujukan
])->json();
$lelong = ($lelong != null) ? $lelong['lelong'] : [];
if( $lelong == null ) if( $lelong == null )
{ {
$lelong = Lelong::on($cawangan['mysql']) $lelong = Lelong::on($cawangan['mysql'])
@@ -656,10 +649,7 @@ class LelongController extends Controller
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $request->kodcaw)->first();
if ($cawangan['tagarrahnbid'] == 0){ if ($cawangan['tagarrahnbid'] == 0){
$lelong = Http::withOptions(['verify' => true])->get(config('app_parameter.api_arrahnbid').'portalApi',[ $lelong = $this->fetchPortalLelong($request->kodcaw, $request->norujukan);
'norujukan' => $request->norujukan
])->json();
$lelong = ($lelong != null) ? $lelong['lelong'] : [];
if( $lelong == null ) if( $lelong == null )
{ {
$lelong = Lelong::on($cawangan['mysql']) $lelong = Lelong::on($cawangan['mysql'])
@@ -729,8 +719,8 @@ class LelongController extends Controller
if($cawangan['tagarrahnbid'] == 1) if($cawangan['tagarrahnbid'] == 1)
{ {
$lelong = Http::withOptions(['verify' => true])->get(config('app_parameter.api_arrahnbid').'portalApi',['norujukan' => $request->norujukan])->json(); $lelong = $this->fetchPortalLelong($request->kodcaw, $request->norujukan);
$lelong = ($lelong != null) ? $lelong['lelong'] : []; $lelong = ($lelong != null) ? $lelong : [];
}else{ }else{
$lelong = Lelong::on($cawangan['mysql'])->where([['norujukan',$request->norujukan]])->first(); $lelong = Lelong::on($cawangan['mysql'])->where([['norujukan',$request->norujukan]])->first();
} }
@@ -2009,4 +1999,31 @@ class LelongController extends Controller
], 200); ], 200);
} }
/**
* Fetch lelong status from arrahnbid. Returns null when the portal is
* unreachable so callers can fall back to the local lelong table.
*/
private function fetchPortalLelong($kodcaw, $norujukan)
{
try {
$payload = Http::withOptions(['verify' => true])
->timeout(5)
->get(Feature::arrahnbidUrl($kodcaw).'portalApi', [
'norujukan' => $norujukan,
])
->json();
return ($payload != null) ? ($payload['lelong'] ?? null) : null;
} catch (ConnectionException $e) {
Log::warning('Arrahnbid portal unreachable; falling back to local lelong.', [
'kodcaw' => $kodcaw,
'norujukan' => $norujukan,
'url' => Feature::arrahnbidUrl($kodcaw).'portalApi',
'error' => $e->getMessage(),
]);
return null;
}
}
} }
+51 -30
View File
@@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Marhun; use App\Marhun;
use App\Gadai;
use App\Jemas; use App\Jemas;
use App\Cawangan; use App\Cawangan;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -160,39 +161,53 @@ class MarhunController extends Controller
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$old_data = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->first(); // Lock the parent first so concurrent edits to this SAG are serialized.
(new Marhun)->setConnection($cawangan['mysql'])->getConnection()->transaction(function () use ($cawangan, $norujukan, $request, $harga, $nilai_marhun, $kodcaw, $current) {
Gadai::on($cawangan['mysql'])->where('norujukan', $norujukan)->lockForUpdate()->firstOrFail();
$old_data = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->firstOrFail();
$marhunToUpdate = [ $marhunToUpdate = [
'karat' => $request->karat, 'karat' => $request->karat,
'marhun' => $request->marhun, 'marhun' => $request->marhun,
'nota' => $request->nota, 'nota' => $request->nota,
'tag_permata' => $request->tag_permata, 'tag_permata' => $request->tag_permata,
'berat_batu_permata' => $request->berat_batu_permata, 'berat_batu_permata' => $request->berat_batu_permata,
'berat' => $request->berat, 'berat' => $request->berat,
'harga' => $harga['harga'], 'harga' => $harga['harga'],
'n_marhun' => $nilai_marhun, 'n_marhun' => $nilai_marhun,
'bil' => $request->kuantiti, 'bil' => $request->kuantiti,
]; ];
// Set value berat_batu_permata to null if value passed is null // Set value berat_batu_permata to null if value passed is null
if($marhunToUpdate['berat_batu_permata'] == '') { if($marhunToUpdate['berat_batu_permata'] == '') {
$marhunToUpdate['berat_batu_permata'] = null; $marhunToUpdate['berat_batu_permata'] = null;
}; };
$jemas = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->update($marhunToUpdate); $jemas = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->update($marhunToUpdate);
$new_data = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->first(); // Recalculate from every item belonging to this SAG, after the edit.
$totals = Marhun::on($cawangan['mysql'])->where('norujukan', $norujukan)
->selectRaw('COALESCE(SUM(berat), 0) AS total_berat, COALESCE(SUM(n_marhun), 0) AS total_nilai, COALESCE(SUM(bil), 0) AS total_kuantiti')
->first();
Gadai::on($cawangan['mysql'])->where('norujukan', $norujukan)->update([
'berat' => $totals->total_berat,
'nilaimarhun' => $totals->total_nilai,
'kuantiti' => $totals->total_kuantiti,
]);
$audit = new Audit(); $new_data = Marhun::on($cawangan['mysql'])->where([['recno','=',$request->recno],['norujukan','=',$norujukan]])->first();
$audit->users = $request->teller;
$audit->actions = 'Ubah Marhun'; $audit = new Audit();
$audit->norujukan = $norujukan; $audit->users = $request->teller;
$audit->old_data = $old_data; $audit->actions = 'Ubah Marhun';
$audit->new_data = $new_data; $audit->norujukan = $norujukan;
$audit->kodcaw = $kodcaw; $audit->old_data = $old_data;
$audit->created_at = $current; $audit->new_data = $new_data;
$audit->updated_at = $current; $audit->kodcaw = $kodcaw;
$audit->save(); $audit->created_at = $current;
$audit->updated_at = $current;
$audit->save();
});
return response('Edit Successfully', 200); return response('Edit Successfully', 200);
} }
@@ -203,7 +218,10 @@ class MarhunController extends Controller
$marhun = Marhun::on($cawangan['mysql']) $marhun = Marhun::on($cawangan['mysql'])
->select('marhun.norujukan','gadai.nokp','customer.nama','marhun.marhun','marhun.nota','marhun.berat','marhun.karat','marhun.harga','marhun.n_marhun','marhun.t_gadai','marhun.tag_permata', 'marhun.berat_batu_permata') ->select('marhun.norujukan','gadai.nokp','customer.nama','marhun.marhun','marhun.nota','marhun.berat','marhun.karat','marhun.harga','marhun.n_marhun','marhun.t_gadai','marhun.tag_permata', 'marhun.berat_batu_permata')
->rightJoin('gadai', 'gadai.norujukan', '=', 'marhun.norujukan') ->rightJoin('gadai', 'gadai.norujukan', '=', 'marhun.norujukan')
->rightJoin(config('tennant_database.master_db') . '.customer as customer', 'customer.kpbaru','=','gadai.nokp') ->rightJoin('master_erahn.customer as customer', function($join) {
$join->on('customer.kpbaru', '=', 'gadai.nokp')
->orOn('customer.kplama', '=', 'gadai.nokp');
})
->where('marhun.t_gadai','=',$tarikh) ->where('marhun.t_gadai','=',$tarikh)
->orderBy('marhun.t_gadai') ->orderBy('marhun.t_gadai')
->get(); ->get();
@@ -221,7 +239,10 @@ class MarhunController extends Controller
$marhun = Marhun::on($cawangan['mysql']) $marhun = Marhun::on($cawangan['mysql'])
->select('marhun.norujukan','gadai.sttebus','gadai.pinjaman','gadai.nokp','customer.nama','marhun.marhun','marhun.nota','marhun.berat','marhun.karat','marhun.harga','marhun.n_marhun','marhun.t_gadai') ->select('marhun.norujukan','gadai.sttebus','gadai.pinjaman','gadai.nokp','customer.nama','marhun.marhun','marhun.nota','marhun.berat','marhun.karat','marhun.harga','marhun.n_marhun','marhun.t_gadai')
->rightJoin('gadai', 'gadai.norujukan', '=', 'marhun.norujukan') ->rightJoin('gadai', 'gadai.norujukan', '=', 'marhun.norujukan')
->rightJoin(config('tennant_database.master_db') . '.customer as customer', 'customer.kpbaru','=','gadai.nokp') ->rightJoin('master_erahn.customer as customer', function($join) {
$join->on('customer.kpbaru', '=', 'gadai.nokp')
->orOn('customer.kplama', '=', 'gadai.nokp');
})
->where('marhun.t_gadai','<=',$tarikh) ->where('marhun.t_gadai','<=',$tarikh)
->whereYear('marhun.t_gadai','=',$current->year) ->whereYear('marhun.t_gadai','=',$current->year)
->where('gadai.sttebus','=','') ->where('gadai.sttebus','=','')
@@ -0,0 +1,160 @@
<?php
namespace App\Http\Controllers;
use App\Services\PembiayaanOnline\ModalOnlineService;
use Illuminate\Http\Request;
class ModalOnlineController extends Controller
{
protected $service;
public function __construct(ModalOnlineService $service = null)
{
$this->service = $service ?: new ModalOnlineService();
}
protected function respond(array $result)
{
return response()->json($result['payload'], $result['status']);
}
/**
* GET /api/modal-online/{kodcaw}
* Read-only: does not create today's row.
*/
public function get($kodcaw)
{
return $this->respond($this->service->get($kodcaw));
}
/**
* @deprecated Alias of get() — no longer auto-creates RM100k.
*/
public function getOrCreate($kodcaw)
{
return $this->get($kodcaw);
}
/**
* POST /api/modal-online/{kodcaw}/reserve
*/
public function reserve($kodcaw, Request $request)
{
return $this->respond($this->service->reserve($kodcaw, $request->input('amaun', 0)));
}
/**
* GET /api/initiate/modal-online?zone=all|{zone}
*/
public function initiate(Request $request)
{
return $this->respond($this->service->initiate($request->input('zone', 'all')));
}
/**
* POST /api/modal-online/{kodcaw}/topup/request
* PC mohon had (hari pertama) atau tambah modal. Body: amaun, dimohon_oleh, nota?
*/
public function requestTopUp($kodcaw, Request $request)
{
return $this->respond($this->service->requestTopUp(
$kodcaw,
$request->input('amaun', 0),
$request->input('dimohon_oleh', ''),
$request->input('nota')
));
}
/**
* GET /api/modal-online/{kodcaw}/topup/requests?status=&tarikh=
*/
public function listTopUpByCawangan($kodcaw, Request $request)
{
return $this->respond($this->service->listTopUpRequests(
$kodcaw,
$request->input('status'),
$request->input('tarikh')
));
}
/**
* GET /api/modal-online/operasi/topup/requests?status=PENDING&tarikh=
*/
public function listTopUpOperasi(Request $request)
{
$status = $request->input('status', 'PENDING');
if ($status === 'all' || $status === '') {
$status = null;
}
return $this->respond($this->service->listTopUpRequests(
null,
$status,
$request->input('tarikh')
));
}
/**
* POST /api/modal-online/operasi/topup/{id}/approve
* Body: diluluskan_oleh, nota?, amaun? (optional override)
*/
public function approveTopUp($id, Request $request)
{
return $this->respond($this->service->approveTopUp(
$id,
$request->input('diluluskan_oleh', ''),
$request->input('nota'),
null,
$request->input('amaun')
));
}
/**
* POST /api/modal-online/operasi/topup/{id}/reject
* Body: diluluskan_oleh, nota?
*/
public function rejectTopUp($id, Request $request)
{
return $this->respond($this->service->rejectTopUp(
$id,
$request->input('diluluskan_oleh', ''),
$request->input('nota')
));
}
/**
* POST /api/modal-online/{kodcaw}/close
* Body: ditutup_oleh
*/
public function close($kodcaw, Request $request)
{
return $this->respond($this->service->close(
$kodcaw,
$request->input('ditutup_oleh', '')
));
}
/**
* GET /api/modal-online/operasi/eod?zone=all&tarikh=
*/
public function listToday(Request $request)
{
return $this->respond($this->service->listToday(
$request->input('zone', 'all'),
$request->input('tarikh')
));
}
/**
* POST /api/modal-online/operasi/eod/close-all
* Body: ditutup_oleh, zone?
*/
public function closeAll(Request $request)
{
return $this->respond($this->service->closeAll(
$request->input('ditutup_oleh', ''),
$request->input('zone', 'all')
));
}
}
@@ -0,0 +1,241 @@
<?php
namespace App\Http\Controllers;
use App\Services\PembiayaanOnline\PembiayaanOnlineService;
use Illuminate\Http\Request;
class PembiayaanOnlineController extends Controller
{
protected $service;
public function __construct(PembiayaanOnlineService $service = null)
{
$this->service = $service ?: new PembiayaanOnlineService();
}
protected function respond(array $result)
{
return response()->json($result['payload'], $result['status']);
}
/**
* POST /api/pembiayaan-online/{kodcaw}
*/
public function store($kodcaw, Request $request)
{
return $this->respond($this->service->store($kodcaw, $request->all()));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/by-norujukan/{norujukan}
*/
public function show($kodcaw, $norujukan)
{
return $this->respond($this->service->show($kodcaw, $norujukan));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/lines/{id}
*/
public function showLine($kodcaw, $id)
{
return $this->respond($this->service->showLine($kodcaw, $id));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/queue
*/
public function queue($kodcaw, Request $request)
{
return $this->respond($this->service->queue($kodcaw, $request->input('status')));
}
/**
* PUT /api/pembiayaan-online/{kodcaw}/lines/{id}
*/
public function updateLine($kodcaw, $id, Request $request)
{
// Prefer only present keys so "has" semantics match previous controller.
$input = [];
foreach ([
'kodbank', 'noakaun', 'nama_bank', 'nama_akaun', 'nota',
'bukti_akaun_file_id', 'borang_file_id', 'update_customer_bank',
'bukti_akaun_data', 'bukti_akaun_filename', 'bukti_akaun_type',
] as $key) {
if ($request->has($key)) {
$input[$key] = $request->input($key);
}
}
return $this->respond($this->service->updateLine($kodcaw, $id, $input));
}
/**
* POST /api/pembiayaan-online/{kodcaw}/lines/{id}/verify
*/
public function verifyLine($kodcaw, $id, Request $request)
{
return $this->respond($this->service->verifyLine(
$kodcaw,
$id,
$request->input('pc_verified_by', '')
));
}
/**
* POST /api/pembiayaan-online/{kodcaw}/lines/{id}/unverify
*/
public function unverifyLine($kodcaw, $id)
{
return $this->respond($this->service->unverifyLine($kodcaw, $id));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/batches
*/
public function listBatches($kodcaw, Request $request)
{
return $this->respond($this->service->listBatches(
$kodcaw,
$request->input('tarikh'),
$request->input('status')
));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/batches/{batchId}
*/
public function showBatch($kodcaw, $batchId)
{
return $this->respond($this->service->showBatch($kodcaw, $batchId));
}
/**
* POST /api/pembiayaan-online/{kodcaw}/batches/submit
*/
public function submitBatch($kodcaw, Request $request)
{
return $this->respond($this->service->submitBatch(
$kodcaw,
$request->input('ids', []),
$request->input('pc_submitted_by', ''),
$request->input('slot')
));
}
/**
* GET /api/pembiayaan-online/operasi/batches
*/
public function operasiListBatches(Request $request)
{
return $this->respond($this->service->operasiListBatches(
$request->input('status', 'SUBMITTED'),
$request->input('tarikh')
));
}
/**
* GET /api/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}
*/
public function operasiShowBatch($kodcaw, $batchId)
{
return $this->showBatch($kodcaw, $batchId);
}
/**
* POST /api/pembiayaan-online/operasi/{kodcaw}/lines/{id}/approve
*/
public function operasiApproveLine($kodcaw, $id, Request $request)
{
return $this->respond($this->service->operasiApproveLine(
$kodcaw,
$id,
$request->input('ops_by', '')
));
}
/**
* POST /api/pembiayaan-online/operasi/{kodcaw}/lines/{id}/reject
*/
public function operasiRejectLine($kodcaw, $id, Request $request)
{
return $this->respond($this->service->operasiRejectLine(
$kodcaw,
$id,
$request->input('ops_by', ''),
$request->input('reject_reason', '')
));
}
/**
* POST /api/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/approve
*/
public function operasiApproveBatch($kodcaw, $batchId, Request $request)
{
return $this->respond($this->service->operasiApproveBatch(
$kodcaw,
$batchId,
$request->input('ops_by', '')
));
}
/**
* POST /api/pembiayaan-online/operasi/{kodcaw}/lines/{id}/paid
* Body: ops_by, no_rujukan_bayaran, tarikh_bayaran?,
* bukti_data + bukti_filename + bukti_type OR bukti_file_id
*/
public function operasiMarkPaid($kodcaw, $id, Request $request)
{
return $this->respond($this->service->operasiMarkPaid($kodcaw, $id, $request->all()));
}
/**
* POST /api/pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/paid
*/
public function operasiMarkPaidBatch($kodcaw, $batchId, Request $request)
{
return $this->respond($this->service->operasiMarkPaidBatch($kodcaw, $batchId, $request->all()));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/lines/{id}/bukti
*/
public function getBukti($kodcaw, $id)
{
return $this->respond($this->service->getBukti($kodcaw, $id));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/lines/{id}/bukti-akaun
*/
public function getBuktiAkaun($kodcaw, $id)
{
return $this->respond($this->service->getBuktiAkaun($kodcaw, $id));
}
/**
* GET /api/pembiayaan-online/{kodcaw}/paid?blast_sent=0
*/
public function paidQueue($kodcaw, Request $request)
{
return $this->respond($this->service->paidQueue(
$kodcaw,
$request->input('blast_sent')
));
}
/**
* POST /api/pembiayaan-online/{kodcaw}/paid/blast
* Body: ids[] (or ids), blast_by?
*/
public function markBlastSent($kodcaw, Request $request)
{
$ids = $request->input('ids', $request->input('id'));
return $this->respond($this->service->markBlastSent(
$kodcaw,
$ids,
$request->input('blast_by')
));
}
}
@@ -48,7 +48,7 @@ class GadaianController extends Controller
$summary_gadaian_data = array_map(function ($db_connection) use ($request) { $summary_gadaian_data = array_map(function ($db_connection) use ($request) {
$query = Gadai::on($db_connection); $query = Gadai::on($db_connection);
$query->select(DB::raw('count(norujukan) as total_gadaian, sum(berat) as sum_berat, sum(nilaimarhun) as sum_nilai_marhun, sum(pinjaman) as sum_pinjaman')); $query->select(DB::raw('count(norujukan) as total_gadaian, sum(berat) as sum_berat, sum(nilaimarhun) as sum_nilai_marhun, sum(pinjaman) as sum_pinjaman, sum(upah12) as sum_upah_12'));
$query->when($request->filled('startDate') and $request->filled('endDate'), function ($q) use ($request) { $query->when($request->filled('startDate') and $request->filled('endDate'), function ($q) use ($request) {
return $q->where('t_gadai', '>=', $request->startDate) return $q->where('t_gadai', '>=', $request->startDate)
+75 -4
View File
@@ -20,6 +20,7 @@ use App\Panjar;
use App\HargaEmas; use App\HargaEmas;
use App\Moratorium; use App\Moratorium;
use App\Audit; use App\Audit;
use App\Feature;
use App\LookupWakil; use App\LookupWakil;
use App\StokAudit1; use App\StokAudit1;
@@ -193,7 +194,7 @@ class TebusController extends Controller
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$tebus_data = Tebus::on($cawangan['mysql']) $tebus_data = Tebus::on($cawangan['mysql'])
->select('tebus.t_tebus', 'gad.t_gadai','gad.hargajualan','gad.bakihargajualan','gad.bakipjm as gadaibakipjm','tebus.nilaimarhun','tebus.tagterima', 'tebus.teller','tebus.jenistebus','tebus.pinjaman','tebus.bakiupah','tebus.bakipjm','gad.norujukan','gad.nokp','tebus.jbg','tebus.ujrah','tebus.onepercent','gad.tagbaru') ->select('tebus.t_tebus', 'gad.t_gadai','gad.hargajualan','gad.bakihargajualan','gad.bakipjm as gadaibakipjm','tebus.nilaimarhun','tebus.tagterima', 'tebus.teller','tebus.jenistebus','tebus.pinjaman','tebus.bakiupah','tebus.bakipjm','gad.norujukan','gad.nokp','tebus.jbg','tebus.ujrah','tebus.onepercent','gad.tagbaru','tebus.diskaun_rebate')
->join('gadai as gad','gad.norujukan','=','tebus.norujukan') ->join('gadai as gad','gad.norujukan','=','tebus.norujukan')
// ->join('online_arrahn.customer as cust', function($join) // ->join('online_arrahn.customer as cust', function($join)
// { // {
@@ -232,7 +233,8 @@ class TebusController extends Controller
"nokp"=>$tebusData['nokp'], "nokp"=>$tebusData['nokp'],
"jbg"=>$tebusData['jbg'], "jbg"=>$tebusData['jbg'],
"ujrah"=>$trans['ujrah_rebet'], "ujrah"=>$trans['ujrah_rebet'],
"onepercent"=>$trans['onepercent_rebet'] "onepercent"=>$trans['onepercent_rebet'],
"diskaun_rebate"=>$tebusData['diskaun_rebate'],
]); ]);
} }
@@ -274,7 +276,8 @@ class TebusController extends Controller
'tebus.jbg', 'tebus.jbg',
'tebus.ujrah', 'tebus.ujrah',
'tebus.onepercent', 'tebus.onepercent',
'gad.tagbaru' 'gad.tagbaru',
'tebus.diskaun_rebate'
) )
->join('gadai as gad', 'gad.norujukan', '=', 'tebus.norujukan') ->join('gadai as gad', 'gad.norujukan', '=', 'tebus.norujukan')
->where('tebus.t_tebus', '>=', $request->mula) ->where('tebus.t_tebus', '>=', $request->mula)
@@ -310,7 +313,8 @@ class TebusController extends Controller
"jbg"=>$tebusData['jbg'], "jbg"=>$tebusData['jbg'],
"ujrah"=>$trans['ujrah_rebet'], "ujrah"=>$trans['ujrah_rebet'],
"onepercent"=>$trans['onepercent_rebet'], "onepercent"=>$trans['onepercent_rebet'],
"tagbaru"=>$tebusData['tagbaru'] "tagbaru"=>$tebusData['tagbaru'],
"diskaun_rebate"=>$tebusData['diskaun_rebate'],
]); ]);
} }
@@ -1672,6 +1676,53 @@ class TebusController extends Controller
return response($rebet); return response($rebet);
} }
public function RebetPromoCalculation($kodcaw, Request $request)
{
$kodcaw = $request->kodcaw ?: $kodcaw;
if (!Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return response(0);
}
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
$rebet = Tebus::on($cawangan['mysql'])
->join('gadai as db2', 'tebus.norujukan', '=', 'db2.norujukan')
->where('tebus.t_tebus', '<=', $request->tarikh)
->where('db2.hargajualan', '>', '0')
->whereNotNull('db2.hargajualan')
->sum('diskaun_rebate');
return response($rebet);
}
public function PelarasanRebate($kodcaw, Request $request)
{
$kodcaw = $request->kodcaw ?: $kodcaw;
if (!Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return response(0);
}
$cawangan = Cawangan::on('mysql7')
->where('kodcaw', $kodcaw)
->first();
$tarikhLaporan = Carbon::parse($request->tarikh)->toDateString();
$tarikhTamatPelarasan = '2026-09-02';
$tarikhAkhir = $tarikhLaporan > $tarikhTamatPelarasan
? $tarikhTamatPelarasan
: $tarikhLaporan;
$rebet = Tebus::on($cawangan['mysql'])
->join('gadai as db2', 'tebus.norujukan', '=', 'db2.norujukan')
->whereDate('tebus.t_tebus', '>=', '2025-05-13')
->whereDate('tebus.t_tebus', '<=', $tarikhAkhir)
->where('db2.hargajualan', '>', 0)
->whereDate('db2.t_gadai', '>', '2023-05-21')
->sum('tebus.diskaun_rebate');
return response($rebet);
}
public function RebetCalculationLejer($kodcaw,Request $request) public function RebetCalculationLejer($kodcaw,Request $request)
{ {
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$request->kodcaw)->first();
@@ -1687,6 +1738,26 @@ class TebusController extends Controller
return response($rebet); return response($rebet);
} }
public function RebetPromoCalculationLejer($kodcaw, Request $request)
{
$kodcaw = $request->kodcaw ?: $kodcaw;
if (!Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return response(0);
}
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
$rebet = Tebus::on($cawangan['mysql'])
->join('gadai as db2', 'tebus.norujukan', '=', 'db2.norujukan')
->where('tebus.t_tebus', '>=', $request->mula)
->where('tebus.t_tebus', '<=', $request->hingga)
->where('db2.hargajualan', '>', '0')
->whereNotNull('db2.hargajualan')
->sum('diskaun_rebate');
return response($rebet);
}
public function getUpahLamaToday($kodcaw,$norujukan) public function getUpahLamaToday($kodcaw,$norujukan)
{ {
$current = Carbon::now(); $current = Carbon::now();
+45
View File
@@ -13,6 +13,7 @@ use App\Vault;
use Auth0\SDK\API\Management\Users; use Auth0\SDK\API\Management\Users;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Carbon\Carbon; use Carbon\Carbon;
use App\Services\StaffNotificationClient;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class TunaiController extends Controller class TunaiController extends Controller
{ {
@@ -476,10 +477,54 @@ class TunaiController extends Controller
$audit->updated_at = $current; $audit->updated_at = $current;
$audit->save(); $audit->save();
$this->notifyKhazanahAliranTunai($kodcaw, $request, $reqteller);
return response()->json($reqteller,200); return response()->json($reqteller,200);
} }
/**
* Inbox for PC/PPC at the branch. Failures must not block the request.
*
* @param string $kodcaw
* @param \Illuminate\Http\Request $request
* @param \App\RequestTeller $reqteller
* @return void
*/
protected function notifyKhazanahAliranTunai($kodcaw, Request $request, $reqteller)
{
try {
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
$namaCaw = $kodcaw;
if ($cawangan) {
$nama = trim((string) $cawangan->nama_cawangan);
if ($nama !== '') {
$namaCaw = $nama;
}
}
$modal = (float) $request->modal;
$serahan = (float) $request->serahan;
app(StaffNotificationClient::class)->notify([
'type' => 'aliran_tunai.pending_khazanah',
'cawangan' => $kodcaw,
'audience' => ['role' => 'pc'],
'data' => [
'request_no' => isset($reqteller->no) ? $reqteller->no : null,
'kodlaluan' => $request->kodlaluan,
'modal' => $modal,
'serahan' => $serahan,
'cawangan_nama' => $namaCaw,
],
]);
} catch (\Throwable $e) {
Log::warning('Aliran tunai khazanah notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'kodlaluan' => $request->kodlaluan,
]);
}
}
public function getTunaiSemalam($kodcaw) public function getTunaiSemalam($kodcaw)
{ {
$current = new Carbon(); $current = new Carbon();
+121 -72
View File
@@ -12,6 +12,7 @@ use App\GlDetail;
use App\imbangdugalama; use App\imbangdugalama;
use App\GlCode; use App\GlCode;
use App\Panjar; use App\Panjar;
use App\ModalOnline;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Carbon\Carbon; use Carbon\Carbon;
@@ -168,37 +169,64 @@ class VaultController extends Controller
return response()->json($updateVault, 200); return response()->json($updateVault, 200);
} }
public function updateVaultDeletePendahuluanSerahan($kodcaw, Request $request)
{
$current = Carbon::now();
$current = new Carbon();
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$vault=Vault::on($cawangan['mysql'])->where('kodcaw','=',$kodcaw)->where('tarikh','=',$current->toDateString())->orderby('tarikh','desc')->first(); public function updateVaultDeletePendahuluanSerahan($kodcaw, Request $request)
{
$current = new Carbon();
$bakiawal = $vault['bakiawal']; $cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
$keluar = $vault['keluar'];
$masuk = $vault['masuk'];
$bakiakhir = $vault['bakiakhir'];
$bakieod = $vault['bakieod'];
$pendahuluan = $vault['pendahuluan'];
$serahan = $vault['serahan'];
$totpanjaran = $vault['totpanjar'];
$glpend = GlDetail::on($cawangan['mysql'])->where('tarikh','=', $current->toDateString())->sum('pendahuluan'); if (!$cawangan) {
$glser = GlDetail::on($cawangan['mysql'])->where('tarikh','=', $current->toDateString())->where('teller','<>','ONLINE')->sum('serahan'); return response()->json(['message' => 'Cawangan tidak dijumpai'], 404);
}
$bakiakhir = $bakiawal - $keluar + $masuk + $glpend - $glser - $totpanjaran; $vault = Vault::on($cawangan['mysql'])
->where('kodcaw', '=', $kodcaw)
->where('tarikh', '=', $current->toDateString())
->first();
$updateVault=Vault::on($cawangan['mysql'])->where([['tarikh','=',$current->toDateString()],['kodcaw','=', $kodcaw]])->update(array( if (!$vault) {
'pendahuluan' => $glpend, return response()->json(['message' => 'Rekod vault hari ini tidak dijumpai'], 404);
'serahan' => $glser, }
'bakiakhir'=>$bakiakhir,
'bakieod'=>$bakiakhir
));
return response()->json($updateVault, 200); $bakiawal = $vault['bakiawal'];
} $keluar = $vault['keluar'];
$masuk = $vault['masuk'];
$totpanjaran = $vault['totpanjar'];
// Pendahuluan HQ (dari GL)
$glpend = GlDetail::on($cawangan['mysql'])
->where('tarikh', '=', $current->toDateString())
->sum('pendahuluan');
// Serahan (GL, exclude teller ONLINE)
$glser = GlDetail::on($cawangan['mysql'])
->where('tarikh', '=', $current->toDateString())
->where('teller', '<>', 'ONLINE')
->sum('serahan');
// Pendahuluan Online (had yang diluluskan hari ini)
$modalOnlineHad = ModalOnline::on($cawangan['mysql'])
->where('kodcaw', '=', $kodcaw)
->where('tarikh', '=', $current->toDateString())
->value('had') ?? 0;
// Gabungan pendahuluan HQ + pendahuluan online
$totalPendahuluan = $glpend + $modalOnlineHad;
$bakiakhir = $bakiawal - $keluar + $masuk + $totalPendahuluan - $glser - $totpanjaran;
$updateVault = Vault::on($cawangan['mysql'])
->where([['tarikh', '=', $current->toDateString()], ['kodcaw', '=', $kodcaw]])
->update([
'pendahuluan' => $totalPendahuluan,
'serahan' => $glser,
'bakiakhir' => $bakiakhir,
'bakieod' => $bakiakhir,
]);
return response()->json($updateVault, 200);
}
public function create($kodcaw,Request $request) public function create($kodcaw,Request $request)
{ {
@@ -400,12 +428,14 @@ class VaultController extends Controller
} }
$bakiakhirVault = $vault ? $vault['bakiakhir'] : 0;
if($total != 0) if($total != 0)
{ {
$totalbakiakhirtunai = $vault['bakiakhir'] + $total; $totalbakiakhirtunai = $bakiakhirVault + $total;
}else }else
{ {
$totalbakiakhirtunai = $vault['bakiakhir']; $totalbakiakhirtunai = $bakiakhirVault;
} }
return response()->json($totalbakiakhirtunai); return response()->json($totalbakiakhirtunai);
@@ -479,25 +509,24 @@ class VaultController extends Controller
} }
public function CalculationPendahuluanSerahan($kodcaw,Request $request) public function CalculationPendahuluanSerahan($kodcaw,Request $request)
{ {
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first(); $cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$serahanpkb = '1000/020'; $serahanpkb = '1000/020';
$serahanjmc = '1000/026'; $serahanjmc = '1000/026';
$pendahuluanpkb = '7000/102'; $pendahuluanpkb = '7000/102';
$pendahuluanjmc = '7000/106'; $pendahuluanjmc = '7000/106';
$serahankoperasi = '1000/090'; $serahankoperasi = '1000/090';
//Selepas Tawarruq //Selepas Tawarruq
$serahanpkbTawarruq = GlDetail::on($cawangan['mysql']) $serahanpkbTawarruq = GlDetail::on($cawangan['mysql'])
->where('ktacct','=',$serahanpkb) ->where('ktacct','=',$serahanpkb)
->where('tarikh','<=', $request->tarikh) ->where('tarikh','<=', $request->tarikh)
->where('tarikh','>','2021-03-24') ->where('tarikh','>','2021-03-24')
->sum('serahan'); ->sum('serahan');
//Sebelum Tawarruq
//Sebelum Tawarruq
if($kodcaw == 'KB2' || $kodcaw == 'WCY' || $kodcaw == '1STOP' || $kodcaw == 'ARM' || $kodcaw == 'APM' || $kodcaw == '010621' || $kodcaw == '010915' || $kodcaw == '020303') if($kodcaw == 'KB2' || $kodcaw == 'WCY' || $kodcaw == '1STOP' || $kodcaw == 'ARM' || $kodcaw == 'APM' || $kodcaw == '010621' || $kodcaw == '010915' || $kodcaw == '020303')
{ {
$serahanpkb = GlDetail::on($cawangan['mysql']) $serahanpkb = GlDetail::on($cawangan['mysql'])
@@ -544,47 +573,53 @@ class VaultController extends Controller
//Selepas Tawarruq //Selepas Tawarruq
$pendahuluanPKBtawarruq = GlDetail::on($cawangan['mysql']) $pendahuluanPKBtawarruq = GlDetail::on($cawangan['mysql'])
->where('ktacct','=',$pendahuluanpkb) ->where('ktacct','=',$pendahuluanpkb)
->where([['tarikh','<=', $request->tarikh],['tarikh','>','2021-03-24']]) ->where([['tarikh','<=', $request->tarikh],['tarikh','>','2021-03-24']])
->sum('pendahuluan'); ->sum('pendahuluan');
//Sebelum Tawarruq //Sebelum Tawarruq
$pendahuluanPKB = GlDetail::on($cawangan['mysql']) $pendahuluanPKB = GlDetail::on($cawangan['mysql'])
->where('ktacct','=',$pendahuluanpkb) ->where('ktacct','=',$pendahuluanpkb)
->where([['tarikh','<=', $request->tarikh],['tarikh','>','2013-12-31']]) ->where([['tarikh','<=', $request->tarikh],['tarikh','>','2013-12-31']])
->sum('amaun');
$pendahuluanJMC = GlDetail::on($cawangan['mysql'])
->where('ktacct','=','7000/106')
->where([['tarikh','<=', $request->tarikh],['tarikh','>','2013-12-31']])
->sum('amaun');
$pendahuluanpkblama = imbangdugalama::on($cawangan['mysql'])
->sum('pendahuluan_pkb');
$pendahuluanjmclama = imbangdugalama::on($cawangan['mysql'])
->sum('pendahuluan_jmc');
$total = $pendahuluanPKBtawarruq + $pendahuluanjmclama + $pendahuluanpkblama + $pendahuluanPKB + $pendahuluanJMC;
$pindahkekpkb = GlCode::on($cawangan['mysql'])
->where('glcode','=',$serahankoperasi)
->sum('amaun'); ->sum('amaun');
if($kodcaw == '011229') $pendahuluanJMC = GlDetail::on($cawangan['mysql'])
$totalserahan -= 0.10; ->where('ktacct','=','7000/106')
->where([['tarikh','<=', $request->tarikh],['tarikh','>','2013-12-31']])
->sum('amaun');
if($kodcaw == '011013') $pendahuluanpkblama = imbangdugalama::on($cawangan['mysql'])
$totalserahan += 641000; ->sum('pendahuluan_pkb');
$arraypend = ['pendahuluan' => $total,'serahan' => $totalserahan,'pindahkekpkb' => $pindahkekpkb]; $pendahuluanjmclama = imbangdugalama::on($cawangan['mysql'])
->sum('pendahuluan_jmc');
return response()->json($arraypend); //Pendahuluan Online (had)
} $modalOnlineHad = ModalOnline::on($cawangan['mysql'])
->where('kodcaw','=', $kodcaw)
->where('tarikh','=', $request->tarikh)
->value('had') ?? 0;
$total = $pendahuluanPKBtawarruq + $pendahuluanjmclama + $pendahuluanpkblama + $pendahuluanPKB + $pendahuluanJMC + $modalOnlineHad;
$pindahkekpkb = GlCode::on($cawangan['mysql'])
->where('glcode','=',$serahankoperasi)
->sum('amaun');
if($kodcaw == '011229')
$totalserahan -= 0.10;
if($kodcaw == '011013')
$totalserahan += 641000;
$arraypend = ['pendahuluan' => $total,'serahan' => $totalserahan,'pindahkekpkb' => $pindahkekpkb];
return response()->json($arraypend);
}
public function CalculationPendahuluanSerahanLejer($kodcaw,Request $request) public function CalculationPendahuluanSerahanLejer($kodcaw,Request $request)
@@ -730,5 +765,19 @@ class VaultController extends Controller
return response()->json($vault); return response()->json($vault);
} }
public function CalculationModalOnlineLejer($kodcaw, Request $request)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw','=',$kodcaw)->first();
$tarikh = $request->hingga;
$modal_online = ModalOnline::on($cawangan['mysql'])
->where('kodcaw', '=', $kodcaw)
->where('tarikh', '=', $tarikh)
->first();
return response()->json([
'had' => $modal_online ? floatval($modal_online->had) : 0,
]);
}
} }
@@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
/**
* Meta WhatsApp Cloud webhook: verify challenge + acknowledge events.
* Sufficient for production onboarding; delivery statuses are logged only.
*/
class WhatsappWebhookController extends Controller
{
/**
* GET /api/whatsapp/webhook
* Meta sends hub.mode, hub.verify_token, hub.challenge.
*/
public function verify(Request $request)
{
$mode = $request->query('hub_mode', $request->query('hub.mode'));
$token = $request->query('hub_verify_token', $request->query('hub.verify_token'));
$challenge = $request->query('hub_challenge', $request->query('hub.challenge'));
$expected = (string) config('whatsapp.verify_token');
if ($mode === 'subscribe' && $expected !== '' && hash_equals($expected, (string) $token)) {
Log::info('WhatsApp webhook verified');
return response((string) $challenge, 200)
->header('Content-Type', 'text/plain');
}
Log::warning('WhatsApp webhook verify failed', [
'mode' => $mode,
'token_match' => $expected !== '' && hash_equals($expected, (string) $token),
]);
return response('Forbidden', 403);
}
/**
* POST /api/whatsapp/webhook
* Acknowledge quickly; optionally check X-Hub-Signature-256 when app secret is set.
*/
public function handle(Request $request)
{
$raw = $request->getContent();
if (!$this->signatureOk($request, $raw)) {
Log::warning('WhatsApp webhook invalid signature');
return response('Invalid signature', 403);
}
$payload = json_decode($raw, true);
if (!is_array($payload)) {
return response('OK', 200);
}
try {
$this->logEvents($payload);
} catch (\Throwable $e) {
Log::error('WhatsApp webhook log failed: ' . $e->getMessage());
}
return response('OK', 200);
}
protected function signatureOk(Request $request, $raw)
{
$secret = trim((string) config('whatsapp.app_secret'));
if ($secret === '') {
return true;
}
$header = (string) $request->header('X-Hub-Signature-256', '');
if ($header === '' || strpos($header, 'sha256=') !== 0) {
return false;
}
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
return hash_equals($expected, $header);
}
protected function logEvents(array $payload)
{
$entries = $payload['entry'] ?? [];
if (!is_array($entries)) {
return;
}
foreach ($entries as $entry) {
$changes = $entry['changes'] ?? [];
if (!is_array($changes)) {
continue;
}
foreach ($changes as $change) {
$value = $change['value'] ?? [];
if (!is_array($value)) {
continue;
}
foreach ($value['statuses'] ?? [] as $status) {
Log::info('WhatsApp status', [
'id' => $status['id'] ?? null,
'status' => $status['status'] ?? null,
'recipient' => $status['recipient_id'] ?? null,
'errors' => $status['errors'] ?? null,
]);
}
foreach ($value['messages'] ?? [] as $message) {
Log::info('WhatsApp inbound', [
'from' => $message['from'] ?? null,
'type' => $message['type'] ?? null,
'id' => $message['id'] ?? null,
]);
}
}
}
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use Closure;
class LogViewerPassword
{
public function handle($request, Closure $next)
{
$passwordHash = config('logviewer.password');
if (!is_string($passwordHash) || $passwordHash === '') {
return response('Akses log belum dikonfigurasi.', 503)
->header('Cache-Control', 'no-store');
}
$provided = $request->getPassword();
if ($request->getUser() !== 'logviewer' || !is_string($provided) ||
!password_verify($provided, $passwordHash)) {
return response('Kata laluan diperlukan untuk mengakses log.', 401, [
'WWW-Authenticate' => 'Basic realm="Log Viewer", charset="UTF-8"',
'Cache-Control' => 'no-store',
]);
}
$response = $next($request);
$response->headers->set('Cache-Control', 'no-store');
return $response;
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
namespace App\Jobs;
use App\BlastWasap;
use App\Customer;
use App\Services\Whatsapp\WhatsappCloudClient;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class WhatsappPembiayaanOnlinePaid extends Job
{
public $kodcaw;
public $cawName;
public $nokp;
public $norujukan;
public $pinjaman;
public $namaBank;
public $noakaun;
public $tarikhBayaran;
public $noRujukanBayaran;
public function __construct(array $data)
{
$this->kodcaw = $data['kodcaw'] ?? null;
$this->cawName = $data['caw_name'] ?? null;
$this->nokp = $data['nokp'] ?? null;
$this->norujukan = $data['norujukan'] ?? null;
$this->pinjaman = $data['pinjaman'] ?? null;
$this->namaBank = $data['nama_bank'] ?? null;
$this->noakaun = $data['noakaun'] ?? null;
$this->tarikhBayaran = $data['tarikh_bayaran'] ?? null;
$this->noRujukanBayaran = $data['no_rujukan_bayaran'] ?? null;
}
public function handle()
{
$client = new WhatsappCloudClient();
$now = Carbon::now();
if (!$client->isConfigured()) {
Log::warning('WhatsApp Cloud skipped: token or phone_number_id missing', [
'norujukan' => $this->norujukan,
]);
return;
}
$customer = Customer::on('mysql7')
->where(function ($q) {
$q->where('kpbaru', $this->nokp)->orWhere('kplama', $this->nokp);
})
->first();
$storedPhone = $customer ? ($customer->telefon ?: $customer->telefon2) : null;
$to = $client->normalizeMsisdn($storedPhone);
if (!config('whatsapp.whatsapp_cloud_enabled')) {
$admin = $client->normalizeMsisdn(env('ADMIN_PHONE_NUMBER'));
if (!$admin) {
Log::warning('WhatsApp Cloud skipped: ADMIN_PHONE_NUMBER required when WHATSAPP_ENABLED is false', [
'norujukan' => $this->norujukan,
]);
$this->logBlast($now, $storedPhone, 'skipped: ADMIN_PHONE_NUMBER empty');
return;
}
$to = $admin;
}
if (!$to) {
Log::warning('WhatsApp Cloud skipped: no phone', [
'nokp' => $this->nokp,
'norujukan' => $this->norujukan,
]);
$this->logBlast($now, $storedPhone, 'skipped: no phone');
return;
}
$template = config('whatsapp.template', 'hello_world');
$lang = config('whatsapp.template_lang', 'en_US');
$bodyParams = $this->isHelloWorld($template) ? [] : $this->bodyParams();
try {
$response = $client->sendTemplate($to, $template, $lang, $bodyParams);
$this->logBlast($now, $to, $response->body());
} catch (\Throwable $e) {
Log::error('WhatsappPembiayaanOnlinePaid failed: ' . $e->getMessage(), [
'norujukan' => $this->norujukan,
]);
$this->logBlast($now, $to, 'exception: ' . $e->getMessage());
}
}
protected function isHelloWorld($template)
{
return strtolower((string) $template) === 'hello_world';
}
/**
* Matches Meta template pembiayaan_online_paid (ms):
* AR-RAHN KoPKB: Pembiayaan bagi No. SAG *{{1}}* sebanyak *RM{{2}}*
* telah berjaya dikreditkan ke akaun *{{3}}* berakhir *{{4}}* pada *{{5}}*.
* Rujukan: *{{6}}*
*
* {{1}} SAG, {{2}} amaun, {{3}} nama bank/akaun, {{4}} 4 digit akhir, {{5}} tarikh, {{6}} rujukan bayaran
*
* @return array<int,string>
*/
protected function bodyParams()
{
$bank = trim((string) $this->namaBank);
$tail = $this->accountTail($this->noakaun);
$tarikh = $this->tarikhBayaran
? Carbon::parse($this->tarikhBayaran)->format('d/m/Y')
: Carbon::now()->format('d/m/Y');
return [
$this->norujukan ?: '-',
number_format((float) $this->pinjaman, 2, '.', ''),
$bank !== '' ? $bank : '-',
$tail !== '' ? $tail : '-',
$tarikh,
$this->noRujukanBayaran ?: '-',
];
}
protected function accountTail($noakaun)
{
$digits = preg_replace('/\D+/', '', (string) $noakaun);
if ($digits === '') {
return '';
}
return substr($digits, -4);
}
protected function logBlast($now, $telefon, $status)
{
try {
$log = new BlastWasap();
$log->kodcaw = $this->kodcaw;
$log->nokp = $this->nokp;
$log->notelefon = $telefon;
$log->norujukan = $this->norujukan;
$log->status = is_string($status) ? $status : json_encode($status);
$log->created_at = $now;
$log->updated_at = $now;
$log->save();
} catch (\Throwable $e) {
Log::error('log_blast_wasap save failed: ' . $e->getMessage());
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class KadarRebet extends Model
{
protected $table = 'kadar_rebet';
protected $connection = 'mysql7';
public $timestamps = false;
protected $fillable = [
'pembiayaan_min',
'pembiayaan_max',
'kadarrebet',
];
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ModalOnline extends Model
{
protected $table = 'modal_online';
protected $fillable = [
'tarikh',
'kodcaw',
'had',
'digunakan',
'baki',
'tambah',
'kurang',
'ditutup',
'ditutup_oleh',
'ditutup_at',
];
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PembiayaanOnline extends Model
{
protected $table = 'pembiayaan_online';
protected $fillable = [
'norujukan',
'nokp',
'nopelanggan',
'nama_pelanggan',
'kodcaw',
'pinjaman',
'kodbank',
'nama_bank',
'noakaun',
'nama_akaun',
'bukti_akaun_file_id',
'borang_file_id',
'status',
'reject_reason',
'rejected_by',
'rejected_at',
'batch_id',
'bukti_file_id',
'no_rujukan_bayaran',
'tarikh_bayaran',
'paid_at',
'created_by',
'pc_submitted_by',
'pc_verified_by',
'pc_verified_at',
'ops_by',
'nota',
'blast_sent',
];
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class RequestModalOnline extends Model
{
protected $table = 'request_modal_online';
protected $connection = 'mysql7';
protected $fillable = [
'kodcaw',
'tarikh',
'amaun',
'dimohon_oleh',
'status',
'diluluskan_oleh',
'nota',
];
}
@@ -0,0 +1,918 @@
<?php
namespace App\Services\PembiayaanOnline;
use App\Cawangan;
use App\Feature;
use App\ModalOnline;
use App\RequestModalOnline;
use App\Services\StaffNotificationClient;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ModalOnlineService
{
/**
* @return array{ok:bool,status:int,payload:mixed,mysql?:string}
*/
public function resolveMysql($kodcaw)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
if (!$cawangan) {
return [
'ok' => false,
'status' => 404,
'payload' => ['message' => 'Cawangan tidak dijumpai'],
];
}
return [
'ok' => true,
'status' => 200,
'payload' => null,
'mysql' => $cawangan['mysql'],
];
}
protected function todayDate()
{
return Carbon::now()->toDateString();
}
protected function findPending($kodcaw)
{
return RequestModalOnline::on('mysql7')
->where('kodcaw', $kodcaw)
->where('status', 'PENDING')
->first();
}
protected function pendingPayload($pending)
{
return $pending ? $pending->toArray() : null;
}
protected function missingSnapshot($kodcaw, $tarikh, $pending = null)
{
return [
'tarikh' => $tarikh,
'kodcaw' => $kodcaw,
'had' => 0.00,
'digunakan' => 0.00,
'baki' => 0.00,
'tambah' => 0.00,
'kurang' => 0.00,
'ditutup' => 0,
'missing' => true,
'pending_request' => $this->pendingPayload($pending),
];
}
protected function recalcBaki($modal)
{
$modal->baki = round(
(float) $modal->had + (float) $modal->tambah - (float) $modal->kurang - (float) $modal->digunakan,
2
);
}
/**
* Get today's modal_online row. Does not create — PC must request had from Operasi.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function get($kodcaw)
{
$resolved = $this->resolveMysql($kodcaw);
if (!$resolved['ok']) {
return $resolved;
}
$mysql = $resolved['mysql'];
$tarikh = $this->todayDate();
$pending = $this->findPending($kodcaw);
$modal = ModalOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('tarikh', $tarikh)
->first();
if (!$modal) {
return [
'ok' => true,
'status' => 200,
'payload' => $this->missingSnapshot($kodcaw, $tarikh, $pending),
];
}
$payload = $modal->toArray();
$payload['missing'] = false;
$payload['pending_request'] = $this->pendingPayload($pending);
return [
'ok' => true,
'status' => 200,
'payload' => $payload,
];
}
/**
* @deprecated Use get(). Kept so existing callers do not auto-seed RM100k.
*/
public function getOrCreate($kodcaw)
{
return $this->get($kodcaw);
}
/**
* Reserve amount inside an existing branch DB connection (caller may wrap in transaction).
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function reserveAmountOnConnection($mysql, $kodcaw, $amaun, $tarikh = null)
{
$amaun = round((float) $amaun, 2);
if ($amaun <= 0) {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'Amaun tidak sah'],
];
}
$tarikh = $tarikh ?: Carbon::now()->toDateString();
$modal = ModalOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('tarikh', $tarikh)
->lockForUpdate()
->first();
if (!$modal) {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Modal online belum diwujudkan. Sila mohon had daripada Operasi terlebih dahulu.',
'missing' => true,
],
];
}
if ((int) ($modal->ditutup ?? 0) === 1) {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Modal online hari ini sudah ditutup (EOD)',
'modal_online' => $modal,
],
];
}
if ((float) $modal->baki < $amaun) {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Baki modal online tidak mencukupi',
'baki' => (float) $modal->baki,
'amaun' => $amaun,
],
];
}
$modal->digunakan = round((float) $modal->digunakan + $amaun, 2);
$modal->baki = round(
(float) $modal->had + (float) $modal->tambah - (float) $modal->kurang - (float) $modal->digunakan,
2
);
$modal->save();
return [
'ok' => true,
'status' => 200,
'payload' => $modal,
];
}
/**
* Reserve amount from today's online float (transactional).
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function reserve($kodcaw, $amaun)
{
$resolved = $this->resolveMysql($kodcaw);
if (!$resolved['ok']) {
return $resolved;
}
if (!Feature::enabled($kodcaw, Feature::PEMBIAYAAN_ONLINE)) {
return Feature::denied('Pembiayaan online tidak diaktifkan untuk cawangan ini');
}
$mysql = $resolved['mysql'];
$amaun = round((float) $amaun, 2);
if ($amaun <= 0) {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'Amaun tidak sah'],
];
}
try {
return DB::connection($mysql)->transaction(function () use ($mysql, $kodcaw, $amaun) {
return $this->reserveAmountOnConnection($mysql, $kodcaw, $amaun);
});
} catch (\Throwable $e) {
Log::error('ModalOnline reserve failed: ' . $e->getMessage());
return [
'ok' => false,
'status' => 500,
'payload' => ['message' => 'Gagal reserve modal online'],
];
}
}
/**
* Had is no longer auto-seeded. Returns how many branches already have a row today.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function initiate($zone = 'all')
{
$current = $this->todayDate();
if ($zone == 'all') {
$cawanganAll = Cawangan::on('mysql7')->get();
} else {
$cawanganAll = Cawangan::on('mysql7')->where('zone', $zone)->get();
}
$existing = 0;
$missing = 0;
foreach ($cawanganAll as $cawangan) {
try {
$exists = ModalOnline::on($cawangan['mysql'])
->where('kodcaw', $cawangan['kodcaw'])
->where('tarikh', $current)
->exists();
if ($exists) {
$existing++;
} else {
$missing++;
}
} catch (\Throwable $e) {
Log::warning('initiate modal_online skip ' . $cawangan['kodcaw'] . ': ' . $e->getMessage());
}
}
return [
'ok' => true,
'status' => 200,
'payload' => [
'tarikh' => $current,
'created' => 0,
'existing' => $existing,
'missing' => $missing,
'message' => 'Modal online tidak di-initiate automatik. PC perlu mohon had daripada Operasi.',
],
];
}
/**
* PC request opening had (or extra top-up) from Operasi.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function requestTopUp($kodcaw, $amaun, $dimohonOleh, $nota = null)
{
$resolved = $this->resolveMysql($kodcaw);
if (!$resolved['ok']) {
return $resolved;
}
$mysql = $resolved['mysql'];
if (!Feature::enabled($kodcaw, Feature::PEMBIAYAAN_ONLINE)) {
return Feature::denied('Pembiayaan online tidak diaktifkan untuk cawangan ini');
}
$amaun = round((float) $amaun, 2);
$dimohonOleh = trim((string) $dimohonOleh);
if ($amaun <= 0) {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'Amaun tidak sah'],
];
}
if ($dimohonOleh === '') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'dimohon_oleh diperlukan'],
];
}
$tarikh = $this->todayDate();
$pending = $this->findPending($kodcaw);
if ($pending) {
return [
'ok' => false,
'status' => 409,
'payload' => [
'message' => 'Masih ada permohonan modal online PENDING untuk cawangan ini',
'request' => $pending,
],
];
}
$modal = ModalOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('tarikh', $tarikh)
->first();
if ($modal && (int) ($modal->ditutup ?? 0) === 1) {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Modal online hari ini sudah ditutup (EOD). Tidak boleh mohon.',
'modal_online' => $modal,
],
];
}
$jenis = $modal ? 'TOPUP' : 'HAD';
$request = RequestModalOnline::on('mysql7')->create([
'kodcaw' => $kodcaw,
'tarikh' => $tarikh,
'amaun' => $amaun,
'dimohon_oleh' => $dimohonOleh,
'status' => 'PENDING',
'nota' => $nota,
]);
$this->notifyOperasiModalRequest($kodcaw, $request, $jenis);
return [
'ok' => true,
'status' => 201,
'payload' => [
'request' => $request,
'jenis' => $jenis,
'modal_online' => $modal ?: $this->missingSnapshot($kodcaw, $tarikh, $request),
],
];
}
/**
* Inbox for all operasi staff. Failures must not block the request.
*
* @param string $kodcaw
* @param \App\RequestModalOnline $request
* @param string $jenis
* @return void
*/
protected function notifyOperasiModalRequest($kodcaw, $request, $jenis)
{
try {
app(StaffNotificationClient::class)->notify([
'type' => 'modal_online.pending_operasi',
'cawangan' => $kodcaw,
'audience' => ['role' => 'operasi'],
'data' => [
'request_id' => $request->id,
'amaun' => (float) $request->amaun,
'dimohon_oleh' => $request->dimohon_oleh,
'jenis' => $jenis,
'nota' => $request->nota,
'cawangan_nama' => $this->cawanganNama($kodcaw),
],
]);
} catch (\Throwable $e) {
Log::warning('Operasi modal-online notification failed: '.$e->getMessage(), [
'kodcaw' => $kodcaw,
'request_id' => $request->id,
]);
}
}
/**
* @param string $kodcaw
* @return string
*/
protected function cawanganNama($kodcaw)
{
$cawangan = Cawangan::on('mysql7')->where('kodcaw', '=', $kodcaw)->first();
if (!$cawangan) {
return $kodcaw;
}
$nama = trim((string) $cawangan->nama_cawangan);
return $nama !== '' ? $nama : $kodcaw;
}
/**
* List top-up requests.
* For PC: pass kodcaw. For Operasi: omit kodcaw (all branches).
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function listTopUpRequests($kodcaw = null, $status = null, $tarikh = null)
{
$query = RequestModalOnline::on('mysql7')->newQuery();
if ($kodcaw) {
$query->where('kodcaw', $kodcaw);
}
if ($status) {
$query->where('status', $status);
}
if ($tarikh) {
$query->where('tarikh', $tarikh);
}
$rows = $query->orderBy('id', 'desc')->get();
$cawMap = [];
if (!$kodcaw) {
foreach (Cawangan::on('mysql7')->get() as $caw) {
$cawMap[$caw->kodcaw] = $caw->nama_cawangan;
}
}
$data = [];
foreach ($rows as $row) {
$item = $row->toArray();
if (!$kodcaw) {
$item['cawangan_nama'] = $cawMap[$row->kodcaw] ?? null;
}
$data[] = $item;
}
return [
'ok' => true,
'status' => 200,
'payload' => [
'bilangan' => count($data),
'data' => $data,
],
];
}
/**
* Operasi approve: first request of the day sets had; later requests add tambah.
* Optional $amaunOverride lets operasi lulus a different amount than dimohon.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function approveTopUp($requestId, $diluluskanOleh, $nota = null, $expectedKodcaw = null, $amaunOverride = null)
{
$diluluskanOleh = trim((string) $diluluskanOleh);
if ($diluluskanOleh === '') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'diluluskan_oleh diperlukan'],
];
}
$req = RequestModalOnline::on('mysql7')->where('id', $requestId)->first();
if (!$req) {
return [
'ok' => false,
'status' => 404,
'payload' => ['message' => 'Permohonan tidak dijumpai'],
];
}
if ($expectedKodcaw && $req->kodcaw !== $expectedKodcaw) {
return [
'ok' => false,
'status' => 404,
'payload' => ['message' => 'Permohonan tidak dijumpai'],
];
}
if ($req->status !== 'PENDING') {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Hanya permohonan PENDING boleh diluluskan',
'status' => $req->status,
],
];
}
$kodcaw = $req->kodcaw;
$resolved = $this->resolveMysql($kodcaw);
if (!$resolved['ok']) {
return $resolved;
}
$mysql = $resolved['mysql'];
$amaunDimohon = round((float) $req->amaun, 2);
$amaun = $amaunDimohon;
if ($amaunOverride !== null && $amaunOverride !== '') {
$amaun = round((float) $amaunOverride, 2);
if ($amaun <= 0) {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'Amaun diluluskan tidak sah'],
];
}
}
$tarikh = $this->todayDate();
try {
$applied = DB::connection($mysql)->transaction(function () use ($mysql, $kodcaw, $tarikh, $amaun) {
$modal = ModalOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('tarikh', $tarikh)
->lockForUpdate()
->first();
$jenis = 'TOPUP';
if (!$modal) {
$modal = ModalOnline::on($mysql)->create([
'tarikh' => $tarikh,
'kodcaw' => $kodcaw,
'had' => $amaun,
'digunakan' => 0.00,
'baki' => $amaun,
'tambah' => 0.00,
'kurang' => 0.00,
'ditutup' => 0,
]);
$modal = ModalOnline::on($mysql)
->where('id', $modal->id)
->lockForUpdate()
->first();
$jenis = 'HAD';
} else {
if ((int) ($modal->ditutup ?? 0) === 1) {
throw new \RuntimeException('MODAL_ONLINE_CLOSED');
}
$modal->tambah = round((float) $modal->tambah + $amaun, 2);
$this->recalcBaki($modal);
$modal->save();
}
return ['modal' => $modal, 'jenis' => $jenis];
});
$modal = $applied['modal'];
$jenis = $applied['jenis'];
$req->status = 'APPROVED';
$req->diluluskan_oleh = $diluluskanOleh;
$req->amaun = $amaun;
$notes = [];
if (abs($amaun - $amaunDimohon) >= 0.01) {
$notes[] = 'Amaun dimohon RM ' . number_format($amaunDimohon, 2)
. ', diluluskan RM ' . number_format($amaun, 2);
}
if ($nota !== null && trim((string) $nota) !== '') {
$notes[] = trim((string) $nota);
}
if ($notes) {
$existing = trim((string) $req->nota);
$req->nota = $existing === ''
? implode(' | ', $notes)
: $existing . ' | ' . implode(' | ', $notes);
}
$req->save();
return [
'ok' => true,
'status' => 200,
'payload' => [
'request' => $req->fresh(),
'jenis' => $jenis,
'modal_online' => $modal,
],
];
} catch (\Throwable $e) {
if ($e->getMessage() === 'MODAL_ONLINE_CLOSED') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'Modal online hari ini sudah ditutup (EOD). Tidak boleh lulus permohonan.'],
];
}
Log::error('approveTopUp failed: ' . $e->getMessage());
return [
'ok' => false,
'status' => 500,
'payload' => ['message' => 'Gagal lulus permohonan modal online'],
];
}
}
/**
* Operasi reject top-up request.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function rejectTopUp($requestId, $diluluskanOleh, $nota = null, $expectedKodcaw = null)
{
$diluluskanOleh = trim((string) $diluluskanOleh);
if ($diluluskanOleh === '') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'diluluskan_oleh diperlukan'],
];
}
$req = RequestModalOnline::on('mysql7')->where('id', $requestId)->first();
if (!$req) {
return [
'ok' => false,
'status' => 404,
'payload' => ['message' => 'Permohonan tidak dijumpai'],
];
}
if ($expectedKodcaw && $req->kodcaw !== $expectedKodcaw) {
return [
'ok' => false,
'status' => 404,
'payload' => ['message' => 'Permohonan tidak dijumpai'],
];
}
if ($req->status !== 'PENDING') {
return [
'ok' => false,
'status' => 422,
'payload' => [
'message' => 'Hanya permohonan PENDING boleh ditolak',
'status' => $req->status,
],
];
}
$req->status = 'REJECTED';
$req->diluluskan_oleh = $diluluskanOleh;
if ($nota !== null && trim((string) $nota) !== '') {
$req->nota = trim(
trim((string) $req->nota) === ''
? (string) $nota
: $req->nota . ' | ' . $nota
);
}
$req->save();
return [
'ok' => true,
'status' => 200,
'payload' => ['request' => $req],
];
}
/**
* EOD close for one branch: return unused baki via kurang, mark ditutup.
* Also auto-rejects PENDING top-up requests for the branch.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function close($kodcaw, $ditutupOleh)
{
$resolved = $this->resolveMysql($kodcaw);
if (!$resolved['ok']) {
return $resolved;
}
$mysql = $resolved['mysql'];
$ditutupOleh = trim((string) $ditutupOleh);
if ($ditutupOleh === '') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'ditutup_oleh diperlukan'],
];
}
$tarikh = Carbon::now()->toDateString();
$now = Carbon::now();
try {
$result = DB::connection($mysql)->transaction(function () use ($mysql, $kodcaw, $tarikh, $ditutupOleh, $now) {
$modal = ModalOnline::on($mysql)
->where('kodcaw', $kodcaw)
->where('tarikh', $tarikh)
->lockForUpdate()
->first();
if (!$modal) {
return [
'ok' => false,
'status' => 404,
'payload' => [
'message' => 'Tiada modal online hari ini untuk ditutup. Teller belum mohon / PC belum lulus had.',
'missing' => true,
],
];
}
if ((int) ($modal->ditutup ?? 0) === 1) {
return [
'ok' => false,
'status' => 409,
'payload' => [
'message' => 'Modal online sudah ditutup untuk hari ini',
'modal_online' => $modal,
],
];
}
$unused = round((float) $modal->baki, 2);
$modal->kurang = round((float) $modal->kurang + $unused, 2);
$modal->baki = round(
(float) $modal->had + (float) $modal->tambah - (float) $modal->kurang - (float) $modal->digunakan,
2
);
$modal->ditutup = 1;
$modal->ditutup_oleh = $ditutupOleh;
$modal->ditutup_at = $now;
$modal->save();
return [
'ok' => true,
'status' => 200,
'payload' => [
'modal_online' => $modal,
'dikembalikan' => $unused,
],
];
});
if (!$result['ok']) {
return $result;
}
$pendingRejected = RequestModalOnline::on('mysql7')
->where('kodcaw', $kodcaw)
->where('status', 'PENDING')
->get();
foreach ($pendingRejected as $req) {
$req->status = 'REJECTED';
$req->diluluskan_oleh = $ditutupOleh;
$tag = 'Auto reject: EOD close';
$req->nota = trim((string) $req->nota) === ''
? $tag
: trim($req->nota) . ' | ' . $tag;
$req->save();
}
$result['payload']['pending_topup_ditolak'] = $pendingRejected->count();
return $result;
} catch (\Throwable $e) {
Log::error('modal online close failed: ' . $e->getMessage());
return [
'ok' => false,
'status' => 500,
'payload' => ['message' => 'Gagal tutup modal online'],
];
}
}
/**
* Operasi: list today's modal_online across branches.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function listToday($zone = 'all', $tarikh = null)
{
$tarikh = $tarikh ?: Carbon::now()->toDateString();
if ($zone == 'all') {
$cawanganAll = Cawangan::on('mysql7')->get();
} else {
$cawanganAll = Cawangan::on('mysql7')->where('zone', $zone)->get();
}
$rows = [];
foreach ($cawanganAll as $cawangan) {
try {
$modal = ModalOnline::on($cawangan['mysql'])
->where('kodcaw', $cawangan['kodcaw'])
->where('tarikh', $tarikh)
->first();
$row = $modal ? $modal->toArray() : [
'tarikh' => $tarikh,
'kodcaw' => $cawangan['kodcaw'],
'had' => 0,
'digunakan' => 0,
'baki' => 0,
'tambah' => 0,
'kurang' => 0,
'ditutup' => 0,
'missing' => true,
];
$row['cawangan_nama'] = $cawangan->nama_cawangan;
$row['missing'] = empty($modal);
$rows[] = $row;
} catch (\Throwable $e) {
Log::warning('listToday modal_online skip ' . $cawangan['kodcaw'] . ': ' . $e->getMessage());
}
}
usort($rows, function ($a, $b) {
return strcmp((string) ($a['kodcaw'] ?? ''), (string) ($b['kodcaw'] ?? ''));
});
$open = 0;
$closed = 0;
$missing = 0;
foreach ($rows as $row) {
if (!empty($row['missing'])) {
$missing++;
} elseif ((int) ($row['ditutup'] ?? 0) === 1) {
$closed++;
} else {
$open++;
}
}
return [
'ok' => true,
'status' => 200,
'payload' => [
'tarikh' => $tarikh,
'bilangan' => count($rows),
'open' => $open,
'closed' => $closed,
'missing' => $missing,
'data' => $rows,
],
];
}
/**
* Operasi bulk EOD close for open branches today.
*
* @return array{ok:bool,status:int,payload:mixed}
*/
public function closeAll($ditutupOleh, $zone = 'all')
{
$ditutupOleh = trim((string) $ditutupOleh);
if ($ditutupOleh === '') {
return [
'ok' => false,
'status' => 422,
'payload' => ['message' => 'ditutup_oleh diperlukan'],
];
}
$list = $this->listToday($zone);
$closed = 0;
$skipped = 0;
$failed = [];
foreach ($list['payload']['data'] as $row) {
if (!empty($row['missing']) || (int) ($row['ditutup'] ?? 0) === 1) {
$skipped++;
continue;
}
$res = $this->close($row['kodcaw'], $ditutupOleh);
if ($res['ok']) {
$closed++;
} else {
$failed[] = [
'kodcaw' => $row['kodcaw'],
'message' => $res['payload']['message'] ?? 'gagal',
];
}
}
return [
'ok' => true,
'status' => 200,
'payload' => [
'closed' => $closed,
'skipped' => $skipped,
'failed' => $failed,
],
];
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -398,7 +398,7 @@ class OnePercentService
$t_gadai = Carbon::parse($gadai->t_gadai); $t_gadai = Carbon::parse($gadai->t_gadai);
$t_bayaran = Carbon::parse($trans->tarikh_bayaran); $t_bayaran = Carbon::parse($trans->tarikh_bayaran);
if ($t_gadai->month === $t_bayaran->month && $t_gadai->year === $t_bayaran->year) { if (($t_gadai->month == $t_bayaran->month) && ($t_gadai->year == $t_bayaran->year)) {
return false; return false;
}else{ }else{
return true; return true;
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class StaffNotificationClient
{
/**
* Ask kaunter to persist a staff inbox row. Never throws.
*
* @param array $payload
* @return void
*/
public function notify(array $payload)
{
$base = rtrim((string) config('kaunter.url'), '/');
$secret = (string) config('kaunter.notification_secret');
if ($base === '' || $secret === '') {
Log::info('Staff notification skipped: KAUNTER_URL or INTERNAL_NOTIFICATION_SECRET is empty');
return;
}
$timeout = (int) config('kaunter.notification_timeout', 3);
if ($timeout < 1) {
$timeout = 3;
}
try {
$response = Http::timeout($timeout)
->withHeaders([
'X-Internal-Token' => $secret,
'Accept' => 'application/json',
])
->asJson()
->post($base . '/internal/notifications', $payload);
if (!$response->successful()) {
Log::warning('Staff notification to kaunter failed', [
'status' => $response->status(),
'body' => $response->body(),
'type' => isset($payload['type']) ? $payload['type'] : null,
]);
}
} catch (\Throwable $e) {
Log::warning('Staff notification to kaunter errored: '.$e->getMessage(), [
'type' => isset($payload['type']) ? $payload['type'] : null,
]);
}
}
}
@@ -0,0 +1,100 @@
<?php
namespace App\Services\Whatsapp;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class WhatsappCloudClient
{
public function isConfigured()
{
return trim((string) config('whatsapp.token')) !== ''
&& trim((string) config('whatsapp.phone_number_id')) !== '';
}
/**
* Malaysia MSISDN for Graph API, e.g. 60193450165.
*
* @return string|null
*/
public function normalizeMsisdn($raw)
{
$digits = preg_replace('/\D+/', '', (string) $raw);
if ($digits === '') {
return null;
}
if (strpos($digits, '60') === 0) {
return $digits;
}
if (strpos($digits, '0') === 0) {
return '60' . substr($digits, 1);
}
return '60' . $digits;
}
/**
* @param string $to
* @param string $templateName
* @param string $lang
* @param array<int,string> $bodyParams
* @return \Illuminate\Http\Client\Response
*/
public function sendTemplate($to, $templateName, $lang, array $bodyParams = [])
{
$payload = [
'messaging_product' => 'whatsapp',
'to' => $to,
'type' => 'template',
'template' => [
'name' => $templateName,
'language' => ['code' => $lang],
],
];
if (count($bodyParams) > 0) {
$payload['template']['components'] = [[
'type' => 'body',
'parameters' => array_map(function ($text) {
return [
'type' => 'text',
'text' => $this->sanitizeParam($text),
];
}, array_values($bodyParams)),
]];
}
$version = config('whatsapp.api_version', 'v25.0');
$phoneId = config('whatsapp.phone_number_id');
$url = 'https://graph.facebook.com/' . $version . '/' . $phoneId . '/messages';
$response = Http::withToken(config('whatsapp.token'))
->acceptJson()
->asJson()
->post($url, $payload);
if (!$response->successful()) {
Log::error('WhatsApp Cloud send failed', [
'to' => $to,
'template' => $templateName,
'status' => $response->status(),
'body' => $response->json() ?: $response->body(),
]);
}
return $response;
}
protected function sanitizeParam($text)
{
$text = trim(preg_replace('/\s+/', ' ', (string) $text));
if ($text === '') {
return '-';
}
return mb_substr($text, 0, 200);
}
}
+56 -2
View File
@@ -2,8 +2,11 @@
namespace App\Support\v2; namespace App\Support\v2;
use App\Feature;
use App\KadarRebet;
use App\KadarUpah; use App\KadarUpah;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
class Calculation class Calculation
{ {
@@ -55,7 +58,7 @@ class Calculation
return $ujrah_sebenar; return $ujrah_sebenar;
} }
public function kiraanKeuntunganRebet(float $pembiayaan = 0.0,float $nilaimarhun = 0.0,float $kadarujrah = 0.0,float $margin = 0.0){ public function kiraanKeuntunganRebet(float $pembiayaan = 0.0,float $nilaimarhun = 0.0,float $kadarujrah = 0.0,float $margin = 0.0, $kodcaw = null){
$this->one_percent = $this->getKadarUpah($nilaimarhun); $this->one_percent = $this->getKadarUpah($nilaimarhun);
@@ -66,8 +69,59 @@ class Calculation
$ujrah_sebenar = (!empty($ujrah_sebenar)) ? $this->getRoundedMethod($ujrah_sebenar) : 0; $ujrah_sebenar = (!empty($ujrah_sebenar)) ? $this->getRoundedMethod($ujrah_sebenar) : 0;
$keuntungan = $onepercent + $ujrah_sebenar; $keuntungan = $onepercent + $ujrah_sebenar;
$annur = $this->kiraanDiskaunAnnur($kodcaw, $pembiayaan, $keuntungan);
return ['keuntungan' => $keuntungan,'onepercent' => $onepercent, 'ujrah' => $ujrah_sebenar]; return [
'keuntungan' => $keuntungan,
'onepercent' => $onepercent,
'ujrah' => $ujrah_sebenar,
'diskaun' => $annur['diskaun'],
'kadarrebet' => $annur['kadarrebet'],
];
}
/**
* Annur promotional discount. Other cawangan get 0 unless rebet_annur is enabled.
*/
public function kiraanDiskaunAnnur($kodcaw, float $pembiayaan = 0.0, float $keuntungan = 0.0)
{
$empty = ['kadarrebet' => 0, 'diskaun' => 0];
try {
if (!$kodcaw || !Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return $empty;
}
} catch (\Exception $e) {
return $empty;
}
$percent = $this->kiraanRebetAnnur($pembiayaan);
if ($percent <= 0 || $keuntungan <= 0) {
return ['kadarrebet' => $percent, 'diskaun' => 0];
}
return [
'kadarrebet' => $percent,
'diskaun' => $this->getRoundedMethod($keuntungan * ($percent / 100)),
];
}
private function kiraanRebetAnnur(float $pembiayaan = 0.0)
{
try {
if (!Schema::connection('mysql7')->hasTable('kadar_rebet')) {
return 0;
}
$kadarrebet = KadarRebet::on('mysql7')
->where('pembiayaan_min', '<=', $pembiayaan)
->where('pembiayaan_max', '>=', $pembiayaan)
->sum('kadarrebet');
return (float) $kadarrebet;
} catch (\Exception $e) {
return 0;
}
} }
public function kiraanKeuntunganSebenar(float $pembiayaan = 0.0,float $nilaimarhun = 0.0,float $kadarujrah = 0.0,float $margin = 0.0){ public function kiraanKeuntunganSebenar(float $pembiayaan = 0.0,float $nilaimarhun = 0.0,float $kadarujrah = 0.0,float $margin = 0.0){
+67 -43
View File
@@ -5,6 +5,7 @@ namespace App\Support\v2;
use App\Audit; use App\Audit;
use App\Cawangan; use App\Cawangan;
use App\Customer; use App\Customer;
use App\Feature;
use App\KadarUpah; use App\KadarUpah;
use App\Support\v2\Calculation; use App\Support\v2\Calculation;
use Carbon\Carbon; use Carbon\Carbon;
@@ -58,31 +59,34 @@ class GadaiV2
} }
} }
private function getKadarUpahAnggota($nokp,$total_nilai_marhun = 0,$groupid = 2) : Float{ private function getKadarUpahAnggota($nokp, $total_nilai_marhun = 0, $groupid = 2): ?float
//Log::info('DEBUG NOKP:' . $nokp); {
if($nokp){ if (!$nokp) {
$customer = Customer::on('mysql7') return null; // tiada nokp = tak boleh tentukan kadar
->select('tag_anggota')
->where(function($q) use ($nokp) {
$q->where('kpbaru', $nokp)
->orWhere('kplama', $nokp);
})
->first();
$customer = $customer ? $customer->toArray() : [];
if(isset($customer) && $customer['tag_anggota'] == 1){
return 0.16;
}else{
return KadarUpah::where('min', '<=', $total_nilai_marhun)
->where('max', '>=', $total_nilai_marhun)
->where('group_id', $groupid)
->value('kadarujrah');
}
}
return 0.0;
} }
$customer = Customer::on('mysql7')
->select('tag_anggota')
->where(function ($q) use ($nokp) {
$q->where('kpbaru', $nokp)
->orWhere('kplama', $nokp);
})
->first();
$customer = $customer ? $customer->toArray() : [];
if (isset($customer['tag_anggota']) && $customer['tag_anggota'] == 1) {
return 0.00; // ahli — kadar 0.00 memang sah
}
$kadar = KadarUpah::where('min', '<=', $total_nilai_marhun)
->where('max', '>=', $total_nilai_marhun)
->where('group_id', $groupid)
->value('kadarujrah');
return is_null($kadar) ? null : (float) $kadar; // tak jumpa row = null (block)
}
public function createMarhun($norujukan,HttpRequest $request){ public function createMarhun($norujukan,HttpRequest $request){
$current = Carbon::now(); $current = Carbon::now();
$current = new Carbon(); $current = new Carbon();
@@ -108,19 +112,18 @@ class GadaiV2
$total_nilai_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun'); $total_nilai_marhun = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('n_marhun');
$total_berat = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat'); $total_berat = Marhun::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->sum('berat');
$kadar_raw = $this->getKadarUpahAnggota($request->nokp, $total_nilai_marhun, $cawangan['group_id']); $kadar_raw = $this->getKadarUpahAnggota($request->nokp, $total_nilai_marhun, $cawangan['group_id']);
if (empty($kadar_raw) || $kadar_raw == 0) { if (is_null($kadar_raw)) {
Log::error('[BLOCKED] Kadar upah tidak dijumpai', [
Log::error('[BLOCKED] Kadar upah kosong / 0', [ 'nokp' => $request->nokp,
'nokp' => $request->nokp, 'total_nilai_marhun' => $total_nilai_marhun,
'total_nilai_marhun' => $total_nilai_marhun, 'group_id' => $cawangan['group_id'],
'group_id' => $cawangan['group_id'], ]);
]); throw new \Exception("Kadar upah tidak dijumpai, transaksi dibatalkan!");
}
throw new \Exception("Kadar upah kosong / 0, transaksi dibatalkan!");
}
// $kadar_raw = 0.00 (ahli) akan lepas ke sini dan diteruskan
// query kadar upah lama // query kadar upah lama
// $kadar_raw = KadarUpah::where('min','<=',$total_nilai_marhun)->where('max','>=',$total_nilai_marhun)->first(); // $kadar_raw = KadarUpah::where('min','<=',$total_nilai_marhun)->where('max','>=',$total_nilai_marhun)->first();
@@ -135,14 +138,13 @@ class GadaiV2
$upah12 = $onepercentservices->getRoundedMethod($upah1 * 12); $upah12 = $onepercentservices->getRoundedMethod($upah1 * 12);
$ujrah = $onepercentservices->getRoundedMethod($keuntungan['ujrah']); $ujrah = $onepercentservices->getRoundedMethod($keuntungan['ujrah']);
$onepercent = $onepercentservices->getRoundedMethod($keuntungan['onepercent']); $onepercent = $onepercentservices->getRoundedMethod($keuntungan['onepercent']);
$annur = $onepercentservices->kiraanDiskaunAnnur($request->kodcaw, $request->pinjaman, $keuntungan['keuntungan']);
$hargajualan = $request->pinjaman + $upah12; $hargajualan = $request->pinjaman + $upah12;
$akrual = $this->kiraupahTawarruqStokAuditUjrahOnePercent(0.5,0,$kadar_raw,$request->pinjaman,$total_nilai_marhun,$margin_pinjam); $akrual = $this->kiraupahTawarruqStokAuditUjrahOnePercent(0.5,0,$kadar_raw,$request->pinjaman,$total_nilai_marhun,$margin_pinjam);
$gadai = Gadai::on($cawangan['mysql']) $gadaiFields = [
->where('norujukan','=',$norujukan)
->update(array(
'nilaimarhun' => $total_nilai_marhun, 'nilaimarhun' => $total_nilai_marhun,
'berat'=> $total_berat, 'berat'=> $total_berat,
'pinjaman' => $request->pinjaman, 'pinjaman' => $request->pinjaman,
@@ -174,7 +176,23 @@ class GadaiV2
'tagbaru' => 1, 'tagbaru' => 1,
'tunggakanujrah' => $akrual['ujrah'], 'tunggakanujrah' => $akrual['ujrah'],
'tunggakanonepercent' => $akrual['keuntungan'], 'tunggakanonepercent' => $akrual['keuntungan'],
)); ];
if (Feature::enabled($request->kodcaw, Feature::REBET_ANNUR)) {
$requestedKadar = (float) $request->input('kadar_rebate', 0);
$requestedDiskaun = (float) $request->input('rebate', 0);
if ($requestedKadar > 0 || $requestedDiskaun > 0) {
$gadaiFields['kadarrebet'] = $requestedKadar;
$gadaiFields['diskaun'] = $requestedDiskaun;
} elseif (($request->transaction ?? '') === 'Daftar Gadai') {
$gadaiFields['kadarrebet'] = $annur['kadarrebet'];
$gadaiFields['diskaun'] = $annur['diskaun'];
}
}
$gadai = Gadai::on($cawangan['mysql'])
->where('norujukan','=',$norujukan)
->update($gadaiFields);
$new_data = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first(); $new_data = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
@@ -238,14 +256,15 @@ class GadaiV2
} }
$bilang_bln = $tempoh - $gadai_data['tempohbayar']; $bilang_bln = $tempoh - $gadai_data['tempohbayar'];
$upah = $onepercentservices->kiraanKeuntunganRebet($gadai_data['bakipjm'],$gadai_data['nilaimarhun'],$gadai_data['kadarupah'],$gadai_data['margin_semasa']); $upah = $onepercentservices->kiraanKeuntunganRebet($gadai_data['bakipjm'],$gadai_data['nilaimarhun'],$gadai_data['kadarupah'],$gadai_data['margin_semasa'], $this->kodcaw);
//$upahrebet_raw = $bilang_bln * $upah['keuntungan']; //$upahrebet_raw = $bilang_bln * $upah['keuntungan'];
$onepercent = $onepercentservices->getRoundedMethod($bilang_bln * $upah['onepercent']); $onepercent = $onepercentservices->getRoundedMethod($bilang_bln * $upah['onepercent']);
$ujrah = $onepercentservices->getRoundedMethod($bilang_bln * $upah['ujrah']); $ujrah = $onepercentservices->getRoundedMethod($bilang_bln * $upah['ujrah']);
$upahrebet_raw = $onepercent + $ujrah; $upahrebet_raw = $onepercent + $ujrah;
$upahrebet_raw = $onepercentservices->getRoundedMethod($upahrebet_raw); $upahrebet_raw = $onepercentservices->getRoundedMethod($upahrebet_raw);
$upahrebet_raw = $upahrebet_raw - ($upah['diskaun'] ?? 0);
return $upahrebet_raw; return $upahrebet_raw;
} }
@@ -280,7 +299,7 @@ class GadaiV2
$margin_semasa = ($gadai['bakipjm'] / $gadai['nilaimarhun']) * 100; $margin_semasa = ($gadai['bakipjm'] / $gadai['nilaimarhun']) * 100;
$keuntungan = $onepercent->kiraanKeuntunganRebet($gadai['bakipjm'],$gadai['nilaimarhun'],$gadai['kadarupah'],$gadai['margin_semasa']); $keuntungan = $onepercent->kiraanKeuntunganRebet($gadai['bakipjm'],$gadai['nilaimarhun'],$gadai['kadarupah'],$gadai['margin_semasa'], $kodcaw);
$ujrahasal = $onepercent->kiraanUjrahSebenar($gadai['nilaimarhun'],$gadai['kadarupah'],$gadai['percentpinj']); $ujrahasal = $onepercent->kiraanUjrahSebenar($gadai['nilaimarhun'],$gadai['kadarupah'],$gadai['percentpinj']);
$onepercentasal = $gadai['pinjaman'] * (1/100); $onepercentasal = $gadai['pinjaman'] * (1/100);
@@ -317,7 +336,7 @@ class GadaiV2
$keun = $onepercent->getRoundedMethod($onepercentasal * $bilang_bln); $keun = $onepercent->getRoundedMethod($onepercentasal * $bilang_bln);
$keuntungan_rebate = $onepercent->getRoundedMethod($keuntungan['onepercent'] * $bilang_bln); $keuntungan_rebate = $onepercent->getRoundedMethod($keuntungan['onepercent'] * $bilang_bln);
$transaction_upah = TransaksiKeuntungan::on('mysql7')->create([ $transactionUpahFields = [
'norujukan' => $norujukan, 'norujukan' => $norujukan,
'bayaran_keuntungan' => $upahsemasa, 'bayaran_keuntungan' => $upahsemasa,
'bayaran_pembiayaan' => $baki_bayaran, 'bayaran_pembiayaan' => $baki_bayaran,
@@ -331,7 +350,12 @@ class GadaiV2
'tempoh' => $gadai['tempoh'], 'tempoh' => $gadai['tempoh'],
'beza_tempoh' => $bilang_bln, 'beza_tempoh' => $bilang_bln,
'margin_semasa' => $margin_semasa, 'margin_semasa' => $margin_semasa,
]); ];
if (Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
$transactionUpahFields['kadardiskaun'] = $keuntungan['diskaun'] ?? 0;
}
$transaction_upah = TransaksiKeuntungan::on('mysql7')->create($transactionUpahFields);
$transaksi_gadai = TransaksiGadai::on('mysql7')->create([ $transaksi_gadai = TransaksiGadai::on('mysql7')->create([
'kodcaw' => $old_data['kodcaw'], 'kodcaw' => $old_data['kodcaw'],
+54 -10
View File
@@ -5,6 +5,7 @@ namespace App\Support\v2;
use App\Advansur; use App\Advansur;
use App\Audit; use App\Audit;
use App\Cawangan; use App\Cawangan;
use App\Feature;
use App\KadarUpah; use App\KadarUpah;
use App\Support\v2\Calculation; use App\Support\v2\Calculation;
use Carbon\Carbon; use Carbon\Carbon;
@@ -39,6 +40,32 @@ class Penebusan
$this->cawangan_info = $cawangan_info; $this->cawangan_info = $cawangan_info;
} }
private function diskaunAnnur($kodcaw, $gadai)
{
try {
if (!Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return 0.0;
}
} catch (\Exception $e) {
return 0.0;
}
return (float) ($gadai['diskaun'] ?? 0);
}
private function tebusAnnurFields($kodcaw, $diskaun)
{
try {
if (!Feature::enabled($kodcaw, Feature::REBET_ANNUR)) {
return [];
}
} catch (\Exception $e) {
return [];
}
return ['diskaun_rebate' => $diskaun];
}
public function tebusTawarruq($kodcaw,$norujukan){ public function tebusTawarruq($kodcaw,$norujukan){
$onepercentservices = new Calculation(); $onepercentservices = new Calculation();
@@ -53,7 +80,8 @@ class Penebusan
$jeniskeuntungan = ($gadai_data['tagbaru'] == 1) ? 'onepercent' : 'asal'; $jeniskeuntungan = ($gadai_data['tagbaru'] == 1) ? 'onepercent' : 'asal';
$rebet = $this->rebetselepastawarruq($kodcaw,$norujukan,$jeniskeuntungan); $rebet = $this->rebetselepastawarruq($kodcaw,$norujukan,$jeniskeuntungan);
$harga_tebus = $gadai_data['bakihargajualan'] - $rebet; $diskaun = $this->diskaunAnnur($kodcaw, $gadai_data);
$harga_tebus = $gadai_data['bakihargajualan'] - $rebet - $diskaun;
// $hargatebus = round($harga_tebus,PHP_ROUND_HALF_UP); // $hargatebus = round($harga_tebus,PHP_ROUND_HALF_UP);
// $hargatebus = bcdiv(round($harga_tebus,2),1,1); // $hargatebus = bcdiv(round($harga_tebus,2),1,1);
@@ -485,6 +513,8 @@ class Penebusan
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first(); $gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
$diskaun = $this->diskaunAnnur($kodcaw, $gadai);
//check duplicate //check duplicate
$data = Transaction::on('mysql7')->where('norujukan','=',$norujukan)->whereIn('trans_type', ['P', 'T', 'S'])->first(); $data = Transaction::on('mysql7')->where('norujukan','=',$norujukan)->whereIn('trans_type', ['P', 'T', 'S'])->first();
@@ -502,7 +532,6 @@ class Penebusan
$harga_tebus = $this->hargaTebusTawarruq($kodcaw,$norujukan,$request->jeniskeuntungan); $harga_tebus = $this->hargaTebusTawarruq($kodcaw,$norujukan,$request->jeniskeuntungan);
$upah_tebus = $this->kiraupahTawarruq($norujukan); $upah_tebus = $this->kiraupahTawarruq($norujukan);
$upah_rebet = $this->kiraUpahRebetSebenar($kodcaw,$norujukan); $upah_rebet = $this->kiraUpahRebetSebenar($kodcaw,$norujukan);
$upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm'];
if($request->jeniskeuntungan == 'onepercent'){ if($request->jeniskeuntungan == 'onepercent'){
$array_onepercent = $this->kiraupahUjrahOnePercent($kodcaw,$norujukan); $array_onepercent = $this->kiraupahUjrahOnePercent($kodcaw,$norujukan);
@@ -514,10 +543,15 @@ class Penebusan
$ujrah = $array_onepercent['ujrah']; $ujrah = $array_onepercent['ujrah'];
$onepercent = $array_onepercent['onepercent']; $onepercent = $array_onepercent['onepercent'];
} }
$harga_tebus['harga'] -= $diskaun;
$upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm'];
} }
$bayaran_keuntungan_bersih = max(0, $upah_rebet_sebenar);
$create_tebus = Tebus::on($cawangan['mysql'])->create([
$create_tebus = Tebus::on($cawangan['mysql'])->create(array_merge([
'kodcaw'=> $kodcaw, 'kodcaw'=> $kodcaw,
'norujukan'=> $norujukan, 'norujukan'=> $norujukan,
'nopelanggan' => $request->nopelanggan, 'nopelanggan' => $request->nopelanggan,
@@ -543,7 +577,7 @@ class Penebusan
'teller' => $request->teller, 'teller' => $request->teller,
'ujrah' => $ujrah_rebet, 'ujrah' => $ujrah_rebet,
'onepercent' => $onepercent_rebet, 'onepercent' => $onepercent_rebet,
]); ], $this->tebusAnnurFields($kodcaw, $diskaun)));
$create_inoutmar = InOutMar::on($cawangan['mysql'])->create([ $create_inoutmar = InOutMar::on($cawangan['mysql'])->create([
'tarikh' => $current->toDateString(), 'tarikh' => $current->toDateString(),
@@ -561,7 +595,7 @@ class Penebusan
$transaction_upah = TransaksiKeuntungan::on('mysql7')->create([ $transaction_upah = TransaksiKeuntungan::on('mysql7')->create([
'norujukan' => $norujukan, 'norujukan' => $norujukan,
'bayaran_keuntungan' => $upah_tebus, 'bayaran_keuntungan' => $bayaran_keuntungan_bersih,
'bayaran_pembiayaan' => $gadai['bakipjm'], 'bayaran_pembiayaan' => $gadai['bakipjm'],
'keuntungan_rebet' => $upah_rebet_sebenar, 'keuntungan_rebet' => $upah_rebet_sebenar,
'tarikh_bayaran'=> $current, 'tarikh_bayaran'=> $current,
@@ -651,6 +685,8 @@ class Penebusan
$gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first(); $gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->first();
$diskaun = $this->diskaunAnnur($kodcaw, $gadai);
//check duplicate //check duplicate
$data = Transaction::on('mysql7')->where('norujukan','=',$norujukan)->where('trans_type','=','AT')->first(); $data = Transaction::on('mysql7')->where('norujukan','=',$norujukan)->where('trans_type','=','AT')->first();
@@ -668,7 +704,6 @@ class Penebusan
$harga_tebus = $this->hargaTebusTawarruq($kodcaw,$norujukan,$request->jeniskeuntungan); $harga_tebus = $this->hargaTebusTawarruq($kodcaw,$norujukan,$request->jeniskeuntungan);
$upah_tebus = $this->kiraupahTawarruq($norujukan); $upah_tebus = $this->kiraupahTawarruq($norujukan);
$upah_rebet = $this->kiraUpahRebetSebenar($kodcaw,$norujukan); $upah_rebet = $this->kiraUpahRebetSebenar($kodcaw,$norujukan);
$upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm'];
if($request->jeniskeuntungan == 'onepercent'){ if($request->jeniskeuntungan == 'onepercent'){
$array_onepercent = $this->kiraupahUjrahOnePercent($kodcaw,$norujukan); $array_onepercent = $this->kiraupahUjrahOnePercent($kodcaw,$norujukan);
@@ -681,6 +716,9 @@ class Penebusan
$ujrah = $array_onepercent['ujrah']; $ujrah = $array_onepercent['ujrah'];
$onepercent = $array_onepercent['onepercent']; $onepercent = $array_onepercent['onepercent'];
} }
$harga_tebus['harga'] -= $diskaun;
$upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm'];
} }
$update_gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->update(array( $update_gadai = Gadai::on($cawangan['mysql'])->where('norujukan','=',$norujukan)->update(array(
@@ -690,7 +728,7 @@ class Penebusan
// $upah_rebet = $this->kiraUpahRebet($kodcaw,$norujukan); // $upah_rebet = $this->kiraUpahRebet($kodcaw,$norujukan);
$create_tebus = Tebus::on($cawangan['mysql'])->create([ $create_tebus = Tebus::on($cawangan['mysql'])->create(array_merge([
'kodcaw'=> $kodcaw, 'kodcaw'=> $kodcaw,
'norujukan'=> $norujukan, 'norujukan'=> $norujukan,
'nopelanggan' => $request->nopelanggan, 'nopelanggan' => $request->nopelanggan,
@@ -716,7 +754,7 @@ class Penebusan
'teller' => $request->teller, 'teller' => $request->teller,
'ujrah' => $ujrah_rebet, 'ujrah' => $ujrah_rebet,
'onepercent' => $onepercent_rebet, 'onepercent' => $onepercent_rebet,
]); ], $this->tebusAnnurFields($kodcaw, $diskaun)));
$create_inoutmar = InOutMar::on($cawangan['mysql'])->create([ $create_inoutmar = InOutMar::on($cawangan['mysql'])->create([
'tarikh' => $current->toDateString(), 'tarikh' => $current->toDateString(),
@@ -734,9 +772,11 @@ class Penebusan
$bayaran_pembiayaan = $request->totalbayaran - $request->bakiupah; $bayaran_pembiayaan = $request->totalbayaran - $request->bakiupah;
$bayaran_keuntungan_bersih = max(0, $upah_rebet_sebenar);
$transaction_upah = TransaksiKeuntungan::on('mysql7')->create([ $transaction_upah = TransaksiKeuntungan::on('mysql7')->create([
'norujukan' => $norujukan, 'norujukan' => $norujukan,
'bayaran_keuntungan' => $upah_tebus, 'bayaran_keuntungan' => $bayaran_keuntungan_bersih,
'bayaran_pembiayaan' => $bayaran_pembiayaan, 'bayaran_pembiayaan' => $bayaran_pembiayaan,
'keuntungan_rebet' => $upah_rebet_sebenar, 'keuntungan_rebet' => $upah_rebet_sebenar,
'tarikh_bayaran'=> $current, 'tarikh_bayaran'=> $current,
@@ -1131,12 +1171,16 @@ class Penebusan
$onepercent_rebet = $array_onepercent_rebet['onepercent'] - $upah_rebet['onepercent']; $onepercent_rebet = $array_onepercent_rebet['onepercent'] - $upah_rebet['onepercent'];
$ujrah_rebet = $array_onepercent_rebet['ujrah'] - $upah_rebet['ujrah']; $ujrah_rebet = $array_onepercent_rebet['ujrah'] - $upah_rebet['ujrah'];
$diskaun = $this->diskaunAnnur($kodcaw, $gadai);
$harga_tebus['harga'] -= $diskaun;
$upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm']; $upah_rebet_sebenar = $harga_tebus['harga'] - $gadai['bakipjm'];
$bayaran_keuntungan_bersih = max(0, $upah_rebet_sebenar);
try{ try{
TransaksiKeuntungan::on('mysql7')->create([ TransaksiKeuntungan::on('mysql7')->create([
'norujukan' => $norujukan, 'norujukan' => $norujukan,
'bayaran_keuntungan' => $upah_tebus, 'bayaran_keuntungan' => $bayaran_keuntungan_bersih,
'bayaran_pembiayaan' => $bayaran_pembiayaan, 'bayaran_pembiayaan' => $bayaran_pembiayaan,
'keuntungan_rebet' => $upah_rebet_sebenar, 'keuntungan_rebet' => $upah_rebet_sebenar,
'tarikh_bayaran'=> $current, 'tarikh_bayaran'=> $current,
+2 -1
View File
@@ -52,7 +52,8 @@ class Tebus extends Model
'pinjamanterkini', 'pinjamanterkini',
'tarikhbyrn', 'tarikhbyrn',
'ujrah', 'ujrah',
'onepercent' 'onepercent',
'diskaun_rebate'
]; ];
/** /**
+1 -1
View File
@@ -14,7 +14,7 @@ class TransaksiKeuntungan extends Model
* @var array * @var array
*/ */
protected $fillable = [ protected $fillable = [
'norujukan','bayaran_keuntungan','tarikh_bayaran','kodcaw','bayaran_pembiayaan','keuntungan_rebet','ujrah','ujrah_rebet','onepercent','onepercent_rebet','tempoh','beza_tempoh','margin_semasa','rounded' 'norujukan','bayaran_keuntungan','tarikh_bayaran','kodcaw','bayaran_pembiayaan','keuntungan_rebet','ujrah','ujrah_rebet','onepercent','onepercent_rebet','tempoh','beza_tempoh','margin_semasa','rounded','kadardiskaun'
]; ];
/** /**
+1
View File
@@ -0,0 +1 @@
/cache
+34 -3
View File
@@ -23,6 +23,33 @@ $app = new Laravel\Lumen\Application(
dirname(__DIR__) dirname(__DIR__)
); );
/*
| Laravel's storage_path() helper resolves app('path.storage'). Lumen does not
| register that binding by default; config files that call storage_path() at
| load time (e.g. config/logging.php) would fail without this.
*/
$app->instance('path.storage', $app->storagePath());
/*
| Laravel's Foundation helpers are loaded before Lumen's and win for names like
| response() and url(). Those helpers resolve Illuminate\Contracts\* from the
| container; Lumen only registers the "url" service, not the contracts. Map the
| contracts to Lumen's implementations so helpers work across the app.
*/
$app->singleton(
\Illuminate\Contracts\Routing\ResponseFactory::class,
function () {
return new \Laravel\Lumen\Http\ResponseFactory();
}
);
$app->singleton(
\Illuminate\Contracts\Routing\UrlGenerator::class,
function ($app) {
return $app->make('url');
}
);
$app->withFacades(); $app->withFacades();
$app->withEloquent(); $app->withEloquent();
@@ -60,11 +87,15 @@ $app->singleton(
*/ */
$app->configure('app'); $app->configure('app');
$app->configure('logging');
$app->configure('logviewer');
$app->configure('tinker'); $app->configure('tinker');
$app->configure('tennant_database'); $app->configure('tennant_database');
$app->configure('report'); $app->configure('report');
$app->configure('app_parameter'); $app->configure('app_parameter');
$app->configure('parameter'); $app->configure('parameter');
$app->configure('whatsapp');
$app->configure('kaunter');
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Register Middleware | Register Middleware
@@ -80,9 +111,9 @@ $app->configure('parameter');
// App\Http\Middleware\ExampleMiddleware::class // App\Http\Middleware\ExampleMiddleware::class
// ]); // ]);
// $app->routeMiddleware([ $app->routeMiddleware([
// 'auth' => App\Http\Middleware\Authenticate::class, 'logviewer.password' => App\Http\Middleware\LogViewerPassword::class,
// ]); ]);
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
-44
View File
@@ -1,44 +0,0 @@
{
"name": "laravel/lumen",
"description": "The Laravel Lumen Framework.",
"keywords": ["framework", "laravel", "lumen"],
"license": "MIT",
"type": "project",
"require": {
"php": "^7.2.5",
"auth0/auth0-php": "^7.4",
"laravel/lumen-framework": "^7.0",
"rap2hpoutre/laravel-log-viewer": "^1.7"
},
"require-dev": {
"fzaninotto/faker": "^1.9.1",
"mockery/mockery": "^1.3.1",
"phpunit/phpunit": "^8.5"
},
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
]
}
}
-7182
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,8 +1,9 @@
<?php <?php
return [ return [
'url' => env('APP_URL', url('/')), 'url' => env('APP_URL', 'http://localhost'),
'api_arrahnbid' => env('API_ARRAHNBID', 'http://lelong.test/api/'), 'api_arrahnbid' => env('API_ARRAHNBID', 'http://lelong.test/api/'),
'api_arrahnbid_annur' => env('API_ARRAHNBID_ANNUR', env('API_ARRAHNBID', 'http://lelong.test/api/')),
'api_v3' => env('API_V3', 'http://apiv3.localhost/api/') 'api_v3' => env('API_V3', 'http://apiv3.localhost/api/')
]; ];
+7
View File
@@ -0,0 +1,7 @@
<?php
return [
'url' => env('KAUNTER_URL', ''),
'notification_secret' => env('INTERNAL_NOTIFICATION_SECRET', ''),
'notification_timeout' => (int) env('KAUNTER_NOTIFICATION_TIMEOUT', 3),
];
+103
View File
@@ -0,0 +1,103 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog", "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => env('LOG_LEVEL', 'critical'),
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => SyslogUdpHandler::class,
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
],
],
'stderr' => [
'driver' => 'monolog',
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
];
+16
View File
@@ -0,0 +1,16 @@
<?php
return [
'password' => env('LOGVIEWER_PASSWORD'),
/*
|--------------------------------------------------------------------------
| Pattern and storage path settings
|--------------------------------------------------------------------------
|
| The env key for pattern and storage path with a default value
|
*/
'max_file_size' => 52428800, // size in Byte
'pattern' => env('LOGVIEWER_PATTERN', '*.log'),
'storage_path' => env('LOGVIEWER_STORAGE_PATH', storage_path('logs')),
];
+12
View File
@@ -0,0 +1,12 @@
<?php
return [
'whatsapp_cloud_enabled' => env('WHATSAPP_CLOUD_ENABLED', true),
'token' => env('WHATSAPP_TOKEN'),
'phone_number_id' => env('WHATSAPP_PHONE_NUMBER_ID'),
'api_version' => env('WHATSAPP_API_VERSION', 'v25.0'),
'template' => env('WHATSAPP_TEMPLATE', 'hello_world'),
'template_lang' => env('WHATSAPP_TEMPLATE_LANG', 'en_US'),
'verify_token' => env('WHATSAPP_VERIFY_TOKEN'),
'app_secret' => env('WHATSAPP_APP_SECRET'),
];
@@ -0,0 +1,53 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateModalOnlineTable extends Migration
{
public function getActiveDB()
{
$config_db = array_keys(config('database.connections'));
return array_filter($config_db, function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
});
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (!Schema::connection($dbase)->hasTable('modal_online')) {
Schema::connection($dbase)->create('modal_online', function (Blueprint $table) {
$table->bigIncrements('id');
$table->date('tarikh');
$table->string('kodcaw', 20);
$table->decimal('had', 12, 2)->default(100000.00);
$table->decimal('digunakan', 12, 2)->default(0.00);
$table->decimal('baki', 12, 2)->default(100000.00);
$table->decimal('tambah', 12, 2)->default(0.00);
$table->decimal('kurang', 12, 2)->default(0.00);
$table->timestamps();
$table->unique(['tarikh', 'kodcaw']);
});
}
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
Schema::connection($dbase)->dropIfExists('modal_online');
}
}
}
@@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreateBatchPembiayaanOnlineTable extends Migration
{
public function getActiveDB()
{
$config_db = array_keys(config('database.connections'));
return array_filter($config_db, function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
});
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (!Schema::connection($dbase)->hasTable('batch_pembiayaan_online')) {
Schema::connection($dbase)->create('batch_pembiayaan_online', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('batch_no', 50);
$table->string('kodcaw', 20);
$table->date('tarikh');
$table->string('slot', 20)->nullable()->comment('BATCH1 / BATCH2');
$table->integer('bilangan')->default(0);
$table->decimal('jumlah_keseluruhan', 12, 2)->default(0.00);
$table->string('status', 30)->default('DRAFT');
$table->string('pc_submitted_by', 100)->nullable();
$table->timestamp('submitted_at')->nullable();
$table->timestamps();
$table->index(['kodcaw', 'tarikh', 'status']);
});
}
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
Schema::connection($dbase)->dropIfExists('batch_pembiayaan_online');
}
}
}
@@ -0,0 +1,74 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class CreatePembiayaanOnlineTable extends Migration
{
public function getActiveDB()
{
$config_db = array_keys(config('database.connections'));
return array_filter($config_db, function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
});
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (!Schema::connection($dbase)->hasTable('pembiayaan_online')) {
Schema::connection($dbase)->create('pembiayaan_online', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('norujukan', 50);
$table->string('nokp', 20);
$table->string('nopelanggan', 50)->nullable();
$table->string('nama_pelanggan', 150)->nullable();
$table->string('kodcaw', 20);
$table->decimal('pinjaman', 12, 2);
$table->string('kodbank', 20)->nullable();
$table->string('nama_bank', 100)->nullable();
$table->string('noakaun', 50)->nullable();
$table->string('nama_akaun', 150)->nullable();
$table->unsignedBigInteger('bukti_akaun_file_id')->nullable();
$table->unsignedBigInteger('borang_file_id')->nullable();
$table->string('status', 30)->default('PENDING_PC');
$table->text('reject_reason')->nullable();
$table->string('rejected_by', 100)->nullable();
$table->timestamp('rejected_at')->nullable();
$table->unsignedBigInteger('batch_id')->nullable();
$table->unsignedBigInteger('bukti_file_id')->nullable();
$table->string('no_rujukan_bayaran', 100)->nullable();
$table->date('tarikh_bayaran')->nullable();
$table->timestamp('paid_at')->nullable();
$table->string('created_by', 100)->nullable();
$table->string('pc_submitted_by', 100)->nullable();
$table->string('ops_by', 100)->nullable();
$table->text('nota')->nullable();
$table->tinyInteger('blast_sent')->default(0);
$table->timestamps();
$table->index(['kodcaw', 'status']);
$table->index('norujukan');
$table->index('batch_id');
});
}
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
Schema::connection($dbase)->dropIfExists('pembiayaan_online');
}
}
}
@@ -0,0 +1,54 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class AddCaraTerimaToGadaiTable extends Migration
{
public function getActiveDB()
{
$config_db = array_keys(config('database.connections'));
return array_filter($config_db, function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
});
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (
Schema::connection($dbase)->hasTable('gadai')
&& !Schema::connection($dbase)->hasColumn('gadai', 'cara_terima')
) {
Schema::connection($dbase)->table('gadai', function (Blueprint $table) {
$table->string('cara_terima', 20)->nullable()->after('pinjaman');
});
}
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
if (
Schema::connection($dbase)->hasTable('gadai')
&& Schema::connection($dbase)->hasColumn('gadai', 'cara_terima')
) {
Schema::connection($dbase)->table('gadai', function (Blueprint $table) {
$table->dropColumn('cara_terima');
});
}
}
}
}
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRequestModalOnlineTable extends Migration
{
public function up()
{
if (!Schema::connection('mysql7')->hasTable('request_modal_online')) {
Schema::connection('mysql7')->create('request_modal_online', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('kodcaw', 20);
$table->date('tarikh');
$table->decimal('amaun', 12, 2);
$table->string('dimohon_oleh', 100)->nullable();
$table->string('status', 20)->default('PENDING');
$table->string('diluluskan_oleh', 100)->nullable();
$table->text('nota')->nullable();
$table->timestamps();
$table->index(['kodcaw', 'tarikh', 'status']);
});
}
}
public function down()
{
Schema::connection('mysql7')->dropIfExists('request_modal_online');
}
}
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateArrahnFilesTable extends Migration
{
/**
* Master DB (mysql7) — shared file store for CIT / permohonan / bukti pembiayaan online.
* Matches App\ARFiles usage: data (base64), filename, type, tarikhmasa; id used as file reference.
*
* @return void
*/
public function up()
{
if (!Schema::connection('mysql7')->hasTable('arrahn_files')) {
Schema::connection('mysql7')->create('arrahn_files', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('files_id')->nullable()->index();
$table->longText('data')->nullable();
$table->string('filename', 255)->nullable();
$table->string('type', 100)->nullable();
$table->string('tarikhmasa', 50)->nullable();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::connection('mysql7')->dropIfExists('arrahn_files');
}
}
@@ -0,0 +1,61 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class AddEodCloseColumnsToModalOnlineTable extends Migration
{
public function getActiveDB()
{
return array_filter(
array_keys(config('database.connections')),
function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
}
);
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
Schema::connection($dbase)->table('modal_online', function (Blueprint $table) {
$table->tinyInteger('ditutup')
->default(0)
->after('kurang');
$table->string('ditutup_oleh', 100)
->nullable()
->after('ditutup');
$table->timestamp('ditutup_at')
->nullable()
->after('ditutup_oleh');
});
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
Schema::connection($dbase)->table('modal_online', function (Blueprint $table) {
$table->dropColumn([
'ditutup',
'ditutup_oleh',
'ditutup_at',
]);
});
}
}
}
@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddGambarMykadToCustomerTable extends Migration
{
/**
* Master DB (mysql7) — stores arrahn_files.id for the MyKad photo.
*
* @return void
*/
public function up()
{
if (
Schema::connection('mysql7')->hasTable('customer')
&& !Schema::connection('mysql7')->hasColumn('customer', 'gambar_mykad')
) {
Schema::connection('mysql7')->table('customer', function (Blueprint $table) {
if (Schema::connection('mysql7')->hasColumn('customer', 'tag_anggota')) {
$table->unsignedBigInteger('gambar_mykad')->nullable()->after('tag_anggota');
} else {
$table->unsignedBigInteger('gambar_mykad')->nullable();
}
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
if (
Schema::connection('mysql7')->hasTable('customer')
&& Schema::connection('mysql7')->hasColumn('customer', 'gambar_mykad')
) {
Schema::connection('mysql7')->table('customer', function (Blueprint $table) {
$table->dropColumn('gambar_mykad');
});
}
}
}
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCawanganFeatureTable extends Migration
{
/**
* Master DB (mysql7) — per-cawangan feature flags.
*
* Missing row = off. Enable a pilot after migrate:
*
* INSERT INTO cawangan_feature (kodcaw, feature, enabled, created_at, updated_at)
* VALUES ('X0301', 'pembiayaan_online', 1, NOW(), NOW())
* ON DUPLICATE KEY UPDATE enabled = 1;
*/
public function up()
{
if (!Schema::connection('mysql7')->hasTable('cawangan_feature')) {
Schema::connection('mysql7')->create('cawangan_feature', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('kodcaw', 20);
$table->string('feature', 64);
$table->tinyInteger('enabled')->default(0);
$table->timestamps();
$table->unique(['kodcaw', 'feature'], 'uniq_cawangan_feature');
});
}
}
public function down()
{
Schema::connection('mysql7')->dropIfExists('cawangan_feature');
}
}
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateKadarRebetTable extends Migration
{
/**
* Master DB (mysql7) — Annur rebet bands by pembiayaan range.
* Empty table + feature flag off = other cawangan unchanged.
*/
public function up()
{
if (Schema::connection('mysql7')->hasTable('kadar_rebet')) {
return;
}
Schema::connection('mysql7')->create('kadar_rebet', function (Blueprint $table) {
$table->increments('id');
$table->decimal('pembiayaan_min', 12, 2)->default(0);
$table->decimal('pembiayaan_max', 12, 2)->default(0);
$table->decimal('kadarrebet', 8, 4)->default(0);
});
}
public function down()
{
Schema::connection('mysql7')->dropIfExists('kadar_rebet');
}
}
@@ -0,0 +1,102 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class AddAnnurDiskaunColumns extends Migration
{
public function getActiveDB()
{
$config_db = array_keys(config('database.connections'));
return array_filter($config_db, function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
});
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (Schema::connection($dbase)->hasTable('gadai')) {
$addKadar = !Schema::connection($dbase)->hasColumn('gadai', 'kadarrebet');
$addDiskaun = !Schema::connection($dbase)->hasColumn('gadai', 'diskaun');
if ($addKadar || $addDiskaun) {
Schema::connection($dbase)->table('gadai', function (Blueprint $table) use ($addKadar, $addDiskaun) {
if ($addKadar) {
$table->decimal('kadarrebet', 8, 4)->nullable()->default(0);
}
if ($addDiskaun) {
$table->decimal('diskaun', 12, 2)->nullable()->default(0);
}
});
}
}
if (
Schema::connection($dbase)->hasTable('tebus')
&& !Schema::connection($dbase)->hasColumn('tebus', 'diskaun_rebate')
) {
Schema::connection($dbase)->table('tebus', function (Blueprint $table) {
$table->decimal('diskaun_rebate', 12, 2)->nullable()->default(0);
});
}
}
if (
Schema::connection('mysql7')->hasTable('transaksi_keuntungan')
&& !Schema::connection('mysql7')->hasColumn('transaksi_keuntungan', 'kadardiskaun')
) {
Schema::connection('mysql7')->table('transaksi_keuntungan', function (Blueprint $table) {
$table->decimal('kadardiskaun', 12, 2)->nullable()->default(0);
});
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
if (Schema::connection($dbase)->hasTable('gadai')) {
Schema::connection($dbase)->table('gadai', function (Blueprint $table) use ($dbase) {
$drops = [];
if (Schema::connection($dbase)->hasColumn('gadai', 'kadarrebet')) {
$drops[] = 'kadarrebet';
}
if (Schema::connection($dbase)->hasColumn('gadai', 'diskaun')) {
$drops[] = 'diskaun';
}
if ($drops) {
$table->dropColumn($drops);
}
});
}
if (
Schema::connection($dbase)->hasTable('tebus')
&& Schema::connection($dbase)->hasColumn('tebus', 'diskaun_rebate')
) {
Schema::connection($dbase)->table('tebus', function (Blueprint $table) {
$table->dropColumn('diskaun_rebate');
});
}
}
if (
Schema::connection('mysql7')->hasTable('transaksi_keuntungan')
&& Schema::connection('mysql7')->hasColumn('transaksi_keuntungan', 'kadardiskaun')
) {
Schema::connection('mysql7')->table('transaksi_keuntungan', function (Blueprint $table) {
$table->dropColumn('kadardiskaun');
});
}
}
}
@@ -0,0 +1,74 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class AddPcVerifiedToPembiayaanOnlineTable extends Migration
{
public function getActiveDB()
{
return array_filter(
array_keys(config('database.connections')),
function ($connection) {
try {
if ($connection === 'mysql7') {
return false;
}
DB::connection($connection)->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
}
);
}
public function up()
{
foreach ($this->getActiveDB() as $dbase) {
if (!Schema::connection($dbase)->hasTable('pembiayaan_online')) {
continue;
}
Schema::connection($dbase)->table('pembiayaan_online', function (Blueprint $table) use ($dbase) {
if (!Schema::connection($dbase)->hasColumn('pembiayaan_online', 'pc_verified_by')) {
$table->string('pc_verified_by', 100)
->nullable()
->after('pc_submitted_by');
}
if (!Schema::connection($dbase)->hasColumn('pembiayaan_online', 'pc_verified_at')) {
$table->timestamp('pc_verified_at')
->nullable()
->after('pc_verified_by');
}
});
}
}
public function down()
{
foreach ($this->getActiveDB() as $dbase) {
if (!Schema::connection($dbase)->hasTable('pembiayaan_online')) {
continue;
}
Schema::connection($dbase)->table('pembiayaan_online', function (Blueprint $table) use ($dbase) {
$cols = [];
if (Schema::connection($dbase)->hasColumn('pembiayaan_online', 'pc_verified_at')) {
$cols[] = 'pc_verified_at';
}
if (Schema::connection($dbase)->hasColumn('pembiayaan_online', 'pc_verified_by')) {
$cols[] = 'pc_verified_by';
}
if (!empty($cols)) {
$table->dropColumn($cols);
}
});
}
}
}
+77
View File
@@ -0,0 +1,77 @@
# Staging stack — isolated from local docker-compose.yml (8080 / 3306)
# and from other apps on the server (unique project name + host ports).
#
# Local:
# docker compose --env-file .env.staging -f docker-compose.staging.yml up -d --build
#
# CI (image already in registry):
# APP_IMAGE=git.koppkb.com/KoPKB/api_arrahn IMAGE_TAG=<sha> \
# docker compose --env-file .env.staging -f docker-compose.staging.yml up -d --no-build --force-recreate app web
#
# Host nginx reverse-proxies the public domain to 127.0.0.1:${HTTP_PORT}.
# See docker/nginx/host-proxy.example.conf
#
# If 8090 or 3308 is already taken, set HTTP_PORT / MYSQL_PUBLISH_PORT in .env.staging.
name: api_arrahn_staging
services:
app:
image: ${APP_IMAGE:-api_arrahn}:${IMAGE_TAG:-staging}
build:
context: .
dockerfile: docker/php/Dockerfile
target: staging
container_name: api_arrahn_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: api_arrahn_staging_web
ports:
- "${HTTP_PORT:-8090}:80"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
restart: unless-stopped
mysql:
image: mysql:8.0
container_name: api_arrahn_staging_mysql
command: --default-authentication-plugin=mysql_native_password
ports:
- "127.0.0.1:${MYSQL_PUBLISH_PORT:-3308}:3306"
environment:
MYSQL_DATABASE: erahn_xtra
MYSQL_USER: xtrauser
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: root
volumes:
- mysql_staging_data:/var/lib/mysql
- ./docker/mysql/init:/docker-entrypoint-initdb.d:ro
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:
+33
View File
@@ -0,0 +1,33 @@
services:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
target: development
working_dir: /var/www/html
ports:
- "8080:8000"
volumes:
- ./:/var/www/html:cached
environment:
APP_ENV: local
APP_DEBUG: "true"
depends_on:
- mysql
command: ["php", "-S", "0.0.0.0:8000", "-t", "public"]
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: erahn_xtra
MYSQL_USER: xtrauser
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: root
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
@@ -0,0 +1,7 @@
CREATE DATABASE IF NOT EXISTS master_erahn;
CREATE DATABASE IF NOT EXISTS kaunter_erahn;
GRANT ALL PRIVILEGES ON master_erahn.* TO 'xtrauser'@'%';
GRANT ALL PRIVILEGES ON kaunter_erahn.* TO 'xtrauser'@'%';
GRANT ALL PRIVILEGES ON erahn_xtra.* TO 'xtrauser'@'%';
FLUSH PRIVILEGES;
+37
View File
@@ -0,0 +1,37 @@
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php index.html;
charset utf-8;
client_max_body_size 50M;
location / {
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;
}
}
+51
View File
@@ -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 \
&& 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
+30
View File
@@ -0,0 +1,30 @@
#!/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
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
chown -R www-data:www-data storage bootstrap/cache
exec docker-php-entrypoint "$@"
+4
View File
@@ -0,0 +1,4 @@
memory_limit=512M
upload_max_filesize=50M
post_max_size=50M
max_execution_time=120
+3
View File
@@ -0,0 +1,3 @@
[www]
; Pass container env vars (from compose env_file) through to PHP workers.
clear_env = no
-257
View File
@@ -1,257 +0,0 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://api-arrahn.test
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION_36=mysql
DB_HOST_36=127.0.0.1
DB_PORT_36=3306
DB_DATABASE_36=arrahn_kb2
DB_USERNAME_36=root
DB_PASSWORD_36=
DB_CONNECTION_2=mysql
DB_HOST_2=127.0.0.1
DB_PORT_2=3306
DB_DATABASE_2=arrahn_wcy
DB_USERNAME_2=root
DB_PASSWORD_2=
DB_CONNECTION_3=mysql
DB_HOST_3=127.0.0.1
DB_PORT_3=3306
DB_DATABASE_3=arrahn_1stop
DB_USERNAME_3=root
DB_PASSWORD_3=
DB_CONNECTION_4=mysql
DB_HOST_4=127.0.0.1
DB_PORT_4=3306
DB_DATABASE_4=arrahn_jlwt
DB_USERNAME_4=root
DB_PASSWORD_4=
DB_CONNECTION_5=mysql
DB_HOST_5=127.0.0.1
DB_PORT_5=3306
DB_DATABASE_5=arrahn_wb
DB_USERNAME_5=root
DB_PASSWORD_5=
DB_CONNECTION_6=mysql
DB_HOST_6=127.0.0.1
DB_PORT_6=3306
DB_DATABASE_6=arrahn_kls
DB_USERNAME_6=root
DB_PASSWORD_6=
DB_CONNECTION_7=mysql
DB_HOST_7=127.0.0.1
DB_PORT_7=3306
DB_DATABASE_7=online_arrahn
DB_USERNAME_7=root
DB_PASSWORD_7=
DB_CONNECTION_8=mysql
DB_HOST_8=127.0.0.1
DB_PORT_8=3306
DB_DATABASE_8=arrahn_pp
DB_USERNAME_8=root
DB_PASSWORD_8=
DB_CONNECTION_9=mysql
DB_HOST_9=127.0.0.1
DB_PORT_9=3306
DB_DATABASE_9=arrahn_mace
DB_USERNAME_9=root
DB_PASSWORD_9=
DB_CONNECTION_10=mysql
DB_HOST_10=127.0.0.1
DB_PORT_10=3306
DB_DATABASE_10=arrahn_pm $
DB_USERNAME_10=root
DB_PASSWORD_10=
DB_CONNECTION_11=mysql
DB_HOST_11=127.0.0.1
DB_PORT_11=3306
DB_DATABASE_11=arrahn_gm
DB_USERNAME_11=root
DB_PASSWORD_11=
DB_CONNECTION_12=mysql
DB_HOST_12=127.0.0.1
DB_PORT_12=3306
DB_DATABASE_12=arrahn_gcl
DB_USERNAME_12=root
DB_PASSWORD_12=
DB_CONNECTION_13=mysql
DB_HOST_13=127.0.0.1
DB_PORT_13=3306
DB_DATABASE_13=arrahn_jli
DB_USERNAME_13=root
DB_PASSWORD_13=
DB_CONNECTION_14=mysql
DB_HOST_14=127.0.0.1
DB_PORT_14=3306
DB_DATABASE_14=arrahn_rp
DB_USERNAME_14=root
DB_PASSWORD_14=
DB_CONNECTION_15=mysql
DB_HOST_15=127.0.0.1
DB_PORT_15=3306
DB_DATABASE_15=arrahn_ktn
DB_USERNAME_15=root
DB_PASSWORD_15=
DB_CONNECTION_16=mysql
DB_HOST_16=127.0.0.1
DB_PORT_16=3306
DB_DATABASE_16=arrahn_pg
DB_USERNAME_16=root
DB_PASSWORD_16=
DB_CONNECTION_17=mysql
DB_HOST_17=127.0.0.1
DB_PORT_17=3306
DB_DATABASE_17=arrahn_skudai
DB_USERNAME_17=root
DB_PASSWORD_17=
DB_CONNECTION_18=mysql
DB_HOST_18=127.0.0.1
DB_PORT_18=3306
DB_DATABASE_18=arrahn_pttn
DB_USERNAME_18=root
DB_PASSWORD_18=
DB_CONNECTION_19=mysql
DB_HOST_19=127.0.0.1
DB_PORT_19=3306
DB_DATABASE_19=arrahn_btg
DB_USERNAME_19=root
DB_PASSWORD_19=
DB_CONNECTION_20=mysql
DB_HOST_20=127.0.0.1
DB_PORT_20=3306
DB_DATABASE_20=arrahn_raub
DB_USERNAME_20=root
DB_PASSWORD_20=
DB_CONNECTION_21=mysql
DB_HOST_21=127.0.0.1
DB_PORT_21=3306
DB_DATABASE_21=arrahn_kgr
DB_USERNAME_21=root
DB_PASSWORD_21=
DB_CONNECTION_22=mysql
DB_HOST_22=127.0.0.1
DB_PORT_22=3306
DB_DATABASE_22=arrahn_sdk
DB_USERNAME_22=root
DB_PASSWORD_22=
DB_CONNECTION_23=mysql
DB_HOST_23=127.0.0.1
DB_PORT_23=3306
DB_DATABASE_23=arrahn_sp
DB_USERNAME_23=root
DB_PASSWORD_23=
DB_CONNECTION_24=mysql
DB_HOST_24=127.0.0.1
DB_PORT_24=3306
DB_DATABASE_24=arrahn_twu
DB_USERNAME_24=root
DB_PASSWORD_24=
DB_CONNECTION_25=mysql
DB_HOST_25=127.0.0.1
DB_PORT_25=3306
DB_DATABASE_25=arrahn_api2
DB_USERNAME_25=root
DB_PASSWORD_25=
DB_CONNECTION_26=mysql
DB_HOST_26=127.0.0.1
DB_PORT_26=3306
DB_DATABASE_26=arrahn_kjg
DB_USERNAME_26=root
DB_PASSWORD_26=
DB_CONNECTION_27=mysql
DB_HOST_27=127.0.0.1
DB_PORT_27=3306
DB_DATABASE_27=arrahn_rl
DB_USERNAME_27=root
DB_PASSWORD_27=
DB_CONNECTION_28=mysql
DB_HOST_28=127.0.0.1
DB_PORT_28=3306
DB_DATABASE_28=arrahn_bfort
DB_USERNAME_28=root
DB_PASSWORD_28=
DB_CONNECTION_29=mysql
DB_HOST_29=127.0.0.1
DB_PORT_29=3306
DB_DATABASE_29=arrahn_kbelud
DB_USERNAME_29=root
DB_PASSWORD_29=
DB_CONNECTION_30=mysql
DB_HOST_30=127.0.0.1
DB_PORT_30=3306
DB_DATABASE_30=arrahn_tuaran
DB_USERNAME_30=root
DB_PASSWORD_30=
DB_CONNECTION_31=mysql
DB_HOST_31=127.0.0.1
DB_PORT_31=3306
DB_DATABASE_31=arrahn_kngau
DB_USERNAME_31=root
DB_PASSWORD_31=
DB_CONNECTION_32=mysql
DB_HOST_32=127.0.0.1
DB_PORT_32=3306
DB_DATABASE_32=arrahn_tm
DB_USERNAME_32=root
DB_PASSWORD_32=
DB_CONNECTION_33=mysql
DB_HOST_33=127.0.0.1
DB_PORT_33=3306
DB_DATABASE_33=arrahn_tmlh
DB_USERNAME_33=root
DB_PASSWORD_33=
DB_CONNECTION_34=mysql
DB_HOST_34=127.0.0.1
DB_PORT_34=3306
DB_DATABASE_34=arrahn_semporna
DB_USERNAME_34=root
DB_PASSWORD_34=
DB_CONNECTION_35=mysql
DB_HOST_35=127.0.0.1
DB_PORT_35=3306
DB_DATABASE_35=arrahn_kb1
DB_USERNAME_35=root
DB_PASSWORD_35=
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
-286
View File
@@ -1,286 +0,0 @@
APP_NAME=Lumen
APP_ENV=localhost
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://apiujrah.arrahn
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
#tukar 12-09-23
#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=arrahn_kb2
#DB_USERNAME=root
#DB_PASSWORD=secret
DB_CONNECTION_36=mysql
DB_HOST_36=database_zone1
DB_PORT_36=3306
DB_DATABASE_36=arrahn_kb2
DB_USERNAME_36=root
DB_PASSWORD_36=secret
DB_CONNECTION_2=mysql
DB_HOST_2=database_zone1
DB_PORT_2=3306
DB_DATABASE_2=arrahn_wcy
DB_USERNAME_2=root
DB_PASSWORD_2=secret
DB_CONNECTION_3=mysql
DB_HOST_3=database_zone1
DB_PORT_3=3306
DB_DATABASE_3=arrahn_1stop
DB_USERNAME_3=root
DB_PASSWORD_3=secret
DB_CONNECTION_4=mysql
DB_HOST_4=database_zone1
DB_PORT_4=3306
DB_DATABASE_4=arrahn_jlwt
DB_USERNAME_4=root
DB_PASSWORD_4=secret
DB_CONNECTION_5=mysql
DB_HOST_5=database_zone1
DB_PORT_5=3306
DB_DATABASE_5=arrahn_wb
DB_USERNAME_5=root
DB_PASSWORD_5=secret
DB_CONNECTION_6=mysql
DB_HOST_6=database_zone1
DB_PORT_6=3306
DB_DATABASE_6=arrahn_kls
DB_USERNAME_6=root
DB_PASSWORD_6=secret
DB_CONNECTION_7=mysql
DB_HOST_7=database_zone1
DB_PORT_7=3306
DB_DATABASE_7=online_arrahn
DB_USERNAME_7=root
DB_PASSWORD_7=secret
DB_CONNECTION_8=mysql
DB_HOST_8=database_zone1
DB_PORT_8=3306
DB_DATABASE_8=arrahn_pp
DB_USERNAME_8=root
DB_PASSWORD_8=secret
DB_CONNECTION_9=mysql
DB_HOST_9=database_zone1
DB_PORT_9=3306
DB_DATABASE_9=arrahn_mace
DB_USERNAME_9=root
DB_PASSWORD_9=secret
DB_CONNECTION_10=mysql
DB_HOST_10=database_zone1
DB_PORT_10=3306
DB_DATABASE_10=arrahn_pm
DB_USERNAME_10=root
DB_PASSWORD_10=secret
DB_CONNECTION_11=mysql
DB_HOST_11=database_zone1
DB_PORT_11=3306
DB_DATABASE_11=arrahn_gm
DB_USERNAME_11=root
DB_PASSWORD_11=secret
DB_CONNECTION_12=mysql
DB_HOST_12=database_zone1
DB_PORT_12=3306
DB_DATABASE_12=arrahn_gcl
DB_USERNAME_12=root
DB_PASSWORD_12=secret
DB_CONNECTION_13=mysql
DB_HOST_13=database_zone1
DB_PORT_13=3306
DB_DATABASE_13=arrahn_jli
DB_USERNAME_13=root
DB_PASSWORD_13=secret
DB_CONNECTION_14=mysql
DB_HOST_14=database_zone1
DB_PORT_14=3306
DB_DATABASE_14=arrahn_rp
DB_USERNAME_14=root
DB_PASSWORD_14=secret
DB_CONNECTION_15=mysql
DB_HOST_15=database_zone2
DB_PORT_15=3306
DB_DATABASE_15=arrahn_ktn
DB_USERNAME_15=root
DB_PASSWORD_15=secret
DB_CONNECTION_16=mysql
DB_HOST_16=database_zone2
DB_PORT_16=3306
DB_DATABASE_16=arrahn_pg
DB_USERNAME_16=root
DB_PASSWORD_16=secret
DB_CONNECTION_17=mysql
DB_HOST_17=database_zone2
DB_PORT_17=3306
DB_DATABASE_17=arrahn_skudai
DB_USERNAME_17=root
DB_PASSWORD_17=secret
DB_CONNECTION_18=mysql
DB_HOST_18=database_zone2
DB_PORT_18=3306
DB_DATABASE_18=arrahn_pttn
DB_USERNAME_18=root
DB_PASSWORD_18=secret
DB_CONNECTION_19=mysql
DB_HOST_19=database_zone2
DB_PORT_19=3306
DB_DATABASE_19=arrahn_btg
DB_USERNAME_19=root
DB_PASSWORD_19=secret
DB_CONNECTION_20=mysql
DB_HOST_20=database_zone2
DB_PORT_20=3306
DB_DATABASE_20=arrahn_raub
DB_USERNAME_20=root
DB_PASSWORD_20=secret
DB_CONNECTION_21=mysql
DB_HOST_21=database_zone2
DB_PORT_21=3306
DB_DATABASE_21=arrahn_kgr
DB_USERNAME_21=root
DB_PASSWORD_21=secret
DB_CONNECTION_22=mysql
DB_HOST_22=database_zone2
DB_PORT_22=3306
DB_DATABASE_22=arrahn_sdk
DB_USERNAME_22=root
DB_PASSWORD_22=secret
DB_CONNECTION_23=mysql
DB_HOST_23=database_zone2
DB_PORT_23=3306
DB_DATABASE_23=arrahn_sp
DB_USERNAME_23=root
DB_PASSWORD_23=secret
DB_CONNECTION_24=mysql
DB_HOST_24=database_zone2
DB_PORT_24=3306
DB_DATABASE_24=arrahn_twu
DB_USERNAME_24=root
DB_PASSWORD_24=secret
DB_CONNECTION_25=mysql
DB_HOST_25=database_zone2
DB_PORT_25=3306
DB_DATABASE_25=arrahn_api2
DB_USERNAME_25=root
DB_PASSWORD_25=secret
DB_CONNECTION_26=mysql
DB_HOST_26=database_zone2
DB_PORT_26=3306
DB_DATABASE_26=arrahn_kjg
DB_USERNAME_26=root
DB_PASSWORD_26=secret
DB_CONNECTION_27=mysql
DB_HOST_27=database_zone2
DB_PORT_27=3306
DB_DATABASE_27=arrahn_rl
DB_USERNAME_27=root
DB_PASSWORD_27=secret
DB_CONNECTION_28=mysql
DB_HOST_28=database_zone2
DB_PORT_28=3306
DB_DATABASE_28=arrahn_bfort
DB_USERNAME_28=root
DB_PASSWORD_28=secret
DB_CONNECTION_29=mysql
DB_HOST_29=database_zone2
DB_PORT_29=3306
DB_DATABASE_29=arrahn_kbelud
DB_USERNAME_29=root
DB_PASSWORD_29=secret
DB_CONNECTION_30=mysql
DB_HOST_30=database_zone2
DB_PORT_30=3306
DB_DATABASE_30=arrahn_tuaran
DB_USERNAME_30=root
DB_PASSWORD_30=secret
DB_CONNECTION_31=mysql
DB_HOST_31=database_zone2
DB_PORT_31=3306
DB_DATABASE_31=arrahn_kngau
DB_USERNAME_31=root
DB_PASSWORD_31=secret
DB_CONNECTION_32=mysql
DB_HOST_32=database_zone1
DB_PORT_32=3306
DB_DATABASE_32=arrahn_tm
DB_USERNAME_32=root
DB_PASSWORD_32=secret
DB_CONNECTION_33=mysql
DB_HOST_33=database_zone2
DB_PORT_33=3306
DB_DATABASE_33=arrahn_tmlh
DB_USERNAME_33=root
DB_PASSWORD_33=secret
DB_CONNECTION_34=mysql
DB_HOST_34=database_zone2
DB_PORT_34=3306
DB_DATABASE_34=arrahn_semporna
DB_USERNAME_34=root
DB_PASSWORD_34=secret
DB_CONNECTION_35=mysql
DB_HOST_35=database_zone1
DB_PORT_35=3306
DB_DATABASE_35=arrahn_kb1
DB_USERNAME_35=root
DB_PASSWORD_35=secret
#DB_CONNECTION_37=mysql
#DB_HOST_37=database_zone2
#DB_PORT_37=3306
#DB_DATABASE_37=arrahn_ld
#DB_USERNAME_37=root
#DB_PASSWORD_37=secret
DB_CONNECTION_38=mysql
DB_HOST_38=database_zone2
DB_PORT_38=3306
DB_DATABASE_38=arrahn_pttn2
DB_USERNAME_38=root
DB_PASSWORD_38=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=database
WABOT_API=https://app.wabot.my/api/send.php
WABOT_INSTANCES_ID=67B69A20D329B
WABOT_TOKEN_KEYS=66a08339284bc
ACTIVE_JOB_SERVER=true
-54
View File
@@ -1,54 +0,0 @@
APP_NAME=Lumen
APP_ENV=localhost
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://apiujrah.arrahn
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
#tukar 12-09-23
#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=arrahn_kb2
#DB_USERNAME=root
#DB_PASSWORD=secret
DB_CONNECTION_2=mysql
DB_HOST_2=database_zone_franchise
DB_PORT_2=3306
DB_DATABASE_2=kguru_kb
DB_USERNAME_2=root
DB_PASSWORD_2=secret
DB_CONNECTION_3=mysql
DB_HOST_3=database_zone_franchise
DB_PORT_3=3306
DB_DATABASE_3=Kguru_melor
DB_USERNAME_3=root
DB_PASSWORD_3=secret
DB_CONNECTION_4=mysql
DB_HOST_4=database_zone_franchise
DB_PORT_4=3306
DB_DATABASE_4=kguru_tumpat
DB_USERNAME_4=root
DB_PASSWORD_4=secret
DB_CONNECTION_7=mysql
DB_HOST_7=database_zone_franchise
DB_PORT_7=3306
DB_DATABASE_7=master_koopguru
DB_USERNAME_7=root
DB_PASSWORD_7=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=database
WABOT_API=https://app.wabot.my/api/send.php
WABOT_INSTANCES_ID=67B69A20D329B
WABOT_TOKEN_KEYS=66a08339284bc
ACTIVE_JOB_SERVER=true
-54
View File
@@ -1,54 +0,0 @@
APP_NAME=Lumen
APP_ENV=localhost
APP_KEY=WbPGkYNmXZ6ZQplGlvl0iZ7zyW1b09xa
APP_DEBUG=true
APP_URL=http://apiujrah.arrahn
APP_TIMEZONE=Asia/Kuala_Lumpur
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
#tukar 12-09-23
#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=arrahn_kb2
#DB_USERNAME=root
#DB_PASSWORD=secret
DB_CONNECTION_3=mysql
DB_HOST_3=database-zone-franchise
DB_PORT_3=3306
DB_DATABASE_3=krk_losong
DB_USERNAME_3=root
DB_PASSWORD_3=secret
DB_CONNECTION_4=mysql
DB_HOST_4=database-zone-franchise
DB_PORT_4=3306
DB_DATABASE_4=krk_kemaman
DB_USERNAME_4=root
DB_PASSWORD_4=secret
DB_CONNECTION_5=mysql
DB_HOST_5=database-zone-franchise
DB_PORT_5=3306
DB_DATABASE_5=krk_tokbali
DB_USERNAME_5=root
DB_PASSWORD_5=secret
DB_CONNECTION_7=mysql
DB_HOST_7=database-zone-franchise
DB_PORT_7=3306
DB_DATABASE_7=master_krk
DB_USERNAME_7=root
DB_PASSWORD_7=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=database
WABOT_API=https://app.wabot.my/api/send.php
WABOT_INSTANCES_ID=67B69A20D329B
WABOT_TOKEN_KEYS=66a08339284bc
ACTIVE_JOB_SERVER=true
+61 -1
View File
@@ -21,7 +21,14 @@ use App\Http\Controllers\TransactionOnlineTrailController;
use App\Http\Controllers\Reports\RingkasanHarianController; use App\Http\Controllers\Reports\RingkasanHarianController;
use App\Transaction; use App\Transaction;
$router->group(['namespace' => '\Rap2hpoutre\LaravelLogViewer'], function() use ($router) { $router->get('/health', function () {
return response()->json(['status' => 'ok']);
});
$router->group([
'namespace' => '\Rap2hpoutre\LaravelLogViewer',
'middleware' => 'logviewer.password',
], function () use ($router) {
$router->get('logs', 'LogViewerController@index'); $router->get('logs', 'LogViewerController@index');
}); });
@@ -50,6 +57,7 @@ $router->group(['prefix'=>'api'], function () use ($router){
$router->get('customers/{ic}/gadai/{status}',['uses'=>'CustomerController@showOneCustomerGadai']); $router->get('customers/{ic}/gadai/{status}',['uses'=>'CustomerController@showOneCustomerGadai']);
$router->get('customers/{ic}/gadai/{status}/{kodcaw}',['uses'=>'CustomerController@showOneCustomerGadaiByCawangan']); $router->get('customers/{ic}/gadai/{status}/{kodcaw}',['uses'=>'CustomerController@showOneCustomerGadaiByCawangan']);
$router->post('customers/create/{kodcaw}',['uses'=>'CustomerController@createCustomer']); $router->post('customers/create/{kodcaw}',['uses'=>'CustomerController@createCustomer']);
$router->post('customers/{ic}/mykad-photo', ['uses' => 'CustomerController@storeMykadPhotoIfMissing']);
$router->delete('customers/{kodcaw}/{nokp}', ['uses' => 'CustomerController@delete']); $router->delete('customers/{kodcaw}/{nokp}', ['uses' => 'CustomerController@delete']);
$router->put('customers/update/{nokp}', ['uses' => 'CustomerController@customerUpdate']); $router->put('customers/update/{nokp}', ['uses' => 'CustomerController@customerUpdate']);
$router->put('customers/update/kaunter/{nokp}/{kodcaw}', ['uses' => 'CustomerController@customerUpdateKaunter']); $router->put('customers/update/kaunter/{nokp}/{kodcaw}', ['uses' => 'CustomerController@customerUpdateKaunter']);
@@ -62,6 +70,7 @@ $router->group(['prefix'=>'api'], function () use ($router){
$router->get('customer/anggota',['uses'=>'CustomerController@anggotaKoperasi']); $router->get('customer/anggota',['uses'=>'CustomerController@anggotaKoperasi']);
$router->get('customer/eachcawangan/{ic}',['uses'=>'CustomerController@showCustomerByEachCawangan']); $router->get('customer/eachcawangan/{ic}',['uses'=>'CustomerController@showCustomerByEachCawangan']);
$router->put('customers/{noic}/assign-anggota', ['uses' => 'CustomerController@assignAnggota']); $router->put('customers/{noic}/assign-anggota', ['uses' => 'CustomerController@assignAnggota']);
$router->post('/customers/update-tag-pdpa', ['uses' => 'CustomerController@updateTagPdpa']);
//arcc //arcc
$router->get('customer/searchbyname',['uses'=>'CustomerController@searchByName']); $router->get('customer/searchbyname',['uses'=>'CustomerController@searchByName']);
@@ -235,6 +244,10 @@ $router->group(['prefix'=>'api'], function () use ($router){
$router->get('cawangan/{code_cawangan}',['uses'=>'CawanganController@showOneCawangan']); $router->get('cawangan/{code_cawangan}',['uses'=>'CawanganController@showOneCawangan']);
$router->get('cawangan/detail/{kodcaw}',['uses'=>'CawanganDetailController@getCawanganDetail']); $router->get('cawangan/detail/{kodcaw}',['uses'=>'CawanganDetailController@getCawanganDetail']);
$router->get('features',['uses'=>'FeatureController@index']);
$router->get('features/cawangan',['uses'=>'FeatureController@cawangan']);
$router->put('features/{kodcaw}/{feature}',['uses'=>'FeatureController@update']);
//Poskod API //Poskod API
$router->post('poskod',['uses'=>'PoskodController@store']); $router->post('poskod',['uses'=>'PoskodController@store']);
$router->put('poskod/{poskod}/update',['uses'=>'PoskodController@update']); $router->put('poskod/{poskod}/update',['uses'=>'PoskodController@update']);
@@ -570,6 +583,8 @@ $router->group(['prefix'=>'api'], function () use ($router){
$router->get('CalculationPendahuluanSerahan/{kodcaw}',['uses'=>'VaultController@CalculationPendahuluanSerahan']); $router->get('CalculationPendahuluanSerahan/{kodcaw}',['uses'=>'VaultController@CalculationPendahuluanSerahan']);
$router->get('KeuntunganArRahn/{kodcaw}',['uses'=>'GadaiController@CalculationKeuntungan']); $router->get('KeuntunganArRahn/{kodcaw}',['uses'=>'GadaiController@CalculationKeuntungan']);
$router->get('RebetCalculation/{kodcaw}',['uses'=>'TebusController@RebetCalculation']); $router->get('RebetCalculation/{kodcaw}',['uses'=>'TebusController@RebetCalculation']);
$router->get('RebetPromoCalculation/{kodcaw}',['uses'=>'TebusController@RebetPromoCalculation']);
$router->get('PelarasanRebate/{kodcaw}',['uses'=>'TebusController@PelarasanRebate']);
//-- Harta Semasa --// //-- Harta Semasa --//
$router->get('PinjamanDiberi/{kodcaw}',['uses'=>'GadaiController@CalculationPinjamanDiberi']); $router->get('PinjamanDiberi/{kodcaw}',['uses'=>'GadaiController@CalculationPinjamanDiberi']);
@@ -597,8 +612,10 @@ $router->group(['prefix'=>'api'], function () use ($router){
//Dua-Dua Table //Dua-Dua Table
$router->get('PendahuluanSerahanLejer/{kodcaw}',['uses'=>'VaultController@CalculationPendahuluanSerahanLejer']); $router->get('PendahuluanSerahanLejer/{kodcaw}',['uses'=>'VaultController@CalculationPendahuluanSerahanLejer']);
$router->get('ModalOnlineLejer/{kodcaw}', ['uses' => 'VaultController@CalculationModalOnlineLejer']);
$router->get('KeuntunganArRahnLejer/{kodcaw}',['uses'=>'GadaiController@CalculationKeuntunganLejer']); $router->get('KeuntunganArRahnLejer/{kodcaw}',['uses'=>'GadaiController@CalculationKeuntunganLejer']);
$router->get('RebetCalculationLejer/{kodcaw}',['uses'=>'TebusController@RebetCalculationLejer']); $router->get('RebetCalculationLejer/{kodcaw}',['uses'=>'TebusController@RebetCalculationLejer']);
$router->get('RebetPromoCalculationLejer/{kodcaw}',['uses'=>'TebusController@RebetPromoCalculationLejer']);
$router->get('cajlelongLejer/{kodcaw}',['uses'=>'LelongController@cajlelongcalculationLejer']); $router->get('cajlelongLejer/{kodcaw}',['uses'=>'LelongController@cajlelongcalculationLejer']);
// //-- Harta Semasa --// // //-- Harta Semasa --//
@@ -668,6 +685,49 @@ $router->group(['prefix'=>'api'], function () use ($router){
//initiateTunai //initiateTunai
$router->get('initiate/tunai',['uses'=>'TunaiController@initiateTunai']); $router->get('initiate/tunai',['uses'=>'TunaiController@initiateTunai']);
// Modal Online (pembiayaan atas talian float)
// Operasi top-up routes before {kodcaw} catch-alls
$router->get('modal-online/operasi/topup/requests', ['uses' => 'ModalOnlineController@listTopUpOperasi']);
$router->post('modal-online/operasi/topup/{id}/approve', ['uses' => 'ModalOnlineController@approveTopUp']);
$router->post('modal-online/operasi/topup/{id}/reject', ['uses' => 'ModalOnlineController@rejectTopUp']);
$router->get('modal-online/operasi/eod', ['uses' => 'ModalOnlineController@listToday']);
$router->post('modal-online/operasi/eod/close-all', ['uses' => 'ModalOnlineController@closeAll']);
$router->get('modal-online/{kodcaw}', ['uses' => 'ModalOnlineController@get']);
$router->post('modal-online/{kodcaw}/reserve', ['uses' => 'ModalOnlineController@reserve']);
$router->post('modal-online/{kodcaw}/topup/request', ['uses' => 'ModalOnlineController@requestTopUp']);
$router->get('modal-online/{kodcaw}/topup/requests', ['uses' => 'ModalOnlineController@listTopUpByCawangan']);
$router->post('modal-online/{kodcaw}/close', ['uses' => 'ModalOnlineController@close']);
$router->get('initiate/modal-online', ['uses' => 'ModalOnlineController@initiate']);
// Operasi kelulusan pembiayaan online (must be before {kodcaw} routes)
$router->get('whatsapp/webhook', ['uses' => 'WhatsappWebhookController@verify']);
$router->post('whatsapp/webhook', ['uses' => 'WhatsappWebhookController@handle']);
$router->get('pembiayaan-online/operasi/batches', ['uses' => 'PembiayaanOnlineController@operasiListBatches']);
$router->get('pembiayaan-online/operasi/{kodcaw}/batches/{batchId}', ['uses' => 'PembiayaanOnlineController@operasiShowBatch']);
$router->post('pembiayaan-online/operasi/{kodcaw}/lines/{id}/approve', ['uses' => 'PembiayaanOnlineController@operasiApproveLine']);
$router->post('pembiayaan-online/operasi/{kodcaw}/lines/{id}/reject', ['uses' => 'PembiayaanOnlineController@operasiRejectLine']);
$router->post('pembiayaan-online/operasi/{kodcaw}/lines/{id}/paid', ['uses' => 'PembiayaanOnlineController@operasiMarkPaid']);
$router->post('pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/approve', ['uses' => 'PembiayaanOnlineController@operasiApproveBatch']);
$router->post('pembiayaan-online/operasi/{kodcaw}/batches/{batchId}/paid', ['uses' => 'PembiayaanOnlineController@operasiMarkPaidBatch']);
// Pembiayaan Online (payout line + PC queue / batch)
$router->post('pembiayaan-online/{kodcaw}', ['uses' => 'PembiayaanOnlineController@store']);
$router->get('pembiayaan-online/{kodcaw}/queue', ['uses' => 'PembiayaanOnlineController@queue']);
$router->get('pembiayaan-online/{kodcaw}/paid', ['uses' => 'PembiayaanOnlineController@paidQueue']);
$router->post('pembiayaan-online/{kodcaw}/paid/blast', ['uses' => 'PembiayaanOnlineController@markBlastSent']);
$router->put('pembiayaan-online/{kodcaw}/lines/{id}', ['uses' => 'PembiayaanOnlineController@updateLine']);
$router->post('pembiayaan-online/{kodcaw}/lines/{id}/verify', ['uses' => 'PembiayaanOnlineController@verifyLine']);
$router->post('pembiayaan-online/{kodcaw}/lines/{id}/unverify', ['uses' => 'PembiayaanOnlineController@unverifyLine']);
$router->get('pembiayaan-online/{kodcaw}/lines/{id}/bukti', ['uses' => 'PembiayaanOnlineController@getBukti']);
$router->get('pembiayaan-online/{kodcaw}/lines/{id}/bukti-akaun', ['uses' => 'PembiayaanOnlineController@getBuktiAkaun']);
$router->get('pembiayaan-online/{kodcaw}/lines/{id}', ['uses' => 'PembiayaanOnlineController@showLine']);
$router->get('pembiayaan-online/{kodcaw}/batches', ['uses' => 'PembiayaanOnlineController@listBatches']);
$router->get('pembiayaan-online/{kodcaw}/batches/{batchId}', ['uses' => 'PembiayaanOnlineController@showBatch']);
$router->post('pembiayaan-online/{kodcaw}/batches/submit', ['uses' => 'PembiayaanOnlineController@submitBatch']);
$router->get('pembiayaan-online/{kodcaw}/by-norujukan/{norujukan}', ['uses' => 'PembiayaanOnlineController@show']);
//WTD //WTD
$router->get('check/wtd',['uses'=>'WTDController@getDraftWTD']); $router->get('check/wtd',['uses'=>'WTDController@getDraftWTD']);
$router->get('check/customer',['uses' => 'WTDController@getCustomerWTD']); $router->get('check/customer',['uses' => 'WTDController@getCustomerWTD']);
+2
View File
@@ -0,0 +1,2 @@
/debugbar
/framework/sessions