diff --git a/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.html b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.html
new file mode 100644
index 0000000..0f470ff
--- /dev/null
+++ b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.html
@@ -0,0 +1,30 @@
+
Automatischen Versand einrichten
+
+
+ E-Mail-Adresse hinzufügen
+
+
+
+ @for (recipient of recipients(); track recipient) {
+
+ {{ recipient }}
+
+
+ }
+
+
+
+
+
+
+
diff --git a/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.spec.ts b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.spec.ts
new file mode 100644
index 0000000..45d7910
--- /dev/null
+++ b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.spec.ts
@@ -0,0 +1,62 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { of } from 'rxjs';
+import { CashboxExportApi } from '../../../../core/team/cashbox-export-api';
+import { CashboxExportSubscriptionDialog } from './cashbox-export-subscription-dialog';
+
+describe('CashboxExportSubscriptionDialog', () => {
+ let fixture: ComponentFixture;
+ let getSubscription: ReturnType;
+ let updateSubscription: ReturnType;
+ let dialogRef: { close: ReturnType };
+
+ beforeEach(async () => {
+ getSubscription = vi.fn(() =>
+ of({ recipients: ['a@example.com'], interval: 'monthly', active: true, nextRunDate: '2026-09-01T00:00:00.000Z' }),
+ );
+ updateSubscription = vi.fn(() =>
+ of({ recipients: ['a@example.com', 'b@example.com'], interval: 'monthly', active: true, nextRunDate: '2026-09-01T00:00:00.000Z' }),
+ );
+ dialogRef = { close: vi.fn() };
+
+ await TestBed.configureTestingModule({
+ imports: [CashboxExportSubscriptionDialog],
+ providers: [
+ { provide: MAT_DIALOG_DATA, useValue: { teamId: 5 } },
+ { provide: MatDialogRef, useValue: dialogRef },
+ { provide: CashboxExportApi, useValue: { getSubscription, updateSubscription } },
+ ],
+ }).compileComponents();
+ fixture = TestBed.createComponent(CashboxExportSubscriptionDialog);
+ fixture.detectChanges();
+ });
+
+ it('loads the existing subscription into the form', () => {
+ expect(getSubscription).toHaveBeenCalledWith(5);
+ expect(fixture.componentInstance['recipients']()).toEqual(['a@example.com']);
+ });
+
+ it('rejects adding an invalid email', () => {
+ fixture.componentInstance['addRecipient']('not-an-email');
+ expect(fixture.componentInstance['recipients']()).toEqual(['a@example.com']);
+ });
+
+ it('adds a valid email and saves the updated recipient list', () => {
+ fixture.componentInstance['addRecipient']('b@example.com');
+ expect(fixture.componentInstance['recipients']()).toEqual(['a@example.com', 'b@example.com']);
+
+ fixture.componentInstance['save']();
+
+ expect(updateSubscription).toHaveBeenCalledWith(5, {
+ recipients: ['a@example.com', 'b@example.com'],
+ interval: 'monthly',
+ active: true,
+ });
+ expect(dialogRef.close).toHaveBeenCalled();
+ });
+
+ it('removes a recipient', () => {
+ fixture.componentInstance['removeRecipient']('a@example.com');
+ expect(fixture.componentInstance['recipients']()).toEqual([]);
+ });
+});
diff --git a/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.ts b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.ts
new file mode 100644
index 0000000..b052c07
--- /dev/null
+++ b/myteamwallet_frontend_modern/src/app/features/team/cashbox/cashbox-export-subscription-dialog/cashbox-export-subscription-dialog.ts
@@ -0,0 +1,67 @@
+import { Component, inject, signal } from '@angular/core';
+import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+import { MatIconModule } from '@angular/material/icon';
+import { CashboxExportApi } from '../../../../core/team/cashbox-export-api';
+import { RecurringTransactionInterval } from '../../../../models/recurring-transaction.model';
+
+const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+@Component({
+ selector: 'app-cashbox-export-subscription-dialog',
+ imports: [
+ ReactiveFormsModule,
+ MatButtonModule,
+ MatChipsModule,
+ MatDialogModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatSelectModule,
+ MatSlideToggleModule,
+ ],
+ templateUrl: './cashbox-export-subscription-dialog.html',
+})
+export class CashboxExportSubscriptionDialog {
+ protected readonly dialogRef = inject(MatDialogRef);
+ private readonly data = inject<{ teamId: number }>(MAT_DIALOG_DATA);
+ private readonly formBuilder = inject(FormBuilder);
+ private readonly api = inject(CashboxExportApi);
+
+ protected readonly recipients = signal([]);
+ protected readonly form = this.formBuilder.nonNullable.group({
+ interval: ['monthly' as RecurringTransactionInterval, Validators.required],
+ active: [false],
+ });
+
+ constructor() {
+ this.api.getSubscription(this.data.teamId).subscribe((subscription) => {
+ this.recipients.set(subscription.recipients);
+ this.form.setValue({ interval: subscription.interval, active: subscription.active });
+ });
+ }
+
+ protected addRecipient(value: string): void {
+ const trimmed = value.trim();
+ if (!EMAIL_PATTERN.test(trimmed) || this.recipients().includes(trimmed)) return;
+ this.recipients.set([...this.recipients(), trimmed]);
+ }
+
+ protected removeRecipient(value: string): void {
+ this.recipients.set(this.recipients().filter((entry) => entry !== value));
+ }
+
+ protected save(): void {
+ if (this.form.invalid) return;
+ const { interval, active } = this.form.getRawValue();
+ this.api
+ .updateSubscription(this.data.teamId, { recipients: this.recipients(), interval, active })
+ .subscribe(() => this.dialogRef.close());
+ }
+}