diff --git a/Modules/Internal/Http/Controllers/Api/AuditTrailController.php b/Modules/Internal/Http/Controllers/Api/AuditTrailController.php index dd73e6b1..f6c3477b 100644 --- a/Modules/Internal/Http/Controllers/Api/AuditTrailController.php +++ b/Modules/Internal/Http/Controllers/Api/AuditTrailController.php @@ -4,6 +4,7 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Helpers\Helper; use App\Models\AuditTrail; +use App\Models\ExclusionRules; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; @@ -16,6 +17,10 @@ class AuditTrailController extends Controller { */ public function index(Request $request, $id) { + $exlusionId = null; + if ($request->model == 'App\Models\ExclusionRules'){ + $exlusionId = ExclusionRules::where('exclusion_id', $id)->get(); + } $audittrails = AuditTrail::query() ->where('model', '=', $request->model) ->where('model_id', '=', $id) diff --git a/Modules/Internal/Services/ExclusionService.php b/Modules/Internal/Services/ExclusionService.php index 984d631b..524850fb 100644 --- a/Modules/Internal/Services/ExclusionService.php +++ b/Modules/Internal/Services/ExclusionService.php @@ -6,6 +6,7 @@ use App\Exceptions\ImportRowException; use App\Models\Benefit; use App\Models\Corporate; use App\Models\Icd; +use App\Models\Exclusion; use App\Models\Plan; use Box\Spout\Writer\Common\Creator\WriterEntityFactory; @@ -87,7 +88,8 @@ class ExclusionService $exclusion = $icd->exclusions()->create([ 'corporate_id' => $corporate->id, 'service_code' => 'OP', - 'type' => 'diagnosis' + 'type' => 'diagnosis', + 'active' => 1 ]); if (!empty($excl_array[1])) { //msc @@ -141,6 +143,22 @@ class ExclusionService ]); }); } + } else if (!$excl_array[0]){ + $icd = Icd::where(['code' => $row['code']])->first(); + if (!$icd) { + throw new ImportRowException(__('icd.NOT_FOUND'), 0, NULL, $row); + } + // Cari entitas Exclusion yang sesuai dengan kriteria tertentu + $exclusion = Exclusion::where([ + 'corporate_id' => $corporate->id, + 'exclusionable_id' => $icd->id, + ])->first(); + + // Jika entitas ditemukan, perbarui nilai 'active' menjadi 0 + if ($exclusion) { + $exclusion->update(['active' => 0]); + } + } } diff --git a/Modules/Internal/Transformers/DiagnosisExclusionResource.php b/Modules/Internal/Transformers/DiagnosisExclusionResource.php index 5ecc8267..63d92fc9 100644 --- a/Modules/Internal/Transformers/DiagnosisExclusionResource.php +++ b/Modules/Internal/Transformers/DiagnosisExclusionResource.php @@ -21,6 +21,7 @@ class DiagnosisExclusionResource extends JsonResource 'diagnosis_type' => $this->exclusionable->type, 'service_code' => $this->service_code, 'type' => $this->type, + 'active' => $this->active, 'rules' => $this->rules->mapToGroups(function ($item, $key) { return [$item['name'] => $item['values']]; }) diff --git a/app/Models/Exclusion.php b/app/Models/Exclusion.php index 3852907b..93090f9b 100644 --- a/app/Models/Exclusion.php +++ b/app/Models/Exclusion.php @@ -6,6 +6,7 @@ use App\Traits\Blameable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Altek\Accountant\Contracts\Recordable; class Exclusion extends Model { @@ -17,6 +18,7 @@ class Exclusion extends Model 'type', 'exclusionable_id', 'exclusionable_type', + 'active', ]; protected $hidden = [ diff --git a/app/Models/ExclusionRules.php b/app/Models/ExclusionRules.php index 156bec87..0f90d2b1 100644 --- a/app/Models/ExclusionRules.php +++ b/app/Models/ExclusionRules.php @@ -6,6 +6,7 @@ use App\Traits\Blameable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Altek\Accountant\Contracts\Recordable; class ExclusionRules extends Model { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 22c52222..339aff78 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -11,6 +11,7 @@ use App\Models\CorporateService; use App\Models\CorporatePlan; use App\Models\CorporateBenefit; use App\Models\Member; +use App\Models\ExclusionRules; use App\Models\Icd; use App\Models\IcdTemplate; use App\Models\AuditTrail; @@ -96,6 +97,17 @@ class AppServiceProvider extends ServiceProvider $this->logAuditTrail($model, 'deleted'); }); + + // Corporate Exclusion + ExclusionRules::updated(function ($model) { + + $this->logAuditTrailExclusion($model, 'updated'); + }); + + ExclusionRules::deleted(function ($model) { + $this->logAuditTrailExclusion($model, 'deleted'); + }); + // ICD or exlusion Icd::updated(function ($model) { $this->logAuditTrail($model, 'updated'); @@ -129,6 +141,21 @@ class AppServiceProvider extends ServiceProvider 'user_id' => Auth::id(), ]); + // Simpan jejak audit + $auditTrail->save(); + } + private function logAuditTrailExclusion($model, $action) + { + // Membuat jejak audit baru + $auditTrail = new AuditTrail([ + 'model' => get_class($model), + 'model_id' => $model->exclusion_id, + 'action' => $action, + 'old_values' => json_encode($model->getOriginal()), + 'new_values' => json_encode($model->getAttributes()), + 'user_id' => Auth::id(), + ]); + // Simpan jejak audit $auditTrail->save(); } diff --git a/database/migrations/2023_09_21_171455_add_active_to_exclusion.php b/database/migrations/2023_09_21_171455_add_active_to_exclusion.php new file mode 100644 index 00000000..127f2dcb --- /dev/null +++ b/database/migrations/2023_09_21_171455_add_active_to_exclusion.php @@ -0,0 +1,32 @@ +integer('active'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('exclusions', function (Blueprint $table) { + $table->dropColumn('active'); + }); + } +}; diff --git a/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/History.tsx b/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/History.tsx new file mode 100644 index 00000000..533a50be --- /dev/null +++ b/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/History.tsx @@ -0,0 +1,211 @@ +// @mui +import { + Box, + Button, + Card, + Collapse, + Container, + FormControl, + Grid, + IconButton, + InputLabel, + MenuItem, + OutlinedInput, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Badge, + Stack, +} from '@mui/material'; +import * as React from 'react'; +import { useParams } from 'react-router-dom'; +import { styled } from '@mui/material/styles'; +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'; +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; +import { useContext, useEffect, useState } from 'react'; +import MuiAccordionSummary, { + AccordionSummaryProps, +} from '@mui/material/AccordionSummary'; +import useSettings from '../../../hooks/useSettings'; +import axios from '../../../utils/axios'; +import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext'; +import MuiAccordionDetails from '@mui/material/AccordionDetails'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import { Corporate } from '@/@types/corporates'; +import { fDate, fDateTime } from '@/utils/formatTime'; + +const Accordion = styled((props: AccordionProps) => ( + +))(({ theme }) => ({ + border: `1px solid ${theme.palette.divider}`, + '&:not(:last-child)': { + borderBottom: 0, + }, + '&:before': { + display: 'none', + }, +})); + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> +))(({ theme }) => ({ + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + }, +})); + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + padding: theme.spacing(2), + borderTop: '1px solid rgba(0, 0, 0, .125)', +})); + +export default function CustomizedAccordions() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const pageTitle = 'Audittrail Corporate'; + + const { themeStretch } = useSettings(); + + const { corporate_id, id } = useParams(); + + const [corporate, setCorporate] = useState(); + const [ currentCorporate, setCurrentCorporate ] = useState(); + + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + const model = 'App\\Models\\ExclusionRules'; + const url = `/audittrail/${id}?model=${model}`; + axios.get(url) + .then((res) => { + setCurrentCorporate(res.data); + }) + .catch((error) => { + console.error('Terjadi kesalahan:', error); + }); + + }, [configuredCorporateContext]); + + return ( +
+ + {currentCorporate?.data.map((item, index) => ( + + + {`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`} + + + + + Field + Old Value + New Values + + + + {Object.entries(item.old_values).map(([key, value]) => { + let renderedValue; + if (key === 'deleted_by' || key === 'created_by' || key === 'updated_by') { + return null; // Melewati iterasi saat key adalah 'deleted_by' + } + switch (key) { + case 'welcome_message': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'help_text': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'active': + renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive'; + value = value == 1 ? 'Active' : 'Inactive'; + break; + case 'created_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'updated_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'updated_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'delete_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + default: + renderedValue = item.new_values[key]; + break; + } + + const field = key.charAt(0).toUpperCase() + key.slice(1); + if (value != renderedValue){ + return ( + + {`${field}`} + {`${value}`} + {renderedValue} + + ); + } + })} + + + + + ))} +
+ ); +} diff --git a/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/List.tsx b/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/List.tsx index 541c7bac..7f79cac8 100644 --- a/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/List.tsx +++ b/frontend/dashboard/src/pages/Corporates/DiagnosisExclusion/List.tsx @@ -44,7 +44,7 @@ import CancelIcon from '@mui/icons-material/Cancel'; // hooks import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; import useSettings from '../../../hooks/useSettings'; -import { useParams, useSearchParams } from 'react-router-dom'; +import { Link, useParams, useSearchParams } from 'react-router-dom'; // components import axios from '../../../utils/axios'; import { LaravelPaginatedData } from '../../../@types/paginated-data'; @@ -53,6 +53,7 @@ import BasePagination from '../../../components/BasePagination'; import { enqueueSnackbar } from 'notistack'; import { Icon } from '@iconify/react'; import { LoadingButton } from '@mui/lab'; +import HistoryIcon from '@mui/icons-material/History'; export default function List(props: any) { const { themeStretch } = useSettings(); @@ -473,9 +474,11 @@ export default function List(props: any) { {row.code} {row.name} {Object.keys(row.rules).length ? 'With Rules' : 'All'} + {row.active ? 'Active' : 'Inactive'} - {openEdit ? ( + + {openEdit ? ( + */} + + + + + {/* COLLAPSIBLE ROW */} @@ -881,7 +889,10 @@ export default function List(props: any) { Rules - + + Status + + Action diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 23725491..6a344a52 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -165,6 +165,10 @@ export default function Router() { path: ':corporate_id/diagnosis-exclusions', element: , }, + { + path: ':corporate_id/diagnosis-exclusions/:id/history', + element: , + }, { path: ':corporate_id/hospitals', @@ -432,6 +436,9 @@ const CorporatePlansHistory = Loadable(lazy(() => import('../pages/Corporates/Pl const DiagnosisExclusions = Loadable( lazy(() => import('../pages/Corporates/DiagnosisExclusion/Index')) ); +const DiagnosisExclusionsHistory = Loadable( + lazy(() => import('../pages/Corporates/DiagnosisExclusion/History')) +); const CorporateFormularium = Loadable(lazy(() => import('../pages/Corporates/Formularium/Index'))); diff --git a/public/files/Corporate Exclusion Import.xlsx b/public/files/Corporate Exclusion Import.xlsx index eb8cf18d..0bc75550 100644 Binary files a/public/files/Corporate Exclusion Import.xlsx and b/public/files/Corporate Exclusion Import.xlsx differ