96 lines
4.1 KiB
PHP
96 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Livewire\Concerns\ResolvesCustomer;
|
|
use App\Services\Billing\AddonCatalogue;
|
|
use App\Services\Billing\CustomDomainAccess;
|
|
use LivewireUI\Modal\ModalComponent;
|
|
|
|
/**
|
|
* Confirmation before several storage packs go into the cart at once (R23).
|
|
*
|
|
* The offer comes from a card that has just told the customer their downgrade is
|
|
* blocked, and it answers with a recurring charge — three packs is thirty euros
|
|
* a month, every month, until they cancel them. That is not something a stray
|
|
* click on a card about SAVING money should do, and the dialog that says so is
|
|
* drawn by this product rather than by the browser.
|
|
*
|
|
* No mutation here. Billing::bookStoragePacks() keeps the work and the customer
|
|
* resolution it already had; this only says yes, with the number it was opened
|
|
* for.
|
|
*/
|
|
class ConfirmBookStorage extends ModalComponent
|
|
{
|
|
use ResolvesCustomer;
|
|
|
|
public int $packs = 1;
|
|
|
|
public int $packGb = 0;
|
|
|
|
/** Warum es hier nichts zu kaufen gibt. Leer, solange es etwas gibt. */
|
|
public string $refusal = '';
|
|
|
|
public function mount(int $packs = 1): void
|
|
{
|
|
$catalogue = app(AddonCatalogue::class);
|
|
|
|
// Der eigene Vertrag, aufgelöst wie in jedem anderen Bestätigungs-Modal
|
|
// (siehe ConfirmCancelAddon, ConfirmRevokeSeat) — nicht aus dem Markup
|
|
// übernommen, denn ein Modal ist ohne die Middleware der Seite
|
|
// erreichbar. Dieselbe Auflösung, die Billing::purchase() nachher
|
|
// tatsächlich bucht (CustomDomainAccess::contractOf()): sonst könnte
|
|
// der Dialog eine Zahl versprechen, die die Aktion nicht einhält.
|
|
$subscription = app(CustomDomainAccess::class)->contractOf($this->customer());
|
|
|
|
// Geklemmt an der tatsächlich noch buchbaren Menge, nicht an der
|
|
// absoluten Grenze: ein Vertrag, der schon einen Block hält, darf im
|
|
// Dialog nicht mehr versprechen, als purchase() nachher wirklich in
|
|
// den Warenkorb legt.
|
|
//
|
|
// Kein `max(1, …)` mehr darum herum: bei null buchbaren Blöcken —
|
|
// Deckel erreicht oder Modul für dieses Paket nicht vorgesehen — machte
|
|
// das daraus wieder einen. Dieselbe Falle, die in purchase() schon
|
|
// behoben ist ($lines floorte auf eine Bestellzeile), hatte im Fenster
|
|
// überlebt: ein direkt geöffnetes oder veraltetes Fenster versprach
|
|
// einen Block und schickte eine Bestellung los, die purchase() danach
|
|
// ablehnt. Nach oben wird weiter geklemmt, nach unten nur noch auf
|
|
// null.
|
|
$max = $catalogue->bookableQuantity($subscription, AddonCatalogue::STORAGE);
|
|
$this->packs = min($max, max(1, $packs));
|
|
$this->packGb = $catalogue->packGb();
|
|
|
|
// Absage statt geschlossenem Fenster: der Kunde hat gerade auf eine
|
|
// Karte geklickt, die ihm diesen Ausweg angeboten hat, und ein Dialog,
|
|
// der sich wortlos wieder schließt, ist von einem kaputten Knopf nicht
|
|
// zu unterscheiden. In dem Satz, den er zu dieser Grenze überall sonst
|
|
// liest — derselbe, mit dem purchase() und BookAddon ablehnen.
|
|
$this->refusal = $this->packs > 0 ? '' : (string) (
|
|
$catalogue->availabilityRefusal($subscription, AddonCatalogue::STORAGE)
|
|
?? $catalogue->quantityRefusal($subscription, AddonCatalogue::STORAGE, 1)
|
|
);
|
|
}
|
|
|
|
public function proceed(): void
|
|
{
|
|
// Nichts buchbar, nichts losgeschickt — der Knopf, der hier ankommt,
|
|
// ist bei einer Absage gar nicht gezeichnet. Die Menge steht in einer
|
|
// öffentlichen Eigenschaft und damit im Browser, deshalb ist das hier
|
|
// nicht die Grenze, sondern nur die Ehrlichkeit des Fensters: gehalten
|
|
// wird sie in purchase() und in BookAddon, wo das Geld fließt.
|
|
if ($this->packs < 1) {
|
|
$this->closeModal();
|
|
|
|
return;
|
|
}
|
|
|
|
$this->dispatch('storage-packs-confirmed', packs: $this->packs);
|
|
$this->closeModal();
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.confirm-book-storage');
|
|
}
|
|
}
|