Warenwirtschaft: Bugfix — Logistik-Mail hängt den Lieferschein IMMER an

Kundenmeldung 16.07.2026: Fehlversand an die Rechnungsadresse, weil der
Lieferschein (mit der Lieferadresse) in der Versand-Mail fehlte. Ursache:
MailLogistic hängte ihn nur bei White-Label-Bestellungen an. Jetzt wird der
Lieferschein unabhängig von Logo/White-Label immer mitgesendet; fehlt die
PDF-Datei auf der Platte (Altdaten), wird sie mit bestehender Rechnungsnummer
regeneriert. Dazu tote Duplikat-Klasse app/Mail/MailLogitic.php entfernt
(Tippfehler-Dateiname, definierte dieselbe Klasse in einer Altversion ganz
ohne Lieferschein-Anhang — Kollisionsrisiko im Classmap-Autoload).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-16 12:11:37 +00:00
parent 748f259949
commit 7d77314805
3 changed files with 178 additions and 74 deletions

View file

@ -3,6 +3,7 @@
namespace App\Mail; namespace App\Mail;
use App\Models\ShoppingOrder; use App\Models\ShoppingOrder;
use App\Repositories\InvoiceRepository;
use App\Services\Invoice; use App\Services\Invoice;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
@ -65,21 +66,20 @@ class MailLogistic extends Mailable
'mime' => $mime, 'mime' => $mime,
]); ]);
// Wenn mindestens ein White-Label-Produkt enthalten ist, wird der Lieferschein (Etikett-Infos) mit angehängt // Kundenanforderung 16.07.2026: Der Lieferschein (mit der Lieferadresse der
$hasWhiteLabel = $this->shopping_order->user_white_label || $this->shopping_order->hasWhitelabelProducts(); // Bestellung) wird IMMER mitgesendet — unabhängig von White-Label/Logo.
if ($hasWhiteLabel) { // Vorher hing er nur bei White-Label-Bestellungen an; Folge waren
$filenameDelivery = Invoice::getDeliveryFilename($this->shopping_order); // Fehlversände an die Rechnungsadresse.
$pathDelivery = Invoice::getDownloadPathDelivery($this->shopping_order); $pathDelivery = Invoice::getDownloadPathDelivery($this->shopping_order);
if (! Storage::disk('public')->exists($pathDelivery)) { if (! Storage::disk('public')->exists($pathDelivery)) {
return; // Datei fehlt auf der Platte (Altdaten) — mit bestehender Nummer neu erzeugen
} (new InvoiceRepository($this->shopping_order))->update([]);
$fileDelivery = Storage::disk('public')->path($pathDelivery); }
$mimeDelivery = Storage::disk('public')->mimeType($pathDelivery); if (Storage::disk('public')->exists($pathDelivery)) {
$mail->attach(Storage::disk('public')->path($pathDelivery), [
$mail->attach($fileDelivery, [ 'as' => Invoice::getDeliveryFilename($this->shopping_order),
'as' => $filenameDelivery, 'mime' => Storage::disk('public')->mimeType($pathDelivery),
'mime' => $mimeDelivery, ]);
]); // attach file;
} }
return $mail; return $mail;

View file

@ -1,59 +0,0 @@
<?php
namespace App\Mail;
use App\User;
use App\Services\Invoice;
use App\Models\ShoppingOrder;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Storage;
use Illuminate\Contracts\Queue\ShouldQueue;
class MailLogistic extends Mailable
{
use Queueable, SerializesModels;
protected $shopping_order;
public $subject;
public function __construct(ShoppingOrder $shopping_order)
{
$this->shopping_order = $shopping_order;
if($shopping_order->user_white_label){
//Bei allen, die ein eigenes Logo haben
$this->subject = 'Partner (mit Logo) - Firma (Vorname Nachname)';
}else{
//Bei allen, die kein Tattoostudio sind
$this->subject = 'Partner - Firma (Vorname Nachname)';
}
}
public function build()
{
$title = false;
$copy1line = false;
$filename = Invoice::getFilename($this->shopping_order);
$path = Invoice::getDownloadPath($this->shopping_order);
if (!Storage::disk('public')->exists($path)) {
return;
}
$file = Storage::disk('public')->path($path);
$mime = Storage::disk('public')->mimeType($path);
return $this->view('emails.blank')->with([
'title' => $title,
'copy1line' => $copy1line,
])->attach($file,[
'as' => $filename,
'mime' => $mime,
]); // attach file;
}
}

View file

