diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..07754d16 Binary files /dev/null and b/.DS_Store differ diff --git a/Modules/Internal/Http/Controllers/Api/ClaimController.php b/Modules/Internal/Http/Controllers/Api/ClaimController.php index 4909f83d..876c2d82 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimController.php @@ -2,10 +2,14 @@ namespace Modules\Internal\Http\Controllers\Api; +use App\Models\Benefit; use App\Models\Claim; +use App\Models\Icd; +use App\Models\Member; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Modules\Internal\Services\ClaimService; class ClaimController extends Controller { @@ -21,6 +25,7 @@ class ClaimController extends Controller 'plan', 'benefit' ]) + ->latest() ->paginate(10); return response()->json($claims); @@ -42,7 +47,28 @@ class ClaimController extends Controller */ public function store(Request $request) { - // + $request->validate([ + 'diagnosis_id' => 'required', + 'member_id' => 'required', + 'total_claim' => 'required', + 'benefit_id' => 'required' + ]); + + // return response()->json($request->toArray()); + + $member = Member::find($request->member_id); + $benefit = Benefit::find($request->benefit_id); + $diagnosis = Icd::find($request->diagnosis_id); + + // Check Eligibility + $validation = ClaimService::checkMemberEligibility($member, $benefit, $diagnosis, $request->total_claim); + + // Store Claim + if ($validation['isEligible']) { + $claim = ClaimService::storeClaim($member, $diagnosis, $request->total_claim, $benefit); + } + + return response()->json($claim); } /** diff --git a/Modules/Internal/Http/Controllers/Api/MemberController.php b/Modules/Internal/Http/Controllers/Api/MemberController.php index f8defb31..96925c7b 100644 --- a/Modules/Internal/Http/Controllers/Api/MemberController.php +++ b/Modules/Internal/Http/Controllers/Api/MemberController.php @@ -15,7 +15,9 @@ class MemberController extends Controller */ public function index() { - return Member::paginate(); + return Member::query() + ->with('currentPlan') + ->paginate(); } /** @@ -77,4 +79,11 @@ class MemberController extends Controller { // } + + public function benefits($member_id) + { + $member = Member::findOrFail($member_id); + + return response()->json($member->currentPlan->benefits()->select(['description', 'code', 'id'])->get()); + } } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 57709300..c9eab7ed 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -96,8 +96,10 @@ Route::prefix('internal')->group(function () { Route::post('master/formulariums/import', [FormulariumController::class, 'import']); Route::get('members', [MemberController::class, 'index']); + Route::get('members/{member_id}/benefits', [MemberController::class, 'benefits']); Route::get('claims', [ClaimController::class, 'index']); + Route::post('claims', [ClaimController::class, 'store']); Route::post('check-limit', [ClaimController::class, 'checkLimit']); }); diff --git a/Modules/Internal/Services/ClaimService.php b/Modules/Internal/Services/ClaimService.php new file mode 100644 index 00000000..569cf8dd --- /dev/null +++ b/Modules/Internal/Services/ClaimService.php @@ -0,0 +1,115 @@ +currentPlan; + $policy = $member->currentPolicy; + $corporate = $policy->corporate; + + + $isEligible = true; + $validationErrors = []; + + // Eligibility Validation + + if (!in_array($member->marital_status, explode(',', $benefit->msc))) { + $validationErrors[] = [ + 'error' => 'msc', + 'message' => 'Only '.$benefit->msc + ]; + $isEligible = false; + } + + if (!in_array($member->gender_code, explode(',', $benefit->genders))) { + $validationErrors[] = [ + 'error' => 'genders', + 'message' => 'Only '.$benefit->genders + ]; + $isEligible = false; + } + + if (!empty($benefit->min_age) && $member->age < $benefit->min_age) { + $validationErrors[] = [ + 'error' => 'min_age', + 'message' => 'Minimum Age is '.$benefit->min_age + ]; + $isEligible = false; + } + + if (!empty($benefit->max_age) && $member->age > $benefit->max_age) { + $validationErrors[] = [ + 'error' => 'max_age', + 'message' => 'Maximum Age is '.$benefit->min_age + ]; + $isEligible = false; + } + + // TODO complete validations + + // Limit Validation + if ($totalClaim > 0) { + if (bcsub($corporate->limit_balance, $totalClaim) < 0) { + $validationErrors[] = [ + 'error' => 'corporate_limit', + 'message' => 'Corporate Limit cannot cover this' + ]; + $isEligible = false; + } + + if (bcsub($benefit->limit_amount, $totalClaim) < 0) { + $validationErrors[] = [ + 'error' => 'benefit_limit', + 'message' => 'Benefit Limit cannot cover this' + ]; + $isEligible = false; + } + + // TODO complete validations + + } + + return [ + 'isEligible' => $isEligible, + 'errors' => $validationErrors + ]; + } + + public static function storeClaim($member, $diagnosis, $totalClaim, $benefit) + { + try { + DB::beginTransaction(); + + $claim = Claim::create([ + 'member_id' => $member->id, + 'diagnosis_id' => $diagnosis->id, + 'total_claim' => $totalClaim, + 'currency' => 'IDR', + 'plan_id' => $member->currentPlan->id, + 'benefit_id' => $benefit->id, + ]); + + $policy = $member->currentPolicy; + $policy->limitJournals()->create([ + 'previous_balance' => $policy->limit_balance, + 'total_credit' => $totalClaim, + 'type' => 'credit', + 'balance' => bcsub($policy->limit_balance, $totalClaim), + 'description' => 'Log for Claim #'. $claim->code, + ]); + + DB::commit(); + return $claim; + } catch (\Exception $error) { + DB::rollBack(); + + return false; + } + } +} \ No newline at end of file diff --git a/Modules/Internal/Services/MemberEnrollmentService.php b/Modules/Internal/Services/MemberEnrollmentService.php index fc45e427..50a7c283 100755 --- a/Modules/Internal/Services/MemberEnrollmentService.php +++ b/Modules/Internal/Services/MemberEnrollmentService.php @@ -427,6 +427,7 @@ class MemberEnrollmentService // TODO validate corporate plan $plan = Plan::query() ->where('code', $row['plan_id']) + ->where('corporate_id', $corporate->id) ->first(); if (!$plan) { throw new ImportRowException(__('enrollment.PLAN_NOT_FOUND'), 0, null, $row); @@ -461,7 +462,7 @@ class MemberEnrollmentService ]); $member->memberPlans()->create([ - 'plan_id' => $plan->code, + 'plan_id' => $plan->id, 'status' => 'active', 'start' => Carbon::parse(strtotime($row['member_effective_date'])), 'end' => Carbon::parse(strtotime($row['member_expiry_date'])), diff --git a/app/Http/Controllers/Api/MemberController.php b/app/Http/Controllers/Api/MemberController.php index 164aa93b..97bcfd29 100755 --- a/app/Http/Controllers/Api/MemberController.php +++ b/app/Http/Controllers/Api/MemberController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Models\Member; use Illuminate\Http\Request; class MemberController extends Controller diff --git a/app/Models/Claim.php b/app/Models/Claim.php index 39919309..5c5d6b63 100644 --- a/app/Models/Claim.php +++ b/app/Models/Claim.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; class Claim extends Model { @@ -19,6 +20,28 @@ class Claim extends Model 'benefit_id', ]; + protected $hidden = [ + 'created_at', + 'updated_at', + 'deleted_at', + 'created_by', + 'updated_by', + 'deleted_by', + ]; + + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + try { + $model->code = (string) Str::orderedUuid(); // generate uuid + } catch (\Exception $e) { + abort(500, $e->getMessage()); + } + }); + } + public function member() { return $this->belongsTo(Member::class, 'member_id'); @@ -38,4 +61,5 @@ class Claim extends Model { return $this->belongsTo(Benefit::class, 'benefit_id'); } + } diff --git a/app/Models/Corporate.php b/app/Models/Corporate.php index d8147665..04ad740f 100755 --- a/app/Models/Corporate.php +++ b/app/Models/Corporate.php @@ -106,4 +106,9 @@ class Corporate extends Model { return $this->hasMany(Corporate::class, 'parent_id'); } + + public function getLimitBalanceAttribute() + { + return $this->currentPolicy->limit_balance; + } } diff --git a/app/Models/CorporatePolicy.php b/app/Models/CorporatePolicy.php index 1e8d99d5..7ccde342 100755 --- a/app/Models/CorporatePolicy.php +++ b/app/Models/CorporatePolicy.php @@ -34,6 +34,11 @@ class CorporatePolicy extends Model return $this->belongsTo(Corporate::class); } + public function limitJournals() + { + return $this->morphMany(LimitJournal::class, 'journalable'); + } + public function setCodeAttribute($value) { $this->attributes['code'] = !empty($value) ? $value : Str::upper(Str::random('6')); @@ -53,4 +58,11 @@ class CorporatePolicy extends Model { $this->attributes['end'] = !empty($value) ? Carbon::parse($value)->format('Y-m-d') : null; } + + public function getLimitBalanceAttribute() + { + $journal = $this->limitJournals()->latest()->first(); + + return $journal ? $journal->balance : (!empty($this->total_premi) ? $this->total_premi : "0"); + } } diff --git a/app/Models/Icd.php b/app/Models/Icd.php index 40115736..9dfa57ff 100755 --- a/app/Models/Icd.php +++ b/app/Models/Icd.php @@ -27,6 +27,15 @@ class Icd extends Model 'active' ]; + protected $hidden = [ + 'created_at', + 'updated_at', + 'deleted_at', + 'created_by', + 'updated_by', + 'deleted_by', + ]; + public function getTypeAttribute() { return 'ICD-'.$this->rev; diff --git a/app/Models/LimitJournal.php b/app/Models/LimitJournal.php index abba9b09..707715e9 100644 --- a/app/Models/LimitJournal.php +++ b/app/Models/LimitJournal.php @@ -11,7 +11,9 @@ class LimitJournal extends Model protected $fillable = [ 'journalable', - 'mutation', + 'previous_balance', + 'total_credit', + 'total_debit', 'type', 'balance', 'description', @@ -21,4 +23,15 @@ class LimitJournal extends Model { return $this->morphTo(); } + + public function setTotalCreditAttribute($value) + { + $this->attributes['total_credit'] = empty($value) ? 0 : $value; + } + + public function setTotalDebitAttribute($value) + { + $this->attributes['total_debit'] = empty($value) ? 0 : $value; + } + } diff --git a/app/Models/Member.php b/app/Models/Member.php index 759b47bd..dd499477 100755 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -57,7 +57,20 @@ class Member extends Model "end_no_claim", ]; - protected $appends = ['full_name']; + protected $appends = [ + 'full_name', + 'age', + 'gender_code' + ]; + + protected $hidden = [ + 'created_at', + 'updated_at', + 'deleted_at', + 'created_by', + 'updated_by', + 'deleted_by', + ]; public function claims() { @@ -105,12 +118,12 @@ class Member extends Model public function plans() { - return $this->hasManyThrough(Plan::class, MemberPlan::class, 'member_id', 'code', 'id', 'plan_id'); + return $this->hasManyThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id'); } public function currentPlan() { - return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'code', 'id', 'plan_id')->latest(); + return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id')->latest(); } public function policies() @@ -120,7 +133,21 @@ class Member extends Model public function currentPolicy() { - return $this->hasOne(MemberPolicy::class, 'member_id', 'member_id')->where('status', 'active')->latestOfMany(); + return $this->hasOneThrough(CorporatePolicy::class, MemberPolicy::class, 'member_id', 'code', 'member_id', 'policy_id') + ->where('corporate_policies.start', '<', now()) + ->where('corporate_policies.end', '>', now()) + ->where('member_policies.start', '<', now()) + ->where('member_policies.end', '>', now()); + // return $this->hasOne(MemberPolicy::class, 'member_id', 'member_id')->where('status', 'active')->latestOfMany(); + } + + public function getAgeAttribute() + { + if ($this->birth_date) { + return Carbon::parse($this->birth_date)->diffInYears(now()); + } else { + return null; + } } public function getFullNameAttribute() @@ -137,6 +164,11 @@ class Member extends Model return implode(' ', $arr); } + public function getGenderCodeAttribute() + { + return $this->gender ? ($this->gender == 'female' ? 'F' : 'M') : $this->gender; + } + public function scopeFilter($query, array $filters) { $query->when($filters['search'] ?? false, function ($query, $search) { diff --git a/database/migrations/2022_11_23_140658_create_limit_journals_table.php b/database/migrations/2022_11_23_140658_create_limit_journals_table.php index ff82510a..3682ca24 100644 --- a/database/migrations/2022_11_23_140658_create_limit_journals_table.php +++ b/database/migrations/2022_11_23_140658_create_limit_journals_table.php @@ -16,7 +16,9 @@ return new class extends Migration Schema::create('limit_journals', function (Blueprint $table) { $table->id(); $table->morphs('journalable'); - $table->string('mutation'); + $table->string('previous_balance'); + $table->string('total_debit')->nullable()->comment('Should be in positive value'); + $table->string('total_credit')->nullable()->comment('Should be in negative value'); $table->string('type'); $table->string('balance'); $table->text('description'); diff --git a/frontend/.DS_Store b/frontend/.DS_Store new file mode 100644 index 00000000..01e20269 Binary files /dev/null and b/frontend/.DS_Store differ diff --git a/frontend/client-portal/index.html b/frontend/client-portal/index.html index ee283328..4112dbb1 100755 --- a/frontend/client-portal/index.html +++ b/frontend/client-portal/index.html @@ -13,8 +13,11 @@ - + + + @@ -29,8 +32,8 @@
- + -