85 lines
2.2 KiB
PHP
85 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* One booked module, at the price it was booked at.
|
|
*
|
|
* Frozen for the same reason the subscription is: the customer agreed to a
|
|
* figure, and raising it afterwards would rewrite that agreement. Cancelling
|
|
* sets `cancelled_at` rather than deleting the row — what someone was paying,
|
|
* and until when, is evidence.
|
|
*/
|
|
class SubscriptionAddon extends Model
|
|
{
|
|
use HasFactory;
|
|
use HasUuids;
|
|
|
|
/** What the customer agreed to. */
|
|
public const FROZEN = ['subscription_id', 'addon_key', 'price_cents', 'currency', 'quantity', 'booked_at'];
|
|
|
|
protected $guarded = [];
|
|
|
|
protected static function booted(): void
|
|
{
|
|
static::updating(function (self $addon) {
|
|
$frozen = array_intersect(array_keys($addon->getDirty()), self::FROZEN);
|
|
|
|
if ($frozen !== []) {
|
|
throw new RuntimeException(
|
|
'A booked module is frozen at its booked price; tried to change: '.implode(', ', $frozen).
|
|
'. Cancel it and book it again at today\'s price instead.'
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'price_cents' => 'integer',
|
|
'quantity' => 'integer',
|
|
'booked_at' => 'datetime',
|
|
'cancelled_at' => 'datetime',
|
|
];
|
|
}
|
|
|
|
public function uniqueIds(): array
|
|
{
|
|
return ['uuid'];
|
|
}
|
|
|
|
public function subscription(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Subscription::class);
|
|
}
|
|
|
|
public function order(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Order::class);
|
|
}
|
|
|
|
public function scopeActive(Builder $query): Builder
|
|
{
|
|
return $query->whereNull('cancelled_at');
|
|
}
|
|
|
|
/** Net per month for this booking, packs included. */
|
|
public function monthlyCents(): int
|
|
{
|
|
return $this->price_cents * $this->quantity;
|
|
}
|
|
|
|
public function isActive(): bool
|
|
{
|
|
return $this->cancelled_at === null;
|
|
}
|
|
}
|