Merge remote-tracking branch 'origin/staging' into origin/production

This commit is contained in:
Linksehat Staging Server
2024-03-05 13:41:28 +07:00
41 changed files with 3431 additions and 2144 deletions

View File

@@ -316,8 +316,8 @@ class ClaimController extends Controller
// ->where('request_logs.status_final_log', '=', 'approved')
->where('request_logs.deleted_at', '=', null)
->when($start != 'all' && $end != 'all', function ($query) use ($start, $end) {
$query->where('request_logs.submission_date', '>=', $start)
->where('request_logs.submission_date', '<=', Carbon::parse($end)->addDay());
$query->where('request_logs.submission_date', '>=',Carbon::parse($start)->subDay())
->where('request_logs.submission_date', '<=',Carbon::parse($end)->addDay());
})
->select(
DB::raw('1 AS no'),

View File

@@ -19,7 +19,7 @@ class ClaimRequestController extends Controller
* Display a listing of the resource.
* @return Renderable
*/
private static $code_prefix = 'CP';
private static $code_prefix = 'CLAIM';
public function index()
{
return view('client::index');
@@ -224,10 +224,6 @@ class ClaimRequestController extends Controller
public static function getNextCode()
{
// $last_number = ClaimRequest::max('code');
// $next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[2] + 1);
// return self::makeCode($next_number);
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, "-", -1) AS SIGNED)) as max_numeric_code'))
->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code');
@@ -247,8 +243,14 @@ class ClaimRequestController extends Controller
{
// Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number);
$requestLogData = RequestLog::where('id', $request_log_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
$sparator = '.';
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 6, '0', STR_PAD_LEFT);
}
}

View File

@@ -229,7 +229,7 @@ class CorporateMemberController extends Controller
'service:code,name',
'files',
])
->find($request_log_id, ['id', 'submission_date', 'discharge_date', 'member_id', 'service_code', 'organization_id', 'diagnosis']);
->find($request_log_id, ['id', 'submission_date', 'discharge_date', 'member_id', 'service_code', 'organization_id', 'diagnosis', 'keterangan', 'catatan']);
$dataBenefit = [];
if (count($data->requestLogBenefits) > 0) {

View File

@@ -105,6 +105,8 @@ class DataServiceMonitoring extends JsonResource
'name' => $requestLogBenefit->benefit->description,
];
})->all() ?? null,
'keterangan' => $this->keterangan ?? null,
'catatan' => $this->catatan ?? null,
'benefitTotal' => $this->benefitTotal ?? null,
'hospital' => $this->organization->name ?? null,
'admissionDate' => $this->submission_date ?? null,

View File

