diff --git a/Modules/Client/Http/Controllers/Api/ClaimController.php b/Modules/Client/Http/Controllers/Api/ClaimController.php index 0e30f332..f378ef81 100644 --- a/Modules/Client/Http/Controllers/Api/ClaimController.php +++ b/Modules/Client/Http/Controllers/Api/ClaimController.php @@ -77,7 +77,7 @@ class ClaimController extends Controller 'claimRequest', 'claimRequest.files', 'items', - 'items.claim_itemable' + 'items.claim_itemable', ]) ->findOrFail($id); diff --git a/Modules/Internal/Http/Controllers/Api/ClaimController.php b/Modules/Internal/Http/Controllers/Api/ClaimController.php index 065d576a..03d9ef7a 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimController.php @@ -107,7 +107,12 @@ class ClaimController extends Controller 'claimRequest', 'claimRequest.files', 'items', - 'items.claim_itemable' + 'items.claim_itemable', + 'encounters', + 'encounters.doctors', + 'encounters.primaryDiagnoses', + 'encounters.primaryDiagnoses.diagnosis', + 'encounters.healthcare' ]) ->findOrFail($id); @@ -150,6 +155,19 @@ class ClaimController extends Controller return true; } + public function updateDetails(Request $request, $id) + { + $request->validate([ + 'healthcare_id' => 'required', + 'doctor_id' => 'required', + 'start' => 'required', + 'end' => 'required' + ]); + + $claim = Claim::findOrFail($id); + + return $claim; + } public function updateItems(Request $request, $id) { @@ -242,8 +260,7 @@ class ClaimController extends Controller { $claim = Claim::findOrFail($id); - // TODO Fix this tipu tipu - $hospital = Organization::where('code', 'ORG000D')->first(); + $hospital = $claim->finalEncounter->healthcare ?? null; // TODO Fix this tipu tipu $inpationBenefit = $claim->member->currentPlan->benefits()->first(); @@ -251,17 +268,17 @@ class ClaimController extends Controller $pdf = PDF::loadView('pdf.final_log', [ 'claim' => $claim, 'member' => $claim->member, - 'dateOfAdmission' => now(), + 'dateOfAdmission' => $claim->start, 'hospital' => $hospital, 'inpationBenefit' => $inpationBenefit ]); - return $pdf->download('invoice.pdf'); + return $pdf->download('Final LOG '.$claim->code.'.pdf'); $view = view('pdf.final_log', [ 'claim' => $claim, 'member' => $claim->member, - 'dateOfAdmission' => now(), + 'dateOfAdmission' => $claim->start, 'hospital' => $hospital, 'inpationBenefit' => $inpationBenefit ]); diff --git a/Modules/Internal/Http/Controllers/ClaimEncounterController.php b/Modules/Internal/Http/Controllers/ClaimEncounterController.php new file mode 100644 index 00000000..0b99058e --- /dev/null +++ b/Modules/Internal/Http/Controllers/ClaimEncounterController.php @@ -0,0 +1,281 @@ +validate([ + 'service_code' => 'required', + 'tanggal_masuk' => 'required', + 'tanggal_keluar' => 'required' + ]); + $claim = Claim::findOrFail($claim_id); + + // return ($request->primary_diagnosis['id']); + // die; + // return $request->toArray(); + try { + DB::beginTransaction(); + + $newEncounterData = [ + 'status' => 'completed', + 'class' => $request->service_code, + 'type' => 'Consultation', + 'patient_id' => $claim->member->person_id, + 'start' => $request->tanggal_masuk, + 'end' => $request->tanggal_keluar, + 'number_of_bed' => $request->number_of_bed, + 'duration_day' => $request->duration_day + ]; + + if ($request->has('healthcare')) { + $newEncounterData['healthcare_id'] = $request->healthcare['id']; + } + + // Create New Encounter + $newEncounter = $claim->encounters()->create($newEncounterData); + + + // -------------------------------------------- + // Meta + // TODO Handle if healthcare not primaya + $newEncounter->metas()->updateOrCreate([ + 'type' => 'MEDRECID', + 'system' => 'primaya-his' + ], [ + 'type' => 'MEDRECID', + 'system' => 'primaya-his', + 'value' => $request->medical_record_number + ]); + + // --------------------------------------------- + // Handle Diagnosis + if ($request->has('primary_diagnosis') && $request->primary_diagnosis) { + $newEncounter->diagnoses()->create([ + 'diagnosis_id' => $request->primary_diagnosis['id'], + 'type' => 'primary', + 'use' => 'discharge', + 'source' => 'primecenter', + 'description' => 'Batching', + ]); + } + + if ($request->has('secondary_diagnoses')) { + foreach ($request->secondary_diagnoses as $diagnosis) { + if (!isset($diagnosis['id'])) { // Handle Null Values + continue; + } + $newEncounter->diagnoses()->create([ + 'diagnosis_id' => $diagnosis['id'], + 'type' => 'secondary', + 'use' => 'discharge', + 'source' => 'primecenter', + 'description' => 'Batching', + ]); + } + } + + // ------------------------------------ + // Handle Doctors as primary Doctor + if ($request->has('doctor')) { + $newEncounter->participants()->create([ + 'type' => 'Doctor', + 'participantable_type' => Practitioner::class, + 'participantable_id' => $request->doctor['id'], + ]); + } + DB::commit(); + + $newEncounter->load(['diagnoses', 'doctors', 'healthcare']); + + return Helper::responseJson(data: EncounterResource::make($newEncounter), message: 'Encounter berhasil ditambahkan'); + + } catch (\Exception $e) { + DB::rollback(); + throw $e; + } + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('internal::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $claim_id, $encounter_id) + { + $request->validate([ + 'service_code' => 'required', + 'tanggal_masuk' => 'required', + 'tanggal_keluar' => 'required' + ]); + // $claim = Claim::findOrFail($claim_id); + + // return ($request->primary_diagnosis['id']); + // die; + // return $request->toArray(); + try { + DB::beginTransaction(); + + $encounter = Encounter::findOrFail($encounter_id); + + $encounterData = [ + 'status' => 'completed', + 'class' => $request->service_code, + 'type' => 'Consultation', + 'start' => $request->tanggal_masuk, + 'end' => $request->tanggal_keluar, + 'number_of_bed' => $request->number_of_bed, + 'duration_day' => $request->duration_day + ]; + + if ($request->has('healthcare')) { + $encounterData['healthcare_id'] = $request->healthcare['id']; + } + + // Update The Encounter + $encounter->fill($encounterData); + $encounter->save(); + + + // -------------------------------------------- + // Meta + // TODO Handle if healthcare not primaya + $encounter->metas()->updateOrCreate([ + 'type' => 'MEDRECID', + 'system' => 'primaya-his' + ], [ + 'type' => 'MEDRECID', + 'system' => 'primaya-his', + 'value' => $request->medical_record_number + ]); + + // --------------------------------------------- + // Handle Diagnosis + if ($request->has('primary_diagnosis')) { + $encounter->diagnoses()->where('type', 'primary')->delete(); + $encounter->diagnoses()->create([ + 'diagnosis_id' => $request->primary_diagnosis['id'], + 'type' => 'primary', + 'use' => 'discharge', + 'source' => 'primecenter', + 'description' => 'Batching', + ]); + } + + if ($request->has('secondary_diagnoses')) { + $encounter->diagnoses()->where('type', 'secondary')->delete(); + foreach ($request->secondary_diagnoses as $diagnosis) { + if (!isset($diagnosis['id'])) { // Handle Null Values + continue; + } + $encounter->diagnoses()->create([ + 'diagnosis_id' => $diagnosis['id'], + 'type' => 'secondary', + 'use' => 'discharge', + 'source' => 'primecenter', + 'description' => 'Batching', + ]); + } + } + + // ------------------------------------ + // Handle Doctors as primary Doctor + // if ($request->has('doctor')) { + // $encounter->participants()->create([ + // 'type' => 'Doctor', + // 'participantable_type' => Practitioner::class, + // 'participantable_id' => $request->doctor['id'], + // ]); + // } + DB::commit(); + + $encounter->load(['diagnoses', 'doctors', 'healthcare']); + + return Helper::responseJson(data: EncounterResource::make($encounter), message: 'Encounter berhasil ditambahkan'); + + } catch (\Exception $e) { + DB::rollback(); + throw $e; + } + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + public function setFinalEncounter(Request $request, $claim_id) + { + $request->validate([ + 'encounter_id' => 'required' + ]); + + $claim = Claim::findOrFail($claim_id); + + $claim->final_encounter_id = $request->encounter_id; + $claim->save(); + + return Helper::responseJson(data: $claim, message: "Success Update Final Encounter"); + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 1bd98692..cc097ef1 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -29,6 +29,7 @@ use Modules\Internal\Http\Controllers\Api\PlanController; use Modules\Internal\Http\Controllers\Api\ProvinceController; use Modules\Internal\Http\Controllers\Api\SpecialityController; use Modules\Internal\Http\Controllers\Api\VillageController; +use Modules\Internal\Http\Controllers\ClaimEncounterController; /* |-------------------------------------------------------------------------- @@ -119,6 +120,10 @@ Route::prefix('internal')->group(function () { Route::get('members', [MemberController::class, 'index']); Route::get('members/{member_id}/benefits', [MemberController::class, 'benefits']); + Route::post('claims/{claim_id}/encounters', [ClaimEncounterController::class, 'store']); + Route::post('claims/{claim_id}/encounters/{encounter_id}/update', [ClaimEncounterController::class, 'update']); + Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']); + Route::get('claims', [ClaimController::class, 'index']); 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'); diff --git a/Modules/Internal/Transformers/ClaimShowResource.php b/Modules/Internal/Transformers/ClaimShowResource.php index 6b399b97..c4a48e10 100644 --- a/Modules/Internal/Transformers/ClaimShowResource.php +++ b/Modules/Internal/Transformers/ClaimShowResource.php @@ -37,6 +37,10 @@ class ClaimShowResource extends JsonResource $data['primary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'primary';})->values(); $data['secondary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'secondary';})->values(); + $data['encounters'] = $this->encounters->map(function($encounter) { + $encounterData = EncounterResource::make($encounter); + return $encounterData; + }); return $data; } } diff --git a/Modules/Internal/Transformers/EncounterResource.php b/Modules/Internal/Transformers/EncounterResource.php new file mode 100644 index 00000000..06d77b25 --- /dev/null +++ b/Modules/Internal/Transformers/EncounterResource.php @@ -0,0 +1,31 @@ +service->name; + $encounter['primary_diagnosis'] = $this->primaryDiagnoses->first(); + $encounter['primary_doctor'] = $this->doctors->first() + ? array_merge( + $this->doctors->first()->person->toArray(), + $this->doctors->first()->toArray() + ) + : null; + $encounter['medical_record_number'] = @$this->meta->MEDRECID ?? null; + + return $encounter; + } +} diff --git a/app/Models/Claim.php b/app/Models/Claim.php index 455f069c..d699d6f1 100644 --- a/app/Models/Claim.php +++ b/app/Models/Claim.php @@ -149,6 +149,16 @@ class Claim extends Model return $this->belongsTo(Member::class, 'member_id'); } + public function encounters() + { + return $this->belongsToMany(Encounter::class, 'claim_encounter'); + } + + public function finalEncounter() + { + return $this->belongsTo(Encounter::class, 'final_encounter_id'); + } + public function diagnoses() { return $this->hasMany(ClaimDiagnosis::class, 'claim_id'); @@ -185,5 +195,10 @@ class Claim extends Model ->whereIn('status', ['approved', 'paid']); // ->whereBetween('requested_at', [$startDate, $endDate]); } + + public function getTotalTagihanAttribute() + { + return $this->items->sum('nominal_ditagihkan'); + } } diff --git a/app/Models/ClaimDiagnosis.php b/app/Models/ClaimDiagnosis.php index ef3788e9..a07d3f85 100644 --- a/app/Models/ClaimDiagnosis.php +++ b/app/Models/ClaimDiagnosis.php @@ -28,6 +28,11 @@ class ClaimDiagnosis extends Model 'deleted_by', ]; + public $type_enums = [ + 'primary' => 'Primary', + 'secondary' => 'Secondary' + ]; + public function icd() { return $this->belongsTo(Icd::class, 'diagnosis_id', 'id'); diff --git a/app/Models/Encounter.php b/app/Models/Encounter.php index 7ece9052..48cd4819 100644 --- a/app/Models/Encounter.php +++ b/app/Models/Encounter.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\Relation; class Encounter extends Model { @@ -15,8 +16,37 @@ class Encounter extends Model 'class', 'type', 'patient_id', + 'healthcare_id', 'start', 'end', + 'number_of_bed', + 'duration_day' + ]; + + public $casts = [ + 'start' => 'datetime', + 'end' => 'datetime' + ]; + + public static $status_enums = [ + 'completed' => 'Completed', + 'planned' => 'Planned', + 'in-progress' => 'In Progress', + 'on-hold' => 'On Hold', + 'discharged' => 'Discharged', + 'completed' => 'Completed', + 'cancelled' => 'Cancelled', + 'discontinued' => 'Discontinued', + 'entered-in-error' => 'Entered in Error', + 'unknown' => 'Unknown', + ]; + + public $with = [ + 'metas' + ]; + + public $append = [ + 'meta' ]; public function participants() @@ -24,8 +54,55 @@ class Encounter extends Model return $this->hasMany(EncounterParticipant::class, 'encounter_id'); } + public function doctors() + { + + // return $this->hasManyThrough(CorporateService::class, Service::class, 'corporate_id', 'service_code', 'id', 'service_code'); + + return $this->hasManyThrough(Practitioner::class, EncounterParticipant::class, 'encounter_id', 'id', 'id', 'participantable_id') + ->where( + 'participantable_type', + array_search(static::class, Relation::morphMap()) ?: Practitioner::class + ); + } + public function diagnoses() { return $this->hasMany(EncounterDiagnosis::class, 'encounter_id'); } + + public function healthcare() + { + return $this->belongsTo(Organization::class, 'healthcare_id'); + } + + public function metas() + { + return $this->morphMany(Meta::class, 'metaable'); + } + + public function primaryDiagnoses() + { + return $this->diagnoses()->where('type', 'primary'); + } + + public function secondaryDiagnoses() + { + return $this->diagnoses()->wher('type', 'primary'); + } + + public function service() + { + return $this->belongsTo(Service::class, 'class', 'code'); + } + + public function getMetaAttribute() + { + $orgMeta = []; + foreach ($this->metas as $meta) { + $orgMeta[$meta->type] = $meta->value; + } + + return (object) $orgMeta; + } } diff --git a/app/Models/EncounterDiagnosis.php b/app/Models/EncounterDiagnosis.php index 7b3fa650..0bbdad64 100644 --- a/app/Models/EncounterDiagnosis.php +++ b/app/Models/EncounterDiagnosis.php @@ -12,6 +12,7 @@ class EncounterDiagnosis extends Model public $fillable = [ 'encounter_id', 'diagnosis_id', + 'type', 'use', 'source', 'description', @@ -22,8 +23,18 @@ class EncounterDiagnosis extends Model 'final' => 'Final' ]; + public static $type_enums = [ + 'primary' => 'Primary', + 'secondary' => 'Secondary' + ]; + public function encounter() { return $this->belongsTo(Encounter::class, 'encounter_id'); } + + public function diagnosis() + { + return $this->belongsTo(Icd::class, 'diagnosis_id'); + } } diff --git a/app/Models/EncounterParticipant.php b/app/Models/EncounterParticipant.php index 08a6a0fe..e60afcab 100644 --- a/app/Models/EncounterParticipant.php +++ b/app/Models/EncounterParticipant.php @@ -12,6 +12,8 @@ class EncounterParticipant extends Model public $fillable = [ 'encounter_id', 'type', + 'participantable_type', + 'participantable_id' ]; diff --git a/database/migrations/2023_03_15_155301_create_encounters_table.php b/database/migrations/2023_03_15_155301_create_encounters_table.php index d267ca63..d9c60f09 100644 --- a/database/migrations/2023_03_15_155301_create_encounters_table.php +++ b/database/migrations/2023_03_15_155301_create_encounters_table.php @@ -15,13 +15,16 @@ return new class extends Migration { Schema::create('encounters', function (Blueprint $table) { $table->id(); - $table->foreignId('part_of')->comment('encounter_id'); + $table->foreignId('part_of')->nullable()->comment('encounter_id')->index(); $table->string('status'); $table->string('class')->nullable(); $table->string('type')->nullable(); - $table->foreignId('patient_id')->nullable(); + $table->foreignId('patient_id')->nullable()->index(); + $table->foreignId('healthcare_id')->nullable()->index(); $table->dateTime('start')->nullable(); $table->dateTime('end')->nullable(); + $table->integer('number_of_bed')->nullable(); + $table->integer('duration_day')->nullable(); $table->timestamps(); $table->softDeletes(); diff --git a/database/migrations/2023_03_15_162148_create_encounter_diagnoses_table.php b/database/migrations/2023_03_15_162148_create_encounter_diagnoses_table.php index 2f391331..fe88fb02 100644 --- a/database/migrations/2023_03_15_162148_create_encounter_diagnoses_table.php +++ b/database/migrations/2023_03_15_162148_create_encounter_diagnoses_table.php @@ -17,6 +17,7 @@ return new class extends Migration $table->id(); $table->foreignId('encounter_id'); $table->foreignId('diagnosis_id'); + $table->string('type')->nullable(); $table->string('use')->nullable(); $table->text('source')->nullable(); $table->text('description')->nullable(); diff --git a/database/migrations/2023_03_16_150733_create_claim_encounter_table.php b/database/migrations/2023_03_16_150733_create_claim_encounter_table.php new file mode 100644 index 00000000..7bd1b4b9 --- /dev/null +++ b/database/migrations/2023_03_16_150733_create_claim_encounter_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('claim_id'); + $table->foreignId('encounter_id'); + + $table->index(['claim_id', 'encounter_id'], 'claim_encounter_index'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('claim_encounter'); + } +}; diff --git a/database/migrations/2023_03_21_151000_add_final_encounter_id_to_claims_table.php b/database/migrations/2023_03_21_151000_add_final_encounter_id_to_claims_table.php new file mode 100644 index 00000000..2682e4c3 --- /dev/null +++ b/database/migrations/2023_03_21_151000_add_final_encounter_id_to_claims_table.php @@ -0,0 +1,32 @@ +foreignId('final_encounter_id')->nullable()->after('benefit_id')->index(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('claims', function (Blueprint $table) { + $table->dropColumn('final_encounter_id'); + }); + } +}; diff --git a/frontend/dashboard/src/components/autocomplete/AutocompleteDoctor.tsx b/frontend/dashboard/src/components/autocomplete/AutocompleteDoctor.tsx index 2dbb30ea..3ad2b0e3 100644 --- a/frontend/dashboard/src/components/autocomplete/AutocompleteDoctor.tsx +++ b/frontend/dashboard/src/components/autocomplete/AutocompleteDoctor.tsx @@ -17,6 +17,7 @@ export default function AutocompleteDoctor({ currentValue, textLabel, currentOptions = [], + ...other }: autocompleteDoctorType) { const [options, setOptions] = useState(currentOptions); const [loading, setLoading] = useState(false); @@ -56,7 +57,7 @@ export default function AutocompleteDoctor({ }} loading={loading} renderInput={(params) => ( - {getOptions(event.target.value)}}/> + {getOptions(event.target.value)}}/> )} /> ); diff --git a/frontend/dashboard/src/components/autocomplete/AutocompleteHealthcare.tsx b/frontend/dashboard/src/components/autocomplete/AutocompleteHealthcare.tsx index eb9b51e8..2c163ec7 100644 --- a/frontend/dashboard/src/components/autocomplete/AutocompleteHealthcare.tsx +++ b/frontend/dashboard/src/components/autocomplete/AutocompleteHealthcare.tsx @@ -17,6 +17,7 @@ export default function AutocompleteHealthcare({ currentValue, textLabel, currentOptions = [], + ...other }: autocompleteHealthcareType) { const [options, setOptions] = useState(currentOptions); const [loading, setLoading] = useState(false); @@ -56,7 +57,7 @@ export default function AutocompleteHealthcare({ }} loading={loading} renderInput={(params) => ( - {getOptions(event.target.value)}}/> + {getOptions(event.target.value)}}/> )} /> ); diff --git a/frontend/dashboard/src/components/hook-form/RHFDatepicker.tsx b/frontend/dashboard/src/components/hook-form/RHFDatepicker.tsx index c8bda14e..630ccaeb 100755 --- a/frontend/dashboard/src/components/hook-form/RHFDatepicker.tsx +++ b/frontend/dashboard/src/components/hook-form/RHFDatepicker.tsx @@ -49,13 +49,30 @@ export default function RHFDatepicker({ name, ...other }: IProps & TextFieldProp /> )} /> */} - { field.onChange(value) }} renderInput={(params) => } + /> */} + + { + field.onChange(value); + }} + renderInput={(params) => ( + + )} /> )} diff --git a/frontend/dashboard/src/pages/Claims/Show.tsx b/frontend/dashboard/src/pages/Claims/Show.tsx index 811306d0..5a7818d2 100644 --- a/frontend/dashboard/src/pages/Claims/Show.tsx +++ b/frontend/dashboard/src/pages/Claims/Show.tsx @@ -19,30 +19,25 @@ import { TextField, Typography, } from '@mui/material'; -import { Controller, useForm } from 'react-hook-form'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs'; -import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form'; import Page from '../../components/Page'; import useSettings from '../../hooks/useSettings'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { styled } from '@mui/system'; import axios from '../../utils/axios'; import { enqueueSnackbar } from 'notistack'; import { LoadingButton } from '@mui/lab'; -import { fCurrency } from '../../utils/formatNumber'; +import { fDate } from '@/utils/formatTime'; import Iconify from '../../components/Iconify'; -import Form from './Form'; import Documents from './components/Documents'; import DiagnosisHistory from './components/DiagnosisHistory'; import ClaimItems from './components/ClaimItems'; import DialogMemberBenefit from './components/DialogMemberBenefit'; -import AutocompleteDiagnosis from '@/components/autocomplete/AutocompleteDiagnosis'; -import AutocompleteDiagnosisControlled from '@/components/autocomplete/AutocompleteDiagnosisControlled'; -import { Icd, IcdType } from '@/@types/diagnosis'; -import { OrganizationType, PractitionerType } from '@/@types/doctor'; import ClaimDetail from './components/ClaimDetail'; +import DialogHistoryPerawatan from './components/DialogHistoryPerawatan'; +import { IconButton } from '@mui/material'; +import { Tooltip } from '@mui/material'; export default function ClaimsCreateUpdate() { const { themeStretch } = useSettings(); @@ -198,8 +193,9 @@ export default function ClaimsCreateUpdate() { }); }; - // ---------------------------------------------------------------- + // History Perawatan + const [openDialogHistoryPerawatan, setOpenDialogHistoryPerawatan] = useState(false); useEffect(() => { axios.get('/claims/' + id).then(({ data }) => { @@ -212,6 +208,33 @@ export default function ClaimsCreateUpdate() { }); }, [id]); + const handleSetFinalEncounter = (encounter) => { + const tempLastFinalEncounterId = currentClaim.final_encounter_id + axios.post(`/claims/${id}/set-final-encounter`, { + encounter_id: encounter.id + }) + .then((res) => { + setCurrentClaim({...currentClaim, ...{final_encounter_id: encounter.id}}) + enqueueSnackbar(res.data.message, {variant: 'success'}) + }) + .catch((err) => { + setCurrentClaim({...currentClaim, ...{final_encounter_id: tempLastFinalEncounterId}}) + enqueueSnackbar(err.message, {variant: 'error'}) + }) + setCurrentClaim({...currentClaim, ...{final_encounter_id: encounter.id}}) + } + + // ------------------------------------------------------------------ + // Edit History Perawatan + + const [openDialogEditHistoryPerawatan, setOpenDialogEditHistoryPerawatan] = useState(false) + const [editedEncounter, setEditedEncounter] = useState(null) + + const showEditEncounter = (encounter) => { + setEditedEncounter(encounter) + setOpenDialogEditHistoryPerawatan(true) + } + return ( @@ -359,9 +382,86 @@ export default function ClaimsCreateUpdate() { - {/* Diagnosis */} + {/* Claim Detail */} - + + + + + History Perawatan + + + { + setOpenDialogHistoryPerawatan(true); + }} + > + + Tambah History Perawatan + + + + {/* For Creation History Perawatan / Encounter */} + {}} + claim={currentClaim} + > + + + + {currentClaim?.encounters && currentClaim?.encounters.map((encounter, index) => ( + + + + {showEditEncounter(encounter)}}>{ encounter.class_name } + + ({ fDate(encounter.start) } - { fDate(encounter.end) }) + + Diagnosis: { encounter.primary_diagnosis ? (`(${encounter.primary_diagnosis.diagnosis?.code}) ${encounter.primary_diagnosis.diagnosis?.name}`) : '-'} + Dokter: { encounter.primary_doctor ? (`${encounter.primary_doctor.name}`) : '-'} + + + {handleSetFinalEncounter(encounter)}}> + + + + + + + + ))} + + {(!currentClaim?.encounters || currentClaim?.encounters.length == 0) && ( + Belum ada History Perawatan + )} + + + + {/* For Editing History Perawatan / Encounter */} + {}} + claim={currentClaim} + encounter={editedEncounter} + > + + {/* */} diff --git a/frontend/dashboard/src/pages/Claims/components/DiagnosisHistory.tsx b/frontend/dashboard/src/pages/Claims/components/DiagnosisHistory.tsx index db44687f..f2b10b82 100644 --- a/frontend/dashboard/src/pages/Claims/components/DiagnosisHistory.tsx +++ b/frontend/dashboard/src/pages/Claims/components/DiagnosisHistory.tsx @@ -21,19 +21,19 @@ export default function DiagnosisHistory({ diagnosis }) { - + Riwayat Diagnosa - { setOpenDialogRequestDocument(true); }} > View All - + */} diff --git a/frontend/dashboard/src/pages/Claims/components/DialogHistoryPerawatan.tsx b/frontend/dashboard/src/pages/Claims/components/DialogHistoryPerawatan.tsx new file mode 100644 index 00000000..e32752d5 --- /dev/null +++ b/frontend/dashboard/src/pages/Claims/components/DialogHistoryPerawatan.tsx @@ -0,0 +1,73 @@ +import MuiDialog from '@/components/MuiDialog'; +import axios from '@/utils/axios'; +import { Button, Checkbox, Typography } from '@mui/material'; +import { Paper } from '@mui/material'; +import { Stack } from '@mui/material'; +import { enqueueSnackbar } from 'notistack'; +import { useState } from 'react'; +import FormHistoryPerawatan from './FormHistoryPerawatan'; + +type DialogHistoryPerawatanType = { + openDialog: boolean; + setOpenDialog: void; + onSubmit?: void; + claim: any; // TODO create ClaimType + encounter?: any; +} + +export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSubmit, claim, encounter } : DialogHistoryPerawatanType) { + + const isEdit = encounter?.id != null + // const benefits = member?.current_plan?.benefits ?? []; + // const [selectedBenefits, setSelectedBenefits] = useState([]); + + // const toggleBenefit = (benefit) => { + // if (selectedBenefits.includes(benefit)) { + // console.log('removing', benefit) + // setSelectedBenefits(selectedBenefits.filter((throughBenefit) => benefit.id != throughBenefit.id)) + // } else { + // console.log('adding', benefit) + // setSelectedBenefits([...selectedBenefits, benefit]) + // } + // } + + const handleSubmit = (data) => { + + if (!isEdit) { + axios.post(`claims/${claim.id}/encounters`, data) + .then((res) => { + enqueueSnackbar(res.data.message, {variant: 'success'}) + setOpenDialog(false); + }) + .catch((err) => { + enqueueSnackbar(err.message, {variant: 'error'}) + }) + } else { + axios.post(`claims/${claim.id}/encounters/${data.id}/update`, data) + .then((res) => { + enqueueSnackbar(res.data.message, {variant: 'success'}) + setOpenDialog(false); + }) + .catch((err) => { + enqueueSnackbar(err.message, {variant: 'error'}) + }) + } + }; + + + const getContent = () => ( + + + + ); + + return ( + + ); +} diff --git a/frontend/dashboard/src/pages/Claims/components/FormHistoryPerawatan.tsx b/frontend/dashboard/src/pages/Claims/components/FormHistoryPerawatan.tsx new file mode 100644 index 00000000..be362b46 --- /dev/null +++ b/frontend/dashboard/src/pages/Claims/components/FormHistoryPerawatan.tsx @@ -0,0 +1,352 @@ +import * as Yup from 'yup'; +import { IcdType } from '@/@types/diagnosis'; +import { OrganizationType, PractitionerType } from '@/@types/doctor'; +import AutocompleteDiagnosisControlled from '@/components/autocomplete/AutocompleteDiagnosisControlled'; +import { LoadingButton } from '@mui/lab'; +import { Paper, Stack, TextField } from '@mui/material'; +import { Controller, useForm } from 'react-hook-form'; +import React, { useEffect, useMemo, useState } from 'react'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { enqueueSnackbar } from 'notistack'; +import axios from '@/utils/axios'; +import { FormProvider, RHFDatepicker, RHFTextField, RHFSelect } from '@/components/hook-form'; +import AutocompleteDoctor from '@/components/autocomplete/AutocompleteDoctor'; +import AutocompleteHealthcare from '@/components/autocomplete/AutocompleteHealthcare'; +import { Typography } from '@mui/material'; +import Iconify from '@/components/Iconify'; +import { Divider } from '@mui/material'; + +type FormHistoryPerawatanProps = { + claim: any; + onSubmit: void; + encounter?: any; // TODO EncounterType +}; + +export default function FormHistoryPerawatan({ + claim, + onSubmit, + encounter, +}: FormHistoryPerawatanProps) { + const isEdit = encounter?.id != null; + // -------------------------------------------------------------- + // Diagnosis + const [primaryDiagnosis, setPrimaryDiagnosis] = useState(null); + const [secondaryDiagnosis, setSecondaryDiagnosis] = useState(null); + const [loadingDiagnosis, setLoadingDiagnosis] = useState(false); + const [primaryDiagnosisOptions, setPrimaryDiagnosisOptions] = useState([]); + const [secondaryDiagnosisOptions, setSecondaryDiagnosisOptions] = useState([]); + const [doctorOptions, setDoctorOptions] = useState([]); + const [healthcareOptions, setHealthcareOptions] = useState([]); + + const ClaimDetailSchema = Yup.object().shape({ + primary_diagnosis: Yup.object().required('Diagnosis Utama Wajib dipilih'), + // secondary_diagnosis: Yup.object().required('Diagnosis Utama Wajib dipilih'), + doctor: Yup.object().required('Dokter Harus dipilih'), + healthcare: Yup.object().required('Healthcare Harus dipilih'), + }); + + interface FormValuesProps extends Partial { + service_code: String; + primary_diagnosis: IcdType; + secondary_diagnosis: IcdType; + doctor: PractitionerType; + healthcare: OrganizationType; + secondary_diagnoses: IcdType[]; + } + + const defaultValues = useMemo( + () => ({ + service_code: 'OP', + tanggal_masuk: null, + tanggal_keluar: null, + primary_diagnosis: null, + doctor: null, + healthcare: null, + no_medrec: '', + bed: '', + duration: '', + secondary_diagnoses: [], + }), + [claim] + ); + + const methods = useForm({ + resolver: yupResolver(ClaimDetailSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + const handleSaveDiagnosis = () => { + setLoadingDiagnosis(true); + + axios + .post(`claims/${claim.id}/update-diagnosis`, { + primary: [getValues('primary_diagnosis')?.id], + secondary: [getValues('secondary_diagnosis')?.id], + }) + .then((res) => { + enqueueSnackbar(res.data.message, { variant: 'success' }); + }) + .catch((err) => { + setLoadingDiagnosis(false); + enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' }); + }) + .then(() => { + setLoadingDiagnosis(false); + }); + }; + + useEffect(() => { + if (claim) { + // SET Default Primary Diagnosis + // if (claim.primary_diagnosis.length && claim.primary_diagnosis[0].icd) { + // setPrimaryDiagnosisOptions( + // claim.primary_diagnosis.map((diagnosis) => { + // if (diagnosis.icd) { + // return diagnosis.icd; + // } + // }) + // ); + // setValue('primary_diagnosis', claim.primary_diagnosis[0].icd); + // } + // SET Default Secondary Diagnosis + // if (claim.secondary_diagnosis.length && claim.secondary_diagnosis[0].icd) { + // setSecondaryDiagnosisOptions( + // claim.secondary_diagnosis.map((diagnosis) => { + // if (diagnosis.icd) { + // return diagnosis.icd; + // } + // }) + // ); + // setValue('secondary_diagnosis', claim.secondary_diagnosis[0].icd); + // } + } + }, [claim]); + + useEffect(() => { + if (encounter?.id) { + // Primary Diagnosis + if (encounter.primary_diagnosis && encounter.primary_diagnosis?.diagnosis) { + setPrimaryDiagnosisOptions([encounter.primary_diagnosis.diagnosis]); + setValue('primary_diagnosis', encounter.primary_diagnosis.diagnosis); + } + + // Sondary Diagnoses + if (encounter.secondary_diagnoses && encounter.secondary_diagnoses.length) { + encounter.secondary_diagnoses.map(); + setPrimaryDiagnosisOptions([encounter.primary_diagnosis.diagnosis]); + setValue('primary_diagnosis', encounter.primary_diagnosis.diagnosis); + } + + if (encounter.primary_doctor) { + // TODO Clear Up Data to used by autocomplete + setDoctorOptions([encounter.primary_doctor]); + setValue('doctor', encounter.primary_doctor); + } + + if (encounter.healthcare) { + // TODO Clear Up Data to used by autocomplete + setDoctorOptions([encounter.healthcare]); + setValue('healthcare', encounter.healthcare); + } + + setValue('service_code', encounter.class); + setValue('tanggal_masuk', encounter.start); + setValue('tanggal_keluar', encounter.end); + setValue('medical_record_number', encounter.medical_record_number); + setValue('number_of_bed', encounter.number_of_bed); + setValue('duration_day', encounter.duration_day); + setValue('secondary_diagnoses', encounter.secondary_diagnosis); + setValue('id', encounter.id); + } + }, [encounter]); + + function handleAddSecondaryDiagnosis() { + setValue('secondary_diagnoses', [...getValues('secondary_diagnoses'), null]); + } + + function handleRemoveSecondaryDiagnosis(diagnosis) { + // TODO Fix Bug + const newDiagnoses = getValues('secondary_diagnoses').filter((val) => { + return diagnosis !== val; + }); + setValue('secondary_diagnoses', newDiagnoses); + } + + const services = [ + { + code: 'OP', + name: 'Rawat Jalan', + }, + { + code: 'IP', + name: 'Rawat Inap', + }, + ]; + + const submitting = () => { + onSubmit(getValues()); + }; + + return ( + + + + + {/* + ))} + + + + + + + ( + + )} + /> + + ( + + )} + /> + + + + + + + + + + + {/* {JSON.stringify(getValues('primary_diagnosis'))} */} + ( + + )} + /> + + {/* SECONDARY DIAGNOSES */} + + + { + handleAddSecondaryDiagnosis(); + }} + > + + Secondary Diagnosis + + + + {getValues('secondary_diagnoses') && + getValues('secondary_diagnoses').map(function (diagnosis, index) { + return ( + + {/* */} + } + > + + #{index + 1} + + { + handleRemoveSecondaryDiagnosis(diagnosis); + }} + > + + ( + + )} + /> + {/* */} + + ); + })} + + + + {(claim?.status == 'requested' || claim?.status == 'received') && ( + { + submitting(); + }} + > + Simpan Detail + + )} + + + ); +} diff --git a/resources/views/pdf/final_log.blade.php b/resources/views/pdf/final_log.blade.php index d6cf2d7a..6abfc19e 100644 --- a/resources/views/pdf/final_log.blade.php +++ b/resources/views/pdf/final_log.blade.php @@ -184,35 +184,35 @@ use App\Helpers\Helper; B. Informasi Perawatan 1. Tanggal Masuk - : {{ '' }} + : {{ $claim->finalEncounter->start ?? "" }} 7. Lama Perawatan - : {{ '' }} + : {{ $claim->finalEncounter->duration_day ?? "" }} 2. Tanggal Keluar - : {{ '' }} + : {{ $claim->finalEncounter->end }} 8. Kamar Perawatan - : + : {{ "" }} 3. Nama Rumah Sakit : {{ $hospital->name }} 9. Jumlah Tempat Tidur - : {{ '' }} + : {{ $claim->finalEncounter->number_of_bed }} 4. Dokter yang Merawat - : {{ '' }} + : {{ $claim->finalEncounter->doctors->first()->person->name ?? '' }} 10. Estimasi Biaya Rawat Inap - : {{ '' }} + : {{ $claim->total_tagihan ? Helper::currencyIdrFormat($claim->total_tagihan) : "" }} 5. No. Rekam Medis - : {{ '' }} + : {{ $claim->finalEncounter->meta->MEDRECID }} 11. Diagnosa : {{ $claim->diagnosis?->icd?->code ?? '' }}/{{ $claim->diagnosis?->icd?->name ?? '' }}