Rechnung/Lieferschein: - PDF-Gruppierung Abo- vs. Einmal-Artikel (is_abo_addon), rueckwaertskompatibel - Vorschau-Command sales:preview-invoice (umgeht Test-System-Gate, ohne Rechnungsnummer) Reminder-Mail (vorbereitet, Cron inaktiv): - MailAboExecutionReminder + Template, abo:send-execution-reminder, Spalte onetime_reminder_sent_for - Berater -> Sales Center, Kunde -> Portal; Vorschau abo:preview-reminder - Datum-Fix: formatDateDB() statt formatDate() (ICU-Format) Sperrfristen konfigurierbar (Admin-Settings): - abo-lock-days-change (Liefertag + Abo-Produkte), abo-lock-days-pause-cancel - Meldungen mit :lock-Platzhalter; Pausieren/Kuendigen vom Change-Lock entkoppelt - Abo-Produkt-Aenderungen im Lock-Fenster server- + clientseitig eingefroren - Selbstbedienung: Add-only/Lock nach $view (kein Admin-Bypass), Stale-Cache-Fix beim Mengen-Update Dashboard: - Einmalprodukte-Card + Team-Abo-KPI-Card, News-Card responsiv (col-xl-6) Lokalisierung: - Sprachdateien en/es/fr vollstaendig mit de synchronisiert (inkl. marketingplan/portal) - LocalizationSyncTest sichert Key-Gleichheit ab Tests: AboOneTimeInvoiceGrouping, AboExecutionReminder, AboLockDaysSetting, AboOnetimeDashboardAndMail, PreviewInvoiceCommand, TeamAboOverview + erweiterte View-Tests Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.1 KiB
PHP
69 lines
2.1 KiB
PHP
<?php
|
||
|
||
namespace App\Mail;
|
||
|
||
use App\Models\UserAbo;
|
||
use App\Services\AboHelper;
|
||
use App\Services\Util;
|
||
use Carbon\Carbon;
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Mail\Mailable;
|
||
use Illuminate\Queue\SerializesModels;
|
||
|
||
class MailAboExecutionReminder extends Mailable
|
||
{
|
||
use Queueable, SerializesModels;
|
||
|
||
public function __construct(public UserAbo $userAbo) {}
|
||
|
||
public function build(): self
|
||
{
|
||
$isConsultant = $this->userAbo->is_for === 'me';
|
||
|
||
$salutation = __('email.hello');
|
||
$firstname = $this->userAbo->shopping_user?->billing_firstname;
|
||
if ($firstname) {
|
||
$salutation = __('email.hello').' '.$firstname;
|
||
}
|
||
|
||
$days = $this->daysUntilExecution();
|
||
|
||
return $this->subject(__('email.abo_reminder_subject'))
|
||
->view('emails.abo_execution_reminder')
|
||
->with([
|
||
'salutation' => $salutation,
|
||
'isConsultant' => $isConsultant,
|
||
'days' => $days,
|
||
'nextDate' => $this->userAbo->next_date
|
||
? Carbon::parse($this->userAbo->next_date)->format(\Util::formatDateDB())
|
||
: '',
|
||
'link' => $this->buildLink($isConsultant),
|
||
'greetings' => __('email.greetings'),
|
||
'sender' => __('email.sender'),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Berater erhalten den Link ins Sales Center (my.mivita.care), Endkunden ins
|
||
* Kundenportal (in.mivita.care) – jeweils zur Abo-Detail-/Übersichtsseite.
|
||
*/
|
||
private function buildLink(bool $isConsultant): string
|
||
{
|
||
if ($isConsultant) {
|
||
return rtrim(Util::getMyMivitaUrl(), '/').'/user/abos/detail/me/'.$this->userAbo->id;
|
||
}
|
||
|
||
return rtrim(Util::getMyMivitaPortalUrl(), '/').'/portal/my-subscriptions';
|
||
}
|
||
|
||
private function daysUntilExecution(): int
|
||
{
|
||
if (! $this->userAbo->next_date) {
|
||
return AboHelper::getOneTimeWindowDays();
|
||
}
|
||
|
||
$days = Carbon::today()->diffInDays(Carbon::parse($this->userAbo->next_date)->startOfDay(), false);
|
||
|
||
return (int) max(0, $days);
|
||
}
|
||
}
|