@@ -133,10 +133,21 @@ class ClaimRequestController extends Controller
if ($request->hasFile('additional_files')) {
foreach ($request->additional_files as $file) {
$pathFile = File::storeFile('additional-files', $newClaimRequest->id, $file);
$newClaimRequest->files()->updateOrCreate([
$pathFile = File::storeFile('additional-files', $request->request_logs_id, $file);
// $newClaimRequest->files()->updateOrCreate([
// 'type' => 'additional-files',
// 'name' => File::getFileName('additional-files', $newClaimRequest->id, $file),
// 'original_name' => $file->getClientOriginalName(),
// 'extension' => $file->getClientOriginalExtension(),
// 'path' => $pathFile,
// 'created_by' => auth()->user()->id,
// 'updated_by' => auth()->user()->id,
// ]);
File::updateOrCreate([
'fileable_type' => 'App\Models\RequestLog',
'fileable_id' => $request->request_logs_id,
'type' => 'additional-files',
'name' => File::getFileName('additional-files', $newClaimRequest->id, $file),
'name' => File::getFileName('additional-files', $request->request_logs_id, $file),
'original_name' => $file->getClientOriginalName(),
'extension' => $file->getClientOriginalExtension(),
'path' => $pathFile,
@@ -265,7 +276,6 @@ class ClaimRequestController extends Controller
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT);
}
public function get_claim_requests(Request $request)

View File

@@ -13,6 +13,11 @@ use App\Models\File;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Support\Facades\View;
use App\Models\Member;
use App\Models\RequestLog;
use App\Models\Organization;
use App\Services\ClaimRequestService;
use App\Models\ClaimRequest;
class RequestLogController extends Controller
{
@@ -20,6 +25,7 @@ class RequestLogController extends Controller
* Display a listing of the resource.
* @return Renderable
*/
private static $code_prefix = 'CLAIM';
public function requestLog(Request $request)
{
$data = [
@@ -244,6 +250,7 @@ class RequestLogController extends Controller
$results = DB::table('request_logs')
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
->where('request_logs.deleted_at', null)
->when($request->input('search'), function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->orWhere('request_logs.code', 'like', "%" . $search . "%")
@@ -820,4 +827,110 @@ class RequestLogController extends Controller
return response($pdf->output(), 200, $headers);
}
public function submitClaims(Request $request)
{
$data = [
'selectedRows' => $request->selectedRows,
];
$validator = Validator::make($request->all(), [
'selectedRows' => 'required'
], [
'selectedRows.required' => trans('Validation.required',['attribute' => 'Request Logs ID']),
]);
if ($validator->fails())
{
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
}
else
{
foreach ($request->selectedRows as $request_logs_id) {
$data_req_logs = DB::table('request_logs')
->where('id', '=', $request_logs_id)
->select('id', 'member_id', 'service_code')
->first();
$check_claim_requests = DB::table('claim_requests')
->where('claim_requests.request_log_id', '=', $request_logs_id)
->first();
if(!$check_claim_requests) {
try {
DB::beginTransaction();
$code = $this->getNextCode($request_logs_id);
$member = Member::find($data_req_logs->member_id);
$requestLogData = RequestLog::where('id',$request_logs_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$newClaimRequest = ClaimRequestService::storeClaimRequest(
row: [],
code: $code,
member: $member,
paymentType: 'cashless',
serviceCode: $data_req_logs->service_code,
requestLogID: $request_logs_id,
organization_code: $provideCode,
);
// Log History
$newClaimRequest->histories()->create([
'title' => 'New Claim Requested',
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'type' => 'info',
'system_origin' => 'hospital-portal'
]);
// Claim Log
DB::table('claim_logs')
->insert([
'claim_request_id' => $newClaimRequest->id,
'status' => 'requested',
'date' => date('Y-m-d H:i:s'),
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'system_origin' => 'hospital-portal',
'created_by' => auth()->user()->id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at'=> date('Y-m-d H:i:s'),
]);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500);
}
}
}
return ApiResponse::apiResponse('Success', $data, trans('Message.success'), 200);
}
}
public static function getNextCode($request_log_id = 0)
{
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code');
// $next_number = 1;
if ($last_numeric_code) {
// // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir
// $parts = explode('-', $last_code);
// $last_number = (int) end($parts);
$next_number = $last_numeric_code + 1;
} else {
$next_number = 1;
}
return self::makeCode($next_number, $request_log_id);
}
public static function makeCode($next_number, $request_log_id)
{
// Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number);
$requestLogData = RequestLog::where('id', $request_log_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
$sparator = '.';
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT);
}
}

View File

@@ -56,6 +56,7 @@ Route::prefix('v1')->group(function() {
Route::post('request-final-log', 'requestFinalLog');
Route::get('download-log/{request_log_id}', 'downlodLog');
Route::get('download-final-log/{request_log_id}', 'downlodFinalLog');
Route::post('submit-claims', 'submitClaims');
});
//Notification
Route::controller(NotificationController::class)->group(function() {

View File

@@ -21,8 +21,18 @@ use Modules\Internal\Transformers\ClaimEditResource;
use Modules\Internal\Transformers\ClaimHistoryCareResource;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
use Box\Spout\Common\Entity\Style\CellAlignment;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\View;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Facades\Storage;
use App\Exports\FailedRowsExport; // Import class export Excel
use Modules\Internal\Transformers\RequestLogResource;
use App\Models\RequestLog;
use PDF;
@@ -34,41 +44,436 @@ class ClaimController extends Controller
*/
public function index(Request $request)
{
// $serviceCode = 'IP';
$claims = Claim::with([
'member',
'member.currentCorporate',
'member.currentCorporate.currentPolicy',
// 'member.currentPlan' => function($memberPlan) use ($serviceCode) {
// $memberPlan->where('plans.service_code', $serviceCode);
// },
'member.currentPlan' => function($memberPlan) {
$memberPlan->join('claim_requests', 'claim_requests.service_code', '=', 'plans.service_code');
},
'diagnoses' => function ($diagnosis) {
$diagnosis->where('type', 'primary');
},
'diagnoses.icd',
'benefit',
'claimRequest',
'claimRequest.service',
])
->when($request->search, function ($q, $search) {
$q->where(function ($subQuery) use ($search) {
$subQuery->whereHas('claimRequest', function ($claimRequest) use ($search) {
$claimRequest->where('code', 'LIKE', "%" . $search . "%");
})
->orWhereHas('member', function ($member) use ($search) {
$member->where('name', 'LIKE', "%" . $search . "%");
});
$limit = $request->has('per_page') ? $request->input('per_page') : 10;
$results = DB::table('claim_requests')
->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
// ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
->when($request->input('search'), function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->orWhere('members.name', 'like', "%" . $search . "%");
$query->orWhere('claim_requests.code', 'like', "%" . $search . "%");
$query->orWhere('members.member_id', 'like', "%" . $search . "%");
});
})
->where('status', '!=', 'requested') // Penjagaan agar approve baru masuk ke claim management
->latest()
->paginate(10);
->when($request->has('orderBy'), function ($query) use ($request) {
$orderBy = $request->orderBy;
$direction = $request->order ?? 'asc';
$query->orderBy($orderBy, $direction);
})
->when($request->input('start_date') , function ($query, $start_date) {
$query->where(function ($query) use ($start_date) {
$query->where('claim_requests.created_at', '>=', $start_date);
});
})
->when($request->input('end_date') , function ($query, $end_date) {
$query->where(function ($query) use ($end_date) {
$query->where('claim_requests.created_at', '<=', $end_date);
});
})
->when($request->input('provider') , function ($query, $provider) {
$query->where(function ($query) use ($provider) {
$query->where('request_logs.organization_id', '=', $provider);
});
})
->where('claim_management', '=', 1)
->select(
'claim_requests.id',
'request_logs.id AS id_log',
'claim_requests.code as code',
'members.name',
DB::raw('
(SELECT members.member_id FROM members WHERE members.id = claim_requests.member_id LIMIT 1) AS member_id
'),
'claim_requests.created_at',
// DB::raw('
// (SELECT plans.code FROM plans WHERE plans.id = member_plans.plan_id LIMIT 1) AS plan_code
// '),
DB::raw('
(SELECT plans.code
FROM plans
WHERE plans.id IN (
SELECT member_plans.plan_id
FROM member_plans
WHERE member_plans.member_id = claim_requests.member_id
)
AND plans.service_code = claim_requests.service_code) AS plan_code
'),
DB::raw('
(SELECT services.description FROM services WHERE services.code = claim_requests.service_code LIMIT 1) AS service_code
'),
DB::raw('
(SELECT corporate_policies.code FROM corporate_policies WHERE corporate_policies.id = claim_requests.policy_id LIMIT 1) AS corporate_policies
'),
DB::raw('
(SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider
'),
DB::raw('
(Select SUM(request_log_benefits.amount_approved) as tot_bill FROM request_log_benefits
WHERE request_log_benefits.request_log_id = request_logs.id LIMIT 1) AS tot_bill
'),
'claim_requests.status_claim_management as status',
)
->paginate($limit);
return response()->json($claims);
return response()->json(Helper::paginateResources($results));
}
public function downloadTemplate()
{
return Helper::responseJson([
'file_name' => "Template - Claim - Management.xlsx",
"file_url" => url('files/Template - Claim - Management.xlsx')
]);
}
public function import(Request $request)
{
if ($request->hasFile('file')) {
$file = $request->file('file');
$data = Excel::toArray([], $file);
$processedData = $this->processCategoryNames($data);
$importedRows = 0;
$failedRows = [];
foreach ($processedData as $row) {
try {
$affectedRows = DB::table('claim_requests')
->where('code','=', $row['code'])
->where('claim_management','=', 1)
->update([
'status_claim_management' => $row['qc'] == 'Y' ? 'approved' : 'declined',
'reason_decline' => $row['reason'] ? $row['reason'] : null,
'approval_by_claim_management' => auth()->user()->id,
'approval_date_claim_management' => date('Y-m-d H:i:s'),
]);
if ($affectedRows === 0) {
$failedRows[] = $row;
} else {
$importedRows += $affectedRows;
}
} catch (\Exception $e) {
$failedRows[] = $row;
}
}
$response = [
'message' => 'File uploaded and data saved to database',
'data' => [
'total_success_row' => $importedRows,
'total_failed_row' => count($failedRows),
'failed_rows' => $failedRows,
],
];
return response()->json($response);
}
return response()->json(['error' => 'No file uploaded.']);
}
private function processCategoryNames($data)
{
$header = [];
$row = [];
for ($i = 1; $i < count($data[0]); $i++) {
$row[] = $data[0][$i];
$header[] = $data[0][0];
}
$filed = [];
foreach ($header[0] as $value)
{
$modelColumn = strtolower(preg_replace('/\s+/', '_', trim($value)));
$modelColumn = str_replace(['*', ' '], '', $modelColumn);
if($modelColumn)
{
$filed[] = $modelColumn;
}
}
$result = [];
foreach ($row as $subarray) {
$trimmedSubarray = [];
for ($i = 0; $i < count($filed); $i++) {
$trimmedSubarray[$filed[$i]] = $subarray[$i] ? $subarray[$i] : null;
}
$result[] = $trimmedSubarray;
}
return $result;
}
public function exportClaimManagement(Request $request)
{
$start_date = $request->input('start_date') ? $request->input('start_date') : 'all';
$end_date = $request->input('end_date') ? $request->input('end_date') : 'all';
$writer = WriterEntityFactory::createXLSXWriter();
$writer->openToFile(public_path('files/Report-Data-Claim-Management-'.$start_date.'-'.$end_date.'.xlsx'));
$header = [
'No',
'Code',
'Name',
'Member ID',
'Date Submission',
'Plan ID',
'Service',
'Policy Number',
'Provider',
'Total Billing',
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
$writer->addRow($headerRow);
// ============================
$results = DB::table('claim_requests')
->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
// ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
->when($request->input('search'), function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->orWhere('members.name', 'like', "%" . $search . "%");
$query->orWhere('claim_requests.code', 'like', "%" . $search . "%");
$query->orWhere('members.member_id', 'like', "%" . $search . "%");
});
})
->when($request->has('orderBy'), function ($query) use ($request) {
$orderBy = $request->orderBy;
$direction = $request->order ?? 'asc';
$query->orderBy($orderBy, $direction);
})
->when($request->input('start_date') , function ($query, $start_date) {
$query->where(function ($query) use ($start_date) {
$query->where('claim_requests.created_at', '>=', $start_date);
});
})
->when($request->input('end_date') , function ($query, $end_date) {
$query->where(function ($query) use ($end_date) {
$query->where('claim_requests.created_at', '<=', $end_date);
});
})
->when($request->input('provider') , function ($query, $provider) {
$query->where(function ($query) use ($provider) {
$query->where('request_logs.organization_id', '=', $provider);
});
})
->where('claim_management', '=', 1)
->select(
'claim_requests.id',
'request_logs.id AS id_log',
'claim_requests.code as code',
'members.name',
DB::raw('
(SELECT members.member_id FROM members WHERE members.id = claim_requests.member_id LIMIT 1) AS member_id
'),
'claim_requests.created_at',
DB::raw('
(SELECT plans.code
FROM plans
WHERE plans.id IN (
SELECT member_plans.plan_id
FROM member_plans
WHERE member_plans.member_id = claim_requests.member_id
)
AND plans.service_code = claim_requests.service_code) AS plan_code
'),
// DB::raw('
// (SELECT plans.code FROM plans WHERE plans.id = member_plans.plan_id LIMIT 1) AS plan_code
// '),
DB::raw('
(SELECT services.description FROM services WHERE services.code = claim_requests.service_code LIMIT 1) AS service_code
'),
DB::raw('
(SELECT corporate_policies.code FROM corporate_policies WHERE corporate_policies.id = claim_requests.policy_id LIMIT 1) AS corporate_policies
'),
DB::raw('
(SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider
'),
DB::raw('
(Select SUM(request_log_benefits.amount_approved) as tot_bill FROM request_log_benefits
WHERE request_log_benefits.request_log_id = request_logs.id LIMIT 1) AS tot_bill
'),
'claim_requests.status_claim_management as status',
)
->get();
$no=0;
$gr_total = 0;
foreach($results as $item)
{
$gr_total += $item->tot_bill;
$no++;
$rowData = [
$no,
$item->code,
$item->name,
$item->member_id,
$item->created_at,
$item->plan_code,
$item->service_code,
$item->corporate_policies,
$item->provider,
$item->tot_bill ? $item->tot_bill : 0
];
$style = (new StyleBuilder())
//->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$row = WriterEntityFactory::createRowFromArray($rowData, $style);
$writer->addRow($row);
}
$footer = [
'Grand Total',
'',
'',
'',
'',
'',
'',
'',
'',
$gr_total,
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
$writer->addRow($footerRow);
$writer->close();
return Helper::responseJson([
'file_name' => 'Report-Data-Claim-Management-'. $start_date.'-'.$end_date,
"file_url" => url('files/Report-Data-Claim-Management-'. $start_date.'-'.$end_date.'.xlsx')
]);
}
public function exportFiled(Request $request)
{
$writer = WriterEntityFactory::createXLSXWriter();
$writer->openToFile(public_path('files/Report-Data-Filed-Import.xlsx'));
$header = [
'Code*',
'QC*',
'Reason'
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
$writer->addRow($headerRow);
// ============================
foreach($request->params as $item)
{
$rowData = [
$item['code'],
$item['qc'],
$item['reason']
];
$style = (new StyleBuilder())
//->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$row = WriterEntityFactory::createRowFromArray($rowData, $style);
$writer->addRow($row);
}
$footer = [
'',
'',
'',
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
$writer->addRow($footerRow);
$writer->close();
return Helper::responseJson([
'file_name' => 'Report-Data-Filed-Import',
"file_url" => url('files/Report-Data-Filed-Import.xlsx')
]);
}
public function getProvider(Request $request)
{
$providers = DB::table('organizations')
->where('organizations.type', '=', 'hospital')
->where('organizations.status', '=', 'active')
->select(
'organizations.id',
'organizations.name'
)
->orderBy('name', 'asc')
->get();
return response()->json($providers);
}
public function cekStatus($id)
{
$cek = DB::table('claim_requests')
->where('claim_requests.id', '=', $id)
->select('claim_requests.status_claim_management as status')
->first();
$data['cek'] = $cek;
$member = DB::table('claim_requests')
->join('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
->join('members', 'request_logs.member_id', '=', 'members.id')
->where('claim_requests.id', '=', $id)
->select('claim_requests.code','members.name','claim_requests.created_at', DB::raw('
(SELECT services.name FROM services WHERE services.code = request_logs.service_code LIMIT 1) AS service_type
'),)
->first();
$data['member'] = $member;
return response()->json($data);
}
/**
@@ -334,21 +739,18 @@ class ClaimController extends Controller
return Helper::responseJson([], message: "Diagnosis berhasil di update");
}
public function decline($id)
public function decline(Request $request, $id)
{
//Get claim request id
$data_claim_requests = DB::table('claim_requests')
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
->where('claims.id', $id)
->select('claim_requests.id')
->first();
$id = $data_claim_requests->id;
DB::table('claims')
->where('claim_request_id', $id)
DB::table('claim_requests')
->where('claim_requests.id', $id)
->update(
[
'status' => 'declined'
'status' => 'declined',
'status_claim_management' => 'declined',
'reason_decline' => $request->reasonDecline ? $request->reasonDecline : '',
'approval_date_claim_management' => date('Y-m-d H:i:s'),
'approval_by_claim_management' => auth()->user()->id
]
);
@@ -370,19 +772,14 @@ class ClaimController extends Controller
public function approve($id)
{
//Get claim request id
$data_claim_requests = DB::table('claim_requests')
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
->where('claims.id', $id)
->select('claim_requests.id')
->first();
$id = $data_claim_requests->id;
DB::table('claims')
->where('claim_request_id', $id)
DB::table('claim_requests')
->where('claim_requests.id', $id)
->update(
[
'status' => 'approved'
'status' => 'approved',
'status_claim_management' => 'approved',
'approval_date_claim_management' => date('Y-m-d H:i:s'),
'approval_by_claim_management' => auth()->user()->id
]
);

View File

@@ -17,15 +17,23 @@ use App\Services\ClaimRequestService;
use App\Exceptions\ImportRowException;
use App\Events\ClaimRequested;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
use Box\Spout\Common\Entity\Style\CellAlignment;
use App\Models\File;
use App\Models\FilesMcu;
use Illuminate\Support\Facades\DB;
use App\Models\Member;
use App\Models\MemberPlan;
use App\Models\Plan;
use App\Models\RequestLogBenefit;
use Carbon\Carbon;
class ClaimRequestController extends Controller
{
private static $code_prefix = 'CRQ-C';
private static $code_prefix = 'CLAIM';
/**
* Display a listing of the resource.
@@ -41,11 +49,17 @@ class ClaimRequestController extends Controller
});
})
->when($request->start_date, function ($q, $startDate) {
$q->where('submission_date', '>', $startDate);
$q->where('submission_date', '>', Carbon::parse($startDate)->subDay());
})
->when($request->end_date, function ($q, $endDate) {
$q->where('submission_date', '<', $endDate);
->when($request->end_date, function ($q, $endDate) use ($request) {
// Jika tanggal akhir diberikan dan tidak sama dengan tanggal mulai
if ($request->start_date != $request->end_date) {
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
} else {
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
}
})
->when($request->service_code, function ($q, $serviceCode) {
$q->whereIn('service_code', $serviceCode);
})
@@ -62,7 +76,7 @@ class ClaimRequestController extends Controller
})
->with(['member', 'files', 'service', 'member.currentPolicy'])
->paginate();
return Helper::paginateResources(ClaimRequestResource::collection($claimRequests));
}
@@ -429,6 +443,7 @@ class ClaimRequestController extends Controller
$import = new ImportService();
$import->read($fileRead);
$import->write($fileWrite, 'xsls');
foreach ($import->sheetsIterator() as $sheetIndex => $sheet) {
if ($sheetIndex == 1) { // Rename First Sheet to Writer
$firstWriterSheet = $import->writer->getCurrentSheet();
@@ -445,6 +460,9 @@ class ClaimRequestController extends Controller
$result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']);
$import->addArrayToRow($result_headers);
$imported_claim_data = 0;
$failed_claim_data = [];
$doc_headers_indexes = [];
foreach ($sheet->getRowIterator() as $index => $row) {
if ($index == 1) { // First Row Must be Header
@@ -465,25 +483,28 @@ class ClaimRequestController extends Controller
}
try { // Process the Row Data
$claimRequestService = new ClaimRequestService();
$claimRequestService->handleClaimRequestRow($row_data);
// Write Success Result to File
// $import->read($fileRead);
// $import->write($fileWrite, 'xsls');
$result_headers = array_merge($row_data, ['Ingest Code' =>200, 'Ingest Note' => 'Success']);
// Mengambil tanggal dari objek DateTime
$dateSubmission = Helper::dateParser($result_headers['date_submission']); // Format tanggal sesuai kebutuhan
// Mengubah nilai date_submission menjadi string tanggal
$result_headers['date_submission'] = $dateSubmission;
// dd($result_headers);
$import->addArrayToRow($result_headers, $sheet->getName());
$imported_claim_data++;
} catch (ImportRowException $e) {
// Write Data Validation Error to File
// $import->read($fileRead);
// $import->write($fileWrite, 'xsls');
$import->addArrayToRow(array_merge($row_data, [
$new_claim_data = $import->addArrayToRow(array_merge($row_data, [
'Ingest Code' => $e->getCode(),
'Ingest Note' => $e->getMessage(),
]), $sheet->getName());
$failed_claim_data[] = ['row_number' => $index, 'error' => $e->getMessage(),'data' => $new_claim_data];
}
// catch (\Exception $e) {
// // throw new \Exception($e);
@@ -502,104 +523,20 @@ class ClaimRequestController extends Controller
$import->reader->close();
Storage::delete('temp/' . $file_name);
$import->writer->close();
return [
// 'total_successed_row' => $imported_plan_data,
// 'total_failed_row' => count($failed_plan_data),
// 'failed_row' => $failed_plan_data,
'result_file' => [
'url' => Storage::disk('public')->url('temp/result-' . $file_name),
'name' => 'result-' . $file_name,
'total_success_row' => $imported_claim_data,
'total_failed_row' => count($failed_claim_data),
'failed_row' => $failed_claim_data,
]
];
}
public function claimRequestDetail($claimRequestId)
{
// $status = DB::table('claim_requests')
// ->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
// ->leftJoin('members', 'claim_requests.member_id', '=', 'members.id')
// ->leftJoin('corporate_employees', 'members.id', '=', 'corporate_employees.member_id')
// ->leftJoin('corporate_divisions', 'corporate_employees.division_id', '=', 'corporate_divisions.id')
// ->where('claim_requests.id', '=', $claimRequestId)
// ->select(
// 'claim_requests.submission_date',
// 'claim_requests.code',
// DB::raw('
// CASE
// WHEN claim_requests.status = "requested" THEN "requested"
// WHEN claim_requests.status = "approved" AND claims.status = "approved" THEN "approved"
// WHEN claim_requests.status = "approved" AND claims.status = "declined" THEN "declined"
// WHEN claim_requests.status = "approved" AND claims.status = "disbrusmented" THEN "disbrusmented"
// /*WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "pending"*/
// WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "reviewed"
// ELSE ""
// END AS status
// ')
// )
// ->first();
// $results['status'] = $status;
// $timeline = DB::table('claim_logs')
// ->where('claim_logs.claim_request_id', '=', $claimRequestId)
// ->select(
// DB::raw('
// CASE
// WHEN claim_logs.status = "requested" THEN "Request"
// WHEN claim_logs.status = "reviewed" THEN "Review"
// WHEN claim_logs.status = "approved" THEN "Approval"
// WHEN claim_logs.status = "declined" THEN "Decline"
// ELSE "-"
// END AS txt_status
// '),
// DB::raw('
// CASE
// WHEN claim_logs.status = "requested" THEN "#159C9C"
// WHEN claim_logs.status = "reviewed" THEN "#0C53B7"
// WHEN claim_logs.status = "approved" THEN "#229A16"
// WHEN claim_logs.status = "declined" THEN "#FF4842"
// ELSE "-"
// END AS txt_status_color
// '),
// DB::raw('
// CASE
// WHEN claim_logs.status = "requested" THEN "#00AB5529"
// WHEN claim_logs.status = "reviewed" THEN "#1890FF29"
// WHEN claim_logs.status = "approved" THEN "#54D62C29"
// WHEN claim_logs.status = "declined" THEN "#FF48427A"
// ELSE "-"
// END AS txt_status_backgroundColor
// '),
// 'claim_logs.date',
// 'claim_logs.description',
// 'claim_logs.status'
// )
// ->orderBy('claim_logs.id', 'desc')
// ->get();
// $results['timeline'] = $timeline;
// $request_files = DB::table('claim_request_files')
// ->where('claim_request_files.claim_request_id', '=', $claimRequestId)
// ->select(
// 'claim_request_files.*',
// DB::raw('(SELECT files.fileable_id FROM files WHERE files.fileable_id = claim_request_files.claim_request_id AND files.type = claim_request_files.type LIMIT 1) AS check_files'),
// )
// ->get();
// $results['request_files'] = $request_files;
// $documents = DB::table('files')
// ->where('fileable_type', 'App\Models\ClaimRequest')
// ->where('fileable_id', $claimRequestId)
// ->select('original_name', \DB::raw("CONCAT('" . env('APP_URL') . "/storage/', path) as path"), 'type')
// ->orderBy('id', 'desc')
// ->get();
// $results['documents'] = $documents;
// $dialog_submits = DB::table('claim_requests')
// ->leftJoin('members', 'claim_requests.member_id','=', 'members.id')
// ->where('claim_requests.id', $claimRequestId)
// ->select('claim_requests.code', 'members.name', 'claim_requests.submission_date', 'claim_requests.service_code','claim_requests.status')
// ->first();
// $results['dialog_submits'] = $dialog_submits;
// return Helper::responseJson($results);
$claimRequest = ClaimRequest::findOrFail($claimRequestId);
$claimRequest->load([
'requestLog',
@@ -704,10 +641,6 @@ class ClaimRequestController extends Controller
public static function getNextCode()
{
// $last_number = ClaimRequest::max('code');
// $next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[2] + 1);
// return self::makeCode($next_number);
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, "-", -1) AS SIGNED)) as max_numeric_code'))
->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code');
@@ -725,11 +658,17 @@ class ClaimRequestController extends Controller
public static function makeCode($next_number)
{
// Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number);
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
// Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number);
$requestLogData = RequestLog::where('id', $request_log_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
$sparator = '.';
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 6, '0', STR_PAD_LEFT);
}
public function requestFiles(Request $request, $claim_id)
@@ -788,4 +727,309 @@ class ClaimRequestController extends Controller
return Helper::responseJson(data: $request->toArray(), message: 'Invoice Success Uploaded');
}
public function exportClaimRequest(Request $request)
{
$claimRequests = ClaimRequest::query()
->when($request->search, function ($q, $search) {
$q->where('code', 'LIKE', "%".$search."%");
$q->orWhereHas('member', function ($subQuery) use ($search) {
$subQuery->where('name', 'LIKE', "%".$search."%");
});
})
->when($request->start_date, function ($q, $startDate) {
$q->where('submission_date', '>', Carbon::parse($startDate)->subDay());
})
->when($request->end_date, function ($q, $endDate) use ($request) {
// Jika tanggal akhir diberikan dan tidak sama dengan tanggal mulai
if ($request->start_date != $request->end_date) {
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
} else {
$q->where('submission_date', '<=', Carbon::parse($endDate));
}
})
->when($request->service_code, function ($q, $serviceCode) {
$q->whereIn('service_code', $serviceCode);
})
->when($request->orderBy, function ($q, $orderBy) use ($request) {
if (in_array($orderBy, ['submission_date', 'code'])) {
$q->orderBy($orderBy, $request->order);
}
})
->when(empty($request->orderBy), function ($q) {
$q->orderBy('created_at', 'desc');
})
->when($request->status, function($q, $status) {
$q->where('status', $status);
})
->with(['member', 'member.currentCorporate', 'files', 'service', 'member.currentPolicy', 'requestLog', 'requestLog.organization',])
->addSelect([
'tot_billing' => function ($query) {
$query->select(DB::raw('SUM(request_log_benefits.amount_approved)'))
->from('request_log_benefits')
->whereColumn('request_log_benefits.request_log_id', 'claim_requests.request_log_id')
->limit(1);
}
])
->get();
$writer = WriterEntityFactory::createXLSXWriter();
$writer->openToFile(public_path('files/Report-Data-Claim-Request.xlsx'));
$header = [
'No',
'Code Claim',
'Date Claim Submission',
'Code LOG',
'Date Submission',
'Date Admission',
'Date Discharge',
'Provider',
'Member ID (BN)',
'Member Name',
'Member Name Principal',
'Plan Code',
'Payor ID',
'Corporate name',
'Policy Number',
'Total Billing',
'Benefit Code',
'Benefit Desc',
'Amt Incurred',
'Amt Approved',
'Amt Not Approved',
'Excess Paid',
// 'Reason',
'Diagnosis',
'Keterangan',
'Catatan',
'Status',
'QC'
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
$writer->addRow($headerRow);
$results = $claimRequests;
$no=0;
$gr_total = 0;
$rowData=[];
foreach($results as $item)
{
// $gr_total += $item->tot_bill;
// $requestLogData = RequestLogBenefit::selectRaw('*,
// (SELECT code FROM benefits WHERE benefits.id = request_log_benefits.benefit_id) AS benefit_code,
// (SELECT description FROM benefits WHERE benefits.id = request_log_benefits.benefit_id) AS benefit_description,
// sum(amount_incurred) AS total_incurred'
// )
// ->where(['request_log_id' => $item->request_log_id, 'deleted_at' => null])
// ->get()->toArray();
//Data Benefit
$requestLogData = DB::table('request_log_benefits')
->where('request_log_benefits.request_log_id', '=', $item->request_log_id)
->select(
'*',
// DB::raw('SUM(request_log_benefits.amount_incurred) AS total_incurred'),
DB::raw('
(Select benefits.description FROM benefits
WHERE benefits.id = request_log_benefits.benefit_id LIMIT 1) AS benefit_description
'),
DB::raw('
(Select benefits.code FROM benefits
WHERE benefits.id = request_log_benefits.benefit_id LIMIT 1) AS benefit_code
')
)
->get();
if ($item->member){
$member = Member::where('member_id', $item->member->principal_id)->first();
$memberPrincipal = $member->name;
$memberPlan = MemberPlan::where('member_id', $item->member->id)->get('plan_id')->toArray();
$plan= Plan::whereIn('id', $memberPlan)->where('service_code', $item->requestLog->service_code)->first();
$planCode = $plan->code;
if ($item->member->currentCorporate->id == $item->requestLog->organization->corporate_id_partner){
$payor = $item->member->currentCorporate->name;
} else {
$payor = 'LinkSehat';
}
} else {
$memberPrincipal = '-';
$planCode = '-';
$payor = '-';
}
if (!$requestLogData->isEmpty()){
foreach($requestLogData as $key => $data){
$no++;
if ($key == 0){
$gr_total += $item->tot_billing;
}
$rowItem = [
$no,
$item->code,
$item->submission_date ? $item->submission_date : '',
$item->requestLog ? $item->requestLog->code : '-',
$item->requestLog ? Helper::dateParser($item->requestLog->created_at) : '-', // submission = created_at
$item->requestLog ? $item->requestLog->submission_date : '-',
$item->requestLog ? $item->requestLog->discharge_date : '-',
$item->requestLog ? $item->requestLog->organization->name : '-',
$item->member ? $item->member->member_id : '-',
$item->member ? $item->member->name : '-',
$memberPrincipal,
$key == 0 ? $planCode : '',
$payor,
$item->member ? $item->member->currentCorporate->name : '-',
$item->member ? $item->member->currentPolicy->code : '-',
$key == 0 ? $item->tot_billing : '',
$data->benefit_code,
$data->benefit_description,
$data->amount_incurred ? $data->amount_incurred : '' ,
$data->amount_approved ? $data->amount_approved : '',
$data->amount_not_approved ? $data->amount_not_approved : '',
$data->excess_paid ? $data->excess_paid : '',
// $data->reason ? $data->reason : '',
$item->requestLog ? $item->requestLog->diagnosis : '-',
$item->requestLog ? $item->requestLog->keterangan : '-',
$item->requestLog ? $item->requestLog->catatan : '-',
$item->status ? $item->status : '-'
];
array_push($rowData,$rowItem);
}
} else {
$no++;
if ($item->member){
$member = Member::where('member_id', $item->member->principal_id)->first();
$memberPrincipal = $member->name;
$memberPlan = MemberPlan::where('member_id', $item->member->id)->get('plan_id')->toArray();
$plan= Plan::whereIn('id', $memberPlan)->where('service_code', $item->requestLog->service_code)->first();
$planCode = $plan->code;
if ($item->member->currentCorporate->id == $item->requestLog->organization->corporate_id_partner){
$payor = $item->member->currentCorporate->name;
} else {
$payor = 'LinkSehat';
}
} else {
$memberPrincipal = '-';
$planCode = '-';
$payor = '-';
}
$rowItem = [
$no,
$item->code,
$item->submission_date ? Helper::dateParser($item->submission_date) : '',
$item->requestLog ? $item->requestLog->code : '-',
$item->requestLog ? Helper::dateParser($item->requestLog->created_at) : '-', // submission = created_at
$item->requestLog ? $item->requestLog->submission_date : '-',
$item->requestLog ? $item->requestLog->discharge_date : '-',
$item->requestLog ? $item->requestLog->organization->name : '-',
$item->member ? $item->member->member_id : '-',
$item->member ? $item->member->name : '-',
$memberPrincipal,
$planCode,
$payor,
$item->member ? $item->member->currentCorporate->name : '-',
$item->member ? $item->member->currentPolicy->code : '-',
'',
'',
'',
'',
'',
'',
'',
// '',
$item->requestLog ? $item->requestLog->diagnosis : '-',
$item->requestLog ? $item->requestLog->keterangan : '-',
$item->requestLog ? $item->requestLog->catatan : '-',
$item->status ? $item->status : '-'
];
array_push($rowData,$rowItem);
}
}
$style = (new StyleBuilder())
//->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
foreach ($rowData as $rowItem) {
// if (is_numeric($rowItem[13])) {
// // Jumlahkan nilai angka ke total
// $grand_total_billing += $rowItem[13];
// }
$row = WriterEntityFactory::createRowFromArray($rowItem, $style);
$writer->addRow($row);
}
//Footer
$footer = [
'Total',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
$gr_total,
'',
'',
'',
'',
'',
'',
''
];
$style = (new StyleBuilder())
->setFontBold()
// ->setFontSize(15)
// ->setFontColor(Color::BLUE)
// ->setShouldWrapText()
->setCellAlignment(CellAlignment::LEFT)
// ->setBackgroundColor(Color::YELLOW)
->build();
$grand_total_billing = 0;
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
$writer->addRow($footerRow);
$writer->close();
return Helper::responseJson([
'file_name' => 'Report-Data-Claim-Request',
"file_url" => url('files/Report-Data-Claim-Request.xlsx')
]);
}
public function submition($id){
$claimRequest = ClaimRequest::findOrFail($id);
$claimRequest->status = 'submission';
$claimRequest->claim_management = 1;
$claimRequest->status_claim_management = 'received';
$claimRequest->submission_date_claim_management = date('Y-m-d H:i:s');
$claimRequest->submission_by_claim_management = auth()->user()->id;
$claimRequest->save();
return response()->json([
'error' => false,
'message' => 'Update succses',
'data' => $claimRequest],
200);
}
}

View File

@@ -559,8 +559,8 @@ class CorporateController extends Controller
break;
case 'claim-request':
return Helper::responseJson([
'file_name' => "Template Format Claim.xlsx",
"file_url" => url('files/Template Format Claim.xlsx')
'file_name' => "Template Claim Request.xlsx",
"file_url" => url('files/Template Claim Request.xlsx')
]);
break;
case 'request-log':

View File

@@ -235,6 +235,11 @@ Route::prefix('internal')->group(function () {
Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']);
Route::get('claims', [ClaimController::class, 'index']);
Route::get('claims/download-template', [ClaimController::class, 'downloadTemplate']);
Route::post('claims/import', [ClaimController::class, 'import']);
Route::post('claims/exportFiled/', [ClaimController::class, 'exportFiled']);
Route::get('claims/export-claim-management', [ClaimController::class, 'exportClaimManagement']);
Route::get('claims/get-provider', [ClaimController::class, 'getProvider']);
Route::post('claims/{id}/update-items', [ClaimController::class, 'updateItems'])->name('claim.update-items');
Route::post('claims/{id}/update-diagnosis', [ClaimController::class, 'updateDiagnosis'])->name('claim.update-diagnosis');
Route::post('claims/{id}/decline', [ClaimController::class, 'decline'])->name('claim.decline');
@@ -248,6 +253,7 @@ Route::prefix('internal')->group(function () {
Route::post('claims', [ClaimController::class, 'store']);
Route::get('claims/{id}', [ClaimController::class, 'show']);
Route::get('claims/cek_status/{id}', [ClaimController::class, 'cekStatus']);
Route::put('claims/{id}', [ClaimController::class, 'update']);
Route::get('claims/{id}/edit', [ClaimController::class, 'edit']);
Route::post('check-limit', [ClaimController::class, 'checkLimit']);
@@ -311,13 +317,15 @@ Route::prefix('internal')->group(function () {
Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
Route::get('claim-requests/list-member', [ClaimRequestController::class, 'getClaimMemberInfiniteScroll']); // Bagaskoro, BSD 31 Oktober 2023
Route::post('claim-requests/{id}/approve', [ClaimRequestController::class, 'approve'])->name('claim-requests.approve');
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
Route::post('claim-requests/{id}/submition', [ClaimRequestController::class, 'submition'])->name('claim-requests.submition');
Route::get('claim-requests/{id}/show', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
Route::post('claim-requests', [ClaimRequestController::class, 'createNew']); // Bagaskoro, BSD 2 November 2023
Route::post('claim-requests/{id}/update', [ClaimRequestController::class, 'update']);
Route::post('claim-requests/import', [ClaimRequestController::class, 'importClaim'])->name('claim-requests.importClaim');
Route::get('claim-requests/detail/{id}', [ClaimRequestController::class, 'claimRequestDetail']);
Route::post('claim-requests/{id}/invoice-files', [ClaimRequestController::class, 'invoiceFiles']);
Route::post('claim-requests/{id}/request-files', [ClaimRequestController::class, 'requestFiles']);
Route::get('claim-requests/export', [ClaimRequestController::class, 'exportClaimRequest']);
Route::get('claim-requests/service/{id}', [ClaimRequestController::class, 'getServiceMember']);

View File

@@ -71,10 +71,22 @@ class ClaimRequestShowResource extends JsonResource
'total_excess_paid' => $total_excess_paid,
];
$isReversal = false;
$isRole = auth()->user()->role_id;
if ($data['request_log']['status'] == 'approved' &&
$data['request_log']['status_final_log'] == 'approved' &&
$data['status'] == 'approved' &&
$data['status_claim_management'] == 'approved' &&
$isRole != 1 // is admin
){
$isReversal = true;
}
$response = [
'id' => $data['id'],
'code' => $data['code'],
'status' => $data['status'],
'request_log_id' => $data['request_log_id'],
'request_log' => $requestLogData,
'provider' => $data['request_log']['organization']['name'],
@@ -98,7 +110,9 @@ class ClaimRequestShowResource extends JsonResource
'service_type' => Helper::serviceName( $data['request_log']['service_code']),
'claim_method' => $data['request_log']['payment_type'],
'files' => $data['request_log']['files'],
'reason_decline' => $data['reason_decline'],
// 'benefit_data' => $benefitDetailLog,
'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi
];

View File

@@ -10,6 +10,7 @@ use App\Models\RequestLogBenefit;
use App\Models\RequestLogMedicine;
use App\Models\Organization;
use App\Models\Exclusion;
use App\Models\ClaimRequest;
use App\Models\Icd;
use App\Helpers\Helper;
use App\Models\CorporatePolicy;
@@ -36,6 +37,23 @@ class RequestLogShowResource extends JsonResource
$benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray();
$medicineDetailLog = RequestLogMedicine::where('request_log_id', $requestLog['id'])->get()->toArray();
$provider = Organization::where('id', $requestLog['organization_id'])->first();
$claimRequest = ClaimRequest::where('request_log_id', $requestLog['id'])->first();
if ($claimRequest) {
$claimCode = $claimRequest->code;
$isReversal = false;
$isRole = auth()->user()->role_id;
if ($requestLog['status'] == 'approved' &&
$requestLog['status_final_log'] == 'approved' &&
$claimRequest->status == 'approved' &&
$claimRequest->status_claim_management == 'approved' &&
$isRole != 1
){
$isReversal = true;
}
} else {
$claimCode = '-';
$isReversal = false;
}
if ($provider){
$providerName = $provider->name;
@@ -90,11 +108,12 @@ class RequestLogShowResource extends JsonResource
->whereIn('code', $diagnosis)
->select('code', 'name')
->get();
}
}
$data = [
'id' => $requestLog['id'],
'code' => $requestLog['code'],
'code_claim' => $claimCode,
'member_id' => $requestLog['member']['member_id'],
'corporate_id' => $corporateId,
'policy_number' =>$policyNumber->code ? $policyNumber->code : '-',
@@ -128,6 +147,7 @@ class RequestLogShowResource extends JsonResource
'catatan' => $requestLog['catatan'],
'reason' => $requestLog['reason'],
'diagnosis' => $icd,
'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi
];

View File

@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\DB;
use App\Models\Member;
use App\Models\User;
use App\Models\Service;
use DateTime;
class Helper
{
@@ -426,4 +427,20 @@ class Helper
ini_set('memory_limit', '256M');
}
public static function dateParser($date_from_row) {
if ($date_from_row instanceof DateTime) {
return $date_from_row->format('Y-m-d');
} else if ($date_from_row != null) {
if (strtotime($date_from_row)){
return date('Y-m-d', strtotime($date_from_row));
} else {
// throw new ImportRowException(__('Format Date Invalid'), 0, null, $date_from_row);
return null;
}
} else {
// throw new ImportRowException(__('Format Date Invalid'), 0, null, $date_from_row);
return null;
}
}
}

View File

@@ -26,6 +26,15 @@ class ClaimRequest extends Model
'service_code',
'policy_id',
'status',
// New field for claim management
'claim_management',
'status_claim_management',
'submission_date_claim_management',
'submission_by_claim_management',
'approval_date_claim_management',
'approval_by_claim_management',
'reason_decline',
//
'claim_id',
'organization_id',
'code',
@@ -43,69 +52,29 @@ class ClaimRequest extends Model
];
public static $doc_headers_to_field_map = [
"PAYOR ID" => "payor_id",
"CORPORATE ID" => "corporate_id",
"POLICY NUMBER" => "policy_number",
"MEMBER ID" => "member_id",
"MEMBER NAME" => "member_name",
"RECORD TYPE (P/D)" => "record_type",
"BENEFIT CODE" => "benefit_code",
"BENEFIT DESC" => "benefit_desc",
"CLAIM TYPE" => "claim_type",
"CLAIM PROCESS STATUS" => "status",
"CLIENT CLAIM ID" => "client_claim_id",
"REFERENCE NO" => "reference_no",
"ADMEDIKA CLAIM ID" => "admika_claim_id",
"PROVIDER CODE" => "provider_code",
"ADMISSION DATE" => "admission_date",
"DISCUTRGE DATE" => "discutrge_date",
"DURATION DAYS" => "duration_days",
"COVERAGE TYPE" => "coverage_type",
"PLAN ID" => "plan_id",
"DIAGNOSIS CODE" => "diagnosis_code",
"DIAGNOSIS DESC" => "diagnosis_desc",
"TOT AMT INCURRED" => "tot_amt_insurred",
"TOT AMT APPROVED" => "tot_amt_approved",
"TOT AMT NOT APPROVED" => "tot_amt_not_approved",
"TOT EXCESS PAID" => "tot_excess_paid",
"REMARKS" => "remarks",
"SECONDARY DIAGNOSIS CODE" => "secondary_diagnosis",
"APPROVED DATE" => "approved_date",
"APPROVED BY" => "approved_by",
"DATE RECEIVED" => "data_received",
"HOSPITAL INVOICE DATE" => "hospital_invoice_date",
"Code LOG" => "code",
"Date Claim Submission" => "date_submission",
"Total Billing" => "total_billing",
"Benefit Code" => "benefit_code",
"Amt Incurred" => "amount_incurred",
"Amt Approved" => "amount_apporve",
"Amt Not Approved" => "amount_not_apporve",
"Excess Paid" => "excess_paid",
"Reason" => "reason",
"QC" => "qc",
];
public static $listing_doc_headers = [
"PAYOR ID",
"CORPORATE ID",
"POLICY NUMBER",
"MEMBER ID",
"MEMBER NAME",
"RECORD TYPE (P/D)",
"CLAIM TYPE",
"CLAIM PROCESS STATUS",
"CLIENT CLAIM ID",
"REFERENCE NO",
"ADMEDIKA CLAIM ID",
"PROVIDER CODE",
"ADMISSION DATE",
"DISCUTRGE DATE",
"DURATION DAYS",
"COVERAGE TYPE",
"PLAN ID",
"DIAGNOSIS CODE",
"DIAGNOSIS DESC",
"TOT AMT INCURRED",
"TOT AMT APPROVED",
"TOT AMT NOT APPROVED",
"TOT EXCESS PAID",
"REMARKS",
"SECONDARY DIAGNOSIS CODE",
"APPROVED DATE",
"APPROVED BY",
"DATE RECEIVED",
"HOSPITAL INVOICE DATE",
"Code LOG",
"Date Claim Submission",
"Total Billing",
"Benefit Code",
"Amt Incurred",
"Amt Approved",
"Amt Not Approved",
"Excess Paid",
// "Reason",
"QC",
];

View File

@@ -6,6 +6,9 @@ use App\Events\ClaimApproved;
use App\Events\ClaimRequested;
use App\Models\Claim;
use App\Models\ClaimRequest;
use App\Models\RequestLog;
use App\Models\Benefit;
use App\Models\RequestLogBenefit;
use App\Models\Organization;
use App\Helpers\Helper;
use App\Models\Icd;
@@ -20,6 +23,7 @@ use Str;
class ClaimRequestService{
private static $code_prefix = 'CLAIM';
public static function storeClaimRequest($row = null, $code, $member, $paymentType, $serviceCode, $requestLogID = null, $submissionDate = null, $status = 'requested', $organization_code = null)
{
// try {
@@ -32,24 +36,74 @@ class ClaimRequestService{
'code' => $row['provider_code']
]), 403, null, $row);
}
};
}
DB::beginTransaction();
$claimRequestData = [
'code' => $code,
'request_log_id' => $requestLogID ?? 0,
'member_id' => $member->id,
'submission_date' => $submissionDate ?? now(),
'status' => $status,
'payment_type' => $paymentType,
'service_code' => $serviceCode,
'policy_id' => $member->currentPolicy->id ?? null,
'organization_id' => $organization ? $organization->id : 0,
];
$claimRequest = ClaimRequest::create($claimRequestData);
if ($status == 'submission'){
$claimManagement = 1;
$submissionDateClaimManagement = $submissionDate;
$submissionByClaimManagement = auth()->user()->id;
$statusClaim = 'received';
} else {
$claimManagement = 0;
$submissionDateClaimManagement = null;
$submissionByClaimManagement = null;
$statusClaim = null;
}
if (count($row)>0){
if($row['total_billing']) {
$data = [
'code' => $code,
'request_log_id' => $requestLogID ?? 0,
'member_id' => $member->id,
'submission_date' => $submissionDate ?? now(),
'status' => $status,
'claim_management' => $claimManagement,
'status_claim_management' => $statusClaim,
'submission_date_claim_management' => $submissionDateClaimManagement,
'submission_by_claim_management' => $submissionByClaimManagement,
'payment_type' => $paymentType,
'service_code' => $serviceCode,
'policy_id' => $member->currentPolicy->id ?? null,
'organization_id' => $organization ? $organization->id : 0,
];
} else {
$data = [];
}
$claimRequest = ClaimRequest::updateOrCreate(['request_log_id' => $requestLogID],$data);
$benefitData = Benefit::where('code', $row['benefit_code'])->first();
$requestLogData = RequestLogBenefit::updateOrCreate(
[
'request_log_id' => $requestLogID,
'benefit_id' => $benefitData->id,
],[
'request_log_id' => $requestLogID,
'benefit_id' => $benefitData->id,
'amount_incurred' => $row['amount_incurred'],
'amount_approved' => $row['amount_apporve'],
'amount_not_approved' => $row['amount_not_apporve'],
'excess_paid' => $row['excess_paid'],
]);
} else { // input from hospital portal
$data = [
'code' => $code,
'request_log_id' => $requestLogID ?? 0,
'member_id' => $member->id,
'submission_date' => $submissionDate ?? now(),
'status' => $status,
'claim_management' => $claimManagement,
'status_claim_management' => $statusClaim,
'submission_date_claim_management' => $submissionDateClaimManagement,
'submission_by_claim_management' => $submissionByClaimManagement,
'payment_type' => $paymentType,
'service_code' => $serviceCode,
'policy_id' => $member->currentPolicy->id ?? null,
'organization_id' => $organization ? $organization->id : 0,
];
$claimRequest = ClaimRequest::updateOrCreate(['request_log_id' => $requestLogID],$data);
}
DB::commit();
return $claimRequest;
@@ -60,8 +114,6 @@ class ClaimRequestService{
// }
}
public static function storeClaimManagement($row, $member, $claim_request_id){
try {
$organization = 0;
@@ -130,53 +182,58 @@ class ClaimRequestService{
}
}
protected function validatePlanRow($row)
{
if (empty($row['member_id'])) {
throw new ImportRowException(__('Member ID Required'), 0, null, $row);
}
}
public function handleClaimRequestRow($row)
{
try {
$member = Member::where('member_id', $row['member_id'])->with(['currentPlan'])->first();
if(!$member){
throw new ImportRowException(__('Member Tidak ditemukan'), 0, null, $row);
$requestLog = RequestLog::where('code', $row['code'])->first();
if(!$requestLog){
throw new ImportRowException(__('LOG Tidak ditemukan'), 0, null, $row);
};
$code = $row['client_claim_id'];
$organization_id = $row['provider_code'];
$submissionDate = Helper::formatDateDB($row['admission_date']);
$paymentType = $row['claim_type'];
$status = $row['status'];
$serviceCode = $row['coverage_type'];
if ($requestLog->status != 'approved' && $requestLog != 'approved'){
throw new ImportRowException(__('Request LOG / Final LOG Belum di Approved'), 0, null, $row);
}
$organization = Organization::where('id', $requestLog->organization_id)->first();
// Create Code
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code');
// $next_number = 1;
if ($last_numeric_code) {
// // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir
// $parts = explode('-', $last_code);
// $last_number = (int) end($parts);
$next_number = $last_numeric_code + 1;
} else {
$next_number = 1;
}
// make code
$member = Member::with('currentCorporate')->where(['id' => $requestLog->member_id])->first();
$sparator = '.';
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
$code = 'CLAIM' . $sparator. 'I' . $sparator. $organization->code . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
$submissionDate = Helper::dateParser($row['date_submission']);
$paymentType = $requestLog->payment_type;
if ($row['qc'] == 'Y'){
$status = 'submission';
} else {
$status = 'requested';
}
$serviceCode = $requestLog->service_code;
$newClaimRequest = $this->storeClaimRequest(
row: $row,
code: $code,
member: $member,
paymentType: $paymentType,
serviceCode: $serviceCode,
requestLogID: $requestLog->id,
submissionDate: $submissionDate,
status: $status,
organization_code: $organization_id
organization_code: $organization->code
);
$newlyCreatedID = $newClaimRequest->id;
$newClaimManangement = $this->storeClaimManagement($row, $member, $newlyCreatedID);
ClaimRequested::dispatch($newClaimRequest);
// Log History
$newClaimRequest->histories()->create([
'title' => 'New Claim Requested',
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'type' => 'info',
'system_origin' => 'import-internal-aso'
]);
$claim_request_data = $row;
$this->validatePlanRow($claim_request_data);
return $newClaimRequest;
} catch (\Exception $e) {
throw $e;

View File

@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('claim_requests', function (Blueprint $table) {
$table->integer('claim_management')
->default(0)
->after('status')
->comment('0=claim request, 1=claim management');
$table->string('status_claim_management')->after('claim_management')->nullable();
$table->dateTime('submission_date_claim_management')->after('status_claim_management')->nullable();
$table->string('submission_by_claim_management')->after('submission_date_claim_management')->nullable();
$table->dateTime('approval_date_claim_management')->after('submission_by_claim_management')->nullable();
$table->string('approval_by_claim_management')->after('approval_date_claim_management')->nullable();
$table->string('reason_decline')->after('approval_by_claim_management')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('claim_requests', function (Blueprint $table) {
$table->dropColumn('claim_management');
$table->dropColumn('status_claim_management');
$table->dropColumn('submission_date_claim_management');
$table->dropColumn('submission_by_claim_management');
$table->dropColumn('approval_date_claim_management');
$table->dropColumn('approval_by_claim_management');
$table->dropColumn('reason_decline');
});
}
};

View File

@@ -203,6 +203,8 @@ type ServiceMonitoringProps = {
}[];
}>
>;
keterangan: string;
catatan: string;
};
export default function ServiceMonitoring() {
@@ -591,6 +593,44 @@ export default function ServiceMonitoring() {
)}
</Grid> */}
</Grid>
{/* Keterangan */}
<Grid item container xs={12} spacing={1.5}>
<Grid item xs={12}>
<Typography variant="subtitle2" color={'grey.600'}>
{loading ? <Skeleton animation={'wave'} width={200} /> : 'Keterangan'}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle1" color={'grey.800'}>
{loading ? (
<Skeleton animation={'wave'} width={300} />
) : data && data.keterangan ? (
data.keterangan
) : (
'-'
)}
</Typography>
</Grid>
</Grid>
{/* Catatan */}
<Grid item container xs={12} spacing={1.5}>
<Grid item xs={12}>
<Typography variant="subtitle2" color={'grey.600'}>
{loading ? <Skeleton animation={'wave'} width={200} /> : 'Catatan'}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle1" color={'grey.800'}>
{loading ? (
<Skeleton animation={'wave'} width={300} />
) : data && data.catatan ? (
data.catatan
) : (
'-'
)}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>

View File

@@ -1,35 +1,30 @@
import MuiDialog from "@/components/MuiDialog";
import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material";
import { Button, Card, Checkbox, DialogActions, Grid, TextField, TextareaAutosize, Typography } from "@mui/material";
import { Paper } from "@mui/material";
import { Stack } from '@mui/material';
import React, { useEffect, useState } from 'react';
import { ClaimRequest, Files } from '@/@types/claims';
import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime";
import { DetailClaimRequest } from "../Model/Types";
import { fDateTimesecond, toTitleCase } from "@/utils/formatTime";
import axios from "@/utils/axios";
import { enqueueSnackbar, useSnackbar } from "notistack";
import { enqueueSnackbar } from "notistack";
import { useNavigate } from "react-router";
import * as Yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { RHFTextField } from "@/components/hook-form";
type DialogConfirmationType = {
openDialog: boolean;
setOpenDialog: any;
onSubmit?: void;
approve: string;
claimRequest: ClaimRequest|undefined;
requestLog: DetailClaimRequest|undefined;
}
export default function DialogConfirmation({claimRequest, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
export default function DialogConfirmation({requestLog, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const [formData, setFormData] = useState({
date: claimRequest?.date,
id: claimRequest?.id,
reason: claimRequest?.reason
});
const handleChange = (field, value) => {
setFormData((prevData) => ({
...prevData,
@@ -38,24 +33,28 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
};
const handleApprove = () => {
setFormData((prevData) => ({
...prevData,
status: approve,
}));
handleSubmit();
};
const handleSubmit = () => {
axios
.post(`customer-service/request/final-log`, formData)
.post(`claim-requests/${requestLog?.id}/submition`, formData)
.then((response) => {
enqueueSnackbar('Verification Request LOG Success', { variant: 'success' });
enqueueSnackbar('Submition Claim Request Success', { variant: 'success' });
setOpenDialog(false);
if (requestLog?.service_type == 'Inpatient'){
navigate('/case_management/inpatient_monitoring');
} else {
navigate('/custormer-service/final-log');
}
navigate('/claim-requests')
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
});
}
const style1 = {
color: '#919EAB',
width: '30%'
@@ -66,34 +65,57 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
const marginBottom1 = {
marginBottom: 1,
}
const marginBottom2 = {
marginBottom: 2,
}
const resetForm = () => {
setFormData({
status: approve,
no_identitas: requestLog?.no_identitas ?? '',
keterangan: '',
hak_kamar_pasien: '',
penempatan_kamar: '',
});
};
const handleCloseDialog = () => {
setOpenDialog(false);
resetForm();
}
const handleNumericInput = (input: any) => {
const numericInput = input.replace(/\D/g, '');
return numericInput;
};
const handleKeyPress = (e:any) => {
if (e.key === 'Enter' && !e.shiftKey) {
// Menghentikan default "Enter" (tidak membuat baris baru)
e.preventDefault();
// Menambahkan karakter baris baru
handleChange('keterangan', `${formData.keterangan}\n`);
}
};
console.log(approve, 'test')
const getContent = () => (
<Stack spacing={1} marginTop={2}>
<Typography variant="subtitle2">Are you sure to {approve == 'approved' ? 'approve' : 'deciline'} this final log ?</Typography>
<Typography variant="subtitle1">Are you sure to submit this claim ?</Typography>
<Grid item xs={12} md={12} marginTop={4}>
<Card sx={{padding:2, marginTop:2}} >
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Member ID</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.member_id}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Policy Number</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.policy_number}</Typography>
<Typography variant='subtitle2' sx={style1} gutterBottom>Code</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.code}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Submission Date</Typography>
<Typography variant='subtitle2' sx={style1} gutterBottom>Date Submission</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
@@ -105,58 +127,10 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.service_type}</Typography>
</Stack>
</Card>
<Card sx={{padding:2, marginTop:2}} >
<Stack direction='row' spacing={2} sx={marginBottom2}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Discharge Date</Typography>
<TextField
label="Discharge Date"
variant="outlined"
fullWidth
type="date"
value={formData.discharge_date ? fDateOnly(formData.discharge_date) : ''}
onChange={(e) => handleChange('discharge_date', e.target.value)}
/>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom2}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Catatan</Typography>
<TextField
label="Catatan"
variant="outlined"
fullWidth
value={formData.catatan}
onChange={(e) => handleChange('catatan', e.target.value)}
/>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom2}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Diagnosis ICD - X</Typography>
<Autocomplete
multiple
options={icdOptions}
getOptionLabel={(option) => option.label}
fullWidth
value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))}
onChange={(e, newValues) => handleChange('icdCodes', newValues.map((value) => value.value))}
renderInput={(params) => (
<TextField
{...params}
label="Diagnosis ICD - X"
variant="outlined"
/>
)}
/>
</Stack>
</Card>
</Grid>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialog}>Cancel</Button>
{approve == 'approved' ? (
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Approve</Button>
) : (
<Button color="error" variant="contained" onClick={() => handleApprove()}>Decline</Button>
) }
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Submit</Button>
</DialogActions>
</Stack>
);
@@ -168,7 +142,7 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="xl"
maxWidth="md"
/>
);
}

View File

@@ -78,15 +78,13 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
);
const [date, setDate] = useState(currentClaim?.submission_date)
const [date, setDate] = useState(currentClaim?.submission_date ? fDateTimesecond(currentClaim?.submission_date) : null)
const id = currentClaim?.id
useEffect(() => {
setDate(currentClaim?.submission_date)
}, [currentClaim]);
console.log(date);
useEffect(() => {
if (isEdit && currentClaim) {
reset(defaultValues);
@@ -254,7 +252,6 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
</Stack>
)
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
@@ -310,10 +307,14 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
<RHFDateTimePicker
{...field}
label="Date of Submission"
value={field.value || null}
onChange={() => setDate(field.value)}
value={field.value}
onChange={() =>
setDate(field.value)
}
dateFormat='dd MMM yyyy HH:mm:ss'
/>
)}
/>
</Grid>
<Grid item xs={3}>

View File

@@ -32,7 +32,7 @@ export default function ClaimsCreateUpdate() {
useEffect(() => {
if (isEdit) {
axios.get('/claim-requests/' + id).then((res) => {
axios.get('/claim-requests/' + id + '/show').then((res) => {
setCurrentClaim(res.data.data);
});

View File

@@ -38,6 +38,8 @@ import DialogDeleteFileLog from './Components/DialogDeleteFileLog';
import DialogBenefit from '../CustomerService/FinalLog/Components/DialogBenefit';
import DialogDeleteBenefit from '../CustomerService/FinalLog/Components/DialogDeleteBenefit';
import DialogEditBenefit from '../CustomerService/FinalLog/Components/DialogEditBenefit';
import DialogConfirmation from './Components/DialogConfirmation';
// ----------------------------------------------------------------------
@@ -49,6 +51,8 @@ export default function Detail() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [claimRequests, setClaimRequest] = useState<DetailClaimRequest>();
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const [isReversal, setIsReversal] = useState(false);
const { id } = useParams();
@@ -57,6 +61,7 @@ export default function Detail() {
.get('claim-requests/detail/'+id)
.then((response) => {
setClaimRequest(response.data.data)
setIsReversal(response.data.data.is_reversal)
})
.catch((error) => {
console.error(error);
@@ -223,13 +228,18 @@ export default function Detail() {
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Document </Typography>
</Stack>
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogUploadFileLog(true)
}} >
<Typography variant="button" display="block">File</Typography>
</Button>
</Stack>
{
!isReversal ? (
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogUploadFileLog(true)
}} >
<Typography variant="button" display="block">File</Typography>
</Button>
</Stack>
) : null
}
</Stack>
{claimRequests?.files?.map((documentType, index) => (
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
@@ -250,14 +260,19 @@ export default function Detail() {
<Typography variant="body1" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
</a>
</Stack>
<Stack direction="column" spacing={2}>
<IconButton onClick={() => {
setDialogDeleteFileLog(true)
setPathFile(documentType.path)
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
<Delete color='error' fontSize="small" />
</IconButton>
</Stack>
{
!isReversal ? (
<Stack direction="column" spacing={2}>
<IconButton onClick={() => {
setDialogDeleteFileLog(true)
setPathFile(documentType.path)
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
<Delete color='error' fontSize="small" />
</IconButton>
</Stack>
) : null
}
</Stack>
))}
@@ -281,11 +296,15 @@ export default function Detail() {
<Card sx={{padding:2}} >
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogBenefit(true);
}} >
<Typography variant="button" display="block">Benefit</Typography>
</Button>
{
!isReversal ? (
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogBenefit(true);
}} >
<Typography variant="button" display="block">Benefit</Typography>
</Button>
) : null
}
</Stack>
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
@@ -298,29 +317,34 @@ export default function Detail() {
{item.benefit?.description}
</Typography>
</Grid>
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setDialogEditBenefit(true)
setIdBenefitData(item.id)
setBenefitConfigurationData(item)
}}
>
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => {
setIdBenefitData(item.id)
setDialogDeleteBenefit(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</Grid>
{
!isReversal ? (
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setDialogEditBenefit(true)
setIdBenefitData(item.id)
setBenefitConfigurationData(item)
}}
>
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => {
setIdBenefitData(item.id)
setDialogDeleteBenefit(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</Grid>
) : null
}
</Grid>
</Grid>
<Grid item xs={12} py={2}>
@@ -421,7 +445,7 @@ export default function Detail() {
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={6}>
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold'}}>
Total Benefit
</Typography>
</Grid>
@@ -436,12 +460,12 @@ export default function Detail() {
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
<Typography variant="subtitle1">
Amount Incurred
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
{totalAmountIncurred ? fNumber(totalAmountIncurred) : '0'}
</Typography>
</Grid>
@@ -452,12 +476,12 @@ export default function Detail() {
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
<Typography variant="subtitle1">
Amount Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
</Typography>
</Grid>
@@ -468,12 +492,12 @@ export default function Detail() {
<Grid item xs={3}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
<Typography variant="subtitle1">
Amount Not Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
{totalAmountNotApproved ? fNumber(totalAmountNotApproved) : 0}
</Typography>
</Grid>
@@ -484,12 +508,12 @@ export default function Detail() {
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
<Typography variant="subtitle1">
Excess Paid
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
{totalExcessPaid ? fNumber(totalExcessPaid) : 0}
</Typography>
</Grid>
@@ -497,6 +521,23 @@ export default function Detail() {
</Grid>
</Grid>
</Box>
<Grid container spacing={1} marginY={2}>
<Grid item xs={6}>
<Typography variant="subtitle1">
Biaya disetujui
</Typography>
</Grid>
<Grid item xs={6} textAlign="right">
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle2">
Nominal diatas 1 juta akan menunggu persetujuan senior analyst
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
@@ -504,9 +545,25 @@ export default function Detail() {
: (
null
)}
</Box>
</Card>
{ claimRequests?.status === 'declined' ? (
<Card sx={{padding:2, marginTop:2}} >
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Reason</Typography>
</Stack>
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Reason Decline</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{claimRequests?.reason_decline}</Typography>
</Stack>
</Box>
</Card>
) : null }
{/* PR Buat pindahin ke componen */}
{/* <CardBenefit
requestLog={requestLog}
@@ -542,6 +599,42 @@ export default function Detail() {
requestLog={requestLog}
openDialog={openDialogEditDetail}
/> */}
{(claimRequests?.status === 'requested') || (claimRequests?.status === 'declined') ? (
<Grid item xs={12} md={12}>
<Stack direction="row" padding={4} sx={{ justifyContent: 'flex-end' }}>
<div style={{ marginRight: '10px' }}> {/* Perubahan sintaksis disini */}
<Button
variant="outlined"
color='inherit'
onClick={() => {
navigate('/claim-requests')
}}
>
Cancel
</Button>
</div>
<div>
<Button
variant="contained"
onClick={() => {
setOpenDialogSubmit(true);
setApprove('approved');
}}
>
Submit
</Button>
</div>
<DialogConfirmation
setOpenDialog={setOpenDialogSubmit}
requestLog={claimRequests}
openDialog={openDialogSubmit}
approve={approve}
>
</DialogConfirmation>
</Stack>
</Grid>
) : null}
</Grid>
</Grid>

View File

@@ -123,6 +123,11 @@ export default function List() {
variant="outlined"
fullWidth
onChange={handleSearchChange}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleSearchSubmit(event);
}
}}
value={searchText}
placeholder='Search Code or Name...'
/>
@@ -152,7 +157,7 @@ export default function List() {
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DesktopDatePicker
label="End Date"
inputFormat="MM/dd/yyyy"
inputFormat="dd/MM/yyyy"
value={searchParams.get('end_date')}
onChange={(value) => {
try {
@@ -274,7 +279,7 @@ export default function List() {
}
const handleGetData = (type :string) => {
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
axios.get(`claim-requests/export`)
.then((response) => {
const link = document.createElement('a');
link.href = response.data.data.file_url;
@@ -361,14 +366,32 @@ export default function List() {
</Stack>
)}
{importResult && (
// <Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
// <Box sx={{ color: 'text.secondary' }}>
// Last Import Result Report :{' '}
// <a href={importResult.result_file?.url ?? '#'}>
// {importResult.result_file?.name ?? '-'}
// </a>
// </Box>
// </Stack>
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result Report :{' '}
Last Import Result :{' '}
<Box sx={{ color: 'success.main', display: 'inline' }}>
{importResult.result_file?.total_success_row ?? 0}
</Box>{' '}
Row Processed,{' '}
<Box sx={{ color: 'error.main', display: 'inline' }}>
{importResult.result_file?.total_failed_row}
</Box>{' '}
Failed, Report :{' '}
<a href={importResult.result_file?.url ?? '#'}>
{importResult.result_file?.name ?? '-'}
</a>
</Box>
</Stack>
)}
</div>
);
@@ -393,7 +416,7 @@ export default function List() {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/claim-requests', { params: filter });
console.log(response.data);
setDataTableLoading(false);
@@ -468,8 +491,12 @@ export default function List() {
<TableCell align="left">{row.service_name}</TableCell>
<TableCell align="left">{row.payment_type_name}</TableCell>
<TableCell align="left">
{ row.status == "requested" ?
{ row.status == "requested" ?
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status)}</Label>) :
row.status == "submission" ?
(<Label color='info'> {capitalizeFirstLetter(row.status)}</Label>) :
row.status == "declined" ?
(<Label color='error'> {capitalizeFirstLetter(row.status)}</Label>) :
(<Label color='success'> {capitalizeFirstLetter(row.status)}</Label>)
}
</TableCell>

View File

@@ -51,6 +51,7 @@ export type DetailClaimRequest = {
benefit_data : BenefitData[],
diagnosis : Diagnosis[],
request_log : RequestLogType | undefined,
reason_decline : string,
}
export type RequestLogType = {

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ import List from "./List";
export default function Claims() {
const pageTitle = 'Claim';
const pageTitle = 'Claim Management';
return (
<Page title={ pageTitle } sx={{ mx: 2}}>
@@ -16,7 +16,7 @@ export default function Claims() {
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Claim',
name: 'Claim Management',
href: '/claims',
},
]}

View File

@@ -1,6 +1,7 @@
// @mui
import {
Box,
Grid,
Button,
Card,
Collapse,
@@ -17,12 +18,24 @@ import {
ButtonGroup,
Tooltip,
TableHead,
Checkbox,
InputAdornment,
TableSortLabel,
FormControl
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { fDateOnly } from '@/utils/formatTime';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import AssessmentIcon from '@mui/icons-material/Assessment';
// hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
import { LoadingButton } from '@mui/lab';
// components
import axios from '../../utils/axios';
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
@@ -32,23 +45,146 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded';
import { Chip } from '@mui/material';
import Iconify from '@/components/Iconify';
import { enqueueSnackbar } from 'notistack';
import { fDate } from '../../utils/formatTime';
import { fDate, fDateTime } from '../../utils/formatTime';
import { Claims } from '@/@types/claims';
import Label from '@/components/Label';
import { capitalizeFirstLetter } from '@/utils/formatString';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import Edit from '@mui/icons-material/Edit';
import { Download } from '@mui/icons-material';
import { Add, Search } from '@mui/icons-material';
import Autocomplete from '@mui/material/Autocomplete';
import DownloadIcon from '@mui/icons-material/Download';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
export default function List() {
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [providers, setProviders] = useState(null);
// const [searchText, setSearchText] = useState('');
const [order, setOrder] = useState<Order>('desc');
const [orderBy, setOrderBy] = useState('created_at');
const [perPage, setPerPage] = useState<number>(0);
const handleChange = (event, newValue) => {
// Jika newValue tidak undefined, atur nilai dataProvider
if (newValue !== undefined) {
setDataProvider(newValue.service_code);
} else {
// Jika tidak ada yang dipilih, set dataProvider menjadi string kosong
setDataProvider(null);
}
};
// Dummy data
const dummyServices = [
{ service_code: '1', name: 'Service 1' },
{ service_code: '2', name: 'Service 2' },
{ service_code: '3', name: 'Service 3' },
// tambahkan data lain sesuai kebutuhan
];
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData.data
.filter(row => row.status === 'received') // Memfilter baris dengan status 'requested'
.map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleRowSelect = (id) => {
if (selectedRows.includes(id)) {
setSelectedRows(selectedRows.filter(rowId => rowId !== id));
} else {
setSelectedRows([...selectedRows, id]);
}
};
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState(null);
const [startDate, setStartDate] = useState(null);
const [searchText, setSearchText] = useState('');
const [endDate, setEndDate] = useState(null);
const navigate = useNavigate();
const [dataProvider, setDataProvider] = useState(null);
useEffect(() => {
if (startDate !== null || endDate !== null || dataProvider !== null
|| order !== null || orderBy !== null || perPage !== 0) {
loadDataTableData();
getProvider();
}
}, [startDate, endDate, dataProvider, order, orderBy, perPage]);
const [isLoading, setIsLoading] = useState(false);
const [isLoadingImport, setIsLoadingImport] = useState(false);
const handleExportReport = async () => {
const year = startDate?.getFullYear();
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
const year1 = endDate?.getFullYear();
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
var filter = Object.fromEntries([...searchParams.entries()]);
setIsLoading(true)
await axios
.get('/claims/export-claim-management',{
params: {
search: searchText,
start_date: formattedDate ? formattedDate : null,
end_date:formattedDate1,
provider: dataProvider,
order: order,
orderBy: orderBy,
page: perPage,
}
})
.then((res) => {
enqueueSnackbar('Data berhasil di Export', {
variant: 'success',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
});
setIsLoading(false)
document.location.href = res.data.data.file_url;
})
.catch((err) =>
enqueueSnackbar('Data Gagal di Export', {
variant: 'error',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
})
);
};
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState('');
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? '';
@@ -73,7 +209,7 @@ export default function List() {
useEffect(() => {
// Trigger First Search
setSearchText(searchParams.get('search') ?? '');
// setSearchText(searchParams.get('search') ?? '');
}, []);
return (
@@ -126,41 +262,331 @@ export default function List() {
);
}
const searchInput = useRef<HTMLInputElement>(null);
//handle search
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? '';
setSearchText(newSearchText);
};
const handleSearchSubmit = (event: any) => {
event.preventDefault();
loadDataTableData();
};
useEffect(() => {
// Trigger First Search
//setSearchText(searchText);
}, []);
const item = [
{
id: '',
value: '',
name: 'Semua',
},
];
// const handleClick = () => {
// }
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
LaravelPaginatedDataDefault
);
const loadDataTableData = async (appliedFilter: any | null = null) => {
setDataTableLoading(true);
const year = startDate?.getFullYear();
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
const year1 = endDate?.getFullYear();
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/claims', { params: filter });
// console.log(response.data);
const response = await axios.get('/claims', {
params: {
search: searchText,
start_date: formattedDate ? formattedDate : null,
end_date:formattedDate1,
provider: dataProvider,
order: order,
orderBy: orderBy,
page: perPage,
}
});
setDataTableLoading(false);
setDataTableData(response.data);
};
const getProvider = async () => {
const response = await axios.get('/claims/get-provider');
setProviders(response.data)
}
const applyFilter = async (searchFilter: { search: string }) => {
await loadDataTableData(searchFilter);
setSearchParams(searchFilter);
};
const handlePageChange = (event: ChangeEvent, value: number): void => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
setPerPage(value);
};
useEffect(() => {
loadDataTableData();
}, []);
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
function toTitleCase(str: string | null) {
return str.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
const [approve, setApprove] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event) => {
setReasonDecline(event.target.value);
// Tambahkan logika yang diperlukan di sini
};
const handleSubmitData = () => {
//approve or decline
if (!reasonDecline && approve == 'decline') {
enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
return false;
}
Promise.all(selectedRows.map(send_bulk))
.then(() => {
enqueueSnackbar('All requests processed successfully', { variant: 'success' });
setOpenDialogSubmit(false);
setTimeout(() => {
window.location.reload();
}, 5000); // Reload the page after 5 seconds
})
.catch((error) => {
enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' });
});
};
function send_bulk(id) {
return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline });
}
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importHospital = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const [importResult, setImportResult] = useState(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importHospital?.current) {
handleClose();
importHospital.current ? importHospital.current.click() : console.log('No File selected');
} else {
alert('No file selected');
}
};
const handleCancelImportButton = () => {
if(importHospital.current)
{
importHospital.current.value = '';
importHospital.current.dispatchEvent(new Event('change', { bubbles: true }));
}
};
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name);
} else {
setCurrentImportFileName(null);
}
};
const handleUpload = () => {
if(importHospital.current && importHospital.current.files)
{
if (importHospital.current?.files.length) {
const formData = new FormData();
formData.append('file', importHospital.current?.files[0]);
setImportLoading(true);
axios
.post('claims/import', formData)
.then((response) => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data);
setImportLoading(false);
enqueueSnackbar('Success Import Hospitals', { variant: 'success' });
})
.catch((response) => {
enqueueSnackbar(
'Looks like something went wrong. Please check your data and try again. ' +
response.message,
{ variant: 'error' }
);
setImportLoading(false);
});
} else {
enqueueSnackbar('No File Selected', { variant: 'warning' });
}
}
};
const handleGetTemplate = () => {
axios.get('claims/download-template').then((response) => {
const link = document.createElement('a');
link.href = response.data.data.file_url;
link.setAttribute('download', response.data.data.file_name);
document.body.appendChild(link);
link.click();
handleClose();
});
};
const handleExportReportFiled = async () => {
await axios
.post('claims/exportFiled', { params: importResult?.data.failed_rows })
.then((res) => {
enqueueSnackbar('Data berhasil di Export', {
variant: 'success',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
});
setIsLoading(false)
document.location.href = res.data.data.file_url;
})
.catch((err) =>
enqueueSnackbar('Data Gagal di Export', {
variant: 'error',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
})
);
};
// useEffect(() => {
// loadDataTableData();
// getProvider();
// }, []);
const headStyle = {
fontWeight: 'bold',
};
const headCells = [
{
id: 'code',
align: 'left',
label: 'Code',
isSort: true,
},
{
id: 'name',
align: 'left',
label: 'Name',
isSort: false,
},
{
id: 'member_id',
align: 'left',
label: 'Member ID',
isSort: false,
},
{
id: 'created_at',
align: 'left',
label: 'Date Submission',
isSort: true,
},
{
id: 'plan_code',
align: 'left',
label: 'Plan ID',
isSort: true,
},
{
id: 'service_code',
align: 'left',
label: 'Service',
isSort: false,
},
{
id: 'corporate_policies',
align: 'left',
label: 'Policy Number',
isSort: true,
},
{
id: 'provider',
align: 'left',
label: 'Provider',
isSort: false,
},
{
id: 'tot_bill',
align: 'left',
label: 'Total Billing',
isSort: false,
},
{
id: 'status',
align: 'left',
label: 'Status',
isSort: false,
},
{
id: 'action',
align: 'left',
label: '',
isSort: false,
},
];
const orders = {
order: order,
setOrder: setOrder,
orderBy: orderBy,
setOrderBy: setOrderBy,
};
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
handleRequestSort(event, property);
};
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
orders?.setOrder(isAsc ? 'desc' : 'asc');
orders?.setOrderBy(property);
};
// Called on every row to map the data to the columns
function createData(data: Claims): Claims {
return {
@@ -171,10 +597,18 @@ export default function List() {
{
/* ------------------ TABLE ROW ------------------ */
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
function Row(props: { row: ReturnType<typeof createData>, isSelected: boolean, onSelect: (id: string) => void }) {
const { row, isSelected, onSelect } = props;
// Memperbaiki destrukturisasi props
const handleRowCheckboxChange = () => {
onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik
};
const [open, setOpen] = React.useState(false);
const test = 1000;
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
@@ -183,16 +617,22 @@ export default function List() {
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell> */}
<TableCell align="left">{row.claim_request?.code}</TableCell>
<TableCell align="left">
{row?.status == 'received' ? (
<Checkbox checked={isSelected} onChange={handleRowCheckboxChange} />
):''}
</TableCell>
<TableCell align="left">{row?.code}</TableCell>
{/* <TableCell align="left">{row.code}</TableCell> */}
<TableCell align="left">{row.member?.current_plan?.code}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.payor_id}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.code}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.current_policy?.code}</TableCell>
<TableCell align="left">{row.member?.member_id}</TableCell>
<TableCell align="left">{row.benefit_desc}</TableCell>
<TableCell align="center">
<TableCell align="left">{row?.name}</TableCell>
<TableCell align="left">{row?.member_id}</TableCell>
<TableCell align="left">{row?.created_at ? fDateTime(row?.created_at) : ''}</TableCell>
<TableCell align="left">{row?.plan_code}</TableCell>
<TableCell align="left">{row?.service_code}</TableCell>
<TableCell align="left">{row?.corporate_policies}</TableCell>
<TableCell align="left">{row?.provider}</TableCell>
<TableCell align="left">Rp. {row?.tot_bill?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".")}</TableCell>
<TableCell align="left">
{row.status == 'draft' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'requested' && (<Label color='primary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'received' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
@@ -202,18 +642,17 @@ export default function List() {
{row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
</TableCell>
<TableMoreMenu actions={
<>
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }>
<Edit />
Edit
</MenuItem>
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
</>
} />
<TableCell align="left">
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate('/claims/detail/'+row.id_log+'/'+row.id+'') }>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
</>
} />
</TableCell>
</TableRow>
@@ -236,40 +675,77 @@ export default function List() {
/* ------------------ END TABLE ROW ------------------ */
}
function TableContent() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableHead>
<TableRow>
{/* <TableCell style={headStyle} align="left" /> */}
<TableCell style={headStyle} align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
Plan ID
</TableCell>
<TableCell style={headStyle} align="left">
Payor ID
</TableCell>
<TableCell style={headStyle} align="left">
Corporate ID
</TableCell>
<TableCell style={headStyle} align="left">
Policy Number
</TableCell>
<TableCell style={headStyle} align="left">
Member ID
</TableCell>
<TableCell style={headStyle} align="left">
Benefit Desc
</TableCell>
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="left">
</TableCell>
{selectedRows.length > 0 ? (
<>
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
<Stack direction="row">
<Checkbox checked={selectAll} onChange={handleSelectAll} />
{selectedRows.length > 0 ? selectedRows.length : '0'} &nbsp;<Typography variant='subtitle2'>Selected</Typography>
</Stack>
</TableCell>
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={6}>
</TableCell>
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="right" colSpan={2}>
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {setOpenDialogSubmit(true);
setApprove('decline');}}>
<Typography variant='subtitle2'>Decline</Typography>
</Button>
</TableCell>
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {setOpenDialogSubmit(true);
setApprove('approve');}}>
<Typography variant='subtitle2'>Approve</Typography>
</Button>
</TableCell>
</>
) : (
<>
<TableCell style={headStyle} align="left">
<Checkbox checked={selectAll} onChange={handleSelectAll} />
</TableCell>
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
</>
)}
</TableRow>
</TableHead>
{/* ------------------ END TABLE HEADER ------------------ */}
@@ -278,7 +754,7 @@ export default function List() {
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={11} align="center">
Loading
</TableCell>
</TableRow>
@@ -286,7 +762,7 @@ export default function List() {
) : dataTableData.data.length === 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={11} align="center">
No Data
</TableCell>
</TableRow>
@@ -294,7 +770,7 @@ export default function List() {
) : (
<TableBody>
{dataTableData.data.map((row) => (
<Row key={row.id} row={row} />
<Row key={row.id} row={row} isSelected={selectedRows.includes(row.id)} onSelect={handleRowSelect} />
))}
</TableBody>
)}
@@ -305,7 +781,212 @@ export default function List() {
return (
<Card>
<ImportForm />
<Grid
container
spacing={2}
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
>
<Grid item xs={12} md={12} lg={12}>
<form style={{ width: '100%' }}>
<Grid container spacing={1} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
<input
type="file"
id="file"
ref={importHospital}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
{!currentImportFileName && (
<>
<Grid item xs={12} md={3}>
<TextField
id="search-input"
ref={searchInput}
variant="outlined"
value={searchText}
fullWidth
onChange={handleSearchChange}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleSearchSubmit(event);
}
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
placeholder: 'Search Code or Name',
}}
/>
</Grid>
<Grid item xs={12} md={4} display="flex" sx={{ gap: '16px' }}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DesktopDatePicker
value={startDate}
inputFormat="dd/MM/yyyy"
onChange={(value) => {
// loadDataTableData();
setStartDate(value);
}}
renderInput={(params) => <TextField {...params} fullWidth label="Start" />}
/>
</LocalizationProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DesktopDatePicker
value={endDate}
inputFormat="dd/MM/yyyy"
onChange={(value) => {
setEndDate(value);
}}
renderInput={(params) => (
<TextField
{...params}
fullWidth
label="End"
// error={!!error}
// helperText={error?.message}
// {...other}
/>
)}
/>
</LocalizationProvider>
</Grid>
<Grid item xs={12} md={2}>
{
providers && (
<Autocomplete
id="provider"
options={providers}
getOptionLabel={(option) => option.name || ''}
value={providers.find((item) => item.id === dataProvider) || null}
onChange={(event, value) => {
if (value) {
setDataProvider(value.id);
} else {
setDataProvider(null);
}
}}
renderInput={(params) => (
<TextField
{...params}
label="Provider"
fullWidth
/>
)}
/>
)
}
</Grid>
<Grid item xs={12} md={3} display="flex" sx={{ gap: '16px' }}>
<FormControl >
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
loading={isLoadingImport}
onClick={handleClick}
>
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
Import
</Typography>
</LoadingButton>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>
<Typography variant='body2'>Import</Typography>
</MenuItem>
<MenuItem
onClick={() => {
handleGetTemplate();
}}
>
<Typography variant='body2'> Download Template</Typography>
</MenuItem>
</Menu>
</FormControl>
<FormControl >
<LoadingButton
id="upload-button"
variant="contained"
startIcon={<Download />}
sx={{ p: 1.8 }}
onClick={handleExportReport}
loading={isLoading}
>
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
Export
</Typography>
</LoadingButton>
</FormControl>
</Grid>
</>
)}
{currentImportFileName && (
<Grid item xs={12} md={12}>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>
{currentImportFileName ?? 'No File Selected'}
</Button>
<Button
onClick={handleCancelImportButton}
size="small"
fullWidth={false}
sx={{ p: 1.8 }}
>
<CancelIcon color="error" />
</Button>
</ButtonGroup>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleUpload}
loading={importLoading}
>
Upload
</LoadingButton>
</Stack>
</Grid>
)}
{importResult && (
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result :{' '}
<Box sx={{ color: 'success.main', display: 'inline' }}>
{importResult.data.total_success_row ?? 0}
</Box>{' '}
Row Processed,{' '}
<Box sx={{ color: 'error.main', display: 'inline' }}>
{importResult.data.total_failed_row}
</Box>{' '}
Failed
{/* {importResult.data.failed_rows.map((row, index) => (
<Typography variant='body' key={index} color="error"> [Code={row.code ? row.code : 'Required'}]</Typography>
))} */}
&nbsp;<u onClick={handleExportReportFiled} style={{cursor:'pointer'}}>Download Data Filed</u>
</Box>
</Stack>
)}
</Grid>
</form>
</Grid>
</Grid>
<DataTable
isLoading={dataTableIsLoading}
@@ -314,6 +995,43 @@ export default function List() {
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Confirmation</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>Are you sure to {toTitleCase(approve)} this claim ?</Typography>
{approve == "decline" ? (
<Stack direction='row' spacing={2} marginTop={2}>
<TextField
id="outlined-multiline-static"
label="Reason decline"
multiline
rows={4} // Tentukan jumlah baris yang diinginkan
defaultValue=""
onChange={handleReasonDeclineChange}
variant="outlined"
sx={{width:'100%'}}
// fullWidth // Gunakan ini jika Anda ingin input memenuhi lebar Stack
/>
</Stack>
): ''}
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
<Button sx={{backgroundColor: (approve === 'decline' ? '' : '#19BBBB'), color: (approve === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(approve === 'decline' ? 'outlined' : 'contained')}>{(approve === "decline" ? 'Decline' : 'Approve')}</Button>
</DialogActions>
</Dialog>
</Card>
);
}

View File

@@ -145,51 +145,62 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
const methods = useForm<any>({
resolver: yupResolver(validationSchema),
defaultValues
defaultValues,
reValidateMode: "onChange"
});
let width = claimInput ? 2 : 2.36;
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control});
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods
// Buat total data
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_incurred || 0);
}, 0);
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_approved || 0);
}, 0);
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_not_approved || 0);
}, 0);
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.excess_paid || 0);
}, 0);
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control,});
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors,isValid } } = methods
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
// Calculate
const benefitData = watch('benefit_data');
const [isDisableSave, setDisableSave] = useState(false)
benefitData?.map((item, index) => {
totalAmountIncurred += parseFloat(item.amount_incurred);
totalAmountApproved += parseFloat(item.amount_approved);
totalAmountNotApproved += parseFloat(item.amount_not_approved);
totalExcessPaid += parseFloat(item.excess_paid);
const totalAll = () => {
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_incurred || 0);
}, 0);
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_approved || 0);
}, 0);
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.amount_not_approved || 0);
}, 0);
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
return accumulator + (item.excess_paid || 0);
}, 0);
if (totalAmountApproved != 0 && totalAmountIncurred != 0) {
if (totalAmountApproved > totalAmountIncurred){
setValue(`benefit_data.${index}.amount_approved`, 0)
alert('Total Amount Approved tidak boleh lebih dari Total Incurred')
}
}
benefitData?.map((item, index) => {
totalAmountIncurred += parseFloat(item.amount_incurred);
totalAmountApproved += parseFloat(item.amount_approved);
totalAmountNotApproved += parseFloat(item.amount_not_approved);
totalExcessPaid += parseFloat(item.excess_paid);
});
return {
totalAmountIncurred,
totalAmountApproved,
totalAmountNotApproved,
totalExcessPaid
}
}
const handleOnChangeNominal = (key) => {
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
// setValue(`benefit_data.${key}.amount_approved`, 0);
setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
} else {
clearErrors(`benefit_data.${key}.amount_approved`);
}
}
});
// Submit Form
// =====================================
const submitHandler = async (data: BenefitConfigurationListType) => {
const mapData = data.benefit_data.map((item) => ({
...item,
reason: item.reason.value
reason: item.reason ? item.reason.value : null
}));
const newData = {
@@ -206,8 +217,6 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
}
}
// Calculate
const getContent = () => !addBenefit ? (
<Stack spacing={2} sx={{marginTop: 2, padding: 2}} direction="column">
<Stack direction="row" spacing={2}>
@@ -281,6 +290,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
name={`benefit_data.${index}.amount_incurred`}
placeholder='Amount Incurred'
required
onChange={(event) => {
setValue(`benefit_data.${index}.amount_incurred`, event.target.value)
handleOnChangeNominal(index)}
}
/>
</Grid>
</Grid>
@@ -301,6 +314,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
name={`benefit_data.${index}.amount_approved`}
placeholder='Amount Approved'
required
onChange={(event) => {
setValue(`benefit_data.${index}.amount_approved`, event.target.value)
handleOnChangeNominal(index)}
}
/>
</Grid>
</Grid>
@@ -434,7 +451,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalAmountIncurred)}
{fNumber(totalAll().totalAmountIncurred)}
</Typography>
</Grid>
</Grid>
@@ -450,7 +467,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalAmountApproved)}
{fNumber(totalAll().totalAmountApproved)}
</Typography>
</Grid>
</Grid>
@@ -466,7 +483,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalAmountNotApproved)}
{fNumber(totalAll().totalAmountNotApproved)}
</Typography>
</Grid>
</Grid>
@@ -482,7 +499,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalExcessPaid)}
{fNumber(totalAll().totalExcessPaid)}
</Typography>
</Grid>
</Grid>
@@ -499,7 +516,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
<DialogActions>
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
<Button variant="outlined" onClick={handleCloseDialogBenefit}><Typography>Cancel</Typography></Button>
<LoadingButton disabled={isDisableSave} type="submit" variant="contained" loading={isSubmitting}>
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
Save
</LoadingButton>
</Stack>

