12-05-2026 Frontend dev
This commit is contained in:
parent
405df0a122
commit
5b8bdf4182
779 changed files with 480564 additions and 6241 deletions
41
app/Models/AdminPreset.php
Normal file
41
app/Models/AdminPreset.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\AdminPresetFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AdminPreset extends Model
|
||||
{
|
||||
/** @use HasFactory<AdminPresetFactory> */
|
||||
use HasFactory;
|
||||
|
||||
public const PRESS_RELEASE_DELETED_PUBLISHED_TEXT = 'press_releases.deleted_published_text';
|
||||
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'area',
|
||||
'type',
|
||||
'label',
|
||||
'value',
|
||||
'payload',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'payload' => 'array',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public static function activeValue(string $key, ?string $fallback = null): ?string
|
||||
{
|
||||
return static::query()
|
||||
->where('key', $key)
|
||||
->where('is_active', true)
|
||||
->value('value') ?? $fallback;
|
||||
}
|
||||
}
|
||||
42
app/Models/ApiUsageLog.php
Normal file
42
app/Models/ApiUsageLog.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
|
||||
class ApiUsageLog extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'personal_access_token_id',
|
||||
'method',
|
||||
'path',
|
||||
'route_name',
|
||||
'status_code',
|
||||
'ip_address',
|
||||
'user_agent',
|
||||
'duration_ms',
|
||||
'requested_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'requested_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function personalAccessToken(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PersonalAccessToken::class);
|
||||
}
|
||||
}
|
||||
29
app/Models/BillingAddress.php
Normal file
29
app/Models/BillingAddress.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class BillingAddress extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'salutation_key',
|
||||
'title',
|
||||
'name',
|
||||
'address1',
|
||||
'address2',
|
||||
'postal_code',
|
||||
'city',
|
||||
'country_code',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
58
app/Models/Category.php
Normal file
58
app/Models/Category.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use Database\Factories\CategoryFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
/** @use HasFactory<CategoryFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'parent_id',
|
||||
'portal',
|
||||
'is_active',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function translations(): HasMany
|
||||
{
|
||||
return $this->hasMany(CategoryTranslation::class);
|
||||
}
|
||||
|
||||
public function pressReleases(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressRelease::class);
|
||||
}
|
||||
|
||||
public function footerCodes(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(FooterCode::class, 'category_footer_code');
|
||||
}
|
||||
}
|
||||
51
app/Models/CategoryTranslation.php
Normal file
51
app/Models/CategoryTranslation.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CategoryTranslation extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'category_id',
|
||||
'locale',
|
||||
'name',
|
||||
'slug',
|
||||
'description',
|
||||
];
|
||||
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a slug that is unique within a single locale.
|
||||
* If `$ignoreCategoryId` is given (when editing), a clash with this very
|
||||
* category's translation is allowed, so renaming back to the same slug
|
||||
* works.
|
||||
*/
|
||||
public static function uniqueSlug(string $source, string $locale, ?int $ignoreCategoryId = null): string
|
||||
{
|
||||
$base = Str::slug($source) ?: 'kategorie';
|
||||
$slug = $base;
|
||||
$i = 2;
|
||||
|
||||
while (
|
||||
self::query()
|
||||
->where('locale', $locale)
|
||||
->where('slug', $slug)
|
||||
->when(
|
||||
$ignoreCategoryId !== null,
|
||||
fn ($q) => $q->where('category_id', '!=', $ignoreCategoryId),
|
||||
)
|
||||
->exists()
|
||||
) {
|
||||
$slug = $base.'-'.$i++;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
}
|
||||
137
app/Models/Company.php
Normal file
137
app/Models/Company.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\CompanyType;
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Concerns\HasUniqueSlug;
|
||||
use App\Scopes\PortalScope;
|
||||
use Database\Factories\CompanyFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Company extends Model
|
||||
{
|
||||
/** @use HasFactory<CompanyFactory> */
|
||||
use HasFactory, HasUniqueSlug, SoftDeletes;
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
protected function slugScopeAttributes(): array
|
||||
{
|
||||
return ['portal'];
|
||||
}
|
||||
|
||||
protected function slugFallback(): string
|
||||
{
|
||||
return 'company';
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope(new PortalScope);
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'portal',
|
||||
'owner_user_id',
|
||||
'type',
|
||||
'name',
|
||||
'slug',
|
||||
'address',
|
||||
'country_code',
|
||||
'phone',
|
||||
'fax',
|
||||
'email',
|
||||
'website',
|
||||
'logo_path',
|
||||
'logo_variants',
|
||||
'is_active',
|
||||
'disable_footer_code',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'type' => CompanyType::class,
|
||||
'logo_variants' => 'array',
|
||||
'is_active' => 'boolean',
|
||||
'disable_footer_code' => 'boolean',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function owner(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'owner_user_id');
|
||||
}
|
||||
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class)
|
||||
->withPivot('role')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function contacts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
public function pressReleases(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressRelease::class);
|
||||
}
|
||||
|
||||
public function userPaymentOptions(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(UserPaymentOption::class, 'user_payment_option_company')
|
||||
->withPivot('is_active')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function logoUrl(): ?string
|
||||
{
|
||||
if (blank($this->logo_path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$logoPath = trim((string) $this->logo_path);
|
||||
$legacyPortal = $this->legacy_portal ?: $this->portal?->value;
|
||||
$logoFilename = basename((string) (parse_url($logoPath, PHP_URL_PATH) ?: $logoPath));
|
||||
$candidates = [];
|
||||
|
||||
if (Str::startsWith($logoPath, '/storage/')) {
|
||||
$candidates[] = Str::after($logoPath, '/storage/');
|
||||
} elseif (! Str::startsWith($logoPath, ['http://', 'https://'])) {
|
||||
$candidates[] = ltrim($logoPath, '/');
|
||||
}
|
||||
|
||||
if ($legacyPortal && filled($logoFilename)) {
|
||||
$candidates[] = "company-logos/{$legacyPortal}/{$this->id}/{$logoFilename}";
|
||||
$candidates[] = "{$legacyPortal}/company/{$logoFilename}";
|
||||
}
|
||||
|
||||
foreach (array_unique($candidates) as $candidate) {
|
||||
if (Storage::disk('public')->exists($candidate)) {
|
||||
return asset('storage/'.$candidate);
|
||||
}
|
||||
}
|
||||
|
||||
if (Str::startsWith($logoPath, ['http://', 'https://']) && blank($this->legacy_portal)) {
|
||||
return $logoPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
109
app/Models/Concerns/HasUniqueSlug.php
Normal file
109
app/Models/Concerns/HasUniqueSlug.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Concerns;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Generates a unique slug for an Eloquent model based on a source attribute.
|
||||
*
|
||||
* Models opt-in by overriding two methods:
|
||||
* - {@see slugScopeAttributes()} returns the attribute names whose current
|
||||
* model values (or provided overrides) should restrict slug uniqueness.
|
||||
* Defaults to an empty array (global uniqueness on the slug column).
|
||||
* - {@see slugFallback()} returns the fallback when the source attribute is
|
||||
* blank (defaults to "item").
|
||||
*
|
||||
* The model is expected to expose `slug_column` (defaults to `slug`) and
|
||||
* the source attribute name to slugify (defaults to `title` or `name`).
|
||||
*
|
||||
* Typical usage:
|
||||
*
|
||||
* $pr->slug = $pr->generateUniqueSlug($pr->title, [
|
||||
* 'portal' => $pr->portal,
|
||||
* 'language' => $pr->language,
|
||||
* ]);
|
||||
*
|
||||
* @mixin Model
|
||||
*/
|
||||
trait HasUniqueSlug
|
||||
{
|
||||
/**
|
||||
* Build a unique slug from `$source`. Optionally pass overrides for the
|
||||
* scoping attributes (matched against {@see slugScopeAttributes()}).
|
||||
*
|
||||
* @param array<string, mixed> $scope
|
||||
*/
|
||||
public function generateUniqueSlug(string $source, array $scope = []): string
|
||||
{
|
||||
$base = Str::slug($source) ?: $this->slugFallback();
|
||||
$slug = $base;
|
||||
$suffix = 2;
|
||||
|
||||
while ($this->slugExists($slug, $scope)) {
|
||||
$slug = $base.'-'.$suffix++;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $scope
|
||||
*/
|
||||
protected function slugExists(string $slug, array $scope): bool
|
||||
{
|
||||
/** @var Model $self */
|
||||
$self = $this;
|
||||
|
||||
$query = $self::query()
|
||||
->withoutGlobalScopes()
|
||||
->where($this->slugColumn(), $slug);
|
||||
|
||||
if ($self->exists) {
|
||||
$query->where($self->getKeyName(), '!=', $self->getKey());
|
||||
}
|
||||
|
||||
foreach ($this->slugScopeAttributes() as $attribute) {
|
||||
$value = array_key_exists($attribute, $scope)
|
||||
? $scope[$attribute]
|
||||
: $self->getAttribute($attribute);
|
||||
|
||||
$value !== null
|
||||
? $query->where($attribute, $value)
|
||||
: $query->whereNull($attribute);
|
||||
}
|
||||
|
||||
$this->applySlugConstraints($query);
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
protected function slugScopeAttributes(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function slugColumn(): string
|
||||
{
|
||||
return 'slug';
|
||||
}
|
||||
|
||||
protected function slugFallback(): string
|
||||
{
|
||||
return 'item';
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for models to add additional WHERE constraints (e.g. soft-deleted
|
||||
* records). Default: no additional constraints.
|
||||
*/
|
||||
protected function applySlugConstraints(Builder $query): void
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
62
app/Models/Contact.php
Normal file
62
app/Models/Contact.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Scopes\PortalScope;
|
||||
use Database\Factories\ContactFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Contact extends Model
|
||||
{
|
||||
/** @use HasFactory<ContactFactory> */
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope(new PortalScope);
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'company_id',
|
||||
'portal',
|
||||
'salutation_key',
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'responsibility',
|
||||
'phone',
|
||||
'fax',
|
||||
'email',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function company(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function pressReleases(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PressRelease::class, 'press_release_contact');
|
||||
}
|
||||
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class)
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
43
app/Models/FooterCode.php
Normal file
43
app/Models/FooterCode.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use Database\Factories\FooterCodeFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class FooterCode extends Model
|
||||
{
|
||||
/** @use HasFactory<FooterCodeFactory> */
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'portal',
|
||||
'language',
|
||||
'title',
|
||||
'content',
|
||||
'is_global',
|
||||
'is_active',
|
||||
'priority',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'is_global' => 'boolean',
|
||||
'is_active' => 'boolean',
|
||||
'priority' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
public function categories(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Category::class, 'category_footer_code');
|
||||
}
|
||||
}
|
||||
62
app/Models/Invoice.php
Normal file
62
app/Models/Invoice.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\InvoiceStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'user_payment_id',
|
||||
'invoice_billing_address_id',
|
||||
'number',
|
||||
'status',
|
||||
'amount_cents',
|
||||
'tax_cents',
|
||||
'total_cents',
|
||||
'currency',
|
||||
'is_netto',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'paid_at',
|
||||
'stripe_invoice_id',
|
||||
'pdf_path',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => InvoiceStatus::class,
|
||||
'amount_cents' => 'integer',
|
||||
'tax_cents' => 'integer',
|
||||
'total_cents' => 'integer',
|
||||
'is_netto' => 'boolean',
|
||||
'invoice_date' => 'date',
|
||||
'due_date' => 'date',
|
||||
'paid_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function userPayment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UserPayment::class, 'user_payment_id');
|
||||
}
|
||||
|
||||
public function invoiceBillingAddress(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(InvoiceBillingAddress::class);
|
||||
}
|
||||
}
|
||||
28
app/Models/InvoiceBillingAddress.php
Normal file
28
app/Models/InvoiceBillingAddress.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class InvoiceBillingAddress extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'salutation_key',
|
||||
'title',
|
||||
'name',
|
||||
'address1',
|
||||
'address2',
|
||||
'postal_code',
|
||||
'city',
|
||||
'country_code',
|
||||
];
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
}
|
||||
32
app/Models/LegacyImportMap.php
Normal file
32
app/Models/LegacyImportMap.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class LegacyImportMap extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $table = 'legacy_import_map';
|
||||
|
||||
protected $fillable = [
|
||||
'legacy_portal',
|
||||
'legacy_table',
|
||||
'legacy_id',
|
||||
'target_table',
|
||||
'target_id',
|
||||
'imported_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'legacy_portal' => Portal::class,
|
||||
'legacy_id' => 'integer',
|
||||
'target_id' => 'integer',
|
||||
'imported_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
}
|
||||
55
app/Models/LegacyInvoice.php
Normal file
55
app/Models/LegacyInvoice.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LegacyInvoice extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
'user_id',
|
||||
'legacy_user_id',
|
||||
'number',
|
||||
'amount_cents',
|
||||
'tax_cents',
|
||||
'total_cents',
|
||||
'status',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'paid_at',
|
||||
'payment_method',
|
||||
'pdf_path',
|
||||
'raw_snapshot',
|
||||
'pdf_payload',
|
||||
'pdf_generated_at',
|
||||
'imported_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'legacy_portal' => Portal::class,
|
||||
'amount_cents' => 'integer',
|
||||
'tax_cents' => 'integer',
|
||||
'total_cents' => 'integer',
|
||||
'invoice_date' => 'date',
|
||||
'due_date' => 'date',
|
||||
'paid_at' => 'datetime',
|
||||
'raw_snapshot' => 'array',
|
||||
'pdf_payload' => 'array',
|
||||
'pdf_generated_at' => 'datetime',
|
||||
'imported_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
34
app/Models/MagicLink.php
Normal file
34
app/Models/MagicLink.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class MagicLink extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'token_hash',
|
||||
'purpose',
|
||||
'payload',
|
||||
'expires_at',
|
||||
'consumed_at',
|
||||
'ip_requested',
|
||||
'ip_consumed',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'payload' => 'array',
|
||||
'expires_at' => 'datetime',
|
||||
'consumed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
47
app/Models/NewsletterSubscription.php
Normal file
47
app/Models/NewsletterSubscription.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Scopes\PortalScope;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class NewsletterSubscription extends Model
|
||||
{
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope(new PortalScope);
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'portal',
|
||||
'user_id',
|
||||
'salutation_key',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'ip_address',
|
||||
'is_confirmed',
|
||||
'confirmation_token',
|
||||
'subscribed_at',
|
||||
'unsubscribed_at',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'is_confirmed' => 'boolean',
|
||||
'subscribed_at' => 'datetime',
|
||||
'unsubscribed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
45
app/Models/PaymentOption.php
Normal file
45
app/Models/PaymentOption.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PaymentOptionType;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class PaymentOption extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'article_number',
|
||||
'type',
|
||||
'price_cents',
|
||||
'currency',
|
||||
'interval',
|
||||
'is_hidden',
|
||||
'stripe_product_id',
|
||||
'stripe_price_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'type' => PaymentOptionType::class,
|
||||
'price_cents' => 'integer',
|
||||
'is_hidden' => 'boolean',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function translations(): HasMany
|
||||
{
|
||||
return $this->hasMany(PaymentOptionTranslation::class);
|
||||
}
|
||||
|
||||
public function userPaymentOptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserPaymentOption::class);
|
||||
}
|
||||
}
|
||||
24
app/Models/PaymentOptionTranslation.php
Normal file
24
app/Models/PaymentOptionTranslation.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PaymentOptionTranslation extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'payment_option_id',
|
||||
'locale',
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
public function paymentOption(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PaymentOption::class);
|
||||
}
|
||||
}
|
||||
105
app/Models/PressRelease.php
Normal file
105
app/Models/PressRelease.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use App\Models\Concerns\HasUniqueSlug;
|
||||
use App\Scopes\PortalScope;
|
||||
use Database\Factories\PressReleaseFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class PressRelease extends Model
|
||||
{
|
||||
/** @use HasFactory<PressReleaseFactory> */
|
||||
use HasFactory, HasUniqueSlug, SoftDeletes;
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
protected function slugScopeAttributes(): array
|
||||
{
|
||||
return ['portal', 'language'];
|
||||
}
|
||||
|
||||
protected function slugFallback(): string
|
||||
{
|
||||
return 'pressemitteilung';
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope(new PortalScope);
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'uuid',
|
||||
'portal',
|
||||
'user_id',
|
||||
'company_id',
|
||||
'category_id',
|
||||
'language',
|
||||
'title',
|
||||
'slug',
|
||||
'text',
|
||||
'backlink_url',
|
||||
'keywords',
|
||||
'status',
|
||||
'hits',
|
||||
'teaser_begin',
|
||||
'teaser_end',
|
||||
'no_export',
|
||||
'published_at',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'portal' => Portal::class,
|
||||
'status' => PressReleaseStatus::class,
|
||||
'hits' => 'integer',
|
||||
'teaser_begin' => 'integer',
|
||||
'teaser_end' => 'integer',
|
||||
'no_export' => 'boolean',
|
||||
'published_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function company(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
public function images(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressReleaseImage::class);
|
||||
}
|
||||
|
||||
public function contacts(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Contact::class, 'press_release_contact');
|
||||
}
|
||||
|
||||
public function statusLogs(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressReleaseStatusLog::class)->orderByDesc('created_at');
|
||||
}
|
||||
}
|
||||
86
app/Models/PressReleaseImage.php
Normal file
86
app/Models/PressReleaseImage.php
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PressReleaseImage extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'press_release_id',
|
||||
'disk',
|
||||
'path',
|
||||
'variants',
|
||||
'title',
|
||||
'description',
|
||||
'copyright',
|
||||
'is_preview',
|
||||
'sort_order',
|
||||
'width',
|
||||
'height',
|
||||
'mime',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'variants' => 'array',
|
||||
'is_preview' => 'boolean',
|
||||
'sort_order' => 'integer',
|
||||
'width' => 'integer',
|
||||
'height' => 'integer',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function pressRelease(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PressRelease::class);
|
||||
}
|
||||
|
||||
public function url(): ?string
|
||||
{
|
||||
if (blank($this->path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveDiskUrl($this->path);
|
||||
}
|
||||
|
||||
public function variantUrl(string $key): ?string
|
||||
{
|
||||
$variantPath = $this->variants[$key] ?? null;
|
||||
|
||||
if (! is_string($variantPath) || blank($variantPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveDiskUrl($variantPath);
|
||||
}
|
||||
|
||||
private function resolveDiskUrl(string $relativePath): ?string
|
||||
{
|
||||
if ($this->disk === 'public') {
|
||||
return asset('storage/'.ltrim($relativePath, '/'));
|
||||
}
|
||||
|
||||
try {
|
||||
$disk = Storage::disk($this->disk);
|
||||
|
||||
if (method_exists($disk, 'url')) {
|
||||
return $disk->url($relativePath);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
app/Models/PressReleaseStatusLog.php
Normal file
41
app/Models/PressReleaseStatusLog.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PressReleaseStatus;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PressReleaseStatusLog extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'press_release_id',
|
||||
'changed_by_user_id',
|
||||
'from_status',
|
||||
'to_status',
|
||||
'reason',
|
||||
'source',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'created_at' => 'datetime',
|
||||
'from_status' => PressReleaseStatus::class,
|
||||
'to_status' => PressReleaseStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function pressRelease(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PressRelease::class);
|
||||
}
|
||||
|
||||
public function changedBy(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'changed_by_user_id');
|
||||
}
|
||||
}
|
||||
52
app/Models/Profile.php
Normal file
52
app/Models/Profile.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\ProfileFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Profile extends Model
|
||||
{
|
||||
/** @use HasFactory<ProfileFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'salutation_key',
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'phone',
|
||||
'address',
|
||||
'country_code',
|
||||
'birthdate',
|
||||
'backlink_url',
|
||||
'show_stats',
|
||||
'validation_date',
|
||||
'contract_date',
|
||||
'validate_token',
|
||||
'tax_id_number',
|
||||
'tax_exempt',
|
||||
'tax_exempt_reason',
|
||||
'disable_footer_code',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'birthdate' => 'date',
|
||||
'show_stats' => 'boolean',
|
||||
'validation_date' => 'datetime',
|
||||
'contract_date' => 'datetime',
|
||||
'tax_exempt' => 'boolean',
|
||||
'disable_footer_code' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,17 +3,25 @@
|
|||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\RegistrationType;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasApiTokens, HasFactory, Notifiable, HasRoles, TwoFactorAuthenticatable;
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasApiTokens, HasFactory, HasRoles, Notifiable, SoftDeletes, TwoFactorAuthenticatable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
|
@ -23,6 +31,17 @@ class User extends Authenticatable
|
|||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'portal',
|
||||
'registration_type',
|
||||
'language',
|
||||
'is_active',
|
||||
'is_super_admin',
|
||||
'last_login_at',
|
||||
'last_login_ip',
|
||||
'gdpr_consent_at',
|
||||
'last_seen_at',
|
||||
'legacy_portal',
|
||||
'legacy_id',
|
||||
'password',
|
||||
];
|
||||
|
||||
|
|
@ -45,6 +64,14 @@ class User extends Authenticatable
|
|||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'portal' => Portal::class,
|
||||
'registration_type' => RegistrationType::class,
|
||||
'is_active' => 'boolean',
|
||||
'is_super_admin' => 'boolean',
|
||||
'last_login_at' => 'datetime',
|
||||
'gdpr_consent_at' => 'datetime',
|
||||
'last_seen_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
|
@ -59,4 +86,89 @@ class User extends Authenticatable
|
|||
->map(fn (string $name) => Str::of($name)->substr(0, 1))
|
||||
->implode('');
|
||||
}
|
||||
|
||||
public function profile(): HasOne
|
||||
{
|
||||
return $this->hasOne(Profile::class);
|
||||
}
|
||||
|
||||
public function magicLinks(): HasMany
|
||||
{
|
||||
return $this->hasMany(MagicLink::class);
|
||||
}
|
||||
|
||||
public function ownedCompanies(): HasMany
|
||||
{
|
||||
return $this->hasMany(Company::class, 'owner_user_id');
|
||||
}
|
||||
|
||||
public function companies(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Company::class)
|
||||
->withPivot('role')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function contacts(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Contact::class)
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function pressReleases(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressRelease::class);
|
||||
}
|
||||
|
||||
public function newsletterSubscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(NewsletterSubscription::class);
|
||||
}
|
||||
|
||||
public function billingAddress(): HasOne
|
||||
{
|
||||
return $this->hasOne(BillingAddress::class);
|
||||
}
|
||||
|
||||
public function userPaymentOptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserPaymentOption::class);
|
||||
}
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
public function legacyInvoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(LegacyInvoice::class);
|
||||
}
|
||||
|
||||
public function filterPresets(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserFilterPreset::class);
|
||||
}
|
||||
|
||||
public function canAccessAdmin(): bool
|
||||
{
|
||||
if (! $this->is_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_super_admin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->hasAnyRole(['admin', 'editor']);
|
||||
}
|
||||
|
||||
public function canAccessCustomer(): bool
|
||||
{
|
||||
if (! $this->is_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->hasAnyRole(['admin', 'editor', 'customer']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
app/Models/UserFilterPreset.php
Normal file
32
app/Models/UserFilterPreset.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class UserFilterPreset extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'page',
|
||||
'name',
|
||||
'is_default',
|
||||
'last_used_at',
|
||||
'filters',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_default' => 'boolean',
|
||||
'last_used_at' => 'datetime',
|
||||
'filters' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
41
app/Models/UserPayment.php
Normal file
41
app/Models/UserPayment.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PaymentStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class UserPayment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_payment_option_id',
|
||||
'amount_cents',
|
||||
'currency',
|
||||
'status',
|
||||
'stripe_charge_id',
|
||||
'stripe_invoice_id',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'amount_cents' => 'integer',
|
||||
'status' => PaymentStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function userPaymentOption(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UserPaymentOption::class);
|
||||
}
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class, 'user_payment_id');
|
||||
}
|
||||
}
|
||||
61
app/Models/UserPaymentOption.php
Normal file
61
app/Models/UserPaymentOption.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\UserPaymentOptionStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class UserPaymentOption extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'payment_option_id',
|
||||
'status',
|
||||
'grandfathered_until',
|
||||
'legacy_conditions',
|
||||
'current_period_start',
|
||||
'current_period_end',
|
||||
'stripe_subscription_id',
|
||||
'cancelled_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => UserPaymentOptionStatus::class,
|
||||
'grandfathered_until' => 'date',
|
||||
'legacy_conditions' => 'array',
|
||||
'current_period_start' => 'date',
|
||||
'current_period_end' => 'date',
|
||||
'cancelled_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function paymentOption(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PaymentOption::class);
|
||||
}
|
||||
|
||||
public function companies(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Company::class, 'user_payment_option_company')
|
||||
->withPivot('is_active')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function payments(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserPayment::class);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue