Tambahkan fitur pembayaran dan perbaikan tampilan terkait

- Tambah file migration untuk update tabel `persetujuan_penawaran` dengan relasi baru.
- Tambah fungsi baru di controller pembayaran: `edit`, `store`, `update`, dan `approval`.
- Perbarui view daftar pembayaran dan tambahkan form pembayaran baru.
- Tambah endpoint dan breadcrumbs untuk fitur pembayaran di routes.
This commit is contained in:
Daeng Deni Mardaeni
2024-12-23 20:12:56 +07:00
parent bbe44ff3fa
commit c90535b5ba
6 changed files with 317 additions and 161 deletions

View File

@@ -9,8 +9,13 @@
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel; use Maatwebsite\Excel\Facades\Excel;
// use Modules\Lpj\Models\JenisPenilaian; use Modules\Lpj\Http\Requests\PersetujuanPenawaranRequest;
use Modules\Lpj\Models\PenawaranTender;
use Modules\Lpj\Models\Permohonan; use Modules\Lpj\Models\Permohonan;
use Modules\Lpj\Models\PersetujuanPenawaran;
// use Modules\Lpj\Models\JenisPenilaian;
// use Modules\Lpj\Models\Regions; // use Modules\Lpj\Models\Regions;
class PembayaranController extends Controller class PembayaranController extends Controller
@@ -22,27 +27,108 @@
return view('lpj::pembayaran.index'); return view('lpj::pembayaran.index');
} }
public function approval() {}
public function dataApprovalForDatatables(Request $request) {}
function edit($id)
{
$permohonan = Permohonan::find($id);
return view('lpj::pembayaran.form', compact('permohonan'));
}
public function store(PersetujuanPenawaranRequest $request)
{
$validated = $request->validated();
$validated['created_by'] = Auth::id();
$validated['status'] = '0';
if (isset($validated['penawaran_id'])) {
$persetujuanPenawaran = PersetujuanPenawaran::create(
['penawaran_id' => $validated['penawaran_id']],
$validated,
);
} else {
$persetujuanPenawaran = PersetujuanPenawaran::create(
$validated
);
}
$folderPath = 'persetujuan_penawaran/' . $validated['penawaran_id'];
if ($request->hasFile('bukti_bayar')) {
$persetujuanPenawaran->bukti_bayar = $request->file('bukti_bayar')->store($folderPath, 'public');
}
$persetujuanPenawaran->save();
// Update the status of the related permohonan to 'spk'
$permohonan = Permohonan::find(request()->get('permohonan_id'));
if ($permohonan) {
$permohonan->status_bayar = request()->get('status_bayar');
$permohonan->status = 'noc';
$permohonan->save();
// andy add, update status penawaran.status='spk'
// $penawaran = PenawaranTender::where('nomor_registrasi',$permohonan->nomor_registrasi)->first();
PenawaranTender::where('nomor_registrasi', $permohonan->nomor_registrasi)->update([
'status' => 'noc',
'updated_by' => Auth::id(),
'updated_at' => now(),
]);
// andy add, update status penawaran.status='spk'
}
return redirect()
->route('pembayaran.index')->with('success', 'Pembayaran berhasil disimpan.');
}
public function update(Request $request, $id)
: JsonResponse {
// init
$data = [];
$dataku = [];
$tindakan = null;
if (request()->ajax()) {
try {
$dataku = [
'approve_bayar_by' => Auth::id(),
'approve_bayar_at' => now(),
];
if ($request->keterangan) {
$dataku['approve_keterangan_bayar'] = $request->keterangan;
}
$data['dataku'] = $dataku;
$modal = Permohonan::find($id);
$modal->update($dataku);
//
$data['status'] = 'success';
$data['message'] = ['Otorisasi' . $modal->nomor_registrasi . 'berhasil di lakukan'];
} catch (Exception $e) {
$data['status'] = 'error';
$data['message'] = ['Otorisasi gagal di lakukan.'];
}
}
return response()->json($data);
}
public function dataForDatatables(Request $request) public function dataForDatatables(Request $request)
{ {
if (is_null($this->user) || !$this->user->can('debitur.view')) { if (is_null($this->user) || !$this->user->can('debitur.view')) {
// abort(403, 'Sorry! You are not allowed to view users.'); // abort(403, 'Sorry! You are not allowed to view users.');
} }
$query = Permohonan::query()->where(['status_bayar' => 'belum_bayar', 'jenis_penilaian_id' => 1])
->whereNotIn('id', function($subquery) {
$subquery->select('permohonan_id')
->from('persetujuan_penawaran')
->whereNotNull('permohonan_id');
});
/*
// Tentukan status berdasarkan otorisator
$status = match ($otorisator) {
'Pelaporan' => 'proses-laporan',
'Pembayaran' => 'proses',
'Pembatalan' => 'batal',
'SLA' => 'freeze',
default => '',
};
*/
$query = Permohonan::query()->whereIn('status_bayar', ['sudah_bayar']);
/*
// Pencarian berdasarkan parameter search // Pencarian berdasarkan parameter search
if ($request->has('search') && !empty($request->get('search'))) { if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search'); $search = $request->get('search');
@@ -51,18 +137,11 @@
$q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%'); $q->orWhere('tanggal_permohonan', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('user', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('debiture', 'name', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('debiture', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('tujuanPenilaian', 'name', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('jenisPenilaian', 'name', 'LIKE', '%' . $search . '%');
$q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%'); $q->orWhereRelation('branch', 'name', 'LIKE', '%' . $search . '%');
$q->orWhere('status', 'LIKE', '%' . $search . '%'); $q->orWhere('status', 'LIKE', '%' . $search . '%');
}); });
} }
*/
// Filter berdasarkan region user yang login
// $query->whereHas('region.teams.teamsUsers', function ($q) {
// $q->where('user_id', Auth::id());
// });
// Sorting berdasarkan sortField dan sortOrder // Sorting berdasarkan sortField dan sortOrder
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
@@ -91,7 +170,7 @@
$filteredRecords = $query->count(); $filteredRecords = $query->count();
// Ambil data dengan relasi // Ambil data dengan relasi
$data = $query->with(['user', 'debiture', 'branch', 'tujuanPenilaian', 'region.teams.teamsUsers'])->get(); $data = $query->with(['user', 'debiture', 'branch', 'jenisPenilaian'])->get();
// Hitung jumlah halaman // Hitung jumlah halaman
@@ -110,41 +189,5 @@
'totalCount' => $totalRecords, 'totalCount' => $totalRecords,
'data' => $data, 'data' => $data,
]); ]);
} }
public function update(Request $request, $id)
: JsonResponse {
// init
$data = [];
$dataku = [];
$tindakan = null;
if (request()->ajax()) {
try {
$dataku = [
'approve_bayar_by' => Auth::id(),
'approve_bayar_at' => now(),
];
if ($request->keterangan) {
$dataku['approve_keterangan_bayar'] = $request->keterangan;
} }
$data['dataku'] = $dataku;
$modal = Permohonan::find($id);
$modal->update($dataku);
//
$data['status'] = 'success';
$data['message'] = ['Otorisasi' . $modal->nomor_registrasi . 'berhasil di lakukan'];
} catch (Exception $e) {
$data['status'] = 'error';
$data['message'] = ['Otorisasi gagal di lakukan.'];
}
}
return response()->json($data);
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('persetujuan_penawaran', function (Blueprint $table) {
$table->unsignedBigInteger('penawaran_id')->change()->nullable();
$table->unsignedBigInteger('permohonan_id')->nullable();
$table->foreign('permohonan_id')->references('id')->on('permohonan')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('persetujuan_penawaran', function (Blueprint $table) {
$table->unsignedBigInteger('penawaran_id')->change()->nullable(false);
$table->dropForeign('persetujuan_penawaran_permohonan_id_foreign');
$table->dropColumn('permohonan_id');
});
}
};

