CluPilotCloud/app/Models/SubscriptionRecord.php

119 lines
3.4 KiB
PHP

<?php
namespace App\Models;
use App\Models\Builders\AppendOnlyBuilder;
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 commercial event, written once and never again.
*
* A register that can be edited proves nothing. So the model refuses updates
* and deletes outright: a mistake is corrected by recording the correction,
* which is how ledgers have always worked and why they can be trusted.
*/
class SubscriptionRecord extends Model
{
use HasFactory;
use HasUuids;
public const EVENT_PURCHASE = 'purchase';
public const EVENT_UPGRADE = 'upgrade';
public const EVENT_DOWNGRADE = 'downgrade';
/** A term that renewed and was paid. Stripe's invoice says so. */
public const EVENT_RENEWAL = 'renewal';
/** A renewal Stripe could not collect. Their dunning takes it from here. */
public const EVENT_PAYMENT_FAILED = 'payment_failed';
/**
* A paid invoice that is not a cycle renewal — a proration, or a charge
* raised by hand. Money received, so it belongs in the register, but it
* does not start a new term and must not move the period.
*/
public const EVENT_INVOICE_PAID = 'invoice_paid';
public const EVENT_ADDON_BOOKED = 'addon_booked';
public const EVENT_ADDON_CANCELLED = 'addon_cancelled';
public const EVENT_CANCELLATION = 'cancellation';
/** The shape of the `snapshot` column. Bump when it changes. */
public const SNAPSHOT_VERSION = 1;
protected $guarded = [];
protected static function booted(): void
{
static::updating(function () {
throw new RuntimeException(
'The proof register is append-only. Record a correcting event instead of editing history.'
);
});
static::deleting(function () {
throw new RuntimeException(
'The proof register is append-only. A record that can be deleted is not evidence.'
);
});
}
protected function casts(): array
{
return [
'net_cents' => 'integer',
'tax_cents' => 'integer',
'gross_cents' => 'integer',
'tax_rate' => 'decimal:2',
'reverse_charge' => 'boolean',
'plan_version' => 'integer',
'snapshot_version' => 'integer',
'snapshot' => 'array',
'occurred_at' => 'datetime',
];
}
public function uniqueIds(): array
{
return ['uuid'];
}
/**
* The model events above only fire for instance operations. A bulk
* `query()->update()` or `->delete()` would sail straight past them, so the
* builder refuses those too.
*/
public function newEloquentBuilder($query): AppendOnlyBuilder
{
return new AppendOnlyBuilder($query);
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
}
public function subscription(): BelongsTo
{
return $this->belongsTo(Subscription::class);
}
public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}
public function planVersion(): BelongsTo
{
return $this->belongsTo(PlanVersion::class, 'plan_version_id');
}
}