View File

@@ -62,23 +62,36 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
defaultValues
});
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors } } = methods;
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
const totalAll = () => {
// Ambil nilai dari form menggunakan watch
const amountIncurred = parseFloat(watch('amount_incurred'));
const amountApproved = parseFloat(watch('amount_approved'));
const amountNotApproved = parseFloat(watch('amount_not_approved'));
const excessPaid = parseFloat(watch('excess_paid'));
// Hitung total baru
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
// Ambil nilai dari form menggunakan watch
const amountIncurred = parseFloat(watch('amount_incurred'));
const amountApproved = parseFloat(watch('amount_approved'));
const amountNotApproved = parseFloat(watch('amount_not_approved'));
const excessPaid = parseFloat(watch('excess_paid'));
return {
totalAmountIncurred,
totalAmountApproved,
totalAmountNotApproved,
totalExcessPaid
}
}
// Hitung total baru
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
if (totalAmountApproved > totalAmountIncurred) {
alert('Total Approve tidak boleh melebihi Total Incurred')
setValue('amount_approved', data?.amount_approved)
const handleOnChangeNominal = (key) => {
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
// setValue(`benefit_data.${key}.amount_approved`, 0);
setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
} else {
clearErrors(`amount_approved`);
}
}
// if (totalAmountIncurred !== (totalAmountApproved+totalAmountNotApproved)){
@@ -142,6 +155,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
name={`amount_incurred`}
placeholder='Amount Incurred'
required
onChange={(event) => {
setValue(`amount_incurred`, event.target.value)
handleOnChangeNominal(id)}
}
/>
</Grid>
</Grid>
@@ -162,6 +179,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
name={`amount_approved`}
placeholder='Amount Approved'
required
onChange={(event) => {
setValue(`amount_approved`, event.target.value)
handleOnChangeNominal(id)}
}
/>
</Grid>
</Grid>
@@ -275,7 +296,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{totalAmountIncurred ? fNumber(totalAmountIncurred) : 0}
{totalAll().totalAmountIncurred ? fNumber(totalAll().totalAmountIncurred) : 0}
</Typography>
</Grid>
</Grid>
@@ -291,7 +312,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalAmountApproved)}
{totalAll().totalAmountApproved ? fNumber(totalAll().totalAmountApproved) : 0}
</Typography>
</Grid>
</Grid>
@@ -307,7 +328,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalAmountNotApproved)}
{totalAll().totalAmountNotApproved ? fNumber(totalAll().totalAmountNotApproved) : 0}
</Typography>
</Grid>
</Grid>
@@ -323,7 +344,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
{fNumber(totalExcessPaid)}
{totalAll().totalExcessPaid ? fNumber(totalAll().totalExcessPaid) : 0}
</Typography>
</Grid>
</Grid>
@@ -337,7 +358,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
<DialogActions>
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
<Button variant="outlined" onClick={handleCloseDialog}><Typography>Cancel</Typography></Button>
<LoadingButton type="submit" variant="contained" loading={isSubmitting}>
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
Save
</LoadingButton>
</Stack>

