Explore/ismail (#15)
Build Docker Image / build-frontend (push) Successful in 38s

Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local>
Co-authored-by: nurafrinaalimi16 <nurafrinaalimi16@gmail.com>
Reviewed-on: #15
This commit was merged in pull request #15.
This commit is contained in:
2026-09-06 12:41:58 +08:00
parent 1f8642e230
commit d60dd8bf39
139 changed files with 6788 additions and 867 deletions
@@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Auth;
class ImpersonateController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function impersonate(Request $request, $id): RedirectResponse
{
Gate::authorize('impersonate');
$impersonator = $request->user();
$id = (int) $id;
$target = User::findOrFail($id);
if ($target->id === $request->user()->id) {
return back()->with('error', 'Tidak boleh impersonate diri sendiri.');
}
if (method_exists($target, 'canBeImpersonated') && !$target->canBeImpersonated()) {
return back()->with('error', 'Akaun ini tidak boleh di-impersonate.');
}
$request->user()->impersonate($target);
return redirect()->route('home');
}
public function leave(Request $request): RedirectResponse
{
if ($request->user() && method_exists($request->user(), 'isImpersonated') && $request->user()->isImpersonated()) {
$impersonated = $request->user();
$impersonator = method_exists($impersonated, 'impersonator') ? $impersonated->impersonator() : null;
$request->user()->leaveImpersonation();
}
return redirect()->route('home');
}
}
+40 -6
View File
@@ -60,9 +60,7 @@ class LoginController extends Controller
'password' => 'required|string',
]);
$credentials = $request->only('email', 'password');
if (Auth::guard('web')->attempt($credentials)) {
if ($this->attemptWebLogin($request)) {
$auth_user = Auth::guard('web')->user();
$api_url = config('api.url');
@@ -103,9 +101,7 @@ class LoginController extends Controller
'password' => 'required|string',
]);
$credentials = $request->only('email', 'password');
if (Auth::guard('dashboard')->attempt($credentials,true)) {
if ($this->attemptDashboardLogin($request)) {
$auth_user = Auth::guard('dashboard')->user();
$user_login['nama'] = $auth_user['name'];
@@ -187,4 +183,42 @@ class LoginController extends Controller
return response()->json($kakitangan,200);
}
/**
* In local environment, log in by email only (password ignored).
*/
private function attemptWebLogin(Request $request): bool
{
if (app()->environment('local')) {
$user = User::where('email', $request->email)->first();
if ($user) {
Auth::guard('web')->login($user);
return true;
}
return false;
}
return Auth::guard('web')->attempt($request->only('email', 'password'));
}
/**
* In local environment, log in by email only (password ignored).
*/
private function attemptDashboardLogin(Request $request): bool
{
if (app()->environment('local')) {
$user = Dashboard::where('email', $request->email)->first();
if ($user) {
Auth::guard('dashboard')->login($user, true);
return true;
}
return false;
}
return Auth::guard('dashboard')->attempt($request->only('email', 'password'), true);
}
}
+118 -2
View File
@@ -358,7 +358,7 @@ class CustomersController extends Controller
])->json();
}
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/customers/create/' . $auth_user['cawangan'], [
$customer_payload = [
'kodcaw' => $auth_user['cawangan'],
'nopelanggan' => $no_pelanggan,
'nokpbaru' => $nokpbaru,
@@ -385,7 +385,14 @@ class CustomersController extends Controller
'nota_pekerjaan' => $request->nota_pekerjaan,
'notaarcc' => $request->notaarcc,
'tujuangadai' => $request->tujuan_gadai,
]);
];
if ($request->filled('photo_base64')) {
$customer_payload['photo_base64'] = $request->input('photo_base64');
$customer_payload['photo_mime'] = $request->input('photo_mime') ?: 'image/jpeg';
}
$customer_info = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/customers/create/' . $auth_user['cawangan'], $customer_payload);
Session::put('customer_info', $customer_info);
Session::put('noic', $request->noic);
@@ -574,4 +581,113 @@ class CustomersController extends Controller
return redirect()->back()->with('error', 'Gagal mengemaskini status anggota. Sila cuba semula.');
}
}
public function printMaklumat(Request $request)
{
$noic = $request->query('noic') ?: Session::get('noic');
if (empty($noic)) {
return redirect('/pelanggan');
}
$auth_user = Auth::user();
$api_url = config('api.url');
$verify = ['verify' => config('app.api_verify')];
$kodcaw = $auth_user['cawangan'];
$customer = Http::withOptions($verify)->get($api_url . '/api/customers/' . $noic)->json();
if (!is_array($customer) || (empty($customer['nama']) && empty($customer['nopelanggan']) && empty($customer['kpbaru']) && empty($customer['kplama']))) {
abort(404, 'Pelanggan tidak dijumpai.');
}
$cawanganResp = Http::withOptions($verify)->get($api_url . '/api/cawangan/detail/' . $kodcaw)->json();
$cawangan_detail = (is_array($cawanganResp) && isset($cawanganResp[0]))
? $cawanganResp[0]
: (is_array($cawanganResp) ? $cawanganResp : []);
$banks = Http::withOptions($verify)->get($api_url . '/api/bank')->json();
$pekerjaanList = Http::withOptions($verify)->get($api_url . '/api/pekerjaan')->json();
$poskodKod = $customer['poskod'] ?? '';
$poskod = [];
if ($poskodKod !== '' && $poskodKod !== null) {
$poskodResp = Http::withOptions($verify)->get($api_url . '/api/poskod/' . $poskodKod)->json();
if (is_array($poskodResp)) {
$poskod = $poskodResp;
}
}
$daerahNama = 'NIL';
$negeriNama = 'NIL';
if (!empty($poskod['koddaerah'])) {
$daerahResp = Http::withOptions($verify)->get($api_url . '/api/daerah/' . $poskod['koddaerah'])->json();
$daerahNama = is_array($daerahResp) ? ($daerahResp['namadaerah'] ?? 'NIL') : 'NIL';
}
if (!empty($poskod['kodnegeri'])) {
$negeriResp = Http::withOptions($verify)->get($api_url . '/api/negeri/' . $poskod['kodnegeri'])->json();
$negeriNama = is_array($negeriResp) ? ($negeriResp['negeri'] ?? 'NIL') : 'NIL';
}
$bankNama = $this->lookupListValue($banks, 'abbreviation', $customer['kodbank'] ?? null, 'name');
$pekerjaanNama = $this->lookupListValue($pekerjaanList, 'kodkerja', $customer['kodkerja'] ?? null, 'keterangan');
if (($customer['kodkerja'] ?? '') == '6' && !empty($customer['nota_pekerjaan'])) {
$pekerjaanNama = $customer['nota_pekerjaan'];
}
$photo_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="320" viewBox="0 0 240 320">'
. '<rect fill="#d9dee5" width="240" height="320"/>'
. '<circle cx="120" cy="118" r="52" fill="#8b949e"/>'
. '<ellipse cx="120" cy="286" rx="90" ry="92" fill="#8b949e"/>'
. '</svg>';
$photo_src = 'data:image/svg+xml;base64,' . base64_encode($photo_svg);
if (!empty($customer['photo_base64'])) {
$mime = $customer['photo_mime'] ?? 'image/jpeg';
$photo_src = 'data:' . $mime . ';base64,' . $customer['photo_base64'];
}
return view('print.maklumat_pelanggan', [
'cawangan' => $cawangan_detail['namacaw'] ?? ($cawangan_detail['details_caw'] ?? $kodcaw),
'kodcaw' => $kodcaw,
'alamat1' => $cawangan_detail['alamat1'] ?? '',
'alamat2' => $cawangan_detail['alamat2'] ?? '',
'notel' => $cawangan_detail['notel'] ?? '',
'tarikh' => Carbon::now()->format('d/m/Y'),
'nama' => $this->printBlank($customer['nama'] ?? null),
'nokp' => $this->printBlank(!empty($customer['kpbaru']) ? $customer['kpbaru'] : ($customer['kplama'] ?? $noic)),
'nopelanggan' => $this->printBlank($customer['nopelanggan'] ?? null),
'status' => (!empty($customer['tag_anggota']) && $customer['tag_anggota'] == 1) ? 'Anggota Koperasi' : 'Pelanggan',
'telefon' => $this->printBlank($customer['telefon'] ?? null),
'telefon2' => $this->printBlank($customer['telefon2'] ?? null),
'alamat' => $this->printBlank($customer['alamat1'] ?? null),
'poskod' => $this->printBlank($customer['poskod'] ?? null),
'daerah' => $daerahNama,
'negeri' => $negeriNama,
'bank' => $this->printBlank($bankNama),
'noakaun' => $this->printBlank($customer['noakaun'] ?? null),
'pekerjaan' => $this->printBlank($pekerjaanNama),
'nota' => $this->printBlank($customer['nota'] ?? null),
'photo_src' => $photo_src,
]);
}
private function printBlank($value)
{
if ($value === null) {
return 'NIL';
}
$value = is_string($value) ? trim($value) : $value;
return $value === '' ? 'NIL' : $value;
}
private function lookupListValue($list, $key, $match, $nameKey)
{
if (!is_array($list) || $match === null || $match === '' || $match === '-') {
return '';
}
foreach ($list as $item) {
if (is_array($item) && ($item[$key] ?? null) == $match) {
return $item[$nameKey] ?? '';
}
}
return '';
}
}
+153 -1
View File
@@ -211,17 +211,169 @@ class GadaianController extends Controller
'teller' => $auth_user['name']
]);
$this->AliranTunaiController->alirantunaiGadai($request, $auth_user['name']);
$caraTerima = strtoupper((string) $request->input('cara_terima', 'TUNAI'));
$pinjamanAmt = round((float) $request->pinjaman, 2);
// Online only for RM5k50k; otherwise force cash path.
if ($caraTerima === 'ONLINE' && ($pinjamanAmt < 5000 || $pinjamanAmt > 50000)) {
$caraTerima = 'TUNAI';
}
if ($caraTerima === 'ONLINE') {
$online = Http::withOptions(['verify' => config('app.api_verify')])
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/' . $auth_user['cawangan'], [
'norujukan' => $request->norujukan_gadai,
'nokp' => $request->nokp,
'pinjaman' => $pinjamanAmt,
'kodbank' => $request->kodbank,
'noakaun' => $request->noakaun,
'nama_bank' => $request->nama_bank,
'nama_akaun' => $request->nama_akaun ?: $request->nama_pelanggan,
'nama_pelanggan' => $request->nama_pelanggan,
'nopelanggan' => $request->nopelanggan,
'created_by' => $auth_user['name'],
'update_customer_bank' => true,
]);
if (!$online->successful()) {
$body = $online->json();
$msg = is_array($body) && isset($body['message'])
? $body['message']
: 'Gagal daftar pembiayaan online. Sila cuba lagi.';
return redirect()->back()->with('error', $msg);
}
} else {
$this->AliranTunaiController->alirantunaiGadai($request, $auth_user['name']);
}
event(new GadaiNotify('lulus', $auth_user['cawangan']));
$request->session()->forget('page_reload');
$request->session()->forget('norujukan');
$request->session()->flash('activate_tab', 'SAG_baru');
if ($caraTerima === 'ONLINE') {
$request->session()->flash('message', 'Gadai berjaya. Pembiayaan online menunggu semakan PC.');
}
return redirect()->route('customer_info');
}
public function printBorangPengesahanOnline($norujukan)
{
$auth_user = Auth::user();
$kodcaw = $auth_user['cawangan'];
$onlineResp = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/by-norujukan/' . $norujukan);
if (!$onlineResp->successful()) {
abort(404, 'Pembiayaan online tidak dijumpai.');
}
$line = $onlineResp->json();
if (!is_array($line) || empty($line['norujukan'])) {
abort(404, 'Pembiayaan online tidak dijumpai.');
}
return $this->renderBorangPengesahanOnline($line, $kodcaw);
}
public function previewBorangPengesahanOnline(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $auth_user['cawangan'];
$line = [
'norujukan' => $request->input('norujukan'),
'nokp' => $request->input('nokp'),
'nopelanggan' => $request->input('nopelanggan'),
'nama_pelanggan' => $request->input('nama_pelanggan'),
'pinjaman' => $request->input('pinjaman'),
'kodbank' => $request->input('kodbank'),
'nama_bank' => $request->input('nama_bank'),
'noakaun' => $request->input('noakaun'),
'nama_akaun' => $request->input('nama_akaun'),
'created_by' => $auth_user['name'] ?? '',
'created_at' => Carbon::now()->toDateTimeString(),
];
if (empty($line['norujukan']) || empty($line['noakaun'])) {
abort(422, 'Sila lengkapkan maklumat bank sebelum cetak borang.');
}
return $this->renderBorangPengesahanOnline($line, $kodcaw);
}
private function renderBorangPengesahanOnline(array $line, $kodcaw)
{
$auth_user = Auth::user();
$cawanganResp = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/cawangan/detail/' . $kodcaw)
->json();
$cawangan_detail = (is_array($cawanganResp) && isset($cawanganResp[0]))
? $cawanganResp[0]
: (is_array($cawanganResp) ? $cawanganResp : []);
$nokpRaw = $line['nokp'] ?? '';
$customer = [];
if ($nokpRaw !== '') {
$customerResp = Http::withOptions(['verify' => config('app.api_verify')])
->get(config('api.url') . '/api/customers/' . $nokpRaw)
->json();
if (is_array($customerResp)) {
$customer = $customerResp;
}
}
$cawangan = $cawangan_detail['namacaw']
?? $cawangan_detail['details_caw']
?? $kodcaw;
$alamat1 = $cawangan_detail['alamat1'] ?? '';
$alamat2 = $cawangan_detail['alamat2'] ?? '';
$notel = $cawangan_detail['notel'] ?? '';
$nama = $line['nama_pelanggan'] ?? ($customer['nama'] ?? '');
$nokp = !empty($customer['kpbaru']) ? $customer['kpbaru'] : ($customer['kplama'] ?? $nokpRaw);
$nopelanggan = $line['nopelanggan'] ?? ($customer['nopelanggan'] ?? '');
$telefon = $customer['telefon'] ?? '';
$teller = $line['created_by'] ?? ($auth_user['name'] ?? '');
$bank = $line['nama_bank'] ?? ($line['kodbank'] ?? '');
$noakaun = $line['noakaun'] ?? '';
$nama_akaun = $line['nama_akaun'] ?? $nama;
$pinjaman = number_format((float) ($line['pinjaman'] ?? 0), 2);
$norujukan = $line['norujukan'] ?? '';
$tarikhSumber = $line['created_at'] ?? null;
try {
$tarikh = $tarikhSumber
? Carbon::parse($tarikhSumber)->format('d/m/Y')
: Carbon::now()->format('d/m/Y');
} catch (\Exception $e) {
$tarikh = Carbon::now()->format('d/m/Y');
}
return view('print.borang_pengesahan_online', compact(
'cawangan',
'kodcaw',
'alamat1',
'alamat2',
'notel',
'norujukan',
'tarikh',
'teller',
'nama',
'nokp',
'nopelanggan',
'telefon',
'pinjaman',
'bank',
'noakaun',
'nama_akaun'
));
}
public function editstore(Request $request)
{
$auth_user = Auth::user();
+36 -3
View File
@@ -20,10 +20,43 @@ class KakitanganController extends Controller
public function daftarKakitangan(){
$api_url = config('api.url');
$auth_user = Auth::user();
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
$http = Http::withOptions(['verify' => config('app.api_verify')]);
$cawangan_all = $http->get($api_url . '/api/cawangan')->json();
$cawangan_all = is_array($cawangan_all) ? $cawangan_all : [];
$cawangan_by_kod = collect($cawangan_all)->keyBy('kodcaw');
$cawangan_info = $cawangan_by_kod->get($auth_user['cawangan'], []);
$role = Role::get();
$kakitangan = User::all();
return view('operasi.daftar_kakitangan',compact('cawangan_info','api_url','role','kakitangan','auth_user'));
$kakitangan_rows = User::where('roles', '!=', 'admin')
->get()
->values()
->map(function ($staff) use ($cawangan_by_kod) {
$kod = (string) $staff->cawangan;
if (in_array($kod, ['operasi', 'callcenter', 'admin'], true)) {
$cawangan_nama = ucwords($kod);
} else {
$cawangan_nama = ucwords((string) data_get($cawangan_by_kod, $kod . '.details_caw', $kod));
}
return [
'id' => $staff->id,
'name' => $staff->name,
'roles' => strtoupper((string) $staff->roles),
'role_harian' => $staff->role_harian ? strtoupper($staff->role_harian) : '-',
'role_asal' => $staff->role_asal ? strtoupper($staff->role_asal) : '-',
'cawangan' => $cawangan_nama,
];
});
return view('operasi.daftar_kakitangan', compact(
'cawangan_info',
'api_url',
'role',
'kakitangan_rows',
'auth_user',
'cawangan_all'
));
}
public function registerStaff(Request $request){
@@ -505,6 +505,9 @@ class KhazanahController extends Controller
public function mintakatalaluansemula(Request $request)
{
if (app()->environment('local')) {
return "True";
}
if(Hash::check($request->passsemula, Auth::user()->password))
{
+156 -5
View File
@@ -87,13 +87,19 @@ class OperasiController extends Controller
'siri' => $request->siri
])->json();
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan, [
$updateData = [
'sahkan' => 1,
'namaoperasi' => $request->namaoperasi,
'siri' => $request->siri
])->json();
} else {
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran', [
];
if ($request->has('actions')) {
$updateData['actions'] = $request->actions;
}
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateKelulusan/' . $request->kodcaw . '/' . $norujukan, $updateData)->json();
}else{
$updatelulus = Http::withOptions(['verify' => config('app.api_verify')])->put(config('api.url') . '/api/UpdateHapusAnsuran',[
'siri' => $request->siri,
"kodcaw" => $request->kodcaw,
"norujukan" => $request->norujukan,
@@ -139,6 +145,49 @@ class OperasiController extends Controller
return response()->json($updatelulus, 200);
}
public function createKelulusanByKhazanah(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$current = Carbon::now();
$current = new Carbon();
$norujukan = $request->norujukan;
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
$getsiri = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/ListLatestLulus/'. $auth_user['cawangan'] . '/' . $norujukan)->json();
if(isset($getsiri['siri']))
{
$siri = $getsiri['siri'] + 1;
}else{
$siri = 1;
}
$createlulus = Http::withOptions(['verify' => config('app.api_verify')])->post(config('api.url') . '/api/CreateKelulusan/' . $auth_user['cawangan'] . '/' . $norujukan . '/roles/khazanah', [
'komen' => $request->komen,
'dategadai' => $current->toDateString(),
'bilgadai' => 1,
'siri' => $siri,
'margin' => $request->margin,
'berat' => $request->berat,
'nilaimarhun' => $request->nilaimarhun,
'status' => $request->status,
'jumpinjam' => $request->jumpinjam,
'jenistebus' => "G",
'teller' => $request->teller,
'peloperasi' => "operasi",
'pelulus' => "operasi",
'actions' => "Mohon Pengesahan Pembayaran Pampasan"
])->json();
event(new OperasiNotice($auth_user['cawangan'],$auth_user['name']));
return response()->json($siri,201);
}
public function updatekelulusananggota(Request $request)
{
$auth_user = Auth::user();
@@ -149,7 +198,7 @@ class OperasiController extends Controller
$kodcaw = $request->kodcaw;
$komen = $request->komenkelulusan;
$namaoperasi = $request->namaoperasi;
$taglulus = $request->taglulus; // contoh: 'Y' = lulus, 'N' = batal
$taglulus = $request->taglulus; // contoh: 'Y' = lulus, 'N' = batalmarginmutu()
// 1️⃣ Tentukan status kelulusan
$status = ($taglulus == 'Y') ? 1 : 2; // 1 = Lulus, 2 = Batal/Tolak
@@ -321,6 +370,9 @@ class OperasiController extends Controller
public function mintakatalaluansemula(Request $request)
{
if (app()->environment('local')) {
return "True";
}
if (Hash::check($request->passsemula, Auth::user()->password)) {
return "True";
@@ -401,4 +453,103 @@ class OperasiController extends Controller
return view('operasi.kelulusan_anggota', compact('cawangan_info', 'api_url', 'arraykelulusan'));
}
public function marginmutu()
{
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
// Get MaxMutuEmas data from API
$maxmutuemas = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/maxmutuemas')->json();
return view('operasi.marginmutu', compact('cawangan_info', 'api_url', 'maxmutuemas'));
}
public function editMarginMutu()
{
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
return view('operasi.edit_marginmutu', compact('cawangan_info', 'api_url'));
}
public function laporanAudit()
{
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url') . '/api/cawangan/'. $auth_user['cawangan'])->json();
return view('homepage.operasi.laporan-audit', compact('cawangan_info', 'api_url'));
}
public function cetakLaporanAuditTrail(Request $request)
{
$api_url = config('api.url');
$auth_user = Auth::user();
$current = Carbon::now();
$current = new Carbon();
$tarikh_cetak = $current->toDateString();
if($request->dateaudit == null){
$date = $current->toDateString();
}else{
$split_date = explode('-', $request->dateaudit);
$date = $split_date[0] . '-' . $split_date[1] . '-' . $split_date[2];
}
if($request->dateaudit_till == null){
$date_till = $current->toDateString();
}else{
$split_date_till = explode('-', $request->dateaudit_till);
$date_till = $split_date_till[0] . '-' . $split_date_till[1] . '-' . $split_date_till[2];
}
$audittrail_raw = Http::withOptions(['verify' => config('app.api_verify')])->get(config('api.url'). '/api/reports/audittrail/operasi',['date' => $date,'date_till' => $date_till])->json();
if(!$audittrail_raw){
return redirect()->back()->with('errormessage', 'Tiada data pada tarikh tersebut.');
}
// Transform the data to match view format
$audittrail = [];
foreach($audittrail_raw as $item) {
$created_at = \Carbon\Carbon::parse($item['created_at']);
// Format differences for display
$differences_text = '-';
if (isset($item['differences']) && !empty($item['differences'])) {
$diff_parts = [];
foreach ($item['differences'] as $field => $changes) {
$old = $changes['old'] ?? 'null';
$new = $changes['new'] ?? 'null';
$diff_parts[] = "{$field}: {$old}{$new}";
}
$differences_text = implode('; ', $diff_parts);
}
$audittrail[] = [
'tarikh' => $created_at->format('d/m/Y'),
'masa' => $created_at->format('H:i:s'),
'users' => $item['users'],
'actions' => $item['actions'],
'differ' => $differences_text
];
}
$cawangan_detail = [
'kodcaw' => 'operasi',
'namacaw' => 'Operasi Ar-Rahn',
'alamat1' => 'Lot 107, Jalan Parit Dalam,',
'alamat2' => 'Seksyen 8',
'poskod' => '15000',
'bandar' => 'Kota Bahru',
'negeri' => 'Kelantan',
'notel' => '03-12345678',
'nofaks' => '03-12345678',
];
return view('print.laporan_audittrail_operasi', compact('audittrail','cawangan_detail','tarikh_cetak','date','date_till','api_url'));
}
}
@@ -0,0 +1,842 @@
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
class PembiayaanOnlineController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
protected function api()
{
return Http::withOptions(['verify' => config('app.api_verify')]);
}
protected function kodcaw()
{
return Auth::user()->cawangan;
}
/**
* PC senarai queue + batch history.
*/
public function index(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$queue = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/queue')
->json();
$batches = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches')
->json();
$banks = $this->api()->get($api_url . '/api/bank')->json();
if (!is_array($banks)) {
$banks = [];
}
$modal = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw)
->json();
$lines = $queue['data'] ?? [];
$slot = $queue['slot_cadangan'] ?? null;
if (!is_array($batches)) {
$batches = [];
}
return view('pembiayaan_online.pc.senarai', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'lines',
'batches',
'banks',
'modal',
'slot',
'queue'
));
}
/**
* Update bank/docs for a queue line.
*/
public function updateLine(Request $request, $id)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$payload = [
'kodbank' => $request->input('kodbank'),
'noakaun' => $request->input('noakaun'),
'nama_bank' => $request->input('nama_bank'),
'nama_akaun' => $request->input('nama_akaun'),
'nota' => $request->input('nota'),
'update_customer_bank' => true,
];
$res = $this->api()
->asForm()
->put($api_url . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id, $payload);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal kemaskini rekod.';
return redirect()->back()->with('error', $msg);
}
return redirect()->route('pembiayaan.online.pc')->with('message', 'Rekod dikemaskini.');
}
/**
* Submit selected lines as a batch to Operasi.
*/
public function submitBatch(Request $request)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$auth_user = Auth::user();
$ids = $request->input('ids', []);
if (!is_array($ids)) {
$ids = array_filter(explode(',', (string) $ids));
}
$res = $this->api()
->asForm()
->post($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/submit', [
'ids' => $ids,
'pc_submitted_by' => $auth_user['name'],
'slot' => $request->input('slot'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar batch.';
return redirect()->back()->with('error', $msg);
}
$payload = $res->json();
$batchNo = is_array($payload) ? ($payload['batch']['batch_no'] ?? '') : '';
$appended = is_array($payload) && !empty($payload['appended']);
$message = $appended
? ('Rekod ditambah ke batch' . ($batchNo ? ' ' . $batchNo : '') . '.')
: ('Batch dihantar ke Operasi' . ($batchNo ? ': ' . $batchNo : '.'));
return redirect()->route('pembiayaan.online.pc')->with('message', $message);
}
/**
* Batch detail.
*/
public function showBatch($batchId)
{
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$detail = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/batches/' . $batchId)
->json();
if (!is_array($detail) || empty($detail['batch'])) {
return redirect()->route('pembiayaan.online.pc')->with('error', 'Batch tidak dijumpai.');
}
return view('pembiayaan_online.pc.batch', [
'batch' => $detail['batch'],
'lines' => $detail['lines'] ?? [],
'kodcaw' => $kodcaw,
'api_url' => $api_url,
'cawangan_info' => $cawangan_info,
]);
}
/**
* PC: senarai PAID untuk tally + blast.
*/
public function paidIndex(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$blastFilter = $request->input('blast_sent', '0');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$query = [];
if ($blastFilter !== 'all' && $blastFilter !== '') {
$query['blast_sent'] = $blastFilter;
}
$paid = $this->api()
->get($api_url . '/api/pembiayaan-online/' . $kodcaw . '/paid', $query)
->json();
$lines = $paid['data'] ?? [];
if (!is_array($lines)) {
$lines = [];
}
$bilangan = $paid['bilangan'] ?? count($lines);
$jumlah = $paid['jumlah'] ?? 0;
return view('pembiayaan_online.pc.paid', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'lines',
'bilangan',
'jumlah',
'blastFilter'
));
}
/**
* PC: view/download bukti bayaran inline.
*/
public function viewBukti($id)
{
$kodcaw = $this->kodcaw();
$res = $this->api()
->get(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/lines/' . $id . '/bukti');
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Bukti tidak dijumpai.';
return redirect()->back()->with('error', $msg);
}
$payload = $res->json();
$bukti = $payload['bukti'] ?? null;
if (!$bukti || empty($bukti['data'])) {
return redirect()->back()->with('error', 'Fail bukti kosong.');
}
$raw = $bukti['data'];
if (strpos($raw, 'base64,') !== false) {
$raw = substr($raw, strpos($raw, 'base64,') + 7);
}
$binary = base64_decode($raw, true);
if ($binary === false) {
$binary = base64_decode($bukti['data']);
}
$type = $bukti['type'] ?? 'application/octet-stream';
$filename = $bukti['filename'] ?? ('bukti-' . $id);
return response($binary, 200, [
'Content-Type' => $type,
'Content-Disposition' => 'inline; filename="' . $filename . '"',
]);
}
/**
* PC: tanda rekod PAID sebagai selesai semak / blast.
*/
public function markBlast(Request $request)
{
$kodcaw = $this->kodcaw();
$ids = $request->input('ids', []);
if (!is_array($ids)) {
$ids = array_filter(explode(',', (string) $ids));
}
if (count($ids) === 0) {
return redirect()->back()->with('error', 'Sila pilih sekurang-kurangnya satu rekod.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/' . $kodcaw . '/paid/blast', [
'ids' => $ids,
'blast_by' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tanda selesai.';
return redirect()->back()->with('error', $msg);
}
$n = $res->json()['dikemaskini'] ?? count($ids);
return redirect()
->route('pembiayaan.online.pc.paid')
->with('message', $n . ' rekod ditanda selesai semak/blast.');
}
/**
* Operasi: senarai batch semua cawangan.
*/
public function operasiIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$status = $request->input('status', 'SUBMITTED');
$tarikh = $request->input('tarikh');
$query = ['status' => $status];
if ($tarikh) {
$query['tarikh'] = $tarikh;
}
$res = $this->api()
->get($api_url . '/api/pembiayaan-online/operasi/batches', $query)
->json();
$batches = $res['data'] ?? [];
if (!is_array($batches)) {
$batches = [];
}
return view('pembiayaan_online.operasi.senarai', compact(
'auth_user',
'api_url',
'batches',
'status',
'tarikh'
));
}
/**
* Operasi: detail batch + lines.
*/
public function operasiShowBatch($kodcaw, $batchId)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$detail = $this->api()
->get($api_url . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId)
->json();
if (!is_array($detail) || empty($detail['batch'])) {
return redirect()
->route('pembiayaan.online.operasi')
->with('error', 'Batch tidak dijumpai.');
}
return view('pembiayaan_online.operasi.batch', [
'auth_user' => $auth_user,
'batch' => $detail['batch'],
'lines' => $detail['lines'] ?? [],
'kodcaw' => $kodcaw,
'api_url' => $api_url,
'cawangan_info' => $cawangan_info,
]);
}
/**
* Operasi: printable batch kelulusan form.
*/
public function operasiPrintBatch($kodcaw, $batchId)
{
$api_url = config('api.url');
$detail = $this->api()
->get($api_url . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId)
->json();
if (!is_array($detail) || empty($detail['batch'])) {
return redirect()
->route('pembiayaan.online.operasi')
->with('error', 'Batch tidak dijumpai.');
}
$batch = $detail['batch'];
$lines = $detail['lines'] ?? [];
if (!is_array($lines)) {
$lines = [];
}
$cawanganResp = $this->api()
->get($api_url . '/api/cawangan/detail/' . $kodcaw)
->json();
$cawangan_detail = (is_array($cawanganResp) && isset($cawanganResp[0]))
? $cawanganResp[0]
: (is_array($cawanganResp) ? $cawanganResp : []);
$cawangan = $cawangan_detail['namacaw']
?? $cawangan_detail['details_caw']
?? $cawangan_detail['nama']
?? $kodcaw;
$alamat1 = $cawangan_detail['alamat1'] ?? '';
$alamat2 = $cawangan_detail['alamat2'] ?? '';
$notel = $cawangan_detail['notel'] ?? '';
$batchNo = $batch['batch_no'] ?? '-';
$slot = $batch['slot'] ?? '-';
$status = $batch['status'] ?? '-';
$dihantarOleh = $batch['pc_submitted_by'] ?? '-';
$jumlah = (float) ($batch['jumlah_keseluruhan'] ?? 0);
if ($jumlah <= 0 && count($lines) > 0) {
$jumlah = (float) collect($lines)->sum(function ($line) {
return (float) ($line['pinjaman'] ?? 0);
});
}
$opsBy = Auth::user()['name'] ?? '';
foreach ($lines as $line) {
if (!empty($line['ops_by'])) {
$opsBy = $line['ops_by'];
break;
}
}
$tarikh = $this->formatPrintDate($batch['tarikh'] ?? null);
$dihantarPada = $this->formatPrintDate($batch['submitted_at'] ?? null, true);
return view('print.borang_batch_pembiayaan_online', compact(
'cawangan',
'kodcaw',
'alamat1',
'alamat2',
'notel',
'tarikh',
'batchNo',
'slot',
'status',
'dihantarOleh',
'dihantarPada',
'opsBy',
'lines',
'jumlah'
));
}
protected function formatPrintDate($value, $withTime = false)
{
if ($value === null || $value === '' || $value === '-') {
return '-';
}
try {
return Carbon::parse($value)->format($withTime ? 'd/m/Y H:i' : 'd/m/Y');
} catch (\Exception $e) {
return (string) $value;
}
}
public function operasiApproveLine(Request $request, $kodcaw, $id)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/approve', [
'ops_by' => Auth::user()['name'],
]);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod diluluskan.');
}
public function operasiRejectLine(Request $request, $kodcaw, $id)
{
$reason = trim((string) $request->input('reject_reason', ''));
if ($reason === '') {
return redirect()->back()->with('error', 'Sebab tolak diperlukan.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/reject', [
'ops_by' => Auth::user()['name'],
'reject_reason' => $reason,
]);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditolak. Dikembalikan ke PC.');
}
public function operasiApproveBatch(Request $request, $kodcaw, $batchId)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/approve', [
'ops_by' => Auth::user()['name'],
]);
return $this->opsRedirect($res, $kodcaw, $batchId, 'Semua rekod menunggu dalam batch diluluskan.');
}
public function operasiMarkPaid(Request $request, $kodcaw, $id)
{
$payload = $this->buildPaidPayload($request);
if (isset($payload['error'])) {
return redirect()->back()->with('error', $payload['error']);
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/lines/' . $id . '/paid', $payload);
return $this->opsRedirect($res, $kodcaw, $request->input('batch_id'), 'Rekod ditanda PAID.');
}
public function operasiMarkPaidBatch(Request $request, $kodcaw, $batchId)
{
$payload = $this->buildPaidPayload($request);
if (isset($payload['error'])) {
return redirect()->back()->with('error', $payload['error']);
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/pembiayaan-online/operasi/' . $kodcaw . '/batches/' . $batchId . '/paid', $payload);
return $this->opsRedirect($res, $kodcaw, $batchId, 'Rekod APPROVED dalam batch ditanda PAID.');
}
/**
* @return array
*/
protected function buildPaidPayload(Request $request)
{
$noRujukan = trim((string) $request->input('no_rujukan_bayaran', ''));
if ($noRujukan === '') {
return ['error' => 'No. rujukan bayaran diperlukan.'];
}
if (!$request->hasFile('bukti')) {
return ['error' => 'Fail bukti bayaran diperlukan.'];
}
$file = $request->file('bukti');
if (!$file->isValid()) {
return ['error' => 'Fail bukti tidak sah.'];
}
$raw = base64_encode(file_get_contents($file->getRealPath()));
$mime = $file->getMimeType() ?: 'application/octet-stream';
return [
'ops_by' => Auth::user()['name'],
'no_rujukan_bayaran' => $noRujukan,
'tarikh_bayaran' => $request->input('tarikh_bayaran') ?: date('Y-m-d'),
'bukti_data' => $raw,
'bukti_filename' => $file->getClientOriginalName(),
'bukti_type' => $mime,
];
}
protected function opsRedirect($res, $kodcaw, $batchId, $successMsg)
{
$batchId = $batchId ?: null;
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Tindakan gagal.';
return redirect()->back()->with('error', $msg);
}
if ($batchId) {
return redirect()
->route('pembiayaan.online.operasi.batch', ['kodcaw' => $kodcaw, 'batchId' => $batchId])
->with('message', $successMsg);
}
return redirect()->route('pembiayaan.online.operasi')->with('message', $successMsg);
}
/**
* PC: modal online float + top-up request form/history.
*/
public function topupIndex(Request $request)
{
$auth_user = Auth::user();
$kodcaw = $this->kodcaw();
$api_url = config('api.url');
$status = $request->input('status', '');
$cawangan_info = $this->api()
->get($api_url . '/api/cawangan/' . $kodcaw)
->json();
$modal = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw)
->json();
$query = [];
if ($status !== '' && $status !== 'all') {
$query['status'] = $status;
}
$reqRes = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', $query)
->json();
$requests = $reqRes['data'] ?? [];
if (!is_array($requests)) {
$requests = [];
}
$pendingRes = $this->api()
->get($api_url . '/api/modal-online/' . $kodcaw . '/topup/requests', ['status' => 'PENDING'])
->json();
$pendingList = $pendingRes['data'] ?? [];
$hasPending = is_array($pendingList) && count($pendingList) > 0;
$isMissing = !empty($modal['missing']);
$pendingRequest = $modal['pending_request'] ?? null;
if (!$pendingRequest && $hasPending) {
$pendingRequest = $pendingList[0];
}
return view('pembiayaan_online.pc.topup', compact(
'auth_user',
'kodcaw',
'api_url',
'cawangan_info',
'modal',
'requests',
'status',
'hasPending',
'isMissing',
'pendingRequest'
));
}
/**
* PC: submit top-up request.
*/
public function topupRequest(Request $request)
{
$kodcaw = $this->kodcaw();
$amaun = (float) $request->input('amaun', 0);
if ($amaun <= 0) {
return redirect()->back()->with('error', 'Amaun mesti lebih daripada 0.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/topup/request', [
'amaun' => $amaun,
'dimohon_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal hantar permohonan.';
return redirect()->back()->with('error', $msg)->withInput();
}
return redirect()
->route('pembiayaan.online.pc.topup')
->with('message', 'Permohonan modal online dihantar. Menunggu kelulusan Operasi.');
}
/**
* Operasi: senarai permohonan tambah modal online.
*/
public function operasiTopupIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$status = $request->input('status', 'PENDING');
$tarikh = $request->input('tarikh');
$query = [];
if ($status === 'all') {
$query['status'] = 'all';
} elseif ($status !== '' && $status !== null) {
$query['status'] = $status;
} else {
$query['status'] = 'PENDING';
}
if ($tarikh) {
$query['tarikh'] = $tarikh;
}
$res = $this->api()
->get($api_url . '/api/modal-online/operasi/topup/requests', $query)
->json();
$requests = $res['data'] ?? [];
if (!is_array($requests)) {
$requests = [];
}
return view('pembiayaan_online.operasi.topup', compact(
'auth_user',
'api_url',
'requests',
'status',
'tarikh'
));
}
public function operasiTopupApprove(Request $request, $id)
{
$amaun = $request->input('amaun');
if ($amaun !== null && $amaun !== '' && (float) $amaun <= 0) {
return redirect()->back()->with('error', 'Amaun diluluskan mesti lebih daripada 0.');
}
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/approve', [
'diluluskan_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
'amaun' => $amaun,
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal lulus permohonan.';
return redirect()->back()->with('error', $msg);
}
$amaunLulus = $res->json()['request']['amaun'] ?? $amaun;
return redirect()
->route('pembiayaan.online.operasi.topup')
->with('message', 'Permohonan diluluskan RM ' . number_format((float) $amaunLulus, 2) . '. Modal online cawangan dikemas kini.');
}
public function operasiTopupReject(Request $request, $id)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/topup/' . $id . '/reject', [
'diluluskan_oleh' => Auth::user()['name'],
'nota' => $request->input('nota'),
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tolak permohonan.';
return redirect()->back()->with('error', $msg);
}
return redirect()
->route('pembiayaan.online.operasi.topup')
->with('message', 'Permohonan ditolak.');
}
/**
* PC: EOD close branch modal online.
*/
public function closeModal(Request $request)
{
$kodcaw = $this->kodcaw();
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
'ditutup_oleh' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
return redirect()->back()->with('error', $msg);
}
$dikembalikan = $res->json()['dikembalikan'] ?? 0;
return redirect()
->route('pembiayaan.online.pc.topup')
->with('message', 'Modal online ditutup. Baki dikembalikan RM ' . number_format((float) $dikembalikan, 2) . '.');
}
/**
* Operasi: EOD dashboard semua cawangan.
*/
public function operasiEodIndex(Request $request)
{
$auth_user = Auth::user();
$api_url = config('api.url');
$tarikh = $request->input('tarikh') ?: date('Y-m-d');
$res = $this->api()
->get($api_url . '/api/modal-online/operasi/eod', [
'zone' => 'all',
'tarikh' => $tarikh,
])
->json();
$rows = $res['data'] ?? [];
if (!is_array($rows)) {
$rows = [];
}
$summary = [
'open' => $res['open'] ?? 0,
'closed' => $res['closed'] ?? 0,
'missing' => $res['missing'] ?? 0,
'bilangan' => $res['bilangan'] ?? count($rows),
];
return view('pembiayaan_online.operasi.eod', compact(
'auth_user',
'api_url',
'rows',
'tarikh',
'summary'
));
}
public function operasiCloseModal(Request $request, $kodcaw)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/' . $kodcaw . '/close', [
'ditutup_oleh' => Auth::user()['name'],
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup modal online.';
return redirect()->back()->with('error', $msg);
}
return redirect()
->route('pembiayaan.online.operasi.eod')
->with('message', 'Modal online ' . $kodcaw . ' ditutup.');
}
public function operasiCloseAllModal(Request $request)
{
$res = $this->api()
->asForm()
->post(config('api.url') . '/api/modal-online/operasi/eod/close-all', [
'ditutup_oleh' => Auth::user()['name'],
'zone' => 'all',
]);
if (!$res->successful()) {
$body = $res->json();
$msg = is_array($body) && isset($body['message']) ? $body['message'] : 'Gagal tutup semua modal online.';
return redirect()->back()->with('error', $msg);
}
$closed = $res->json()['closed'] ?? 0;
$skipped = $res->json()['skipped'] ?? 0;
return redirect()
->route('pembiayaan.online.operasi.eod')
->with('message', 'EOD selesai: ' . $closed . ' ditutup, ' . $skipped . ' sudah ditutup.');
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ class TrustProxies extends Middleware
*
* @var array|string|null
*/
protected $proxies;
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.