diff --git a/Modules/HospitalPortal/Http/Controllers/Api/ProviderOnlineController.php b/Modules/HospitalPortal/Http/Controllers/Api/ProviderOnlineController.php new file mode 100644 index 00000000..192e4804 --- /dev/null +++ b/Modules/HospitalPortal/Http/Controllers/Api/ProviderOnlineController.php @@ -0,0 +1,661 @@ +validate([ + 'kodeprovider' => 'required|string', + 'username' => 'required|string', + 'password' => 'required|string', + 'status' => 'required|string', + 'header_token' => 'required|string', + 'namaprovider' => 'required|string', + ]); + + $organization = Organization::query()->firstOrCreate( + ['code' => $request->kodeprovider], + [ + 'name' => $request->namaprovider, + 'status' => 'active', + ] + ); + + if ((string) $organization->name !== (string) $request->namaprovider) { + $organization->name = $request->namaprovider; + $organization->save(); + } + + $provider = Provider::query()->firstOrNew([ + 'organization_id' => $organization->id, + 'username' => $request->username, + ]); + + $provider->password = Hash::make($request->password); + $provider->code = $request->kodeprovider; + $provider->status = $request->status; + $provider->header_token = $request->header_token; + $provider->token = $provider->token ?: Str::random(64); + $provider->save(); + + return response()->json([ + 'header-token' => $provider->header_token, + 'userid' => $provider->id, + 'usertoken' => $provider->token, + 'kodeprovider' => $organization->code, + 'namaprovider' => $organization->name, + 'errornumber' => 0, + 'messagestring' => 'Success', + ]); + } + + public function getHeaderKey(Request $request) + { + $request->validate([ + 'username' => 'required|string', + 'password' => 'required|string', + 'kodeprovider' => 'required|string', + ]); + + $organization = Organization::query()->where('code', $request->kodeprovider)->first(); + if (!$organization) { + return $this->headerError('Kode provider tidak ditemukan'); + } + + $provider = Provider::query()->firstOrNew([ + 'organization_id' => $organization->id, + 'username' => $request->username, + ]); + + if ($provider->exists) { + $isPasswordValid = Hash::check($request->password, $provider->password) || $provider->password === $request->password; + if (!$isPasswordValid) { + return $this->headerError('Username atau password tidak valid'); + } + } else { + $provider->password = Hash::make($request->password); + } + + $provider->code = $request->kodeprovider; + $provider->status = 'active'; + $provider->header_token = Str::random(64); + $provider->token = Str::random(64); + $provider->save(); + + return response()->json([ + 'header-token' => $provider->header_token, + 'userid' => $provider->id, + 'usertoken' => $provider->token, + 'kodeprovider' => $organization->code, + 'namaprovider' => $organization->name, + 'errornumber' => 0, + 'messagestring' => 'Success', + ]); + } + + public function checkEligibilitasPeserta(Request $request) + { + $request->validate([ + 'nokartu' => 'required|string', + 'kodeprovider' => 'required|string', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $member = Member::query() + ->with(['person', 'currentCorporate', 'currentPolicy', 'memberPlans.plan']) + ->where('member_id', $request->nokartu) + ->first(); + + if (!$member) { + return $this->statusError('Peserta tidak ditemukan'); + } + + $planIds = $member->memberPlans + ->pluck('plan_id') + ->filter() + ->unique() + ->values(); + + $benefits = collect(); + if ($planIds->isNotEmpty()) { + $benefits = CorporateBenefit::query() + ->with(['benefit', 'plan']) + ->whereIn('plan_id', $planIds) + ->get() + ->map(function (CorporateBenefit $corporateBenefit) { + return [ + // Keep response keys unchanged, but fill with sub-benefit data + 'kodebenefit' => optional($corporateBenefit->benefit)->code, + 'namabenefit' => optional($corporateBenefit->benefit)->description, + 'planid' => optional($corporateBenefit->plan)->code, + ]; + }) + ->filter(function (array $item) { + return !empty($item['kodebenefit']); + }) + ->values(); + } + + return response()->json([ + 'Status' => $this->okStatus(), + 'Data' => [ + 'nokartu' => $member->member_id, + 'memberid' => (string) $member->id, + 'namapeserta' => $member->full_name, + 'nomorbpjs' => $member->bpjs_id, + 'jeniskelamin' => $member->gender_code, + 'tanggallahir' => $this->isoDate($member->birth_date), + 'hubungankeluarga' => $member->relation_with_principal, + 'namaperusahaan' => optional($member->currentCorporate)->name, + 'pesertavip' => 'N', + 'namapenjamin' => optional($organization)->name, + 'nomorpolis' => optional($member->currentPolicy)->code, + 'tglmulaipolis' => $this->isoDate(optional($member->currentPolicy)->start), + 'tglberakhirpolis' => $this->isoDate(optional($member->currentPolicy)->end), + 'phone' => optional($member->person)->phone, + 'email' => $member->email, + ], + 'Benefit' => $benefits, + ]); + } + + public function createPendaftaran(Request $request) + { + $request->validate([ + 'kodeprovider' => 'required|string', + 'kodebenefit' => 'required|string', + 'statusrujukan' => 'nullable|string', + 'nomorrujukan' => 'nullable|string', + 'keterangan' => 'nullable|string', + 'nomorsep' => 'nullable|string', + 'nokartu' => 'required|string', + 'kelaskamar' => 'nullable|string', + 'cobbpjs' => 'nullable|numeric', + 'notransaksiprovider' => 'nullable|string', + 'inacbgscode' => 'nullable|string', + 'inacbgsamount' => 'nullable|numeric', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $member = Member::query() + ->with(['person', 'currentCorporate', 'currentPolicy']) + ->where('member_id', $request->nokartu) + ->first(); + if (!$member) { + return $this->statusError('Peserta tidak ditemukan'); + } + + $plan = $this->resolvePlan($member, $request->kodebenefit); + $generatedLogCode = $this->generateNextRequestLogCode($organization, $member); + + $requestLog = RequestLog::query()->create([ + 'code' => $generatedLogCode, + 'organization_id' => $organization->id, + 'member_id' => $member->id, + 'plan_id' => optional($plan)->id, + 'policy_id' => optional($member->currentPolicy)->id ?? 0, + 'payment_type' => 'cashless', + 'service_code' => $request->kodebenefit, + 'type_of_member' => 'member', + 'status' => 'approved', + 'source' => 'api', + 'keterangan' => $request->keterangan, + 'hak_kamar_pasien' => $request->kelaskamar ?? '', + 'penempatan_kamar' => $request->kelaskamar, + 'total_cob' => $request->cobbpjs, + 'nominal' => 0, + 'import_system' => 0, + 'nomor_sep' => $request->nomorsep, + 'inacbgs_code' => $request->inacbgscode, + 'inacbgs_amount' => $request->inacbgsamount, + 'no_transaksi_provider' => $request->notransaksiprovider, + 'diagnosis' => '', + 'reason' => '', + 'reason_final' => '', + 'catatan' => '', + 'nomor_rujukan' => $request->nomorrujukan, + 'status_rujukan' => $request->statusrujukan, + 'submission_date' => now(), + 'admission_date' => now(), + ]); + + $limitSubBenefit = collect(); + if ($plan) { + $limitSubBenefit = CorporateBenefit::query() + ->with('benefit') + ->where('plan_id', $plan->id) + ->get() + ->map(function (CorporateBenefit $corporateBenefit) { + return [ + 'kodesubbenefit' => optional($corporateBenefit->benefit)->code, + 'namasubbenefit' => optional($corporateBenefit->benefit)->description, + 'batasan' => (string) ($corporateBenefit->limit_amount ?? 0), + ]; + }); + } + + return response()->json([ + 'Status' => $this->okStatus(), + 'Data' => [[ + 'noklaim' => $requestLog->code, + 'namapeserta' => $member->full_name, + 'tanggallahir' => $this->isoDate($member->birth_date), + 'nokartu' => $member->member_id, + 'nopolis' => optional($member->currentPolicy)->code, + 'nobpjs' => $member->bpjs_id, + 'nosep' => $requestLog->nomor_sep, + 'nomorrujukan' => $requestLog->nomor_rujukan, + 'planid' => optional($plan)->code, + 'masapolis' => optional($member->currentPolicy)->end ? Carbon::parse($member->currentPolicy->end)->format('d/m/Y') : null, + 'namaperusahaan' => optional($member->currentCorporate)->name, + 'namapenjamin' => $organization->name, + 'tanggalmasuk' => $this->isoDate($requestLog->admission_date), + 'tanggalkeluar' => $this->isoDate($requestLog->discharge_date), + 'hakkamar' => $requestLog->penempatan_kamar, + 'hakicu' => null, + 'nosuratjaminan' => $requestLog->code, + 'namapegawai' => null, + 'namabenefit' => optional($plan)->corporate_plan_id, + 'kodediagnosa' => $requestLog->diagnosis, + 'keterangan' => $requestLog->keterangan, + 'catatanTC1' => null, + 'catatanTC2' => null, + 'catatanTC3' => null, + 'catatanTC4' => null, + 'catatanTC5' => null, + 'catatanTC6' => null, + 'catatanTC7' => null, + 'catatanTC8' => null, + 'catatanTC9' => null, + 'catatanTC10' => null, + 'statusrujukan' => $requestLog->status_rujukan, + 'statusklaim' => 0, + 'notransaksiprovider' => $requestLog->no_transaksi_provider, + 'inacbgscode' => $requestLog->inacbgs_code, + 'inacbgsamount' => (float) ($requestLog->inacbgs_amount ?? 0), + ]], + 'LimitSubBenefit' => $limitSubBenefit->values(), + ]); + } + + public function createPengesahan(Request $request) + { + $request->validate([ + 'noklaim' => 'required|string', + 'kodeprovider' => 'required|string', + 'tanggalkeluar' => 'nullable|date', + 'kodediagnosa' => 'nullable|string', + 'inacbgscode' => 'nullable|string', + 'inacbgsamount' => 'nullable|numeric', + 'daftarbiaya' => 'nullable|array', + 'daftarbiaya.*.kodesubbenefit' => 'required_with:daftarbiaya|string', + 'daftarbiaya.*.biayaaju' => 'required_with:daftarbiaya|numeric', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $requestLog = RequestLog::query() + ->with(['member.person', 'member.currentCorporate', 'member.currentPolicy', 'plan']) + ->where('code', $request->noklaim) + ->where('organization_id', $organization->id) + ->first(); + if (!$requestLog) { + return $this->statusError('Nomor klaim tidak ditemukan'); + } + + $requestLog->update([ + 'status_final_log' => 'approved', + 'final_log' => true, + 'discharge_date' => $request->tanggalkeluar, + 'diagnosis' => $request->kodediagnosa, + 'inacbgs_code' => $request->inacbgscode ?? $requestLog->inacbgs_code, + 'inacbgs_amount' => $request->inacbgsamount ?? $requestLog->inacbgs_amount, + ]); + + foreach (($request->daftarbiaya ?? []) as $biaya) { + $benefit = Benefit::query()->where('code', $biaya['kodesubbenefit'])->first(); + if (!$benefit) { + continue; + } + + RequestLogBenefit::query()->updateOrCreate( + [ + 'request_log_id' => $requestLog->id, + 'benefit_id' => $benefit->id, + ], + [ + 'amount_incurred' => $biaya['biayaaju'], + 'amount_approved' => 0, + 'amount_not_approved' => 0, + 'excess_paid' => 0, + 'keterangan' => null, + ] + ); + } + + $requestLog->load(['requestLogBenefits.benefit']); + + $biayaResponse = $requestLog->requestLogBenefits->map(function (RequestLogBenefit $requestLogBenefit) use ($requestLog) { + return [ + 'noklaim' => $requestLog->code, + 'kodesubbenefit' => optional($requestLogBenefit->benefit)->code, + 'namasubbenefit' => optional($requestLogBenefit->benefit)->description, + 'kodebenefit' => optional($requestLog->plan)->code, + 'namabenefit' => optional($requestLog->plan)->corporate_plan_id, + 'biayaaju' => (float) ($requestLogBenefit->amount_incurred ?? 0), + 'jaminanasuransi' => (float) ($requestLogBenefit->amount_approved ?? 0), + 'jaminanpeserta' => (float) ($requestLogBenefit->excess_paid ?? 0), + 'keterangan' => $requestLogBenefit->keterangan, + ]; + })->values(); + + return response()->json([ + 'Status' => $this->okStatus(), + 'Data' => [[ + 'noklaim' => $requestLog->code, + 'namapeserta' => optional($requestLog->member)->full_name, + 'tanggallahir' => $this->isoDate(optional($requestLog->member)->birth_date), + 'nokartu' => optional($requestLog->member)->member_id, + 'nopolis' => optional(optional($requestLog->member)->currentPolicy)->code, + 'nobpjs' => optional($requestLog->member)->bpjs_id, + 'nosep' => $requestLog->nomor_sep, + 'nomorrujukan' => $requestLog->nomor_rujukan, + 'planid' => optional($requestLog->plan)->code, + 'masapolis' => optional(optional($requestLog->member)->currentPolicy)->end ? Carbon::parse($requestLog->member->currentPolicy->end)->format('d/m/Y') : null, + 'namaperusahaan' => optional(optional($requestLog->member)->currentCorporate)->name, + 'namapenjamin' => $organization->name, + 'tanggalmasuk' => $this->isoDate($requestLog->admission_date), + 'tanggalkeluar' => $this->isoDate($requestLog->discharge_date), + 'hakkamar' => $requestLog->penempatan_kamar, + 'hakicu' => null, + 'nosuratjaminan' => $requestLog->code, + 'namapegawai' => null, + 'namabenefit' => optional($requestLog->plan)->corporate_plan_id, + 'kodediagnosa' => $requestLog->diagnosis, + 'keterangan' => $requestLog->keterangan, + 'catatanTC1' => null, + 'catatanTC2' => null, + 'catatanTC3' => null, + 'catatanTC4' => null, + 'catatanTC5' => null, + 'catatanTC6' => null, + 'catatanTC7' => null, + 'catatanTC8' => null, + 'catatanTC9' => null, + 'catatanTC10' => null, + 'statusrujukan' => $requestLog->status_rujukan, + 'statusklaim' => 0, + 'notransaksiprovider' => $requestLog->no_transaksi_provider, + 'inacbgscode' => $requestLog->inacbgs_code, + 'inacbgsamount' => (float) ($requestLog->inacbgs_amount ?? 0), + ]], + 'Biaya' => $biayaResponse, + ]); + } + + public function getRincianBiayaKlaim(Request $request) + { + $request->validate([ + 'noklaim' => 'required|string', + 'kodeprovider' => 'required|string', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $requestLog = RequestLog::query() + ->with(['plan', 'requestLogBenefits.benefit']) + ->where('code', $request->noklaim) + ->where('organization_id', $organization->id) + ->first(); + if (!$requestLog) { + return $this->statusError('Nomor klaim tidak ditemukan'); + } + + $biaya = $requestLog->requestLogBenefits->map(function (RequestLogBenefit $requestLogBenefit) use ($requestLog) { + return [ + 'noklaim' => $requestLog->code, + 'kodesubbenefit' => optional($requestLogBenefit->benefit)->code, + 'namasubbenefit' => optional($requestLogBenefit->benefit)->description, + 'kodebenefit' => optional($requestLog->plan)->code, + 'namabenefit' => optional($requestLog->plan)->corporate_plan_id, + 'biayaaju' => (float) ($requestLogBenefit->amount_incurred ?? 0), + 'jaminanasuransi' => (float) ($requestLogBenefit->amount_approved ?? 0), + 'jaminanpeserta' => (float) ($requestLogBenefit->excess_paid ?? 0), + 'keterangan' => $requestLogBenefit->keterangan, + ]; + })->values(); + + return response()->json([ + 'Status' => $this->okStatus(), + 'Data' => [ + 'noklaim' => $requestLog->code, + 'kodeprovider' => $organization->code, + 'nosep' => $requestLog->nomor_sep, + 'keterangan' => $requestLog->keterangan, + 'statusklaim' => $requestLog->status, + ], + 'Biaya' => $biaya, + ]); + } + + public function downloadStrukPendaftaran(Request $request) + { + $request->validate([ + 'noklaim' => 'required|string', + 'kodeprovider' => 'required|string', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $requestLog = RequestLog::query()->where('code', $request->noklaim)->where('organization_id', $organization->id)->first(); + if (!$requestLog) { + return $this->statusError('Nomor klaim tidak ditemukan'); + } + + try { + $requestLogController = new RequestLogController(); + return $requestLogController->downlodLog($requestLog->id); + } catch (Throwable $e) { + $fallbackUrl = url('api/v1/hospitalportal/download-log/' . $requestLog->id); + return $this->statusError( + 'Gagal generate PDF struk pendaftaran: ' . $e->getMessage() . '. request_log_id=' . $requestLog->id . ', fallback_url=' . $fallbackUrl, + 500 + ); + } + } + + public function downloadStrukPengesahan(Request $request) + { + $request->validate([ + 'noklaim' => 'required|string', + 'kodeprovider' => 'required|string', + 'p_user_no' => 'required', + 'p_token' => 'required|string', + ]); + + [$provider, $organization, $authError] = $this->resolveProvider($request->kodeprovider, $request->p_user_no, $request->p_token); + if ($authError) { + return $authError; + } + + $requestLog = RequestLog::query()->where('code', $request->noklaim)->where('organization_id', $organization->id)->first(); + if (!$requestLog) { + return $this->statusError('Nomor klaim tidak ditemukan'); + } + + try { + $requestLogController = new RequestLogController(); + return $requestLogController->downlodFinalLog($requestLog->id); + } catch (Throwable $e) { + $fallbackUrl = url('api/v1/hospitalportal/download-final-log/' . $requestLog->id); + return $this->statusError( + 'Gagal generate PDF struk pengesahan: ' . $e->getMessage() . '. request_log_id=' . $requestLog->id . ', fallback_url=' . $fallbackUrl, + 500 + ); + } + } + + private function resolveProvider(string $kodeProvider, $userId, string $token): array + { + $organization = Organization::query()->where('code', $kodeProvider)->first(); + if (!$organization) { + return [null, null, $this->statusError('Kode provider tidak ditemukan')]; + } + + $provider = Provider::query() + ->where('id', $userId) + ->where('organization_id', $organization->id) + ->where('code', $kodeProvider) + ->where('status', 'active') + ->first(); + if (!$provider) { + return [null, null, $this->statusError('User provider tidak ditemukan')]; + } + + if ((string) $provider->token !== (string) $token) { + return [null, null, $this->statusError('Token tidak valid')]; + } + + return [$provider, $organization, null]; + } + + private function resolvePlan(Member $member, string $serviceCode) + { + $memberPlan = $member->memberPlans()->with('plan') + ->where('status', 'active') + ->orderByDesc('id') + ->get() + ->first(function (MemberPlan $item) use ($serviceCode) { + return optional($item->plan)->service_code === $serviceCode; + }); + + if ($memberPlan && $memberPlan->plan) { + return $memberPlan->plan; + } + + return optional($member->memberPlans()->with('plan')->orderByDesc('id')->first())->plan; + } + + private function okStatus(): array + { + return [ + 'errornumber' => 0, + 'messagestring' => 'Success', + ]; + } + + private function statusError(string $message, int $statusCode = 400) + { + return response()->json([ + 'Status' => [ + 'errornumber' => 1, + 'messagestring' => $message, + ], + 'Data' => null, + ], $statusCode); + } + + private function headerError(string $message, int $statusCode = 400) + { + return response()->json([ + 'header-token' => null, + 'userid' => 0, + 'usertoken' => null, + 'kodeprovider' => null, + 'namaprovider' => null, + 'errornumber' => 1, + 'messagestring' => $message, + ], $statusCode); + } + + private function isoDate($date): ?string + { + if (empty($date)) { + return null; + } + + return Carbon::parse($date)->toISOString(); + } + + private function generateNextRequestLogCode(Organization $organization, Member $member): string + { + $data = [ + 'source' => 'H', + 'provideCode' => $organization->code ?? '', + 'date' => date('ymd'), + 'policy' => optional($member->currentPolicy)->code ?? '-', + 'member_code' => $member->member_id ?? '-', + ]; + + $lastNumericCode = RequestLog::query() + ->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'); + + $nextNumber = $lastNumericCode ? ((int) $lastNumericCode + 1) : 1; + + return $this->makeRequestLogCode($nextNumber, $data); + } + + private function makeRequestLogCode(int $nextNumber, array $data): string + { + $nextNumber = max(1, $nextNumber); + + return implode('.', [ + 'LOG', + $data['source'], + $data['provideCode'], + $data['date'], + $data['policy'], + $data['member_code'], + str_pad((string) $nextNumber, 5, '0', STR_PAD_LEFT), + ]); + } +}