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.status_final_log', '=', 'approved')
->where('request_logs.deleted_at', '=', null) ->where('request_logs.deleted_at', '=', null)
->when($start != 'all' && $end != 'all', function ($query) use ($start, $end) { ->when($start != 'all' && $end != 'all', function ($query) use ($start, $end) {
$query->where('request_logs.submission_date', '>=', $start) $query->where('request_logs.submission_date', '>=',Carbon::parse($start)->subDay())
->where('request_logs.submission_date', '<=', Carbon::parse($end)->addDay()); ->where('request_logs.submission_date', '<=',Carbon::parse($end)->addDay());
}) })
->select( ->select(
DB::raw('1 AS no'), DB::raw('1 AS no'),

View File

@@ -19,7 +19,7 @@ class ClaimRequestController extends Controller
* Display a listing of the resource. * Display a listing of the resource.
* @return Renderable * @return Renderable
*/ */
private static $code_prefix = 'CP'; private static $code_prefix = 'CLAIM';
public function index() public function index()
{ {
return view('client::index'); return view('client::index');
@@ -224,10 +224,6 @@ class ClaimRequestController extends Controller
public static function getNextCode() 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')) $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]+$"') ->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code'); ->value('max_numeric_code');
@@ -247,8 +243,14 @@ class ClaimRequestController extends Controller
{ {
// Pastikan $next_number adalah integer positif // Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number); $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 // 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', 'service:code,name',
'files', '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 = []; $dataBenefit = [];
if (count($data->requestLogBenefits) > 0) { if (count($data->requestLogBenefits) > 0) {

View File

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

View File

@@ -133,10 +133,21 @@ class ClaimRequestController extends Controller
if ($request->hasFile('additional_files')) { if ($request->hasFile('additional_files')) {
foreach ($request->additional_files as $file) { foreach ($request->additional_files as $file) {
$pathFile = File::storeFile('additional-files', $newClaimRequest->id, $file); $pathFile = File::storeFile('additional-files', $request->request_logs_id, $file);
$newClaimRequest->files()->updateOrCreate([ // $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', '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(), 'original_name' => $file->getClientOriginalName(),
'extension' => $file->getClientOriginalExtension(), 'extension' => $file->getClientOriginalExtension(),
'path' => $pathFile, 'path' => $pathFile,
@@ -265,7 +276,6 @@ class ClaimRequestController extends Controller
$date = date('ymd'); $date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan // 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 . $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) public function get_claim_requests(Request $request)

View File

@@ -13,6 +13,11 @@ use App\Models\File;
use Dompdf\Dompdf; use Dompdf\Dompdf;
use Dompdf\Options; use Dompdf\Options;
use Illuminate\Support\Facades\View; 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 class RequestLogController extends Controller
{ {
@@ -20,6 +25,7 @@ class RequestLogController extends Controller
* Display a listing of the resource. * Display a listing of the resource.
* @return Renderable * @return Renderable
*/ */
private static $code_prefix = 'CLAIM';
public function requestLog(Request $request) public function requestLog(Request $request)
{ {
$data = [ $data = [
@@ -244,6 +250,7 @@ class RequestLogController extends Controller
$results = DB::table('request_logs') $results = DB::table('request_logs')
->leftJoin('members', 'request_logs.member_id', '=', 'members.id') ->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
->where('request_logs.deleted_at', null)
->when($request->input('search'), function ($query, $search) { ->when($request->input('search'), function ($query, $search) {
$query->where(function ($query) use ($search) { $query->where(function ($query) use ($search) {
$query->orWhere('request_logs.code', 'like', "%" . $search . "%") $query->orWhere('request_logs.code', 'like', "%" . $search . "%")
@@ -820,4 +827,110 @@ class RequestLogController extends Controller
return response($pdf->output(), 200, $headers); 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::post('request-final-log', 'requestFinalLog');
Route::get('download-log/{request_log_id}', 'downlodLog'); Route::get('download-log/{request_log_id}', 'downlodLog');
Route::get('download-final-log/{request_log_id}', 'downlodFinalLog'); Route::get('download-final-log/{request_log_id}', 'downlodFinalLog');
Route::post('submit-claims', 'submitClaims');
}); });
//Notification //Notification
Route::controller(NotificationController::class)->group(function() { Route::controller(NotificationController::class)->group(function() {

View File

@@ -21,8 +21,18 @@ use Modules\Internal\Transformers\ClaimEditResource;
use Modules\Internal\Transformers\ClaimHistoryCareResource; use Modules\Internal\Transformers\ClaimHistoryCareResource;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory; use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory; 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\DB;
use Illuminate\Support\Facades\View; 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; use PDF;
@@ -34,41 +44,436 @@ class ClaimController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
// $serviceCode = 'IP'; $limit = $request->has('per_page') ? $request->input('per_page') : 10;
$claims = Claim::with([ $results = DB::table('claim_requests')
'member', ->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
'member.currentCorporate', ->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
'member.currentCorporate.currentPolicy', // ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
// 'member.currentPlan' => function($memberPlan) use ($serviceCode) { ->when($request->input('search'), function ($query, $search) {
// $memberPlan->where('plans.service_code', $serviceCode); $query->where(function ($query) use ($search) {
// }, $query->orWhere('members.name', 'like', "%" . $search . "%");
'member.currentPlan' => function($memberPlan) { $query->orWhere('claim_requests.code', 'like', "%" . $search . "%");
$memberPlan->join('claim_requests', 'claim_requests.service_code', '=', 'plans.service_code'); $query->orWhere('members.member_id', 'like', "%" . $search . "%");
},
'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 . "%");
});
}); });
}) })
->where('status', '!=', 'requested') // Penjagaan agar approve baru masuk ke claim management ->when($request->has('orderBy'), function ($query) use ($request) {
->latest() $orderBy = $request->orderBy;
->paginate(10); $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"); 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') DB::table('claim_requests')
->where('claim_request_id', $id) ->where('claim_requests.id', $id)
->update( ->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) public function approve($id)
{ {
//Get claim request id DB::table('claim_requests')
$data_claim_requests = DB::table('claim_requests') ->where('claim_requests.id', $id)
->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)
->update( ->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\Exceptions\ImportRowException;
use App\Events\ClaimRequested; 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\File;
use App\Models\FilesMcu; use App\Models\FilesMcu;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Models\Member; use App\Models\Member;
use App\Models\MemberPlan;
use App\Models\Plan;
use App\Models\RequestLogBenefit;
use Carbon\Carbon;
class ClaimRequestController extends Controller class ClaimRequestController extends Controller
{ {
private static $code_prefix = 'CRQ-C'; private static $code_prefix = 'CLAIM';
/** /**
* Display a listing of the resource. * Display a listing of the resource.
@@ -41,11 +49,17 @@ class ClaimRequestController extends Controller
}); });
}) })
->when($request->start_date, function ($q, $startDate) { ->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) { ->when($request->end_date, function ($q, $endDate) use ($request) {
$q->where('submission_date', '<', $endDate); // 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) { ->when($request->service_code, function ($q, $serviceCode) {
$q->whereIn('service_code', $serviceCode); $q->whereIn('service_code', $serviceCode);
}) })
@@ -429,6 +443,7 @@ class ClaimRequestController extends Controller
$import = new ImportService(); $import = new ImportService();
$import->read($fileRead); $import->read($fileRead);
$import->write($fileWrite, 'xsls'); $import->write($fileWrite, 'xsls');
foreach ($import->sheetsIterator() as $sheetIndex => $sheet) { foreach ($import->sheetsIterator() as $sheetIndex => $sheet) {
if ($sheetIndex == 1) { // Rename First Sheet to Writer if ($sheetIndex == 1) { // Rename First Sheet to Writer
$firstWriterSheet = $import->writer->getCurrentSheet(); $firstWriterSheet = $import->writer->getCurrentSheet();
@@ -445,6 +460,9 @@ class ClaimRequestController extends Controller
$result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']); $result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']);
$import->addArrayToRow($result_headers); $import->addArrayToRow($result_headers);
$imported_claim_data = 0;
$failed_claim_data = [];
$doc_headers_indexes = []; $doc_headers_indexes = [];
foreach ($sheet->getRowIterator() as $index => $row) { foreach ($sheet->getRowIterator() as $index => $row) {
if ($index == 1) { // First Row Must be Header if ($index == 1) { // First Row Must be Header
@@ -465,25 +483,28 @@ class ClaimRequestController extends Controller
} }
try { // Process the Row Data try { // Process the Row Data
$claimRequestService = new ClaimRequestService(); $claimRequestService = new ClaimRequestService();
$claimRequestService->handleClaimRequestRow($row_data); $claimRequestService->handleClaimRequestRow($row_data);
// Write Success Result to File // Write Success Result to File
// $import->read($fileRead); // $import->read($fileRead);
// $import->write($fileWrite, 'xsls'); // $import->write($fileWrite, 'xsls');
$result_headers = array_merge($row_data, ['Ingest Code' =>200, 'Ingest Note' => 'Success']); $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()); $import->addArrayToRow($result_headers, $sheet->getName());
$imported_claim_data++;
} catch (ImportRowException $e) { } catch (ImportRowException $e) {
// Write Data Validation Error to File // Write Data Validation Error to File
// $import->read($fileRead); // $import->read($fileRead);
// $import->write($fileWrite, 'xsls'); // $import->write($fileWrite, 'xsls');
$new_claim_data = $import->addArrayToRow(array_merge($row_data, [
$import->addArrayToRow(array_merge($row_data, [
'Ingest Code' => $e->getCode(), 'Ingest Code' => $e->getCode(),
'Ingest Note' => $e->getMessage(), 'Ingest Note' => $e->getMessage(),
]), $sheet->getName()); ]), $sheet->getName());
$failed_claim_data[] = ['row_number' => $index, 'error' => $e->getMessage(),'data' => $new_claim_data];
} }
// catch (\Exception $e) { // catch (\Exception $e) {
// // throw new \Exception($e); // // throw new \Exception($e);
@@ -502,104 +523,20 @@ class ClaimRequestController extends Controller
$import->reader->close(); $import->reader->close();
Storage::delete('temp/' . $file_name); Storage::delete('temp/' . $file_name);
$import->writer->close(); $import->writer->close();
return [ return [
// 'total_successed_row' => $imported_plan_data,
// 'total_failed_row' => count($failed_plan_data),
// 'failed_row' => $failed_plan_data,
'result_file' => [ 'result_file' => [
'url' => Storage::disk('public')->url('temp/result-' . $file_name), 'url' => Storage::disk('public')->url('temp/result-' . $file_name),
'name' => '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) 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 = ClaimRequest::findOrFail($claimRequestId);
$claimRequest->load([ $claimRequest->load([
'requestLog', 'requestLog',
@@ -704,10 +641,6 @@ class ClaimRequestController extends Controller
public static function getNextCode() 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')) $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]+$"') ->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code'); ->value('max_numeric_code');
@@ -725,11 +658,17 @@ class ClaimRequestController extends Controller
public static function makeCode($next_number) public static function makeCode($next_number)
{ {
// Pastikan $next_number adalah integer positif // Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number); $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);
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
} }
public function requestFiles(Request $request, $claim_id) 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'); 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; break;
case 'claim-request': case 'claim-request':
return Helper::responseJson([ return Helper::responseJson([
'file_name' => "Template Format Claim.xlsx", 'file_name' => "Template Claim Request.xlsx",
"file_url" => url('files/Template Format Claim.xlsx') "file_url" => url('files/Template Claim Request.xlsx')
]); ]);
break; break;
case 'request-log': 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::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']);
Route::get('claims', [ClaimController::class, 'index']); 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-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}/update-diagnosis', [ClaimController::class, 'updateDiagnosis'])->name('claim.update-diagnosis');
Route::post('claims/{id}/decline', [ClaimController::class, 'decline'])->name('claim.decline'); 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::post('claims', [ClaimController::class, 'store']);
Route::get('claims/{id}', [ClaimController::class, 'show']); Route::get('claims/{id}', [ClaimController::class, 'show']);
Route::get('claims/cek_status/{id}', [ClaimController::class, 'cekStatus']);
Route::put('claims/{id}', [ClaimController::class, 'update']); Route::put('claims/{id}', [ClaimController::class, 'update']);
Route::get('claims/{id}/edit', [ClaimController::class, 'edit']); Route::get('claims/{id}/edit', [ClaimController::class, 'edit']);
Route::post('check-limit', [ClaimController::class, 'checkLimit']); 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', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
Route::get('claim-requests/list-member', [ClaimRequestController::class, 'getClaimMemberInfiniteScroll']); // Bagaskoro, BSD 31 Oktober 2023 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::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', [ClaimRequestController::class, 'createNew']); // Bagaskoro, BSD 2 November 2023
Route::post('claim-requests/{id}/update', [ClaimRequestController::class, 'update']); Route::post('claim-requests/{id}/update', [ClaimRequestController::class, 'update']);
Route::post('claim-requests/import', [ClaimRequestController::class, 'importClaim'])->name('claim-requests.importClaim'); Route::post('claim-requests/import', [ClaimRequestController::class, 'importClaim'])->name('claim-requests.importClaim');
Route::get('claim-requests/detail/{id}', [ClaimRequestController::class, 'claimRequestDetail']); Route::get('claim-requests/detail/{id}', [ClaimRequestController::class, 'claimRequestDetail']);
Route::post('claim-requests/{id}/invoice-files', [ClaimRequestController::class, 'invoiceFiles']); Route::post('claim-requests/{id}/invoice-files', [ClaimRequestController::class, 'invoiceFiles']);
Route::post('claim-requests/{id}/request-files', [ClaimRequestController::class, 'requestFiles']); 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']); 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, '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 = [ $response = [
'id' => $data['id'], 'id' => $data['id'],
'code' => $data['code'], 'code' => $data['code'],
'status' => $data['status'],
'request_log_id' => $data['request_log_id'], 'request_log_id' => $data['request_log_id'],
'request_log' => $requestLogData, 'request_log' => $requestLogData,
'provider' => $data['request_log']['organization']['name'], 'provider' => $data['request_log']['organization']['name'],
@@ -98,7 +110,9 @@ class ClaimRequestShowResource extends JsonResource
'service_type' => Helper::serviceName( $data['request_log']['service_code']), 'service_type' => Helper::serviceName( $data['request_log']['service_code']),
'claim_method' => $data['request_log']['payment_type'], 'claim_method' => $data['request_log']['payment_type'],
'files' => $data['request_log']['files'], 'files' => $data['request_log']['files'],
'reason_decline' => $data['reason_decline'],
// 'benefit_data' => $benefitDetailLog, // '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\RequestLogMedicine;
use App\Models\Organization; use App\Models\Organization;
use App\Models\Exclusion; use App\Models\Exclusion;
use App\Models\ClaimRequest;
use App\Models\Icd; use App\Models\Icd;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Models\CorporatePolicy; use App\Models\CorporatePolicy;
@@ -36,6 +37,23 @@ class RequestLogShowResource extends JsonResource
$benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray(); $benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray();
$medicineDetailLog = RequestLogMedicine::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(); $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){ if ($provider){
$providerName = $provider->name; $providerName = $provider->name;
@@ -95,6 +113,7 @@ class RequestLogShowResource extends JsonResource
$data = [ $data = [
'id' => $requestLog['id'], 'id' => $requestLog['id'],
'code' => $requestLog['code'], 'code' => $requestLog['code'],
'code_claim' => $claimCode,
'member_id' => $requestLog['member']['member_id'], 'member_id' => $requestLog['member']['member_id'],
'corporate_id' => $corporateId, 'corporate_id' => $corporateId,
'policy_number' =>$policyNumber->code ? $policyNumber->code : '-', 'policy_number' =>$policyNumber->code ? $policyNumber->code : '-',
@@ -128,6 +147,7 @@ class RequestLogShowResource extends JsonResource
'catatan' => $requestLog['catatan'], 'catatan' => $requestLog['catatan'],
'reason' => $requestLog['reason'], 'reason' => $requestLog['reason'],
'diagnosis' => $icd, '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\Member;
use App\Models\User; use App\Models\User;
use App\Models\Service; use App\Models\Service;
use DateTime;
class Helper class Helper
{ {
@@ -426,4 +427,20 @@ class Helper
ini_set('memory_limit', '256M'); 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', 'service_code',
'policy_id', 'policy_id',
'status', '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', 'claim_id',
'organization_id', 'organization_id',
'code', 'code',
@@ -43,69 +52,29 @@ class ClaimRequest extends Model
]; ];
public static $doc_headers_to_field_map = [ public static $doc_headers_to_field_map = [
"PAYOR ID" => "payor_id", "Code LOG" => "code",
"CORPORATE ID" => "corporate_id", "Date Claim Submission" => "date_submission",
"POLICY NUMBER" => "policy_number", "Total Billing" => "total_billing",
"MEMBER ID" => "member_id", "Benefit Code" => "benefit_code",
"MEMBER NAME" => "member_name", "Amt Incurred" => "amount_incurred",
"RECORD TYPE (P/D)" => "record_type", "Amt Approved" => "amount_apporve",
"BENEFIT CODE" => "benefit_code", "Amt Not Approved" => "amount_not_apporve",
"BENEFIT DESC" => "benefit_desc", "Excess Paid" => "excess_paid",
"CLAIM TYPE" => "claim_type", "Reason" => "reason",
"CLAIM PROCESS STATUS" => "status", "QC" => "qc",
"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",
]; ];
public static $listing_doc_headers = [ public static $listing_doc_headers = [
"PAYOR ID", "Code LOG",
"CORPORATE ID", "Date Claim Submission",
"POLICY NUMBER", "Total Billing",
"MEMBER ID", "Benefit Code",
"MEMBER NAME", "Amt Incurred",
"RECORD TYPE (P/D)", "Amt Approved",
"CLAIM TYPE", "Amt Not Approved",
"CLAIM PROCESS STATUS", "Excess Paid",
"CLIENT CLAIM ID", // "Reason",
"REFERENCE NO", "QC",
"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",
]; ];

View File

@@ -6,6 +6,9 @@ use App\Events\ClaimApproved;
use App\Events\ClaimRequested; use App\Events\ClaimRequested;
use App\Models\Claim; use App\Models\Claim;
use App\Models\ClaimRequest; use App\Models\ClaimRequest;
use App\Models\RequestLog;
use App\Models\Benefit;
use App\Models\RequestLogBenefit;
use App\Models\Organization; use App\Models\Organization;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Models\Icd; use App\Models\Icd;
@@ -20,6 +23,7 @@ use Str;
class ClaimRequestService{ 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) public static function storeClaimRequest($row = null, $code, $member, $paymentType, $serviceCode, $requestLogID = null, $submissionDate = null, $status = 'requested', $organization_code = null)
{ {
// try { // try {
@@ -32,24 +36,74 @@ class ClaimRequestService{
'code' => $row['provider_code'] 'code' => $row['provider_code']
]), 403, null, $row); ]), 403, null, $row);
} }
}; }
DB::beginTransaction(); DB::beginTransaction();
$claimRequestData = [ if ($status == 'submission'){
'code' => $code, $claimManagement = 1;
'request_log_id' => $requestLogID ?? 0, $submissionDateClaimManagement = $submissionDate;
'member_id' => $member->id, $submissionByClaimManagement = auth()->user()->id;
'submission_date' => $submissionDate ?? now(), $statusClaim = 'received';
'status' => $status, } else {
'payment_type' => $paymentType, $claimManagement = 0;
'service_code' => $serviceCode, $submissionDateClaimManagement = null;
'policy_id' => $member->currentPolicy->id ?? null, $submissionByClaimManagement = null;
'organization_id' => $organization ? $organization->id : 0, $statusClaim = null;
]; }
if (count($row)>0){
$claimRequest = ClaimRequest::create($claimRequestData); 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(); DB::commit();
return $claimRequest; return $claimRequest;
@@ -60,8 +114,6 @@ class ClaimRequestService{
// } // }
} }
public static function storeClaimManagement($row, $member, $claim_request_id){ public static function storeClaimManagement($row, $member, $claim_request_id){
try { try {
$organization = 0; $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) public function handleClaimRequestRow($row)
{ {
try { try {
$member = Member::where('member_id', $row['member_id'])->with(['currentPlan'])->first(); $requestLog = RequestLog::where('code', $row['code'])->first();
if(!$member){ if(!$requestLog){
throw new ImportRowException(__('Member Tidak ditemukan'), 0, null, $row); 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( $newClaimRequest = $this->storeClaimRequest(
row: $row, row: $row,
code: $code, code: $code,
member: $member, member: $member,
paymentType: $paymentType, paymentType: $paymentType,
serviceCode: $serviceCode, serviceCode: $serviceCode,
requestLogID: $requestLog->id,
submissionDate: $submissionDate, submissionDate: $submissionDate,
status: $status, status: $status,
organization_code: $organization_id organization_code: $organization->code
); );
$newlyCreatedID = $newClaimRequest->id; $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; return $newClaimRequest;
} catch (\Exception $e) { } catch (\Exception $e) {
throw $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() { export default function ServiceMonitoring() {
@@ -591,6 +593,44 @@ export default function ServiceMonitoring() {
)} )}
</Grid> */} </Grid> */}
</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>
</Grid> </Grid>

View File

@@ -1,35 +1,30 @@
import MuiDialog from "@/components/MuiDialog"; 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 { Paper } from "@mui/material";
import { Stack } from '@mui/material'; import { Stack } from '@mui/material';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { ClaimRequest, Files } from '@/@types/claims'; import { DetailClaimRequest } from "../Model/Types";
import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; import { fDateTimesecond, toTitleCase } from "@/utils/formatTime";
import axios from "@/utils/axios"; import axios from "@/utils/axios";
import { enqueueSnackbar, useSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import * as Yup from 'yup'; import { RHFTextField } from "@/components/hook-form";
import { yupResolver } from '@hookform/resolvers/yup';
type DialogConfirmationType = { type DialogConfirmationType = {
openDialog: boolean; openDialog: boolean;
setOpenDialog: any; setOpenDialog: any;
onSubmit?: void; onSubmit?: void;
approve: string; 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 navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
date: claimRequest?.date,
id: claimRequest?.id,
reason: claimRequest?.reason
});
});
const handleChange = (field, value) => { const handleChange = (field, value) => {
setFormData((prevData) => ({ setFormData((prevData) => ({
...prevData, ...prevData,
@@ -38,24 +33,28 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
}; };
const handleApprove = () => {
setFormData((prevData) => ({
...prevData,
status: approve,
}));
handleSubmit();
};
const handleSubmit = () => { const handleSubmit = () => {
axios axios
.post(`customer-service/request/final-log`, formData) .post(`claim-requests/${requestLog?.id}/submition`, formData)
.then((response) => { .then((response) => {
enqueueSnackbar('Verification Request LOG Success', { variant: 'success' }); enqueueSnackbar('Submition Claim Request Success', { variant: 'success' });
setOpenDialog(false); setOpenDialog(false);
if (requestLog?.service_type == 'Inpatient'){ navigate('/claim-requests')
navigate('/case_management/inpatient_monitoring');
} else {
navigate('/custormer-service/final-log');
}
}) })
.catch(({ response }) => { .catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
}); });
} }
const style1 = { const style1 = {
color: '#919EAB', color: '#919EAB',
width: '30%' width: '30%'
@@ -66,34 +65,57 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
const marginBottom1 = { const marginBottom1 = {
marginBottom: 1, marginBottom: 1,
} }
const marginBottom2 = { const marginBottom2 = {
marginBottom: 2, marginBottom: 2,
} }
const resetForm = () => {
setFormData({
status: approve,
no_identitas: requestLog?.no_identitas ?? '',
keterangan: '',
hak_kamar_pasien: '',
penempatan_kamar: '',
});
};
const handleCloseDialog = () => { const handleCloseDialog = () => {
setOpenDialog(false); 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 = () => ( const getContent = () => (
<Stack spacing={1} marginTop={2}> <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}> <Grid item xs={12} md={12} marginTop={4}>
<Card sx={{padding:2, marginTop:2}} > <Card sx={{padding:2, marginTop:2}} >
<Stack direction='row' spacing={2} sx={marginBottom1}> <Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Member ID</Typography> <Typography variant='subtitle2' sx={style1} gutterBottom>Code</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.member_id}</Typography> <Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.code}</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>
</Stack> </Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}> <Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography> <Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography> <Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography>
</Stack> </Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}> <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> <Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'}</Typography>
</Stack> </Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}> <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> <Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.service_type}</Typography>
</Stack> </Stack>
</Card> </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> </Grid>
<DialogActions> <DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialog}>Cancel</Button> <Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialog}>Cancel</Button>
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Submit</Button>
{approve == 'approved' ? (
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Approve</Button>
) : (
<Button color="error" variant="contained" onClick={() => handleApprove()}>Decline</Button>
) }
</DialogActions> </DialogActions>
</Stack> </Stack>
); );
@@ -168,7 +142,7 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
openDialog={openDialog} openDialog={openDialog}
setOpenDialog={setOpenDialog} setOpenDialog={setOpenDialog}
content={getContent()} 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 const id = currentClaim?.id
useEffect(() => { useEffect(() => {
setDate(currentClaim?.submission_date) setDate(currentClaim?.submission_date)
}, [currentClaim]); }, [currentClaim]);
console.log(date);
useEffect(() => { useEffect(() => {
if (isEdit && currentClaim) { if (isEdit && currentClaim) {
reset(defaultValues); reset(defaultValues);
@@ -254,7 +252,6 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
</Stack> </Stack>
) )
return ( return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}> <FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}> <Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
@@ -310,10 +307,14 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
<RHFDateTimePicker <RHFDateTimePicker
{...field} {...field}
label="Date of Submission" label="Date of Submission"
value={field.value || null} value={field.value}
onChange={() => setDate(field.value)} onChange={() =>
setDate(field.value)
}
dateFormat='dd MMM yyyy HH:mm:ss'
/> />
)} )}
/> />
</Grid> </Grid>
<Grid item xs={3}> <Grid item xs={3}>

View File

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

View File

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

View File

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

View File

@@ -51,6 +51,7 @@ export type DetailClaimRequest = {
benefit_data : BenefitData[], benefit_data : BenefitData[],
diagnosis : Diagnosis[], diagnosis : Diagnosis[],
request_log : RequestLogType | undefined, request_log : RequestLogType | undefined,
reason_decline : string,
} }
export type RequestLogType = { 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() { export default function Claims() {
const pageTitle = 'Claim'; const pageTitle = 'Claim Management';
return ( return (
<Page title={ pageTitle } sx={{ mx: 2}}> <Page title={ pageTitle } sx={{ mx: 2}}>
@@ -16,7 +16,7 @@ export default function Claims() {
links={[ links={[
{ name: 'Dashboard', href: '/dashboard' }, { name: 'Dashboard', href: '/dashboard' },
{ {
name: 'Claim', name: 'Claim Management',
href: '/claims', href: '/claims',
}, },
]} ]}

View File

@@ -1,6 +1,7 @@
// @mui // @mui
import { import {
Box, Box,
Grid,
Button, Button,
Card, Card,
Collapse, Collapse,
@@ -17,12 +18,24 @@ import {
ButtonGroup, ButtonGroup,
Tooltip, Tooltip,
TableHead, TableHead,
Checkbox,
InputAdornment,
TableSortLabel,
FormControl
} from '@mui/material'; } 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 FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import AssessmentIcon from '@mui/icons-material/Assessment'; import AssessmentIcon from '@mui/icons-material/Assessment';
// hooks // hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
import { LoadingButton } from '@mui/lab';
// components // components
import axios from '../../utils/axios'; import axios from '../../utils/axios';
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data'; import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
@@ -32,23 +45,146 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded';
import { Chip } from '@mui/material'; import { Chip } from '@mui/material';
import Iconify from '@/components/Iconify'; import Iconify from '@/components/Iconify';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { fDate } from '../../utils/formatTime'; import { fDate, fDateTime } from '../../utils/formatTime';
import { Claims } from '@/@types/claims'; import { Claims } from '@/@types/claims';
import Label from '@/components/Label'; import Label from '@/components/Label';
import { capitalizeFirstLetter } from '@/utils/formatString'; import { capitalizeFirstLetter } from '@/utils/formatString';
import TableMoreMenu from '@/components/table/TableMoreMenu'; import TableMoreMenu from '@/components/table/TableMoreMenu';
import Edit from '@mui/icons-material/Edit'; import Edit from '@mui/icons-material/Edit';
import { Download } from '@mui/icons-material'; 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() { 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 [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 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) { function SearchInput(props: any) {
// SEARCH // SEARCH
const searchInput = useRef<HTMLInputElement>(null); const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState('');
const handleSearchChange = (event: any) => { const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''; const newSearchText = event.target.value ?? '';
@@ -73,7 +209,7 @@ export default function List() {
useEffect(() => { useEffect(() => {
// Trigger First Search // Trigger First Search
setSearchText(searchParams.get('search') ?? ''); // setSearchText(searchParams.get('search') ?? '');
}, []); }, []);
return ( 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 // Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true); const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>( const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
LaravelPaginatedDataDefault LaravelPaginatedDataDefault
); );
const loadDataTableData = async (appliedFilter: any | null = null) => { const loadDataTableData = async (appliedFilter: any | null = null) => {
setDataTableLoading(true); 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 filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/claims', { params: filter }); const response = await axios.get('/claims', {
// console.log(response.data); params: {
search: searchText,
start_date: formattedDate ? formattedDate : null,
end_date:formattedDate1,
provider: dataProvider,
order: order,
orderBy: orderBy,
page: perPage,
}
});
setDataTableLoading(false); setDataTableLoading(false);
setDataTableData(response.data); setDataTableData(response.data);
}; };
const getProvider = async () => {
const response = await axios.get('/claims/get-provider');
setProviders(response.data)
}
const applyFilter = async (searchFilter: { search: string }) => { const applyFilter = async (searchFilter: { search: string }) => {
await loadDataTableData(searchFilter); await loadDataTableData(searchFilter);
setSearchParams(searchFilter); setSearchParams(searchFilter);
}; };
const handlePageChange = (event: ChangeEvent, value: number): void => { const handlePageChange = (event: ChangeEvent, value: number): void => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); setPerPage(value);
loadDataTableData(filter);
setSearchParams(filter);
}; };
useEffect(() => { const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
loadDataTableData(); 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 = { const headStyle = {
fontWeight: 'bold', 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 // Called on every row to map the data to the columns
function createData(data: Claims): Claims { function createData(data: Claims): Claims {
return { return {
@@ -171,10 +597,18 @@ export default function List() {
{ {
/* ------------------ TABLE ROW ------------------ */ /* ------------------ TABLE ROW ------------------ */
} }
function Row(props: { row: ReturnType<typeof createData> }) { function Row(props: { row: ReturnType<typeof createData>, isSelected: boolean, onSelect: (id: string) => void }) {
const { row } = props; 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 [open, setOpen] = React.useState(false);
const test = 1000;
return ( return (
<React.Fragment> <React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}> <TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
@@ -183,16 +617,22 @@ export default function List() {
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />} {open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton> </IconButton>
</TableCell> */} </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.code}</TableCell> */}
<TableCell align="left">{row.member?.current_plan?.code}</TableCell> <TableCell align="left">{row?.name}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.payor_id}</TableCell> <TableCell align="left">{row?.member_id}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.code}</TableCell> <TableCell align="left">{row?.created_at ? fDateTime(row?.created_at) : ''}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.current_policy?.code}</TableCell> <TableCell align="left">{row?.plan_code}</TableCell>
<TableCell align="left">{row.member?.member_id}</TableCell> <TableCell align="left">{row?.service_code}</TableCell>
<TableCell align="left">{row.benefit_desc}</TableCell> <TableCell align="left">{row?.corporate_policies}</TableCell>
<TableCell align="left">{row?.provider}</TableCell>
<TableCell align="center"> <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 == 'draft' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'requested' && (<Label color='primary' 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>)} {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>)} {row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
</TableCell> </TableCell>
<TableMoreMenu actions={ <TableCell align="left">
<> <TableMoreMenu actions={
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }> <>
<Edit /> <MenuItem onClick={() => navigate('/claims/detail/'+row.id_log+'/'+row.id+'') }>
Edit <FindInPageOutlinedIcon />
</MenuItem> Detail
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }> </MenuItem>
<FindInPageOutlinedIcon /> </>
Detail } />
</MenuItem> </TableCell>
</>
} />
</TableRow> </TableRow>
@@ -236,40 +675,77 @@ export default function List() {
/* ------------------ END TABLE ROW ------------------ */ /* ------------------ END TABLE ROW ------------------ */
} }
function TableContent() { function TableContent() {
return ( return (
<Table aria-label="collapsible table"> <Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */} {/* ------------------ TABLE HEADER ------------------ */}
<TableHead> <TableHead>
<TableRow> <TableRow>
{/* <TableCell style={headStyle} align="left" /> */} {selectedRows.length > 0 ? (
<TableCell style={headStyle} align="left"> <>
Code <TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
</TableCell> <Stack direction="row">
<TableCell style={headStyle} align="left"> <Checkbox checked={selectAll} onChange={handleSelectAll} />
Plan ID {selectedRows.length > 0 ? selectedRows.length : '0'} &nbsp;<Typography variant='subtitle2'>Selected</Typography>
</TableCell> </Stack>
<TableCell style={headStyle} align="left"> </TableCell>
Payor ID <TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={6}>
</TableCell>
<TableCell style={headStyle} align="left"> </TableCell>
Corporate ID <TableCell style={{ backgroundColor: '#D1F1F1' }} align="right" colSpan={2}>
</TableCell> <Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {setOpenDialogSubmit(true);
<TableCell style={headStyle} align="left"> setApprove('decline');}}>
Policy Number <Typography variant='subtitle2'>Decline</Typography>
</TableCell> </Button>
<TableCell style={headStyle} align="left"> </TableCell>
Member ID <TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
</TableCell> <Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {setOpenDialogSubmit(true);
<TableCell style={headStyle} align="left"> setApprove('approve');}}>
Benefit Desc <Typography variant='subtitle2'>Approve</Typography>
</TableCell> </Button>
<TableCell style={headStyle} align="left"> </TableCell>
Status
</TableCell> </>
<TableCell style={headStyle} align="left"> ) : (
<>
<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>
))}
</>
)}
</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
{/* ------------------ END TABLE HEADER ------------------ */} {/* ------------------ END TABLE HEADER ------------------ */}
@@ -278,7 +754,7 @@ export default function List() {
{dataTableIsLoading ? ( {dataTableIsLoading ? (
<TableBody> <TableBody>
<TableRow> <TableRow>
<TableCell colSpan={8} align="center"> <TableCell colSpan={11} align="center">
Loading Loading
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -286,7 +762,7 @@ export default function List() {
) : dataTableData.data.length === 0 ? ( ) : dataTableData.data.length === 0 ? (
<TableBody> <TableBody>
<TableRow> <TableRow>
<TableCell colSpan={8} align="center"> <TableCell colSpan={11} align="center">
No Data No Data
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -294,7 +770,7 @@ export default function List() {
) : ( ) : (
<TableBody> <TableBody>
{dataTableData.data.map((row) => ( {dataTableData.data.map((row) => (
<Row key={row.id} row={row} /> <Row key={row.id} row={row} isSelected={selectedRows.includes(row.id)} onSelect={handleRowSelect} />
))} ))}
</TableBody> </TableBody>
)} )}
@@ -305,7 +781,212 @@ export default function List() {
return ( return (
<Card> <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 <DataTable
isLoading={dataTableIsLoading} isLoading={dataTableIsLoading}
@@ -314,6 +995,43 @@ export default function List() {
handlePageChange={handlePageChange} handlePageChange={handlePageChange}
TableContent={<TableContent />} 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> </Card>
); );
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,5 +50,14 @@
"txtNew" : "New", "txtNew" : "New",
"txtBeforeThat" : "Before that", "txtBeforeThat" : "Before that",
"txtDischargeDate" : "Discharge Date", "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", "txtNew" : "Baru",
"txtBeforeThat" : "Sebelum", "txtBeforeThat" : "Sebelum",
"txtDischargeDate" : "Tanggal Keluar", "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, TableSortLabel,
Box, Box,
Card, Card,
Checkbox,
Grid, Grid,
FormControl, FormControl,
InputLabel, InputLabel,
@@ -25,6 +26,10 @@ import {
linearProgressClasses, linearProgressClasses,
} from '@mui/material'; } from '@mui/material';
import { visuallyHidden } from '@mui/utils'; import { visuallyHidden } from '@mui/utils';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
/* ---------------------------------- axios --------------------------------- */ /* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios'; import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */ /* ---------------------------------- react --------------------------------- */
@@ -43,6 +48,8 @@ import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '
import { InputAdornment } from '@mui/material'; import { InputAdornment } from '@mui/material';
import GetAppIcon from '@mui/icons-material/GetApp'; import GetAppIcon from '@mui/icons-material/GetApp';
import { LanguageContext } from '@/contexts/LanguageContext'; import { LanguageContext } from '@/contexts/LanguageContext';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
/* --------------------------------- styled --------------------------------- */ /* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
@@ -71,6 +78,7 @@ export default function Table<T>({
filterEndDate, filterEndDate,
searchs, searchs,
exportReport, exportReport,
selected,
}: TableListProps<T>) { }: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */ /* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => { const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -98,34 +106,73 @@ export default function Table<T>({
return ( return (
<TableHead> <TableHead>
<TableRow> <TableRow>
{headCells && {selected.useSelected && selected.selectedRows.length > 0 ? (
headCells.map((headCell, index) => ( <>
<TableCell <TableCell style={{ backgroundColor: '#D1F1F1', }} align="left" colSpan={selected.totRows} sx={{ padding: 2 }}>
key={index} <Grid container alignItems="center" justifyContent="space-between">
sortDirection={orders?.orderBy === headCell.id ? orders.order : false} <Grid item>
// @ts-ignore <Stack direction="row" alignItems="center">
align={headCell.align} <Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
sx={{ padding: 2 }} <Typography variant='subtitle2'>
width={headCell.width ? headCell.width : 'auto'} {selected.selectedRows.length > 0 ? selected.selectedRows.length : '0'} &nbsp; {localeData.txtSelected}
> </Typography>
{headCell.isSort ? ( </Stack>
<TableSortLabel </Grid>
active={orders?.orderBy === headCell.id} <Grid item>
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'} <Stack direction="row" spacing={2}>
onClick={createSortHandler(headCell.id)} {selected.useDecline ? (
> <Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('decline');}}>
{headCell.label} <Typography variant='subtitle2'>{selected.txtDecline}</Typography>
{orders?.orderBy === headCell.id ? ( </Button>
<Box component="span" sx={visuallyHidden}> ):''}
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} <Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('approve');}}>
</Box> <Typography variant='subtitle2'>{selected.txtApprove}</Typography>
) : null} </Button>
</TableSortLabel> </Stack>
) : ( </Grid>
headCell.label </Grid>
)}
</TableCell> </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> </TableRow>
</TableHead> </TableHead>
); );
@@ -162,7 +209,6 @@ export default function Table<T>({
params.setAppliedParams(parameters); params.setAppliedParams(parameters);
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
return ( return (
// <Card> // <Card>
<Grid container> <Grid container>
@@ -255,7 +301,7 @@ export default function Table<T>({
{/* Start date */} {/* Start date */}
{filterStartDate && filterStartDate.useFilter ? ( {filterStartDate && filterStartDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}> <Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterStartDate.handleStartDateChange(event)}> {/* <form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
<TextField <TextField
id="date-input" id="date-input"
type="date" type="date"
@@ -267,7 +313,18 @@ export default function Table<T>({
shrink: true, 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> </Grid>
) : null } ) : null }
@@ -275,7 +332,7 @@ export default function Table<T>({
{filterEndDate && filterEndDate.useFilter ? ( {filterEndDate && filterEndDate.useFilter ? (
<Grid item xs={12} lg={2} xl={2}> <Grid item xs={12} lg={2} xl={2}>
<form onChange={(event) => filterEndDate.handleEndDateChange(event)}> {/* <form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
<TextField <TextField
id="date-input" id="date-input"
type="date" type="date"
@@ -287,7 +344,18 @@ export default function Table<T>({
shrink: true, 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> </Grid>
) : null } ) : null }
@@ -349,6 +417,20 @@ export default function Table<T>({
) : rows && rows.length >= 1 ? ( ) : rows && rows.length >= 1 ? (
rows.map((row, rowIndex) => ( rows.map((row, rowIndex) => (
<TableRow key={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 && {headCells &&
//@ts-ignore //@ts-ignore
headCells.map((head, headIndex) => ( 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 ----------------------------- */ /* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
@@ -252,12 +304,12 @@ export default function TableList() {
label: localeData.txtStatus, label: localeData.txtStatus,
isSort: true, isSort: true,
}, },
{ // {
id: 'action', // id: 'action',
align: 'right', // align: 'right',
label: '', // label: '',
isSort: false, // isSort: false,
}, // },
]; ];
@@ -280,6 +332,7 @@ export default function TableList() {
const response = await axios.get(`/get-claim-requests`, { const response = await axios.get(`/get-claim-requests`, {
params: { ...parameters, type: 'final-log' }, params: { ...parameters, type: 'final-log' },
}); });
setDataTableData(response.data);
setData( setData(
response.data.data.map((obj: any) => ({ response.data.data.map((obj: any) => ({
...obj, ...obj,
@@ -310,15 +363,15 @@ export default function TableList() {
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''} {obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
</Label> </Label>
, ,
action: // action:
<TableMoreMenu actions={ // <TableMoreMenu actions={
<> // <>
<MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}> // <MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}>
<Iconify icon="eva:eye-fill" /> // <Iconify icon="eva:eye-fill" />
View // View
</MenuItem> // </MenuItem>
</> // </>
} /> // } />
})) }))
); );
@@ -357,6 +410,7 @@ export default function TableList() {
params={params} params={params}
searchs={searchs} searchs={searchs}
filterStatus={filterStatus} filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate} // filterStartDate={filterStartDate}
// filterEndDate={filterEndDate} // filterEndDate={filterEndDate}
/> />

View File

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

View File

@@ -1,11 +1,11 @@
/* ---------------------------------- @mui ---------------------------------- */ /* ---------------------------------- @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 --------------------------------- */ /* ---------------------------------- axios --------------------------------- */
// import axios from 'axios'; // import axios from 'axios';
import axios from '../../utils/axios'; import axios from '../../utils/axios';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */ /* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react'; import { SetStateAction, useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */ /* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify'; import Iconify from '../../components/Iconify';
@@ -30,6 +30,8 @@ import MuiDialog from '@/components/MuiDialog';
import DialogMember from './DialogMember'; import DialogMember from './DialogMember';
import DialogClaimSubmit from './DialogClaimSubmit'; import DialogClaimSubmit from './DialogClaimSubmit';
import { fPostFormat } from '@/utils/formatTime'; import { fPostFormat } from '@/utils/formatTime';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
export default function TableListFinalLog() { export default function TableListFinalLog() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -68,6 +70,7 @@ export default function TableListFinalLog() {
return `${day}${month}${year}`; return `${day}${month}${year}`;
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* setting up for the table */ /* 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 ----------------------------- */ /* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
@@ -335,11 +417,14 @@ export default function TableListFinalLog() {
params: { ...parameters, search:searchText, order: order, params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'final-log' }, orderBy: orderBy, status:statusValue, type: 'final-log' },
}); });
setDataTableData(response.data);
setData( setData(
response.data.data.map((obj: any) => ({ response.data.data.map((obj: any) => ({
...obj, ...obj,
provider:formatTitleCase(obj.provider), provider:formatTitleCase(obj.provider),
full_name:formatTitleCase(obj.full_name), full_name:formatTitleCase(obj.full_name),
check_status: obj.status,
check_claim: obj.check_claim,
status: status:
obj.status === 'requested' ? ( obj.status === 'requested' ? (
<Label color='primary'> <Label color='primary'>
@@ -445,6 +530,7 @@ export default function TableListFinalLog() {
params={params} params={params}
searchs={searchs} searchs={searchs}
filterStatus={filterStatus} filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate} // filterStartDate={filterStartDate}
// filterEndDate={filterEndDate} // filterEndDate={filterEndDate}
/> />
@@ -466,7 +552,6 @@ export default function TableListFinalLog() {
member={dataViewClaimSubmit} member={dataViewClaimSubmit}
getData={getData} getData={getData}
onClose={(data:any, getData:any) => { onClose={(data:any, getData:any) => {
console.log('Data returned:', data);
getData(); getData();
setOpenDialogClaimSubmit(false); setOpenDialogClaimSubmit(false);
}} }}
@@ -476,6 +561,42 @@ export default function TableListFinalLog() {
} }
maxWidth="sm" 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 axios from '../../utils/axios';
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */ /* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState, SetStateAction } from 'react';
/* -------------------------------- component ------------------------------- */ /* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify'; 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 ----------------------------- */ /* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
@@ -187,7 +239,7 @@ export default function TableList() {
}; };
// handle start date // handle start date
const [startDateValue, setStartDateValue] = useState(''); const [startDateValue, setStartDateValue] = useState(null);
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => { const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
@@ -211,7 +263,7 @@ export default function TableList() {
}; };
// handle end date // handle end date
const [endDateValue, setEndDateValue] = useState(''); const [endDateValue, setEndDateValue] = useState(null);
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => { const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
@@ -328,6 +380,7 @@ export default function TableList() {
params: { ...parameters, search:searchText, order: order, params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'request-log' }, orderBy: orderBy, status:statusValue, type: 'request-log' },
}); });
setDataTableData(response.data);
setData( setData(
response.data.data.map((obj: any) => ({ response.data.data.map((obj: any) => ({
...obj, ...obj,
@@ -440,8 +493,9 @@ export default function TableList() {
params={params} params={params}
searchs={searchs} searchs={searchs}
filterStatus={filterStatus} filterStatus={filterStatus}
// filterStartDate={filterStartDate} selected={selected}
// filterEndDate={filterEndDate} //filterStartDate={filterStartDate}
//filterEndDate={filterEndDate}
/> />
<MuiDialog <MuiDialog
title={{name: nameMember}} title={{name: nameMember}}

Binary file not shown.

Binary file not shown.

Binary file not shown.