diff --git a/app/Invoice/Actions/HasValidation.php b/app/Invoice/Actions/HasValidation.php index 652ed662..20a2084f 100644 --- a/app/Invoice/Actions/HasValidation.php +++ b/app/Invoice/Actions/HasValidation.php @@ -17,13 +17,13 @@ trait HasValidation 'status' => ['required', 'string', 'max:255', Rule::in(InvoiceStatus::values())], 'via' => ['required', 'string', 'max:255', Rule::in(BillKind::values())], 'usage' => 'required|max:255|string', - 'mail_email' => 'nullable|string|max:255|email', 'to' => 'array', 'to.address' => 'required|string|max:255', 'to.location' => 'required|string|max:255', 'to.zip' => 'required|string|max:255', 'to.name' => 'required|string|max:255', - 'greeting' => 'required|string|max:255', + 'to.email' => 'nullable|string|max:255|email', + 'to.greeting' => 'required|string|max:255', 'positions' => 'array', 'positions.*.description' => 'required|string|max:300', 'positions.*.price' => 'required|integer|min:0', @@ -42,6 +42,8 @@ trait HasValidation 'to.name' => 'Name', 'to.zip' => 'PLZ', 'to.location' => 'Ort', + 'to.greeting' => 'Anrede', + 'to.email' => 'E-Mail-Adresse', 'status' => 'Status', 'via' => 'Rechnungsweg', 'usage' => 'Verwendungszweck', diff --git a/app/Invoice/Actions/InvoiceSendAction.php b/app/Invoice/Actions/InvoiceSendAction.php index e66d8b5b..f6e504f2 100644 --- a/app/Invoice/Actions/InvoiceSendAction.php +++ b/app/Invoice/Actions/InvoiceSendAction.php @@ -37,14 +37,14 @@ class InvoiceSendAction foreach (Invoice::whereNeedsBill()->where('via', BillKind::EMAIL)->get() as $invoice) { $document = BillDocument::fromInvoice($invoice); $path = Storage::disk('temp')->path(Tex::compile($document)->storeIn('', 'temp')); - Mail::to($invoice->getMailRecipient())->send(new BillMail($invoice, $path)); + Mail::to($invoice->getRecipient())->send(new BillMail($invoice, $path)); $invoice->sent($document); } foreach (Invoice::whereNeedsRemember()->where('via', BillKind::EMAIL)->get() as $invoice) { $document = RememberDocument::fromInvoice($invoice); $path = Storage::disk('temp')->path(Tex::compile($document)->storeIn('', 'temp')); - Mail::to($invoice->getMailRecipient())->send(new RememberMail($invoice, $path)); + Mail::to($invoice->getRecipient())->send(new RememberMail($invoice, $path)); $invoice->sent($document); } diff --git a/app/Invoice/Data/ReceiverData.php b/app/Invoice/Data/ReceiverData.php new file mode 100644 index 00000000..f360c3bd --- /dev/null +++ b/app/Invoice/Data/ReceiverData.php @@ -0,0 +1,21 @@ +getRecipient(); return static::factory()->withoutMagicalCreation()->from([ - 'toName' => $invoice->to['name'], - 'toAddress' => $invoice->to['address'], - 'toZip' => $invoice->to['zip'], - 'toLocation' => $invoice->to['location'], - 'greeting' => $invoice->greeting, + 'toName' => $recipient->name, + 'toAddress' => $recipient->address, + 'toZip' => $recipient->zip, + 'toLocation' => $recipient->location, + 'greeting' => $recipient->greeting, 'positions' => static::renderPositions($invoice), 'usage' => $invoice->usage, ]); diff --git a/app/Invoice/Models/Invoice.php b/app/Invoice/Models/Invoice.php index e6bcd490..3fd26ffd 100644 --- a/app/Invoice/Models/Invoice.php +++ b/app/Invoice/Models/Invoice.php @@ -4,6 +4,7 @@ namespace App\Invoice\Models; use App\Invoice\BillDocument; use App\Invoice\BillKind; +use App\Invoice\Data\ReceiverData; use App\Invoice\Enums\InvoiceStatus; use App\Invoice\InvoiceDocument; use App\Invoice\InvoiceSettings; @@ -28,7 +29,7 @@ class Invoice extends Model public $guarded = []; public $casts = [ - 'to' => 'json', + 'to' => ReceiverData::class, 'status' => InvoiceStatus::class, 'via' => BillKind::class, 'sent_at' => 'datetime', @@ -54,12 +55,12 @@ class Invoice extends Model 'address' => $member->address, 'zip' => $member->zip, 'location' => $member->location, + 'greeting' => 'Liebe Familie ' . $member->lastname, + 'email' => $member->email_parents ?: $member->email, ], - 'greeting' => 'Liebe Familie ' . $member->lastname, 'status' => InvoiceStatus::NEW, 'via' => $member->bill_kind, 'usage' => 'Mitgliedsbeitrag für ' . $member->lastname, - 'mail_email' => $member->email_parents ?: $member->email, ]); $positions = collect([]); @@ -121,12 +122,8 @@ class Invoice extends Model ->where('last_remembered_at', '<=', now()->subWeeks($weeks)); } - public function getMailRecipient(): stdClass - { - return (object) [ - 'email' => $this->mail_email, - 'name' => $this->to['name'] - ]; + public function getRecipient(): ReceiverData { + return $this->to; } public function sent(InvoiceDocument $document): void @@ -157,8 +154,6 @@ class Invoice extends Model return [ 'to' => implode(', ', $this->to), 'usage' => $this->usage, - 'greeting' => $this->greeting, - 'mail_email' => $this->mail_email, 'status' => $this->status->value, ]; } diff --git a/app/Invoice/Resources/InvoiceResource.php b/app/Invoice/Resources/InvoiceResource.php index 99ed955d..6ddcc94c 100644 --- a/app/Invoice/Resources/InvoiceResource.php +++ b/app/Invoice/Resources/InvoiceResource.php @@ -3,6 +3,7 @@ namespace App\Invoice\Resources; use App\Invoice\BillKind; +use App\Invoice\Data\ReceiverData; use App\Invoice\Enums\InvoiceStatus; use App\Invoice\Models\Invoice; use App\Invoice\Scopes\InvoiceFilterScope; @@ -35,9 +36,7 @@ class InvoiceResource extends JsonResource 'status' => $this->status->value, 'via' => $this->via->value, 'positions' => InvoicePositionResource::collection($this->whenLoaded('positions')), - 'greeting' => $this->greeting, 'usage' => $this->usage, - 'mail_email' => $this->mail_email, 'links' => [ 'pdf' => route('invoice.pdf', ['invoice' => $this->getModel()]), 'rememberpdf' => route('invoice.rememberpdf', ['invoice' => $this->getModel()]), @@ -65,18 +64,11 @@ class InvoiceResource extends JsonResource 'subscriptions' => Subscription::forSelect(), 'filter' => InvoiceFilterScope::fromRequest(request()->input('filter', '')), 'default' => [ - 'to' => [ - 'name' => '', - 'address' => '', - 'zip' => '', - 'location' => '', - ], + 'to' => ReceiverData::default(), 'positions' => [], - 'greeting' => '', 'status' => InvoiceStatus::NEW->value, 'via' => null, 'usage' => '', - 'mail_email' => '', ], 'default_position' => [ 'id' => null, diff --git a/config/scout.php b/config/scout.php index 85d135f3..5c81343a 100644 --- a/config/scout.php +++ b/config/scout.php @@ -156,10 +156,10 @@ return [ ] ], Invoice::class => [ - 'filterableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'], - 'searchableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'], + 'filterableAttributes' => ['to', 'usage', 'status', 'id'], + 'searchableAttributes' => ['to', 'usage', 'status', 'id'], 'sortableAttributes' => [], - 'displayedAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'], + 'displayedAttributes' => ['to', 'usage', 'status', 'id'], 'pagination' => [ 'maxTotalHits' => 1000000, ] diff --git a/database/factories/Invoice/Models/InvoiceFactory.php b/database/factories/Invoice/Models/InvoiceFactory.php index 9001d2a8..dd9eef9f 100644 --- a/database/factories/Invoice/Models/InvoiceFactory.php +++ b/database/factories/Invoice/Models/InvoiceFactory.php @@ -24,12 +24,10 @@ class InvoiceFactory extends Factory public function definition() { return [ - 'greeting' => $this->faker->words(4, true), 'to' => ReceiverRequestFactory::new()->create(), 'status' => InvoiceStatus::NEW->value, 'via' => BillKind::POST->value, 'usage' => $this->faker->words(4, true), - 'mail_email' => $this->faker->safeEmail(), ]; } diff --git a/database/migrations/2026_06_19_225430_update_invoice_recipient.php b/database/migrations/2026_06_19_225430_update_invoice_recipient.php new file mode 100644 index 00000000..0f3e0fd7 --- /dev/null +++ b/database/migrations/2026_06_19_225430_update_invoice_recipient.php @@ -0,0 +1,32 @@ +count()) { + throw new \Exception('Migration not possibue for current bills.'); + } + + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('greeting'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('invoices', function (Blueprint $table) { + $table->string('greeting'); + }); + } +}; diff --git a/resources/js/views/invoice/Index.vue b/resources/js/views/invoice/Index.vue index 7882ce2c..8217fae5 100644 --- a/resources/js/views/invoice/Index.vue +++ b/resources/js/views/invoice/Index.vue @@ -41,16 +41,16 @@ Speichern - - + + - + + - diff --git a/resources/views/mail/invoice/bill.blade.php b/resources/views/mail/invoice/bill.blade.php index 35278c7b..7487dd98 100644 --- a/resources/views/mail/invoice/bill.blade.php +++ b/resources/views/mail/invoice/bill.blade.php @@ -1,5 +1,4 @@ @component('mail::message') -# {{ $invoice->greeting }}, Im Anhang findet ihr die aktuelle Rechnung an {{$settings->from}} für das laufende Jahr. Bitte begleicht diese bis zum angegebenen Datum. diff --git a/resources/views/mail/invoice/remember.blade.php b/resources/views/mail/invoice/remember.blade.php index 94bba5b5..6c007f13 100644 --- a/resources/views/mail/invoice/remember.blade.php +++ b/resources/views/mail/invoice/remember.blade.php @@ -1,5 +1,4 @@ @component('mail::message') -# {{ $invoice->greeting }}, Hiermit möchten wir euch an die noch ausstehenden Mitgliedsbeiträge an {{$settings->from}} für das laufende Jahr erinnern. Bitte begleicht diese bis zum angegebenen Datum. diff --git a/tests/Feature/Invoice/InvoiceRequestFactory.php b/tests/Feature/Invoice/InvoiceRequestFactory.php index b862dbed..61b6cb8c 100644 --- a/tests/Feature/Invoice/InvoiceRequestFactory.php +++ b/tests/Feature/Invoice/InvoiceRequestFactory.php @@ -15,7 +15,6 @@ class InvoiceRequestFactory extends RequestFactory { return [ 'to' => ReceiverRequestFactory::new(), - 'greeting' => 'Hallo Familie', 'status' => InvoiceStatus::NEW->value, 'via' => BillKind::EMAIL->value, 'positions' => [], diff --git a/tests/Feature/Invoice/InvoiceSendActionTest.php b/tests/Feature/Invoice/InvoiceSendActionTest.php index 7a6a48f8..d6d12602 100644 --- a/tests/Feature/Invoice/InvoiceSendActionTest.php +++ b/tests/Feature/Invoice/InvoiceSendActionTest.php @@ -29,10 +29,10 @@ class InvoiceSendActionTest extends TestCase Storage::fake('temp'); $this->withoutExceptionHandling()->login()->loginNami(); $invoice = Invoice::factory() - ->to(ReceiverRequestFactory::new()->name('Familie Muster')) + ->to(ReceiverRequestFactory::new()->name('Familie Muster')->email('max@muster.de')) ->has(InvoicePosition::factory()->withMember(), 'positions') ->via(BillKind::EMAIL) - ->create(['mail_email' => 'max@muster.de']); + ->create(); InvoiceSendAction::run(); @@ -50,11 +50,11 @@ class InvoiceSendActionTest extends TestCase $this->withoutExceptionHandling()->login()->loginNami(); app(InvoiceSettings::class)->fill(['from_long' => 'Stammname', 'replyTo' => 'reply@mail.com'])->save(); $invoice = Invoice::factory() - ->to(ReceiverRequestFactory::new()->name('Familie Muster')) + ->to(ReceiverRequestFactory::new()->name('Familie Muster')->email('max@muster.de')) ->has(InvoicePosition::factory()->withMember(), 'positions') ->via(BillKind::EMAIL) ->status(InvoiceStatus::SENT) - ->create(['sent_at' => now()->subMonths(6), 'mail_email' => 'max@muster.de', 'last_remembered_at' => now()->subMonths(6)]); + ->create(['sent_at' => now()->subMonths(6), 'last_remembered_at' => now()->subMonths(6)]); InvoiceSendAction::run(); diff --git a/tests/Feature/Invoice/InvoiceStoreActionTest.php b/tests/Feature/Invoice/InvoiceStoreActionTest.php index 877b2206..a6bec449 100644 --- a/tests/Feature/Invoice/InvoiceStoreActionTest.php +++ b/tests/Feature/Invoice/InvoiceStoreActionTest.php @@ -24,36 +24,32 @@ class InvoiceStoreActionTest extends TestCase $response = $this->postJson( route('invoice.store'), InvoiceRequestFactory::new() - ->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')) + ->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')->greeting('Hallo Familie')->email('a@b.de')) ->status(InvoiceStatus::PAID) ->via(BillKind::POST) - ->state([ - 'greeting' => 'Hallo Familie', - ]) ->position(InvoicePositionRequestFactory::new()->description('Beitrag Abc')->price(3250)->member($member)) - ->create(['mail_email' => 'a@b.de']) + ->create() ); $response->assertOk(); $this->assertDatabaseHas('invoices', [ - 'greeting' => 'Hallo Familie', 'via' => BillKind::POST->value, 'status' => InvoiceStatus::PAID->value, - 'mail_email' => 'a@b.de', ]); - $invoice = Invoice::firstWhere('greeting', 'Hallo Familie'); + $invoice = Invoice::firstWhere('to->greeting', 'Hallo Familie'); $this->assertDatabaseHas('invoice_positions', [ 'invoice_id' => $invoice->id, 'member_id' => $member->id, 'price' => 3250, 'description' => 'Beitrag Abc', ]); - $this->assertEquals([ - 'name' => 'Familie Blabla', - 'address' => 'Musterstr 44', - 'zip' => '22222', - 'location' => 'Solingen', - ], $invoice->to); + + $this->assertEquals('Familie Blabla', $invoice->to->name); + $this->assertEquals('Musterstr 44', $invoice->to->address); + $this->assertEquals('22222', $invoice->to->zip); + $this->assertEquals('Solingen', $invoice->to->location); + $this->assertEquals('Hallo Familie', $invoice->to->greeting); + $this->assertEquals('a@b.de', $invoice->to->email); } public static function validationDataProvider(): Generator diff --git a/tests/Feature/Invoice/InvoiceUpdateActionTest.php b/tests/Feature/Invoice/InvoiceUpdateActionTest.php index 5350931c..61b1b7c9 100644 --- a/tests/Feature/Invoice/InvoiceUpdateActionTest.php +++ b/tests/Feature/Invoice/InvoiceUpdateActionTest.php @@ -25,29 +25,27 @@ class InvoiceUpdateActionTest extends TestCase $this->patchJson( route('invoice.update', ['invoice' => $invoice]), InvoiceRequestFactory::new() - ->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')) + ->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')->greeting('Hallo Familie Blabla')) ->status(InvoiceStatus::PAID) ->via(BillKind::POST) ->state([ - 'greeting' => 'Hallo Familie', ]) ->create() )->assertOk(); $this->assertDatabaseCount('invoices', 1); $this->assertDatabaseHas('invoices', [ - 'greeting' => 'Hallo Familie', 'via' => BillKind::POST->value, 'status' => InvoiceStatus::PAID->value, 'id' => $invoice->id, ]); - $invoice = Invoice::firstWhere('greeting', 'Hallo Familie'); - $this->assertEquals([ - 'name' => 'Familie Blabla', - 'address' => 'Musterstr 44', - 'zip' => '22222', - 'location' => 'Solingen', - ], $invoice->to); + $invoice = Invoice::firstWhere('to->greeting', 'Hallo Familie Blabla'); + $this->assertEquals('Familie Blabla', $invoice->to->name); + $this->assertEquals('Musterstr 44', $invoice->to->address); + $this->assertEquals('22222', $invoice->to->zip); + $this->assertEquals('Solingen', $invoice->to->location); + $this->assertEquals('Hallo Familie Blabla', $invoice->to->greeting); + $this->assertNull($invoice->to->email); } public function testItAddsAPosition(): void diff --git a/tests/Feature/Invoice/MassStoreActionTest.php b/tests/Feature/Invoice/MassStoreActionTest.php index 2f51f0b0..9204d22f 100644 --- a/tests/Feature/Invoice/MassStoreActionTest.php +++ b/tests/Feature/Invoice/MassStoreActionTest.php @@ -57,14 +57,13 @@ class MassStoreActionTest extends TestCase $invoice = Invoice::first(); $this->assertNotNull($invoice); - $this->assertEquals([ - 'name' => 'Familie Muster', - 'address' => 'Maxstr 4', - 'zip' => '33445', - 'location' => 'Solingen', - ], $invoice->to); + $this->assertEquals('Familie Muster', $invoice->to->name); + $this->assertEquals('Maxstr 4', $invoice->to->address); + $this->assertEquals('33445', $invoice->to->zip); + $this->assertEquals('Solingen', $invoice->to->location); + $this->assertEquals('Liebe Familie Muster', $invoice->to->greeting); + $this->assertEquals('lala@b.de', $invoice->to->email); $this->assertEquals('Mitgliedsbeitrag für Muster', $invoice->usage); - $this->assertEquals('lala@b.de', $invoice->mail_email); $this->assertEquals(BillKind::EMAIL, $invoice->via); $this->assertDatabaseHas('invoice_positions', [ 'invoice_id' => $invoice->id, diff --git a/tests/Feature/Invoice/MemberNewInvoiceActionTest.php b/tests/Feature/Invoice/MemberNewInvoiceActionTest.php index 173c8760..bfc86e95 100644 --- a/tests/Feature/Invoice/MemberNewInvoiceActionTest.php +++ b/tests/Feature/Invoice/MemberNewInvoiceActionTest.php @@ -32,14 +32,14 @@ class MemberNewInvoiceActionTest extends TestCase $this->post(route('invoice.new-invoice-attributes'), ['member_id' => $member->id, 'year' => 2019, 'subscription_id' => $subscription->id]) ->assertOk() - ->assertJsonPath('greeting', 'Liebe Familie Muster') + ->assertJsonPath('to.greeting', 'Liebe Familie Muster') ->assertJsonPath('to.address', 'Maxstr 4') ->assertJsonPath('to.location', 'Solingen') ->assertJsonPath('to.zip', '33445') ->assertJsonPath('to.name', 'Familie Muster') + ->assertJsonPath('to.email', 'lala@b.de') ->assertJsonPath('usage', 'Mitgliedsbeitrag für Muster') ->assertJsonPath('via', 'E-Mail') - ->assertJsonPath('mail_email', 'lala@b.de') ->assertJsonPath('status', 'Neu') ->assertJsonPath('positions.0.description', 'beitrag Max Muster') ->assertJsonPath('positions.0.member_id', $member->id) diff --git a/tests/Feature/Invoice/ReceiverRequestFactory.php b/tests/Feature/Invoice/ReceiverRequestFactory.php index a01dc0e6..ce1bf243 100644 --- a/tests/Feature/Invoice/ReceiverRequestFactory.php +++ b/tests/Feature/Invoice/ReceiverRequestFactory.php @@ -13,6 +13,8 @@ class ReceiverRequestFactory extends RequestFactory 'address' => 'Musterstr 44', 'zip' => '22222', 'location' => 'Solingen', + 'greeting' => 'Liebe Familie Blabla', + 'email' => null, ]; } @@ -35,4 +37,14 @@ class ReceiverRequestFactory extends RequestFactory { return $this->state(['location' => $location]); } + + public function greeting(string $greeting): self + { + return $this->state(['greeting' => $greeting]); + } + + public function email(string $email): self + { + return $this->state(['email' => $email]); + } }