Merge remote-tracking branch 'origin/staging' into origin/production
This commit is contained in:
@@ -316,8 +316,8 @@ class ClaimController extends Controller
|
||||
// ->where('request_logs.status_final_log', '=', 'approved')
|
||||
->where('request_logs.deleted_at', '=', null)
|
||||
->when($start != 'all' && $end != 'all', function ($query) use ($start, $end) {
|
||||
$query->where('request_logs.submission_date', '>=', $start)
|
||||
->where('request_logs.submission_date', '<=', Carbon::parse($end)->addDay());
|
||||
$query->where('request_logs.submission_date', '>=',Carbon::parse($start)->subDay())
|
||||
->where('request_logs.submission_date', '<=',Carbon::parse($end)->addDay());
|
||||
})
|
||||
->select(
|
||||
DB::raw('1 AS no'),
|
||||
|
||||
@@ -19,7 +19,7 @@ class ClaimRequestController extends Controller
|
||||
* Display a listing of the resource.
|
||||
* @return Renderable
|
||||
*/
|
||||
private static $code_prefix = 'CP';
|
||||
private static $code_prefix = 'CLAIM';
|
||||
public function index()
|
||||
{
|
||||
return view('client::index');
|
||||
@@ -224,10 +224,6 @@ class ClaimRequestController extends Controller
|
||||
|
||||
public static function getNextCode()
|
||||
{
|
||||
// $last_number = ClaimRequest::max('code');
|
||||
// $next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[2] + 1);
|
||||
// return self::makeCode($next_number);
|
||||
|
||||
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, "-", -1) AS SIGNED)) as max_numeric_code'))
|
||||
->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
|
||||
->value('max_numeric_code');
|
||||
@@ -247,8 +243,14 @@ class ClaimRequestController extends Controller
|
||||
{
|
||||
// Pastikan $next_number adalah integer positif
|
||||
$next_number = max(1, (int) $next_number);
|
||||
|
||||
$requestLogData = RequestLog::where('id', $request_log_id)->first();
|
||||
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
|
||||
$provideCode = $organization ? $organization->code : '';
|
||||
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
|
||||
$sparator = '.';
|
||||
$date = date('ymd');
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 6, '0', STR_PAD_LEFT);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ class CorporateMemberController extends Controller
|
||||
'service:code,name',
|
||||
'files',
|
||||
])
|
||||
->find($request_log_id, ['id', 'submission_date', 'discharge_date', 'member_id', 'service_code', 'organization_id', 'diagnosis']);
|
||||
->find($request_log_id, ['id', 'submission_date', 'discharge_date', 'member_id', 'service_code', 'organization_id', 'diagnosis', 'keterangan', 'catatan']);
|
||||
|
||||
$dataBenefit = [];
|
||||
if (count($data->requestLogBenefits) > 0) {
|
||||
|
||||
@@ -105,6 +105,8 @@ class DataServiceMonitoring extends JsonResource
|
||||
'name' => $requestLogBenefit->benefit->description,
|
||||
];
|
||||
})->all() ?? null,
|
||||
'keterangan' => $this->keterangan ?? null,
|
||||
'catatan' => $this->catatan ?? null,
|
||||
'benefitTotal' => $this->benefitTotal ?? null,
|
||||
'hospital' => $this->organization->name ?? null,
|
||||
'admissionDate' => $this->submission_date ?? null,
|
||||
|
||||
@@ -133,10 +133,21 @@ class ClaimRequestController extends Controller
|
||||
|
||||
if ($request->hasFile('additional_files')) {
|
||||
foreach ($request->additional_files as $file) {
|
||||
$pathFile = File::storeFile('additional-files', $newClaimRequest->id, $file);
|
||||
$newClaimRequest->files()->updateOrCreate([
|
||||
$pathFile = File::storeFile('additional-files', $request->request_logs_id, $file);
|
||||
// $newClaimRequest->files()->updateOrCreate([
|
||||
// 'type' => 'additional-files',
|
||||
// 'name' => File::getFileName('additional-files', $newClaimRequest->id, $file),
|
||||
// 'original_name' => $file->getClientOriginalName(),
|
||||
// 'extension' => $file->getClientOriginalExtension(),
|
||||
// 'path' => $pathFile,
|
||||
// 'created_by' => auth()->user()->id,
|
||||
// 'updated_by' => auth()->user()->id,
|
||||
// ]);
|
||||
File::updateOrCreate([
|
||||
'fileable_type' => 'App\Models\RequestLog',
|
||||
'fileable_id' => $request->request_logs_id,
|
||||
'type' => 'additional-files',
|
||||
'name' => File::getFileName('additional-files', $newClaimRequest->id, $file),
|
||||
'name' => File::getFileName('additional-files', $request->request_logs_id, $file),
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'extension' => $file->getClientOriginalExtension(),
|
||||
'path' => $pathFile,
|
||||
@@ -265,7 +276,6 @@ class ClaimRequestController extends Controller
|
||||
$date = date('ymd');
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||
return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
public function get_claim_requests(Request $request)
|
||||
|
||||
@@ -13,6 +13,11 @@ use App\Models\File;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use App\Models\Member;
|
||||
use App\Models\RequestLog;
|
||||
use App\Models\Organization;
|
||||
use App\Services\ClaimRequestService;
|
||||
use App\Models\ClaimRequest;
|
||||
|
||||
class RequestLogController extends Controller
|
||||
{
|
||||
@@ -20,6 +25,7 @@ class RequestLogController extends Controller
|
||||
* Display a listing of the resource.
|
||||
* @return Renderable
|
||||
*/
|
||||
private static $code_prefix = 'CLAIM';
|
||||
public function requestLog(Request $request)
|
||||
{
|
||||
$data = [
|
||||
@@ -244,6 +250,7 @@ class RequestLogController extends Controller
|
||||
|
||||
$results = DB::table('request_logs')
|
||||
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
|
||||
->where('request_logs.deleted_at', null)
|
||||
->when($request->input('search'), function ($query, $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->orWhere('request_logs.code', 'like', "%" . $search . "%")
|
||||
@@ -820,4 +827,110 @@ class RequestLogController extends Controller
|
||||
|
||||
return response($pdf->output(), 200, $headers);
|
||||
}
|
||||
|
||||
public function submitClaims(Request $request)
|
||||
{
|
||||
$data = [
|
||||
'selectedRows' => $request->selectedRows,
|
||||
];
|
||||
$validator = Validator::make($request->all(), [
|
||||
'selectedRows' => 'required'
|
||||
], [
|
||||
'selectedRows.required' => trans('Validation.required',['attribute' => 'Request Logs ID']),
|
||||
]);
|
||||
if ($validator->fails())
|
||||
{
|
||||
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($request->selectedRows as $request_logs_id) {
|
||||
$data_req_logs = DB::table('request_logs')
|
||||
->where('id', '=', $request_logs_id)
|
||||
->select('id', 'member_id', 'service_code')
|
||||
->first();
|
||||
|
||||
$check_claim_requests = DB::table('claim_requests')
|
||||
->where('claim_requests.request_log_id', '=', $request_logs_id)
|
||||
->first();
|
||||
if(!$check_claim_requests) {
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$code = $this->getNextCode($request_logs_id);
|
||||
$member = Member::find($data_req_logs->member_id);
|
||||
$requestLogData = RequestLog::where('id',$request_logs_id)->first();
|
||||
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
|
||||
$provideCode = $organization ? $organization->code : '';
|
||||
|
||||
$newClaimRequest = ClaimRequestService::storeClaimRequest(
|
||||
row: [],
|
||||
code: $code,
|
||||
member: $member,
|
||||
paymentType: 'cashless',
|
||||
serviceCode: $data_req_logs->service_code,
|
||||
requestLogID: $request_logs_id,
|
||||
organization_code: $provideCode,
|
||||
);
|
||||
// Log History
|
||||
$newClaimRequest->histories()->create([
|
||||
'title' => 'New Claim Requested',
|
||||
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
|
||||
'type' => 'info',
|
||||
'system_origin' => 'hospital-portal'
|
||||
]);
|
||||
|
||||
// Claim Log
|
||||
DB::table('claim_logs')
|
||||
->insert([
|
||||
'claim_request_id' => $newClaimRequest->id,
|
||||
'status' => 'requested',
|
||||
'date' => date('Y-m-d H:i:s'),
|
||||
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
|
||||
'system_origin' => 'hospital-portal',
|
||||
'created_by' => auth()->user()->id,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at'=> date('Y-m-d H:i:s'),
|
||||
]);
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ApiResponse::apiResponse('Success', $data, trans('Message.success'), 200);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getNextCode($request_log_id = 0)
|
||||
{
|
||||
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
|
||||
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
|
||||
->value('max_numeric_code');
|
||||
// $next_number = 1;
|
||||
if ($last_numeric_code) {
|
||||
// // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir
|
||||
// $parts = explode('-', $last_code);
|
||||
// $last_number = (int) end($parts);
|
||||
$next_number = $last_numeric_code + 1;
|
||||
} else {
|
||||
$next_number = 1;
|
||||
}
|
||||
return self::makeCode($next_number, $request_log_id);
|
||||
}
|
||||
|
||||
public static function makeCode($next_number, $request_log_id)
|
||||
{
|
||||
// Pastikan $next_number adalah integer positif
|
||||
$next_number = max(1, (int) $next_number);
|
||||
$requestLogData = RequestLog::where('id', $request_log_id)->first();
|
||||
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
|
||||
$provideCode = $organization ? $organization->code : '';
|
||||
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
|
||||
$sparator = '.';
|
||||
$date = date('ymd');
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||
return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ Route::prefix('v1')->group(function() {
|
||||
Route::post('request-final-log', 'requestFinalLog');
|
||||
Route::get('download-log/{request_log_id}', 'downlodLog');
|
||||
Route::get('download-final-log/{request_log_id}', 'downlodFinalLog');
|
||||
Route::post('submit-claims', 'submitClaims');
|
||||
});
|
||||
//Notification
|
||||
Route::controller(NotificationController::class)->group(function() {
|
||||
|
||||
@@ -21,8 +21,18 @@ use Modules\Internal\Transformers\ClaimEditResource;
|
||||
use Modules\Internal\Transformers\ClaimHistoryCareResource;
|
||||
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
|
||||
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
|
||||
|
||||
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
|
||||
use Box\Spout\Common\Entity\Style\CellAlignment;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Exports\FailedRowsExport; // Import class export Excel
|
||||
|
||||
use Modules\Internal\Transformers\RequestLogResource;
|
||||
|
||||
use App\Models\RequestLog;
|
||||
|
||||
use PDF;
|
||||
|
||||
@@ -34,41 +44,436 @@ class ClaimController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// $serviceCode = 'IP';
|
||||
$claims = Claim::with([
|
||||
'member',
|
||||
'member.currentCorporate',
|
||||
'member.currentCorporate.currentPolicy',
|
||||
// 'member.currentPlan' => function($memberPlan) use ($serviceCode) {
|
||||
// $memberPlan->where('plans.service_code', $serviceCode);
|
||||
// },
|
||||
'member.currentPlan' => function($memberPlan) {
|
||||
$memberPlan->join('claim_requests', 'claim_requests.service_code', '=', 'plans.service_code');
|
||||
},
|
||||
'diagnoses' => function ($diagnosis) {
|
||||
$diagnosis->where('type', 'primary');
|
||||
},
|
||||
'diagnoses.icd',
|
||||
'benefit',
|
||||
'claimRequest',
|
||||
'claimRequest.service',
|
||||
])
|
||||
->when($request->search, function ($q, $search) {
|
||||
$q->where(function ($subQuery) use ($search) {
|
||||
$subQuery->whereHas('claimRequest', function ($claimRequest) use ($search) {
|
||||
$claimRequest->where('code', 'LIKE', "%" . $search . "%");
|
||||
})
|
||||
->orWhereHas('member', function ($member) use ($search) {
|
||||
$member->where('name', 'LIKE', "%" . $search . "%");
|
||||
});
|
||||
$limit = $request->has('per_page') ? $request->input('per_page') : 10;
|
||||
$results = DB::table('claim_requests')
|
||||
->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
|
||||
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
|
||||
// ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
|
||||
->when($request->input('search'), function ($query, $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->orWhere('members.name', 'like', "%" . $search . "%");
|
||||
$query->orWhere('claim_requests.code', 'like', "%" . $search . "%");
|
||||
$query->orWhere('members.member_id', 'like', "%" . $search . "%");
|
||||
});
|
||||
})
|
||||
->where('status', '!=', 'requested') // Penjagaan agar approve baru masuk ke claim management
|
||||
->latest()
|
||||
->paginate(10);
|
||||
->when($request->has('orderBy'), function ($query) use ($request) {
|
||||
$orderBy = $request->orderBy;
|
||||
$direction = $request->order ?? 'asc';
|
||||
|
||||
$query->orderBy($orderBy, $direction);
|
||||
})
|
||||
->when($request->input('start_date') , function ($query, $start_date) {
|
||||
$query->where(function ($query) use ($start_date) {
|
||||
$query->where('claim_requests.created_at', '>=', $start_date);
|
||||
});
|
||||
})
|
||||
->when($request->input('end_date') , function ($query, $end_date) {
|
||||
$query->where(function ($query) use ($end_date) {
|
||||
$query->where('claim_requests.created_at', '<=', $end_date);
|
||||
});
|
||||
})
|
||||
->when($request->input('provider') , function ($query, $provider) {
|
||||
$query->where(function ($query) use ($provider) {
|
||||
$query->where('request_logs.organization_id', '=', $provider);
|
||||
});
|
||||
})
|
||||
->where('claim_management', '=', 1)
|
||||
->select(
|
||||
'claim_requests.id',
|
||||
'request_logs.id AS id_log',
|
||||
'claim_requests.code as code',
|
||||
'members.name',
|
||||
DB::raw('
|
||||
(SELECT members.member_id FROM members WHERE members.id = claim_requests.member_id LIMIT 1) AS member_id
|
||||
'),
|
||||
'claim_requests.created_at',
|
||||
// DB::raw('
|
||||
// (SELECT plans.code FROM plans WHERE plans.id = member_plans.plan_id LIMIT 1) AS plan_code
|
||||
// '),
|
||||
DB::raw('
|
||||
(SELECT plans.code
|
||||
FROM plans
|
||||
WHERE plans.id IN (
|
||||
SELECT member_plans.plan_id
|
||||
FROM member_plans
|
||||
WHERE member_plans.member_id = claim_requests.member_id
|
||||
)
|
||||
AND plans.service_code = claim_requests.service_code) AS plan_code
|
||||
'),
|
||||
DB::raw('
|
||||
(SELECT services.description FROM services WHERE services.code = claim_requests.service_code LIMIT 1) AS service_code
|
||||
'),
|
||||
DB::raw('
|
||||
(SELECT corporate_policies.code FROM corporate_policies WHERE corporate_policies.id = claim_requests.policy_id LIMIT 1) AS corporate_policies
|
||||
'),
|
||||
DB::raw('
|
||||
(SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider
|
||||
'),
|
||||
DB::raw('
|
||||
(Select SUM(request_log_benefits.amount_approved) as tot_bill FROM request_log_benefits
|
||||
WHERE request_log_benefits.request_log_id = request_logs.id LIMIT 1) AS tot_bill
|
||||
'),
|
||||
'claim_requests.status_claim_management as status',
|
||||
)
|
||||
->paginate($limit);
|
||||
|
||||
|
||||
|
||||
return response()->json($claims);
|
||||
return response()->json(Helper::paginateResources($results));
|
||||
}
|
||||
|
||||
public function downloadTemplate()
|
||||
{
|
||||
return Helper::responseJson([
|
||||
'file_name' => "Template - Claim - Management.xlsx",
|
||||
"file_url" => url('files/Template - Claim - Management.xlsx')
|
||||
]);
|
||||
}
|
||||
|
||||
public function import(Request $request)
|
||||
{
|
||||
if ($request->hasFile('file')) {
|
||||
$file = $request->file('file');
|
||||
$data = Excel::toArray([], $file);
|
||||
|
||||
$processedData = $this->processCategoryNames($data);
|
||||
|
||||
$importedRows = 0;
|
||||
$failedRows = [];
|
||||
|
||||
foreach ($processedData as $row) {
|
||||
try {
|
||||
$affectedRows = DB::table('claim_requests')
|
||||
->where('code','=', $row['code'])
|
||||
->where('claim_management','=', 1)
|
||||
->update([
|
||||
'status_claim_management' => $row['qc'] == 'Y' ? 'approved' : 'declined',
|
||||
'reason_decline' => $row['reason'] ? $row['reason'] : null,
|
||||
'approval_by_claim_management' => auth()->user()->id,
|
||||
'approval_date_claim_management' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
if ($affectedRows === 0) {
|
||||
$failedRows[] = $row;
|
||||
} else {
|
||||
$importedRows += $affectedRows;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$failedRows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'message' => 'File uploaded and data saved to database',
|
||||
'data' => [
|
||||
'total_success_row' => $importedRows,
|
||||
'total_failed_row' => count($failedRows),
|
||||
'failed_rows' => $failedRows,
|
||||
],
|
||||
];
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
return response()->json(['error' => 'No file uploaded.']);
|
||||
}
|
||||
|
||||
private function processCategoryNames($data)
|
||||
{
|
||||
$header = [];
|
||||
$row = [];
|
||||
for ($i = 1; $i < count($data[0]); $i++) {
|
||||
$row[] = $data[0][$i];
|
||||
$header[] = $data[0][0];
|
||||
}
|
||||
|
||||
$filed = [];
|
||||
foreach ($header[0] as $value)
|
||||
{
|
||||
$modelColumn = strtolower(preg_replace('/\s+/', '_', trim($value)));
|
||||
$modelColumn = str_replace(['*', ' '], '', $modelColumn);
|
||||
if($modelColumn)
|
||||
{
|
||||
$filed[] = $modelColumn;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($row as $subarray) {
|
||||
$trimmedSubarray = [];
|
||||
for ($i = 0; $i < count($filed); $i++) {
|
||||
$trimmedSubarray[$filed[$i]] = $subarray[$i] ? $subarray[$i] : null;
|
||||
}
|
||||
|
||||
$result[] = $trimmedSubarray;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function exportClaimManagement(Request $request)
|
||||
{
|
||||
$start_date = $request->input('start_date') ? $request->input('start_date') : 'all';
|
||||
$end_date = $request->input('end_date') ? $request->input('end_date') : 'all';
|
||||
$writer = WriterEntityFactory::createXLSXWriter();
|
||||
$writer->openToFile(public_path('files/Report-Data-Claim-Management-'.$start_date.'-'.$end_date.'.xlsx'));
|
||||
$header = [
|
||||
'No',
|
||||
'Code',
|
||||
'Name',
|
||||
'Member ID',
|
||||
'Date Submission',
|
||||
'Plan ID',
|
||||
'Service',
|
||||
'Policy Number',
|
||||
'Provider',
|
||||
'Total Billing',
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
|
||||
$writer->addRow($headerRow);
|
||||
// ============================
|
||||
$results = DB::table('claim_requests')
|
||||
->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
|
||||
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
|
||||
// ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
|
||||
->when($request->input('search'), function ($query, $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->orWhere('members.name', 'like', "%" . $search . "%");
|
||||
$query->orWhere('claim_requests.code', 'like', "%" . $search . "%");
|
||||
$query->orWhere('members.member_id', 'like', "%" . $search . "%");
|
||||
});
|
||||
})
|
||||
->when($request->has('orderBy'), function ($query) use ($request) {
|
||||
$orderBy = $request->orderBy;
|
||||
$direction = $request->order ?? 'asc';
|
||||
|
||||
$query->orderBy($orderBy, $direction);
|
||||
})
|
||||
->when($request->input('start_date') , function ($query, $start_date) {
|
||||
$query->where(function ($query) use ($start_date) {
|
||||
$query->where('claim_requests.created_at', '>=', $start_date);
|
||||
});
|
||||
})
|
||||
->when($request->input('end_date') , function ($query, $end_date) {
|
||||
$query->where(function ($query) use ($end_date) {
|
||||
$query->where('claim_requests.created_at', '<=', $end_date);
|
||||
});
|
||||
})
|
||||
->when($request->input('provider') , function ($query, $provider) {
|
||||
$query->where(function ($query) use ($provider) {
|
||||
$query->where('request_logs.organization_id', '=', $provider);
|
||||
});
|
||||
})
|
||||
->where('claim_management', '=', 1)
|
||||
->select(
|
||||
'claim_requests.id',
|
||||
'request_logs.id AS id_log',
|
||||
'claim_requests.code as code',
|
||||
'members.name',
|
||||
DB::raw('
|
||||
(SELECT members.member_id FROM members WHERE members.id = claim_requests.member_id LIMIT 1) AS member_id
|
||||
'),
|
||||
'claim_requests.created_at',
|
||||
DB::raw('
|
||||
(SELECT plans.code
|
||||
FROM plans
|
||||
WHERE plans.id IN (
|
||||
SELECT member_plans.plan_id
|
||||
FROM member_plans
|
||||
WHERE member_plans.member_id = claim_requests.member_id
|
||||
)
|
||||
AND plans.service_code = claim_requests.service_code) AS plan_code
|
||||
'),
|
||||
// DB::raw('
|
||||
// (SELECT plans.code FROM plans WHERE plans.id = member_plans.plan_id LIMIT 1) AS plan_code
|
||||
// '),
|
||||
DB::raw('
|
||||
(SELECT services.description FROM services WHERE services.code = claim_requests.service_code LIMIT 1) AS service_code
|
||||
'),
|
||||
DB::raw('
|
||||
(SELECT corporate_policies.code FROM corporate_policies WHERE corporate_policies.id = claim_requests.policy_id LIMIT 1) AS corporate_policies
|
||||
'),
|
||||
DB::raw('
|
||||
(SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider
|
||||
'),
|
||||
DB::raw('
|
||||
(Select SUM(request_log_benefits.amount_approved) as tot_bill FROM request_log_benefits
|
||||
WHERE request_log_benefits.request_log_id = request_logs.id LIMIT 1) AS tot_bill
|
||||
'),
|
||||
'claim_requests.status_claim_management as status',
|
||||
)
|
||||
->get();
|
||||
$no=0;
|
||||
$gr_total = 0;
|
||||
foreach($results as $item)
|
||||
{
|
||||
$gr_total += $item->tot_bill;
|
||||
$no++;
|
||||
$rowData = [
|
||||
$no,
|
||||
$item->code,
|
||||
$item->name,
|
||||
$item->member_id,
|
||||
$item->created_at,
|
||||
$item->plan_code,
|
||||
$item->service_code,
|
||||
$item->corporate_policies,
|
||||
$item->provider,
|
||||
$item->tot_bill ? $item->tot_bill : 0
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
//->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
$row = WriterEntityFactory::createRowFromArray($rowData, $style);
|
||||
$writer->addRow($row);
|
||||
}
|
||||
$footer = [
|
||||
'Grand Total',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
$gr_total,
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
|
||||
$writer->addRow($footerRow);
|
||||
|
||||
$writer->close();
|
||||
|
||||
return Helper::responseJson([
|
||||
'file_name' => 'Report-Data-Claim-Management-'. $start_date.'-'.$end_date,
|
||||
"file_url" => url('files/Report-Data-Claim-Management-'. $start_date.'-'.$end_date.'.xlsx')
|
||||
]);
|
||||
}
|
||||
|
||||
public function exportFiled(Request $request)
|
||||
{
|
||||
$writer = WriterEntityFactory::createXLSXWriter();
|
||||
$writer->openToFile(public_path('files/Report-Data-Filed-Import.xlsx'));
|
||||
$header = [
|
||||
'Code*',
|
||||
'QC*',
|
||||
'Reason'
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
|
||||
$writer->addRow($headerRow);
|
||||
// ============================
|
||||
|
||||
foreach($request->params as $item)
|
||||
{
|
||||
|
||||
$rowData = [
|
||||
$item['code'],
|
||||
$item['qc'],
|
||||
$item['reason']
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
//->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
$row = WriterEntityFactory::createRowFromArray($rowData, $style);
|
||||
$writer->addRow($row);
|
||||
}
|
||||
$footer = [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
|
||||
$writer->addRow($footerRow);
|
||||
|
||||
$writer->close();
|
||||
|
||||
return Helper::responseJson([
|
||||
'file_name' => 'Report-Data-Filed-Import',
|
||||
"file_url" => url('files/Report-Data-Filed-Import.xlsx')
|
||||
]);
|
||||
}
|
||||
public function getProvider(Request $request)
|
||||
{
|
||||
$providers = DB::table('organizations')
|
||||
->where('organizations.type', '=', 'hospital')
|
||||
->where('organizations.status', '=', 'active')
|
||||
->select(
|
||||
'organizations.id',
|
||||
'organizations.name'
|
||||
)
|
||||
->orderBy('name', 'asc')
|
||||
->get();
|
||||
|
||||
return response()->json($providers);
|
||||
}
|
||||
|
||||
public function cekStatus($id)
|
||||
{
|
||||
$cek = DB::table('claim_requests')
|
||||
->where('claim_requests.id', '=', $id)
|
||||
->select('claim_requests.status_claim_management as status')
|
||||
->first();
|
||||
|
||||
$data['cek'] = $cek;
|
||||
|
||||
$member = DB::table('claim_requests')
|
||||
->join('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
|
||||
->join('members', 'request_logs.member_id', '=', 'members.id')
|
||||
->where('claim_requests.id', '=', $id)
|
||||
->select('claim_requests.code','members.name','claim_requests.created_at', DB::raw('
|
||||
(SELECT services.name FROM services WHERE services.code = request_logs.service_code LIMIT 1) AS service_type
|
||||
'),)
|
||||
->first();
|
||||
|
||||
$data['member'] = $member;
|
||||
|
||||
return response()->json($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,21 +739,18 @@ class ClaimController extends Controller
|
||||
return Helper::responseJson([], message: "Diagnosis berhasil di update");
|
||||
}
|
||||
|
||||
public function decline($id)
|
||||
public function decline(Request $request, $id)
|
||||
{
|
||||
//Get claim request id
|
||||
$data_claim_requests = DB::table('claim_requests')
|
||||
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
|
||||
->where('claims.id', $id)
|
||||
->select('claim_requests.id')
|
||||
->first();
|
||||
$id = $data_claim_requests->id;
|
||||
|
||||
DB::table('claims')
|
||||
->where('claim_request_id', $id)
|
||||
DB::table('claim_requests')
|
||||
->where('claim_requests.id', $id)
|
||||
->update(
|
||||
[
|
||||
'status' => 'declined'
|
||||
'status' => 'declined',
|
||||
'status_claim_management' => 'declined',
|
||||
'reason_decline' => $request->reasonDecline ? $request->reasonDecline : '',
|
||||
'approval_date_claim_management' => date('Y-m-d H:i:s'),
|
||||
'approval_by_claim_management' => auth()->user()->id
|
||||
]
|
||||
);
|
||||
|
||||
@@ -370,19 +772,14 @@ class ClaimController extends Controller
|
||||
|
||||
public function approve($id)
|
||||
{
|
||||
//Get claim request id
|
||||
$data_claim_requests = DB::table('claim_requests')
|
||||
->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
|
||||
->where('claims.id', $id)
|
||||
->select('claim_requests.id')
|
||||
->first();
|
||||
$id = $data_claim_requests->id;
|
||||
|
||||
DB::table('claims')
|
||||
->where('claim_request_id', $id)
|
||||
DB::table('claim_requests')
|
||||
->where('claim_requests.id', $id)
|
||||
->update(
|
||||
[
|
||||
'status' => 'approved'
|
||||
'status' => 'approved',
|
||||
'status_claim_management' => 'approved',
|
||||
'approval_date_claim_management' => date('Y-m-d H:i:s'),
|
||||
'approval_by_claim_management' => auth()->user()->id
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -17,15 +17,23 @@ use App\Services\ClaimRequestService;
|
||||
use App\Exceptions\ImportRowException;
|
||||
use App\Events\ClaimRequested;
|
||||
|
||||
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
|
||||
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
|
||||
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
|
||||
use Box\Spout\Common\Entity\Style\CellAlignment;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\FilesMcu;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Member;
|
||||
use App\Models\MemberPlan;
|
||||
use App\Models\Plan;
|
||||
use App\Models\RequestLogBenefit;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ClaimRequestController extends Controller
|
||||
{
|
||||
private static $code_prefix = 'CRQ-C';
|
||||
private static $code_prefix = 'CLAIM';
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
@@ -41,11 +49,17 @@ class ClaimRequestController extends Controller
|
||||
});
|
||||
})
|
||||
->when($request->start_date, function ($q, $startDate) {
|
||||
$q->where('submission_date', '>', $startDate);
|
||||
$q->where('submission_date', '>', Carbon::parse($startDate)->subDay());
|
||||
})
|
||||
->when($request->end_date, function ($q, $endDate) {
|
||||
$q->where('submission_date', '<', $endDate);
|
||||
->when($request->end_date, function ($q, $endDate) use ($request) {
|
||||
// Jika tanggal akhir diberikan dan tidak sama dengan tanggal mulai
|
||||
if ($request->start_date != $request->end_date) {
|
||||
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
|
||||
} else {
|
||||
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
|
||||
}
|
||||
})
|
||||
|
||||
->when($request->service_code, function ($q, $serviceCode) {
|
||||
$q->whereIn('service_code', $serviceCode);
|
||||
})
|
||||
@@ -62,7 +76,7 @@ class ClaimRequestController extends Controller
|
||||
})
|
||||
->with(['member', 'files', 'service', 'member.currentPolicy'])
|
||||
->paginate();
|
||||
|
||||
|
||||
return Helper::paginateResources(ClaimRequestResource::collection($claimRequests));
|
||||
}
|
||||
|
||||
@@ -429,6 +443,7 @@ class ClaimRequestController extends Controller
|
||||
$import = new ImportService();
|
||||
$import->read($fileRead);
|
||||
$import->write($fileWrite, 'xsls');
|
||||
|
||||
foreach ($import->sheetsIterator() as $sheetIndex => $sheet) {
|
||||
if ($sheetIndex == 1) { // Rename First Sheet to Writer
|
||||
$firstWriterSheet = $import->writer->getCurrentSheet();
|
||||
@@ -445,6 +460,9 @@ class ClaimRequestController extends Controller
|
||||
$result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']);
|
||||
|
||||
$import->addArrayToRow($result_headers);
|
||||
$imported_claim_data = 0;
|
||||
$failed_claim_data = [];
|
||||
|
||||
$doc_headers_indexes = [];
|
||||
foreach ($sheet->getRowIterator() as $index => $row) {
|
||||
if ($index == 1) { // First Row Must be Header
|
||||
@@ -465,25 +483,28 @@ class ClaimRequestController extends Controller
|
||||
}
|
||||
try { // Process the Row Data
|
||||
$claimRequestService = new ClaimRequestService();
|
||||
|
||||
$claimRequestService->handleClaimRequestRow($row_data);
|
||||
|
||||
// Write Success Result to File
|
||||
// $import->read($fileRead);
|
||||
// $import->write($fileWrite, 'xsls');
|
||||
$result_headers = array_merge($row_data, ['Ingest Code' =>200, 'Ingest Note' => 'Success']);
|
||||
|
||||
// Mengambil tanggal dari objek DateTime
|
||||
$dateSubmission = Helper::dateParser($result_headers['date_submission']); // Format tanggal sesuai kebutuhan
|
||||
// Mengubah nilai date_submission menjadi string tanggal
|
||||
$result_headers['date_submission'] = $dateSubmission;
|
||||
// dd($result_headers);
|
||||
$import->addArrayToRow($result_headers, $sheet->getName());
|
||||
|
||||
$imported_claim_data++;
|
||||
} catch (ImportRowException $e) {
|
||||
// Write Data Validation Error to File
|
||||
// $import->read($fileRead);
|
||||
// $import->write($fileWrite, 'xsls');
|
||||
|
||||
$import->addArrayToRow(array_merge($row_data, [
|
||||
$new_claim_data = $import->addArrayToRow(array_merge($row_data, [
|
||||
'Ingest Code' => $e->getCode(),
|
||||
'Ingest Note' => $e->getMessage(),
|
||||
]), $sheet->getName());
|
||||
|
||||
$failed_claim_data[] = ['row_number' => $index, 'error' => $e->getMessage(),'data' => $new_claim_data];
|
||||
}
|
||||
// catch (\Exception $e) {
|
||||
// // throw new \Exception($e);
|
||||
@@ -502,104 +523,20 @@ class ClaimRequestController extends Controller
|
||||
$import->reader->close();
|
||||
Storage::delete('temp/' . $file_name);
|
||||
$import->writer->close();
|
||||
|
||||
return [
|
||||
// 'total_successed_row' => $imported_plan_data,
|
||||
// 'total_failed_row' => count($failed_plan_data),
|
||||
// 'failed_row' => $failed_plan_data,
|
||||
|
||||
'result_file' => [
|
||||
'url' => Storage::disk('public')->url('temp/result-' . $file_name),
|
||||
'name' => 'result-' . $file_name,
|
||||
'total_success_row' => $imported_claim_data,
|
||||
'total_failed_row' => count($failed_claim_data),
|
||||
'failed_row' => $failed_claim_data,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function claimRequestDetail($claimRequestId)
|
||||
{
|
||||
// $status = DB::table('claim_requests')
|
||||
// ->leftJoin('claims', 'claim_requests.id', '=', 'claims.claim_request_id')
|
||||
// ->leftJoin('members', 'claim_requests.member_id', '=', 'members.id')
|
||||
// ->leftJoin('corporate_employees', 'members.id', '=', 'corporate_employees.member_id')
|
||||
// ->leftJoin('corporate_divisions', 'corporate_employees.division_id', '=', 'corporate_divisions.id')
|
||||
// ->where('claim_requests.id', '=', $claimRequestId)
|
||||
// ->select(
|
||||
// 'claim_requests.submission_date',
|
||||
// 'claim_requests.code',
|
||||
// DB::raw('
|
||||
// CASE
|
||||
// WHEN claim_requests.status = "requested" THEN "requested"
|
||||
// WHEN claim_requests.status = "approved" AND claims.status = "approved" THEN "approved"
|
||||
// WHEN claim_requests.status = "approved" AND claims.status = "declined" THEN "declined"
|
||||
// WHEN claim_requests.status = "approved" AND claims.status = "disbrusmented" THEN "disbrusmented"
|
||||
// /*WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "pending"*/
|
||||
// WHEN claim_requests.status = "approved" AND claims.status = "received" THEN "reviewed"
|
||||
// ELSE ""
|
||||
// END AS status
|
||||
// ')
|
||||
// )
|
||||
// ->first();
|
||||
// $results['status'] = $status;
|
||||
// $timeline = DB::table('claim_logs')
|
||||
// ->where('claim_logs.claim_request_id', '=', $claimRequestId)
|
||||
// ->select(
|
||||
// DB::raw('
|
||||
// CASE
|
||||
// WHEN claim_logs.status = "requested" THEN "Request"
|
||||
// WHEN claim_logs.status = "reviewed" THEN "Review"
|
||||
// WHEN claim_logs.status = "approved" THEN "Approval"
|
||||
// WHEN claim_logs.status = "declined" THEN "Decline"
|
||||
// ELSE "-"
|
||||
// END AS txt_status
|
||||
// '),
|
||||
// DB::raw('
|
||||
// CASE
|
||||
// WHEN claim_logs.status = "requested" THEN "#159C9C"
|
||||
// WHEN claim_logs.status = "reviewed" THEN "#0C53B7"
|
||||
// WHEN claim_logs.status = "approved" THEN "#229A16"
|
||||
// WHEN claim_logs.status = "declined" THEN "#FF4842"
|
||||
// ELSE "-"
|
||||
// END AS txt_status_color
|
||||
// '),
|
||||
// DB::raw('
|
||||
// CASE
|
||||
// WHEN claim_logs.status = "requested" THEN "#00AB5529"
|
||||
// WHEN claim_logs.status = "reviewed" THEN "#1890FF29"
|
||||
// WHEN claim_logs.status = "approved" THEN "#54D62C29"
|
||||
// WHEN claim_logs.status = "declined" THEN "#FF48427A"
|
||||
// ELSE "-"
|
||||
// END AS txt_status_backgroundColor
|
||||
// '),
|
||||
// 'claim_logs.date',
|
||||
// 'claim_logs.description',
|
||||
// 'claim_logs.status'
|
||||
// )
|
||||
// ->orderBy('claim_logs.id', 'desc')
|
||||
// ->get();
|
||||
// $results['timeline'] = $timeline;
|
||||
// $request_files = DB::table('claim_request_files')
|
||||
// ->where('claim_request_files.claim_request_id', '=', $claimRequestId)
|
||||
// ->select(
|
||||
// 'claim_request_files.*',
|
||||
// DB::raw('(SELECT files.fileable_id FROM files WHERE files.fileable_id = claim_request_files.claim_request_id AND files.type = claim_request_files.type LIMIT 1) AS check_files'),
|
||||
// )
|
||||
// ->get();
|
||||
// $results['request_files'] = $request_files;
|
||||
// $documents = DB::table('files')
|
||||
// ->where('fileable_type', 'App\Models\ClaimRequest')
|
||||
// ->where('fileable_id', $claimRequestId)
|
||||
// ->select('original_name', \DB::raw("CONCAT('" . env('APP_URL') . "/storage/', path) as path"), 'type')
|
||||
// ->orderBy('id', 'desc')
|
||||
// ->get();
|
||||
// $results['documents'] = $documents;
|
||||
// $dialog_submits = DB::table('claim_requests')
|
||||
// ->leftJoin('members', 'claim_requests.member_id','=', 'members.id')
|
||||
// ->where('claim_requests.id', $claimRequestId)
|
||||
// ->select('claim_requests.code', 'members.name', 'claim_requests.submission_date', 'claim_requests.service_code','claim_requests.status')
|
||||
// ->first();
|
||||
// $results['dialog_submits'] = $dialog_submits;
|
||||
|
||||
// return Helper::responseJson($results);
|
||||
|
||||
$claimRequest = ClaimRequest::findOrFail($claimRequestId);
|
||||
$claimRequest->load([
|
||||
'requestLog',
|
||||
@@ -704,10 +641,6 @@ class ClaimRequestController extends Controller
|
||||
|
||||
public static function getNextCode()
|
||||
{
|
||||
// $last_number = ClaimRequest::max('code');
|
||||
// $next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[2] + 1);
|
||||
// return self::makeCode($next_number);
|
||||
|
||||
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, "-", -1) AS SIGNED)) as max_numeric_code'))
|
||||
->whereRaw('SUBSTRING_INDEX(code, "-", -1) REGEXP "^[0-9]+$"')
|
||||
->value('max_numeric_code');
|
||||
@@ -725,11 +658,17 @@ class ClaimRequestController extends Controller
|
||||
|
||||
public static function makeCode($next_number)
|
||||
{
|
||||
// Pastikan $next_number adalah integer positif
|
||||
$next_number = max(1, (int) $next_number);
|
||||
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
return self::$code_prefix . '-' . str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||
// Pastikan $next_number adalah integer positif
|
||||
$next_number = max(1, (int) $next_number);
|
||||
$requestLogData = RequestLog::where('id', $request_log_id)->first();
|
||||
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
|
||||
$provideCode = $organization ? $organization->code : '';
|
||||
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
|
||||
$sparator = '.';
|
||||
$date = date('ymd');
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 6, '0', STR_PAD_LEFT);
|
||||
|
||||
}
|
||||
|
||||
public function requestFiles(Request $request, $claim_id)
|
||||
@@ -788,4 +727,309 @@ class ClaimRequestController extends Controller
|
||||
|
||||
return Helper::responseJson(data: $request->toArray(), message: 'Invoice Success Uploaded');
|
||||
}
|
||||
|
||||
public function exportClaimRequest(Request $request)
|
||||
{
|
||||
$claimRequests = ClaimRequest::query()
|
||||
->when($request->search, function ($q, $search) {
|
||||
$q->where('code', 'LIKE', "%".$search."%");
|
||||
$q->orWhereHas('member', function ($subQuery) use ($search) {
|
||||
$subQuery->where('name', 'LIKE', "%".$search."%");
|
||||
});
|
||||
})
|
||||
->when($request->start_date, function ($q, $startDate) {
|
||||
$q->where('submission_date', '>', Carbon::parse($startDate)->subDay());
|
||||
})
|
||||
->when($request->end_date, function ($q, $endDate) use ($request) {
|
||||
// Jika tanggal akhir diberikan dan tidak sama dengan tanggal mulai
|
||||
if ($request->start_date != $request->end_date) {
|
||||
$q->where('submission_date', '<', Carbon::parse($endDate)->addDay());
|
||||
} else {
|
||||
$q->where('submission_date', '<=', Carbon::parse($endDate));
|
||||
}
|
||||
})
|
||||
->when($request->service_code, function ($q, $serviceCode) {
|
||||
$q->whereIn('service_code', $serviceCode);
|
||||
})
|
||||
->when($request->orderBy, function ($q, $orderBy) use ($request) {
|
||||
if (in_array($orderBy, ['submission_date', 'code'])) {
|
||||
$q->orderBy($orderBy, $request->order);
|
||||
}
|
||||
})
|
||||
->when(empty($request->orderBy), function ($q) {
|
||||
$q->orderBy('created_at', 'desc');
|
||||
})
|
||||
->when($request->status, function($q, $status) {
|
||||
$q->where('status', $status);
|
||||
})
|
||||
->with(['member', 'member.currentCorporate', 'files', 'service', 'member.currentPolicy', 'requestLog', 'requestLog.organization',])
|
||||
->addSelect([
|
||||
'tot_billing' => function ($query) {
|
||||
$query->select(DB::raw('SUM(request_log_benefits.amount_approved)'))
|
||||
->from('request_log_benefits')
|
||||
->whereColumn('request_log_benefits.request_log_id', 'claim_requests.request_log_id')
|
||||
->limit(1);
|
||||
}
|
||||
])
|
||||
->get();
|
||||
$writer = WriterEntityFactory::createXLSXWriter();
|
||||
$writer->openToFile(public_path('files/Report-Data-Claim-Request.xlsx'));
|
||||
$header = [
|
||||
'No',
|
||||
'Code Claim',
|
||||
'Date Claim Submission',
|
||||
'Code LOG',
|
||||
'Date Submission',
|
||||
'Date Admission',
|
||||
'Date Discharge',
|
||||
'Provider',
|
||||
'Member ID (BN)',
|
||||
'Member Name',
|
||||
'Member Name Principal',
|
||||
'Plan Code',
|
||||
'Payor ID',
|
||||
'Corporate name',
|
||||
'Policy Number',
|
||||
'Total Billing',
|
||||
'Benefit Code',
|
||||
'Benefit Desc',
|
||||
'Amt Incurred',
|
||||
'Amt Approved',
|
||||
'Amt Not Approved',
|
||||
'Excess Paid',
|
||||
// 'Reason',
|
||||
'Diagnosis',
|
||||
'Keterangan',
|
||||
'Catatan',
|
||||
'Status',
|
||||
'QC'
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
$headerRow = WriterEntityFactory::createRowFromArray($header, $style);
|
||||
$writer->addRow($headerRow);
|
||||
|
||||
$results = $claimRequests;
|
||||
$no=0;
|
||||
$gr_total = 0;
|
||||
$rowData=[];
|
||||
foreach($results as $item)
|
||||
{
|
||||
// $gr_total += $item->tot_bill;
|
||||
// $requestLogData = RequestLogBenefit::selectRaw('*,
|
||||
// (SELECT code FROM benefits WHERE benefits.id = request_log_benefits.benefit_id) AS benefit_code,
|
||||
// (SELECT description FROM benefits WHERE benefits.id = request_log_benefits.benefit_id) AS benefit_description,
|
||||
// sum(amount_incurred) AS total_incurred'
|
||||
// )
|
||||
// ->where(['request_log_id' => $item->request_log_id, 'deleted_at' => null])
|
||||
// ->get()->toArray();
|
||||
|
||||
//Data Benefit
|
||||
$requestLogData = DB::table('request_log_benefits')
|
||||
->where('request_log_benefits.request_log_id', '=', $item->request_log_id)
|
||||
->select(
|
||||
'*',
|
||||
// DB::raw('SUM(request_log_benefits.amount_incurred) AS total_incurred'),
|
||||
|
||||
DB::raw('
|
||||
(Select benefits.description FROM benefits
|
||||
WHERE benefits.id = request_log_benefits.benefit_id LIMIT 1) AS benefit_description
|
||||
'),
|
||||
DB::raw('
|
||||
(Select benefits.code FROM benefits
|
||||
WHERE benefits.id = request_log_benefits.benefit_id LIMIT 1) AS benefit_code
|
||||
')
|
||||
)
|
||||
->get();
|
||||
if ($item->member){
|
||||
$member = Member::where('member_id', $item->member->principal_id)->first();
|
||||
$memberPrincipal = $member->name;
|
||||
$memberPlan = MemberPlan::where('member_id', $item->member->id)->get('plan_id')->toArray();
|
||||
$plan= Plan::whereIn('id', $memberPlan)->where('service_code', $item->requestLog->service_code)->first();
|
||||
$planCode = $plan->code;
|
||||
if ($item->member->currentCorporate->id == $item->requestLog->organization->corporate_id_partner){
|
||||
$payor = $item->member->currentCorporate->name;
|
||||
} else {
|
||||
$payor = 'LinkSehat';
|
||||
}
|
||||
} else {
|
||||
$memberPrincipal = '-';
|
||||
$planCode = '-';
|
||||
$payor = '-';
|
||||
}
|
||||
|
||||
if (!$requestLogData->isEmpty()){
|
||||
foreach($requestLogData as $key => $data){
|
||||
$no++;
|
||||
if ($key == 0){
|
||||
$gr_total += $item->tot_billing;
|
||||
}
|
||||
$rowItem = [
|
||||
$no,
|
||||
$item->code,
|
||||
$item->submission_date ? $item->submission_date : '',
|
||||
$item->requestLog ? $item->requestLog->code : '-',
|
||||
$item->requestLog ? Helper::dateParser($item->requestLog->created_at) : '-', // submission = created_at
|
||||
$item->requestLog ? $item->requestLog->submission_date : '-',
|
||||
$item->requestLog ? $item->requestLog->discharge_date : '-',
|
||||
$item->requestLog ? $item->requestLog->organization->name : '-',
|
||||
$item->member ? $item->member->member_id : '-',
|
||||
$item->member ? $item->member->name : '-',
|
||||
$memberPrincipal,
|
||||
$key == 0 ? $planCode : '',
|
||||
$payor,
|
||||
$item->member ? $item->member->currentCorporate->name : '-',
|
||||
$item->member ? $item->member->currentPolicy->code : '-',
|
||||
$key == 0 ? $item->tot_billing : '',
|
||||
$data->benefit_code,
|
||||
$data->benefit_description,
|
||||
$data->amount_incurred ? $data->amount_incurred : '' ,
|
||||
$data->amount_approved ? $data->amount_approved : '',
|
||||
$data->amount_not_approved ? $data->amount_not_approved : '',
|
||||
$data->excess_paid ? $data->excess_paid : '',
|
||||
// $data->reason ? $data->reason : '',
|
||||
$item->requestLog ? $item->requestLog->diagnosis : '-',
|
||||
$item->requestLog ? $item->requestLog->keterangan : '-',
|
||||
$item->requestLog ? $item->requestLog->catatan : '-',
|
||||
$item->status ? $item->status : '-'
|
||||
];
|
||||
array_push($rowData,$rowItem);
|
||||
}
|
||||
} else {
|
||||
$no++;
|
||||
if ($item->member){
|
||||
$member = Member::where('member_id', $item->member->principal_id)->first();
|
||||
$memberPrincipal = $member->name;
|
||||
$memberPlan = MemberPlan::where('member_id', $item->member->id)->get('plan_id')->toArray();
|
||||
$plan= Plan::whereIn('id', $memberPlan)->where('service_code', $item->requestLog->service_code)->first();
|
||||
$planCode = $plan->code;
|
||||
if ($item->member->currentCorporate->id == $item->requestLog->organization->corporate_id_partner){
|
||||
$payor = $item->member->currentCorporate->name;
|
||||
} else {
|
||||
$payor = 'LinkSehat';
|
||||
}
|
||||
} else {
|
||||
$memberPrincipal = '-';
|
||||
$planCode = '-';
|
||||
$payor = '-';
|
||||
}
|
||||
$rowItem = [
|
||||
$no,
|
||||
$item->code,
|
||||
$item->submission_date ? Helper::dateParser($item->submission_date) : '',
|
||||
$item->requestLog ? $item->requestLog->code : '-',
|
||||
$item->requestLog ? Helper::dateParser($item->requestLog->created_at) : '-', // submission = created_at
|
||||
$item->requestLog ? $item->requestLog->submission_date : '-',
|
||||
$item->requestLog ? $item->requestLog->discharge_date : '-',
|
||||
$item->requestLog ? $item->requestLog->organization->name : '-',
|
||||
$item->member ? $item->member->member_id : '-',
|
||||
$item->member ? $item->member->name : '-',
|
||||
$memberPrincipal,
|
||||
$planCode,
|
||||
$payor,
|
||||
$item->member ? $item->member->currentCorporate->name : '-',
|
||||
$item->member ? $item->member->currentPolicy->code : '-',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
// '',
|
||||
$item->requestLog ? $item->requestLog->diagnosis : '-',
|
||||
$item->requestLog ? $item->requestLog->keterangan : '-',
|
||||
$item->requestLog ? $item->requestLog->catatan : '-',
|
||||
$item->status ? $item->status : '-'
|
||||
];
|
||||
array_push($rowData,$rowItem);
|
||||
}
|
||||
|
||||
}
|
||||
$style = (new StyleBuilder())
|
||||
//->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
foreach ($rowData as $rowItem) {
|
||||
// if (is_numeric($rowItem[13])) {
|
||||
// // Jumlahkan nilai angka ke total
|
||||
// $grand_total_billing += $rowItem[13];
|
||||
// }
|
||||
$row = WriterEntityFactory::createRowFromArray($rowItem, $style);
|
||||
$writer->addRow($row);
|
||||
}
|
||||
|
||||
//Footer
|
||||
$footer = [
|
||||
'Total',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
$gr_total,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
];
|
||||
$style = (new StyleBuilder())
|
||||
->setFontBold()
|
||||
// ->setFontSize(15)
|
||||
// ->setFontColor(Color::BLUE)
|
||||
// ->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::LEFT)
|
||||
// ->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
$grand_total_billing = 0;
|
||||
$footerRow = WriterEntityFactory::createRowFromArray($footer, $style);
|
||||
$writer->addRow($footerRow);
|
||||
|
||||
$writer->close();
|
||||
return Helper::responseJson([
|
||||
'file_name' => 'Report-Data-Claim-Request',
|
||||
"file_url" => url('files/Report-Data-Claim-Request.xlsx')
|
||||
]);
|
||||
}
|
||||
|
||||
public function submition($id){
|
||||
$claimRequest = ClaimRequest::findOrFail($id);
|
||||
$claimRequest->status = 'submission';
|
||||
$claimRequest->claim_management = 1;
|
||||
$claimRequest->status_claim_management = 'received';
|
||||
$claimRequest->submission_date_claim_management = date('Y-m-d H:i:s');
|
||||
$claimRequest->submission_by_claim_management = auth()->user()->id;
|
||||
|
||||
$claimRequest->save();
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => 'Update succses',
|
||||
'data' => $claimRequest],
|
||||
200);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,8 +559,8 @@ class CorporateController extends Controller
|
||||
break;
|
||||
case 'claim-request':
|
||||
return Helper::responseJson([
|
||||
'file_name' => "Template Format Claim.xlsx",
|
||||
"file_url" => url('files/Template Format Claim.xlsx')
|
||||
'file_name' => "Template Claim Request.xlsx",
|
||||
"file_url" => url('files/Template Claim Request.xlsx')
|
||||
]);
|
||||
break;
|
||||
case 'request-log':
|
||||
|
||||
@@ -235,6 +235,11 @@ Route::prefix('internal')->group(function () {
|
||||
Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']);
|
||||
|
||||
Route::get('claims', [ClaimController::class, 'index']);
|
||||
Route::get('claims/download-template', [ClaimController::class, 'downloadTemplate']);
|
||||
Route::post('claims/import', [ClaimController::class, 'import']);
|
||||
Route::post('claims/exportFiled/', [ClaimController::class, 'exportFiled']);
|
||||
Route::get('claims/export-claim-management', [ClaimController::class, 'exportClaimManagement']);
|
||||
Route::get('claims/get-provider', [ClaimController::class, 'getProvider']);
|
||||
Route::post('claims/{id}/update-items', [ClaimController::class, 'updateItems'])->name('claim.update-items');
|
||||
Route::post('claims/{id}/update-diagnosis', [ClaimController::class, 'updateDiagnosis'])->name('claim.update-diagnosis');
|
||||
Route::post('claims/{id}/decline', [ClaimController::class, 'decline'])->name('claim.decline');
|
||||
@@ -248,6 +253,7 @@ Route::prefix('internal')->group(function () {
|
||||
|
||||
Route::post('claims', [ClaimController::class, 'store']);
|
||||
Route::get('claims/{id}', [ClaimController::class, 'show']);
|
||||
Route::get('claims/cek_status/{id}', [ClaimController::class, 'cekStatus']);
|
||||
Route::put('claims/{id}', [ClaimController::class, 'update']);
|
||||
Route::get('claims/{id}/edit', [ClaimController::class, 'edit']);
|
||||
Route::post('check-limit', [ClaimController::class, 'checkLimit']);
|
||||
@@ -311,13 +317,15 @@ Route::prefix('internal')->group(function () {
|
||||
Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
|
||||
Route::get('claim-requests/list-member', [ClaimRequestController::class, 'getClaimMemberInfiniteScroll']); // Bagaskoro, BSD 31 Oktober 2023
|
||||
Route::post('claim-requests/{id}/approve', [ClaimRequestController::class, 'approve'])->name('claim-requests.approve');
|
||||
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
|
||||
Route::post('claim-requests/{id}/submition', [ClaimRequestController::class, 'submition'])->name('claim-requests.submition');
|
||||
Route::get('claim-requests/{id}/show', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
|
||||
Route::post('claim-requests', [ClaimRequestController::class, 'createNew']); // Bagaskoro, BSD 2 November 2023
|
||||
Route::post('claim-requests/{id}/update', [ClaimRequestController::class, 'update']);
|
||||
Route::post('claim-requests/import', [ClaimRequestController::class, 'importClaim'])->name('claim-requests.importClaim');
|
||||
Route::get('claim-requests/detail/{id}', [ClaimRequestController::class, 'claimRequestDetail']);
|
||||
Route::post('claim-requests/{id}/invoice-files', [ClaimRequestController::class, 'invoiceFiles']);
|
||||
Route::post('claim-requests/{id}/request-files', [ClaimRequestController::class, 'requestFiles']);
|
||||
Route::get('claim-requests/export', [ClaimRequestController::class, 'exportClaimRequest']);
|
||||
|
||||
Route::get('claim-requests/service/{id}', [ClaimRequestController::class, 'getServiceMember']);
|
||||
|
||||
|
||||
@@ -71,10 +71,22 @@ class ClaimRequestShowResource extends JsonResource
|
||||
'total_excess_paid' => $total_excess_paid,
|
||||
];
|
||||
|
||||
$isReversal = false;
|
||||
$isRole = auth()->user()->role_id;
|
||||
if ($data['request_log']['status'] == 'approved' &&
|
||||
$data['request_log']['status_final_log'] == 'approved' &&
|
||||
$data['status'] == 'approved' &&
|
||||
$data['status_claim_management'] == 'approved' &&
|
||||
$isRole != 1 // is admin
|
||||
){
|
||||
$isReversal = true;
|
||||
}
|
||||
|
||||
|
||||
$response = [
|
||||
'id' => $data['id'],
|
||||
'code' => $data['code'],
|
||||
'status' => $data['status'],
|
||||
'request_log_id' => $data['request_log_id'],
|
||||
'request_log' => $requestLogData,
|
||||
'provider' => $data['request_log']['organization']['name'],
|
||||
@@ -98,7 +110,9 @@ class ClaimRequestShowResource extends JsonResource
|
||||
'service_type' => Helper::serviceName( $data['request_log']['service_code']),
|
||||
'claim_method' => $data['request_log']['payment_type'],
|
||||
'files' => $data['request_log']['files'],
|
||||
'reason_decline' => $data['reason_decline'],
|
||||
// 'benefit_data' => $benefitDetailLog,
|
||||
'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\RequestLogBenefit;
|
||||
use App\Models\RequestLogMedicine;
|
||||
use App\Models\Organization;
|
||||
use App\Models\Exclusion;
|
||||
use App\Models\ClaimRequest;
|
||||
use App\Models\Icd;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\CorporatePolicy;
|
||||
@@ -36,6 +37,23 @@ class RequestLogShowResource extends JsonResource
|
||||
$benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray();
|
||||
$medicineDetailLog = RequestLogMedicine::where('request_log_id', $requestLog['id'])->get()->toArray();
|
||||
$provider = Organization::where('id', $requestLog['organization_id'])->first();
|
||||
$claimRequest = ClaimRequest::where('request_log_id', $requestLog['id'])->first();
|
||||
if ($claimRequest) {
|
||||
$claimCode = $claimRequest->code;
|
||||
$isReversal = false;
|
||||
$isRole = auth()->user()->role_id;
|
||||
if ($requestLog['status'] == 'approved' &&
|
||||
$requestLog['status_final_log'] == 'approved' &&
|
||||
$claimRequest->status == 'approved' &&
|
||||
$claimRequest->status_claim_management == 'approved' &&
|
||||
$isRole != 1
|
||||
){
|
||||
$isReversal = true;
|
||||
}
|
||||
} else {
|
||||
$claimCode = '-';
|
||||
$isReversal = false;
|
||||
}
|
||||
|
||||
if ($provider){
|
||||
$providerName = $provider->name;
|
||||
@@ -90,11 +108,12 @@ class RequestLogShowResource extends JsonResource
|
||||
->whereIn('code', $diagnosis)
|
||||
->select('code', 'name')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $requestLog['id'],
|
||||
'code' => $requestLog['code'],
|
||||
'code_claim' => $claimCode,
|
||||
'member_id' => $requestLog['member']['member_id'],
|
||||
'corporate_id' => $corporateId,
|
||||
'policy_number' =>$policyNumber->code ? $policyNumber->code : '-',
|
||||
@@ -128,6 +147,7 @@ class RequestLogShowResource extends JsonResource
|
||||
'catatan' => $requestLog['catatan'],
|
||||
'reason' => $requestLog['reason'],
|
||||
'diagnosis' => $icd,
|
||||
'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi
|
||||
|
||||
];
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Member;
|
||||
use App\Models\User;
|
||||
use App\Models\Service;
|
||||
use DateTime;
|
||||
|
||||
class Helper
|
||||
{
|
||||
@@ -426,4 +427,20 @@ class Helper
|
||||
ini_set('memory_limit', '256M');
|
||||
}
|
||||
|
||||
public static function dateParser($date_from_row) {
|
||||
if ($date_from_row instanceof DateTime) {
|
||||
return $date_from_row->format('Y-m-d');
|
||||
} else if ($date_from_row != null) {
|
||||
if (strtotime($date_from_row)){
|
||||
return date('Y-m-d', strtotime($date_from_row));
|
||||
} else {
|
||||
// throw new ImportRowException(__('Format Date Invalid'), 0, null, $date_from_row);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// throw new ImportRowException(__('Format Date Invalid'), 0, null, $date_from_row);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,15 @@ class ClaimRequest extends Model
|
||||
'service_code',
|
||||
'policy_id',
|
||||
'status',
|
||||
// New field for claim management
|
||||
'claim_management',
|
||||
'status_claim_management',
|
||||
'submission_date_claim_management',
|
||||
'submission_by_claim_management',
|
||||
'approval_date_claim_management',
|
||||
'approval_by_claim_management',
|
||||
'reason_decline',
|
||||
//
|
||||
'claim_id',
|
||||
'organization_id',
|
||||
'code',
|
||||
@@ -43,69 +52,29 @@ class ClaimRequest extends Model
|
||||
];
|
||||
|
||||
public static $doc_headers_to_field_map = [
|
||||
"PAYOR ID" => "payor_id",
|
||||
"CORPORATE ID" => "corporate_id",
|
||||
"POLICY NUMBER" => "policy_number",
|
||||
"MEMBER ID" => "member_id",
|
||||
"MEMBER NAME" => "member_name",
|
||||
"RECORD TYPE (P/D)" => "record_type",
|
||||
"BENEFIT CODE" => "benefit_code",
|
||||
"BENEFIT DESC" => "benefit_desc",
|
||||
"CLAIM TYPE" => "claim_type",
|
||||
"CLAIM PROCESS STATUS" => "status",
|
||||
"CLIENT CLAIM ID" => "client_claim_id",
|
||||
"REFERENCE NO" => "reference_no",
|
||||
"ADMEDIKA CLAIM ID" => "admika_claim_id",
|
||||
"PROVIDER CODE" => "provider_code",
|
||||
"ADMISSION DATE" => "admission_date",
|
||||
"DISCUTRGE DATE" => "discutrge_date",
|
||||
"DURATION DAYS" => "duration_days",
|
||||
"COVERAGE TYPE" => "coverage_type",
|
||||
"PLAN ID" => "plan_id",
|
||||
"DIAGNOSIS CODE" => "diagnosis_code",
|
||||
"DIAGNOSIS DESC" => "diagnosis_desc",
|
||||
"TOT AMT INCURRED" => "tot_amt_insurred",
|
||||
"TOT AMT APPROVED" => "tot_amt_approved",
|
||||
"TOT AMT NOT APPROVED" => "tot_amt_not_approved",
|
||||
"TOT EXCESS PAID" => "tot_excess_paid",
|
||||
"REMARKS" => "remarks",
|
||||
"SECONDARY DIAGNOSIS CODE" => "secondary_diagnosis",
|
||||
"APPROVED DATE" => "approved_date",
|
||||
"APPROVED BY" => "approved_by",
|
||||
"DATE RECEIVED" => "data_received",
|
||||
"HOSPITAL INVOICE DATE" => "hospital_invoice_date",
|
||||
"Code LOG" => "code",
|
||||
"Date Claim Submission" => "date_submission",
|
||||
"Total Billing" => "total_billing",
|
||||
"Benefit Code" => "benefit_code",
|
||||
"Amt Incurred" => "amount_incurred",
|
||||
"Amt Approved" => "amount_apporve",
|
||||
"Amt Not Approved" => "amount_not_apporve",
|
||||
"Excess Paid" => "excess_paid",
|
||||
"Reason" => "reason",
|
||||
"QC" => "qc",
|
||||
];
|
||||
|
||||
public static $listing_doc_headers = [
|
||||
"PAYOR ID",
|
||||
"CORPORATE ID",
|
||||
"POLICY NUMBER",
|
||||
"MEMBER ID",
|
||||
"MEMBER NAME",
|
||||
"RECORD TYPE (P/D)",
|
||||
"CLAIM TYPE",
|
||||
"CLAIM PROCESS STATUS",
|
||||
"CLIENT CLAIM ID",
|
||||
"REFERENCE NO",
|
||||
"ADMEDIKA CLAIM ID",
|
||||
"PROVIDER CODE",
|
||||
"ADMISSION DATE",
|
||||
"DISCUTRGE DATE",
|
||||
"DURATION DAYS",
|
||||
"COVERAGE TYPE",
|
||||
"PLAN ID",
|
||||
"DIAGNOSIS CODE",
|
||||
"DIAGNOSIS DESC",
|
||||
"TOT AMT INCURRED",
|
||||
"TOT AMT APPROVED",
|
||||
"TOT AMT NOT APPROVED",
|
||||
"TOT EXCESS PAID",
|
||||
"REMARKS",
|
||||
"SECONDARY DIAGNOSIS CODE",
|
||||
"APPROVED DATE",
|
||||
"APPROVED BY",
|
||||
"DATE RECEIVED",
|
||||
"HOSPITAL INVOICE DATE",
|
||||
"Code LOG",
|
||||
"Date Claim Submission",
|
||||
"Total Billing",
|
||||
"Benefit Code",
|
||||
"Amt Incurred",
|
||||
"Amt Approved",
|
||||
"Amt Not Approved",
|
||||
"Excess Paid",
|
||||
// "Reason",
|
||||
"QC",
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ use App\Events\ClaimApproved;
|
||||
use App\Events\ClaimRequested;
|
||||
use App\Models\Claim;
|
||||
use App\Models\ClaimRequest;
|
||||
use App\Models\RequestLog;
|
||||
use App\Models\Benefit;
|
||||
use App\Models\RequestLogBenefit;
|
||||
use App\Models\Organization;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Icd;
|
||||
@@ -20,6 +23,7 @@ use Str;
|
||||
|
||||
class ClaimRequestService{
|
||||
|
||||
private static $code_prefix = 'CLAIM';
|
||||
public static function storeClaimRequest($row = null, $code, $member, $paymentType, $serviceCode, $requestLogID = null, $submissionDate = null, $status = 'requested', $organization_code = null)
|
||||
{
|
||||
// try {
|
||||
@@ -32,24 +36,74 @@ class ClaimRequestService{
|
||||
'code' => $row['provider_code']
|
||||
]), 403, null, $row);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$claimRequestData = [
|
||||
'code' => $code,
|
||||
'request_log_id' => $requestLogID ?? 0,
|
||||
'member_id' => $member->id,
|
||||
'submission_date' => $submissionDate ?? now(),
|
||||
'status' => $status,
|
||||
'payment_type' => $paymentType,
|
||||
'service_code' => $serviceCode,
|
||||
'policy_id' => $member->currentPolicy->id ?? null,
|
||||
'organization_id' => $organization ? $organization->id : 0,
|
||||
];
|
||||
|
||||
$claimRequest = ClaimRequest::create($claimRequestData);
|
||||
if ($status == 'submission'){
|
||||
$claimManagement = 1;
|
||||
$submissionDateClaimManagement = $submissionDate;
|
||||
$submissionByClaimManagement = auth()->user()->id;
|
||||
$statusClaim = 'received';
|
||||
} else {
|
||||
$claimManagement = 0;
|
||||
$submissionDateClaimManagement = null;
|
||||
$submissionByClaimManagement = null;
|
||||
$statusClaim = null;
|
||||
}
|
||||
if (count($row)>0){
|
||||
if($row['total_billing']) {
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'request_log_id' => $requestLogID ?? 0,
|
||||
'member_id' => $member->id,
|
||||
'submission_date' => $submissionDate ?? now(),
|
||||
'status' => $status,
|
||||
'claim_management' => $claimManagement,
|
||||
'status_claim_management' => $statusClaim,
|
||||
'submission_date_claim_management' => $submissionDateClaimManagement,
|
||||
'submission_by_claim_management' => $submissionByClaimManagement,
|
||||
'payment_type' => $paymentType,
|
||||
'service_code' => $serviceCode,
|
||||
'policy_id' => $member->currentPolicy->id ?? null,
|
||||
'organization_id' => $organization ? $organization->id : 0,
|
||||
];
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
$claimRequest = ClaimRequest::updateOrCreate(['request_log_id' => $requestLogID],$data);
|
||||
|
||||
$benefitData = Benefit::where('code', $row['benefit_code'])->first();
|
||||
$requestLogData = RequestLogBenefit::updateOrCreate(
|
||||
[
|
||||
'request_log_id' => $requestLogID,
|
||||
'benefit_id' => $benefitData->id,
|
||||
],[
|
||||
'request_log_id' => $requestLogID,
|
||||
'benefit_id' => $benefitData->id,
|
||||
'amount_incurred' => $row['amount_incurred'],
|
||||
'amount_approved' => $row['amount_apporve'],
|
||||
'amount_not_approved' => $row['amount_not_apporve'],
|
||||
'excess_paid' => $row['excess_paid'],
|
||||
]);
|
||||
} else { // input from hospital portal
|
||||
$data = [
|
||||
'code' => $code,
|
||||
'request_log_id' => $requestLogID ?? 0,
|
||||
'member_id' => $member->id,
|
||||
'submission_date' => $submissionDate ?? now(),
|
||||
'status' => $status,
|
||||
'claim_management' => $claimManagement,
|
||||
'status_claim_management' => $statusClaim,
|
||||
'submission_date_claim_management' => $submissionDateClaimManagement,
|
||||
'submission_by_claim_management' => $submissionByClaimManagement,
|
||||
'payment_type' => $paymentType,
|
||||
'service_code' => $serviceCode,
|
||||
'policy_id' => $member->currentPolicy->id ?? null,
|
||||
'organization_id' => $organization ? $organization->id : 0,
|
||||
];
|
||||
$claimRequest = ClaimRequest::updateOrCreate(['request_log_id' => $requestLogID],$data);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return $claimRequest;
|
||||
@@ -60,8 +114,6 @@ class ClaimRequestService{
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function storeClaimManagement($row, $member, $claim_request_id){
|
||||
try {
|
||||
$organization = 0;
|
||||
@@ -130,53 +182,58 @@ class ClaimRequestService{
|
||||
}
|
||||
}
|
||||
|
||||
protected function validatePlanRow($row)
|
||||
{
|
||||
if (empty($row['member_id'])) {
|
||||
throw new ImportRowException(__('Member ID Required'), 0, null, $row);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleClaimRequestRow($row)
|
||||
{
|
||||
try {
|
||||
$member = Member::where('member_id', $row['member_id'])->with(['currentPlan'])->first();
|
||||
if(!$member){
|
||||
throw new ImportRowException(__('Member Tidak ditemukan'), 0, null, $row);
|
||||
$requestLog = RequestLog::where('code', $row['code'])->first();
|
||||
if(!$requestLog){
|
||||
throw new ImportRowException(__('LOG Tidak ditemukan'), 0, null, $row);
|
||||
};
|
||||
$code = $row['client_claim_id'];
|
||||
$organization_id = $row['provider_code'];
|
||||
$submissionDate = Helper::formatDateDB($row['admission_date']);
|
||||
$paymentType = $row['claim_type'];
|
||||
$status = $row['status'];
|
||||
$serviceCode = $row['coverage_type'];
|
||||
|
||||
if ($requestLog->status != 'approved' && $requestLog != 'approved'){
|
||||
throw new ImportRowException(__('Request LOG / Final LOG Belum di Approved'), 0, null, $row);
|
||||
}
|
||||
$organization = Organization::where('id', $requestLog->organization_id)->first();
|
||||
|
||||
// Create Code
|
||||
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
|
||||
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
|
||||
->value('max_numeric_code');
|
||||
// $next_number = 1;
|
||||
if ($last_numeric_code) {
|
||||
// // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir
|
||||
// $parts = explode('-', $last_code);
|
||||
// $last_number = (int) end($parts);
|
||||
$next_number = $last_numeric_code + 1;
|
||||
} else {
|
||||
$next_number = 1;
|
||||
}
|
||||
// make code
|
||||
$member = Member::with('currentCorporate')->where(['id' => $requestLog->member_id])->first();
|
||||
$sparator = '.';
|
||||
$date = date('ymd');
|
||||
// Menghasilkan kode dengan format yang diinginkan
|
||||
$code = 'CLAIM' . $sparator. 'I' . $sparator. $organization->code . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
|
||||
$submissionDate = Helper::dateParser($row['date_submission']);
|
||||
$paymentType = $requestLog->payment_type;
|
||||
if ($row['qc'] == 'Y'){
|
||||
$status = 'submission';
|
||||
} else {
|
||||
$status = 'requested';
|
||||
}
|
||||
$serviceCode = $requestLog->service_code;
|
||||
$newClaimRequest = $this->storeClaimRequest(
|
||||
row: $row,
|
||||
code: $code,
|
||||
member: $member,
|
||||
paymentType: $paymentType,
|
||||
serviceCode: $serviceCode,
|
||||
requestLogID: $requestLog->id,
|
||||
submissionDate: $submissionDate,
|
||||
status: $status,
|
||||
organization_code: $organization_id
|
||||
organization_code: $organization->code
|
||||
);
|
||||
|
||||
$newlyCreatedID = $newClaimRequest->id;
|
||||
|
||||
$newClaimManangement = $this->storeClaimManagement($row, $member, $newlyCreatedID);
|
||||
ClaimRequested::dispatch($newClaimRequest);
|
||||
// Log History
|
||||
$newClaimRequest->histories()->create([
|
||||
'title' => 'New Claim Requested',
|
||||
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
|
||||
'type' => 'info',
|
||||
'system_origin' => 'import-internal-aso'
|
||||
]);
|
||||
|
||||
$claim_request_data = $row;
|
||||
$this->validatePlanRow($claim_request_data);
|
||||
|
||||
return $newClaimRequest;
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -203,6 +203,8 @@ type ServiceMonitoringProps = {
|
||||
}[];
|
||||
}>
|
||||
>;
|
||||
keterangan: string;
|
||||
catatan: string;
|
||||
};
|
||||
|
||||
export default function ServiceMonitoring() {
|
||||
@@ -591,6 +593,44 @@ export default function ServiceMonitoring() {
|
||||
)}
|
||||
</Grid> */}
|
||||
</Grid>
|
||||
{/* Keterangan */}
|
||||
<Grid item container xs={12} spacing={1.5}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2" color={'grey.600'}>
|
||||
{loading ? <Skeleton animation={'wave'} width={200} /> : 'Keterangan'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" color={'grey.800'}>
|
||||
{loading ? (
|
||||
<Skeleton animation={'wave'} width={300} />
|
||||
) : data && data.keterangan ? (
|
||||
data.keterangan
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{/* Catatan */}
|
||||
<Grid item container xs={12} spacing={1.5}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2" color={'grey.600'}>
|
||||
{loading ? <Skeleton animation={'wave'} width={200} /> : 'Catatan'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1" color={'grey.800'}>
|
||||
{loading ? (
|
||||
<Skeleton animation={'wave'} width={300} />
|
||||
) : data && data.catatan ? (
|
||||
data.catatan
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
import MuiDialog from "@/components/MuiDialog";
|
||||
import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material";
|
||||
import { Button, Card, Checkbox, DialogActions, Grid, TextField, TextareaAutosize, Typography } from "@mui/material";
|
||||
import { Paper } from "@mui/material";
|
||||
import { Stack } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ClaimRequest, Files } from '@/@types/claims';
|
||||
import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime";
|
||||
import { DetailClaimRequest } from "../Model/Types";
|
||||
import { fDateTimesecond, toTitleCase } from "@/utils/formatTime";
|
||||
import axios from "@/utils/axios";
|
||||
import { enqueueSnackbar, useSnackbar } from "notistack";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useNavigate } from "react-router";
|
||||
import * as Yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { RHFTextField } from "@/components/hook-form";
|
||||
|
||||
|
||||
type DialogConfirmationType = {
|
||||
openDialog: boolean;
|
||||
setOpenDialog: any;
|
||||
onSubmit?: void;
|
||||
approve: string;
|
||||
claimRequest: ClaimRequest|undefined;
|
||||
requestLog: DetailClaimRequest|undefined;
|
||||
}
|
||||
|
||||
export default function DialogConfirmation({claimRequest, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
|
||||
|
||||
export default function DialogConfirmation({requestLog, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) {
|
||||
const navigate = useNavigate();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
date: claimRequest?.date,
|
||||
id: claimRequest?.id,
|
||||
reason: claimRequest?.reason
|
||||
|
||||
});
|
||||
|
||||
const handleChange = (field, value) => {
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
@@ -38,24 +33,28 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
|
||||
};
|
||||
|
||||
const handleApprove = () => {
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
status: approve,
|
||||
}));
|
||||
handleSubmit();
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = () => {
|
||||
axios
|
||||
.post(`customer-service/request/final-log`, formData)
|
||||
.post(`claim-requests/${requestLog?.id}/submition`, formData)
|
||||
.then((response) => {
|
||||
enqueueSnackbar('Verification Request LOG Success', { variant: 'success' });
|
||||
enqueueSnackbar('Submition Claim Request Success', { variant: 'success' });
|
||||
setOpenDialog(false);
|
||||
if (requestLog?.service_type == 'Inpatient'){
|
||||
navigate('/case_management/inpatient_monitoring');
|
||||
} else {
|
||||
navigate('/custormer-service/final-log');
|
||||
}
|
||||
navigate('/claim-requests')
|
||||
})
|
||||
.catch(({ response }) => {
|
||||
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const style1 = {
|
||||
color: '#919EAB',
|
||||
width: '30%'
|
||||
@@ -66,34 +65,57 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
const marginBottom1 = {
|
||||
marginBottom: 1,
|
||||
}
|
||||
|
||||
const marginBottom2 = {
|
||||
marginBottom: 2,
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
status: approve,
|
||||
no_identitas: requestLog?.no_identitas ?? '',
|
||||
keterangan: '',
|
||||
hak_kamar_pasien: '',
|
||||
penempatan_kamar: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
setOpenDialog(false);
|
||||
resetForm();
|
||||
}
|
||||
|
||||
|
||||
const handleNumericInput = (input: any) => {
|
||||
const numericInput = input.replace(/\D/g, '');
|
||||
return numericInput;
|
||||
};
|
||||
|
||||
const handleKeyPress = (e:any) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
// Menghentikan default "Enter" (tidak membuat baris baru)
|
||||
e.preventDefault();
|
||||
|
||||
// Menambahkan karakter baris baru
|
||||
handleChange('keterangan', `${formData.keterangan}\n`);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(approve, 'test')
|
||||
const getContent = () => (
|
||||
<Stack spacing={1} marginTop={2}>
|
||||
<Typography variant="subtitle2">Are you sure to {approve == 'approved' ? 'approve' : 'deciline'} this final log ?</Typography>
|
||||
<Typography variant="subtitle1">Are you sure to submit this claim ?</Typography>
|
||||
<Grid item xs={12} md={12} marginTop={4}>
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Member ID</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.member_id}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Policy Number</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.policy_number}</Typography>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Code</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.code}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Submission Date</Typography>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Date Submission</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'}</Typography>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
@@ -105,58 +127,10 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.service_type}</Typography>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Discharge Date</Typography>
|
||||
<TextField
|
||||
label="Discharge Date"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
type="date"
|
||||
value={formData.discharge_date ? fDateOnly(formData.discharge_date) : ''}
|
||||
onChange={(e) => handleChange('discharge_date', e.target.value)}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Catatan</Typography>
|
||||
<TextField
|
||||
label="Catatan"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={formData.catatan}
|
||||
onChange={(e) => handleChange('catatan', e.target.value)}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom2}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Diagnosis ICD - X</Typography>
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={icdOptions}
|
||||
getOptionLabel={(option) => option.label}
|
||||
fullWidth
|
||||
value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))}
|
||||
onChange={(e, newValues) => handleChange('icdCodes', newValues.map((value) => value.value))}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Diagnosis ICD - X"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialog}>Cancel</Button>
|
||||
|
||||
{approve == 'approved' ? (
|
||||
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Approve</Button>
|
||||
) : (
|
||||
<Button color="error" variant="contained" onClick={() => handleApprove()}>Decline</Button>
|
||||
) }
|
||||
|
||||
<Button color="primary" variant="contained" onClick={() => handleApprove()}>Submit</Button>
|
||||
</DialogActions>
|
||||
</Stack>
|
||||
);
|
||||
@@ -168,7 +142,7 @@ export default function DialogConfirmation({claimRequest, setOpenDialog, openDia
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
maxWidth="xl"
|
||||
maxWidth="md"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -78,15 +78,13 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
);
|
||||
|
||||
|
||||
const [date, setDate] = useState(currentClaim?.submission_date)
|
||||
const [date, setDate] = useState(currentClaim?.submission_date ? fDateTimesecond(currentClaim?.submission_date) : null)
|
||||
const id = currentClaim?.id
|
||||
|
||||
useEffect(() => {
|
||||
setDate(currentClaim?.submission_date)
|
||||
}, [currentClaim]);
|
||||
|
||||
console.log(date);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit && currentClaim) {
|
||||
reset(defaultValues);
|
||||
@@ -254,7 +252,6 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
</Stack>
|
||||
)
|
||||
|
||||
|
||||
return (
|
||||
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
|
||||
@@ -310,10 +307,14 @@ export default function FormEdit({ isEdit, currentClaim }: Props) {
|
||||
<RHFDateTimePicker
|
||||
{...field}
|
||||
label="Date of Submission"
|
||||
value={field.value || null}
|
||||
onChange={() => setDate(field.value)}
|
||||
value={field.value}
|
||||
onChange={() =>
|
||||
setDate(field.value)
|
||||
}
|
||||
dateFormat='dd MMM yyyy HH:mm:ss'
|
||||
/>
|
||||
)}
|
||||
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function ClaimsCreateUpdate() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isEdit) {
|
||||
axios.get('/claim-requests/' + id).then((res) => {
|
||||
axios.get('/claim-requests/' + id + '/show').then((res) => {
|
||||
setCurrentClaim(res.data.data);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ import DialogDeleteFileLog from './Components/DialogDeleteFileLog';
|
||||
import DialogBenefit from '../CustomerService/FinalLog/Components/DialogBenefit';
|
||||
import DialogDeleteBenefit from '../CustomerService/FinalLog/Components/DialogDeleteBenefit';
|
||||
import DialogEditBenefit from '../CustomerService/FinalLog/Components/DialogEditBenefit';
|
||||
import DialogConfirmation from './Components/DialogConfirmation';
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -49,6 +51,8 @@ export default function Detail() {
|
||||
const navigate = useNavigate();
|
||||
const { themeStretch } = useSettings();
|
||||
const [claimRequests, setClaimRequest] = useState<DetailClaimRequest>();
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const [isReversal, setIsReversal] = useState(false);
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
@@ -57,6 +61,7 @@ export default function Detail() {
|
||||
.get('claim-requests/detail/'+id)
|
||||
.then((response) => {
|
||||
setClaimRequest(response.data.data)
|
||||
setIsReversal(response.data.data.is_reversal)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -223,13 +228,18 @@ export default function Detail() {
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Document </Typography>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">File</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">File</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Stack>
|
||||
{claimRequests?.files?.map((documentType, index) => (
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
|
||||
@@ -250,14 +260,19 @@ export default function Detail() {
|
||||
<Typography variant="body1" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
|
||||
</a>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
@@ -281,11 +296,15 @@ export default function Detail() {
|
||||
<Card sx={{padding:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
</Stack>
|
||||
|
||||
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
|
||||
@@ -298,29 +317,34 @@ export default function Detail() {
|
||||
{item.benefit?.description}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
) : null
|
||||
}
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} py={2}>
|
||||
@@ -421,7 +445,7 @@ export default function Detail() {
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold'}}>
|
||||
Total Benefit
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -436,12 +460,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Incurred
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountIncurred ? fNumber(totalAmountIncurred) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -452,12 +476,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -468,12 +492,12 @@ export default function Detail() {
|
||||
<Grid item xs={3}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Amount Not Approved
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountNotApproved ? fNumber(totalAmountNotApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -484,12 +508,12 @@ export default function Detail() {
|
||||
<Grid item xs={2}>
|
||||
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="subtitle1">
|
||||
Excess Paid
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>
|
||||
{totalExcessPaid ? fNumber(totalExcessPaid) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
@@ -497,6 +521,23 @@ export default function Detail() {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid container spacing={1} marginY={2}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="subtitle1">
|
||||
Biaya disetujui
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} textAlign="right">
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>
|
||||
{totalAmountApproved ? fNumber(totalAmountApproved) : '0'}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2">
|
||||
Nominal diatas 1 juta akan menunggu persetujuan senior analyst
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
@@ -504,9 +545,25 @@ export default function Detail() {
|
||||
: (
|
||||
null
|
||||
)}
|
||||
|
||||
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
{ claimRequests?.status === 'declined' ? (
|
||||
<Card sx={{padding:2, marginTop:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Reason</Typography>
|
||||
</Stack>
|
||||
<Box sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
|
||||
<Stack direction='row' spacing={2} sx={marginBottom1}>
|
||||
<Typography variant='subtitle2' sx={style1} gutterBottom>Reason Decline</Typography>
|
||||
<Typography variant='subtitle2' sx={style2} gutterBottom>{claimRequests?.reason_decline}</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Card>
|
||||
) : null }
|
||||
|
||||
{/* PR Buat pindahin ke componen */}
|
||||
{/* <CardBenefit
|
||||
requestLog={requestLog}
|
||||
@@ -542,6 +599,42 @@ export default function Detail() {
|
||||
requestLog={requestLog}
|
||||
openDialog={openDialogEditDetail}
|
||||
/> */}
|
||||
|
||||
{(claimRequests?.status === 'requested') || (claimRequests?.status === 'declined') ? (
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction="row" padding={4} sx={{ justifyContent: 'flex-end' }}>
|
||||
<div style={{ marginRight: '10px' }}> {/* Perubahan sintaksis disini */}
|
||||
<Button
|
||||
variant="outlined"
|
||||
color='inherit'
|
||||
onClick={() => {
|
||||
navigate('/claim-requests')
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setOpenDialogSubmit(true);
|
||||
setApprove('approved');
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
<DialogConfirmation
|
||||
setOpenDialog={setOpenDialogSubmit}
|
||||
requestLog={claimRequests}
|
||||
openDialog={openDialogSubmit}
|
||||
approve={approve}
|
||||
>
|
||||
</DialogConfirmation>
|
||||
</Stack>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ export default function List() {
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearchSubmit(event);
|
||||
}
|
||||
}}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Name...'
|
||||
/>
|
||||
@@ -152,7 +157,7 @@ export default function List() {
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
label="End Date"
|
||||
inputFormat="MM/dd/yyyy"
|
||||
inputFormat="dd/MM/yyyy"
|
||||
value={searchParams.get('end_date')}
|
||||
onChange={(value) => {
|
||||
try {
|
||||
@@ -274,7 +279,7 @@ export default function List() {
|
||||
}
|
||||
|
||||
const handleGetData = (type :string) => {
|
||||
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
|
||||
axios.get(`claim-requests/export`)
|
||||
.then((response) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = response.data.data.file_url;
|
||||
@@ -361,14 +366,32 @@ export default function List() {
|
||||
</Stack>
|
||||
)}
|
||||
{importResult && (
|
||||
// <Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
// <Box sx={{ color: 'text.secondary' }}>
|
||||
// Last Import Result Report :{' '}
|
||||
// <a href={importResult.result_file?.url ?? '#'}>
|
||||
// {importResult.result_file?.name ?? '-'}
|
||||
// </a>
|
||||
// </Box>
|
||||
// </Stack>
|
||||
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result Report :{' '}
|
||||
Last Import Result :{' '}
|
||||
<Box sx={{ color: 'success.main', display: 'inline' }}>
|
||||
{importResult.result_file?.total_success_row ?? 0}
|
||||
</Box>{' '}
|
||||
Row Processed,{' '}
|
||||
<Box sx={{ color: 'error.main', display: 'inline' }}>
|
||||
{importResult.result_file?.total_failed_row}
|
||||
</Box>{' '}
|
||||
Failed, Report :{' '}
|
||||
<a href={importResult.result_file?.url ?? '#'}>
|
||||
{importResult.result_file?.name ?? '-'}
|
||||
</a>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -393,7 +416,7 @@ export default function List() {
|
||||
setDataTableLoading(true);
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/claim-requests', { params: filter });
|
||||
console.log(response.data);
|
||||
|
||||
setDataTableLoading(false);
|
||||
|
||||
|
||||
@@ -468,8 +491,12 @@ export default function List() {
|
||||
<TableCell align="left">{row.service_name}</TableCell>
|
||||
<TableCell align="left">{row.payment_type_name}</TableCell>
|
||||
<TableCell align="left">
|
||||
{ row.status == "requested" ?
|
||||
{ row.status == "requested" ?
|
||||
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status)}</Label>) :
|
||||
row.status == "submission" ?
|
||||
(<Label color='info'> {capitalizeFirstLetter(row.status)}</Label>) :
|
||||
row.status == "declined" ?
|
||||
(<Label color='error'> {capitalizeFirstLetter(row.status)}</Label>) :
|
||||
(<Label color='success'> {capitalizeFirstLetter(row.status)}</Label>)
|
||||
}
|
||||
</TableCell>
|
||||
|
||||
@@ -51,6 +51,7 @@ export type DetailClaimRequest = {
|
||||
benefit_data : BenefitData[],
|
||||
diagnosis : Diagnosis[],
|
||||
request_log : RequestLogType | undefined,
|
||||
reason_decline : string,
|
||||
}
|
||||
|
||||
export type RequestLogType = {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import List from "./List";
|
||||
|
||||
export default function Claims() {
|
||||
|
||||
const pageTitle = 'Claim';
|
||||
const pageTitle = 'Claim Management';
|
||||
return (
|
||||
<Page title={ pageTitle } sx={{ mx: 2}}>
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function Claims() {
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Claim',
|
||||
name: 'Claim Management',
|
||||
href: '/claims',
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
@@ -17,12 +18,24 @@ import {
|
||||
ButtonGroup,
|
||||
Tooltip,
|
||||
TableHead,
|
||||
Checkbox,
|
||||
InputAdornment,
|
||||
TableSortLabel,
|
||||
FormControl
|
||||
} from '@mui/material';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
|
||||
import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { fDateOnly } from '@/utils/formatTime';
|
||||
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
// components
|
||||
import axios from '../../utils/axios';
|
||||
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
|
||||
@@ -32,23 +45,146 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||
import { Chip } from '@mui/material';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { fDate } from '../../utils/formatTime';
|
||||
import { fDate, fDateTime } from '../../utils/formatTime';
|
||||
import { Claims } from '@/@types/claims';
|
||||
import Label from '@/components/Label';
|
||||
import { capitalizeFirstLetter } from '@/utils/formatString';
|
||||
import TableMoreMenu from '@/components/table/TableMoreMenu';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import { Download } from '@mui/icons-material';
|
||||
import { Add, Search } from '@mui/icons-material';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
|
||||
export default function List() {
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [providers, setProviders] = useState(null);
|
||||
// const [searchText, setSearchText] = useState('');
|
||||
const [order, setOrder] = useState<Order>('desc');
|
||||
const [orderBy, setOrderBy] = useState('created_at');
|
||||
const [perPage, setPerPage] = useState<number>(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
// Jika newValue tidak undefined, atur nilai dataProvider
|
||||
if (newValue !== undefined) {
|
||||
setDataProvider(newValue.service_code);
|
||||
} else {
|
||||
// Jika tidak ada yang dipilih, set dataProvider menjadi string kosong
|
||||
setDataProvider(null);
|
||||
}
|
||||
};
|
||||
// Dummy data
|
||||
const dummyServices = [
|
||||
{ service_code: '1', name: 'Service 1' },
|
||||
{ service_code: '2', name: 'Service 2' },
|
||||
{ service_code: '3', name: 'Service 3' },
|
||||
// tambahkan data lain sesuai kebutuhan
|
||||
];
|
||||
|
||||
|
||||
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData.data
|
||||
.filter(row => row.status === 'received') // Memfilter baris dengan status 'requested'
|
||||
.map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRowSelect = (id) => {
|
||||
if (selectedRows.includes(id)) {
|
||||
setSelectedRows(selectedRows.filter(rowId => rowId !== id));
|
||||
} else {
|
||||
setSelectedRows([...selectedRows, id]);
|
||||
}
|
||||
};
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [importResult, setImportResult] = useState(null);
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [endDate, setEndDate] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
const [dataProvider, setDataProvider] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (startDate !== null || endDate !== null || dataProvider !== null
|
||||
|| order !== null || orderBy !== null || perPage !== 0) {
|
||||
loadDataTableData();
|
||||
getProvider();
|
||||
}
|
||||
}, [startDate, endDate, dataProvider, order, orderBy, perPage]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadingImport, setIsLoadingImport] = useState(false);
|
||||
const handleExportReport = async () => {
|
||||
|
||||
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
|
||||
|
||||
var filter = Object.fromEntries([...searchParams.entries()]);
|
||||
setIsLoading(true)
|
||||
await axios
|
||||
.get('/claims/export-claim-management',{
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Data berhasil di Export', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
});
|
||||
setIsLoading(false)
|
||||
|
||||
document.location.href = res.data.data.file_url;
|
||||
})
|
||||
.catch((err) =>
|
||||
enqueueSnackbar('Data Gagal di Export', {
|
||||
variant: 'error',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
@@ -73,7 +209,7 @@ export default function List() {
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
setSearchText(searchParams.get('search') ?? '');
|
||||
// setSearchText(searchParams.get('search') ?? '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -126,41 +262,331 @@ export default function List() {
|
||||
);
|
||||
}
|
||||
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
||||
//handle search
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
setSearchText(newSearchText);
|
||||
};
|
||||
|
||||
const handleSearchSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
loadDataTableData();
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
//setSearchText(searchText);
|
||||
}, []);
|
||||
|
||||
const item = [
|
||||
{
|
||||
id: '',
|
||||
value: '',
|
||||
name: 'Semua',
|
||||
},
|
||||
];
|
||||
|
||||
// const handleClick = () => {
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Dummy Default Data
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState(true);
|
||||
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
|
||||
LaravelPaginatedDataDefault
|
||||
);
|
||||
|
||||
|
||||
|
||||
const loadDataTableData = async (appliedFilter: any | null = null) => {
|
||||
setDataTableLoading(true);
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/claims', { params: filter });
|
||||
// console.log(response.data);
|
||||
const response = await axios.get('/claims', {
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
});
|
||||
|
||||
setDataTableLoading(false);
|
||||
|
||||
setDataTableData(response.data);
|
||||
};
|
||||
|
||||
const getProvider = async () => {
|
||||
const response = await axios.get('/claims/get-provider');
|
||||
setProviders(response.data)
|
||||
}
|
||||
|
||||
const applyFilter = async (searchFilter: { search: string }) => {
|
||||
await loadDataTableData(searchFilter);
|
||||
setSearchParams(searchFilter);
|
||||
};
|
||||
|
||||
|
||||
const handlePageChange = (event: ChangeEvent, value: number): void => {
|
||||
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
|
||||
loadDataTableData(filter);
|
||||
setSearchParams(filter);
|
||||
setPerPage(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadDataTableData();
|
||||
}, []);
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
|
||||
function toTitleCase(str: string | null) {
|
||||
return str.replace(/\w\S*/g, function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
const [approve, setApprove] = useState('');
|
||||
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
|
||||
const handleReasonDeclineChange = (event) => {
|
||||
setReasonDecline(event.target.value);
|
||||
// Tambahkan logika yang diperlukan di sini
|
||||
};
|
||||
|
||||
const handleSubmitData = () => {
|
||||
//approve or decline
|
||||
if (!reasonDecline && approve == 'decline') {
|
||||
enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
|
||||
return false;
|
||||
}
|
||||
Promise.all(selectedRows.map(send_bulk))
|
||||
.then(() => {
|
||||
enqueueSnackbar('All requests processed successfully', { variant: 'success' });
|
||||
setOpenDialogSubmit(false);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000); // Reload the page after 5 seconds
|
||||
})
|
||||
.catch((error) => {
|
||||
enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
};
|
||||
|
||||
function send_bulk(id) {
|
||||
return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline });
|
||||
}
|
||||
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const createMenu = Boolean(anchorEl);
|
||||
const importHospital = useRef<HTMLInputElement>(null);
|
||||
const [currentImportFileName, setCurrentImportFileName] = useState(null);
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
const [importResult, setImportResult] = useState(null);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleImportButton = () => {
|
||||
if (importHospital?.current) {
|
||||
handleClose();
|
||||
importHospital.current ? importHospital.current.click() : console.log('No File selected');
|
||||
} else {
|
||||
alert('No file selected');
|
||||
}
|
||||
};
|
||||
const handleCancelImportButton = () => {
|
||||
if(importHospital.current)
|
||||
{
|
||||
importHospital.current.value = '';
|
||||
importHospital.current.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
};
|
||||
const handleImportChange = (event: any) => {
|
||||
if (event.target.files[0]) {
|
||||
setCurrentImportFileName(event.target.files[0].name);
|
||||
} else {
|
||||
setCurrentImportFileName(null);
|
||||
}
|
||||
};
|
||||
const handleUpload = () => {
|
||||
if(importHospital.current && importHospital.current.files)
|
||||
{
|
||||
if (importHospital.current?.files.length) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', importHospital.current?.files[0]);
|
||||
setImportLoading(true);
|
||||
axios
|
||||
.post('claims/import', formData)
|
||||
.then((response) => {
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
setImportResult(response.data);
|
||||
setImportLoading(false);
|
||||
enqueueSnackbar('Success Import Hospitals', { variant: 'success' });
|
||||
})
|
||||
.catch((response) => {
|
||||
enqueueSnackbar(
|
||||
'Looks like something went wrong. Please check your data and try again. ' +
|
||||
response.message,
|
||||
{ variant: 'error' }
|
||||
);
|
||||
setImportLoading(false);
|
||||
});
|
||||
} else {
|
||||
enqueueSnackbar('No File Selected', { variant: 'warning' });
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleGetTemplate = () => {
|
||||
axios.get('claims/download-template').then((response) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = response.data.data.file_url;
|
||||
link.setAttribute('download', response.data.data.file_name);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
handleClose();
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
const handleExportReportFiled = async () => {
|
||||
|
||||
await axios
|
||||
.post('claims/exportFiled', { params: importResult?.data.failed_rows })
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Data berhasil di Export', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
});
|
||||
setIsLoading(false)
|
||||
|
||||
document.location.href = res.data.data.file_url;
|
||||
})
|
||||
.catch((err) =>
|
||||
enqueueSnackbar('Data Gagal di Export', {
|
||||
variant: 'error',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// loadDataTableData();
|
||||
// getProvider();
|
||||
// }, []);
|
||||
|
||||
const headStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
const headCells = [
|
||||
{
|
||||
id: 'code',
|
||||
align: 'left',
|
||||
label: 'Code',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
align: 'left',
|
||||
label: 'Name',
|
||||
isSort: false,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'member_id',
|
||||
align: 'left',
|
||||
label: 'Member ID',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'created_at',
|
||||
align: 'left',
|
||||
label: 'Date Submission',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'plan_code',
|
||||
align: 'left',
|
||||
label: 'Plan ID',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'service_code',
|
||||
align: 'left',
|
||||
label: 'Service',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'corporate_policies',
|
||||
align: 'left',
|
||||
label: 'Policy Number',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'provider',
|
||||
align: 'left',
|
||||
label: 'Provider',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'tot_bill',
|
||||
align: 'left',
|
||||
label: 'Total Billing',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
align: 'left',
|
||||
label: 'Status',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
align: 'left',
|
||||
label: '',
|
||||
isSort: false,
|
||||
},
|
||||
];
|
||||
|
||||
const orders = {
|
||||
order: order,
|
||||
setOrder: setOrder,
|
||||
orderBy: orderBy,
|
||||
setOrderBy: setOrderBy,
|
||||
};
|
||||
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
|
||||
handleRequestSort(event, property);
|
||||
};
|
||||
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
|
||||
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
|
||||
|
||||
orders?.setOrder(isAsc ? 'desc' : 'asc');
|
||||
orders?.setOrderBy(property);
|
||||
};
|
||||
// Called on every row to map the data to the columns
|
||||
function createData(data: Claims): Claims {
|
||||
return {
|
||||
@@ -171,10 +597,18 @@ export default function List() {
|
||||
{
|
||||
/* ------------------ TABLE ROW ------------------ */
|
||||
}
|
||||
function Row(props: { row: ReturnType<typeof createData> }) {
|
||||
const { row } = props;
|
||||
function Row(props: { row: ReturnType<typeof createData>, isSelected: boolean, onSelect: (id: string) => void }) {
|
||||
const { row, isSelected, onSelect } = props;
|
||||
// Memperbaiki destrukturisasi props
|
||||
|
||||
const handleRowCheckboxChange = () => {
|
||||
onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik
|
||||
};
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const test = 1000;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
|
||||
@@ -183,16 +617,22 @@ export default function List() {
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell> */}
|
||||
<TableCell align="left">{row.claim_request?.code}</TableCell>
|
||||
<TableCell align="left">
|
||||
{row?.status == 'received' ? (
|
||||
<Checkbox checked={isSelected} onChange={handleRowCheckboxChange} />
|
||||
):''}
|
||||
</TableCell>
|
||||
<TableCell align="left">{row?.code}</TableCell>
|
||||
{/* <TableCell align="left">{row.code}</TableCell> */}
|
||||
<TableCell align="left">{row.member?.current_plan?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.payor_id}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.current_corporate?.current_policy?.code}</TableCell>
|
||||
<TableCell align="left">{row.member?.member_id}</TableCell>
|
||||
<TableCell align="left">{row.benefit_desc}</TableCell>
|
||||
|
||||
<TableCell align="center">
|
||||
<TableCell align="left">{row?.name}</TableCell>
|
||||
<TableCell align="left">{row?.member_id}</TableCell>
|
||||
<TableCell align="left">{row?.created_at ? fDateTime(row?.created_at) : ''}</TableCell>
|
||||
<TableCell align="left">{row?.plan_code}</TableCell>
|
||||
<TableCell align="left">{row?.service_code}</TableCell>
|
||||
<TableCell align="left">{row?.corporate_policies}</TableCell>
|
||||
<TableCell align="left">{row?.provider}</TableCell>
|
||||
<TableCell align="left">Rp. {row?.tot_bill?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".")}</TableCell>
|
||||
<TableCell align="left">
|
||||
{row.status == 'draft' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'requested' && (<Label color='primary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
{row.status == 'received' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
@@ -202,18 +642,17 @@ export default function List() {
|
||||
{row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
|
||||
</TableCell>
|
||||
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }>
|
||||
<Edit />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
<TableCell align="left">
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate('/claims/detail/'+row.id_log+'/'+row.id+'') }>
|
||||
<FindInPageOutlinedIcon />
|
||||
Detail
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</TableCell>
|
||||
|
||||
|
||||
|
||||
</TableRow>
|
||||
@@ -236,40 +675,77 @@ export default function List() {
|
||||
/* ------------------ END TABLE ROW ------------------ */
|
||||
}
|
||||
|
||||
|
||||
|
||||
function TableContent() {
|
||||
return (
|
||||
<Table aria-label="collapsible table">
|
||||
{/* ------------------ TABLE HEADER ------------------ */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{/* <TableCell style={headStyle} align="left" /> */}
|
||||
<TableCell style={headStyle} align="left">
|
||||
Code
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Plan ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Payor ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Corporate ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Policy Number
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Member ID
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Benefit Desc
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
Status
|
||||
</TableCell>
|
||||
<TableCell style={headStyle} align="left">
|
||||
|
||||
</TableCell>
|
||||
{selectedRows.length > 0 ? (
|
||||
<>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
|
||||
<Stack direction="row">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
{selectedRows.length > 0 ? selectedRows.length : '0'} <Typography variant='subtitle2'>Selected</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={6}>
|
||||
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="right" colSpan={2}>
|
||||
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('decline');}}>
|
||||
<Typography variant='subtitle2'>Decline</Typography>
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
|
||||
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('approve');}}>
|
||||
<Typography variant='subtitle2'>Approve</Typography>
|
||||
</Button>
|
||||
</TableCell>
|
||||
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TableCell style={headStyle} align="left">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
</TableCell>
|
||||
{headCells &&
|
||||
headCells.map((headCell, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||
// @ts-ignore
|
||||
align={headCell.align}
|
||||
sx={{ padding: 2 }}
|
||||
width={headCell.width ? headCell.width : 'auto'}
|
||||
>
|
||||
{headCell.isSort ? (
|
||||
<TableSortLabel
|
||||
active={orders?.orderBy === headCell.id}
|
||||
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orders?.orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
headCell.label
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
@@ -278,7 +754,7 @@ export default function List() {
|
||||
{dataTableIsLoading ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
<TableCell colSpan={11} align="center">
|
||||
Loading
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -286,7 +762,7 @@ export default function List() {
|
||||
) : dataTableData.data.length === 0 ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} align="center">
|
||||
<TableCell colSpan={11} align="center">
|
||||
No Data
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -294,7 +770,7 @@ export default function List() {
|
||||
) : (
|
||||
<TableBody>
|
||||
{dataTableData.data.map((row) => (
|
||||
<Row key={row.id} row={row} />
|
||||
<Row key={row.id} row={row} isSelected={selectedRows.includes(row.id)} onSelect={handleRowSelect} />
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
@@ -305,7 +781,212 @@ export default function List() {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ImportForm />
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
|
||||
>
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<form style={{ width: '100%' }}>
|
||||
<Grid container spacing={1} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={importHospital}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleImportChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
|
||||
/>
|
||||
{!currentImportFileName && (
|
||||
<>
|
||||
<Grid item xs={12} md={3}>
|
||||
<TextField
|
||||
id="search-input"
|
||||
ref={searchInput}
|
||||
variant="outlined"
|
||||
value={searchText}
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearchSubmit(event);
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search />
|
||||
</InputAdornment>
|
||||
),
|
||||
placeholder: 'Search Code or Name',
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} display="flex" sx={{ gap: '16px' }}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={startDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
|
||||
// loadDataTableData();
|
||||
setStartDate(value);
|
||||
}}
|
||||
renderInput={(params) => <TextField {...params} fullWidth label="Start" />}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={endDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
setEndDate(value);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
fullWidth
|
||||
label="End"
|
||||
// error={!!error}
|
||||
// helperText={error?.message}
|
||||
// {...other}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
{
|
||||
providers && (
|
||||
<Autocomplete
|
||||
id="provider"
|
||||
options={providers}
|
||||
getOptionLabel={(option) => option.name || ''}
|
||||
value={providers.find((item) => item.id === dataProvider) || null}
|
||||
onChange={(event, value) => {
|
||||
if (value) {
|
||||
setDataProvider(value.id);
|
||||
} else {
|
||||
setDataProvider(null);
|
||||
}
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Provider"
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3} display="flex" sx={{ gap: '16px' }}>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
loading={isLoadingImport}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Import
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
<Menu
|
||||
id="import-button"
|
||||
anchorEl={anchorEl}
|
||||
open={createMenu}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>
|
||||
<Typography variant='body2'>Import</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleGetTemplate();
|
||||
}}
|
||||
>
|
||||
<Typography variant='body2'> Download Template</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</FormControl>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="contained"
|
||||
startIcon={<Download />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleExportReport}
|
||||
loading={isLoading}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Export
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
{currentImportFileName && (
|
||||
<Grid item xs={12} md={12}>
|
||||
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
|
||||
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
|
||||
<Button onClick={handleImportButton} fullWidth>
|
||||
{currentImportFileName ?? 'No File Selected'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCancelImportButton}
|
||||
size="small"
|
||||
fullWidth={false}
|
||||
sx={{ p: 1.8 }}
|
||||
>
|
||||
<CancelIcon color="error" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleUpload}
|
||||
loading={importLoading}
|
||||
>
|
||||
Upload
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Grid>
|
||||
)}
|
||||
{importResult && (
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: 'text.secondary' }}>
|
||||
Last Import Result :{' '}
|
||||
<Box sx={{ color: 'success.main', display: 'inline' }}>
|
||||
{importResult.data.total_success_row ?? 0}
|
||||
</Box>{' '}
|
||||
Row Processed,{' '}
|
||||
<Box sx={{ color: 'error.main', display: 'inline' }}>
|
||||
{importResult.data.total_failed_row}
|
||||
</Box>{' '}
|
||||
Failed
|
||||
{/* {importResult.data.failed_rows.map((row, index) => (
|
||||
<Typography variant='body' key={index} color="error"> [Code={row.code ? row.code : 'Required'}]</Typography>
|
||||
))} */}
|
||||
<u onClick={handleExportReportFiled} style={{cursor:'pointer'}}>Download Data Filed</u>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
@@ -314,6 +995,43 @@ export default function List() {
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
|
||||
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<Stack direction="row" alignItems='center' spacing={1}>
|
||||
<Typography variant="h6">Confirmation</Typography>
|
||||
</Stack>
|
||||
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
||||
<Stack spacing={2} padding={2}>
|
||||
<Typography variant='body1'>Are you sure to {toTitleCase(approve)} this claim ?</Typography>
|
||||
{approve == "decline" ? (
|
||||
<Stack direction='row' spacing={2} marginTop={2}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label="Reason decline"
|
||||
multiline
|
||||
rows={4} // Tentukan jumlah baris yang diinginkan
|
||||
defaultValue=""
|
||||
onChange={handleReasonDeclineChange}
|
||||
variant="outlined"
|
||||
sx={{width:'100%'}}
|
||||
// fullWidth // Gunakan ini jika Anda ingin input memenuhi lebar Stack
|
||||
/>
|
||||
</Stack>
|
||||
): ''}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
|
||||
<Button sx={{backgroundColor: (approve === 'decline' ? '' : '#19BBBB'), color: (approve === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(approve === 'decline' ? 'outlined' : 'contained')}>{(approve === "decline" ? 'Decline' : 'Approve')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,51 +145,62 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
|
||||
const methods = useForm<any>({
|
||||
resolver: yupResolver(validationSchema),
|
||||
defaultValues
|
||||
defaultValues,
|
||||
reValidateMode: "onChange"
|
||||
});
|
||||
|
||||
let width = claimInput ? 2 : 2.36;
|
||||
|
||||
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control});
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods
|
||||
|
||||
// Buat total data
|
||||
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_incurred || 0);
|
||||
}, 0);
|
||||
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_approved || 0);
|
||||
}, 0);
|
||||
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_not_approved || 0);
|
||||
}, 0);
|
||||
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.excess_paid || 0);
|
||||
}, 0);
|
||||
const {fields, append, remove} = useFieldArray({name: 'benefit_data',control: methods.control,});
|
||||
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors,isValid } } = methods
|
||||
|
||||
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
|
||||
// Calculate
|
||||
const benefitData = watch('benefit_data');
|
||||
const [isDisableSave, setDisableSave] = useState(false)
|
||||
|
||||
benefitData?.map((item, index) => {
|
||||
totalAmountIncurred += parseFloat(item.amount_incurred);
|
||||
totalAmountApproved += parseFloat(item.amount_approved);
|
||||
totalAmountNotApproved += parseFloat(item.amount_not_approved);
|
||||
totalExcessPaid += parseFloat(item.excess_paid);
|
||||
const totalAll = () => {
|
||||
let totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_incurred || 0);
|
||||
}, 0);
|
||||
let totalAmountApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_approved || 0);
|
||||
}, 0);
|
||||
let totalAmountNotApproved = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.amount_not_approved || 0);
|
||||
}, 0);
|
||||
let totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => {
|
||||
return accumulator + (item.excess_paid || 0);
|
||||
}, 0);
|
||||
|
||||
if (totalAmountApproved != 0 && totalAmountIncurred != 0) {
|
||||
if (totalAmountApproved > totalAmountIncurred){
|
||||
setValue(`benefit_data.${index}.amount_approved`, 0)
|
||||
alert('Total Amount Approved tidak boleh lebih dari Total Incurred')
|
||||
}
|
||||
}
|
||||
benefitData?.map((item, index) => {
|
||||
totalAmountIncurred += parseFloat(item.amount_incurred);
|
||||
totalAmountApproved += parseFloat(item.amount_approved);
|
||||
totalAmountNotApproved += parseFloat(item.amount_not_approved);
|
||||
totalExcessPaid += parseFloat(item.excess_paid);
|
||||
});
|
||||
|
||||
return {
|
||||
totalAmountIncurred,
|
||||
totalAmountApproved,
|
||||
totalAmountNotApproved,
|
||||
totalExcessPaid
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnChangeNominal = (key) => {
|
||||
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
|
||||
// setValue(`benefit_data.${key}.amount_approved`, 0);
|
||||
setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
|
||||
} else {
|
||||
clearErrors(`benefit_data.${key}.amount_approved`);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Submit Form
|
||||
// =====================================
|
||||
const submitHandler = async (data: BenefitConfigurationListType) => {
|
||||
const mapData = data.benefit_data.map((item) => ({
|
||||
...item,
|
||||
reason: item.reason.value
|
||||
reason: item.reason ? item.reason.value : null
|
||||
}));
|
||||
|
||||
const newData = {
|
||||
@@ -206,8 +217,6 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate
|
||||
|
||||
const getContent = () => !addBenefit ? (
|
||||
<Stack spacing={2} sx={{marginTop: 2, padding: 2}} direction="column">
|
||||
<Stack direction="row" spacing={2}>
|
||||
@@ -281,6 +290,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
name={`benefit_data.${index}.amount_incurred`}
|
||||
placeholder='Amount Incurred'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`benefit_data.${index}.amount_incurred`, event.target.value)
|
||||
handleOnChangeNominal(index)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -301,6 +314,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
name={`benefit_data.${index}.amount_approved`}
|
||||
placeholder='Amount Approved'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`benefit_data.${index}.amount_approved`, event.target.value)
|
||||
handleOnChangeNominal(index)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -434,7 +451,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountIncurred)}
|
||||
{fNumber(totalAll().totalAmountIncurred)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -450,7 +467,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountApproved)}
|
||||
{fNumber(totalAll().totalAmountApproved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -466,7 +483,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountNotApproved)}
|
||||
{fNumber(totalAll().totalAmountNotApproved)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -482,7 +499,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalExcessPaid)}
|
||||
{fNumber(totalAll().totalExcessPaid)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -499,7 +516,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl
|
||||
<DialogActions>
|
||||
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Button variant="outlined" onClick={handleCloseDialogBenefit}><Typography>Cancel</Typography></Button>
|
||||
<LoadingButton disabled={isDisableSave} type="submit" variant="contained" loading={isSubmitting}>
|
||||
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
|
||||
@@ -62,23 +62,36 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
defaultValues
|
||||
});
|
||||
|
||||
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
|
||||
const { handleSubmit, reset, watch, setValue, setError, clearErrors, formState: { isDirty, isSubmitting, errors } } = methods;
|
||||
const errorsExist = errors ? Object.keys(errors).length > 0 : false;
|
||||
const totalAll = () => {
|
||||
// Ambil nilai dari form menggunakan watch
|
||||
const amountIncurred = parseFloat(watch('amount_incurred'));
|
||||
const amountApproved = parseFloat(watch('amount_approved'));
|
||||
const amountNotApproved = parseFloat(watch('amount_not_approved'));
|
||||
const excessPaid = parseFloat(watch('excess_paid'));
|
||||
|
||||
// Hitung total baru
|
||||
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
|
||||
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
|
||||
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
|
||||
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
|
||||
|
||||
// Ambil nilai dari form menggunakan watch
|
||||
const amountIncurred = parseFloat(watch('amount_incurred'));
|
||||
const amountApproved = parseFloat(watch('amount_approved'));
|
||||
const amountNotApproved = parseFloat(watch('amount_not_approved'));
|
||||
const excessPaid = parseFloat(watch('excess_paid'));
|
||||
return {
|
||||
totalAmountIncurred,
|
||||
totalAmountApproved,
|
||||
totalAmountNotApproved,
|
||||
totalExcessPaid
|
||||
}
|
||||
}
|
||||
|
||||
// Hitung total baru
|
||||
const totalAmountIncurred = total.totalAmountIncurred - data?.amount_incurred + amountIncurred;
|
||||
const totalAmountApproved = total.totalAmountApproved - data?.amount_approved + amountApproved;
|
||||
const totalAmountNotApproved = total.totalAmountNotApproved - data?.amount_not_approved + amountNotApproved;
|
||||
const totalExcessPaid = total.totalExcessPaid - data?.excess_paid + excessPaid;
|
||||
|
||||
if (totalAmountApproved > totalAmountIncurred) {
|
||||
alert('Total Approve tidak boleh melebihi Total Incurred')
|
||||
setValue('amount_approved', data?.amount_approved)
|
||||
const handleOnChangeNominal = (key) => {
|
||||
if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){
|
||||
// setValue(`benefit_data.${key}.amount_approved`, 0);
|
||||
setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'});
|
||||
} else {
|
||||
clearErrors(`amount_approved`);
|
||||
}
|
||||
}
|
||||
|
||||
// if (totalAmountIncurred !== (totalAmountApproved+totalAmountNotApproved)){
|
||||
@@ -142,6 +155,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
name={`amount_incurred`}
|
||||
placeholder='Amount Incurred'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`amount_incurred`, event.target.value)
|
||||
handleOnChangeNominal(id)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -162,6 +179,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
name={`amount_approved`}
|
||||
placeholder='Amount Approved'
|
||||
required
|
||||
onChange={(event) => {
|
||||
setValue(`amount_approved`, event.target.value)
|
||||
handleOnChangeNominal(id)}
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -275,7 +296,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{totalAmountIncurred ? fNumber(totalAmountIncurred) : 0}
|
||||
{totalAll().totalAmountIncurred ? fNumber(totalAll().totalAmountIncurred) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -291,7 +312,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountApproved)}
|
||||
{totalAll().totalAmountApproved ? fNumber(totalAll().totalAmountApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -307,7 +328,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalAmountNotApproved)}
|
||||
{totalAll().totalAmountNotApproved ? fNumber(totalAll().totalAmountNotApproved) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -323,7 +344,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 'bold', textAlign: 'right' }}>
|
||||
{fNumber(totalExcessPaid)}
|
||||
{totalAll().totalExcessPaid ? fNumber(totalAll().totalExcessPaid) : 0}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -337,7 +358,7 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog,
|
||||
<DialogActions>
|
||||
<Stack direction="row" sx={{marginTop:3}} alignItems="center" justifyContent="space-between" spacing={2}>
|
||||
<Button variant="outlined" onClick={handleCloseDialog}><Typography>Cancel</Typography></Button>
|
||||
<LoadingButton type="submit" variant="contained" loading={isSubmitting}>
|
||||
<LoadingButton disabled={errorsExist} type="submit" variant="contained" loading={isSubmitting}>
|
||||
Save
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
|
||||
@@ -68,6 +68,7 @@ export default function Detail() {
|
||||
const navigate = useNavigate();
|
||||
const { themeStretch } = useSettings();
|
||||
const [requestLog, setRequestLog] = useState<DetailFinalLogType>();
|
||||
const [isReversal, setIsReversal] = useState(false);
|
||||
|
||||
|
||||
const { id } = useParams();
|
||||
@@ -77,6 +78,7 @@ export default function Detail() {
|
||||
.get('customer-service/request/'+id)
|
||||
.then((response) => {
|
||||
setRequestLog(response.data.data)
|
||||
setIsReversal(response.data.data.is_reversal)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -304,11 +306,15 @@ export default function Detail() {
|
||||
<Card sx={{padding:2}} >
|
||||
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogBenefit(true);
|
||||
}} >
|
||||
<Typography variant="button" display="block">Benefit</Typography>
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
</Stack>
|
||||
|
||||
{requestLog?.benefit_data?.map((item, index) => (
|
||||
@@ -321,29 +327,33 @@ export default function Detail() {
|
||||
{item.benefit?.description}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
{
|
||||
!isReversal ? (
|
||||
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
|
||||
<MoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => {
|
||||
setDialogEditBenefit(true)
|
||||
setIdBenefitData(item.id)
|
||||
setBenefitConfigurationData(item)
|
||||
}}
|
||||
>
|
||||
<EditOutlined />
|
||||
Edit
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => {
|
||||
setIdBenefitData(item.id)
|
||||
setDialogDeleteBenefit(true)
|
||||
}}
|
||||
>
|
||||
<Delete color='error'/>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
</Grid>
|
||||
) : null
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -619,13 +629,16 @@ export default function Detail() {
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Files </Typography>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">Files</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
{ !isReversal ? (
|
||||
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
|
||||
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
|
||||
setDialogUploadFileLog(true)
|
||||
}} >
|
||||
<Typography variant="button" display="block">Files</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
) : null }
|
||||
|
||||
</Stack>
|
||||
{requestLog?.files?.map((documentType, index) => (
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
|
||||
@@ -638,14 +651,17 @@ export default function Detail() {
|
||||
<Typography variant="body2" gutterBottom>{documentType.original_name ? documentType.original_name : '-'}</Typography>
|
||||
</a>
|
||||
</Stack>
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
{ !isReversal ? (
|
||||
<Stack direction="column" spacing={2}>
|
||||
<IconButton onClick={() => {
|
||||
setDialogDeleteFileLog(true)
|
||||
setPathFile(documentType.path)
|
||||
}} aria-label="delete" size="small" sx={{ marginLeft: 'auto' }}>
|
||||
<Delete color='error' fontSize="small" />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
) : null }
|
||||
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
|
||||
@@ -456,7 +456,7 @@ export default function Router() {
|
||||
element: <ClaimsCreate />,
|
||||
},
|
||||
{
|
||||
path: 'claims/detail/:id',
|
||||
path: 'claims/detail/:id/:id_claim',
|
||||
element: <ClaimsDetail />,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -50,5 +50,14 @@
|
||||
"txtNew" : "New",
|
||||
"txtBeforeThat" : "Before that",
|
||||
"txtDischargeDate" : "Discharge Date",
|
||||
"txtPatner" : "Patner"
|
||||
"txtPatner" : "Patner",
|
||||
"txtSelected": "Selected",
|
||||
"txtConfirmation": "Confirmation",
|
||||
"txtReason": "Reason Decline",
|
||||
"txtCancel": "Cancel",
|
||||
"txtDecline": "Decline",
|
||||
"txtApprove": "Approve",
|
||||
"txtDialogConfirmation": "Are you sure you want to proceed with this action?",
|
||||
"txtStartDate": "Start Date",
|
||||
"txtEndDate": "End Date"
|
||||
}
|
||||
|
||||
@@ -50,5 +50,14 @@
|
||||
"txtNew" : "Baru",
|
||||
"txtBeforeThat" : "Sebelum",
|
||||
"txtDischargeDate" : "Tanggal Keluar",
|
||||
"txtPatner" : "Rekanan"
|
||||
"txtPatner" : "Rekanan",
|
||||
"txtSelected": "Terpilih",
|
||||
"txtConfirmation": "Konfirmasi",
|
||||
"txtReason": "Alasan Penolakan",
|
||||
"txtCancel": "Batal",
|
||||
"txtDecline": "Tolak",
|
||||
"txtApprove": "Terima",
|
||||
"txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?",
|
||||
"txtStartDate": "Tanggal Mulai",
|
||||
"txtEndDate": "Tanggal Akhir"
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
TableSortLabel,
|
||||
Box,
|
||||
Card,
|
||||
Checkbox,
|
||||
Grid,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
@@ -25,6 +26,10 @@ import {
|
||||
linearProgressClasses,
|
||||
} from '@mui/material';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
|
||||
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
|
||||
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
/* ---------------------------------- axios --------------------------------- */
|
||||
import axios from '../utils/axios';
|
||||
/* ---------------------------------- react --------------------------------- */
|
||||
@@ -43,6 +48,8 @@ import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '
|
||||
import { InputAdornment } from '@mui/material';
|
||||
import GetAppIcon from '@mui/icons-material/GetApp';
|
||||
import { LanguageContext } from '@/contexts/LanguageContext';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
|
||||
/* --------------------------------- styled --------------------------------- */
|
||||
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
|
||||
@@ -71,6 +78,7 @@ export default function Table<T>({
|
||||
filterEndDate,
|
||||
searchs,
|
||||
exportReport,
|
||||
selected,
|
||||
}: TableListProps<T>) {
|
||||
/* ------------------------------- handle sort ------------------------------ */
|
||||
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
|
||||
@@ -98,34 +106,73 @@ export default function Table<T>({
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{headCells &&
|
||||
headCells.map((headCell, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||
// @ts-ignore
|
||||
align={headCell.align}
|
||||
sx={{ padding: 2 }}
|
||||
width={headCell.width ? headCell.width : 'auto'}
|
||||
>
|
||||
{headCell.isSort ? (
|
||||
<TableSortLabel
|
||||
active={orders?.orderBy === headCell.id}
|
||||
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orders?.orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
headCell.label
|
||||
)}
|
||||
{selected.useSelected && selected.selectedRows.length > 0 ? (
|
||||
<>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1', }} align="left" colSpan={selected.totRows} sx={{ padding: 2 }}>
|
||||
<Grid container alignItems="center" justifyContent="space-between">
|
||||
<Grid item>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
|
||||
<Typography variant='subtitle2'>
|
||||
{selected.selectedRows.length > 0 ? selected.selectedRows.length : '0'} {localeData.txtSelected}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Stack direction="row" spacing={2}>
|
||||
{selected.useDecline ? (
|
||||
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('decline');}}>
|
||||
<Typography variant='subtitle2'>{selected.txtDecline}</Typography>
|
||||
</Button>
|
||||
):''}
|
||||
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('approve');}}>
|
||||
<Typography variant='subtitle2'>{selected.txtApprove}</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
):(
|
||||
<>
|
||||
{selected.useSelected ? (
|
||||
<TableCell>
|
||||
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
|
||||
</TableCell>
|
||||
):''}
|
||||
{headCells &&
|
||||
headCells.map((headCell, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||
// @ts-ignore
|
||||
align={headCell.align}
|
||||
sx={{ padding: 2 }}
|
||||
width={headCell.width ? headCell.width : 'auto'}
|
||||
>
|
||||
{headCell.isSort ? (
|
||||
<TableSortLabel
|
||||
active={orders?.orderBy === headCell.id}
|
||||
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orders?.orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
headCell.label
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
@@ -162,7 +209,6 @@ export default function Table<T>({
|
||||
params.setAppliedParams(parameters);
|
||||
};
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
return (
|
||||
// <Card>
|
||||
<Grid container>
|
||||
@@ -255,7 +301,7 @@ export default function Table<T>({
|
||||
{/* Start date */}
|
||||
{filterStartDate && filterStartDate.useFilter ? (
|
||||
<Grid item xs={12} lg={2} xl={2}>
|
||||
<form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
|
||||
{/* <form onChange={(event) => filterStartDate.handleStartDateChange(event)}>
|
||||
<TextField
|
||||
id="date-input"
|
||||
type="date"
|
||||
@@ -267,7 +313,18 @@ export default function Table<T>({
|
||||
shrink: true,
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</form> */}
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DatePicker
|
||||
label={localeData.txtStartDate}
|
||||
value={filterStartDate.startDate}
|
||||
onChange={(newValue:any) => {
|
||||
filterStartDate.setStartDate( (newValue));
|
||||
}}
|
||||
inputFormat="dd-MM-yyyy"
|
||||
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} required/>}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Grid>
|
||||
) : null }
|
||||
|
||||
@@ -275,7 +332,7 @@ export default function Table<T>({
|
||||
|
||||
{filterEndDate && filterEndDate.useFilter ? (
|
||||
<Grid item xs={12} lg={2} xl={2}>
|
||||
<form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
|
||||
{/* <form onChange={(event) => filterEndDate.handleEndDateChange(event)}>
|
||||
<TextField
|
||||
id="date-input"
|
||||
type="date"
|
||||
@@ -287,7 +344,18 @@ export default function Table<T>({
|
||||
shrink: true,
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</form> */}
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DatePicker
|
||||
label={localeData.txtEndDate}
|
||||
value={filterEndDate.endDate}
|
||||
onChange={(newValue:any) => {
|
||||
filterEndDate.setEndDate( (newValue));
|
||||
}}
|
||||
inputFormat="dd-MM-yyyy"
|
||||
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} required/>}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Grid>
|
||||
) : null }
|
||||
|
||||
@@ -349,6 +417,20 @@ export default function Table<T>({
|
||||
) : rows && rows.length >= 1 ? (
|
||||
rows.map((row, rowIndex) => (
|
||||
<TableRow key={rowIndex}>
|
||||
{!selected.useSelected ? (
|
||||
''
|
||||
): (selected.useSelected && row.check_status === 'approved' && !row.check_claim ? (
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selected.selectedRows.includes(row.id)}
|
||||
onChange={() => selected.handleCheckboxChange(row.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
):(
|
||||
<TableCell>
|
||||
|
||||
</TableCell>
|
||||
))}
|
||||
{headCells &&
|
||||
//@ts-ignore
|
||||
headCells.map((head, headIndex) => (
|
||||
|
||||
@@ -118,6 +118,58 @@ export default function TableList() {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// ----------------------------------------- handle selected ---------------------
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [dataTableData, setDataTableData] = useState<any>();
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData?.data
|
||||
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
|
||||
.map((row: { id: any }) => row.id);
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (id: any) => {
|
||||
setSelectedRows(prevSelectedRows => {
|
||||
const isSelected = prevSelectedRows.includes(id);
|
||||
if (isSelected) {
|
||||
return prevSelectedRows.filter(rowId => rowId !== id);
|
||||
} else {
|
||||
return [...prevSelectedRows, id];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
const [valDialog, setValDialog] = useState('');
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
|
||||
setReasonDecline(event.target.value);
|
||||
};
|
||||
|
||||
const selected = {
|
||||
useSelected: false,
|
||||
selectAll: selectAll,
|
||||
handleSelectAll: handleSelectAll,
|
||||
selectedRows: selectedRows,
|
||||
handleCheckboxChange : handleCheckboxChange,
|
||||
totRows: 9,
|
||||
useDecline: false,
|
||||
txtDecline: 'Decline',
|
||||
useApprove:true,
|
||||
txtApprove: 'Submit Claim',
|
||||
setOpenDialogSubmit: setOpenDialogSubmit,
|
||||
setValDialog: setValDialog
|
||||
};
|
||||
|
||||
/* ------------------------------ handle search ----------------------------- */
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
@@ -252,12 +304,12 @@ export default function TableList() {
|
||||
label: localeData.txtStatus,
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
align: 'right',
|
||||
label: '',
|
||||
isSort: false,
|
||||
},
|
||||
// {
|
||||
// id: 'action',
|
||||
// align: 'right',
|
||||
// label: '',
|
||||
// isSort: false,
|
||||
// },
|
||||
];
|
||||
|
||||
|
||||
@@ -280,6 +332,7 @@ export default function TableList() {
|
||||
const response = await axios.get(`/get-claim-requests`, {
|
||||
params: { ...parameters, type: 'final-log' },
|
||||
});
|
||||
setDataTableData(response.data);
|
||||
setData(
|
||||
response.data.data.map((obj: any) => ({
|
||||
...obj,
|
||||
@@ -310,15 +363,15 @@ export default function TableList() {
|
||||
{obj.submission_date ? fDateSuffix(obj.submission_date) : ''}
|
||||
</Label>
|
||||
,
|
||||
action:
|
||||
<TableMoreMenu actions={
|
||||
<>
|
||||
<MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}>
|
||||
<Iconify icon="eva:eye-fill" />
|
||||
View
|
||||
</MenuItem>
|
||||
</>
|
||||
} />
|
||||
// action:
|
||||
// <TableMoreMenu actions={
|
||||
// <>
|
||||
// <MenuItem onClick={() => navigate ('/claim/detail/'+obj.claim_request_id)}>
|
||||
// <Iconify icon="eva:eye-fill" />
|
||||
// View
|
||||
// </MenuItem>
|
||||
// </>
|
||||
// } />
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -357,6 +410,7 @@ export default function TableList() {
|
||||
params={params}
|
||||
searchs={searchs}
|
||||
filterStatus={filterStatus}
|
||||
selected={selected}
|
||||
// filterStartDate={filterStartDate}
|
||||
// filterEndDate={filterEndDate}
|
||||
/>
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function DialogClaimSubmit({ member, getData, onClose, handleSubm
|
||||
<Card sx={{ p: 1, background: '#f4f6f8'}}>
|
||||
<Stack direction="row">
|
||||
<Avatar
|
||||
src="https://minimal-assets-api.vercel.app/assets/images/avatars/avatar_5.jpg"
|
||||
src=""
|
||||
alt={member?.full_name ?? ''}
|
||||
sx={{ marginTop: 1, width: 48, height: 48 }}
|
||||
/>
|
||||
@@ -143,7 +143,7 @@ export default function DialogClaimSubmit({ member, getData, onClose, handleSubm
|
||||
style={{ display: 'none' }}
|
||||
multiple
|
||||
onChange={handleKondisiInputChange}
|
||||
accept="application/pdf"
|
||||
accept="application/pdf, image/*"
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Stack>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* ---------------------------------- @mui ---------------------------------- */
|
||||
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
|
||||
import { Stack, Button, Checkbox, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box, IconButton, TextField } from '@mui/material';
|
||||
/* ---------------------------------- axios --------------------------------- */
|
||||
// import axios from 'axios';
|
||||
import axios from '../../utils/axios';
|
||||
import { styled } from '@mui/material/styles';
|
||||
/* ---------------------------------- react --------------------------------- */
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { SetStateAction, useContext, useEffect, useState } from 'react';
|
||||
|
||||
/* -------------------------------- component ------------------------------- */
|
||||
import Iconify from '../../components/Iconify';
|
||||
@@ -30,6 +30,8 @@ import MuiDialog from '@/components/MuiDialog';
|
||||
import DialogMember from './DialogMember';
|
||||
import DialogClaimSubmit from './DialogClaimSubmit';
|
||||
import { fPostFormat } from '@/utils/formatTime';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
export default function TableListFinalLog() {
|
||||
const navigate = useNavigate();
|
||||
@@ -68,6 +70,7 @@ export default function TableListFinalLog() {
|
||||
return `${day}${month}${year}`;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* setting up for the table */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -128,6 +131,85 @@ export default function TableListFinalLog() {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// ----------------------------------------- handle selected ---------------------
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [dataTableData, setDataTableData] = useState<any>();
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData?.data
|
||||
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
|
||||
.map((row: { id: any }) => row.id);
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (id: any) => {
|
||||
setSelectedRows(prevSelectedRows => {
|
||||
const isSelected = prevSelectedRows.includes(id);
|
||||
if (isSelected) {
|
||||
return prevSelectedRows.filter(rowId => rowId !== id);
|
||||
} else {
|
||||
return [...prevSelectedRows, id];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
const [valDialog, setValDialog] = useState('');
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
|
||||
setReasonDecline(event.target.value);
|
||||
};
|
||||
|
||||
const selected = {
|
||||
useSelected: true,
|
||||
selectAll: selectAll,
|
||||
handleSelectAll: handleSelectAll,
|
||||
selectedRows: selectedRows,
|
||||
handleCheckboxChange : handleCheckboxChange,
|
||||
totRows: 9,
|
||||
useDecline: false,
|
||||
txtDecline: 'Decline',
|
||||
useApprove:true,
|
||||
txtApprove: 'Submit Claim',
|
||||
setOpenDialogSubmit: setOpenDialogSubmit,
|
||||
setValDialog: setValDialog
|
||||
};
|
||||
const handleSubmitData = () => {
|
||||
//approve or decline
|
||||
// if (!reasonDecline && valDialog == 'decline') {
|
||||
// enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
|
||||
// return false;
|
||||
// }
|
||||
axios.post('/submit-claims', {
|
||||
selectedRows: selectedRows
|
||||
})
|
||||
.then((response) => {
|
||||
if(response.data.meta.code === 200)
|
||||
{
|
||||
setOpenDialogSubmit(false);
|
||||
enqueueSnackbar(response.data.meta.message, {variant : "success"});
|
||||
}
|
||||
else{
|
||||
setOpenDialogSubmit(false);
|
||||
enqueueSnackbar(response.data.meta.message, {variant : "error"});
|
||||
}
|
||||
getData();
|
||||
setSelectedRows([]);
|
||||
})
|
||||
.catch(({response}) => {
|
||||
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
|
||||
})
|
||||
.then(() => {
|
||||
});
|
||||
};
|
||||
/* ------------------------------ handle search ----------------------------- */
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
@@ -335,11 +417,14 @@ export default function TableListFinalLog() {
|
||||
params: { ...parameters, search:searchText, order: order,
|
||||
orderBy: orderBy, status:statusValue, type: 'final-log' },
|
||||
});
|
||||
setDataTableData(response.data);
|
||||
setData(
|
||||
response.data.data.map((obj: any) => ({
|
||||
...obj,
|
||||
response.data.data.map((obj: any) => ({
|
||||
...obj,
|
||||
provider:formatTitleCase(obj.provider),
|
||||
full_name:formatTitleCase(obj.full_name),
|
||||
check_status: obj.status,
|
||||
check_claim: obj.check_claim,
|
||||
status:
|
||||
obj.status === 'requested' ? (
|
||||
<Label color='primary'>
|
||||
@@ -445,6 +530,7 @@ export default function TableListFinalLog() {
|
||||
params={params}
|
||||
searchs={searchs}
|
||||
filterStatus={filterStatus}
|
||||
selected={selected}
|
||||
// filterStartDate={filterStartDate}
|
||||
// filterEndDate={filterEndDate}
|
||||
/>
|
||||
@@ -466,7 +552,6 @@ export default function TableListFinalLog() {
|
||||
member={dataViewClaimSubmit}
|
||||
getData={getData}
|
||||
onClose={(data:any, getData:any) => {
|
||||
console.log('Data returned:', data);
|
||||
getData();
|
||||
setOpenDialogClaimSubmit(false);
|
||||
}}
|
||||
@@ -475,7 +560,43 @@ export default function TableListFinalLog() {
|
||||
/>
|
||||
}
|
||||
maxWidth="sm"
|
||||
/>
|
||||
/>
|
||||
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
|
||||
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<Stack direction="row" alignItems='center' spacing={1}>
|
||||
<Typography variant="h6">{localeData.txtConfirmation}</Typography>
|
||||
</Stack>
|
||||
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
||||
<Stack spacing={2} padding={2}>
|
||||
<Typography variant='body1'>{localeData.txtDialogConfirmation}</Typography>
|
||||
{valDialog == "decline" ? (
|
||||
<Stack direction='row' spacing={2} marginTop={2}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label={localeData.txtReason}
|
||||
multiline
|
||||
rows={4}
|
||||
defaultValue=""
|
||||
onChange={handleReasonDeclineChange}
|
||||
variant="outlined"
|
||||
sx={{width:'100%'}}
|
||||
/>
|
||||
</Stack>
|
||||
): ''}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>{localeData.txtCancel}</Button>
|
||||
<Button sx={{backgroundColor: (valDialog === 'decline' ? '' : '#19BBBB'), color: (valDialog === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(valDialog === 'decline' ? 'outlined' : 'contained')}>{(valDialog === "decline" ? localeData.txtDeclaine : 'Submit')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from
|
||||
import axios from '../../utils/axios';
|
||||
import { styled } from '@mui/material/styles';
|
||||
/* ---------------------------------- react --------------------------------- */
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useContext, useEffect, useState, SetStateAction } from 'react';
|
||||
|
||||
/* -------------------------------- component ------------------------------- */
|
||||
import Iconify from '../../components/Iconify';
|
||||
@@ -132,6 +132,58 @@ export default function TableList() {
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// ----------------------------------------- handle selected ---------------------
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [dataTableData, setDataTableData] = useState<any>();
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData?.data
|
||||
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
|
||||
.map((row: { id: any }) => row.id);
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (id: any) => {
|
||||
setSelectedRows(prevSelectedRows => {
|
||||
const isSelected = prevSelectedRows.includes(id);
|
||||
if (isSelected) {
|
||||
return prevSelectedRows.filter(rowId => rowId !== id);
|
||||
} else {
|
||||
return [...prevSelectedRows, id];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
const [valDialog, setValDialog] = useState('');
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
|
||||
setReasonDecline(event.target.value);
|
||||
};
|
||||
|
||||
const selected = {
|
||||
useSelected: false,
|
||||
selectAll: selectAll,
|
||||
handleSelectAll: handleSelectAll,
|
||||
selectedRows: selectedRows,
|
||||
handleCheckboxChange : handleCheckboxChange,
|
||||
totRows: 9,
|
||||
useDecline: false,
|
||||
txtDecline: 'Decline',
|
||||
useApprove:true,
|
||||
txtApprove: 'Submit Claim',
|
||||
setOpenDialogSubmit: setOpenDialogSubmit,
|
||||
setValDialog: setValDialog
|
||||
};
|
||||
|
||||
/* ------------------------------ handle search ----------------------------- */
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
@@ -187,7 +239,7 @@ export default function TableList() {
|
||||
};
|
||||
|
||||
// handle start date
|
||||
const [startDateValue, setStartDateValue] = useState('');
|
||||
const [startDateValue, setStartDateValue] = useState(null);
|
||||
|
||||
const handleStartDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -211,7 +263,7 @@ export default function TableList() {
|
||||
};
|
||||
|
||||
// handle end date
|
||||
const [endDateValue, setEndDateValue] = useState('');
|
||||
const [endDateValue, setEndDateValue] = useState(null);
|
||||
|
||||
const handleEndDateChanges = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -328,6 +380,7 @@ export default function TableList() {
|
||||
params: { ...parameters, search:searchText, order: order,
|
||||
orderBy: orderBy, status:statusValue, type: 'request-log' },
|
||||
});
|
||||
setDataTableData(response.data);
|
||||
setData(
|
||||
response.data.data.map((obj: any) => ({
|
||||
...obj,
|
||||
@@ -440,8 +493,9 @@ export default function TableList() {
|
||||
params={params}
|
||||
searchs={searchs}
|
||||
filterStatus={filterStatus}
|
||||
// filterStartDate={filterStartDate}
|
||||
// filterEndDate={filterEndDate}
|
||||
selected={selected}
|
||||
//filterStartDate={filterStartDate}
|
||||
//filterEndDate={filterEndDate}
|
||||
/>
|
||||
<MuiDialog
|
||||
title={{name: nameMember}}
|
||||
|
||||
BIN
public/files/Report-Data-Claim-Request.xlsx
Normal file
BIN
public/files/Report-Data-Claim-Request.xlsx
Normal file
Binary file not shown.
BIN
public/files/Template - Claim - Management.xlsx
Normal file
BIN
public/files/Template - Claim - Management.xlsx
Normal file
Binary file not shown.
BIN
public/files/Template Claim Request.xlsx
Normal file
BIN
public/files/Template Claim Request.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user