Merge pull request 'Fix #111: Zwei zusätzliche CC-E-Mail-Adressen pro Kunde' (#112) from fds2610-bot/Steuerkram:feature/kunden-cc-emails into main
Reviewed-on: https://codeberg.org/fds2610/Steuerkram/pulls/112
This commit is contained in:
commit
436cacf683
9 changed files with 110 additions and 6 deletions
10
Changes.md
10
Changes.md
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
---
|
||||
|
||||
## 2026-07-02 – Feature: Zusätzliche CC-E-Mail-Adressen pro Kunde (Issue #111)
|
||||
|
||||
- **`migrations/084_customer_cc_emails.sql`:** Neue Spalten `email_cc1`, `email_cc2` in `customers`
|
||||
- **`src/Models/Customer.php`:** `create()` und `update()` speichern die beiden CC-Adressen (Whitespace wird zu NULL normalisiert)
|
||||
- **`src/Views/customers/form.php`:** Zwei neue Eingabefelder „E-Mail CC 1/2" unter der Haupt-E-Mail
|
||||
- **`src/Controllers/InvoiceController.php`:** `show()` lädt den Kunden und übergibt ihn an die Detailansicht
|
||||
- **`src/Views/invoices/show.php`:** CC-Felder im E-Mail-Sende-Modal werden mit den hinterlegten Kunden-CC-Adressen vorbelegt
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-29 – Feature: Ereignisse-Log für Nextcloud-Import (Issue #109)
|
||||
|
||||
- **`migrations/083_system_events.sql`:** Neue Tabelle `system_events` (id, channel, message, created_at)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ CRUD für Lieferantenstammdaten. Lieferanten können Belegen zugeordnet werden;
|
|||
|
||||
CRUD für Kundenstammdaten. Kunden werden beim Erstellen von Ausgangsrechnungen ausgewählt.
|
||||
|
||||
- Pro Kunde können neben der Haupt-E-Mail-Adresse **zwei zusätzliche CC-Adressen** hinterlegt werden. Beim Rechnungsversand werden diese automatisch in die CC-Felder des E-Mail-Dialogs übernommen.
|
||||
|
||||
---
|
||||
|
||||
## Ausgangsrechnungen
|
||||
|
|
|
|||
7
migrations/084_customer_cc_emails.sql
Normal file
7
migrations/084_customer_cc_emails.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
-- 084_customer_cc_emails.sql
|
||||
-- Zwei zusätzliche CC-E-Mail-Adressen pro Kunde, die beim Rechnungsversand
|
||||
-- automatisch in die CC-Felder übernommen werden.
|
||||
|
||||
ALTER TABLE customers
|
||||
ADD COLUMN IF NOT EXISTS email_cc1 VARCHAR(255) NULL DEFAULT NULL AFTER email,
|
||||
ADD COLUMN IF NOT EXISTS email_cc2 VARCHAR(255) NULL DEFAULT NULL AFTER email_cc1;
|
||||
|
|
@ -131,6 +131,11 @@ class InvoiceController extends BaseController
|
|||
$accounts = Account::all(['aktiv' => 1]);
|
||||
}
|
||||
|
||||
// Kunde für CC-Vorbelegung im E-Mail-Modal
|
||||
$customer = !empty($invoice['customer_id'])
|
||||
? \Steuerkram\Models\Customer::find((int)$invoice['customer_id'])
|
||||
: null;
|
||||
|
||||
$view = new View();
|
||||
$view->render('layout/base.php', [
|
||||
'pageTitle' => 'Rechnung ' . ($invoice['rechnungs_nr'] ?? 'Entwurf'),
|
||||
|
|
@ -142,6 +147,7 @@ class InvoiceController extends BaseController
|
|||
'originalrechnung' => $originalrechnung,
|
||||
'bookings' => $bookings,
|
||||
'accounts' => $accounts,
|
||||
'customer' => $customer ?: null,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class Customer
|
|||
$nr = self::generateKundennummer();
|
||||
Database::execute(
|
||||
'INSERT INTO customers (kundennummer, firma, anrede, vorname, nachname, strasse, plz, ort, land,
|
||||
email, telefon, ust_id, steuernummer, payment_days, notes, mail_subject_template, mail_body_template)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
email, email_cc1, email_cc2, telefon, ust_id, steuernummer, payment_days, notes, mail_subject_template, mail_body_template)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
$data['kundennummer'] ?? $nr,
|
||||
$data['firma'] ?? null,
|
||||
|
|
@ -52,6 +52,8 @@ class Customer
|
|||
$data['ort'],
|
||||
$data['land'] ?? 'DE',
|
||||
$data['email'] ?? null,
|
||||
trim($data['email_cc1'] ?? '') !== '' ? trim($data['email_cc1']) : null,
|
||||
trim($data['email_cc2'] ?? '') !== '' ? trim($data['email_cc2']) : null,
|
||||
$data['telefon'] ?? null,
|
||||
$data['ust_id'] ?? null,
|
||||
$data['steuernummer'] ?? null,
|
||||
|
|
@ -68,7 +70,7 @@ class Customer
|
|||
{
|
||||
Database::execute(
|
||||
'UPDATE customers SET firma=?, anrede=?, vorname=?, nachname=?, strasse=?, plz=?, ort=?, land=?,
|
||||
email=?, telefon=?, ust_id=?, steuernummer=?, payment_days=?, notes=?,
|
||||
email=?, email_cc1=?, email_cc2=?, telefon=?, ust_id=?, steuernummer=?, payment_days=?, notes=?,
|
||||
mail_subject_template=?, mail_body_template=?, updated_at=NOW()
|
||||
WHERE id=?',
|
||||
[
|
||||
|
|
@ -81,6 +83,8 @@ class Customer
|
|||
$data['ort'],
|
||||
$data['land'] ?? 'DE',
|
||||
$data['email'] ?? null,
|
||||
trim($data['email_cc1'] ?? '') !== '' ? trim($data['email_cc1']) : null,
|
||||
trim($data['email_cc2'] ?? '') !== '' ? trim($data['email_cc2']) : null,
|
||||
$data['telefon'] ?? null,
|
||||
$data['ust_id'] ?? null,
|
||||
$data['steuernummer'] ?? null,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid form-grid-2">
|
||||
<div class="form-group">
|
||||
<label for="email_cc1">E-Mail CC 1 <span style="font-weight:normal;color:var(--color-text-muted)">(optional)</span></label>
|
||||
<input type="email" id="email_cc1" name="email_cc1" value="<?= $h($customer['email_cc1'] ?? '') ?>"
|
||||
placeholder="cc1@example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email_cc2">E-Mail CC 2 <span style="font-weight:normal;color:var(--color-text-muted)">(optional)</span></label>
|
||||
<input type="email" id="email_cc2" name="email_cc2" value="<?= $h($customer['email_cc2'] ?? '') ?>"
|
||||
placeholder="cc2@example.com">
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size:.78rem;color:var(--color-text-muted);margin:-.5rem 0 1rem">
|
||||
Zusätzliche CC-Adressen werden beim Rechnungsversand automatisch als Kopie vorgeschlagen.
|
||||
</p>
|
||||
|
||||
<div class="form-grid form-grid-3">
|
||||
<div class="form-group">
|
||||
<label for="ust_id">Umsatzsteuer-ID</label>
|
||||
|
|
|
|||
|
|
@ -370,11 +370,13 @@ $badgeClass = match($invoice['status']) {
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label>CC 1 <span style="font-weight:normal;color:#888">(optional)</span></label>
|
||||
<input type="email" name="email_cc1" placeholder="cc1@example.com">
|
||||
<input type="email" name="email_cc1" placeholder="cc1@example.com"
|
||||
value="<?= $h($customer['email_cc1'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>CC 2 <span style="font-weight:normal;color:#888">(optional)</span></label>
|
||||
<input type="email" name="email_cc2" placeholder="cc2@example.com">
|
||||
<input type="email" name="email_cc2" placeholder="cc2@example.com"
|
||||
value="<?= $h($customer['email_cc2'] ?? '') ?>">
|
||||
</div>
|
||||
<div style="display:flex;gap:.75rem;justify-content:flex-end;margin-top:1.25rem">
|
||||
<button type="button" class="btn btn-secondary"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1-260629
|
||||
1-260702
|
||||
|
|
|
|||
57
tests/48_CustomerCcEmailsTest.php
Normal file
57
tests/48_CustomerCcEmailsTest.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Steuerkram\Models\Customer;
|
||||
|
||||
class CustomerCcEmailsTest extends TestCase
|
||||
{
|
||||
protected string $testName = 'Test 48: Zusätzliche CC-E-Mail-Adressen pro Kunde';
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
// Kunde mit zwei CC-Adressen anlegen
|
||||
$id = Customer::create([
|
||||
'nachname' => 'CC-Test',
|
||||
'strasse' => 'Musterweg 1',
|
||||
'plz' => '12345',
|
||||
'ort' => 'Musterstadt',
|
||||
'email' => 'haupt@example.com',
|
||||
'email_cc1' => 'kopie1@example.com',
|
||||
'email_cc2' => 'kopie2@example.com',
|
||||
]);
|
||||
$this->assertGreaterThan(0, $id, 'Kunde angelegt');
|
||||
|
||||
$customer = Customer::find($id);
|
||||
$this->assertEquals('kopie1@example.com', $customer['email_cc1'], 'CC1 gespeichert');
|
||||
$this->assertEquals('kopie2@example.com', $customer['email_cc2'], 'CC2 gespeichert');
|
||||
|
||||
// Leere CC-Felder werden als NULL gespeichert
|
||||
Customer::update($id, [
|
||||
'nachname' => 'CC-Test',
|
||||
'strasse' => 'Musterweg 1',
|
||||
'plz' => '12345',
|
||||
'ort' => 'Musterstadt',
|
||||
'email' => 'haupt@example.com',
|
||||
'email_cc1' => '',
|
||||
'email_cc2' => ' ',
|
||||
]);
|
||||
$customer = Customer::find($id);
|
||||
$this->assert($customer['email_cc1'] === null, 'Leeres CC1 wird NULL');
|
||||
$this->assert($customer['email_cc2'] === null, 'Whitespace-CC2 wird NULL');
|
||||
|
||||
// CC1 aktualisieren, mit Trimmen
|
||||
Customer::update($id, [
|
||||
'nachname' => 'CC-Test',
|
||||
'strasse' => 'Musterweg 1',
|
||||
'plz' => '12345',
|
||||
'ort' => 'Musterstadt',
|
||||
'email' => 'haupt@example.com',
|
||||
'email_cc1' => ' neu@example.com ',
|
||||
'email_cc2' => 'kopie2@example.com',
|
||||
]);
|
||||
$customer = Customer::find($id);
|
||||
$this->assertEquals('neu@example.com', $customer['email_cc1'], 'CC1 getrimmt aktualisiert');
|
||||
$this->assertEquals('kopie2@example.com', $customer['email_cc2'], 'CC2 aktualisiert');
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue