Steuerkram/src/Views/assets/form.php
2026-05-24 12:56:21 +02:00

680 lines
35 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use Steuerkram\View;
$h = fn($v) => View::h($v);
$assetId = (int)($asset['id'] ?? 0);
$isEdit = $assetId > 0;
$nutzung = isset($asset['nutzungsdauer']) && $asset['nutzungsdauer'] !== '' ? (int)$asset['nutzungsdauer'] : null;
$predefined = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 20, 25, 50];
$isCustom = $nutzung !== null && !in_array($nutzung, $predefined, true);
$centFlag = !empty($asset['cent_im_ersten_jahr']);
$autoFlag = isset($asset['automatisch_berechnet']) ? (bool)(int)$asset['automatisch_berechnet'] : true;
?>
<div class="card" style="max-width:620px">
<div class="card-body">
<form method="post" action="<?= $h($action) ?>">
<div class="form-group">
<label for="account_id">Anlageart (Konto)</label>
<select id="account_id" name="account_id">
<option value="">– bitte wählen –</option>
<?php foreach ($anlagearten as $kto): ?>
<option value="<?= $h($kto['id']) ?>"
<?= (string)($asset['account_id'] ?? '') === (string)$kto['id'] ? 'selected' : '' ?>>
<?= $h($kto['kontonummer']) ?> – <?= $h($kto['bezeichnung']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="bezeichnung">Bezeichnung des Wirtschaftsguts *</label>
<input type="text" id="bezeichnung" name="bezeichnung"
value="<?= $h($asset['bezeichnung'] ?? '') ?>"
class="<?= isset($errors['bezeichnung']) ? 'input-error' : '' ?>"
required autofocus>
<?php if (isset($errors['bezeichnung'])): ?>
<div class="error-text"><?= $h($errors['bezeichnung']) ?></div>
<?php endif; ?>
</div>
<div class="form-group">
<label for="anschaffungsdatum">Anschaffungsdatum *</label>
<input type="date" id="anschaffungsdatum" name="anschaffungsdatum"
value="<?= $h($asset['anschaffungsdatum'] ?? '') ?>"
class="<?= isset($errors['anschaffungsdatum']) ? 'input-error' : '' ?>"
required>
<?php if (isset($errors['anschaffungsdatum'])): ?>
<div class="error-text"><?= $h($errors['anschaffungsdatum']) ?></div>
<?php endif; ?>
</div>
<div class="form-group">
<label for="anschaffungskosten">Anschaffungskosten (€) *</label>
<input type="number" id="anschaffungskosten" name="anschaffungskosten"
value="<?= $h($asset['anschaffungskosten'] ?? '0') ?>"
class="<?= isset($errors['anschaffungskosten']) ? 'input-error' : '' ?>"
min="0" step="0.01" required>
<?php if (isset($errors['anschaffungskosten'])): ?>
<div class="error-text"><?= $h($errors['anschaffungskosten']) ?></div>
<?php endif; ?>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem">
<div class="form-group">
<label for="zugaenge">Zugänge (€)</label>
<input type="number" id="zugaenge" name="zugaenge"
value="<?= $h($asset['zugaenge'] ?? '0') ?>"
min="0" step="0.01">
</div>
<div class="form-group">
<label for="abgaenge">Abgänge (€)</label>
<input type="number" id="abgaenge" name="abgaenge"
value="<?= $h($asset['abgaenge'] ?? '0') ?>"
min="0" step="0.01">
</div>
</div>
<div class="form-group">
<label for="abschreibung_jahr">Abschreibung lfd. Jahr (€)</label>
<input type="number" id="abschreibung_jahr" name="abschreibung_jahr"
value="<?= $h($asset['abschreibung_jahr'] ?? '0') ?>"
min="0" step="0.01">
</div>
<!-- Nutzungsdauer + Abschreibungstabelle -->
<div style="display:grid;grid-template-columns:1fr auto;gap:.75rem;align-items:flex-end">
<div class="form-group" style="margin-bottom:0">
<label for="nutzungsdauer_select">Nutzungsdauer (AfA)</label>
<div style="display:flex;gap:.5rem">
<select id="nutzungsdauer_select" style="flex:1">
<option value="" <?= $nutzung === null ? 'selected' : '' ?>>– keine –</option>
<option value="0" <?= $nutzung === 0 ? 'selected' : '' ?>>sofort</option>
<?php foreach ([1,2,3,4,5,6,7,8,9,10,12,13,15,20,25,50] as $j): ?>
<option value="<?= $j ?>" <?= $nutzung === $j ? 'selected' : '' ?>>
<?= $j ?> <?= $j === 1 ? 'Jahr' : 'Jahre' ?>
</option>
<?php endforeach; ?>
<option value="custom" <?= $isCustom ? 'selected' : '' ?>>Individuell …</option>
</select>
<input type="number" id="nutzungsdauer_custom" name="nutzungsdauer"
value="<?= $h($nutzung !== null ? $nutzung : '') ?>"
min="1" step="1" placeholder="Jahre"
style="width:90px;<?= !$isCustom ? 'display:none' : '' ?>">
<input type="hidden" id="nutzungsdauer_hidden" name="nutzungsdauer"
value="<?= $h($nutzung !== null ? $nutzung : '') ?>"
<?= $isCustom ? 'disabled' : '' ?>>
</div>
</div>
<div>
<?php if ($isEdit): ?>
<button type="button" class="btn btn-secondary" data-modal-open="modal-afa"
style="white-space:nowrap" id="btn-show-afa">
AfA-Tabelle
</button>
<?php endif; ?>
</div>
</div>
<div class="form-group" style="margin-top:.75rem">
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer;font-weight:normal">
<input type="checkbox" name="cent_im_ersten_jahr" id="cent_im_ersten_jahr" value="1"
<?= $centFlag ? 'checked' : '' ?>>
Centbeträge im ersten Jahr abschreiben
</label>
<div class="text-muted text-sm" style="margin-top:.25rem;margin-left:1.5rem">
Wenn aktiv: Restwert nach Jahr&nbsp;1 auf volle&nbsp;Euro gerundet; Folgejahre gleichmäßig ohne Centabweichungen.
</div>
</div>
<div class="form-group">
<label for="notizen">Notizen</label>
<textarea id="notizen" name="notizen" rows="3"><?= $h($asset['notizen'] ?? '') ?></textarea>
</div>
<!-- Automatisch-Flag (versteckt, wird per JS aktualisiert) -->
<input type="hidden" name="automatisch_berechnet" id="automatisch_berechnet_input"
value="<?= $autoFlag ? '1' : '0' ?>">
<div style="display:flex;gap:.75rem;margin-top:1rem">
<button type="submit" class="btn btn-primary">Speichern</button>
<a href="/assets" class="btn btn-secondary">Abbrechen</a>
</div>
</form>
</div>
</div>
<?php if ($isEdit): ?>
<?php
$gwgKontonummer = '0480';
$isGwg = ($asset['anlageart_kontonummer'] ?? '') === $gwgKontonummer;
$abschreibungskonten = $abschreibungskonten ?? [];
$defaultKontoNr = \Steuerkram\Constants::KONTO_AFA_GWG;
$defaultKontoId = 0;
foreach ($abschreibungskonten as $k) {
if ($k['kontonummer'] === $defaultKontoNr) { $defaultKontoId = $k['id']; break; }
}
if (!$isGwg) { $defaultKontoId = 0; } // nur bei GWG vorauswählen
// Früher genutztes Konto hat Vorrang vor dem GWG-Standard
if (!empty($previousAfaKontoId)) { $defaultKontoId = (int)$previousAfaKontoId; }
$testMode = $testMode ?? false;
?>
<!-- ── AfA-Tabellen-Modal ─────────────────────────────────── -->
<div class="modal-overlay" id="modal-afa">
<div class="modal" style="width:min(760px,95vw);max-height:85vh;overflow-y:auto">
<div class="modal-title">Abschreibungstabelle</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem;flex-wrap:wrap;gap:.5rem">
<div style="display:flex;align-items:center;gap:.75rem">
<span class="text-sm text-muted" id="afa-mode-label">Modus:</span>
<label style="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.875rem;font-weight:normal">
<input type="checkbox" id="afa-manual-toggle">
Manuell bearbeiten
</label>
</div>
<button type="button" class="btn btn-secondary btn-sm" id="afa-recalc-btn"
title="Tabelle neu berechnen (überschreibt manuelle Änderungen)">
Neu berechnen
</button>
</div>
<div id="afa-warning" class="flash flash-warning text-sm" style="display:none;margin-bottom:.75rem">
Achtung: Die Abschreibungstabelle wurde manuell bearbeitet. Neu berechnen überschreibt alle manuellen Änderungen.
</div>
<div id="afa-table-wrap">
<table style="width:100%;border-collapse:collapse">
<thead>
<tr>
<th style="text-align:left;padding:.5rem .75rem;border-bottom:2px solid var(--color-border)">Jahr</th>
<th style="text-align:right;padding:.5rem .75rem;border-bottom:2px solid var(--color-border)">Abschreibung&nbsp;(€)</th>
<th style="text-align:right;padding:.5rem .75rem;border-bottom:2px solid var(--color-border)">Restwert&nbsp;(€)</th>
<th style="text-align:center;padding:.5rem .75rem;border-bottom:2px solid var(--color-border);width:2.5rem" title="Buchungsstatus">Buchung</th>
</tr>
</thead>
<tbody id="afa-table-body">
<?php if (empty($scheduleRows)): ?>
<tr id="afa-empty-row">
<td colspan="4" class="text-center text-muted" style="padding:1.5rem">
Noch keine Abschreibungstabelle vorhanden. Klicke „Neu berechnen".
</td>
</tr>
<?php else: ?>
<?php foreach ($scheduleRows as $row): ?>
<?php $gebucht = !empty($row['afa_booking_id']); ?>
<tr>
<td style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)">
<?= $h($row['jahr']) ?>
</td>
<td class="td-num afa-betrag" style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)">
<?= number_format((float)$row['abschreibungsbetrag'], 2, ',', '.') ?>
</td>
<td class="td-num afa-restwert" style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)">
<?= number_format((float)$row['restwert'], 2, ',', '.') ?>
</td>
<td style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border);text-align:center">
<?php if ($gebucht): ?>
<div style="display:inline-flex;gap:.2rem;align-items:center;justify-content:center">
<span title="Buchung vorhanden" style="color:#22c55e;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
</span>
<button type="button"
class="afa-reverse-btn"
data-row-id="<?= (int)$row['id'] ?>"
title="AFA-Buchung stornieren"
style="background:none;border:none;cursor:pointer;color:var(--color-muted);padding:.2rem;line-height:1;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7v6h6"/>
<path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/>
</svg>
</button>
<?php if ($testMode): ?>
<button type="button"
class="afa-delete-btn"
data-row-id="<?= (int)$row['id'] ?>"
title="AFA-Buchung löschen (Testmodus)"
style="background:none;border:none;cursor:pointer;color:var(--color-danger);padding:.2rem;line-height:1;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3,6 5,6 21,6"/>
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
<path d="M10 11v6M14 11v6"/>
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
</svg>
</button>
<?php endif; ?>
</div>
<?php else: ?>
<button type="button"
class="afa-book-btn"
data-row-id="<?= (int)$row['id'] ?>"
data-jahr="<?= (int)$row['jahr'] ?>"
data-betrag="<?= number_format((float)$row['abschreibungsbetrag'], 2, ',', '.') ?>"
data-default-konto="<?= $defaultKontoId ?>"
title="AFA-Buchung für <?= (int)$row['jahr'] ?> anlegen"
style="background:none;border:none;cursor:pointer;color:var(--color-muted);padding:.2rem;line-height:1;display:inline-flex;align-items:center;justify-content:center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="14" height="14" rx="2"/>
<polyline points="17 8 21 12 17 16"/>
<line x1="10" y1="12" x2="21" y2="12"/>
</svg>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" data-modal-close>Schließen</button>
<button type="button" class="btn btn-primary" id="afa-save-btn">Speichern</button>
</div>
</div>
</div>
<!-- ── AFA-Buchungs-Dialog ──────────────────────────────────── -->
<div class="modal-overlay" id="modal-afa-book">
<div class="modal" style="width:min(460px,95vw)">
<div class="modal-title">AFA-Buchung anlegen</div>
<p class="text-sm text-muted" style="margin-bottom:1rem">
Jahr <strong id="afabook-jahr"></strong> &middot; Betrag <strong id="afabook-betrag"></strong>&nbsp;€
</p>
<div class="form-group">
<label for="afabook-konto">Abschreibungskonto (Aufwand)</label>
<select id="afabook-konto">
<?php foreach ($abschreibungskonten as $k): ?>
<option value="<?= (int)$k['id'] ?>"><?= $h($k['kontonummer']) ?> – <?= $h($k['bezeichnung']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
<button type="button" class="btn btn-primary" id="afabook-confirm-btn">Buchung anlegen</button>
</div>
</div>
</div>
<?php endif; ?>
<script>
(function() {
// ── Nutzungsdauer Dropdown ──────────────────────────────
const sel = document.getElementById('nutzungsdauer_select');
const custom = document.getElementById('nutzungsdauer_custom');
const hidden = document.getElementById('nutzungsdauer_hidden');
if (sel) {
sel.addEventListener('change', () => {
const val = sel.value;
if (val === 'custom') {
custom.style.display = '';
custom.removeAttribute('disabled');
hidden.setAttribute('disabled', '');
custom.focus();
} else {
custom.style.display = 'none';
custom.setAttribute('disabled', '');
hidden.removeAttribute('disabled');
hidden.value = val;
}
// Automatisch-Flag zurücksetzen bei Parameteränderung
scheduleMarkAuto();
});
custom.addEventListener('input', () => {
scheduleMarkAuto();
});
}
function getNutzungsdauer() {
if (!sel) return null;
const val = sel.value;
if (val === 'custom') return parseInt(custom.value, 10) || null;
if (val === '') return null;
return parseInt(val, 10);
}
function scheduleMarkAuto() {
// Wenn Tabelle war manuell und Parameter ändert sich: Rückfrage
const autoInput = document.getElementById('automatisch_berechnet_input');
if (autoInput && autoInput.value === '0') {
const confirm = window.confirm(
'Manuell geänderte Werte vorhanden. Sollen diese überschrieben werden?\n\n' +
'OK = Neu berechnen, Abbrechen = Manuelle Werte behalten'
);
if (confirm) {
autoInput.value = '1';
updateAfaWarning(true);
}
}
}
// ── AfA-Tabellen-Modal ──────────────────────────────────
const manualToggle = document.getElementById('afa-manual-toggle');
const recalcBtn = document.getElementById('afa-recalc-btn');
const saveBtn = document.getElementById('afa-save-btn');
const tbody = document.getElementById('afa-table-body');
const autoInput = document.getElementById('automatisch_berechnet_input');
const assetId = <?= $assetId ?>;
// Spiegele aktuellen Auto-Status in Modal-Toggle
function syncToggle() {
if (!manualToggle || !autoInput) return;
manualToggle.checked = autoInput.value === '0';
updateAfaWarning(autoInput.value === '0');
}
function updateAfaWarning(isManual) {
const warn = document.getElementById('afa-warning');
if (warn) warn.style.display = isManual ? '' : 'none';
if (recalcBtn) recalcBtn.style.display = isManual ? '' : 'none';
}
// Modal öffnen → Toggle syncen
document.addEventListener('click', (e) => {
if (e.target.closest('[data-modal-open="modal-afa"]')) {
syncToggle();
setTableEditable(manualToggle?.checked ?? false);
}
});
if (manualToggle) {
manualToggle.addEventListener('change', () => {
const isManual = manualToggle.checked;
if (autoInput) autoInput.value = isManual ? '0' : '1';
updateAfaWarning(isManual);
setTableEditable(isManual);
});
}
function setTableEditable(editable) {
if (!tbody) return;
tbody.querySelectorAll('tr').forEach(tr => {
tr.querySelectorAll('.afa-betrag, .afa-restwert').forEach(cell => {
cell.contentEditable = editable ? 'true' : 'false';
cell.style.background = editable ? 'var(--color-input-bg, #fff)' : '';
cell.style.outline = editable ? '1px solid var(--color-border)' : '';
cell.style.borderRadius = editable ? '3px' : '';
});
});
}
function fmtNum(v) {
return parseFloat(String(v).replace(',', '.')).toLocaleString('de-DE', {
minimumFractionDigits: 2, maximumFractionDigits: 2
});
}
function parseNum(s) {
return parseFloat(String(s).replace(/\./g, '').replace(',', '.')) || 0;
}
// Neu berechnen
if (recalcBtn) {
recalcBtn.addEventListener('click', async () => {
if (!window.confirm('Abschreibungstabelle neu berechnen? Manuelle Änderungen gehen verloren.')) return;
await loadPreview();
if (autoInput) autoInput.value = '1';
if (manualToggle) manualToggle.checked = false;
updateAfaWarning(false);
setTableEditable(false);
});
}
async function loadPreview() {
const aks = document.getElementById('anschaffungskosten')?.value ?? '0';
const zug = document.getElementById('zugaenge')?.value ?? '0';
const abg = document.getElementById('abgaenge')?.value ?? '0';
const dat = document.getElementById('anschaffungsdatum')?.value ?? '';
const nd = getNutzungsdauer();
const cent = document.getElementById('cent_im_ersten_jahr')?.checked ? '1' : '0';
if (nd === null) {
if (tbody) tbody.innerHTML = '<tr><td colspan="3" class="text-center text-muted" style="padding:1.5rem">Keine Nutzungsdauer angegeben.</td></tr>';
return;
}
const qs = new URLSearchParams({
anschaffungskosten: aks, zugaenge: zug, abgaenge: abg,
nutzungsdauer: nd, cent_im_ersten_jahr: cent,
anschaffungsdatum: dat
});
try {
const res = await fetch('/assets/depreciation-preview?' + qs);
const data = await res.json();
renderTable(data.rows ?? []);
} catch (_) {
if (tbody) tbody.innerHTML = '<tr><td colspan="3" class="text-center text-muted" style="padding:1rem">Fehler beim Laden.</td></tr>';
}
}
function renderTable(rows) {
if (!tbody) return;
if (!rows.length) {
tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted" style="padding:1.5rem">Keine Daten.</td></tr>';
return;
}
const isManual = manualToggle?.checked ?? false;
tbody.innerHTML = rows.map(r => `
<tr>
<td style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)">${r.jahr}</td>
<td class="td-num afa-betrag" contenteditable="${isManual}"
style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)${isManual ? ';background:var(--color-input-bg,#fff);outline:1px solid var(--color-border);border-radius:3px' : ''}">
${fmtNum(r.abschreibungsbetrag)}
</td>
<td class="td-num afa-restwert" contenteditable="${isManual}"
style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border)${isManual ? ';background:var(--color-input-bg,#fff);outline:1px solid var(--color-border);border-radius:3px' : ''}">
${fmtNum(r.restwert)}
</td>
<td style="padding:.4rem .75rem;border-bottom:1px solid var(--color-border);text-align:center;width:2.5rem">
<span title="Tabelle neu speichern, um Buchungen anzulegen" style="color:var(--color-muted);font-size:.7rem">–</span>
</td>
</tr>`).join('');
}
// Speichern
if (saveBtn) {
saveBtn.addEventListener('click', async () => {
const rows = [];
if (tbody) {
tbody.querySelectorAll('tr').forEach((tr, i) => {
const cells = tr.querySelectorAll('td');
if (cells.length < 3) return;
rows.push({
jahr: parseInt(cells[0].textContent.trim(), 10),
abschreibungsbetrag: cells[1].textContent.trim(),
restwert: cells[2].textContent.trim(),
});
});
}
const automatisch = !(manualToggle?.checked ?? false) ? 1 : 0;
try {
const res = await fetch(`/assets/${assetId}/depreciation`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ rows, automatisch }),
});
const data = await res.json();
if (data.ok) {
if (autoInput) autoInput.value = automatisch ? '1' : '0';
document.getElementById('modal-afa')?.classList.remove('open');
} else {
alert('Fehler beim Speichern: ' + (data.error ?? 'Unbekannt'));
}
} catch (_) {
alert('Netzwerkfehler beim Speichern.');
}
});
}
// Init: Neu-Berechnen-Button nur bei manueller Tabelle zeigen
syncToggle();
// ── AFA-Buchungs-Dialog ──────────────────────────────────────
let afabookRowId = 0;
const afabookJahr = document.getElementById('afabook-jahr');
const afabookBetrag = document.getElementById('afabook-betrag');
const afabookKonto = document.getElementById('afabook-konto');
const afabookModal = document.getElementById('modal-afa-book');
const afabookBtn = document.getElementById('afabook-confirm-btn');
document.addEventListener('click', (e) => {
const btn = e.target.closest('.afa-book-btn');
if (!btn) return;
afabookRowId = parseInt(btn.dataset.rowId, 10);
if (afabookJahr) afabookJahr.textContent = btn.dataset.jahr;
if (afabookBetrag) afabookBetrag.textContent = btn.dataset.betrag;
const defaultId = parseInt(btn.dataset.defaultKonto, 10);
if (afabookKonto && defaultId) {
afabookKonto.value = defaultId;
}
if (afabookModal) afabookModal.classList.add('open');
});
const isTestMode = <?= $testMode ? 'true' : 'false' ?>;
function bookedCellHtml(rowId, buchungsNr) {
const title = buchungsNr ? `Buchung vorhanden (${buchungsNr})` : 'Buchung vorhanden';
const deleteBtn = isTestMode
? `<button type="button" class="afa-delete-btn" data-row-id="${rowId}"
title="AFA-Buchung löschen (Testmodus)"
style="background:none;border:none;cursor:pointer;color:var(--color-danger);padding:.2rem;line-height:1;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3,6 5,6 21,6"/>
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
<path d="M10 11v6M14 11v6"/>
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
</svg>
</button>`
: '';
return `<div style="display:inline-flex;gap:.2rem;align-items:center;justify-content:center">
<span title="${title}" style="color:#22c55e;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"/>
</svg>
</span>
<button type="button" class="afa-reverse-btn" data-row-id="${rowId}"
title="AFA-Buchung stornieren"
style="background:none;border:none;cursor:pointer;color:var(--color-muted);padding:.2rem;line-height:1;display:inline-flex;align-items:center">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7v6h6"/>
<path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/>
</svg>
</button>
${deleteBtn}
</div>`;
}
// SVG für "noch nicht gebucht" (Pfeil aus Box)
function unbookedCellHtml(rowId) {
return `<button type="button"
class="afa-book-btn"
data-row-id="${rowId}"
data-jahr=""
data-betrag=""
data-default-konto="${<?= $defaultKontoId ?>}"
title="AFA-Buchung anlegen"
style="background:none;border:none;cursor:pointer;color:var(--color-muted);padding:.2rem;line-height:1;display:inline-flex;align-items:center;justify-content:center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="14" height="14" rx="2"/>
<polyline points="17 8 21 12 17 16"/>
<line x1="10" y1="12" x2="21" y2="12"/>
</svg>
</button>`;
}
// Storno: Buchung stornieren, danach Haken → Pfeil-Button
document.addEventListener('click', async (e) => {
const btn = e.target.closest('.afa-reverse-btn');
if (!btn) return;
if (!confirm('AFA-Buchung wirklich stornieren? Es wird eine Gegenbuchung angelegt.')) return;
const rowId = parseInt(btn.dataset.rowId, 10);
try {
const res = await fetch(`/assets/${assetId}/depreciation/${rowId}/reverse`, { method: 'POST' });
const data = await res.json();
if (data.ok) {
const cell = btn.closest('td');
cell.innerHTML = unbookedCellHtml(rowId);
// Jahr/Betrag aus Tabellenzeile ermitteln und in data-* setzen
const tr = cell.closest('tr');
const tds = tr.querySelectorAll('td');
const newBtn = cell.querySelector('.afa-book-btn');
if (newBtn && tds.length >= 2) {
newBtn.dataset.jahr = tds[0].textContent.trim();
newBtn.dataset.betrag = tds[1].textContent.trim();
}
} else {
alert('Fehler: ' + (data.error ?? 'Unbekannt'));
}
} catch (_) { alert('Netzwerkfehler beim Stornieren.'); }
});
// Löschen (Testmodus): Buchung löschen, danach Haken → Pfeil-Button
document.addEventListener('click', async (e) => {
const btn = e.target.closest('.afa-delete-btn');
if (!btn) return;
if (!confirm('AFA-Buchung wirklich löschen? (Nur im Testmodus – nicht GoBD-konform!)')) return;
const rowId = parseInt(btn.dataset.rowId, 10);
try {
const res = await fetch(`/assets/${assetId}/depreciation/${rowId}/delete`, { method: 'POST' });
const data = await res.json();
if (data.ok) {
const cell = btn.closest('td');
cell.innerHTML = unbookedCellHtml(rowId);
const tr = cell.closest('tr');
const tds = tr.querySelectorAll('td');
const newBtn = cell.querySelector('.afa-book-btn');
if (newBtn && tds.length >= 2) {
newBtn.dataset.jahr = tds[0].textContent.trim();
newBtn.dataset.betrag = tds[1].textContent.trim();
}
} else {
alert('Fehler: ' + (data.error ?? 'Unbekannt'));
}
} catch (_) { alert('Netzwerkfehler beim Löschen.'); }
});
if (afabookBtn) {
afabookBtn.addEventListener('click', async () => {
const kontoId = afabookKonto ? parseInt(afabookKonto.value, 10) : 0;
if (!kontoId) { alert('Bitte ein Abschreibungskonto wählen.'); return; }
try {
const res = await fetch(`/assets/${assetId}/depreciation/${afabookRowId}/book`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ausgabe_konto_id: kontoId }),
});
const data = await res.json();
if (data.ok) {
if (afabookModal) afabookModal.classList.remove('open');
const bookBtn = tbody?.querySelector(`.afa-book-btn[data-row-id="${afabookRowId}"]`);
if (bookBtn) {
const cell = bookBtn.closest('td');
cell.innerHTML = bookedCellHtml(afabookRowId, data.buchungs_nr);
}
} else {
alert('Fehler: ' + (data.error ?? 'Unbekannt'));
}
} catch (_) {
alert('Netzwerkfehler beim Buchen.');
}
});
}
})();
</script>