View File

@@ -0,0 +1,108 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render(request()->route()->getName()) }}
@endsection
@section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<div class="card pb-2.5">
<div class="card-header" id="basic_settings">
<div class="card-title flex flex-row gap-1.5">
Pembayaran
</div>
<div class="flex items-center gap-2">
<a href="{{ route('pembayaran.index') }}" class="btn btn-xs btn-info"><i class="ki-filled ki-exit-left"></i> Back</a>
</div>
</div>
<div class="card-body">
<form action="{{ route('pembayaran.store') }}" method="POST" class="grid gap-5" enctype="multipart/form-data">
@csrf
<input type="hidden" name="permohonan_id" value="{{ $permohonan->id }}">
<input type="hidden" name="penawaran_id" value="{{ $permohonan->penawaran->id ?? "" }}">
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Nomor Registrasi
</label>
<div class="flex flex-wrap items-baseline w-full">
<input readonly type="text" name="nomor_registrasi" id="nomor_registrasi" class="input w-full @error('nomor_registrasi') border-danger bg-danger-light @enderror" value="{{ old('nomor_registrasi', $permohonan->nomor_registrasi ?? '') }}" placeholder="Nomor Registrasi">
@error('nomor_registrasi')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Debitur
</label>
<div class="flex flex-wrap items-baseline w-full">
<input readonly type="text" name="debitur" id="debitur" class="input w-full @error('debitur') border-danger bg-danger-light @enderror" value="{{ old('debitur', $permohonan->debiture->name ?? '') }}" placeholder="Debitur">
@error('debitur')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 hidden">
<label class="form-label max-w-56">
Status Bayar
</label>
<div class="flex flex-wrap items-baseline w-full">
<select class="input tomselect w-full @error('status_bayar') border-danger bg-danger-light @enderror" name="status_bayar" id="status_bayar">
<option value="">Pilih Status Bayar</option>
<option value="sudah_bayar" {{ (old('status_bayar') == 'sudah_bayar') || (isset($permohonan) && $permohonan->status_bayar == 'sudah_bayar') ? 'selected' : '' }}>Sudah Bayar</option>
<option value="belum_bayar" {{ (old('status_bayar') == 'belum_bayar') || (isset($permohonan) && $permohonan->status_bayar == 'belum_bayar') ? 'selected' : '' }}>Belum Bayar</option>
</select>
@error('status_bayar')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Nominal Bayar
</label>
<div class="flex flex-wrap items-baseline w-full">
<input type="number" name="nominal_bayar" id="nominal_bayar" class="input w-full @error('nominal_bayar') border-danger bg-danger-light @enderror" value="{{ old('nominal_bayar', $persetujuanPenawaran->nominal_bayar ?? '') }}" placeholder="Masukkan nominal bayar">
@error('nominal_bayar')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Bukti Bayar
</label>
<div class="flex flex-wrap items-baseline w-full">
<input type="file" name="bukti_bayar" id="bukti_bayar" class="file-input w-full @error('bukti_bayar') border-danger bg-danger-light @enderror" accept=".pdf,.jpg,.jpeg,.png">
@error('bukti_bayar')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Catatan
</label>
<div class="flex flex-wrap items-baseline w-full">
<textarea name="catatan" id="catatan" rows="4" class="textarea w-full @error('catatan') border-danger bg-danger-light @enderror" placeholder="Masukkan catatan">{{ old('catatan', $persetujuanPenawaran->catatan ?? '') }}</textarea>
@error('catatan')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
Proses
</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@@ -31,7 +31,7 @@
<thead> <thead>
<tr> <tr>
<th class="w-14"> <th class="w-14">
<input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox" /> <input class="checkbox checkbox-sm" data-datatable-check="true" type="checkbox"/>
</th> </th>
<th class="min-w-[150px]" data-datatable-column="nomor_registrasi"> <th class="min-w-[150px]" data-datatable-column="nomor_registrasi">
<span class="sort"> <span class="sort-label"> Nomor Registrasi </span> <span class="sort"> <span class="sort-label"> Nomor Registrasi </span>
@@ -53,16 +53,6 @@
<span class="sort"> <span class="sort-label"> Debitur </span> <span class="sort"> <span class="sort-label"> Debitur </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
</th> </th>
<th class="min-w-[150px]" data-datatable-column="tujuan_penilaian_id">
<span class="sort"> <span class="sort-label"> Tujuan Penilaian </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px] text-center" data-datatable-column="approve_bayar_by">
<span class="sort"> <span class="sort-label"> Status Approve </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px] text-center" data-datatable-column="tujuan_penilaian_id"> <th class="min-w-[150px] text-center" data-datatable-column="tujuan_penilaian_id">
<span class="sort"> <span class="sort-label"> Status Bayar </span> <span class="sort"> <span class="sort-label"> Status Bayar </span>
<span class="sort-icon"> </span> </span> <span class="sort-icon"> </span> </span>
@@ -93,8 +83,7 @@
@push('scripts') @push('scripts')
<script type="text/javascript"> <script type="text/javascript">
function pembayaranOtorisator(id) function pembayaranOtorisator(id) {
{
// alert('hai id = ' + id); // alert('hai id = ' + id);
Swal.fire({ Swal.fire({
title: 'Apakah Anda yakin?', title: 'Apakah Anda yakin?',
@@ -114,12 +103,12 @@
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
let useURL= "{{ URL::to('pembayaran') }}"+"/"+id; let useURL = "{{ URL::to('pembayaran') }}" + "/" + id;
const keterangan = result.value || ''; // Ambil pesan dari textarea const keterangan = result.value || ''; // Ambil pesan dari textarea
var input_data = new Object(); var input_data = new Object();
input_data._method= 'PUT'; input_data._method = 'PUT';
input_data.id= id; input_data.id = id;
input_data.keterangan = keterangan; input_data.keterangan = keterangan;
$.ajaxSetup({ $.ajaxSetup({
@@ -133,14 +122,11 @@
data: input_data, data: input_data,
dataType: "json", dataType: "json",
success: (response) => { success: (response) => {
if('success' == response.status) if ('success' == response.status) {
{
Swal.fire('Berhasil!', response.message, 'success').then(() => { Swal.fire('Berhasil!', response.message, 'success').then(() => {
window.location.reload(); window.location.reload();
}); });
} } else if ('error' == response.status) {
else if('error' == response.status)
{
Swal.fire('Error!', response.message, 'error').then(() => { Swal.fire('Error!', response.message, 'error').then(() => {
window.location.reload(); window.location.reload();
}); });
@@ -192,7 +178,10 @@
branch_id: { branch_id: {
title: 'Cabang Pemohon', title: 'Cabang Pemohon',
render: (item, data) => { render: (item, data) => {
if (data.branch) {
return `${data.branch.name}`; return `${data.branch.name}`;
}
return '-';
}, },
}, },
debitur_id: { debitur_id: {
@@ -201,23 +190,6 @@
return `${data.debiture.name}`; return `${data.debiture.name}`;
}, },
}, },
tujuan_penilaian_id: {
title: 'Tujuan Penilaian',
render: (item, data) => {
return `${data.tujuan_penilaian.name}`;
},
},
approve_bayar_by: {
title: 'Status Approve',
render: (item, data) => {
var status_bayar='';
if(data.approve_bayar_by)
status_bayar=`<span class="text-md font-bold text-green-600 uppercase">CLEAR</span>`;
// return `${data.tujuan_penilaian.name}`;
return status_bayar;
},
},
status_bayar: { status_bayar: {
title: 'Status Bayar', title: 'Status Bayar',
render: (item, data) => { render: (item, data) => {
@@ -233,21 +205,11 @@
actions: { actions: {
title: 'Status', title: 'Status',
render: (item, data) => { render: (item, data) => {
var iconPembayaranOtorisator =''; return `<div class="flex justify-center gap-2">
if(!data.approve_bayar_by) <a class="btn btn-icon btn-clear btn-warning " href="pembayaran/${data.id}/edit" title="Lakukan Pembayaran">
{ <i class="ki-outline ki-credit-cart"></i>
iconPembayaranOtorisator = `<a class="btn btn-sm btn-icon btn-clear btn-primary " onclick="pembayaranOtorisator(${data.id})"> </a>
<i class="ki-filled ki-double-check"></i>
</a>`;
}
return `<div class="flex flex-nowrap justify-center">
<a class="btn btn-sm btn-icon btn-clear btn-warning " href="#">
<i class="ki-outline ki-eye"></i>
</a>`
+iconPembayaranOtorisator+`
</div>`; </div>`;
}, },
} }
}, },
@@ -255,7 +217,7 @@
let dataTable = new KTDataTable(element, dataTableOptions); let dataTable = new KTDataTable(element, dataTableOptions);
// Custom search functionality // Custom search functionality
searchInput.addEventListener('input', function() { searchInput.addEventListener('input', function () {
const searchValue = this.value.trim(); const searchValue = this.value.trim();
dataTable.search(searchValue, true); dataTable.search(searchValue, true);

View File

@@ -108,4 +108,9 @@ Breadcrumbs::for('registrasifinal.edit', function (BreadcrumbTrail $trail) {
Breadcrumbs::for('pembayaran', function (BreadcrumbTrail $trail) { Breadcrumbs::for('pembayaran', function (BreadcrumbTrail $trail) {
$trail->push('Pembayaran', route('pembayaran.index')); $trail->push('Pembayaran', route('pembayaran.index'));
}); });
Breadcrumbs::for('pembayaran.edit', function (BreadcrumbTrail $trail) {
$trail->parent('pembayaran');
$trail->push('Lakukan Pembayaran');
});
// pembayaran // pembayaran

View File

@@ -128,8 +128,13 @@ Route::middleware(['auth'])->group(function () {
Route::controller(PembayaranController::class)->group(function () { Route::controller(PembayaranController::class)->group(function () {
Route::get('/pembayaran', 'index')->name('pembayaran.index'); Route::get('/pembayaran', 'index')->name('pembayaran.index');
Route::get('/pembayaran/{pembayaran}/edit', 'edit')->name('pembayaran.edit');
Route::post('pembayaran','store')->name('pembayaran.store');
Route::get('/pembayaran/datatables', 'dataForDatatables')->name('pembayaran.datatables'); Route::get('/pembayaran/datatables', 'dataForDatatables')->name('pembayaran.datatables');
Route::put('/pembayaran/{pembayaran}', 'update')->name('pembayaran.update'); Route::put('/pembayaran/{pembayaran}', 'update')->name('pembayaran.update');
Route::get('/pembayaran/approval', 'approval')->name('pembayaran.approval.index');
Route::get('/pembayaran/datatables-approval', 'dataApprovalForDatatables')->name('pembayaran.approval.datatables');
}); });
}); });