From 5dc04d01e24ab2f9c08c8cc1a856b510541cc41b Mon Sep 17 00:00:00 2001 From: Iqbal Date: Mon, 11 May 2026 09:10:13 +0700 Subject: [PATCH] Add Primayan Medicare report with LOG codes and aggregated amounts --- .../Api/PrimayanMedicareController.php | 465 ++++++++++++++++++ Modules/Internal/Routes/api.php | 5 + 2 files changed, 470 insertions(+) create mode 100644 Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php diff --git a/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php new file mode 100644 index 00000000..21be051b --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/PrimayanMedicareController.php @@ -0,0 +1,465 @@ +has('per_page') ? $request->input('per_page') : 10; + + $corporate = Corporate::where('code', 'PRAY')->first(); + + if (!$corporate) { + return response()->json([ + 'message' => 'Corporate Primayan Medicare (PRAY) not found', + 'data' => [] + ], 404); + } + + // Subquery to aggregate amounts from request_log_benefits + $subQueryAmount = DB::table('request_log_benefits') + ->select('request_log_id', + DB::raw('SUM(amount_incurred) as total_incurred'), + DB::raw('SUM(amount_approved) as total_approved'), + DB::raw('SUM(excess_paid) as total_excess')) + ->whereNull('deleted_at') + ->groupBy('request_log_id'); + + $query = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->leftJoin('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->leftJoin('claims', 'claim_requests.claim_id', '=', 'claims.id') + ->leftJoin('request_logs', 'claim_requests.request_log_id', '=', 'request_logs.id') + ->leftJoinSub($subQueryAmount, 'log_amounts', function ($join) { + $join->on('request_logs.id', '=', 'log_amounts.request_log_id'); + }) + ->leftJoin('plans', 'claims.plan_id', '=', 'plans.id') + ->leftJoin('benefits', 'claims.benefit_id', '=', 'benefits.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('claim_requests.deleted_at') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('members.name', 'like', "%" . $search . "%") + ->orWhere('members.member_id', 'like', "%" . $search . "%") + ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); + }); + }) + ->when($request->input('start_date'), function ($query, $start_date) { + $query->where('claim_requests.created_at', '>=', $start_date); + }) + ->when($request->input('end_date'), function ($query, $end_date) { + $query->where('claim_requests.created_at', '<=', $end_date . ' 23:59:59'); + }) + ->when($request->input('status'), function ($query, $status) { + if ($status !== 'all') { + $query->where('claim_requests.status', $status); + } + }) + ->when($request->input('provider_id'), function ($query, $provider_id) { + if ($provider_id !== 'all') { + $query->where('claim_requests.organization_id', $provider_id); + } + }) + ->select( + 'claim_requests.id', + 'claim_requests.code as request_code', + 'claim_requests.status', + 'claim_requests.created_at as request_date', + 'claim_requests.service_code', + 'claims.code as claim_code', + 'request_logs.code as log_code', + DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), + DB::raw('COALESCE(log_amounts.total_approved, claims.amount_approved, 0) as amount_approved'), + DB::raw('COALESCE(claims.amount_not_approved, 0) as amount_not_approved'), + DB::raw('COALESCE(log_amounts.total_excess, claims.excess_paid, 0) as excess_paid'), + 'claims.benefit_code', + 'claims.benefit_desc', + 'members.name as member_name', + 'members.member_id as member_number', + 'members.birth_date', + 'members.gender', + 'corporate_employees.nik', + 'corporate_employees.branch_code', + 'organizations.name as provider_name', + 'plans.code as plan_code', + 'benefits.code as benefit_code_ref' + ) + ->orderBy('claim_requests.created_at', 'desc'); + + $results = $query->paginate($limit)->appends($request->all()); + + // Get Providers for filter + $providers = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->join('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('claim_requests.deleted_at') + ->select('organizations.id', 'organizations.name') + ->groupBy('organizations.id', 'organizations.name') + ->get(); + + return Helper::responseJson([ + 'results' => Helper::paginateResources($results), + 'providers' => $providers, + ]); + } + + /** + * Export transactions to Excel + */ + public function export(Request $request) + { + $corporate = Corporate::where('code', 'PRAY')->first(); + + if (!$corporate) { + return response()->json([ + 'message' => 'Corporate Primayan Medicare (PRAY) not found', + ], 404); + } + + $exportType = $request->input('export_type', 'list'); + + if ($exportType === 'summary') { + return $this->exportSummary($corporate); + } + + // Subquery to aggregate amounts from request_log_benefits + $subQueryAmount = DB::table('request_log_benefits') + ->select('request_log_id', + DB::raw('SUM(amount_incurred) as total_incurred'), + DB::raw('SUM(amount_approved) as total_approved'), + DB::raw('SUM(excess_paid) as total_excess')) + ->whereNull('deleted_at') + ->groupBy('request_log_id'); + + $results = DB::table('claim_requests') + ->join('members', 'claim_requests.member_id', '=', 'members.id') + ->join('corporate_employees', 'corporate_employees.member_id', '=', 'members.id') + ->leftJoin('organizations', 'claim_requests.organization_id', '=', 'organizations.id') + ->leftJoin('claims', 'claim_requests.claim_id', '=', 'claims.id') + ->leftJoin('request_logs', 'claim_requests.request_log_id', '=', 'request_logs.id') + ->leftJoinSub($subQueryAmount, 'log_amounts', function ($join) { + $join->on('request_logs.id', '=', 'log_amounts.request_log_id'); + }) + ->leftJoin('plans', 'claims.plan_id', '=', 'plans.id') + ->leftJoin('benefits', 'claims.benefit_id', '=', 'benefits.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('claim_requests.deleted_at') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('members.name', 'like', "%" . $search . "%") + ->orWhere('members.member_id', 'like', "%" . $search . "%") + ->orWhere('claim_requests.code', 'like', "%" . $search . "%") + ->orWhere('corporate_employees.nik', 'like', "%" . $search . "%"); + }); + }) + ->when($request->input('start_date'), function ($query, $start_date) { + $query->where('claim_requests.created_at', '>=', $start_date); + }) + ->when($request->input('end_date'), function ($query, $end_date) { + $query->where('claim_requests.created_at', '<=', $end_date . ' 23:59:59'); + }) + ->when($request->input('status'), function ($query, $status) { + if ($status !== 'all') { + $query->where('claim_requests.status', $status); + } + }) + ->when($request->input('provider_id'), function ($query, $provider_id) { + if ($provider_id !== 'all') { + $query->where('claim_requests.organization_id', $provider_id); + } + }) + ->select( + 'claim_requests.id', + 'claim_requests.code as request_code', + 'claim_requests.status', + 'claim_requests.created_at as request_date', + 'claim_requests.service_code', + 'claims.code as claim_code', + 'request_logs.code as log_code', + DB::raw('COALESCE(log_amounts.total_incurred, claims.amount_incurred, 0) as amount_incurred'), + DB::raw('COALESCE(log_amounts.total_approved, claims.amount_approved, 0) as amount_approved'), + DB::raw('COALESCE(claims.amount_not_approved, 0) as amount_not_approved'), + DB::raw('COALESCE(log_amounts.total_excess, claims.excess_paid, 0) as excess_paid'), + 'claims.benefit_code', + 'claims.benefit_desc', + 'members.name as member_name', + 'members.member_id as member_number', + 'members.birth_date', + 'members.gender', + 'corporate_employees.nik', + 'corporate_employees.branch_code', + 'organizations.name as provider_name', + 'plans.code as plan_code', + 'benefits.code as benefit_code_ref' + ) + ->orderBy('claim_requests.created_at', 'desc') + ->get(); + + $fileName = 'PrimayanMedicare-List-' . now()->format('Y-m-d_H-i-s') . '.xlsx'; + $filePath = storage_path('app/public/temp/' . $fileName); + + if (!is_dir(storage_path('app/public/temp'))) { + mkdir(storage_path('app/public/temp'), 0755, true); + } + + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile($filePath); + + $serviceCodeMap = [ + 'OP' => 'Outpatient', + 'IP' => 'Inpatient', + 'DE' => 'Dental', + 'MA' => 'Maternal', + 'GL' => 'Optical', + 'MCU' => 'Medical Check Up', + 'MEDIVAC' => 'Medical Emergency Evacuation', + ]; + + $headers = [ + 'No', + 'Code Claim / Code LOG', + 'Member Name', + 'Member Number', + 'NIK', + 'Birth Date', + 'Gender', + 'Plan', + 'Benefit', + 'Service', + 'Provider', + 'Request Date', + 'Amount Incurred', + 'Amount Approved', + 'Amount Not Approved', + 'Excess Paid', + 'Status', + ]; + $headerRow = WriterEntityFactory::createRowFromArray($headers); + $writer->addRow($headerRow); + + $no = 1; + foreach ($results as $row) { + $serviceCode = $row->service_code ?? '-'; + $serviceName = $serviceCodeMap[$serviceCode] ?? $serviceCode; + + $rowData = [ + $no++, + ($row->claim_code && $row->log_code) + ? $row->claim_code . ' / ' . $row->log_code + : ($row->claim_code ?? $row->log_code ?? $row->request_code ?? '-'), + $row->member_name ?? '-', + $row->member_number ?? '-', + $row->nik ?? '-', + $row->birth_date ?? '-', + $row->gender ?? '-', + $row->plan_code ?? '-', + $row->benefit_name ?? $row->benefit_desc ?? '-', + $serviceName, + $row->provider_name ?? '-', + $row->request_date ?? '-', + $row->amount_incurred ?? 0, + $row->amount_approved ?? 0, + $row->amount_not_approved ?? 0, + $row->excess_paid ?? 0, + $row->status ?? '-', + ]; + $dataRow = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($dataRow); + } + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Primayan Medicare List ' . now()->format('Y-m-d H:i:s'), + 'file_url' => Storage::disk('public')->url('temp/' . $fileName), + ]); + } + + private function exportSummary($corporate) + { + // Get all branch codes + $branches = DB::table('corporate_employees') + ->where('corporate_id', $corporate->id) + ->whereNull('deleted_at') + ->select('branch_code') + ->groupBy('branch_code') + ->get(); + + $fileName = 'PrimayanMedicare-Summary-' . now()->format('Y-m-d_H-i-s') . '.xlsx'; + $filePath = storage_path('app/public/temp/' . $fileName); + + if (!is_dir(storage_path('app/public/temp'))) { + mkdir(storage_path('app/public/temp'), 0755, true); + } + + $codeRSMap = [ + 'AI' => 'ABI', 'BB' => 'PHBB', 'BT' => 'PHBT', 'BU' => 'PHBU', + 'BW' => 'PHBW', 'CI' => 'PHPC', 'CO' => 'FABS', 'DE' => 'PHDE', + 'EV' => 'PHEV', 'HE' => 'PHHE', 'KG' => 'PHKG', 'KI' => 'KMI', + 'KR' => 'PHKA', 'KV' => 'KAVA', 'LM' => 'LMS', 'LY' => 'LYNAS', + 'MK' => 'PHMA', 'MS' => 'EYEQU', 'PF' => 'FMC', 'PS' => 'PHPK', + 'PY' => 'PHBP', 'RA' => 'PHRA', 'SB' => 'PHSB', 'SF' => 'SFI', + 'SG' => 'PHSM', 'SM' => 'SIM', 'SO' => 'PHSW', 'SS' => 'SAS', + 'TG' => 'PHTA', 'UP' => 'UKRIDA', 'WS' => 'WEST' + ]; + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + // Header Date Range (Row 1) + $dateRange = now()->startOfMonth()->format('j F') . ' - ' . now()->format('j F Y'); + $sheet->setCellValue('A1', strtoupper($dateRange)); + $sheet->mergeCells('A1:G1'); + $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + + // Table Headers (Row 3) + $headers = ['Code RS', 'Branch Code', 'Total Benefit', 'Deposit 10%', 'Pemakaian Benefit Obat & Rujukan', 'Pemakaian Telekonsultasi', 'Sisa Deposit']; + $sheet->fromArray($headers, null, 'A3'); + $sheet->getStyle('A3:G3')->applyFromArray([ + 'font' => [ + 'bold' => true, + ], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => [ + 'rgb' => 'C6E0B4' + ] + ], + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + ] + ] + ]); + + $row = 4; + foreach ($branches as $branch) { + $branchCode = $branch->branch_code ?? '-'; + $codeRS = $codeRSMap[$branchCode] ?? '-'; + + $totalDeposit = DB::table('corporate_employees') + ->join('member_plans', 'corporate_employees.member_id', '=', 'member_plans.member_id') + ->join('plans', 'member_plans.plan_id', '=', 'plans.id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code')->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->whereNull('corporate_employees.deleted_at') + ->whereNull('member_plans.deleted_at') + ->sum(DB::raw('CAST(plans.limit_rules as UNSIGNED)')); + + $deposit10 = $totalDeposit * 0.1; + + $usageMedicines = DB::table('request_log_medicines') + ->join('request_logs', 'request_log_medicines.request_log_id', '=', 'request_logs.id') + ->join('corporate_employees', 'request_logs.member_id', '=', 'corporate_employees.member_id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code')->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->whereIn('request_logs.log_type', ['prescription', 'reference']) + ->sum('request_log_medicines.price'); + + $consultationCount = DB::table('request_logs') + ->join('corporate_employees', 'request_logs.member_id', '=', 'corporate_employees.member_id') + ->where('corporate_employees.corporate_id', $corporate->id) + ->where(function($query) use ($branchCode) { + if ($branchCode === '-') { + $query->whereNull('corporate_employees.branch_code') + ->orWhere('corporate_employees.branch_code', ''); + } else { + $query->where('corporate_employees.branch_code', $branchCode); + } + }) + ->where('request_logs.log_type', 'consultation') + ->count(); + + $usageConsultation = $consultationCount * 11100; + $remainingDeposit = $deposit10 - $usageMedicines - $usageConsultation; + + $sheet->setCellValue('A' . $row, $codeRS); + $sheet->setCellValue('B' . $row, $branchCode); + $sheet->setCellValue('C' . $row, $totalDeposit); + $sheet->setCellValue('D' . $row, $deposit10); + $sheet->setCellValue('E' . $row, $usageMedicines); + $sheet->setCellValue('F' . $row, $usageConsultation); + $sheet->setCellValue('G' . $row, $remainingDeposit); + + // Number format with thousand separator + $sheet->getStyle('C' . $row . ':G' . $row)->getNumberFormat()->setFormatCode('#,##0'); + + $row++; + } + + $lastRow = $row - 1; + foreach (range('A', 'G') as $col) { + $sheet->getColumnDimension($col)->setAutoSize(true); + } + + $sheet->getStyle('A3:G' . $lastRow)->applyFromArray([ + 'borders' => [ + 'allBorders' => [ + 'borderStyle' => Border::BORDER_THIN, + 'color' => ['rgb' => '000000'] + ] + ] + ]); + + $sheet->freezePane('A4'); + + $sheet->setCellValue('E1', '=SUM(E4:E' . $lastRow . ')'); + $sheet->setCellValue('F1', '=SUM(F4:F' . $lastRow . ')'); + $sheet->getStyle('E1:F1') + ->getNumberFormat() + ->setFormatCode('#,##0'); + $sheet->getStyle('E1:F1')->applyFromArray([ + 'font' => [ + 'bold' => true, + 'size' => 12, + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_CENTER, + ] + ]); + + $writer = new Xlsx($spreadsheet); + $writer->save($filePath); + + return Helper::responseJson([ + 'file_name' => $fileName, + 'file_url' => Storage::disk('public')->url('temp/' . $fileName), + ]); + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 48d8f406..509c540e 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -62,6 +62,7 @@ use Modules\Primaya\Http\Controllers\Api\MasterController; // Report use Modules\Internal\Http\Controllers\Api\ReportLogController; +use Modules\Internal\Http\Controllers\Api\PrimayanMedicareController; /* @@ -112,6 +113,10 @@ Route::prefix('internal')->group(function () { Route::get('linksehat/rujukan', [RujukanController::class, 'index']); Route::get('linksehat/rujukan/generate-excel', [RujukanController::class, 'generateExcel']); + // Report Primayan Medicare + Route::get('primayan-medicare', [PrimayanMedicareController::class, 'index']); + Route::get('primayan-medicare/export', [PrimayanMedicareController::class, 'export']); + Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::get('/user', function (Request $request) { return $request->user();