Update Member Import

This commit is contained in:
2022-07-24 14:30:19 +07:00
parent 0f4c84da6a
commit 42e4129a79
21 changed files with 1184 additions and 164 deletions

View 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,
]
];
}
}

View File

@@ -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']);
});
});

View 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;
}
}