feat(laufzeiten): anlegen, bearbeiten und zusammenfuehren (#043)
Laufzeiten entstanden bisher nur als Vorschlag aus dem Agenten-Report; bestaetigen oder verwerfen war alles. Anlegen von Hand gab es als Aktion ohne Oberflaeche, Aendern und Zusammenfuehren gar nicht - obwohl der Doppelfall zwangslaeufig auftritt, sobald das gemeldete Ablaufdatum von der Bestellung abweicht. Jetzt: Formular zum Anlegen auf der Rechnungsseite (Key vorgeschlagen, Ende aus dem Beginn vorbelegt), Bearbeiten von Key, Zeitraum und Notiz im Laufzeit-Detail, und Zusammenfuehren zweier Laufzeiten desselben Hosts. Die aufgeloeste gibt ihre Rechnungen ab, sofern das Ziel dort nichts hat; sonst bricht der Vorgang mit Erklaerung ab - zwei Einkaufsrechnungen auf einem Lizenzjahr waeren nicht mehr zuzuordnen. Dazu die Kundenansicht: Laufzeit und Host verlinkt, Marge und Rabatt je Zeile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -204,7 +204,7 @@ export default async function KundenDetailSeite({ params }: { params: Promise<{
|
||||
|
||||
<Card titel="Laufzeiten und Rechnungen" rahmen="info" tabelle>
|
||||
<div className="table-responsive">
|
||||
<table className="table table-striped align-middle">
|
||||
<table className="table table-striped align-middle pl-karten-tabelle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Laufzeit</th>
|
||||
@@ -213,26 +213,46 @@ export default async function KundenDetailSeite({ params }: { params: Promise<{
|
||||
<th scope="col">Einkauf</th>
|
||||
<th scope="col">Kundenrechnung</th>
|
||||
<th scope="col">Verkauf</th>
|
||||
<th scope="col">Marge</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{laufzeiten.map((laufzeit) => (
|
||||
<tr key={laufzeit.id}>
|
||||
<td>
|
||||
{formatDatum(laufzeit.start)} – {formatDatum(laufzeit.ende)}
|
||||
{/* Von hier aus werden die Rechnungen erfasst – ohne Link führte die
|
||||
Kundenansicht in eine Sackgasse (#043). */}
|
||||
<td className="pl-karten-kopf">
|
||||
<Link
|
||||
href={`/rechnungen/${laufzeit.id}`}
|
||||
className="fw-semibold text-decoration-none"
|
||||
>
|
||||
{formatDatum(laufzeit.start)} – {formatDatum(laufzeit.ende)}
|
||||
</Link>
|
||||
{laufzeit.vorschlag ? (
|
||||
<span className="badge pl-bg-info ms-2">Vorschlag</span>
|
||||
) : null}
|
||||
</td>
|
||||
<td>{laufzeit.hostname}</td>
|
||||
<td>
|
||||
<td data-label="Host">
|
||||
<Link href={`/hosts/${laufzeit.hostId}`} className="text-decoration-none">
|
||||
{laufzeit.hostname}
|
||||
</Link>
|
||||
</td>
|
||||
<td data-label="Proxmox-Rechnung">
|
||||
<CopyableCode wert={laufzeit.einkauf?.nummer ?? null} />
|
||||
<div>
|
||||
<EinkaufBadge bezahlt={laufzeit.einkauf ? laufzeit.einkauf.bezahlt : null} />
|
||||
</div>
|
||||
</td>
|
||||
<td>{formatEuro(laufzeit.einkauf?.nettoBetrag)}</td>
|
||||
<td>
|
||||
<td data-label="Einkauf">
|
||||
{formatEuro(laufzeit.einkauf?.nettoBetrag)}
|
||||
{laufzeit.einkauf && laufzeit.einkauf.rabatt > 0 ? (
|
||||
<div className="small text-body-secondary">
|
||||
{formatEuro(laufzeit.einkauf.listenpreis)} − {formatEuro(laufzeit.einkauf.rabatt)}{" "}
|
||||
Rabatt
|
||||
</div>
|
||||
) : null}
|
||||
</td>
|
||||
<td data-label="Kundenrechnung">
|
||||
<CopyableCode wert={laufzeit.verkauf?.nummer ?? null} />
|
||||
<div>
|
||||
<VerkaufBadge
|
||||
@@ -241,12 +261,26 @@ export default async function KundenDetailSeite({ params }: { params: Promise<{
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>{formatEuro(laufzeit.verkauf?.nettoBetrag)}</td>
|
||||
<td data-label="Verkauf">{formatEuro(laufzeit.verkauf?.nettoBetrag)}</td>
|
||||
<td
|
||||
data-label="Marge"
|
||||
className={
|
||||
laufzeit.einkauf && laufzeit.verkauf
|
||||
? (laufzeit.verkauf.nettoBetrag ?? 0) - laufzeit.einkauf.nettoBetrag >= 0
|
||||
? "pl-text-success"
|
||||
: "pl-text-danger"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{laufzeit.einkauf && laufzeit.verkauf
|
||||
? formatEuro((laufzeit.verkauf.nettoBetrag ?? 0) - laufzeit.einkauf.nettoBetrag)
|
||||
: "–"}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{laufzeiten.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center text-body-secondary py-4">
|
||||
<td colSpan={7} className="text-center text-body-secondary py-4">
|
||||
Noch keine Laufzeiten erfasst.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user