@ -0,0 +1,163 @@
<?php
use App\Mail\MailLogistic;
use App\Models\Country;
use App\Models\Product;
use App\Models\Setting;
use App\Models\Shipping;
use App\Models\ShippingCountry;
use App\Models\ShoppingOrder;
use App\Models\ShoppingOrderItem;
use App\Models\ShoppingUser;
use App\Models\UserAccount;
use App\Repositories\InvoiceRepository;
use App\Services\Invoice;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
function lmMakeOrder(): ShoppingOrder
{
$country = Country::query()->forceCreate([
'code' => 'DE',
'en' => 'Germany',
'de' => 'Deutschland',
'es' => 'Alemania',
'fr' => 'Allemagne',
'it' => 'Germania',
'ru' => 'Germanija',
]);
$shipping = Shipping::query()->forceCreate(['name' => 'Standard']);
$shippingCountry = ShippingCountry::query()->forceCreate([
'shipping_id' => $shipping->id,
'country_id' => $country->id,
]);
$shoppingUser = ShoppingUser::query()->forceCreate([
'billing_country_id' => $country->id,
'shipping_country_id' => $country->id,
'billing_firstname' => 'Lisa',
'billing_lastname' => 'Logistik',
'billing_address' => 'Rechnungsweg 1',
'billing_zipcode' => '11111',
'billing_city' => 'Rechnungsstadt',
'same_as_billing' => false,
'shipping_firstname' => 'Anders',
'shipping_lastname' => 'Empfänger',
'shipping_address' => 'Liefergasse 9',
'shipping_zipcode' => '99999',
'shipping_city' => 'Lieferdorf',
]);
$authUser = User::query()->create([
'email' => uniqid('lm_', true).'@test.example',
'password' => bcrypt('password'),
]);
$account = UserAccount::query()->create([
'm_first_name' => 'Lena',
'm_last_name' => 'Partner',
'country_id' => $country->id,
]);
$authUser->forceFill(['account_id' => $account->id])->save();
$order = ShoppingOrder::query()->forceCreate([
'shopping_user_id' => $shoppingUser->id,
'auth_user_id' => $authUser->id,
'country_id' => $shippingCountry->id,
'shipped' => 0,
'txaction' => 'paid',
'total' => 20,
'user_white_label' => false,
]);
$product = Product::query()->create([
'name' => 'LM Creme',
'title' => 'LM Creme',
'active' => true,
'is_set' => false,
'whitelabel' => false,
]);
ShoppingOrderItem::query()->forceCreate([
'shopping_order_id' => $order->id,
'product_id' => $product->id,
'qty' => 1,
'price' => 10,
]);
return $order->fresh();
}
function lmCreateDocuments(ShoppingOrder $order): void
{
Setting::setContentBySlug('invoice-number', 4000, 'int');
$cwd = getcwd();
chdir(public_path());
try {
(new InvoiceRepository($order))->create([]);
} finally {
chdir($cwd);
}
}
test('logistik-mail haengt den lieferschein immer an - auch ohne white-label', function () {
Storage::fake('public');
$order = lmMakeOrder();
lmCreateDocuments($order);
$order->refresh();
expect($order->user_white_label)->toBeFalse()
->and($order->hasWhitelabelProducts())->toBeFalse();
$mailable = new MailLogistic($order);
$mailable->build();
$names = collect($mailable->attachments)->pluck('options.as');
expect($names)->toHaveCount(2)
->and($names->first(fn ($n) => str_starts_with((string) $n, 'Rechnung-')))->not->toBeNull()
->and($names->first(fn ($n) => str_starts_with((string) $n, 'Lieferschein-')))->not->toBeNull();
});
test('fehlende lieferschein-datei wird fuer die mail regeneriert', function () {
Storage::fake('public');
$order = lmMakeOrder();
lmCreateDocuments($order);
$order->refresh();
$deliveryPath = Invoice::getDownloadPathDelivery($order);
Storage::disk('public')->delete($deliveryPath);
expect(Storage::disk('public')->exists($deliveryPath))->toBeFalse();
$cwd = getcwd();
chdir(public_path());
try {
$mailable = new MailLogistic($order);
$mailable->build();
} finally {
chdir($cwd);
}
expect(Storage::disk('public')->exists($deliveryPath))->toBeTrue();
$names = collect($mailable->attachments)->pluck('options.as');
expect($names->first(fn ($n) => str_starts_with((string) $n, 'Lieferschein-')))->not->toBeNull();
});
test('versand-button verschickt die logistik-mail', function () {
Mail::fake();
Storage::fake('public');
$order = lmMakeOrder();
lmCreateDocuments($order);
$admin = User::query()->create([
'email' => uniqid('lmadmin_', true).'@test.example',
'password' => bcrypt('password'),
]);
$admin->forceFill(['admin' => 7, 'confirmed' => true, 'active' => true, 'wizard' => 100, 'blocked' => false])->save();
$this->actingAs($admin->fresh())
->get(route('admin_sales_send_logistic_mail', $order->id))
->assertRedirect(route('admin_sales_detail', $order->id));
Mail::assertSent(MailLogistic::class);
});