Merge branch 'feature/report-primayan'

This commit is contained in:
Iqbal
2026-05-11 15:25:02 +07:00
8 changed files with 1067 additions and 46 deletions

View File

@@ -0,0 +1,502 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Models\Corporate;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
class PrimayanMedicareController extends Controller
{
/**
* List transactions for Primayan Medicare (corporate code: PRAY)
*/
public function index(Request $request)
{
$limit = $request->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('request_logs.code', 'like', "%" . $search . "%")
->orWhere('claims.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',
'request_logs.log_type',
'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('request_logs.code', 'like', "%" . $search . "%")
->orWhere('claims.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',
'request_logs.log_type',
'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);
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$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',
'Tipe LOG',
'Provider',
'Request Date',
'Amount Incurred',
'Amount Approved',
'Amount Not Approved',
'Excess Paid',
'Status',
];
$sheet->fromArray($headers, null, 'A1');
$sheet->getStyle('A1:R1')->applyFromArray([
'font' => [
'bold' => true,
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
]
]);
$rowIndex = 2;
$no = 1;
$logTypeMap = [
'reference' => 'Rujukan',
'prescription' => 'Resep',
'consultation' => 'Konsultasi',
];
foreach ($results as $item) {
$logTypeName = $logTypeMap[$item->log_type] ?? $item->log_type ?? '-';
$rowData = [
$no++,
($item->claim_code && $item->log_code)
? $item->claim_code . ' / ' . $item->log_code
: ($item->claim_code ?? $item->log_code ?? $item->request_code ?? '-'),
$item->member_name ?? '-',
$item->member_number ?? '-',
$item->nik ?? '-',
$item->birth_date ?? '-',
$item->gender ?? '-',
$item->plan_code ?? '-',
$item->benefit_name ?? $item->benefit_desc ?? '-',
$item->service_code ?? '-',
$logTypeName,
$item->provider_name ?? '-',
$item->request_date ?? '-',
(int)($item->amount_incurred ?? 0),
(int)($item->amount_approved ?? 0),
(int)($item->amount_not_approved ?? 0),
(int)($item->excess_paid ?? 0),
$item->status ?? '-',
];
$sheet->fromArray($rowData, null, 'A' . $rowIndex);
$rowIndex++;
}
foreach (range('A', 'R') as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
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:B1');
$sheet->getStyle('A1')->applyFromArray([
'font' => [
'bold' => true,
],
'alignment' => [
'horizontal' => 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,
]
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
]
]);
$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),
]);
}
}

View File

@@ -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();