Export Excel Invoice & Imporve Invoice

This commit is contained in:
ivan-sim
2025-12-18 09:53:01 +07:00
parent 9976e8e438
commit e178682fcf
13 changed files with 622 additions and 80 deletions

View File

@@ -42,11 +42,15 @@ class RequestLogController extends Controller
$validator = Validator::make($request->all(), [
'member_id' => 'required',
'service_code' => 'required',
'submission_date' => 'required'
'submission_date' => 'required',
'specialities_id' => 'required',
'dppj' => 'required',
], [
'member_id.required' => trans('Validation.required',['attribute' => 'Member ID']),
'service_code.required' => trans('Validation.required',['attribute' => 'Service Code']),
'submission_date.required' => trans('Validation.required',['attribute' => 'Submission Date']),
'specialities_id.required' => trans('Validation.required',['attribute' => 'Specialities']),
'dppj.required' => trans('Validation.required',['attribute' => 'DPJP']),
]);
if(!empty($request->organization_id))
{
@@ -54,12 +58,16 @@ class RequestLogController extends Controller
'organization_id' => 'required',
'member_id' => 'required',
'service_code' => 'required',
'submission_date' => 'required'
'submission_date' => 'required',
'specialities_id' => 'required',
'dppj' => 'required',
], [
'organization_id.required' => trans('Validation.required',['attribute' => 'Provider ID']),
'member_id.required' => trans('Validation.required',['attribute' => 'Member ID']),
'service_code.required' => trans('Validation.required',['attribute' => 'Service Code']),
'submission_date.required' => trans('Validation.required',['attribute' => 'Submission Date']),
'specialities_id.required' => trans('Validation.required',['attribute' => 'Specialities']),
'dppj.required' => trans('Validation.required',['attribute' => 'DPJP']),
]);
}
if ($validator->fails())
@@ -245,7 +253,7 @@ class RequestLogController extends Controller
'request_logs.approved_at')
->paginate($limit);
return response()->json(Helper::paginateResources($results));
}

View File

