Merge branch 'staging' of https://dev.sismedika.online/febio/aso into staging

This commit is contained in:
2024-02-27 13:37:45 +07:00
8 changed files with 450 additions and 38 deletions

View File

@@ -13,6 +13,11 @@ use App\Models\File;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Support\Facades\View;
use App\Models\Member;
use App\Models\RequestLog;
use App\Models\Organization;
use App\Services\ClaimRequestService;
use App\Models\ClaimRequest;
class RequestLogController extends Controller
{
@@ -20,6 +25,7 @@ class RequestLogController extends Controller
* Display a listing of the resource.
* @return Renderable
*/
private static $code_prefix = 'CLAIM';
public function requestLog(Request $request)
{
$data = [
@@ -820,4 +826,110 @@ class RequestLogController extends Controller
return response($pdf->output(), 200, $headers);
}
public function submitClaims(Request $request)
{
$data = [
'selectedRows' => $request->selectedRows,
];
$validator = Validator::make($request->all(), [
'selectedRows' => 'required'
], [
'selectedRows.required' => trans('Validation.required',['attribute' => 'Request Logs ID']),
]);
if ($validator->fails())
{
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
}
else
{
foreach ($request->selectedRows as $request_logs_id) {
$data_req_logs = DB::table('request_logs')
->where('id', '=', $request_logs_id)
->select('id', 'member_id', 'service_code')
->first();
$check_claim_requests = DB::table('claim_requests')
->where('claim_requests.request_log_id', '=', $request_logs_id)
->first();
if(!$check_claim_requests) {
try {
DB::beginTransaction();
$code = $this->getNextCode($request_logs_id);
$member = Member::find($data_req_logs->member_id);
$requestLogData = RequestLog::where('id',$request_logs_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$newClaimRequest = ClaimRequestService::storeClaimRequest(
row: [],
code: $code,
member: $member,
paymentType: 'cashless',
serviceCode: $data_req_logs->service_code,
requestLogID: $request_logs_id,
organization_code: $provideCode,
);
// Log History
$newClaimRequest->histories()->create([
'title' => 'New Claim Requested',
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'type' => 'info',
'system_origin' => 'hospital-portal'
]);
// Claim Log
DB::table('claim_logs')
->insert([
'claim_request_id' => $newClaimRequest->id,
'status' => 'requested',
'date' => date('Y-m-d H:i:s'),
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'system_origin' => 'hospital-portal',
'created_by' => auth()->user()->id,
'created_at' => date('Y-m-d H:i:s'),
'updated_at'=> date('Y-m-d H:i:s'),
]);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500);
}
}
}
return ApiResponse::apiResponse('Success', $data, trans('Message.success'), 200);
}
}
public static function getNextCode($request_log_id = 0)
{
$last_numeric_code = ClaimRequest::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code'))
->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"')
->value('max_numeric_code');
// $next_number = 1;
if ($last_numeric_code) {
// // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir
// $parts = explode('-', $last_code);
// $last_number = (int) end($parts);
$next_number = $last_numeric_code + 1;
} else {
$next_number = 1;
}
return self::makeCode($next_number, $request_log_id);
}
public static function makeCode($next_number, $request_log_id)
{
// Pastikan $next_number adalah integer positif
$next_number = max(1, (int) $next_number);
$requestLogData = RequestLog::where('id', $request_log_id)->first();
$organization = Organization::where(['id' => $requestLogData->organization_id, 'type' => 'hospital'])->first('code');
$provideCode = $organization ? $organization->code : '';
$member = Member::with('currentCorporate')->where(['id' => $requestLogData->member_id])->first();
$sparator = '.';
$date = date('ymd');
// Menghasilkan kode dengan format yang diinginkan
return self::$code_prefix . $sparator. 'H' . $sparator. $provideCode . $sparator. $date. $sparator . $member->currentPolicy->code . $sparator. $member->member_id . $sparator. str_pad($next_number, 5, '0', STR_PAD_LEFT);
return self::$code_prefix . '.' . str_pad($next_number, 6, '0', STR_PAD_LEFT);
}
}

View File

@@ -56,6 +56,7 @@ Route::prefix('v1')->group(function() {
Route::post('request-final-log', 'requestFinalLog');
Route::get('download-log/{request_log_id}', 'downlodLog');
Route::get('download-final-log/{request_log_id}', 'downlodFinalLog');
Route::post('submit-claims', 'submitClaims');
});
//Notification
Route::controller(NotificationController::class)->group(function() {

View File

@@ -50,5 +50,12 @@
"txtNew" : "New",
"txtBeforeThat" : "Before that",
"txtDischargeDate" : "Discharge Date",
"txtPatner" : "Patner"
"txtPatner" : "Patner",
"txtSelected": "Selected",
"txtConfirmation": "Confirmation",
"txtReason": "Reason Decline",
"txtCancel": "Cancel",
"txtDecline": "Decline",
"txtApprove": "Approve",
"txtDialogConfirmation": "Are you sure you want to proceed with this action?"
}

View File

@@ -50,5 +50,12 @@
"txtNew" : "Baru",
"txtBeforeThat" : "Sebelum",
"txtDischargeDate" : "Tanggal Keluar",
"txtPatner" : "Rekanan"
"txtPatner" : "Rekanan",
"txtSelected": "Terpilih",
"txtConfirmation": "Konfirmasi",
"txtReason": "Alasan Penolakan",
"txtCancel": "Batal",
"txtDecline": "Tolak",
"txtApprove": "Terima",
"txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?"
}

View File

@@ -13,6 +13,7 @@ import {
TableSortLabel,
Box,
Card,
Checkbox,
Grid,
FormControl,
InputLabel,
@@ -43,6 +44,8 @@ import { DivisionDataProps, Order, PaginationTableProps, TableListProps } from '
import { InputAdornment } from '@mui/material';
import GetAppIcon from '@mui/icons-material/GetApp';
import { LanguageContext } from '@/contexts/LanguageContext';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
@@ -71,6 +74,7 @@ export default function Table<T>({
filterEndDate,
searchs,
exportReport,
selected,
}: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -98,34 +102,73 @@ export default function Table<T>({
return (
<TableHead>
<TableRow>
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
{selected.useSelected && selected.selectedRows.length > 0 ? (
<>
<TableCell style={{ backgroundColor: '#D1F1F1', }} align="left" colSpan={selected.totRows} sx={{ padding: 2 }}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Stack direction="row" alignItems="center">
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
<Typography variant='subtitle2'>
{selected.selectedRows.length > 0 ? selected.selectedRows.length : '0'} &nbsp; {localeData.txtSelected}
</Typography>
</Stack>
</Grid>
<Grid item>
<Stack direction="row" spacing={2}>
{selected.useDecline ? (
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('decline');}}>
<Typography variant='subtitle2'>{selected.txtDecline}</Typography>
</Button>
):''}
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {selected.setOpenDialogSubmit(true);selected.setValDialog('approve');}}>
<Typography variant='subtitle2'>{selected.txtApprove}</Typography>
</Button>
</Stack>
</Grid>
</Grid>
</TableCell>
))}
</>
):(
<>
{selected.useSelected ? (
<TableCell>
<Checkbox checked={selected.selectAll} onChange={selected.handleSelectAll} />
</TableCell>
):''}
{headCells &&
headCells.map((headCell, index) => (
<TableCell
key={index}
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
// @ts-ignore
align={headCell.align}
sx={{ padding: 2 }}
width={headCell.width ? headCell.width : 'auto'}
>
{headCell.isSort ? (
<TableSortLabel
active={orders?.orderBy === headCell.id}
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orders?.orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
) : (
headCell.label
)}
</TableCell>
))}
</>
)}
</TableRow>
</TableHead>
);
@@ -162,7 +205,6 @@ export default function Table<T>({
params.setAppliedParams(parameters);
};
/* -------------------------------------------------------------------------- */
return (
// <Card>
<Grid container>
@@ -349,6 +391,20 @@ export default function Table<T>({
) : rows && rows.length >= 1 ? (
rows.map((row, rowIndex) => (
<TableRow key={rowIndex}>
{!selected.useSelected ? (
''
): (selected.useSelected && row.check_status === 'approved' && !row.check_claim ? (
<TableCell>
<Checkbox
checked={selected.selectedRows.includes(row.id)}
onChange={() => selected.handleCheckboxChange(row.id)}
/>
</TableCell>
):(
<TableCell>
</TableCell>
))}
{headCells &&
//@ts-ignore
headCells.map((head, headIndex) => (

View File

@@ -118,6 +118,58 @@ export default function TableList() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: false,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -280,6 +332,7 @@ export default function TableList() {
const response = await axios.get(`/get-claim-requests`, {
params: { ...parameters, type: 'final-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
@@ -357,6 +410,7 @@ export default function TableList() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>

View File

@@ -1,11 +1,11 @@
/* ---------------------------------- @mui ---------------------------------- */
import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from '@mui/material';
import { Stack, Button, Checkbox, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box, IconButton, TextField } from '@mui/material';
/* ---------------------------------- axios --------------------------------- */
// import axios from 'axios';
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
import { SetStateAction, useContext, useEffect, useState } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
@@ -30,6 +30,8 @@ import MuiDialog from '@/components/MuiDialog';
import DialogMember from './DialogMember';
import DialogClaimSubmit from './DialogClaimSubmit';
import { fPostFormat } from '@/utils/formatTime';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
export default function TableListFinalLog() {
const navigate = useNavigate();
@@ -68,6 +70,7 @@ export default function TableListFinalLog() {
return `${day}${month}${year}`;
}
/* -------------------------------------------------------------------------- */
/* setting up for the table */
/* -------------------------------------------------------------------------- */
@@ -128,6 +131,85 @@ export default function TableListFinalLog() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: true,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
const handleSubmitData = () => {
//approve or decline
// if (!reasonDecline && valDialog == 'decline') {
// enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
// return false;
// }
axios.post('/submit-claims', {
selectedRows: selectedRows
})
.then((response) => {
if(response.data.meta.code === 200)
{
setOpenDialogSubmit(false);
enqueueSnackbar(response.data.meta.message, {variant : "success"});
}
else{
setOpenDialogSubmit(false);
enqueueSnackbar(response.data.meta.message, {variant : "error"});
}
getData();
setSelectedRows([]);
})
.catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
})
.then(() => {
});
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -335,11 +417,14 @@ export default function TableListFinalLog() {
params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'final-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
response.data.data.map((obj: any) => ({
...obj,
provider:formatTitleCase(obj.provider),
full_name:formatTitleCase(obj.full_name),
check_status: obj.status,
check_claim: obj.check_claim,
status:
obj.status === 'requested' ? (
<Label color='primary'>
@@ -387,7 +472,7 @@ export default function TableListFinalLog() {
Download Final LOG
</MenuItem>
):''}
{!obj.check_claim && obj.status === 'approved' ? (
{!obj.check_claim && obj.status === 'requested' ? (
<MenuItem onClick={() => handleRequestClaimSubmit(obj.member_id, obj.service_code, obj.id, obj.full_name, obj.no_polis, obj.submission_date) }>
<Iconify icon="fa:file-text" />
Submit Claim
@@ -445,6 +530,7 @@ export default function TableListFinalLog() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>
@@ -466,7 +552,6 @@ export default function TableListFinalLog() {
member={dataViewClaimSubmit}
getData={getData}
onClose={(data:any, getData:any) => {
console.log('Data returned:', data);
getData();
setOpenDialogClaimSubmit(false);
}}
@@ -475,7 +560,43 @@ export default function TableListFinalLog() {
/>
}
maxWidth="sm"
/>
/>
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">{localeData.txtConfirmation}</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>{localeData.txtDialogConfirmation}</Typography>
{valDialog == "decline" ? (
<Stack direction='row' spacing={2} marginTop={2}>
<TextField
id="outlined-multiline-static"
label={localeData.txtReason}
multiline
rows={4}
defaultValue=""
onChange={handleReasonDeclineChange}
variant="outlined"
sx={{width:'100%'}}
/>
</Stack>
): ''}
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>{localeData.txtCancel}</Button>
<Button sx={{backgroundColor: (valDialog === 'decline' ? '' : '#19BBBB'), color: (valDialog === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(valDialog === 'decline' ? 'outlined' : 'contained')}>{(valDialog === "decline" ? localeData.txtDeclaine : 'Submit')}</Button>
</DialogActions>
</Dialog>
</>
);
}

View File

@@ -5,7 +5,7 @@ import { Stack, Button, MenuItem, SelectChangeEvent, Tab, Tabs, Card, Box } from
import axios from '../../utils/axios';
import { styled } from '@mui/material/styles';
/* ---------------------------------- react --------------------------------- */
import { useContext, useEffect, useState } from 'react';
import { useContext, useEffect, useState, SetStateAction } from 'react';
/* -------------------------------- component ------------------------------- */
import Iconify from '../../components/Iconify';
@@ -132,6 +132,58 @@ export default function TableList() {
/* -------------------------------------------------------------------------- */
// ----------------------------------------- handle selected ---------------------
const [selectAll, setSelectAll] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [dataTableData, setDataTableData] = useState<any>();
const handleSelectAll = () => {
setSelectAll(!selectAll);
if (!selectAll) {
const requestedIds = dataTableData?.data
.filter((row: { status: string, check_claim:any }) => row.status === 'approved' && !row.check_claim)
.map((row: { id: any }) => row.id);
setSelectedRows(requestedIds);
} else {
setSelectedRows([]);
}
};
const handleCheckboxChange = (id: any) => {
setSelectedRows(prevSelectedRows => {
const isSelected = prevSelectedRows.includes(id);
if (isSelected) {
return prevSelectedRows.filter(rowId => rowId !== id);
} else {
return [...prevSelectedRows, id];
}
});
};
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const [valDialog, setValDialog] = useState('');
const [reasonDecline, setReasonDecline] = useState('');
const handleReasonDeclineChange = (event: { target: { value: SetStateAction<string>; }; }) => {
setReasonDecline(event.target.value);
};
const selected = {
useSelected: false,
selectAll: selectAll,
handleSelectAll: handleSelectAll,
selectedRows: selectedRows,
handleCheckboxChange : handleCheckboxChange,
totRows: 9,
useDecline: false,
txtDecline: 'Decline',
useApprove:true,
txtApprove: 'Submit Claim',
setOpenDialogSubmit: setOpenDialogSubmit,
setValDialog: setValDialog
};
/* ------------------------------ handle search ----------------------------- */
const [searchText, setSearchText] = useState('');
@@ -328,6 +380,7 @@ export default function TableList() {
params: { ...parameters, search:searchText, order: order,
orderBy: orderBy, status:statusValue, type: 'request-log' },
});
setDataTableData(response.data);
setData(
response.data.data.map((obj: any) => ({
...obj,
@@ -440,6 +493,7 @@ export default function TableList() {
params={params}
searchs={searchs}
filterStatus={filterStatus}
selected={selected}
// filterStartDate={filterStartDate}
// filterEndDate={filterEndDate}
/>