Fix #108: Lieferant in Belegliste inline bearbeiten
This commit is contained in:
parent
fe3f64ae89
commit
6ce2c72cd9
7 changed files with 152 additions and 4 deletions
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-06-29 – Feature: Lieferant in Belegliste inline bearbeiten (Issue #108)
|
||||||
|
|
||||||
|
- **`src/Views/belege/list.php`:** Lieferant-Spalte ist direkt in der Liste klickbar; zeigt ein Inline-Textfeld zur Eingabe, speichert mit Enter, bricht mit Escape ab; andere Spalten öffnen weiterhin die Vorschau
|
||||||
|
- **`src/Models/Beleg.php`:** Neue Methode `updateSupplier()` aktualisiert nur die `supplier_id` ohne andere Metadaten zu verändern
|
||||||
|
- **`src/Controllers/BelegController.php`:** Neue Methode `updateSupplier()` als AJAX-Endpunkt
|
||||||
|
- **`public/index.php`:** Route `POST /belege/{id}/supplier` registriert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-06-11 – Fix: Undefined-Array-Key-Warning bei Buchungsvorlagen ohne Standard-Lieferant
|
## 2026-06-11 – Fix: Undefined-Array-Key-Warning bei Buchungsvorlagen ohne Standard-Lieferant
|
||||||
|
|
||||||
- **`src/Models/Buchungsvorlage.php`:** `create()`/`update()` prüften `$data['standard_lieferant']` direkt ohne Null-Coalescing, was bei fehlendem Schlüssel eine PHP-Warning auslöste
|
- **`src/Models/Buchungsvorlage.php`:** `create()`/`update()` prüften `$data['standard_lieferant']` direkt ohne Null-Coalescing, was bei fehlendem Schlüssel eine PHP-Warning auslöste
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ $router->get('/belege/{id}', fn($p) => (new BelegController())->show($p));
|
||||||
$router->get('/belege/{id}/file', fn($p) => (new BelegController())->serveFile($p));
|
$router->get('/belege/{id}/file', fn($p) => (new BelegController())->serveFile($p));
|
||||||
$router->post('/belege/{id}/delete', fn($p) => (new BelegController())->destroy($p));
|
$router->post('/belege/{id}/delete', fn($p) => (new BelegController())->destroy($p));
|
||||||
$router->post('/belege/{id}/meta', fn($p) => (new BelegController())->updateMeta($p));
|
$router->post('/belege/{id}/meta', fn($p) => (new BelegController())->updateMeta($p));
|
||||||
|
$router->post('/belege/{id}/supplier', fn($p) => (new BelegController())->updateSupplier($p));
|
||||||
$router->post('/belege/{id}/crop', fn($p) => (new BelegController())->cropImage($p));
|
$router->post('/belege/{id}/crop', fn($p) => (new BelegController())->cropImage($p));
|
||||||
$router->post('/belege/{id}/vorschlag/generate', fn($p) => (new BelegController())->generateVorschlag($p));
|
$router->post('/belege/{id}/vorschlag/generate', fn($p) => (new BelegController())->generateVorschlag($p));
|
||||||
$router->post('/belege/{id}/vorschlag/reset', fn($p) => (new BelegController())->resetVorschlag($p));
|
$router->post('/belege/{id}/vorschlag/reset', fn($p) => (new BelegController())->resetVorschlag($p));
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,30 @@ class BelegController extends BaseController
|
||||||
View::redirect("/belege/{$id}", 'Metadaten gespeichert.');
|
View::redirect("/belege/{$id}", 'Metadaten gespeichert.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateSupplier(array $params): void
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$id = (int)$params['id'];
|
||||||
|
$beleg = Beleg::find($id);
|
||||||
|
|
||||||
|
if (!$beleg) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'Nicht gefunden.']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($beleg['locked']) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'GoBD: Gesperrter Beleg.']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$supplierName = trim($_POST['supplier_name'] ?? '');
|
||||||
|
$supplierId = $supplierName !== '' ? Supplier::findOrCreate($supplierName) : null;
|
||||||
|
Beleg::updateSupplier($id, $supplierId);
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true, 'supplier_name' => $supplierName !== '' ? $supplierName : null]);
|
||||||
|
}
|
||||||
|
|
||||||
public function generateVorschlag(array $params): void
|
public function generateVorschlag(array $params): void
|
||||||
{
|
{
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,14 @@ class Beleg
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function updateSupplier(int $id, ?int $supplierId): void
|
||||||
|
{
|
||||||
|
Database::execute(
|
||||||
|
'UPDATE belege SET supplier_id=?, updated_at=NOW() WHERE id=? AND locked=0',
|
||||||
|
[$supplierId, $id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static function delete(int $id): bool
|
public static function delete(int $id): bool
|
||||||
{
|
{
|
||||||
$beleg = self::find($id);
|
$beleg = self::find($id);
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,13 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="td-muted"><?= View::formatDate($b['beleg_datum']) ?></td>
|
<td class="td-muted"><?= View::formatDate($b['beleg_datum']) ?></td>
|
||||||
<td class="td-muted"><?= $h($b['supplier_name'] ?? '–') ?></td>
|
<td class="td-muted bl-supplier-cell"
|
||||||
|
data-beleg-id="<?= (int)$b['id'] ?>"
|
||||||
|
data-supplier="<?= $h($b['supplier_name'] ?? '') ?>"
|
||||||
|
onclick="event.stopPropagation(); blInlineSupplier(this)"
|
||||||
|
style="cursor:text;min-width:80px"
|
||||||
|
title="Klicken zum Lieferant bearbeiten"
|
||||||
|
><?= $h($b['supplier_name'] ?? '–') ?></td>
|
||||||
<td class="td-muted"><?= $h($b['rechnungs_nr'] ?? '–') ?></td>
|
<td class="td-muted"><?= $h($b['rechnungs_nr'] ?? '–') ?></td>
|
||||||
<td class="td-right td-mono"><?= $b['betrag_brutto'] !== null ? View::formatMoney($b['betrag_brutto']) : '–' ?></td>
|
<td class="td-right td-mono"><?= $b['betrag_brutto'] !== null ? View::formatMoney($b['betrag_brutto']) : '–' ?></td>
|
||||||
<td onclick="event.stopPropagation()">
|
<td onclick="event.stopPropagation()">
|
||||||
|
|
@ -245,4 +251,49 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
function blInlineSupplier(td) {
|
||||||
|
if (td.querySelector('input')) return;
|
||||||
|
|
||||||
|
const current = td.dataset.supplier || '';
|
||||||
|
const belegId = td.dataset.belegId;
|
||||||
|
let committed = false;
|
||||||
|
|
||||||
|
td.textContent = '';
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'text';
|
||||||
|
input.value = current;
|
||||||
|
input.style.cssText = 'width:100%;border:1px solid var(--color-primary,#3b82f6);border-radius:3px;padding:.1rem .3rem;font-size:inherit;background:#fff;box-sizing:border-box;color:inherit';
|
||||||
|
|
||||||
|
function commit(val) {
|
||||||
|
if (committed) return;
|
||||||
|
committed = true;
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('supplier_name', val);
|
||||||
|
fetch('/belege/' + belegId + '/supplier', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
body: fd,
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
td.dataset.supplier = val;
|
||||||
|
td.textContent = val || '–';
|
||||||
|
})
|
||||||
|
.catch(() => { td.textContent = current || '–'; });
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Enter') { e.preventDefault(); commit(input.value.trim()); }
|
||||||
|
if (e.key === 'Escape') { committed = true; td.textContent = current || '–'; }
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener('blur', function () {
|
||||||
|
commit(input.value.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
td.appendChild(input);
|
||||||
|
input.focus();
|
||||||
|
input.select();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1-260611
|
1-260629
|
||||||
|
|
|
||||||
55
tests/46_BelegLieferantInlineTest.php
Normal file
55
tests/46_BelegLieferantInlineTest.php
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Steuerkram\Models\Beleg;
|
||||||
|
use Steuerkram\Models\Supplier;
|
||||||
|
|
||||||
|
class BelegLieferantInlineTest extends TestCase
|
||||||
|
{
|
||||||
|
protected string $testName = 'Test 46: Beleg-Lieferant Inline-Update via updateSupplier()';
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$belegId = Beleg::create([
|
||||||
|
'beleg_nr' => 'TEST-SUPP-' . uniqid(),
|
||||||
|
'filename' => 'inline-supplier-test.pdf',
|
||||||
|
'storage_path' => '',
|
||||||
|
'mime_type' => 'application/pdf',
|
||||||
|
'file_hash' => hash('sha256', 'inline-supplier-' . uniqid()),
|
||||||
|
'beleg_datum' => date('Y-m-d'),
|
||||||
|
'belegtyp' => 'Eingangsrechnung',
|
||||||
|
]);
|
||||||
|
$this->assertGreaterThan(0, $belegId, 'Beleg angelegt');
|
||||||
|
|
||||||
|
// Kein Lieferant initial
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assert($beleg['supplier_name'] === null || $beleg['supplier_name'] === '', 'Kein Lieferant initial');
|
||||||
|
|
||||||
|
// Lieferant setzen
|
||||||
|
$supplierId = Supplier::findOrCreate('Testfirma GmbH');
|
||||||
|
$this->assertGreaterThan(0, $supplierId, 'Supplier angelegt oder gefunden');
|
||||||
|
Beleg::updateSupplier($belegId, $supplierId);
|
||||||
|
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assertEquals('Testfirma GmbH', $beleg['supplier_name'], 'Lieferant nach updateSupplier gesetzt');
|
||||||
|
|
||||||
|
// Lieferant auf null zurücksetzen (löschen)
|
||||||
|
Beleg::updateSupplier($belegId, null);
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assert(
|
||||||
|
$beleg['supplier_name'] === null || $beleg['supplier_name'] === '',
|
||||||
|
'Lieferant nach Nullsetzung leer'
|
||||||
|
);
|
||||||
|
|
||||||
|
// updateSupplier darf bei gesperrtem Beleg nichts ändern
|
||||||
|
\Steuerkram\Database::execute('UPDATE belege SET locked=1 WHERE id=?', [$belegId]);
|
||||||
|
$supplierId2 = Supplier::findOrCreate('Gesperrte Firma');
|
||||||
|
Beleg::updateSupplier($belegId, $supplierId2);
|
||||||
|
$beleg = Beleg::find($belegId);
|
||||||
|
$this->assert(
|
||||||
|
$beleg['supplier_name'] === null || $beleg['supplier_name'] === '',
|
||||||
|
'Lieferant bei gesperrtem Beleg unverändert'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue