This commit is contained in:
Dwi Swandhana
2026-03-06 12:58:40 +07:00
parent 9bd38a470e
commit 5f37ce8def
5 changed files with 333 additions and 85 deletions
@@ -89,13 +89,10 @@ class FrontpageController extends Controller
'poli.subpoli',
'poli.modaliti2'
)
->where(function($q) {
$q->whereNull('periksa.status')
->orWhere(function($sub) {
$sub->where('periksa.status', '<>', 'Selesai')
->where('periksa.status', 'not like', 'Dibatalkan%');
});
})
->whereNotNull('periksa.status')
->where('periksa.status', '<>', '')
->whereRaw('LOWER(periksa.status) <> ?', ['selesai'])
->whereRaw('LOWER(periksa.status) not like ?', ['%dibatalkan%'])
->whereNotNull('periksa.daftar')
->orderBy('periksa.daftar', 'ASC')
->get();
@@ -14,6 +14,7 @@ use App\Jadwalperiksa;
use App\Riwayat;
use App\RekapAntibiotik;
use App\Organisms;
use App\RekapPenerimaanSamplePrintHistory;
use DateTime;
use Carbon\Carbon;
use Session;
@@ -880,9 +881,30 @@ class ReportController extends Controller
if ($periksa->count() == 0) {
return response()->json([]);
}
$histories = RekapPenerimaanSamplePrintHistory::whereDate('tanggal', $tanggal)
->orderBy('printed_at', 'DESC')
->orderBy('id', 'DESC')
->get()
->map(function ($h) {
$rowIds = json_decode($h->row_ids, true);
$rowsPayload = json_decode($h->rows_payload, true);
return [
'id' => $h->id,
'printed_by_id' => $h->printed_by_id,
'printed_by_name' => $h->printed_by_name,
'printed_at' => $h->printed_at,
'printed_at_label' => $h->printed_at ? Carbon::parse($h->printed_at)->format('Y-m-d H:i:s') : '-',
'row_ids' => is_array($rowIds) ? $rowIds : [],
'rows_payload' => is_array($rowsPayload) ? $rowsPayload : [],
'total_rows' => is_array($rowIds) ? count($rowIds) : 0,
];
})
->values();
return view('cetak.rekappenerimaansample', [
'tanggal' => $tanggal,
'detail' => $periksa
'detail' => $periksa,
'printHistories' => $histories
]);
}
public function markRekapPenerimaanSamplePrinted(Request $request) {
@@ -920,6 +942,8 @@ class ReportController extends Controller
'message' => 'Data yang dipilih tidak valid.'
], 422);
}
$tanggal = $request->input('tanggal');
$tanggal = !empty($tanggal) ? $tanggal : null;
$printableIds = Periksa::whereIn('id', $ids)
->where(function ($query) {
@@ -929,18 +953,72 @@ class ReportController extends Controller
->pluck('id')
->toArray();
if (count($printableIds) === 0) {
return response()->json([
'status' => 'error',
'message' => 'Semua data yang dipilih sudah pernah dicetak.'
], 422);
}
$rowsToPrint = Periksa::whereIn('id', $printableIds)
->orderBy('id', 'ASC')
->get()
->map(function ($row) {
$petugas = trim(($row->nmppdsmiddle2 ?? '').' '.($row->nmppdsjunior2 ?? ''));
return [
'id' => $row->id,
'noregister' => $row->noregister ?? '',
'nmpasien' => $row->nmpasien ?? '',
'nofoto' => $row->nofoto ?? '',
'asalpasien' => $row->asalpasien ?? '',
'daftar' => $row->daftar ?? '',
'reques' => $row->reques ?? '',
'kd_spesimen' => $row->kd_spesimen ?? '',
'nm_spesimen' => $row->nm_spesimen ?? '',
'status' => $row->status ?? '',
'tgldraft' => $row->tgldraft ?? '',
'petugas' => $petugas,
];
})
->values()
->toArray();
if (count($printableIds) > 0) {
Periksa::whereIn('id', $printableIds)->update([
'ppdsjunior2' => $userId,
'nmppdsjunior2' => $userNama,
'updated_at' => date('Y-m-d H:i:s'),
]);
}
$history = RekapPenerimaanSamplePrintHistory::create([
'tanggal' => $tanggal,
'printed_by_id' => $userId,
'printed_by_name' => $userNama,
'printed_at' => Carbon::now()->format('Y-m-d H:i:s'),
'row_ids' => json_encode(array_values($printableIds)),
'rows_payload' => json_encode($rowsToPrint),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$historyResponse = [
'id' => $history->id,
'printed_by_id' => $history->printed_by_id,
'printed_by_name' => $history->printed_by_name,
'printed_at' => $history->printed_at,
'printed_at_label' => Carbon::parse($history->printed_at)->format('Y-m-d H:i:s'),
'row_ids' => $printableIds,
'rows_payload' => $rowsToPrint,
'total_rows' => count($printableIds),
];
return response()->json([
'status' => 'success',
'message' => 'Status cetak berhasil diperbarui.',
'affected_ids'=> $printableIds,
'printed_by' => $userNama
'printed_by' => $userNama,
'history' => $historyResponse
]);
}
}
@@ -0,0 +1,11 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class RekapPenerimaanSamplePrintHistory extends Model
{
protected $table = 'rekap_penerimaan_sample_print_histories';
protected $guarded = [];
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('rekap_penerimaan_sample_print_histories', function (Blueprint $table) {
$table->id();
$table->date('tanggal')->nullable();
$table->integer('printed_by_id')->nullable();
$table->string('printed_by_name', 250)->nullable();
$table->timestamp('printed_at')->nullable();
$table->text('row_ids')->nullable();
$table->longText('rows_payload')->nullable();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('rekap_penerimaan_sample_print_histories');
}
};
@@ -63,6 +63,41 @@
</select>
<button type="button" class="btn btn-success" id="btnprintselected"><i class="fa fa-print"></i> Cetak Terpilih</button>
</div>
<div class="mb-2">
<h5>Riwayat Cetak</h5>
<div class="table-responsive" style="max-height: 220px; overflow:auto; border:1px solid #dee2e6;">
<table class="table table-sm table-bordered mb-0" id="printHistoryTable">
<thead>
<tr>
<th>No</th>
<th>Waktu Cetak</th>
<th>Petugas</th>
<th>Jumlah Row</th>
<th>Aksi</th>
</tr>
</thead>
<tbody id="printHistoryBody">
@forelse(($printHistories ?? []) as $idx => $history)
<tr data-history-id="{{ $history['id'] }}">
<td>{{ $idx + 1 }}</td>
<td>{{ $history['printed_at_label'] }}</td>
<td>{{ $history['printed_by_name'] }}</td>
<td>{{ $history['total_rows'] }}</td>
<td>
<button type="button" class="btn btn-xs btn-info btn-reprint-history" data-history-id="{{ $history['id'] }}">
Cetak Ulang
</button>
</td>
</tr>
@empty
<tr id="emptyHistoryRow">
<td colspan="5" class="text-center">Belum ada riwayat cetak.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="rekap-table-wrap">
<table class="table table-bordered table-striped" id="penerimaansamplereport">
@@ -100,7 +135,7 @@
@endphp
<tr data-id="{{ $r['id'] }}" data-status="{{ strtolower($statusValue) }}" data-order="{{ $loop->index }}" class="{{ $isPrinted ? 'printed-row' : '' }}">
<td style="text-align:center;">
<input type="checkbox" class="row-check">
<input type="checkbox" class="row-check" {{ $isPrinted ? 'disabled' : '' }}>
</td>
<td class="print-status-cell">
@if($isPrinted)
@@ -145,14 +180,150 @@
var printOrientation = document.getElementById('printOrientation');
var sortStatus = document.getElementById('sortStatus');
var filterStatus = document.getElementById('filterStatus');
var printHistoryBody = document.getElementById('printHistoryBody');
var token = document.getElementById('token').value;
var markPrintedUrl = @json(route('markRekapPenerimaanSamplePrinted'));
var selectedTanggal = @json($tanggal);
var printHistories = @json($printHistories ?? []);
var printHistoryMap = {};
var appName = @json(config('global.namaapps'));
var appDomain = @json(config('global.domainapps'));
var appSubDomain = @json(config('global.subdomainapps'));
var appSubSubDomain = @json(config('global.subsubdomainapps'));
var appAddress = @json(config('global.addressapps'));
var petugasCetak = @json(Session('nama'));
printHistories.forEach(function(item) {
printHistoryMap[String(item.id)] = item.rows_payload || [];
});
function renderHistoryNumbers() {
if (!printHistoryBody) {
return;
}
var rows = printHistoryBody.querySelectorAll('tr[data-history-id]');
rows.forEach(function(row, idx) {
var firstCell = row.querySelector('td');
if (firstCell) {
firstCell.innerText = idx + 1;
}
});
}
function addHistoryRow(history) {
if (!printHistoryBody || !history) {
return;
}
var emptyRow = document.getElementById('emptyHistoryRow');
if (emptyRow) {
emptyRow.remove();
}
printHistoryMap[String(history.id)] = history.rows_payload || [];
var tr = document.createElement('tr');
tr.setAttribute('data-history-id', history.id);
tr.innerHTML = ''
+ '<td>1</td>'
+ '<td>' + (history.printed_at_label || '-') + '</td>'
+ '<td>' + (history.printed_by_name || '-') + '</td>'
+ '<td>' + (history.total_rows || 0) + '</td>'
+ '<td><button type="button" class="btn btn-xs btn-info btn-reprint-history" data-history-id="' + history.id + '">Cetak Ulang</button></td>';
if (printHistoryBody.firstChild) {
printHistoryBody.insertBefore(tr, printHistoryBody.firstChild);
} else {
printHistoryBody.appendChild(tr);
}
renderHistoryNumbers();
}
function openPrintWindowFromRows(printedRows) {
if (!printedRows || printedRows.length === 0) {
alert('Data riwayat cetak tidak ditemukan.');
return;
}
var now = new Date();
var orientation = printOrientation ? printOrientation.value : 'landscape';
var tanggalCetak = now.toLocaleString('id-ID', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
var printWindow = window.open('', '_blank');
if (!printWindow) {
alert('Pop-up diblokir browser. Izinkan pop-up untuk mencetak.');
return;
}
var rowsHtml = '';
printedRows.forEach(function(rowData) {
rowsHtml += '<tr>'
+ '<td>' + (rowData.noregister || '') + '</td>'
+ '<td>' + (rowData.nmpasien || '') + '</td>'
+ '<td>' + (rowData.nofoto || '') + '</td>'
+ '<td>' + (rowData.asalpasien || '') + '</td>'
+ '<td>' + (rowData.daftar || '') + '</td>'
+ '<td>' + (rowData.reques || '') + '</td>'
+ '<td>' + (rowData.kd_spesimen || '') + '</td>'
+ '<td>' + (rowData.nm_spesimen || '') + '</td>'
+ '<td>' + (rowData.status || '') + '</td>'
+ '<td>' + (rowData.tgldraft || '') + '</td>'
+ '<td>' + (rowData.petugas || '') + '</td>'
+ '</tr>';
});
var html = ''
+ '<!DOCTYPE html>'
+ '<html><head><title>Cetak Rekap Penerimaan Sample</title>'
+ '<style>'
+ '@page { size: A4 ' + orientation + '; margin: 10mm; }'
+ 'body { font-family: Arial, sans-serif; margin: 0; color: #111; }'
+ '.header { text-align: center; margin-bottom: 8px; }'
+ '.header h2 { margin: 0; font-size: 16px; }'
+ '.header .line { margin: 2px 0; font-size: 12px; }'
+ '.meta { display: flex; justify-content: space-between; margin: 8px 0 10px; font-size: 11px; }'
+ '.title { text-align: center; font-weight: 700; margin-bottom: 8px; font-size: 12px; }'
+ 'table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 10px; }'
+ 'th, td { border: 1px solid #000; padding: 3px; word-break: break-word; vertical-align: top; }'
+ 'th { background: #f1f1f1; }'
+ '</style></head><body>'
+ '<div class="header">'
+ '<h2>' + (appName || '') + '</h2>'
+ '<div class="line">' + (appDomain || '') + '</div>'
+ '<div class="line">' + (appSubDomain || '') + ', ' + (appSubSubDomain || '') + '</div>'
+ '<div class="line">' + (appAddress || '') + '</div>'
+ '</div>'
+ '<div class="title">Rekapitulasi Penerimaan Sample {{$tanggal}}</div>'
+ '<div class="meta">'
+ '<div>Tanggal Cetak: ' + tanggalCetak + '</div>'
+ '<div>Petugas: ' + (petugasCetak || '-') + '</div>'
+ '</div>'
+ '<table>'
+ '<thead><tr>'
+ '<th>No.RM</th>'
+ '<th>Nama</th>'
+ '<th>No.Sample</th>'
+ '<th>Asal Pasien</th>'
+ '<th>Daftar</th>'
+ '<th>Order</th>'
+ '<th>Kode</th>'
+ '<th>Spesimen</th>'
+ '<th>Status</th>'
+ '<th>Tanggal Terima</th>'
+ '<th>Petugas</th>'
+ '</tr></thead>'
+ '<tbody>' + rowsHtml + '</tbody>'
+ '</table>'
+ '<script>window.onload = function(){ window.print(); window.close(); };<\/script>'
+ '</body></html>';
printWindow.document.open();
printWindow.document.write(html);
printWindow.document.close();
}
function getEnabledVisibleChecks() {
return table ? table.querySelectorAll('tbody tr:not([style*="display: none"]) .row-check:not(:disabled)') : [];
@@ -305,7 +476,7 @@
return;
}
$.post(markPrintedUrl, { ids: selectedIds, _token: token }, function(resp) {
$.post(markPrintedUrl, { ids: selectedIds, tanggal: selectedTanggal, _token: token }, function(resp) {
if (!resp || resp.status !== 'success') {
alert((resp && resp.message) ? resp.message : 'Gagal memperbarui status cetak.');
return;
@@ -341,81 +512,26 @@
checkAllRows.checked = false;
}
updateCheckAllState();
var now = new Date();
var orientation = printOrientation ? printOrientation.value : 'landscape';
var tanggalCetak = now.toLocaleString('id-ID', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
var printWindow = window.open('', '_blank');
if (!printWindow) {
alert('Pop-up diblokir browser. Izinkan pop-up untuk mencetak.');
return;
if (resp.history) {
addHistoryRow(resp.history);
openPrintWindowFromRows(resp.history.rows_payload || []);
} else {
openPrintWindowFromRows(printedRows.map(function(cols) {
return {
noregister: cols[0] || '',
nmpasien: cols[1] || '',
nofoto: cols[2] || '',
asalpasien: cols[3] || '',
daftar: cols[4] || '',
reques: cols[5] || '',
kd_spesimen: cols[6] || '',
nm_spesimen: cols[7] || '',
status: cols[8] || '',
tgldraft: cols[9] || '',
petugas: cols[10] || ''
};
}));
}
var rowsHtml = '';
printedRows.forEach(function(rowData) {
rowsHtml += '<tr>';
rowData.forEach(function(value) {
rowsHtml += '<td>' + value + '</td>';
});
rowsHtml += '</tr>';
});
var html = ''
+ '<!DOCTYPE html>'
+ '<html><head><title>Cetak Rekap Penerimaan Sample</title>'
+ '<style>'
+ '@page { size: A4 ' + orientation + '; margin: 10mm; }'
+ 'body { font-family: Arial, sans-serif; margin: 0; color: #111; }'
+ '.header { text-align: center; margin-bottom: 8px; }'
+ '.header h2 { margin: 0; font-size: 16px; }'
+ '.header .line { margin: 2px 0; font-size: 12px; }'
+ '.meta { display: flex; justify-content: space-between; margin: 8px 0 10px; font-size: 11px; }'
+ '.title { text-align: center; font-weight: 700; margin-bottom: 8px; font-size: 12px; }'
+ 'table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 10px; }'
+ 'th, td { border: 1px solid #000; padding: 3px; word-break: break-word; vertical-align: top; }'
+ 'th { background: #f1f1f1; }'
+ '</style></head><body>'
+ '<div class="header">'
+ '<h2>' + (appName || '') + '</h2>'
+ '<div class="line">' + (appDomain || '') + '</div>'
+ '<div class="line">' + (appSubDomain || '') + ', ' + (appSubSubDomain || '') + '</div>'
+ '<div class="line">' + (appAddress || '') + '</div>'
+ '</div>'
+ '<div class="title">Rekapitulasi Penerimaan Sample {{$tanggal}}</div>'
+ '<div class="meta">'
+ '<div>Tanggal Cetak: ' + tanggalCetak + '</div>'
+ '<div>Petugas: ' + (petugasCetak || '-') + '</div>'
+ '</div>'
+ '<table>'
+ '<thead><tr>'
+ '<th>No.RM</th>'
+ '<th>Nama</th>'
+ '<th>No.Sample</th>'
+ '<th>Asal Pasien</th>'
+ '<th>Daftar</th>'
+ '<th>Order</th>'
+ '<th>Kode</th>'
+ '<th>Spesimen</th>'
+ '<th>Status</th>'
+ '<th>Tanggal Terima</th>'
+ '<th>Petugas</th>'
+ '</tr></thead>'
+ '<tbody>' + rowsHtml + '</tbody>'
+ '</table>'
+ '<script>window.onload = function(){ window.print(); window.close(); };<\/script>'
+ '</body></html>';
printWindow.document.open();
printWindow.document.write(html);
printWindow.document.close();
}).fail(function(xhr) {
var err = 'Gagal memperbarui status cetak.';
if (xhr.responseJSON && xhr.responseJSON.message) {
@@ -424,6 +540,18 @@
alert(err);
});
});
if (printHistoryBody) {
printHistoryBody.addEventListener('click', function(e) {
var btn = e.target.closest('.btn-reprint-history');
if (!btn) {
return;
}
var historyId = btn.getAttribute('data-history-id');
var payload = printHistoryMap[String(historyId)] || [];
openPrintWindowFromRows(payload);
});
}
});
</script>
@endpush