Update Member Import
This commit is contained in:
188
Modules/Internal/Http/Controllers/Api/MemberController.php
Normal file
188
Modules/Internal/Http/Controllers/Api/MemberController.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Internal\Http\Controllers\Api;
|
||||
|
||||
use App\Exceptions\ImportRowException;
|
||||
use App\Models\Corporate;
|
||||
use App\Models\Member;
|
||||
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
|
||||
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
|
||||
use Box\Spout\Common\Entity\Row;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Internal\Services\MemberEnrollmentService;
|
||||
|
||||
class MemberController extends Controller
|
||||
{
|
||||
public function __construct(MemberEnrollmentService $memberEnrollmentService)
|
||||
{
|
||||
$this->memberEnrollmentService = $memberEnrollmentService;
|
||||
}
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
* @return Renderable
|
||||
*/
|
||||
public function index(Request $request, $corporate_id)
|
||||
{
|
||||
$benefits = Member::query()
|
||||
->filter($request->all())
|
||||
// ->where('corporate_id', $corporate_id)
|
||||
->paginate(20)
|
||||
->appends($request->all());
|
||||
|
||||
return $benefits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
* @return Renderable
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('internal::create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* @param Request $request
|
||||
* @return Renderable
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
* @param int $id
|
||||
* @return Renderable
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
public function import(Request $request, $corporate_id)
|
||||
{
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:xls,xlsx,csv,txt',
|
||||
]);
|
||||
$corporate = Corporate::findOrFail($corporate_id);
|
||||
|
||||
$file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName();
|
||||
$file = $request->file('file')->storeAs('temp', $file_name);
|
||||
|
||||
$reader = ReaderEntityFactory::createReaderFromFile(Storage::path('temp/'.$file_name));
|
||||
$reader->open(Storage::path('temp/'.$file_name));
|
||||
|
||||
$writer = WriterEntityFactory::createXLSXWriter();
|
||||
$writer->openToFile(Storage::disk('public')->path('temp/result-'.$file_name));
|
||||
|
||||
$headers_map_to_table_fields = Member::$doc_headers_to_field_map;
|
||||
|
||||
// Write Header to File
|
||||
$result_headers = array_keys($headers_map_to_table_fields);
|
||||
array_push($result_headers, 'Ingestion Status');
|
||||
$singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRow($result_headers));
|
||||
$writer->addRow($singleRow);
|
||||
|
||||
$imported_member_data = 0;
|
||||
$failed_member_data = [];
|
||||
foreach ($reader->getSheetIterator() as $sheet) {
|
||||
$doc_headers_indexes = [];
|
||||
foreach ($sheet->getRowIterator() as $index => $row) {
|
||||
if ($index == 1) { // First Row Must be Header
|
||||
foreach ($row->getCells() as $index => $cell) {
|
||||
// Clear up the string and remove all spaces
|
||||
$title = $cell->getValue();
|
||||
$title = preg_replace( "/\r|\n/", " ", $title );
|
||||
$title = preg_replace('/\xc2\xa0/', " ", $title );
|
||||
$title = rtrim($title);
|
||||
$title = ltrim($title);
|
||||
$doc_headers_indexes[$index] = $title;
|
||||
}
|
||||
} else { // Next Row Should be Data
|
||||
$new_member_data = [];
|
||||
foreach ($row->getCells() as $header_index => $cell) {
|
||||
if (isset($headers_map_to_table_fields[$doc_headers_indexes[$header_index]])) {
|
||||
$new_member_data[$headers_map_to_table_fields[$doc_headers_indexes[$header_index]]] = $cell->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$rowResponse = $this->memberEnrollmentService->handleImportRow($corporate, $new_member_data);
|
||||
|
||||
// Write Success Result to File
|
||||
array_push($new_member_data, 'SUCCESS');
|
||||
$singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRow($new_member_data));
|
||||
$writer->addRow($singleRow);
|
||||
$imported_member_data++;
|
||||
} catch (ImportRowException $e) {
|
||||
// Write Data Validation Error to File
|
||||
array_push($new_member_data, $e->getMessage());
|
||||
$singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRow($new_member_data));
|
||||
$writer->addRow($singleRow);
|
||||
$failed_member_data[] = ['row_number' => $index, 'error' => $e->getMessage()];
|
||||
} catch (\Exception $e) {
|
||||
// Write Server Error to File
|
||||
array_push($new_member_data, "Server Error");
|
||||
$singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRow($new_member_data));
|
||||
$writer->addRow($singleRow);
|
||||
$failed_member_data[] = ['row_number' => $index, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break; //only read first sheet
|
||||
}
|
||||
$reader->close();
|
||||
$writer->close();
|
||||
Storage::delete('temp/'.$file_name);
|
||||
// throw(404);
|
||||
|
||||
return [
|
||||
'total_success_row' => $imported_member_data,
|
||||
'total_failed_row' => count($failed_member_data),
|
||||
'failed_row' => $failed_member_data,
|
||||
'result_file' => [
|
||||
'url' => Storage::disk('public')->url('temp/result-'.$file_name),
|
||||
'name' => 'result-'.$file_name,
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use Modules\Internal\Http\Controllers\Api\CorporateBenefitController;
|
||||
use Modules\Internal\Http\Controllers\Api\CorporateController;
|
||||
use Modules\Internal\Http\Controllers\Api\CorporatePlanController;
|
||||
use Modules\Internal\Http\Controllers\Api\DivisionController;
|
||||
use Modules\Internal\Http\Controllers\Api\MemberController;
|
||||
use Modules\Internal\Http\Controllers\Api\PlanController;
|
||||
|
||||
/*
|
||||
@@ -57,5 +58,9 @@ Route::prefix('internal')->group(function () {
|
||||
Route::post('corporates/{corporate_id}/divisions', [DivisionController::class, 'store']);
|
||||
Route::get('corporates/{corporate_id}/divisions/{id}/edit', [DivisionController::class, 'edit']);
|
||||
Route::put('corporates/{corporate_id}/divisions/{id}', [DivisionController::class, 'update']);
|
||||
|
||||
Route::get('corporates/{corporate_id}/members', [MemberController::class, 'index']);
|
||||
Route::post('corporates/{corporate_id}/members/import', [MemberController::class, 'import']);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
537
Modules/Internal/Services/MemberEnrollmentService.php
Normal file
537
Modules/Internal/Services/MemberEnrollmentService.php
Normal file
@@ -0,0 +1,537 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Internal\Services;
|
||||
|
||||
use App\Exceptions\ImportRowException;
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Corporate;
|
||||
use App\Models\CorporateDivision;
|
||||
use App\Models\CorporatePlan;
|
||||
use App\Models\Member;
|
||||
use App\Models\MemberPolicy;
|
||||
use App\Models\Plan;
|
||||
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
|
||||
use Box\Spout\Common\Entity\Row;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
|
||||
class MemberEnrollmentService
|
||||
{
|
||||
public function __construct(Member $member)
|
||||
{
|
||||
$this->member = $member;
|
||||
}
|
||||
|
||||
protected function validateRow($row)
|
||||
{
|
||||
if (empty($row['record_type'])) {
|
||||
throw new ImportRowException(__('enrollment.RECORD_TYPE_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['payor_id'])) {
|
||||
throw new ImportRowException(__('enrollment.PAYOR_ID_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['member_id'])) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_ID_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($row['record_type'] == 'P') {
|
||||
if (!empty($row['principal_id'])) {
|
||||
throw new ImportRowException(__('enrollment.PRINCIPAL_ID_NOT_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['corporate_id'])) {
|
||||
throw new ImportRowException(__('enrollment.CORPORATE_ID_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
}
|
||||
|
||||
if ($row['record_type'] == 'D') {
|
||||
if (empty($row['principal_id'])) {
|
||||
throw new ImportRowException(__('enrollment.PRINCIPAL_ID_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
}
|
||||
|
||||
// TOOD RECORD BCA ONLY
|
||||
if ($row['record_type'] == 'D' && !empty($row['branch_code'])) {
|
||||
throw new ImportRowException(__('enrollment.BRANCH_CODE_NOT_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
// TODO BANK VALIDATION
|
||||
// if ($row['record_type'] == 'D' && !empty($row['branch_code'])) {
|
||||
// throw new ImportRowException(__('enrollment.BRANCH_CODE_NOT_REQUIRED'), 0, null, $row);
|
||||
// }
|
||||
|
||||
if (!empty($row['language']) && !in_array($row['language'], ['M', 'E', 'C', 'I', 'O'])) {
|
||||
throw new ImportRowException(__('enrollment.INVALID_LANGUAGE'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (!empty($row['type_of_work']) && !in_array($row['type_of_work'], ['0', '1', '2', '3'])) {
|
||||
throw new ImportRowException(__('enrollment.INVALID_TYPE_OF_WORK'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (!empty($row['race']) && !in_array($row['race'], ['M', 'C', 'I', 'O'])) {
|
||||
throw new ImportRowException(__('enrollment.INVALID_RACE'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['policy_number'])) {
|
||||
throw new ImportRowException(__('enrollment.POLICY_NUMBER_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (!empty($row['marital_status']) && !in_array($row['marital_status'], ['S', 'M', 'D'])) {
|
||||
throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['member_effective_date']) ) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EFFECTIVE_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
// TODO EFFECTIVE DATE VALIDATION
|
||||
|
||||
if (empty($row['member_expiry_date']) ) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
// TODO EFFECTIVE DATE VALIDATION
|
||||
|
||||
// TODO FKTP VALIDATION
|
||||
// TODO FKRTL VALIDATION
|
||||
|
||||
if (!empty($row['marital_status']) && !in_array($row['marital_status'], ['S', 'M', 'D'])) {
|
||||
throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['name'])) {
|
||||
throw new ImportRowException(__('enrollment.NAME_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (!empty($row['telephone_mobile'])
|
||||
&& substr($row['telephone_mobile'], 0, 4) != '+628') {
|
||||
throw new ImportRowException(__('enrollment.PHONE_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (!empty($row['email'])
|
||||
&& !filter_var($row['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new ImportRowException(__('enrollment.EMAIL_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['date_of_birth'])) {
|
||||
throw new ImportRowException(__('enrollment.DATE_OF_BIRTH_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
// TODO DOB FORMAT VALIDATION
|
||||
|
||||
if (empty($row['sex'])) {
|
||||
throw new ImportRowException(__('enrollment.SEX_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (empty($row['sex'])) {
|
||||
throw new ImportRowException(__('enrollment.SEX_REQUIRED'), 0, null, $row);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleImportRow(Corporate $corporate, $row)
|
||||
{
|
||||
try {
|
||||
$member_data = [
|
||||
"name" => $row['name'],
|
||||
"member_id" => $row['member_id'],
|
||||
"payor_id" => $row['payor_id'],
|
||||
"nik" => $row['nik'],
|
||||
"birth_date" => Carbon::parse($row['date_of_birth']),
|
||||
"gender" => Helper::genderNormalization($row['sex']),
|
||||
"language" => $row['language'],
|
||||
"race" => $row['race'],
|
||||
"marital_status" => $row['marital_status'],
|
||||
"record_type" => $row['record_type'],
|
||||
"principal_id" => $row['principal_id'],
|
||||
"relation_with_principal" => $row['relationship_with_principal'],
|
||||
"bpjs_class" => $row['bpjs_class'],
|
||||
];
|
||||
|
||||
switch ($row['record_mode']) {
|
||||
case "1": // New Member
|
||||
$member = Member::query()
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if ($member) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
} else {
|
||||
$member = new Member();
|
||||
}
|
||||
|
||||
$memberPolicy = $member->policies()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->first();
|
||||
|
||||
if ($memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
$this->validateRow($row);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$member->fill($member_data);
|
||||
if ($member->save()) {
|
||||
$memberPolicy = new MemberPolicy();
|
||||
$memberPolicy->fill([
|
||||
'member_id' => $member->id,
|
||||
'policy_id' => $row['policy_number'],
|
||||
'start' => Carbon::parse($row['member_effective_date']),
|
||||
'end' => Carbon::parse($row['member_expiry_date']),
|
||||
'status' => 'active'
|
||||
]);
|
||||
$memberPolicy->save();
|
||||
|
||||
if (!empty($row['division'])) {
|
||||
$division_id = CorporateDivision::where('code', $row['division_code'])->where('')->pluck('id');
|
||||
}
|
||||
|
||||
$member->employeds()->create([
|
||||
'corporate_id' => $corporate->id,
|
||||
'branch_code' => $row['branch_code'],
|
||||
'division_id' => $division_id ?? null,
|
||||
'nik' => $row['nik']
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
throw new ImportRowException($e->getMessage(), $e->getCode(), $e, $row);
|
||||
}
|
||||
break;
|
||||
case "2": // Member Information Update (Without Replacement Card)
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->with('member')
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy->member->fill($member_data);
|
||||
if (!$memberPolicy->member->isDirty()) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NO_CHANGE'), 0, null, $row);
|
||||
}
|
||||
|
||||
return $memberPolicy->member->save();
|
||||
|
||||
break;
|
||||
case "3": // Member Deletion
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
return $memberPolicy->delete();
|
||||
break;
|
||||
case "4": // Member Update Start and End Date
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy->fill([
|
||||
'start' => $row['start_date'],
|
||||
'end' => $row['end_date']
|
||||
]);
|
||||
|
||||
if (!$memberPolicy->isDirty()) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_NO_CHANGE'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($row['member_effective_date']) > Carbon::parse($row['end_date'])) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
return $memberPolicy->save();
|
||||
|
||||
break;
|
||||
case "5": // Member Renewal Policy (without card)
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($row['member_effective_date']) > Carbon::parse($row['member_expiry_date'])) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($memberPolicy->end) > Carbon::parse($row['member_expiry_date']
|
||||
|| $memberPolicy->end > Carbon::parse($row['member_expiry_date']))) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy->fill([
|
||||
'start' => $row['member_expiry_date'],
|
||||
'end' => $row['member_expiry_date']
|
||||
]);
|
||||
|
||||
if (!$memberPolicy->isDirty()) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_NO_CHANGE'), 0, null, $row);
|
||||
}
|
||||
|
||||
return $memberPolicy->save();
|
||||
break;
|
||||
case "6": // Member Renewal Policy (with card)
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($row['member_effective_date']) > Carbon::parse($row['member_expiry_date'])) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($memberPolicy->end) > Carbon::parse($row['member_expiry_date']
|
||||
|| $memberPolicy->end > Carbon::parse($row['member_expiry_date']))) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy->fill([
|
||||
'start' => $row['member_expiry_date'],
|
||||
'end' => $row['member_expiry_date']
|
||||
]);
|
||||
|
||||
if (!$memberPolicy->isDirty()) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_NO_CHANGE'), 0, null, $row);
|
||||
}
|
||||
|
||||
return $memberPolicy->save();
|
||||
break;
|
||||
case "7": // Requesting to Change the Unique Data of the Member
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->with('member')
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
// TODO OPTION MODE FORMAT VALIDATION
|
||||
if (empty($row["option_mode"])) {
|
||||
throw new ImportRowException(__('enrollment.OPTION_MODE_INVALID_FORMAT', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if (
|
||||
$row['record_type'] == 'P' &&
|
||||
$memberPolicy->member->corporate->code == $row["corporate_id"] &&
|
||||
$memberPolicy->policy_id == $row['policy_number'] &&
|
||||
$memberPolicy->member_id == $row['member_id'] &&
|
||||
$memberPolicy->member->record_type == $row['record_type']
|
||||
) {
|
||||
throw new ImportRowException(__('enrollment.UNIQUE_CHANGE_PRINCIPAL_INVALID', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if (
|
||||
$row['record_type'] == 'D' &&
|
||||
$memberPolicy->member->corporate->code == $row["corporate_id"] &&
|
||||
$memberPolicy->policy_id == $row['policy_number'] &&
|
||||
$memberPolicy->member->record_type == $row['record_type'] &&
|
||||
$memberPolicy->member->payor_id == $row['payor_id']
|
||||
) {
|
||||
throw new ImportRowException(__('enrollment.UNIQUE_CHANGE_DEPENDANT_INVALID', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
throw new ImportRowException(__('MODE 7 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
case "8": // Member Information Update (With Replacement Card)
|
||||
|
||||
break;
|
||||
case "9": // Member Reactivation and Personal information update (Without replacement Card)
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($row['member_effective_date']) < now() || Carbon::parse($row['member_expiry_date']) < now()) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_MUST_BE_AFTER_TODAY'), 0, null, $row);
|
||||
}
|
||||
|
||||
if (Carbon::parse($row['member_effective_date']) > Carbon::parse($row['member_expiry_date'])) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy->fill([
|
||||
'start' => $row['member_effective_date'],
|
||||
'end' => $row['member_expiry_date']
|
||||
]);
|
||||
|
||||
return $memberPolicy->save();
|
||||
break;
|
||||
// case "10": // No Information Available
|
||||
|
||||
// break;
|
||||
case "11": // Advance Renewal with OLD Card No. (PRINT)
|
||||
|
||||
throw new ImportRowException(__('MODE 11 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
case "12": // Advance Renewal iwh NEW Card No. (PRINT)
|
||||
|
||||
throw new ImportRowException(__('MODE 12 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
case "13": // Advance Renewal with OLD Card No. (NO PRINT)
|
||||
|
||||
throw new ImportRowException(__('MODE 13 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
// case "14": // No Information Available
|
||||
|
||||
// break;
|
||||
case "15": // Lost Card / Change Card with new card number (Print) (Rarely Used)
|
||||
|
||||
throw new ImportRowException(__('MODE 15 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
case "16": // Endorsement Plan OLD Card No. (NO PRINT)
|
||||
$plan = CorporatePlan::query()
|
||||
->where('corporate_id', $corporate->id)
|
||||
->where('code', $row['plan_id'])
|
||||
->where('active', true)
|
||||
->first();
|
||||
if (!$plan) {
|
||||
throw new ImportRowException(__('enrollment.PLAN_NOT_FOUND'), 0, null, $row);
|
||||
}
|
||||
|
||||
$memberPolicy = MemberPolicy::query()
|
||||
->where('policy_id', $row['policy_number'])
|
||||
->where('member_id', $row['member_id'])
|
||||
->first();
|
||||
|
||||
if (!$memberPolicy) {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
if ($memberPolicy->status != 'active') {
|
||||
throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [
|
||||
'member_id' => $row['member_id'],
|
||||
'policy_id' => $row['policy_number']
|
||||
]), 0, null, $row);
|
||||
}
|
||||
|
||||
throw new ImportRowException(__('MODE 16 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
case "17": // Endorsement Plan OLD Card No. (PRINT)
|
||||
|
||||
throw new ImportRowException(__('MODE 17 NOT HANDLED PROPERLY'), 0, null, $row);
|
||||
break;
|
||||
default:
|
||||
throw new ImportRowException(__("enrollment.MODE_UNAVAILABLE"), 0, null, $row);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
throw new ImportRowException($e->getMessage(), (int) $e->getCode(), $e, $row);
|
||||
}
|
||||
}
|
||||
|
||||
public function makeResultRow($row_data)
|
||||
{
|
||||
$cells = [];
|
||||
foreach ($row_data as $cellValue) {
|
||||
$cells[] = WriterEntityFactory::createCell($cellValue);
|
||||
}
|
||||
|
||||
return $cells;
|
||||
}
|
||||
}
|
||||
22
app/Exceptions/ImportRowException.php
Normal file
22
app/Exceptions/ImportRowException.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class ImportRowException extends Exception
|
||||
{
|
||||
protected $data = null;
|
||||
|
||||
public function __construct($message, $code = 0, Throwable $previous = null, $data)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
20
app/Helpers/Helper.php
Normal file
20
app/Helpers/Helper.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class Helper{
|
||||
public static function genderNormalization($anyGenderCode)
|
||||
{
|
||||
if ($anyGenderCode == 'M') {
|
||||
return 'male';
|
||||
} else if ($anyGenderCode == 'F') {
|
||||
return 'female';
|
||||
} else if ($anyGenderCode == 'O') {
|
||||
return 'others';
|
||||
} else if ($anyGenderCode == 'U') {
|
||||
return 'unknown';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,4 +42,19 @@ class Corporate extends Model
|
||||
->where('active', true)
|
||||
->latestOfMany();
|
||||
}
|
||||
|
||||
public function corporatePlans()
|
||||
{
|
||||
return $this->hasMany(CorporatePlan::class, 'corporate_id');
|
||||
}
|
||||
|
||||
public function corporateBenefits()
|
||||
{
|
||||
return $this->hasMany(CorporateBenefit::class, 'corporate_id');
|
||||
}
|
||||
|
||||
public function corporateDivisions()
|
||||
{
|
||||
return $this->hasMany(CorporateDivision::class, 'corporate_id');
|
||||
}
|
||||
}
|
||||
|
||||
20
app/Models/CorporateEmployee.php
Normal file
20
app/Models/CorporateEmployee.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CorporateEmployee extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'corporate_id',
|
||||
'member_id',
|
||||
'branch_code',
|
||||
'division_id',
|
||||
'nik',
|
||||
'status'
|
||||
];
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CorporateEmployees extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
@@ -8,4 +8,118 @@ use Illuminate\Database\Eloquent\Model;
|
||||
class Member extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
"id",
|
||||
"member_id",
|
||||
"record_type",
|
||||
"payor_id",
|
||||
"user_id",
|
||||
"name_prefix",
|
||||
"name",
|
||||
"name_suffix",
|
||||
"birth_date",
|
||||
"gender",
|
||||
"language",
|
||||
"race",
|
||||
"marital_status",
|
||||
"principal_id",
|
||||
"relation_with_principal",
|
||||
"bpjs_class",
|
||||
"active",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"deleted_by"
|
||||
];
|
||||
|
||||
public static $doc_headers_to_field_map = [
|
||||
"Record Mode" => "record_mode",
|
||||
"Record Type" => "record_type",
|
||||
"Payor ID" => "payor_id",
|
||||
"Member ID" => "member_id",
|
||||
"Mapping ID" => "principal_id",
|
||||
"Halodoc Member ID" => "halodoc_member_id",
|
||||
"Corporate ID" => "corporate_id",
|
||||
"NIK" => "nik",
|
||||
"Division" => "division_code",
|
||||
"Branch Code" => "branch_code",
|
||||
"Bank Info" => "banks_info",
|
||||
"Language" => "language",
|
||||
"Type of work" => "type_of_work",
|
||||
"Race" => "race",
|
||||
"Policy Number" => "policy_number",
|
||||
"Marital Status" => "marital_status",
|
||||
"Relationship" => "relationship_with_principal",
|
||||
"Member's Effective Date" => "member_effective_date",
|
||||
"Member's Expiry Date" => "member_expiry_date",
|
||||
"Faskes FKTP (First Level Provider) or Individual preferred provider" => "faskes_fktp",
|
||||
"Faskes FKRTL (Next Level Provider) or Individual group preferred provider" => "faskes_fkrtl",
|
||||
"The Right Classes Room of BPJS Participants" => "bpjs_class",
|
||||
"Name of Faskes" => "faskes_name",
|
||||
"Rule_BPJSK ('Y' or 'N')" => "bpjsk",
|
||||
"Agent Code / intermediary code" => "agent_code",
|
||||
"Member Name" => "name",
|
||||
"Address1" => "address1",
|
||||
"Address2" => "address2",
|
||||
"Address3" => "address3",
|
||||
"Address4" => "address4",
|
||||
"City" => "city",
|
||||
"State" => "state",
|
||||
"Post Code" => "post_code",
|
||||
"Telephone – Mobile" => "telephone_mobile",
|
||||
"Telephone – Res" => "telephone_res",
|
||||
"Telephone – Office" => "telephone_office",
|
||||
"NRIC" => "nric",
|
||||
"Passport No" => "passport_number",
|
||||
"Passport Country" => "passport_country",
|
||||
"Email" => "email",
|
||||
"Identification Code" => "identification_code",
|
||||
"Date of Birth" => "date_of_birth",
|
||||
"Sex" => "sex",
|
||||
"Internal Use" => "internal_use",
|
||||
"Plan-ID" => "plan_id",
|
||||
"Employment-Status" => "employment_status",
|
||||
"Internal Use" => "internal_use_1",
|
||||
"Internal Use" => "internal_use_2",
|
||||
"Internal Use" => "internal_use_3",
|
||||
"Date Terminated" => "date_terminated",
|
||||
"Pre Existing" => "pre_existing",
|
||||
"BPJS ID" => "bpjs_id",
|
||||
"Endorsement Date" => "endorsement_date",
|
||||
"Remarks" => "remarks",
|
||||
"Internal Use" => "internal_use_4",
|
||||
"Member Since" => "member_since",
|
||||
"Internal Use" => "internal_use_5",
|
||||
"Policy In Force" => "policy_in_force",
|
||||
"Member Suspended" => "member_suspended",
|
||||
"Activation Date" => "activation_date",
|
||||
"Internal Use" => "internal_use_6",
|
||||
"StartNoClaim" => "start_no_claim",
|
||||
"EndNoClaim" => "end_no_claim",
|
||||
"Option Mode" => "option_mode",
|
||||
];
|
||||
|
||||
public function employeds()
|
||||
{
|
||||
return $this->hasMany(CorporateEmployee::class, 'member_id');
|
||||
}
|
||||
|
||||
public function policies()
|
||||
{
|
||||
return $this->hasMany(MemberPolicy::class, 'member_id');
|
||||
}
|
||||
|
||||
public function scopeFilter($query, array $filters)
|
||||
{
|
||||
$query->when($filters['search'] ?? false, function ($query, $search) {
|
||||
return $query
|
||||
->where('member_id', 'like', "%" . $search . "%")
|
||||
->orWhere('payor_id', 'like', "%" . $search . "%")
|
||||
->orWhere('name', 'like', "%" . $search . "%")
|
||||
;
|
||||
// ->orWhereHas('corporatePlan', function ($query) use ($search) {
|
||||
// $query->where('code', 'like', "%" . $search . "%");
|
||||
// });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
31
app/Models/MemberPolicy.php
Normal file
31
app/Models/MemberPolicy.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Blameable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class MemberPolicy extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, Blameable;
|
||||
|
||||
protected $fillable = [
|
||||
'policy_id',
|
||||
'member_id',
|
||||
'status',
|
||||
'start',
|
||||
'end'
|
||||
];
|
||||
|
||||
public function policy()
|
||||
{
|
||||
return $this->belongsTo(Policy::class, 'policy_id', 'policy_id');
|
||||
}
|
||||
|
||||
public function member()
|
||||
{
|
||||
return $this->belongsTo(Member::class, 'member_id', 'member_id');
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ return new class extends Migration
|
||||
Schema::create('members', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('member_id')->nullable()->unique();
|
||||
$table->string('payor_id')->nullable();
|
||||
$table->string('name_prefix')->nullable();
|
||||
$table->string('name');
|
||||
$table->string('name_suffix')->nullable();
|
||||
@@ -25,9 +27,10 @@ return new class extends Migration
|
||||
$table->string('language')->nullable();
|
||||
$table->string('race')->nullable();
|
||||
$table->string('marital_status')->nullable();
|
||||
$table->foreignId('principle_id')->nullable()->index();
|
||||
$table->string('relation_with_principle')->nullable();
|
||||
$table->date('bpjs_class')->nullable();
|
||||
$table->string('record_type', 1)->nullable();
|
||||
$table->string('principal_id')->nullable()->index();
|
||||
$table->string('relation_with_principal')->nullable();
|
||||
$table->string('bpjs_class')->nullable();
|
||||
$table->boolean('active')->default(true);
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
@@ -15,7 +15,19 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('corporate_employees', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('corporate_id')->index();
|
||||
$table->foreignId('member_id')->index();
|
||||
$table->string('branch_code')->nullable()->index();
|
||||
$table->foreignId('division_id')->nullable()->index();
|
||||
$table->string('nik')->nullable()->index();
|
||||
$table->string('status')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->unsignedBigInteger('deleted_by')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ return new class extends Migration
|
||||
Schema::create('corporate_policies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('corporate_id');
|
||||
$table->string('code')->unique();
|
||||
$table->string('policy_id')->unique();
|
||||
$table->decimal('total_premi', 15, 2)->nullable();
|
||||
$table->decimal('minimal_deposit_percentage', 15, 2)->nullable();
|
||||
$table->decimal('minimal_deposit_net', 15, 2)->nullable();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('member_policies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('policy_id');
|
||||
$table->string('member_id');
|
||||
$table->string('status')->default('active');
|
||||
$table->dateTime('start')->nullable();
|
||||
$table->dateTime('end')->nullalbe();
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->unsignedBigInteger('deleted_by')->nullable();
|
||||
|
||||
$table->index(['policy_id', 'member_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('member_policies');
|
||||
}
|
||||
};
|
||||
@@ -15,5 +15,10 @@ class DatabaseSeeder extends Seeder
|
||||
public function run()
|
||||
{
|
||||
// \App\Models\User::factory(10)->create();
|
||||
|
||||
$this->call([
|
||||
DummyMemberSeeder::class,
|
||||
DummyCorprateSeeder::class
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
49
database/seeders/DummyCorporateSeeder.php
Normal file
49
database/seeders/DummyCorporateSeeder.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Corporate;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DummyCorporateSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Corporate::truncate();
|
||||
|
||||
$corporate = Corporate::create([
|
||||
'code' => 'UNTR0001',
|
||||
'name' => 'United Tractor',
|
||||
'welcome_message' => 'Welcome to United Tractor',
|
||||
'help_text' => 'You can blah blah blah blah',
|
||||
'active' => true
|
||||
]);
|
||||
|
||||
$corporate->corporatePlans()->create([
|
||||
'code' => 'PLAN001',
|
||||
'name' => 'PLAN Name',
|
||||
'description' => 'Description of PLAN Name',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$corporate->corporateBenefits()->create([
|
||||
'code' => 'BENEFIT001',
|
||||
'name' => 'BENEFIT Name',
|
||||
'description' => 'Description of BENEFIT Name',
|
||||
'active' => true,
|
||||
]);
|
||||
|
||||
$corporate->corporateDivisions()->create([
|
||||
'code' => 'DIVISION001',
|
||||
'name' => 'DIVISION Name',
|
||||
'description' => 'Description of DIVISION Name',
|
||||
'active' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,21 @@
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export type Member = {
|
||||
|
||||
id: string,
|
||||
member_id: string,
|
||||
record_type: string,
|
||||
payor_id: string,
|
||||
user_id: string,
|
||||
name_prefix: string,
|
||||
name: string,
|
||||
name_suffix: string,
|
||||
birth_date: string,
|
||||
gender: string,
|
||||
language: string,
|
||||
race: string,
|
||||
marital_status: string,
|
||||
principal_id: string,
|
||||
relation_with_principal: string,
|
||||
bpjs_class: string,
|
||||
active: string,
|
||||
};
|
||||
|
||||
@@ -151,15 +151,15 @@ export default function PlanCreate() {
|
||||
},
|
||||
{
|
||||
name: 'Corporate Name',
|
||||
href: '/corporates/'+id,
|
||||
href: '/corporates/'+corporate_id,
|
||||
},
|
||||
{
|
||||
name: 'Plans',
|
||||
href: '/corporates/'+id+'/plans',
|
||||
href: '/corporates/'+corporate_id+'/plans',
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
href: '/corporates/'+id+'/plans/create',
|
||||
href: '/corporates/'+corporate_id+'/plans/create',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -34,20 +34,10 @@ export default function Divisions() {
|
||||
]}
|
||||
/>
|
||||
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={8}>
|
||||
<Card>
|
||||
<CorporateTabNavigations position={'members'} />
|
||||
<DivisionsList />
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Card sx={{ p: 2 }}>
|
||||
Corporate Detail Goes Here
|
||||
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Card>
|
||||
<CorporateTabNavigations position={'members'} />
|
||||
<DivisionsList />
|
||||
</Card>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import axios from '../../../utils/axios';
|
||||
import { Plan } from '../../../@types/corporates';
|
||||
import { LaravelPaginatedData } from '../../../@types/paginated-data';
|
||||
import { Member } from '../../../@types/member';
|
||||
|
||||
export default function CorporatePlanList() {
|
||||
const { themeStretch } = useSettings();
|
||||
const { corporate_id } = useParams();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [importResult, setImportResult] = useState(null)
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
@@ -90,13 +92,16 @@ export default function CorporatePlanList() {
|
||||
formData.append("file", importPlan.current?.files[0])
|
||||
axios.post(`corporates/${corporate_id}/members/import`, formData )
|
||||
.then(response => {
|
||||
setImportResult(response.data)
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
|
||||
})
|
||||
.catch(response => {
|
||||
alert('Looks like something went wrong. Please check your data and try again. ' + response.message)
|
||||
})
|
||||
.then(x => {
|
||||
console.log('motherfucker', importResult)
|
||||
})
|
||||
} else {
|
||||
alert('No File Selected')
|
||||
}
|
||||
@@ -150,6 +155,11 @@ export default function CorporatePlanList() {
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
{( importResult &&
|
||||
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
|
||||
<Box sx={{ color: "text.secondary" }}>Last Import Result : <Box sx={{ color: "success.main", display: "inline" }}>{ importResult.total_success_row ?? 0 }</Box> Row Processed, <Box sx={{ color: "error.main", display: "inline" }}>{ importResult.total_failed_row }</Box> Failed, Report : <a href={importResult.result_file?.url ?? "#"}>{importResult.result_file?.name ?? "-"}</a></Box>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -161,6 +171,20 @@ export default function CorporatePlanList() {
|
||||
}
|
||||
}
|
||||
|
||||
const [columns, setColumns] = React.useState([
|
||||
{ id: 'member_id', label: 'MemberID', minWidth: 100, align: "left" },
|
||||
{ id: 'principal_id', label: 'Mapping ID', minWidth: 100, align: "left" },
|
||||
{ id: 'nik', label: 'NIK', minWidth: 100, align: "left" },
|
||||
{ id: 'policy_number', label: 'Policy Number', minWidth: 100, align: "left" },
|
||||
{ id: 'effective_date', label: 'Effective Date', minWidth: 100, align: "left" },
|
||||
{ id: 'name', label: 'Name', minWidth: 100, align: "left" },
|
||||
{ id: 'nric', label: 'NRIC', minWidth: 100, align: "left" },
|
||||
{ id: 'email', label: 'E-mail', minWidth: 100, align: "left" },
|
||||
{ id: 'plan_id', label: 'PlanID', minWidth: 100, align: "left" },
|
||||
{ id: 'termination_date', label: 'Termination Date', minWidth: 100, align: 'right' },
|
||||
{ id: 'activation_date', label: 'Activation Date', minWidth: 100, align: "right" },
|
||||
]);
|
||||
|
||||
// Generate the every row of the table
|
||||
function Row(props: { row: ReturnType<typeof createData> }) {
|
||||
const { row } = props;
|
||||
@@ -178,54 +202,9 @@ export default function CorporatePlanList() {
|
||||
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<TableCell align="left">{row.service_code}</TableCell>
|
||||
<TableCell align="left">{row.corporate_plan?.code}</TableCell>
|
||||
<TableCell align="left">{row.code}</TableCell>
|
||||
<TableCell align="left">{row.type}</TableCell>
|
||||
<TableCell align="left">{row.start}</TableCell>
|
||||
<TableCell align="left">{row.end}</TableCell>
|
||||
<TableCell align="left">{row.require_referral}</TableCell>
|
||||
<TableCell align="left">{row.referral_source}</TableCell>
|
||||
<TableCell align="left">{row.referral_duration}</TableCell>
|
||||
<TableCell align="left">{row.family_plan}</TableCell>
|
||||
<TableCell align="left">{row.family_plan_share_rules}</TableCell>
|
||||
<TableCell align="left">{row.limit_rules}</TableCell>
|
||||
<TableCell align="left">{row.layer}</TableCell>
|
||||
<TableCell align="left">{row.layer_conditions}</TableCell>
|
||||
<TableCell align="left">{row.budget_type}</TableCell>
|
||||
<TableCell align="left">{row.budget_code}</TableCell>
|
||||
<TableCell align="left">{row.budget_conditions}</TableCell>
|
||||
<TableCell align="left">{row.surgery_limit}</TableCell>
|
||||
<TableCell align="left">{row.non_surgery_limit}</TableCell>
|
||||
<TableCell align="left">{row.max_claim_limit}</TableCell>
|
||||
<TableCell align="left">{row.max_claim_count}</TableCell>
|
||||
<TableCell align="left">{row.area_limit}</TableCell>
|
||||
<TableCell align="left">{row.limit_shared_plans}</TableCell>
|
||||
<TableCell align="left">{row.limit_shared_plan_type}</TableCell>
|
||||
<TableCell align="left">{row.cashless_percentage}</TableCell>
|
||||
<TableCell align="left">{row.reimbursement_percentage}</TableCell>
|
||||
<TableCell align="left">{row.digital_percentage}</TableCell>
|
||||
<TableCell align="left">{row.co_share_m_percentage}</TableCell>
|
||||
<TableCell align="left">{row.co_share_s_percentage}</TableCell>
|
||||
<TableCell align="left">{row.co_share_c_percentage}</TableCell>
|
||||
<TableCell align="left">{row.cashless_deductible}</TableCell>
|
||||
<TableCell align="left">{row.reimbursement_deductible}</TableCell>
|
||||
<TableCell align="left">{row.digital_deductible}</TableCell>
|
||||
<TableCell align="left">{row.co_share_m_deductible}</TableCell>
|
||||
<TableCell align="left">{row.co_share_s_deductible}</TableCell>
|
||||
<TableCell align="left">{row.co_share_c_deductible}</TableCell>
|
||||
<TableCell align="left">{row.co_share_deductible_condition}</TableCell>
|
||||
<TableCell align="left">{row.msc}</TableCell>
|
||||
<TableCell align="left">{row.genders}</TableCell>
|
||||
<TableCell align="left">{row.min_age}</TableCell>
|
||||
<TableCell align="left">{row.max_age}</TableCell>
|
||||
<TableCell align="left">{row.rule_of_excess}</TableCell>
|
||||
<TableCell align="left">{row.max_excess_covered}</TableCell>
|
||||
<TableCell align="left">{row.prorate_type}</TableCell>
|
||||
<TableCell align="left">{row.prorate_lookup}</TableCell>
|
||||
<TableCell align="left">{row.currency}</TableCell>
|
||||
<TableCell align="left">{row.max_surgery_reinstatement_days}</TableCell>
|
||||
<TableCell align="left">{row.max_surgery_periode_days}</TableCell>
|
||||
{ columns.map((column, index) =>
|
||||
<TableCell key={ index } align={ column.align } minwidth={ column.minWidth }>{ row[column.id] ?? '-' }</TableCell>
|
||||
) }
|
||||
<TableCell align="right"><Button variant="outlined" color="success" size="small">Active</Button></TableCell>
|
||||
<TableCell align="right"><Button variant="outlined" color="success" size="small">Edit</Button></TableCell>
|
||||
</TableRow>
|
||||
@@ -238,37 +217,7 @@ export default function CorporatePlanList() {
|
||||
No Extra Data
|
||||
</Typography>
|
||||
</Box>
|
||||
{false && <Box sx={{ margin: 1 }}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Rules
|
||||
</Typography>
|
||||
<Table size="small" aria-label="purchases">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Date</TableCell>
|
||||
<TableCell>Customer</TableCell>
|
||||
<TableCell align="right">Amount</TableCell>
|
||||
<TableCell align="right">Total price ($)</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{/* {row.history ? row.history.map((historyRow) => ( */}
|
||||
<TableRow key={row.id}>
|
||||
<TableCell component="th" scope="row">{row.start} - {row.end}</TableCell>
|
||||
<TableCell>{row.start}</TableCell>
|
||||
<TableCell align="right">{row.start}</TableCell>
|
||||
<TableCell align="right">{row.start}</TableCell>
|
||||
</TableRow>
|
||||
{/* ))
|
||||
: (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8}>No Data</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
} */}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>}
|
||||
{false && <Box sx={{ margin: 1 }} />}
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -296,7 +245,7 @@ export default function CorporatePlanList() {
|
||||
const loadDataTableData = async (appliedFilter = null) => {
|
||||
setDataTableLoading(true);
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/corporates/'+corporate_id+'/plans', { params: filter });
|
||||
const response = await axios.get('/corporates/'+corporate_id+'/members', { params: filter });
|
||||
// console.log(response.data);
|
||||
setDataTableLoading(false);
|
||||
|
||||
@@ -327,54 +276,9 @@ export default function CorporatePlanList() {
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell style={headStyle} align="left" />
|
||||
<TableCell style={headStyle} align="left">Service</TableCell>
|
||||
<TableCell style={headStyle} align="left">Plan</TableCell>
|
||||
<TableCell style={headStyle} align="left">Code</TableCell>
|
||||
<TableCell style={headStyle} align="left">Type</TableCell>
|
||||
<TableCell style={headStyle} align="left">Start</TableCell>
|
||||
<TableCell style={headStyle} align="left">End</TableCell>
|
||||
<TableCell style={headStyle} align="left">Referral</TableCell>
|
||||
<TableCell style={headStyle} align="left">Referral Source</TableCell>
|
||||
<TableCell style={headStyle} align="left">Referral Duration</TableCell>
|
||||
<TableCell style={headStyle} align="left">Family Plan</TableCell>
|
||||
<TableCell style={headStyle} align="left">Family Sharing Overflow</TableCell>
|
||||
<TableCell style={headStyle} align="left">Plan Limit</TableCell>
|
||||
<TableCell style={headStyle} align="left">Layer ID</TableCell>
|
||||
<TableCell style={headStyle} align="left">Layer Condition</TableCell>
|
||||
<TableCell style={headStyle} align="left">Budget Type</TableCell>
|
||||
<TableCell style={headStyle} align="left">Budget Code</TableCell>
|
||||
<TableCell style={headStyle} align="left">Budget Condition</TableCell>
|
||||
<TableCell style={headStyle} align="left">Surgery</TableCell>
|
||||
<TableCell style={headStyle} align="left">Non Surgery</TableCell>
|
||||
<TableCell style={headStyle} align="left">Max/Claim</TableCell>
|
||||
<TableCell style={headStyle} align="left">Max Count of Claim</TableCell>
|
||||
<TableCell style={headStyle} align="left">Area</TableCell>
|
||||
<TableCell style={headStyle} align="left">Shared Plan</TableCell>
|
||||
<TableCell style={headStyle} align="left">Limit Shared Type</TableCell>
|
||||
<TableCell style={headStyle} align="left">Cashless(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">Reimbursement(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">Digital(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">CoShare M(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">CoShare S(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">CoShare C(%)</TableCell>
|
||||
<TableCell style={headStyle} align="left">Cashless Deductible</TableCell>
|
||||
<TableCell style={headStyle} align="left">Reimbursement Deductible</TableCell>
|
||||
<TableCell style={headStyle} align="left">Digital Deductible</TableCell>
|
||||
<TableCell style={headStyle} align="left">DeductibleM</TableCell>
|
||||
<TableCell style={headStyle} align="left">DeductibleS</TableCell>
|
||||
<TableCell style={headStyle} align="left">DeductibleC</TableCell>
|
||||
<TableCell style={headStyle} align="left">CoShare & Deductible Condition</TableCell>
|
||||
<TableCell style={headStyle} align="left">MSC</TableCell>
|
||||
<TableCell style={headStyle} align="left">Gender</TableCell>
|
||||
<TableCell style={headStyle} align="left">Min Age</TableCell>
|
||||
<TableCell style={headStyle} align="left">Max Age</TableCell>
|
||||
<TableCell style={headStyle} align="left">Rule of Excess</TableCell>
|
||||
<TableCell style={headStyle} align="left">Max Excess Covered</TableCell>
|
||||
<TableCell style={headStyle} align="left">Prorate Type</TableCell>
|
||||
<TableCell style={headStyle} align="left">Prorate Lookup</TableCell>
|
||||
<TableCell style={headStyle} align="left">Currency</TableCell>
|
||||
<TableCell style={headStyle} align="left">Reinstatement Surgery</TableCell>
|
||||
<TableCell style={headStyle} align="left">Period of Surgery</TableCell>
|
||||
{ columns.map((column, index) => (
|
||||
<TableCell style={headStyle} key={index} align={column.align}>{column.label}</TableCell>
|
||||
)) }
|
||||
<TableCell style={headStyle} align="right">Status</TableCell>
|
||||
<TableCell style={headStyle} align="right">Action</TableCell>
|
||||
</TableRow>
|
||||
@@ -396,8 +300,8 @@ export default function CorporatePlanList() {
|
||||
</TableBody>
|
||||
) : (
|
||||
<TableBody>
|
||||
{dataTableData.data.map(row => (
|
||||
<Row key={row.code} row={row} />
|
||||
{dataTableData.data.map((row, index) => (
|
||||
<Row key={index} row={row} />
|
||||
))}
|
||||
</TableBody>
|
||||
)
|
||||
|
||||
56
lang/en/enrollment.php
Normal file
56
lang/en/enrollment.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enrollment Import Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during import enrollment for various
|
||||
|
|
||||
*/
|
||||
"RECORD_MODE_REQUIRED" => "Record mode must be filled",
|
||||
"MODE_UNAVAILABLE" => "Record mode for member is not available",
|
||||
"RECORD_TYPE_REQUIRED" => "Record Type must be filled for member (Member ID)",
|
||||
"MEMBER_EXISTS" => "Member (:member_id) for policy (:policy_id) already exist in database",
|
||||
"MEMBER_NOT_EXISTS" => "Member (Member ID) for policy (Policy No) not found",
|
||||
"MEMBER_INACTIVE" => "Member (Member ID) for policy (Policy No) is inactive",
|
||||
"MEMBER_NO_CHANGE" => "No changes in plan/ personal info found",
|
||||
"MEMBER_EXPIRY_DATE_NO_CHANGE" => "No changes in member effective/ expiry date found",
|
||||
"MEMBER_EXPIRY_DATE_INVALID" => "Member Effective Date must be before or equal to Member Expiry Date",
|
||||
"MEMBER_RENEWAL_STILL_ACTIVE" => "Policy period is still active, please use mode 11/12/13",
|
||||
"OPTION_MODE_INVALID_FORMAT" => "Option Mode must follow delimited format",
|
||||
"UNIQUE_CHANGE_PRINCIPAL_INVALID" => "No changes in the following info found:
|
||||
> Corporate Code (Field 7)
|
||||
> Policy No (Field 15)
|
||||
> Member ID (Field 4)
|
||||
> Record Type (Field 2)",
|
||||
"UNIQUE_CHANGE_DEPENDANT_INVALID" => "No changes in the following info found:
|
||||
> Corporate Code (Field 7)
|
||||
> Policy No (Field 15)
|
||||
> Record Type (Field 2)
|
||||
> Payor ID (Field 3)",
|
||||
"MEMBER_EXPIRY_MUST_BE_AFTER_TODAY" => "Valid if Activation Date is later than member effective date, not empty and in YYYYMMDD format",
|
||||
"PLAN_NOT_FOUND" => "Plan ID inactive / not found in the system",
|
||||
|
||||
"PAYOR_ID_REQUIRED" => "Payor ID must be filled for member (Member ID)",
|
||||
"MEMBER_ID_REQUIRED" => "Member ID must be filled",
|
||||
"PRINCIPAL_ID_NOT_REQUIRED" => "Mapping ID should only be filled for dependents",
|
||||
"CORPORATE_ID_REQUIRED" => "Corporate ID must be filled",
|
||||
"PRINCIPAL_ID_REQUIRED" => "Mapping ID must be filled",
|
||||
"BRANCH_CODE_NOT_REQUIRED" => "Dependents don't need to fill in Branch Code ",
|
||||
"INVALID_LANGUAGE" => "Language (field 12) is invalid",
|
||||
"INVALID_TYPE_OF_WORK" => "Type of work (field 13) is invalid",
|
||||
"INVALID_RACE" => "Race (field 14) is invalid",
|
||||
"POLICY_NUMBER_REQUIRED" => "Policy Number must be filled for member (Member ID)",
|
||||
"MEMBER_EFFECTIVE_REQUIRED" => "Member's Effective Date must be filled for member (Member ID)",
|
||||
"MEMBER_EXPIRY_REQUIRED" => "Member's Expiry Date must be filled for member (Member ID)",
|
||||
"INVALID_MARITAL_STATUS" => "Marital Status (field 16) is invalid",
|
||||
"NAME_REQUIRED" => "Member Name must be filled",
|
||||
"PHONE_INVALID" => "Telephone - Mobile must follow +628 format",
|
||||
"EMAIL_INVALID" => "Email must folllow email format e.g. xx@gmail.com",
|
||||
"DATE_OF_BIRTH_REQUIRED" => "Date of Birth must be filled",
|
||||
"SEX_REQUIRED" => "Sex must be filled",
|
||||
|
||||
];
|
||||
Reference in New Issue
Block a user