@@ -12,6 +12,8 @@ use App\Helpers\Helper;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
use Box\Spout\Common\Entity\Style\CellAlignment;
use Illuminate\Support\Facades\Storage;
use App\Services\ExportExcelService;
class InvoicePaymentController extends Controller
{
@@ -30,15 +32,11 @@ class InvoicePaymentController extends Controller
$query->orderBy($orderBy, $direction);
})
->when($request->input('start_date') , function ($query, $start_date) {
$query->where(function ($query) use ($start_date) {
$query->where('invoice_payments.created_at', '>=', $start_date);
});
->when($request->input('start_date'), function ($query, $start_date) {
$query->whereDate('invoice_payments.created_at', '>=', $start_date);
})
->when($request->input('end_date') , function ($query, $end_date) {
$query->where(function ($query) use ($end_date) {
$query->where('invoice_payments.created_at', '<=', $end_date);
});
->when($request->input('end_date'), function ($query, $end_date) {
$query->whereDate('invoice_payments.created_at', '<=', $end_date);
})
->when($request->input('status') , function ($query, $status) {
$query->where(function ($query) use ($status) {
@@ -183,8 +181,10 @@ class InvoicePaymentController extends Controller
->leftJoin('request_logs', 'claim_requests.request_log_id','=', 'request_logs.id')
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
->where('invoice_payment_details.invoice_payment_id', $id)
->whereNull('invoice_payment_details.deleted_by')
->orWhere('invoice_payment_details.deleted_by', 0)
->where(function ($q) {
$q->whereNull('invoice_payment_details.deleted_by')
->orWhere('invoice_payment_details.deleted_by', 0);
})
->select(
'claim_requests.id',
'request_logs.invoice_no',
@@ -240,6 +240,7 @@ class InvoicePaymentController extends Controller
'invoice_payments.id',
'files.id as file_id',
'invoice_payments.amount_paid',
'invoice_payments.no_reference',
'invoice_payments.payment_number',
'files.path',
'files.source',
@@ -274,6 +275,7 @@ class InvoicePaymentController extends Controller
return [
'id' => $group->first()->id,
'amount_paid' => $group->first()->amount_paid,
'no_reference' => $group->first()->no_reference,
'payment_number' => $group->first()->payment_number,
'files' => $group->map(function ($file) {
return [
@@ -349,6 +351,7 @@ class InvoicePaymentController extends Controller
'start_date' => $request->start_date,
'end_date' => $request->start_date,
'amount_paid' => $this->normalizeCurrency($valuePayments['amount']),
'no_reference' => $valuePayments['noReference'],
'status' => 'submitted',
'created_by' => auth()->user()->id,
'created_at' => date('Y-m-d H:i:s'),
@@ -405,40 +408,56 @@ class InvoicePaymentController extends Controller
'start_date' => $request->start_date,
'end_date' => $request->end_date,
'amount_paid' => $this->normalizeCurrency($valuePayments['amount']),
'no_reference' => $valuePayments['noReference'],
'updated_by' => auth()->user()->id,
'updated_at' => date('Y-m-d H:i:s'),
]);
$existingClaims = DB::table('invoice_payment_details')
->where('invoice_payment_id', $invoicePaymentId)
->whereNull('deleted_at') // hanya data aktif
->pluck('claim_request_id')
->map(fn($id) => (int)$id)
->toArray();
$newClaims = $request->invoice_payment_details;
// pastikan newClaims integer semua
$newClaims = array_map('intval', $request->invoice_payment_details);
// Data yang mau di-insert
// =============================
// INSERT DATA BARU
// =============================
$claimsToInsert = array_diff($newClaims, $existingClaims);
foreach ($claimsToInsert as $claim) {
DB::table('invoice_payment_details')->insert([
'invoice_payment_id' => $invoicePaymentId,
'claim_request_id' => $claim,
'updated_by' => auth()->user()->id,
'updated_at' => now(),
]);
if (!empty($claimsToInsert)) {
foreach ($claimsToInsert as $claim) {
DB::table('invoice_payment_details')->insert([
'invoice_payment_id' => $invoicePaymentId,
'claim_request_id' => $claim,
'created_by' => auth()->id(),
'created_at' => now(),
]);
}
}
// Data yang mau di-delete (tidak ada di data baru)
// =============================
// SOFT DELETE DATA YANG DIHAPUS
// =============================
$claimsToDelete = array_diff($existingClaims, $newClaims);
if (!empty($claimsToDelete)) {
DB::table('invoice_payment_details')
->where('invoice_payment_id', $invoicePaymentId)
->whereIn('claim_request_id', $claimsToDelete)
->whereNull('deleted_at') // penting supaya tidak over-update
->update([
'deleted_by' => auth()->user()->id,
'deleted_by' => auth()->id(),
'deleted_at' => now(),
]);
}
// Handle existing files
$existingFiles = DB::table('files')
->where('files.fileable_id', $invoicePaymentId)
@@ -524,7 +543,22 @@ class InvoicePaymentController extends Controller
return response()->json(['message' => 'Status berhasil diperbarui']);
}
public function export(Request $request)
public function export(Request $request, ExportExcelService $service)
{
\Log::info('EXPORT REQUEST', $request->all());
$filters = [
'search' => $request->input('search'),
'start_date' => $request->input('start_date'),
'end_date' => $request->input('end_date'),
'orderBy' => $request->input('orderBy'),
'order' => $request->input('order', 'asc'),
];
return $service->exportReport($filters, 'Report-Vale-Payment.xlsx');
}
public function export3(Request $request)
{
$start_date = $request->input('start_date') ? $request->input('start_date') : 'all';
$end_date = $request->input('end_date') ? $request->input('end_date') : 'all';

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Exports;
use App\Exports\Sheets\Invoices\VOPSheet;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use App\Exports\Sheets\UsersSheet;
use App\Exports\Sheets\OrdersSheet;
class ReportExport implements WithMultipleSheets
{
protected array $filters;
public function __construct(array $filters = [])
{
$this->filters = $filters;
}
public function sheets(): array
{
return [
new VOPSheet($this->filters),
];
}
}

View File

@@ -0,0 +1,349 @@
<?php
namespace App\Exports\Sheets\Invoices;
use Illuminate\Support\Facades\DB;
use PhpOffice\PhpSpreadsheet\Cell\{
Cell,
DataType,
StringValueBinder
};
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Maatwebsite\Excel\Concerns\{
FromCollection,
WithHeadings,
WithMapping,
WithTitle,
WithCustomValueBinder,
WithEvents
};
use Maatwebsite\Excel\Events\AfterSheet;
class VOPSheet extends StringValueBinder implements
FromCollection,
WithHeadings,
WithMapping,
WithTitle,
WithCustomValueBinder,
WithEvents
{
protected array $filters;
protected int $no = 0;
protected float $grandTotal = 0;
protected array $benefits = [];
protected $benefitAmounts;
protected bool $benefitInitialized = false;
public function __construct(array $filters = [])
{
$this->filters = $filters;
}
public function collection()
{
$query = DB::table('invoice_payments')
->leftJoin('invoice_payment_details', 'invoice_payment_details.invoice_payment_id', '=', 'invoice_payments.id')
->leftJoin('claim_requests', 'claim_requests.id', '=', 'invoice_payment_details.claim_request_id')
->leftJoin('request_logs', 'request_logs.id', '=', 'claim_requests.request_log_id')
->leftJoin('organizations', 'organizations.id', '=', 'request_logs.organization_id')
->leftJoin('members', 'members.id', '=', 'request_logs.member_id')
->leftJoin('specialities', 'specialities.id', '=', 'request_logs.specialities_id')
->leftJoin('icd', DB::raw("icd.code"), '=', DB::raw("SUBSTRING_INDEX(request_logs.diagnosis, ',', 1)"))
->orderBy('request_logs.service_code', 'desc')
->select(
'invoice_payments.*',
'organizations.name as organization_name',
'members.name as member_name',
'request_logs.diagnosis as code_diagnosis',
'request_logs.service_code',
'request_logs.dppj',
'specialities.name as specialities_name',
'request_logs.submission_date as in',
'request_logs.discharge_date as out',
'request_logs.code as code_log',
'members.payor_id',
'request_logs.nominal as amount',
'claim_requests.request_log_id',
'members.member_id as batch_number',
'request_logs.keterangan',
'icd.name as diagnosis',
'request_logs.type_of_member'
);
if (!empty($this->filters['search'])) {
$query->where('invoice_number', 'like', '%' . $this->filters['search'] . '%');
}
if (!empty($this->filters['start_date'])) {
$query->whereDate('invoice_payments.created_at', '>=', $this->filters['start_date']);
}
if (!empty($this->filters['end_date'])) {
$query->whereDate('invoice_payments.created_at', '<=', $this->filters['end_date']);
}
if (!empty($this->filters['orderBy'])) {
$query->orderBy(
$this->filters['orderBy'],
$this->filters['order'] ?? 'asc'
);
}
$items = $query->get();
// ambil request_log_id
$requestLogIds = $items
->pluck('request_log_id')
->filter()
->unique()
->values();
// set benefit header (hanya yg dipakai)
$this->benefits = DB::table('request_log_benefits')
->join('benefits', 'benefits.id', '=', 'request_log_benefits.benefit_id')
->whereIn('request_log_benefits.request_log_id', $requestLogIds)
->select('benefits.id', 'benefits.description')
->distinct()
->orderBy('benefits.id')
->pluck('benefits.description', 'benefits.id')
->toArray();
// 🔥 preload benefit amount SEKALI (anti N+1)
$this->benefitAmounts = DB::table('request_log_benefits')
->whereIn('request_log_id', $requestLogIds)
->get()
->groupBy('request_log_id');
return $items;
}
public function map($item): array
{
$this->no++;
$this->grandTotal += (float) $item->amount_paid;
$benefitData = $this->getBenefitAmountsWithTotal($item->request_log_id);
return array_merge([
// $this->no,
(string) $item->invoice_number,
// (string) $item->payment_number,
'',
'',
'',
(string) $item->no_reference,
'',
'',
(string) ($item->code_log ?? '-'),
(string) ($item->payor_id ?? '-'),
'',
'',
(string) ($item->organization_name ?? '-'),
(string) ($item->member_name ?? '-'),
(string) ($item->batch_number ?? '-'),
(string) $benefitData['total'],
'',
'',
],
$benefitData['amounts'],
[
(string) ($item->in ?? '-'),
(string) ($item->out ?? '-'),
'',
(string) ($item->diagnosis ?? '-'),
(string) ($item->type_of_member ?? '-'), // omt
(string) ($item->service_code == 'OP' ? 'RJ' : 'RI'),
'',
'',
'',
(string) ($item->dppj ?? '-'),
(string) ($item->in ?? '-'),
(string) ($item->out ?? '-'),
(string) ($item->specialities_name ?? '-'),
'Karyawan',
'',
'',
'',
(string) ($item->keterangan ?? '-'),
// (string) $item->invoice_date,
// (string) $item->start_date,
// (string) $item->end_date,
// (float) $item->amount_paid,
// (string) $item->created_at,
// (string) $item->updated_at,
]);
}
public function headings(): array
{
$this->initBenefits();
return array_merge([
// 'No',
'No Invoice', //ok
// 'Pembayaran Ke',
'MONTH', //ok
'PERIODE SES', //ok
'SUBMIT INV/SES', //ok
'REFERENCE', //ok
'RECEIVED', //ok
'INV. DATE', //ok
'CODE', //ok
'VENDOR', //ok
'PO', //ok
'SES', //ok
'MAIN DESCRIPTION', //ok
'DESCRIPTION', //ok
'BN', //ok
'AMOUNT', //ok
'WBS', // ok
'NOTE', // ok
],
array_values($this->benefits),
[ 'DEPARTURE', // ok
'ARRIVED', // ok
'TEMPORARY DIAGNOSA', // ok
'FINAL DIAGNOSA',
'NOTE (OMT / NON OMT)',
'RI/RJ', // OK
'Dept', // OK
'Cost Center', // OK
'No.TA', // OK
'DPJP', // OK
'IN', // OK
'OUT', // OK
'SPECIALIST', // OK
'Employee', // OK
'Total Invoice sebelum BPJS', // OK
'BPJS', // OK
'Ditanggung Perusahaan', // OK
'KETERANGAN', // OK
// 'Tanggal Invoice',
// 'Start Date',
// 'End Date',
// 'Nominal Pembayaran',
// 'Created At',
// 'Updated At',
]);
}
public function title(): string
{
return 'VOP & VIP';
}
/**
* FOOTER: Grand Total
*/
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
// $event->sheet->getStyle('A1:AO1')
// ->getFont()
// ->setBold(true);
$highestColumn = $event->sheet->getDelegate()->getHighestColumn();
$event->sheet->getStyle('A1:' . $highestColumn . '1')
->getFont()
->setBold(true);
// $row = $event->sheet->getHighestRow() + 1;
// $event->sheet->setCellValue("A{$row}", 'Grand Total');
// $event->sheet->setCellValue("G{$row}", $this->grandTotal);
// // Bold footer
// $event->sheet->getStyle("A{$row}:G{$row}")
// ->getFont()
// ->setBold(true);
},
];
}
/**
* FORCE: object string
*/
public function bindValue(Cell $cell, $value)
{
if (is_object($value) || is_array($value)) {
$value = json_encode($value);
}
$cell->setValueExplicit($value, DataType::TYPE_STRING);
return true;
}
protected function getBenefitAmountsWithTotal(?int $requestLogId): array
{
if (!$requestLogId || !isset($this->benefitAmounts[$requestLogId])) {
return [
'amounts' => array_fill(0, count($this->benefits), 0),
'total' => 0,
];
}
$rows = $this->benefitAmounts[$requestLogId]
->pluck('amount_approved', 'benefit_id')
->toArray();
$amounts = [];
$total = 0;
foreach ($this->benefits as $benefitId => $desc) {
$amount = (float) ($rows[$benefitId] ?? 0);
$amounts[] = $amount;
$total += $amount;
}
// 🔥 kalau ini memang mau jadi grand total global
$this->grandTotal += $total;
return [
'amounts' => $amounts,
'total' => $total,
];
}
protected function initBenefits(): void
{
if ($this->benefitInitialized) {
return;
}
// ambil request_log_id dulu
$requestLogIds = DB::table('invoice_payments')
->leftJoin('invoice_payment_details', 'invoice_payment_details.invoice_payment_id', '=', 'invoice_payments.id')
->leftJoin('claim_requests', 'claim_requests.id', '=', 'invoice_payment_details.claim_request_id')
->whereNotNull('claim_requests.request_log_id')
->pluck('claim_requests.request_log_id')
->unique()
->values();
if ($requestLogIds->isEmpty()) {
$this->benefits = [];
return;
}
$this->benefits = DB::table('request_log_benefits')
->join('benefits', 'benefits.id', '=', 'request_log_benefits.benefit_id')
->whereIn('request_log_benefits.request_log_id', $requestLogIds)
->select('benefits.id', 'benefits.description')
->distinct()
->orderBy('benefits.id')
->pluck('benefits.description', 'benefits.id')
->toArray();
$this->benefitInitialized = true;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Services;
use App\Exports\ReportExport;
use Maatwebsite\Excel\Facades\Excel;
class ExportExcelService
{
public function exportReport(array $filters = [], string $filename = 'report.xlsx')
{
return Excel::download(new ReportExport($filters), $filename);
}
}

View File

@@ -0,0 +1,32 @@
<?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::table('invoice_payments', function (Blueprint $table) {
$table->string('no_reference', 255)->after('amount_paid')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('invoice_payments', function (Blueprint $table) {
$table->dropColumn('no_reference');
});
}
};

View File

@@ -440,7 +440,7 @@ import { DatePicker, MobileDatePicker } from '@mui/x-date-pickers';
setIsLoading(false)
enqueueSnackbar(response.data.message, { variant: 'success' });
setTimeout(() => {
window.location.reload();
navigate('/invoice-payment');
}, 1000);
})
.catch(({ response }) => {
@@ -911,7 +911,11 @@ const handleGetClaimDetails = async () => {
// Hitung Grand Total
const grandTotal = dataLogs.reduce((sum, item) => sum + item.totalBilling, 0);
const grandTotal = dataLogs.reduce(
(sum, item) => sum + Number(item.totalBilling),
0
);
const [fileBukti, setFileBukti] = useState<any>([]);
const removeBuktiFiles = (filesState:any, index:any) => {
setFileBukti(
@@ -976,7 +980,7 @@ const handleGetClaimDetails = async () => {
}
}, [invoicePayment]);
const [payments, setPayments] = useState([{ id: Date.now(), amount: "", existingFiles: [], files: [], paymentNumber: 1 }]);
const [payments, setPayments] = useState([{ id: Date.now(), amount: "", existingFiles: [], files: [], noReference: "", paymentNumber: 1 }]);
const fileInputRefs = useRef({});
useEffect(() => {
@@ -986,6 +990,7 @@ const handleGetClaimDetails = async () => {
amount: fCurrency(parseFloat(payment.amount_paid)),
existingFiles: Array.isArray(payment.files) ? payment.files : [],
files: [],
noReference: payment.no_reference,
paymentNumber: payment.payment_number
}));
setPayments(mappedPayments);
@@ -995,7 +1000,7 @@ const handleGetClaimDetails = async () => {
const handleAddPayment = () => {
const newId = Date.now();
setPayments([...payments, { id: newId, amount: "", existingFiles: [], files: [], paymentNumber: payments.length + 1 }]);
setPayments([...payments, { id: newId, amount: "", existingFiles: [], files: [], noReference: "", paymentNumber: payments.length + 1 }]);
fileInputRefs.current[newId] = null;
};
@@ -1005,6 +1010,12 @@ const handleGetClaimDetails = async () => {
setPayments(updatedPayments);
};
const handleReferenceChange = (index, value) => {
const updatedPayments = [...payments];
updatedPayments[index].noReference = value;
setPayments(updatedPayments);
};
const handleFileChange = (index, event) => {
const newFiles = Array.from(event.target.files);
const updatedPayments = [...payments];
@@ -1029,9 +1040,14 @@ const handleGetClaimDetails = async () => {
};
const totalAmount = payments.reduce((sum, payment) => {
const num = parseFloat(payment.amount.replace(/[^\d]/g, "")) || 0;
const num =
parseFloat(
(payment.amount ?? '0').toString().replace(/[^\d]/g, '')
) || 0;
return sum + num;
}, 0);
}, 0);
return (
@@ -1066,7 +1082,7 @@ const handleGetClaimDetails = async () => {
<Stack direction="row" spacing={2} width="100%">
<Stack spacing={2} sx={{ width: '50%' }}>
<Typography variant="subtitle1">Nomor Invoice *</Typography>
<Typography variant="subtitle1">Nomor Invoice <span style={{color: 'red'}}>*</span></Typography>
<TextField
id="nomor_invoice"
variant="outlined"
@@ -1191,7 +1207,7 @@ const handleGetClaimDetails = async () => {
{/* Input Jumlah Bayar */}
<Stack direction="row" spacing={2} width="100%">
<Stack spacing={2} sx={{ width: "50%" }}>
<Stack spacing={2} sx={{ width: "35%" }}>
<Typography variant="subtitle1">Nominal Pembayaran</Typography>
<TextField
variant="outlined"
@@ -1202,7 +1218,7 @@ const handleGetClaimDetails = async () => {
</Stack>
{/* Input File Bukti */}
<Stack spacing={2} sx={{ width: "50%" }}>
<Stack spacing={2} sx={{ width: "35%" }}>
<Typography variant="subtitle1">Upload Bukti Pembayaran</Typography>
<Stack>
<Stack divider={<Divider orientation="horizontal" flexItem />} spacing={1}>
@@ -1262,6 +1278,15 @@ const handleGetClaimDetails = async () => {
/>
</Stack>
</Stack>
<Stack spacing={2} sx={{ width: "30%" }}>
<Typography variant="subtitle1">No. Referensi</Typography>
<TextField
variant="outlined"
placeholder="Nomor Referensi"
value={payment.noReference}
onChange={(e) => handleReferenceChange(index, e.target.value)}
/>
</Stack>
</Stack>
</Stack>
))}

View File

@@ -268,13 +268,13 @@ export default function Detail() {
{/* Input Jumlah Bayar */}
<Stack direction="row" spacing={2} width="100%">
<Stack spacing={2} sx={{ width: "50%" }}>
<Stack spacing={2} sx={{ width: "35%" }}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Nominal Pembayaran</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{fCurrency(parseFloat(file.amount_paid))}</Typography>
</Stack>
{/* Input File Bukti */}
<Stack spacing={2} sx={{ width: "50%" }}>
<Stack spacing={2} sx={{ width: "35%" }}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Bukti Pembayaran</Typography>
<Stack>
<Stack divider={<Divider orientation="horizontal" flexItem />} spacing={1}>
@@ -292,6 +292,10 @@ export default function Detail() {
</Stack>
</Stack>
</Stack>
<Stack spacing={2} sx={{ width: "30%" }}>
<Typography variant='subtitle2' sx={style1} gutterBottom>No. Reference</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{file.no_reference}</Typography>
</Stack>
</Stack>
</Stack>
))}

View File

@@ -146,35 +146,60 @@ import { ClearOutlined, LoopOutlined } from "@mui/icons-material";
var filter = Object.fromEntries([...searchParams.entries()]);
setIsLoading(true)
await axios
.get('/invoice-payment/export',{
params: {
search: searchText,
start_date: formattedDate ? formattedDate : null,
end_date:formattedDate1,
provider: dataProvider,
status: dataStatus,
order: order,
orderBy: orderBy,
page: perPage,
}
})
.then((res) => {
enqueueSnackbar('Data berhasil di Export', {
variant: 'success',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
});
setIsLoading(false)
document.location.href = res.data.data.file_url;
})
.catch((err) =>
enqueueSnackbar('Data Gagal di Export', {
variant: 'error',
anchorOrigin: { horizontal: 'right', vertical: 'top' },
})
const res = await axios.get('/invoice-payment/export', {
params: {
// search: searchText,
start_date: formattedDate ? formattedDate : null,
end_date: formattedDate1,
provider: dataProvider,
status: dataStatus,
order: order,
orderBy: orderBy,
},
responseType: 'blob',
});
);
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Report-Vale-Payment.xlsx');
document.body.appendChild(link);
link.click();
setIsLoading(false);
// await axios
// .get('/invoice-payment/export',{
// params: {
// search: searchText,
// start_date: formattedDate ? formattedDate : null,
// end_date:formattedDate1,
// provider: dataProvider,
// status: dataStatus,
// order: order,
// orderBy: orderBy,
// page: perPage,
// }
// })
// .then((res) => {
// enqueueSnackbar('Data berhasil di Export', {
// variant: 'success',
// anchorOrigin: { horizontal: 'right', vertical: 'top' },
// });
// setIsLoading(false)
// document.location.href = res.data.data.file_url;
// })
// .catch((err) =>
// enqueueSnackbar('Data Gagal di Export', {
// variant: 'error',
// anchorOrigin: { horizontal: 'right', vertical: 'top' },
// })
// );
};

View File

@@ -140,7 +140,9 @@
"txtAttention": "Attention",
"txtAttentionInfo": "There are pending orders that require approval.",
"txtDPPJ": "DPJP",
"txtSpecialist": "Specialist"
"txtSpecialist": "Specialist",
"txtWarningDPPJ": "Please input DPJP",
"txtWarningSpecialist": "Please select Specialist"
}

View File

@@ -140,5 +140,7 @@
"txtAttention": "Perhatian",
"txtAttentionInfo": "Terdapat pesanan pending mohon untuk segera di approve.",
"txtDPPJ": "DPJP",
"txtSpecialist": "Spesialis"
"txtSpecialist": "Spesialis",
"txtWarningDPPJ": "Mohon isi DPJP",
"txtWarningSpecialist": "Mohon pilih Spesialis"
}

View File

@@ -95,6 +95,18 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
enqueueSnackbar(localeData.txtWarningDischargeDate, { variant: 'warning' });
return false;
}
//cek spesialis
if(!idSpecialities)
{
enqueueSnackbar(localeData.txtWarningSpecialist, { variant: 'warning' });
return false;
}
//cek dpjp
if(!inputDppj)
{
enqueueSnackbar(localeData.txtWarningDPPJ, { variant: 'warning' });
return false;
}
setSubmitLoading(true);
const formData = makeFormData({
request_logs_id: member.id,
@@ -142,7 +154,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
}).catch((error) => {
console.error('Error fetching ICD options:', error);
});
}, []);
console.log(serviceOptions, member, 'test')
@@ -210,7 +222,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
@@ -271,7 +283,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
@@ -332,7 +344,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
@@ -357,7 +369,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
<Stack direction="row" spacing={2} sx={{ width: '100%' }}>
{/* Kolom Tanggal Discharge */}
<Stack spacing={2} sx={{ flex: 1 }}>
<Typography variant="subtitle1">{localeData.txtDischargeDate} *</Typography>
<Typography variant="subtitle1">{localeData.txtDischargeDate} <span style={{ color: 'red' }}>*</span></Typography>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={localeData.txtDischargeDate}
@@ -373,7 +385,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
{/* Kolom Service Type */}
<Stack spacing={2} sx={{ flex: 1 }}>
<Typography variant="subtitle1">{localeData.txtDialogMember1} *</Typography>
<Typography variant="subtitle1">{localeData.txtDialogMember1} <span style={{ color: 'red' }}>*</span></Typography>
<Autocomplete
id="service_type"
options={serviceOptions}
@@ -393,7 +405,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
{/* Specialist */}
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{localeData.txtSpecialist}</Typography>
<Typography variant='subtitle1'>{localeData.txtSpecialist} <span style={{ color: 'red' }}>*</span></Typography>
<Autocomplete
id='specialities'
options={specialisOptions}
@@ -412,7 +424,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{ localeData.txtDPPJ }</Typography>
<Typography variant='subtitle1'>{ localeData.txtDPPJ } <span style={{ color: 'red' }}>*</span></Typography>
<TextField
id='dppj'
variant='outlined'
@@ -426,7 +438,7 @@ export default function DialogFinalLog({ member, getData, onClose, handleSubmitS
</Stack>
</Stack>
<LoadingButton
variant="contained"

View File

@@ -72,6 +72,18 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
enqueueSnackbar(localeData.txtDialogMember6, { variant: 'warning' });
return false;
}
//cek spesialis
if(!idSpecialities)
{
enqueueSnackbar(localeData.txtWarningSpecialist, { variant: 'warning' });
return false;
}
//cek dpjp
if(!inputDppj)
{
enqueueSnackbar(localeData.txtWarningDPPJ, { variant: 'warning' });
return false;
}
setSubmitLoading(true);
const formData = {
member_id: member.members.id,
@@ -145,7 +157,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{localeData.txtProvider} *</Typography>
<Typography variant='subtitle1'>{localeData.txtProvider} <span style={{ color: 'red' }}>*</span></Typography>
<Autocomplete
id="provider"
options={[{ name: localeData.txtAddNew, id: 0 }, ...member?.providers || []]}
@@ -172,7 +184,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
/>
{showAddNewForm && (
<Stack direction="column" spacing={1} padding={1}>
<Typography variant='body2'>{localeData.txtAddNew} *</Typography>
<Typography variant='body2'>{localeData.txtAddNew} <span style={{ color: 'red' }}>*</span></Typography>
<TextField
required={true}
label={localeData.txtName}
@@ -204,7 +216,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{localeData.txtDialogMember1} *</Typography>
<Typography variant='subtitle1'>{localeData.txtDialogMember1} <span style={{ color: 'red' }}>*</span></Typography>
<Autocomplete
id="service_type"
options={member?.services || []}
@@ -228,7 +240,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
{/* Specialist */}
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{localeData.txtSpecialist}</Typography>
<Typography variant='subtitle1'>{localeData.txtSpecialist} <span style={{ color: 'red' }}>*</span></Typography>
<Autocomplete
id='specialities'
options={member?.specialities || []}
@@ -251,7 +263,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{ localeData.txtDPPJ }</Typography>
<Typography variant='subtitle1'>{ localeData.txtDPPJ } <span style={{ color: 'red' }}>*</span></Typography>
<TextField
id='dppj'
variant='outlined'
@@ -266,7 +278,7 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }: FormRe
<Stack direction="row" spacing={2}>
<Stack spacing={2} sx={{ width: '100%' }}>
<Typography variant='subtitle1'>{localeData.txtDialogMember5} *</Typography>
<Typography variant='subtitle1'>{localeData.txtDialogMember5} <span style={{ color: 'red' }}>*</span></Typography>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label={localeData.txtDialogMember5}