View File

@@ -68,6 +68,7 @@ export default function Detail() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [requestLog, setRequestLog] = useState<DetailFinalLogType>();
const [isReversal, setIsReversal] = useState(false);
const { id } = useParams();
@@ -77,6 +78,7 @@ export default function Detail() {
.get('customer-service/request/'+id)
.then((response) => {
setRequestLog(response.data.data)
setIsReversal(response.data.data.is_reversal)
})
.catch((error) => {
console.error(error);
@@ -304,11 +306,15 @@ export default function Detail() {
<Card sx={{padding:2}} >
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogBenefit(true);
}} >
<Typography variant="button" display="block">Benefit</Typography>
</Button>
{
!isReversal ? (
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogBenefit(true);
}} >
<Typography variant="button" display="block">Benefit</Typography>
</Button>
) : null
}
</Stack>
{requestLog?.benefit_data?.map((item, index) => (
@@ -321,29 +327,33 @@ export default function Detail() {
{item.benefit?.description}
</Typography>
</Grid>
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setDialogEditBenefit(true)
setIdBenefitData(item.id)
setBenefitConfigurationData(item)
}}
>
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => {
setIdBenefitData(item.id)
setDialogDeleteBenefit(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</Grid>
{
!isReversal ? (
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setDialogEditBenefit(true)
setIdBenefitData(item.id)
setBenefitConfigurationData(item)
}}
>
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => {
setIdBenefitData(item.id)
setDialogDeleteBenefit(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</Grid>
) : null
}
</Grid>
</Grid>
<Grid item xs={12}>
@@ -619,13 +629,16 @@ export default function Detail() {
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Files </Typography>
</Stack>
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogUploadFileLog(true)
}} >
<Typography variant="button" display="block">Files</Typography>
</Button>
</Stack>
{ !isReversal ? (
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogUploadFileLog(true)
}} >
<Typography variant="button" display="block">Files</Typography>
</Button>
</Stack>
) : null }
</Stack>
{requestLog?.files?.map((documentType, index) => (
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
@@ -638,14 +651,17 @@ export default function Detail() {
<Typography variant="body2" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
</a>
</Stack>
<Stack direction="column" spacing={2}>
<IconButton onClick={() => {
setDialogDeleteFileLog(true)
setPathFile(documentType.path)
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
<Delete color='error' fontSize="small" />
</IconButton>
</Stack>
{ !isReversal ? (
<Stack direction="column" spacing={2}>
<IconButton onClick={() => {
setDialogDeleteFileLog(true)
setPathFile(documentType.path)
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
<Delete color='error' fontSize="small" />
</IconButton>
</Stack>
) : null }
</Stack>
))}

View File

@@ -456,7 +456,7 @@ export default function Router() {
element: <ClaimsCreate />,
},
{
path: 'claims/detail/:id',
path: 'claims/detail/:id/:id_claim',
element: <ClaimsDetail />,
},
{

View File

@@ -50,5 +50,14 @@
"txtNew" : "New",
"txtBeforeThat" : "Before that",
"txtDischargeDate" : "Discharge Date",
"txtPatner" : "Patner"
"txtPatner" : "Patner",
"txtSelected": "Selected",
"txtConfirmation": "Confirmation",
"txtReason": "Reason Decline",
"txtCancel": "Cancel",
"txtDecline": "Decline",
"txtApprove": "Approve",
"txtDialogConfirmation": "Are you sure you want to proceed with this action?",
"txtStartDate": "Start Date",
"txtEndDate": "End Date"
}

View File

@@ -50,5 +50,14 @@
"txtNew" : "Baru",
"txtBeforeThat" : "Sebelum",
"txtDischargeDate" : "Tanggal Keluar",
"txtPatner" : "Rekanan"
"txtPatner" : "Rekanan",
"txtSelected": "Terpilih",
"txtConfirmation": "Konfirmasi",
"txtReason": "Alasan Penolakan",
"txtCancel": "Batal",
"txtDecline": "Tolak",
"txtApprove": "Terima",
"txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?",
"txtStartDate": "Tanggal Mulai",
"txtEndDate": "Tanggal Akhir"
}

View File

@@ -13,6 +13,7 @@ import {
TableSortLabel,
Box,
Card,
Checkbox,
Grid,
FormControl,
InputLabel,
@@ -25,6 +26,10 @@ import {
linearProgressClasses,
} from '@mui/material';
import { visuallyHidden } from '@mui/utils';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
/* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */
@@ -43,6 +48,8 @@ import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '
import { InputAdornment } from '@mui/material';
import GetAppIcon from '@mui/icons-material/GetApp';
import { LanguageContext } from '@/contexts/LanguageContext';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
@@ -71,6 +78,7 @@ export default function Table<T>({
filterEndDate,
searchs,
exportReport,
selected,
}: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -98,34 +106,73 @@ export default function Table<T>({
return (
<TableHead>
<TableRow>
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
{selected.useSelected && selected.selectedRows.length > 0 ? (
<>
<TableCell style={{ backgroundColor: '#D1F1F1', }} align="left" colSpan={selected.totRows} sx={{ padding: 2 }}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Stack direction="row" alignItems="center">
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
<Typography variant='subtitle2'>
{selected.selectedRows.length > 0 ? selected.selectedRows.length : '0'} &nbsp; {localeData.txtSelected}
</Typography>
</Stack>
</Grid>
<Grid item>
<Stack direction="row" spacing={2}>
{selected.useDecline ? (
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('decline');}}>
<Typography variant='subtitle2'>{selected.txtDecline}</Typography>
</Button>
):''}
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('approve');}}>
<Typography variant='subtitle2'>{selected.txtApprove}</Typography>
</Button>
</Stack>
</Grid>
</Grid>
</TableCell>
))}
</>
):(
<>
{selected.useSelected ? (
<TableCell>
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
</TableCell>
):''}
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
</>
)}
</TableRow>
</TableHead>
);
@@ -162,7 +209,6 @@ export default function Table<T>({
params.setAppliedParams(parameters);
};
/* -------------------------------------------------------------------------- */
return (
// <Card>
<Grid container>
@@ -255,7 +301,7 @@ export default function Table<T>({
{/* Start date */}
{filterStartDate && filterStartDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
{/* <form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
<TextField
id="date-input"
type="date"
@@ -267,7 +313,18 @@ export default function Table<T>({
shrink: true,
}}
/>
</form>
</form> */}
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={localeData.txtStartDate}
value={filterStartDate.startDate}
onChange={(newValue:any) => {
filterStartDate.setStartDate( (newValue));
}}
inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} required/>}
/>
</LocalizationProvider>
</Grid>
) : null }
@@ -275,7 +332,7 @@ export default function Table<T>({
{filterEndDate && filterEndDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
{/* <form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
<TextField
id="date-input"
type="date"
@@ -287,7 +344,18 @@ export default function Table<T>({
shrink: true,
}}
/>
</form>
</form> */}
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={localeData.txtEndDate}
value={filterEndDate.endDate}
onChange={(newValue:any) => {
filterEndDate.setEndDate( (newValue));
}}
inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} required/>}
/>
</LocalizationProvider>
</Grid>
) : null }
@@ -349,6 +417,20 @@ export default function Table<T>({
) : rows && rows.length >= 1 ? (
rows.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{!selected.useSelected ? (
''
): (selected.useSelected && row.check_status === 'approved' && !row.check_claim ? (
<TableCell>
<Checkbox
checked={selected.selectedRows.includes(row.id)}
onChange={() => selected.handleCheckboxChange(row.id)}
/>
</TableCell>
):(
<TableCell>
</TableCell>
))}
{headCells &&
//@ts-ignore
headCells.map((head, headIndex) => (

View File

@@ -118,6 +118,58 @@ export default function TableList() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: false,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -252,12 +304,12 @@ export default function TableList() {
label: localeData.txtStatus,
isSort: true,
},
{
id: 'action',
align: 'right',
label: '',
isSort: false,
},
// {
// id: 'action',
// align: 'right',
// label: '',
// isSort: false,
// },
];
@@ -280,6 +332,7 @@ export default function TableList() {
const response = await axios.get(`/get-claim-requests`, {
params: { ...parameters, type: 'final-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
@@ -310,15 +363,15 @@ export default function TableList() {
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label>
,
action:
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}>
<Iconify icon="eva:eye-fill" />
View
</MenuItem>
</>
} />
// action:
// <TableMoreMenu actions={
// <>
// <MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}>
// <Iconify icon="eva:eye-fill" />
// View
// </MenuItem>
// </>
// } />
}))
);
@@ -357,6 +410,7 @@ export default function TableList() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>

View File

@@ -76,7 +76,7 @@ export default function DialogClaimSubmit({ member, getData, onClose, handleSubm
<Card sx={{ p: 1, background: '#f4f6f8'}}>
<Stack direction="row">
<Avatar
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
src=""
alt={member?.full_name ?? ''}
sx={{ marginTop: 1, width: 48, height: 48 }}
/>
@@ -143,7 +143,7 @@ export default function DialogClaimSubmit({ member, getData, onClose, handleSubm
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept="application/pdf"
accept="application/pdf, image/*"
/>
</ButtonBase>
</Stack>

View File

@@ -1,11 +1,11 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
import { Stack, Button, Checkbox, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box, IconButton, TextField } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
import { SetStateAction, useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
@@ -30,6 +30,8 @@ import MuiDialog from '@/components/MuiDialog';
import DialogMember from './DialogMember';
import DialogClaimSubmit from './DialogClaimSubmit';
import { fPostFormat } from '@/utils/formatTime';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
export default function TableListFinalLog() {
const navigate = useNavigate();
@@ -68,6 +70,7 @@ export default function TableListFinalLog() {
return `${day}${month}${year}`;
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
@@ -128,6 +131,85 @@ export default function TableListFinalLog() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: true,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
const handleSubmitData = () => {
//approve or decline
// if (!reasonDecline && valDialog == 'decline') {
// enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
// return false;
// }
axios.post('/submit-claims', {
selectedRows: selectedRows
})
.then((response) => {
if(response.data.meta.code === 200)
{
setOpenDialogSubmit(false);
enqueueSnackbar(response.data.meta.message, {variant : "success"});
}
else{
setOpenDialogSubmit(false);
enqueueSnackbar(response.data.meta.message, {variant : "error"});
}
getData();
setSelectedRows([]);
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
})
.then(() => {
});
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -335,11 +417,14 @@ export default function TableListFinalLog() {
params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'final-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
response.data.data.map((obj: any) => ({
...obj,
provider:formatTitleCase(obj.provider),
full_name:formatTitleCase(obj.full_name),
check_status: obj.status,
check_claim: obj.check_claim,
status:
obj.status === 'requested' ? (
<Label color='primary'>
@@ -445,6 +530,7 @@ export default function TableListFinalLog() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
@@ -466,7 +552,6 @@ export default function TableListFinalLog() {
member={dataViewClaimSubmit}
getData={getData}
onClose={(data:any, getData:any) => {
console.log('Data returned:', data);
getData();
setOpenDialogClaimSubmit(false);
}}
@@ -475,7 +560,43 @@ export default function TableListFinalLog() {
/>
}
maxWidth="sm"
/>
/>
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">{localeData.txtConfirmation}</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>{localeData.txtDialogConfirmation}</Typography>
{valDialog == "decline" ? (
<Stack direction='row' spacing={2} marginTop={2}>
<TextField
id="outlined-multiline-static"
label={localeData.txtReason}
multiline
rows={4}
defaultValue=""
onChange={handleReasonDeclineChange}
variant="outlined"
sx={{width:'100%'}}
/>
</Stack>
): ''}
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>{localeData.txtCancel}</Button>
<Button sx={{backgroundColor: (valDialog === 'decline' ? '' : '#19BBBB'), color: (valDialog === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(valDialog === 'decline' ? 'outlined' : 'contained')}>{(valDialog === "decline" ? localeData.txtDeclaine : 'Submit')}</Button>
</DialogActions>
</Dialog>
</>
);
}

View File

@@ -5,7 +5,7 @@ import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
import { useContext, useEffect, useState, SetStateAction } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
@@ -132,6 +132,58 @@ export default function TableList() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: false,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -187,7 +239,7 @@ export default function TableList() {
};
// handle start date
const [startDateValue, setStartDateValue] = useState('');
const [startDateValue, setStartDateValue] = useState(null);
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -211,7 +263,7 @@ export default function TableList() {
};
// handle end date
const [endDateValue, setEndDateValue] = useState('');
const [endDateValue, setEndDateValue] = useState(null);
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -328,6 +380,7 @@ export default function TableList() {
params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'request-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
@@ -440,8 +493,9 @@ export default function TableList() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
selected={selected}
//filterStartDate={filterStartDate}
//filterEndDate={filterEndDate}
/>
<MuiDialog
title={{name: nameMember}}

Binary file not shown.

Binary file not shown.

Binary file not shown.