Files
anjuma-dashboard/rechnungen.php
T
2026-07-22 01:12:45 +02:00

158 lines
5.8 KiB
PHP

<?php
require_once "config/session.php";
if (!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) {
header("location: index.php");
exit;
}
include('navbar.php');
?>
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ANJUMA • Rechnungen</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/css/dataTables.bootstrap5.min.css">
<style>
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
@media (max-width: 768px){ .d-md-table-cell{display:none!important;} }
</style>
</head>
<body class="bg-light">
<div class="container-fluid py-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<h3 class="mb-0">Rechnungen</h3>
</div>
<div class="d-flex gap-2 flex-wrap">
<button id="btnRefresh" class="btn btn-outline-secondary btn-sm">↻ Refresh</button>
</div>
</div>
<div class="card shadow-sm mb-3">
<div class="card-body">
<div class="row g-2 align-items-end">
<div class="col-12 col-md-3">
<label class="form-label mb-1">Von</label>
<input type="date" class="form-control" id="fromDate">
</div>
<div class="col-12 col-md-3">
<label class="form-label mb-1">Bis</label>
<input type="date" class="form-control" id="toDate">
</div>
<div class="col-12 col-md-6 d-flex gap-2">
<button id="btnThisMonth" class="btn btn-outline-primary">Dieser Monat</button>
<button id="btnThisYear" class="btn btn-outline-primary">Dieses Jahr</button>
</div>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<div class="table-responsive">
<table id="tblInv" class="table table-striped table-hover align-middle w-100">
<thead>
<tr>
<th>Nr</th>
<th>Datum</th>
<th class="d-md-table-cell">Leistung</th>
<th>Kunde</th>
<th>Status</th>
<th>Summe</th>
<th>PDF</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="text-muted small mt-2" id="info"></div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/datatables.net@1.13.10/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
const API_LIST = '/api/invoice_list.php';
const PDF_URL = '/api/invoice_pdf.php?id=';
let dt;
function isoLocal(d){ const y=d.getFullYear(), m=String(d.getMonth()+1).padStart(2,'0'), day=String(d.getDate()).padStart(2,'0'); return `${y}-${m}-${day}`; }
function thisMonthRange(){ const d=new Date(); return [isoLocal(new Date(d.getFullYear(), d.getMonth(), 1)), isoLocal(new Date(d.getFullYear(), d.getMonth()+1, 0))]; }
function thisYearRange(){ const d=new Date(); return [isoLocal(new Date(d.getFullYear(), 0, 1)), isoLocal(new Date(d.getFullYear(), 11, 31))]; }
function fmtDateDE(iso){ const [y,m,d]=String(iso||'').split('-'); return (y && m && d) ? `${d}.${m}.${y}` : ''; }
function eur(n){ return (Number(n)||0).toLocaleString('de-DE',{style:'currency',currency:'EUR',maximumFractionDigits:0}); }
function statusBadge(s){
s = String(s||'draft');
if (s==='paid') return '<span class="badge text-bg-success">PAID</span>';
if (s==='sent') return '<span class="badge text-bg-primary">SENT</span>';
if (s==='canceled') return '<span class="badge text-bg-danger">CANCELED</span>';
return '<span class="badge text-bg-secondary">DRAFT</span>';
}
function pdfBtn(row){
return `<a class="btn btn-outline-secondary btn-sm" href="${PDF_URL}${row.id}" target="_blank">PDF</a>`;
}
async function fetchJSON(url){
const res = await fetch(url, { cache:'no-store' });
const text = await res.text();
const json = JSON.parse(text);
if (!json.ok) throw new Error(json.error || 'API error');
return json;
}
async function refresh(){
const qs = new URLSearchParams({ from: $('#fromDate').val(), to: $('#toDate').val() }).toString();
const json = await fetchJSON(`${API_LIST}?${qs}`);
const items = json.items || [];
dt.clear().rows.add(items).draw(false);
$('#info').text(`Treffer: ${items.length} • Zeitraum: ${json.from} bis ${json.to}`);
}
$(function(){
const [f,t] = thisMonthRange();
$('#fromDate').val(f); $('#toDate').val(t);
dt = $('#tblInv').DataTable({
pageLength: 25,
order: [[1,'desc']],
columns: [
{ data:'invoice_no', className:'mono' },
{ data:'invoice_date', className:'mono', render:(d)=>fmtDateDE(d) },
{ data:'service_date', className:'mono d-md-table-cell', render:(d)=>fmtDateDE(d) },
{ data:'customer_name' },
{ data:'status', render:(d)=>statusBadge(d) },
{ data:'amount_net_eur', className:'mono', render:(d)=>eur(d) },
{ data:null, orderable:false, searchable:false, render:(row)=>pdfBtn(row) },
]
});
$('#btnRefresh').on('click', refresh);
$('#fromDate,#toDate').on('change', refresh);
$('#btnThisMonth').on('click', () => {
const [a,b]=thisMonthRange(); $('#fromDate').val(a); $('#toDate').val(b); refresh();
});
$('#btnThisYear').on('click', () => {
const [a,b]=thisYearRange(); $('#fromDate').val(a); $('#toDate').val(b); refresh();
});
refresh();
});
</script>
</body>
</html>