79 lines
2.7 KiB
PHP
79 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Livewire\Concerns\ResolvesCustomer;
|
|
use App\Models\Instance;
|
|
use LivewireUI\Modal\ModalComponent;
|
|
|
|
/**
|
|
* Cancel the customer's package (R5). Per the founder's decision: effective at
|
|
* the end of the billing term, irreversible once confirmed; at term end the
|
|
* customer receives a finished data export, then the instance is deprovisioned
|
|
* (both mocked for now). Requires typing the instance subdomain to confirm.
|
|
*/
|
|
class ConfirmCancelPackage extends ModalComponent
|
|
{
|
|
use ResolvesCustomer;
|
|
|
|
public string $confirmName = '';
|
|
|
|
public function cancelPackage()
|
|
{
|
|
$customer = $this->customer();
|
|
// Target the ACTIVE package explicitly — a newer failed/deprovisioned
|
|
// record must not shadow an older active instance (v1: one active/customer).
|
|
$instance = $customer?->instances()->where('status', 'active')->latest('id')->first();
|
|
|
|
if ($instance === null) {
|
|
// Explain rather than silently closing: no customer behind this login
|
|
// (e.g. an operator account) or no active package to cancel.
|
|
$this->addError('confirmName', __($customer === null ? 'dashboard.no_customer_action' : 'settings.no_package'));
|
|
|
|
return null;
|
|
}
|
|
|
|
// Typed confirmation must match the instance subdomain.
|
|
if (trim($this->confirmName) !== (string) $instance->subdomain) {
|
|
$this->addError('confirmName', __('settings.cancel_mismatch'));
|
|
|
|
return;
|
|
}
|
|
|
|
$instance->update([
|
|
'status' => 'cancellation_scheduled',
|
|
'cancel_requested_at' => now(),
|
|
'service_ends_at' => $this->currentPeriodEnd($instance),
|
|
]);
|
|
|
|
return $this->redirectRoute('settings', navigate: true);
|
|
}
|
|
|
|
/**
|
|
* End of the current monthly billing period, anchored on the subscription
|
|
* start (the order date) rather than assuming calendar-month billing.
|
|
*/
|
|
private function currentPeriodEnd(Instance $instance): \Illuminate\Support\Carbon
|
|
{
|
|
$start = $instance->order?->created_at ?? $instance->created_at ?? now();
|
|
|
|
// Always add from the immutable start so the billing-day anchor is kept
|
|
// (chaining addMonth would drift a Jan-31 start to Feb-28 → Mar-28).
|
|
$months = 1;
|
|
while ($start->copy()->addMonthsNoOverflow($months)->lessThanOrEqualTo(now())) {
|
|
$months++;
|
|
}
|
|
|
|
return $start->copy()->addMonthsNoOverflow($months);
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
$instance = $this->customer()?->instances()->where('status', 'active')->latest('id')->first();
|
|
|
|
return view('livewire.confirm-cancel-package', [
|
|
'subdomain' => $instance?->subdomain ?? '',
|
|
]);
|
|
}
|
|
}
|