- Vorlagenbuchung verknüpft jetzt korrekt die Bankbuchung (bank_tx_id) - Buchungs-Redirect bleibt nach Vorlagenbuchung auf dem Beleg - Zahldatum (Wertstellung der Bankbuchung) im Vorlagendialog neben Belegdatum - USt-Felder im Vorlagendialog als Text (nicht mehr als editierbare Inputs) - Standard-Lieferant pro Vorlage speicherbar; wird im Buchungsdialog vorbelegt - Migration 081: spalte standard_lieferant in buchungsvorlagen
254 lines
9.3 KiB
PHP
254 lines
9.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Steuerkram\Controllers;
|
|
|
|
use Steuerkram\Models\Account;
|
|
use Steuerkram\Models\Beleg;
|
|
use Steuerkram\Models\BankTransaction;
|
|
use Steuerkram\Models\Buchungsvorlage;
|
|
use Steuerkram\Services\VorlageBuchungsService;
|
|
use Steuerkram\View;
|
|
|
|
class BuchungsvorlageController extends BaseController
|
|
{
|
|
// ── Pflege-UI ──────────────────────────────────────────────────────────
|
|
|
|
public function index(array $params): void
|
|
{
|
|
$vorlagen = Buchungsvorlage::findAllIncludingInactive();
|
|
$view = new View();
|
|
$view->render('layout/base.php', [
|
|
'pageTitle' => 'Buchungsvorlagen',
|
|
'topbarActions' => '<a href="/einstellungen/buchungsvorlagen/neu" class="btn btn-primary">Neue Vorlage</a>',
|
|
'content' => $this->renderView('buchungsvorlagen/index', ['vorlagen' => $vorlagen]),
|
|
]);
|
|
}
|
|
|
|
public function create(array $params): void
|
|
{
|
|
$view = new View();
|
|
$view->render('layout/base.php', [
|
|
'pageTitle' => 'Neue Buchungsvorlage',
|
|
'content' => $this->renderView('buchungsvorlagen/form', [
|
|
'vorlage' => null,
|
|
'felder' => [],
|
|
'regeln' => [],
|
|
'errors' => [],
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public function store(array $params): void
|
|
{
|
|
$data = $_POST;
|
|
$errors = $this->validate($data);
|
|
|
|
if ($errors) {
|
|
$view = new View();
|
|
$view->render('layout/base.php', [
|
|
'pageTitle' => 'Neue Buchungsvorlage',
|
|
'content' => $this->renderView('buchungsvorlagen/form', [
|
|
'vorlage' => $data,
|
|
'felder' => $this->parseFelderFromPost($data),
|
|
'regeln' => $this->parseRegelnFromPost($data),
|
|
'errors' => $errors,
|
|
]),
|
|
]);
|
|
return;
|
|
}
|
|
|
|
$id = Buchungsvorlage::create($data);
|
|
Buchungsvorlage::saveFelderForVorlage($id, $this->parseFelderFromPost($data));
|
|
Buchungsvorlage::saveRegelnForVorlage($id, $this->parseRegelnFromPost($data));
|
|
|
|
View::redirect('/einstellungen/buchungsvorlagen', 'Vorlage gespeichert.');
|
|
}
|
|
|
|
public function edit(array $params): void
|
|
{
|
|
$id = (int)$params['id'];
|
|
$vorlage = Buchungsvorlage::find($id);
|
|
if (!$vorlage) { http_response_code(404); return; }
|
|
|
|
$view = new View();
|
|
$view->render('layout/base.php', [
|
|
'pageTitle' => 'Vorlage bearbeiten',
|
|
'content' => $this->renderView('buchungsvorlagen/form', [
|
|
'vorlage' => $vorlage,
|
|
'felder' => Buchungsvorlage::getFelder($id),
|
|
'regeln' => Buchungsvorlage::getRegeln($id),
|
|
'errors' => [],
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public function update(array $params): void
|
|
{
|
|
$id = (int)$params['id'];
|
|
$vorlage = Buchungsvorlage::find($id);
|
|
if (!$vorlage) { http_response_code(404); return; }
|
|
|
|
$data = $_POST;
|
|
$errors = $this->validate($data);
|
|
|
|
if ($errors) {
|
|
$view = new View();
|
|
$view->render('layout/base.php', [
|
|
'pageTitle' => 'Vorlage bearbeiten',
|
|
'content' => $this->renderView('buchungsvorlagen/form', [
|
|
'vorlage' => array_merge($vorlage, $data),
|
|
'felder' => $this->parseFelderFromPost($data),
|
|
'regeln' => $this->parseRegelnFromPost($data),
|
|
'errors' => $errors,
|
|
]),
|
|
]);
|
|
return;
|
|
}
|
|
|
|
Buchungsvorlage::update($id, $data);
|
|
Buchungsvorlage::saveFelderForVorlage($id, $this->parseFelderFromPost($data));
|
|
Buchungsvorlage::saveRegelnForVorlage($id, $this->parseRegelnFromPost($data));
|
|
|
|
View::redirect('/einstellungen/buchungsvorlagen', 'Vorlage aktualisiert.');
|
|
}
|
|
|
|
public function destroy(array $params): void
|
|
{
|
|
$id = (int)$params['id'];
|
|
Buchungsvorlage::delete($id);
|
|
View::redirect('/einstellungen/buchungsvorlagen', 'Vorlage gelöscht.');
|
|
}
|
|
|
|
// ── AJAX: Modal-HTML ───────────────────────────────────────────────────
|
|
|
|
public function modal(array $params): void
|
|
{
|
|
$vorlageId = (int)$params['id'];
|
|
$belegId = (int)($_GET['beleg_id'] ?? 0);
|
|
|
|
$vorlage = Buchungsvorlage::find($vorlageId);
|
|
if (!$vorlage || !$vorlage['aktiv']) {
|
|
http_response_code(404);
|
|
echo '<p>Vorlage nicht gefunden.</p>';
|
|
return;
|
|
}
|
|
|
|
$beleg = $belegId > 0 ? Beleg::find($belegId) : null;
|
|
|
|
$felder = Buchungsvorlage::getFelder($vorlageId);
|
|
$zahlungskonten = Account::all(['kontenart' => 'Zahlungskonto', 'aktiv' => 1]);
|
|
$zahlungskonten = array_filter($zahlungskonten, fn($a) => (int)$a['kontonummer'] >= 1200 && (int)$a['kontonummer'] <= 1299);
|
|
|
|
$bankTxId = (int)($_GET['bank_tx'] ?? 0);
|
|
$bankTxDatum = '';
|
|
$hatBankbuchung = false;
|
|
if ($beleg) {
|
|
$linked = BankTransaction::findByBelegId((int)$beleg['id']);
|
|
$hatBankbuchung = !empty($linked);
|
|
if (!$hatBankbuchung && $bankTxId > 0) {
|
|
$hatBankbuchung = true;
|
|
}
|
|
}
|
|
if ($bankTxId > 0) {
|
|
$bankTx = BankTransaction::find($bankTxId);
|
|
$bankTxDatum = $bankTx['wertstellung'] ?? '';
|
|
}
|
|
|
|
echo $this->renderView('buchungsvorlagen/modal', [
|
|
'vorlage' => $vorlage,
|
|
'felder' => $felder,
|
|
'beleg' => $beleg,
|
|
'belegId' => $belegId,
|
|
'zahlungskonten' => array_values($zahlungskonten),
|
|
'hatBankbuchung' => $hatBankbuchung,
|
|
'bankTxId' => $bankTxId,
|
|
'bankTxDatum' => $bankTxDatum,
|
|
]);
|
|
}
|
|
|
|
// ── Buchungen ausführen ────────────────────────────────────────────────
|
|
|
|
public function buchen(array $params): void
|
|
{
|
|
$belegId = (int)$params['belegId'];
|
|
$vorlageId = (int)$params['vorlageId'];
|
|
|
|
$beleg = Beleg::find($belegId);
|
|
if (!$beleg) { http_response_code(404); return; }
|
|
|
|
$datum = !empty($_POST['buchungs_datum']) ? $_POST['buchungs_datum'] : date('Y-m-d');
|
|
$bankTxId = (int)($_POST['bank_tx_id'] ?? 0);
|
|
|
|
try {
|
|
$svc = new VorlageBuchungsService();
|
|
$svc->buchen($belegId, $vorlageId, $_POST, $datum);
|
|
|
|
if ($bankTxId > 0) {
|
|
$totalBrutto = \Steuerkram\Models\Booking::sumBruttoForBeleg($belegId);
|
|
BankTransaction::linkBeleg($bankTxId, $belegId, $totalBrutto);
|
|
}
|
|
} catch (\RuntimeException $e) {
|
|
View::redirect("/belege/{$belegId}", $e->getMessage(), 'error');
|
|
return;
|
|
}
|
|
|
|
$msg = $bankTxId > 0 ? 'Buchungen aus Vorlage erfasst und Bankbuchung verknüpft.' : 'Buchungen aus Vorlage erfasst.';
|
|
View::redirect("/belege/{$belegId}", $msg);
|
|
}
|
|
|
|
// ── Hilfsmethoden ──────────────────────────────────────────────────────
|
|
|
|
private function validate(array $data): array
|
|
{
|
|
$errors = [];
|
|
if (empty(trim($data['name'] ?? ''))) {
|
|
$errors['name'] = 'Name ist ein Pflichtfeld.';
|
|
}
|
|
return $errors;
|
|
}
|
|
|
|
private function parseFelderFromPost(array $data): array
|
|
{
|
|
$felder = [];
|
|
$raws = $data['felder'] ?? [];
|
|
foreach ($raws as $f) {
|
|
if (empty(trim($f['feldname'] ?? ''))) continue;
|
|
$felder[] = [
|
|
'gruppe_nr' => (int)($f['gruppe_nr'] ?? 1),
|
|
'sort_order' => (int)($f['sort_order'] ?? 0),
|
|
'feldname' => trim($f['feldname']),
|
|
'label' => trim($f['label'] ?? ''),
|
|
'typ' => $f['typ'] ?? 'text',
|
|
'ust_satz' => $f['ust_satz'] ?? '',
|
|
'meta_key' => trim($f['meta_key'] ?? ''),
|
|
];
|
|
}
|
|
return $felder;
|
|
}
|
|
|
|
private function parseRegelnFromPost(array $data): array
|
|
{
|
|
$regeln = [];
|
|
$raws = $data['regeln'] ?? [];
|
|
foreach ($raws as $r) {
|
|
if (empty(trim($r['bedingung_feld'] ?? ''))) continue;
|
|
$buchungen = [];
|
|
foreach (($r['buchungen'] ?? []) as $b) {
|
|
if (empty(trim($b['betrag_ref'] ?? '')) || empty(trim($b['nach_konto'] ?? ''))) continue;
|
|
$buchungen[] = [
|
|
'betrag_ref' => trim($b['betrag_ref']),
|
|
'nach_konto' => trim($b['nach_konto']),
|
|
];
|
|
}
|
|
$regeln[] = [
|
|
'sort_order' => (int)($r['sort_order'] ?? 0),
|
|
'bedingung_feld' => trim($r['bedingung_feld']),
|
|
'bedingung_op' => $r['bedingung_op'] ?? 'neq_zero',
|
|
'buchungen' => $buchungen,
|
|
];
|
|
}
|
|
return $regeln;
|
|
}
|
|
}
|