mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Refactor models.
This commit is contained in:
		| @@ -23,13 +23,13 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use Illuminate\Database\Eloquent\Attributes\ObservedBy; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use FireflyIII\Enums\AccountTypeEnum; | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Database\Eloquent\Attributes\ObservedBy; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Illuminate\Database\Eloquent\Builder as EloquentBuilder; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| @@ -50,9 +50,9 @@ class Account extends Model | ||||
|     use ReturnsIntegerUserIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     protected $fillable              = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; | ||||
|     protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban', 'native_virtual_balance']; | ||||
| 
 | ||||
|     protected $hidden                = ['encrypted']; | ||||
|     protected    $hidden             = ['encrypted']; | ||||
|     private bool $joinedAccountTypes = false; | ||||
| 
 | ||||
|     /** | ||||
| @@ -63,13 +63,13 @@ class Account extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $accountId = (int) $value; | ||||
|             $accountId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user      = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Account $account */ | ||||
|             $account   = $user->accounts()->with(['accountType'])->find($accountId); | ||||
|             $account = $user->accounts()->with(['accountType'])->find($accountId); | ||||
|             if (null !== $account) { | ||||
|                 return $account; | ||||
|             } | ||||
| @@ -98,39 +98,6 @@ class Account extends Model | ||||
|         return $this->morphMany(Attachment::class, 'attachable'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the account number. | ||||
|      */ | ||||
|     protected function accountNumber(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: function () { | ||||
|             /** @var null|AccountMeta $metaValue */ | ||||
|             $metaValue = $this->accountMeta() | ||||
|                 ->where('name', 'account_number') | ||||
|                 ->first() | ||||
|             ; | ||||
| 
 | ||||
|             return null !== $metaValue ? $metaValue->data : ''; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public function accountMeta(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(AccountMeta::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function editName(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: function () { | ||||
|             $name = $this->name; | ||||
|             if (AccountTypeEnum::CASH->value === $this->accountType->type) { | ||||
|                 return ''; | ||||
|             } | ||||
| 
 | ||||
|             return $name; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public function locations(): MorphMany | ||||
|     { | ||||
|         return $this->morphMany(Location::class, 'locatable'); | ||||
| @@ -157,19 +124,9 @@ class Account extends Model | ||||
|         return $this->belongsToMany(PiggyBank::class); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function accountTypeIn(EloquentBuilder $query, array $types): void | ||||
|     { | ||||
|         if (false === $this->joinedAccountTypes) { | ||||
|             $query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id'); | ||||
|             $this->joinedAccountTypes = true; | ||||
|         } | ||||
|         $query->whereIn('account_types.type', $types); | ||||
|     } | ||||
| 
 | ||||
|     public function setVirtualBalanceAttribute(mixed $value): void | ||||
|     { | ||||
|         $value                               = (string) $value; | ||||
|         $value = (string)$value; | ||||
|         if ('' === $value) { | ||||
|             $value = null; | ||||
|         } | ||||
| @@ -189,42 +146,48 @@ class Account extends Model | ||||
|     protected function accountId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the account number. | ||||
|      */ | ||||
|     protected function accountNumber(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: function () { | ||||
|             /** @var null|AccountMeta $metaValue */ | ||||
|             $metaValue = $this->accountMeta() | ||||
|                               ->where('name', 'account_number') | ||||
|                               ->first(); | ||||
| 
 | ||||
|             return null !== $metaValue ? $metaValue->data : ''; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public function accountMeta(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(AccountMeta::class); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the user ID | ||||
|      */ | ||||
|     protected function accountTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function iban(): Attribute | ||||
|     #[Scope]
 | ||||
|     protected function accountTypeIn(EloquentBuilder $query, array $types): void | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string) $value)), | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the virtual balance | ||||
|      */ | ||||
|     protected function virtualBalance(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|         if (false === $this->joinedAccountTypes) { | ||||
|             $query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id'); | ||||
|             $this->joinedAccountTypes = true; | ||||
|         } | ||||
|         $query->whereIn('account_types.type', $types); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
| @@ -241,4 +204,40 @@ class Account extends Model | ||||
|             'native_virtual_balance' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function editName(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: function () { | ||||
|             $name = $this->name; | ||||
|             if (AccountTypeEnum::CASH->value === $this->accountType->type) { | ||||
|                 return ''; | ||||
|             } | ||||
| 
 | ||||
|             return $name; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     protected function iban(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)), | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the virtual balance | ||||
|      */ | ||||
|     protected function virtualBalance(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,11 +23,10 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| 
 | ||||
| use function Safe\json_decode; | ||||
| use function Safe\json_encode; | ||||
| 
 | ||||
| @@ -43,11 +42,6 @@ class AccountMeta extends Model | ||||
|         return $this->belongsTo(Account::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn (mixed $value) => (string) json_decode((string) $value, true), set: fn (mixed $value) => ['data' => json_encode($value)]); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -55,4 +49,9 @@ class AccountMeta extends Model | ||||
|             'updated_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn(mixed $value) => (string)json_decode((string)$value, true), set: fn(mixed $value) => ['data' => json_encode($value)]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,55 +32,69 @@ class AccountType extends Model | ||||
| { | ||||
|     use ReturnsIntegerIdTrait; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string ASSET            = 'Asset account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string ASSET = 'Asset account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string BENEFICIARY      = 'Beneficiary account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string BENEFICIARY = 'Beneficiary account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string CASH             = 'Cash account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string CASH = 'Cash account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string CREDITCARD       = 'Credit card'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string CREDITCARD = 'Credit card'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string DEBT             = 'Debt'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEBT    = 'Debt'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string DEFAULT          = 'Default account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEFAULT = 'Default account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string EXPENSE          = 'Expense account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string EXPENSE = 'Expense account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string IMPORT           = 'Import account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string IMPORT = 'Import account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string INITIAL_BALANCE  = 'Initial balance account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string INITIAL_BALANCE = 'Initial balance account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string LIABILITY_CREDIT = 'Liability credit account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string LOAN             = 'Loan'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string LOAN = 'Loan'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string MORTGAGE         = 'Mortgage'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string MORTGAGE = 'Mortgage'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string RECONCILIATION   = 'Reconciliation account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string RECONCILIATION = 'Reconciliation account'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string REVENUE          = 'Revenue account'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string REVENUE = 'Revenue account'; | ||||
| 
 | ||||
|     protected $casts | ||||
|                                          = [ | ||||
|         = [ | ||||
|             'created_at' => 'datetime', | ||||
|             'updated_at' => 'datetime', | ||||
|         ]; | ||||
| 
 | ||||
|     protected $fillable                  = ['type']; | ||||
|     protected $fillable = ['type']; | ||||
| 
 | ||||
|     public function accounts(): HasMany | ||||
|     { | ||||
|   | ||||
| @@ -53,13 +53,13 @@ class Attachment extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $attachmentId = (int) $value; | ||||
|             $attachmentId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user         = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Attachment $attachment */ | ||||
|             $attachment   = $user->attachments()->find($attachmentId); | ||||
|             $attachment = $user->attachments()->find($attachmentId); | ||||
|             if (null !== $attachment) { | ||||
|                 return $attachment; | ||||
|             } | ||||
| @@ -86,7 +86,7 @@ class Attachment extends Model | ||||
|      */ | ||||
|     public function fileName(): string | ||||
|     { | ||||
|         return sprintf('at-%s.data', (string) $this->id); | ||||
|         return sprintf('at-%s.data', (string)$this->id); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -100,7 +100,7 @@ class Attachment extends Model | ||||
|     protected function attachableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -48,14 +48,7 @@ class AuditLogEntry extends Model | ||||
|     protected function auditableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function changerId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| @@ -69,4 +62,11 @@ class AuditLogEntry extends Model | ||||
|             'deleted_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function changerId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -84,6 +84,22 @@ class AvailableBudget extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'              => 'datetime', | ||||
|             'updated_at'              => 'datetime', | ||||
|             'deleted_at'              => 'datetime', | ||||
|             'start_date'              => 'date', | ||||
|             'end_date'                => 'date', | ||||
|             'transaction_currency_id' => 'int', | ||||
|             'amount'                  => 'string', | ||||
|             'native_amount'           => 'string', | ||||
|             'user_id'                 => 'integer', | ||||
|             'user_group_id'           => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function endDate(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
| @@ -106,20 +122,4 @@ class AvailableBudget extends Model | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'              => 'datetime', | ||||
|             'updated_at'              => 'datetime', | ||||
|             'deleted_at'              => 'datetime', | ||||
|             'start_date'              => 'date', | ||||
|             'end_date'                => 'date', | ||||
|             'transaction_currency_id' => 'int', | ||||
|             'amount'                  => 'string', | ||||
|             'native_amount'           => 'string', | ||||
|             'user_id'                 => 'integer', | ||||
|             'user_group_id'           => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -165,6 +165,27 @@ class Bill extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'        => 'datetime', | ||||
|             'updated_at'        => 'datetime', | ||||
|             'deleted_at'        => 'datetime', | ||||
|             'date'              => SeparateTimezoneCaster::class, | ||||
|             'end_date'          => SeparateTimezoneCaster::class, | ||||
|             'extension_date'    => SeparateTimezoneCaster::class, | ||||
|             'skip'              => 'int', | ||||
|             'automatch'         => 'boolean', | ||||
|             'active'            => 'boolean', | ||||
|             'name_encrypted'    => 'boolean', | ||||
|             'match_encrypted'   => 'boolean', | ||||
|             'amount_min'        => 'string', | ||||
|             'amount_max'        => 'string', | ||||
|             'native_amount_min' => 'string', | ||||
|             'native_amount_max' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
| @@ -188,25 +209,4 @@ class Bill extends Model | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'        => 'datetime', | ||||
|             'updated_at'        => 'datetime', | ||||
|             'deleted_at'        => 'datetime', | ||||
|             'date'              => SeparateTimezoneCaster::class, | ||||
|             'end_date'          => SeparateTimezoneCaster::class, | ||||
|             'extension_date'    => SeparateTimezoneCaster::class, | ||||
|             'skip'              => 'int', | ||||
|             'automatch'         => 'boolean', | ||||
|             'active'            => 'boolean', | ||||
|             'name_encrypted'    => 'boolean', | ||||
|             'match_encrypted'   => 'boolean', | ||||
|             'amount_min'        => 'string', | ||||
|             'amount_max'        => 'string', | ||||
|             'native_amount_min' => 'string', | ||||
|             'native_amount_max' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class Budget extends Model | ||||
| 
 | ||||
|     protected $fillable = ['user_id', 'user_group_id', 'name', 'active', 'order', 'user_group_id']; | ||||
| 
 | ||||
|     protected $hidden   = ['encrypted']; | ||||
|     protected $hidden = ['encrypted']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
| @@ -56,13 +56,13 @@ class Budget extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $budgetId = (int) $value; | ||||
|             $budgetId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user     = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Budget $budget */ | ||||
|             $budget   = $user->budgets()->find($budgetId); | ||||
|             $budget = $user->budgets()->find($budgetId); | ||||
|             if (null !== $budget) { | ||||
|                 return $budget; | ||||
|             } | ||||
| @@ -109,13 +109,6 @@ class Budget extends Model | ||||
|         return $this->belongsToMany(Transaction::class, 'budget_transaction', 'budget_id'); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -128,4 +121,11 @@ class Budget extends Model | ||||
|             'user_group_id' => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -96,13 +96,6 @@ class BudgetLimit extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -115,4 +108,11 @@ class BudgetLimit extends Model | ||||
|             'native_amount' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\CategoryObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| @@ -46,7 +45,7 @@ class Category extends Model | ||||
| 
 | ||||
|     protected $fillable = ['user_id', 'user_group_id', 'name']; | ||||
| 
 | ||||
|     protected $hidden   = ['encrypted']; | ||||
|     protected $hidden = ['encrypted']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
| @@ -56,13 +55,13 @@ class Category extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $categoryId = (int) $value; | ||||
|             $categoryId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user       = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Category $category */ | ||||
|             $category   = $user->categories()->find($categoryId); | ||||
|             $category = $user->categories()->find($categoryId); | ||||
|             if (null !== $category) { | ||||
|                 return $category; | ||||
|             } | ||||
|   | ||||
| @@ -23,11 +23,10 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\SoftDeletes; | ||||
| 
 | ||||
| use function Safe\json_decode; | ||||
| use function Safe\json_encode; | ||||
| 
 | ||||
| @@ -38,14 +37,6 @@ class Configuration extends Model | ||||
| 
 | ||||
|     protected $table = 'configuration'; | ||||
| 
 | ||||
|     /** | ||||
|      * TODO can be replaced with native laravel code. | ||||
|      */ | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn ($value) => json_decode((string) $value), set: fn ($value) => ['data' => json_encode($value)]); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -54,4 +45,12 @@ class Configuration extends Model | ||||
|             'deleted_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * TODO can be replaced with native laravel code. | ||||
|      */ | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn($value) => json_decode((string)$value), set: fn($value) => ['data' => json_encode($value)]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -37,6 +37,7 @@ class CurrencyExchangeRate extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use ReturnsIntegerUserIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate']; | ||||
| 
 | ||||
|     public function fromCurrency(): BelongsTo | ||||
| @@ -54,34 +55,6 @@ class CurrencyExchangeRate extends Model | ||||
|         return $this->belongsTo(User::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function fromCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function rate(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function toCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function userRate(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -96,4 +69,32 @@ class CurrencyExchangeRate extends Model | ||||
|             'user_rate'        => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function fromCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function rate(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function toCurrencyId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function userRate(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -53,13 +53,6 @@ class GroupMembership extends Model | ||||
|         return $this->belongsTo(UserRole::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function userRoleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -69,4 +62,11 @@ class GroupMembership extends Model | ||||
|             'user_group_id' => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function userRoleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,6 +36,7 @@ class InvitedUser extends Model | ||||
| { | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use ReturnsIntegerUserIdTrait; | ||||
| 
 | ||||
|     protected $fillable = ['user_group_id', 'user_id', 'email', 'invite_code', 'expires', 'expires_tz', 'redeemed']; | ||||
| 
 | ||||
|     /** | ||||
| @@ -44,10 +45,10 @@ class InvitedUser extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $attemptId = (int) $value; | ||||
|             $attemptId = (int)$value; | ||||
| 
 | ||||
|             /** @var null|InvitedUser $attempt */ | ||||
|             $attempt   = self::find($attemptId); | ||||
|             $attempt = self::find($attemptId); | ||||
|             if (null !== $attempt) { | ||||
|                 return $attempt; | ||||
|             } | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class LinkType extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $linkTypeId = (int) $value; | ||||
|             $linkTypeId = (int)$value; | ||||
|             $linkType   = self::find($linkTypeId); | ||||
|             if (null !== $linkType) { | ||||
|                 return $linkType; | ||||
|   | ||||
| @@ -66,13 +66,6 @@ class Location extends Model | ||||
|         return $this->morphMany(TransactionJournal::class, 'locatable'); | ||||
|     } | ||||
| 
 | ||||
|     protected function locatableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -84,4 +77,11 @@ class Location extends Model | ||||
|             'longitude'  => 'float', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function locatableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -44,13 +44,6 @@ class Note extends Model | ||||
|         return $this->morphTo(); | ||||
|     } | ||||
| 
 | ||||
|     protected function noteableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -59,4 +52,11 @@ class Note extends Model | ||||
|             'deleted_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function noteableId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -37,6 +37,7 @@ class ObjectGroup extends Model | ||||
| { | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use ReturnsIntegerUserIdTrait; | ||||
| 
 | ||||
|     protected $fillable = ['title', 'order', 'user_id', 'user_group_id']; | ||||
| 
 | ||||
|     /** | ||||
| @@ -47,12 +48,11 @@ class ObjectGroup extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $objectGroupId = (int) $value; | ||||
|             $objectGroupId = (int)$value; | ||||
| 
 | ||||
|             /** @var null|ObjectGroup $objectGroup */ | ||||
|             $objectGroup   = self::where('object_groups.id', $objectGroupId) | ||||
|                 ->where('object_groups.user_id', auth()->user()->id)->first() | ||||
|             ; | ||||
|             $objectGroup = self::where('object_groups.id', $objectGroupId) | ||||
|                                ->where('object_groups.user_id', auth()->user()->id)->first(); | ||||
|             if (null !== $objectGroup) { | ||||
|                 return $objectGroup; | ||||
|             } | ||||
| @@ -90,13 +90,6 @@ class ObjectGroup extends Model | ||||
|         return $this->morphedByMany(PiggyBank::class, 'object_groupable'); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -107,4 +100,11 @@ class ObjectGroup extends Model | ||||
|             'deleted_at'    => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -126,6 +126,22 @@ class PiggyBank extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'           => 'datetime', | ||||
|             'updated_at'           => 'datetime', | ||||
|             'deleted_at'           => 'datetime', | ||||
|             'start_date'           => 'date', | ||||
|             'target_date'          => 'date', | ||||
|             'order'                => 'int', | ||||
|             'active'               => 'boolean', | ||||
|             'encrypted'            => 'boolean', | ||||
|             'target_amount'        => 'string', | ||||
|             'native_target_amount' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
| @@ -142,20 +158,4 @@ class PiggyBank extends Model | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'           => 'datetime', | ||||
|             'updated_at'           => 'datetime', | ||||
|             'deleted_at'           => 'datetime', | ||||
|             'start_date'           => 'date', | ||||
|             'target_date'          => 'date', | ||||
|             'order'                => 'int', | ||||
|             'active'               => 'boolean', | ||||
|             'encrypted'            => 'boolean', | ||||
|             'target_amount'        => 'string', | ||||
|             'native_target_amount' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -68,13 +68,6 @@ class PiggyBankEvent extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function piggyBankId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -85,4 +78,11 @@ class PiggyBankEvent extends Model | ||||
|             'native_amount' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function piggyBankId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,10 +23,10 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Casts\SeparateTimezoneCaster; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Illuminate\Database\Eloquent\Builder as EloquentBuilder; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| @@ -43,12 +43,48 @@ class PiggyBankRepetition extends Model | ||||
|         return $this->belongsTo(PiggyBank::class); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setCurrentAmountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['current_amount'] = (string)$value; | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'      => 'datetime', | ||||
|             'updated_at'      => 'datetime', | ||||
|             'start_date'      => SeparateTimezoneCaster::class, | ||||
|             'target_date'     => SeparateTimezoneCaster::class, | ||||
|             'virtual_balance' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|     protected function currentAmount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function onDates(EloquentBuilder $query, Carbon $start, Carbon $target): EloquentBuilder | ||||
|     { | ||||
|         return $query->where('start_date', $start->format('Y-m-d'))->where('target_date', $target->format('Y-m-d')); | ||||
|     } | ||||
| 
 | ||||
|     protected function piggyBankId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return EloquentBuilder | ||||
|      */ | ||||
| @@ -61,48 +97,11 @@ class PiggyBankRepetition extends Model | ||||
|                 $q->orWhereNull('start_date'); | ||||
|             } | ||||
|         ) | ||||
|             ->where( | ||||
|                 static function (EloquentBuilder $q) use ($date): void { | ||||
|                     $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); | ||||
|                     $q->orWhereNull('target_date'); | ||||
|                 } | ||||
|             ) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setCurrentAmountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['current_amount'] = (string) $value; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|     protected function currentAmount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function piggyBankId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'      => 'datetime', | ||||
|             'updated_at'      => 'datetime', | ||||
|             'start_date'      => SeparateTimezoneCaster::class, | ||||
|             'target_date'     => SeparateTimezoneCaster::class, | ||||
|             'virtual_balance' => 'string', | ||||
|         ]; | ||||
|                      ->where( | ||||
|                          static function (EloquentBuilder $q) use ($date): void { | ||||
|                              $q->where('target_date', '>=', $date->format('Y-m-d 00:00:00')); | ||||
|                              $q->orWhereNull('target_date'); | ||||
|                          } | ||||
|                      ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -46,16 +46,16 @@ class Preference extends Model | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             /** @var User $user */ | ||||
|             $user        = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             // some preferences do not have an administration ID.
 | ||||
|             // some need it, to make sure the correct one is selected.
 | ||||
|             $userGroupId = (int) $user->user_group_id; | ||||
|             $userGroupId = (int)$user->user_group_id; | ||||
|             $userGroupId = 0 === $userGroupId ? null : $userGroupId; | ||||
| 
 | ||||
|             /** @var null|Preference $preference */ | ||||
|             $preference  = null; | ||||
|             $items       = config('firefly.admin_specific_prefs'); | ||||
|             $preference = null; | ||||
|             $items      = config('firefly.admin_specific_prefs'); | ||||
|             if (null !== $userGroupId && in_array($value, $items, true)) { | ||||
|                 // find a preference with a specific user_group_id
 | ||||
|                 $preference = $user->preferences()->where('user_group_id', $userGroupId)->where('name', $value)->first(); | ||||
| @@ -67,18 +67,18 @@ class Preference extends Model | ||||
| 
 | ||||
|             // try again with ID, but this time don't care about the preferred user_group_id
 | ||||
|             if (null === $preference) { | ||||
|                 $preference = $user->preferences()->where('id', (int) $value)->first(); | ||||
|                 $preference = $user->preferences()->where('id', (int)$value)->first(); | ||||
|             } | ||||
|             if (null !== $preference) { | ||||
|                 /** @var Preference $preference */ | ||||
|                 return $preference; | ||||
|             } | ||||
|             $default     = config('firefly.default_preferences'); | ||||
|             $default = config('firefly.default_preferences'); | ||||
|             if (array_key_exists($value, $default)) { | ||||
|                 $preference                = new self(); | ||||
|                 $preference->name          = $value; | ||||
|                 $preference->data          = $default[$value]; | ||||
|                 $preference->user_id       = (int) $user->id; | ||||
|                 $preference->user_id       = (int)$user->id; | ||||
|                 $preference->user_group_id = in_array($value, $items, true) ? $userGroupId : null; | ||||
|                 $preference->save(); | ||||
| 
 | ||||
|   | ||||
| @@ -116,13 +116,6 @@ class Recurrence extends Model | ||||
|         return $this->belongsTo(TransactionType::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -142,4 +135,11 @@ class Recurrence extends Model | ||||
|             'user_group_id' => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -37,20 +37,13 @@ class RecurrenceMeta extends Model | ||||
| 
 | ||||
|     protected $fillable = ['recurrence_id', 'name', 'value']; | ||||
| 
 | ||||
|     protected $table    = 'recurrences_meta'; | ||||
|     protected $table = 'recurrences_meta'; | ||||
| 
 | ||||
|     public function recurrence(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(Recurrence::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function recurrenceId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -61,4 +54,11 @@ class RecurrenceMeta extends Model | ||||
|             'value'      => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function recurrenceId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,20 +36,24 @@ class RecurrenceRepetition extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const int WEEKEND_DO_NOTHING    = 1; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_DO_NOTHING = 1; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_SKIP_CREATION = 2; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const int WEEKEND_TO_FRIDAY     = 3; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_TO_FRIDAY = 3; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const int WEEKEND_TO_MONDAY     = 4; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const int WEEKEND_TO_MONDAY = 4; | ||||
| 
 | ||||
|     protected $casts | ||||
|                                            = [ | ||||
|         = [ | ||||
|             'created_at'        => 'datetime', | ||||
|             'updated_at'        => 'datetime', | ||||
|             'deleted_at'        => 'datetime', | ||||
| @@ -59,9 +63,9 @@ class RecurrenceRepetition extends Model | ||||
|             'weekend'           => 'int', | ||||
|         ]; | ||||
| 
 | ||||
|     protected $fillable                    = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; | ||||
|     protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; | ||||
| 
 | ||||
|     protected $table                       = 'recurrences_repetitions'; | ||||
|     protected $table = 'recurrences_repetitions'; | ||||
| 
 | ||||
|     public function recurrence(): BelongsTo | ||||
|     { | ||||
| @@ -78,21 +82,21 @@ class RecurrenceRepetition extends Model | ||||
|     protected function recurrenceId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function repetitionSkip(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function weekend(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -95,6 +95,18 @@ class RecurrenceTransaction extends Model | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'     => 'datetime', | ||||
|             'updated_at'     => 'datetime', | ||||
|             'deleted_at'     => 'datetime', | ||||
|             'amount'         => 'string', | ||||
|             'foreign_amount' => 'string', | ||||
|             'description'    => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function destinationId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
| @@ -136,16 +148,4 @@ class RecurrenceTransaction extends Model | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'created_at'     => 'datetime', | ||||
|             'updated_at'     => 'datetime', | ||||
|             'deleted_at'     => 'datetime', | ||||
|             'amount'         => 'string', | ||||
|             'foreign_amount' => 'string', | ||||
|             'description'    => 'string', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -37,20 +37,13 @@ class RecurrenceTransactionMeta extends Model | ||||
| 
 | ||||
|     protected $fillable = ['rt_id', 'name', 'value']; | ||||
| 
 | ||||
|     protected $table    = 'rt_meta'; | ||||
|     protected $table = 'rt_meta'; | ||||
| 
 | ||||
|     public function recurrenceTransaction(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); | ||||
|     } | ||||
| 
 | ||||
|     protected function rtId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -61,4 +54,11 @@ class RecurrenceTransactionMeta extends Model | ||||
|             'value'      => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function rtId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -87,30 +87,11 @@ class Rule extends Model | ||||
|         return $this->hasMany(RuleTrigger::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function description(): Attribute | ||||
|     { | ||||
|         return Attribute::make(set: fn($value) => ['description' => e($value)]); | ||||
|     } | ||||
| 
 | ||||
|     public function userGroup(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(UserGroup::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleGroupId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -126,4 +107,23 @@ class Rule extends Model | ||||
|             'user_group_id'   => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function description(): Attribute | ||||
|     { | ||||
|         return Attribute::make(set: fn($value) => ['description' => e($value)]); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleGroupId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class RuleAction extends Model | ||||
|         if (false === config('firefly.feature_flags.expression_engine')) { | ||||
|             Log::debug('Expression engine is disabled, returning action value as string.'); | ||||
| 
 | ||||
|             return (string) $this->action_value; | ||||
|             return (string)$this->action_value; | ||||
|         } | ||||
|         if (true === config('firefly.feature_flags.expression_engine') && str_starts_with($this->action_value, '\=')) { | ||||
|             // return literal string.
 | ||||
| @@ -54,7 +54,7 @@ class RuleAction extends Model | ||||
|             $result = $expr->evaluate($journal); | ||||
|         } catch (SyntaxError $e) { | ||||
|             Log::error(sprintf('Expression engine failed to evaluate expression "%s" with error "%s".', $this->action_value, $e->getMessage())); | ||||
|             $result = (string) $this->action_value; | ||||
|             $result = (string)$this->action_value; | ||||
|         } | ||||
|         Log::debug(sprintf('Expression engine is enabled, result of expression "%s" is "%s".', $this->action_value, $result)); | ||||
| 
 | ||||
| @@ -66,20 +66,6 @@ class RuleAction extends Model | ||||
|         return $this->belongsTo(Rule::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -90,4 +76,18 @@ class RuleAction extends Model | ||||
|             'stop_processing' => 'boolean', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\RuleGroupObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| @@ -53,13 +52,13 @@ class RuleGroup extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $ruleGroupId = (int) $value; | ||||
|             $ruleGroupId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user        = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|RuleGroup $ruleGroup */ | ||||
|             $ruleGroup   = $user->ruleGroups()->find($ruleGroupId); | ||||
|             $ruleGroup = $user->ruleGroups()->find($ruleGroupId); | ||||
|             if (null !== $ruleGroup) { | ||||
|                 return $ruleGroup; | ||||
|             } | ||||
| @@ -78,13 +77,6 @@ class RuleGroup extends Model | ||||
|         return $this->hasMany(Rule::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -98,4 +90,11 @@ class RuleGroup extends Model | ||||
|             'user_group_id'   => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -39,20 +39,6 @@ class RuleTrigger extends Model | ||||
|         return $this->belongsTo(Rule::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -63,4 +49,18 @@ class RuleTrigger extends Model | ||||
|             'stop_processing' => 'boolean', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function ruleId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Casts\SeparateTimezoneCaster; | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\TagObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| @@ -46,7 +45,7 @@ class Tag extends Model | ||||
| 
 | ||||
|     protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tag_mode']; | ||||
| 
 | ||||
|     protected $hidden   = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; | ||||
|     protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
| @@ -56,13 +55,13 @@ class Tag extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $tagId = (int) $value; | ||||
|             $tagId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user  = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Tag $tag */ | ||||
|             $tag   = $user->tags()->find($tagId); | ||||
|             $tag = $user->tags()->find($tagId); | ||||
|             if (null !== $tag) { | ||||
|                 return $tag; | ||||
|             } | ||||
|   | ||||
| @@ -23,12 +23,11 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Handlers\Observer\TransactionObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Attributes\ObservedBy; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| @@ -45,7 +44,7 @@ class Transaction extends Model | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     protected $fillable | ||||
|                       = [ | ||||
|         = [ | ||||
|             'account_id', | ||||
|             'transaction_journal_id', | ||||
|             'description', | ||||
| @@ -93,6 +92,31 @@ class Transaction extends Model | ||||
|         return $this->belongsTo(TransactionCurrency::class, 'foreign_currency_id'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setAmountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['amount'] = (string)$value; | ||||
|     } | ||||
| 
 | ||||
|     public function transactionCurrency(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionCurrency::class); | ||||
|     } | ||||
| 
 | ||||
|     public function transactionJournal(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionJournal::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function accountId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check for transactions AFTER a specified date. | ||||
|      */ | ||||
| @@ -121,6 +145,23 @@ class Transaction extends Model | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|     protected function amount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function balanceDirty(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => 1 === (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check for transactions BEFORE the specified date. | ||||
|      */ | ||||
| @@ -133,78 +174,6 @@ class Transaction extends Model | ||||
|         $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function transactionTypes(Builder $query, array $types): void | ||||
|     { | ||||
|         if (!self::isJoined($query, 'transaction_journals')) { | ||||
|             $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'); | ||||
|         } | ||||
| 
 | ||||
|         if (!self::isJoined($query, 'transaction_types')) { | ||||
|             $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         } | ||||
|         $query->whereIn('transaction_types.type', $types); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function setAmountAttribute($value): void | ||||
|     { | ||||
|         $this->attributes['amount'] = (string) $value; | ||||
|     } | ||||
| 
 | ||||
|     public function transactionCurrency(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionCurrency::class); | ||||
|     } | ||||
| 
 | ||||
|     public function transactionJournal(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionJournal::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function accountId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
|     protected function amount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function balanceDirty(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => 1 === (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the foreign amount | ||||
|      */ | ||||
|     protected function foreignAmount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (string) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionJournalId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -225,4 +194,34 @@ class Transaction extends Model | ||||
|             'native_foreign_amount' => 'string', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the foreign amount | ||||
|      */ | ||||
|     protected function foreignAmount(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (string)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionJournalId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function transactionTypes(Builder $query, array $types): void | ||||
|     { | ||||
|         if (!self::isJoined($query, 'transaction_journals')) { | ||||
|             $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'); | ||||
|         } | ||||
| 
 | ||||
|         if (!self::isJoined($query, 'transaction_types')) { | ||||
|             $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         } | ||||
|         $query->whereIn('transaction_types.type', $types); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -40,7 +40,7 @@ class TransactionCurrency extends Model | ||||
|     public ?bool $userGroupEnabled = null; | ||||
|     public ?bool $userGroupNative  = null; | ||||
| 
 | ||||
|     protected $fillable            = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; | ||||
|     protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
| @@ -50,7 +50,7 @@ class TransactionCurrency extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $currencyId = (int) $value; | ||||
|             $currencyId = (int)$value; | ||||
|             $currency   = self::find($currencyId); | ||||
|             if (null !== $currency) { | ||||
|                 $currency->refreshForUser(auth()->user()); | ||||
| @@ -101,13 +101,6 @@ class TransactionCurrency extends Model | ||||
|         return $this->belongsToMany(User::class)->withTimestamps()->withPivot('user_default'); | ||||
|     } | ||||
| 
 | ||||
|     protected function decimalPlaces(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -118,4 +111,11 @@ class TransactionCurrency extends Model | ||||
|             'enabled'        => 'bool', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function decimalPlaces(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,6 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\TransactionGroupObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| @@ -53,17 +52,16 @@ class TransactionGroup extends Model | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value)); | ||||
|         if (auth()->check()) { | ||||
|             $groupId = (int) $value; | ||||
|             $groupId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user    = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
|             app('log')->debug(sprintf('User authenticated as %s', $user->email)); | ||||
| 
 | ||||
|             /** @var null|TransactionGroup $group */ | ||||
|             $group   = $user->transactionGroups() | ||||
|                 ->with(['transactionJournals', 'transactionJournals.transactions']) | ||||
|                 ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']) | ||||
|             ; | ||||
|             $group = $user->transactionGroups() | ||||
|                           ->with(['transactionJournals', 'transactionJournals.transactions']) | ||||
|                           ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']); | ||||
|             if (null !== $group) { | ||||
|                 app('log')->debug(sprintf('Found group #%d.', $group->id)); | ||||
| 
 | ||||
|   | ||||
| @@ -23,16 +23,15 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Models; | ||||
| 
 | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\TransactionJournalObserver; | ||||
| use Illuminate\Database\Eloquent\Attributes\ObservedBy; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Casts\SeparateTimezoneCaster; | ||||
| use FireflyIII\Enums\TransactionTypeEnum; | ||||
| use FireflyIII\Handlers\Observer\TransactionJournalObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Database\Eloquent\Attributes\ObservedBy; | ||||
| use Illuminate\Database\Eloquent\Attributes\Scope; | ||||
| use Illuminate\Database\Eloquent\Builder as EloquentBuilder; | ||||
| use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| @@ -49,7 +48,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
|  * @method        EloquentBuilder|static after() | ||||
|  * @method static EloquentBuilder|static query() | ||||
|  */ | ||||
| 
 | ||||
| #[ObservedBy([TransactionJournalObserver::class])]
 | ||||
| class TransactionJournal extends Model | ||||
| { | ||||
| @@ -59,7 +57,7 @@ class TransactionJournal extends Model | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     protected $fillable | ||||
|                       = [ | ||||
|         = [ | ||||
|             'user_id', | ||||
|             'user_group_id', | ||||
|             'transaction_type_id', | ||||
| @@ -83,13 +81,13 @@ class TransactionJournal extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $journalId = (int) $value; | ||||
|             $journalId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user      = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|TransactionJournal $journal */ | ||||
|             $journal   = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); | ||||
|             $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); | ||||
|             if (null !== $journal) { | ||||
|                 return $journal; | ||||
|             } | ||||
| @@ -170,32 +168,6 @@ class TransactionJournal extends Model | ||||
|         return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function transactionTypes(EloquentBuilder $query, array $types): void | ||||
|     { | ||||
|         if (!self::isJoined($query, 'transaction_types')) { | ||||
|             $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         } | ||||
|         if (0 !== count($types)) { | ||||
|             $query->whereIn('transaction_types.type', $types); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if tables are joined. | ||||
|      */ | ||||
|     public static function isJoined(EloquentBuilder $query, string $table): bool | ||||
|     { | ||||
|         $joins = $query->getQuery()->joins; | ||||
|         foreach ($joins as $join) { | ||||
|             if ($join->table === $table) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public function sourceJournalLinks(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(TransactionJournalLink::class, 'source_id'); | ||||
| @@ -236,20 +208,6 @@ class TransactionJournal extends Model | ||||
|         return $this->belongsTo(UserGroup::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -268,4 +226,44 @@ class TransactionJournal extends Model | ||||
|             'user_group_id' => 'integer', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function order(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[Scope]
 | ||||
|     protected function transactionTypes(EloquentBuilder $query, array $types): void | ||||
|     { | ||||
|         if (!self::isJoined($query, 'transaction_types')) { | ||||
|             $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); | ||||
|         } | ||||
|         if (0 !== count($types)) { | ||||
|             $query->whereIn('transaction_types.type', $types); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if tables are joined. | ||||
|      */ | ||||
|     public static function isJoined(EloquentBuilder $query, string $table): bool | ||||
|     { | ||||
|         $joins = $query->getQuery()->joins; | ||||
|         foreach ($joins as $join) { | ||||
|             if ($join->table === $table) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -44,14 +44,13 @@ class TransactionJournalLink extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $linkId = (int) $value; | ||||
|             $linkId = (int)$value; | ||||
|             $link   = self::where('journal_links.id', $linkId) | ||||
|                 ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') | ||||
|                 ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') | ||||
|                 ->where('t_a.user_id', auth()->user()->id) | ||||
|                 ->where('t_b.user_id', auth()->user()->id) | ||||
|                 ->first(['journal_links.*']) | ||||
|             ; | ||||
|                           ->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id') | ||||
|                           ->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id') | ||||
|                           ->where('t_a.user_id', auth()->user()->id) | ||||
|                           ->where('t_b.user_id', auth()->user()->id) | ||||
|                           ->first(['journal_links.*']); | ||||
|             if (null !== $link) { | ||||
|                 return $link; | ||||
|             } | ||||
| @@ -83,27 +82,6 @@ class TransactionJournalLink extends Model | ||||
|         return $this->belongsTo(TransactionJournal::class, 'source_id'); | ||||
|     } | ||||
| 
 | ||||
|     protected function destinationId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function linkTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function sourceId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -111,4 +89,25 @@ class TransactionJournalLink extends Model | ||||
|             'updated_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function destinationId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function linkTypeId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function sourceId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Database\Eloquent\SoftDeletes; | ||||
| 
 | ||||
| use function Safe\json_decode; | ||||
| use function Safe\json_encode; | ||||
| 
 | ||||
| @@ -39,29 +38,13 @@ class TransactionJournalMeta extends Model | ||||
| 
 | ||||
|     protected $fillable = ['transaction_journal_id', 'name', 'data', 'hash']; | ||||
| 
 | ||||
|     protected $table    = 'journal_meta'; | ||||
| 
 | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn ($value) => json_decode((string) $value, false), set: function ($value) { | ||||
|             $data = json_encode($value); | ||||
| 
 | ||||
|             return ['data' => $data, 'hash' => hash('sha256', $data)]; | ||||
|         }); | ||||
|     } | ||||
|     protected $table = 'journal_meta'; | ||||
| 
 | ||||
|     public function transactionJournal(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(TransactionJournal::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionJournalId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
| @@ -70,4 +53,20 @@ class TransactionJournalMeta extends Model | ||||
|             'deleted_at' => 'datetime', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     protected function data(): Attribute | ||||
|     { | ||||
|         return Attribute::make(get: fn($value) => json_decode((string)$value, false), set: function ($value) { | ||||
|             $data = json_encode($value); | ||||
| 
 | ||||
|             return ['data' => $data, 'hash' => hash('sha256', $data)]; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     protected function transactionJournalId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -36,34 +36,41 @@ class TransactionType extends Model | ||||
|     use ReturnsIntegerIdTrait; | ||||
|     use SoftDeletes; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string DEPOSIT          = 'Deposit'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string DEPOSIT = 'Deposit'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string INVALID          = 'Invalid'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string INVALID = 'Invalid'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string LIABILITY_CREDIT = 'Liability credit'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string OPENING_BALANCE  = 'Opening balance'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string OPENING_BALANCE = 'Opening balance'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string RECONCILIATION   = 'Reconciliation'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string RECONCILIATION = 'Reconciliation'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string TRANSFER         = 'Transfer'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string TRANSFER = 'Transfer'; | ||||
| 
 | ||||
|     #[Deprecated] /** @deprecated */
 | ||||
|     public const string WITHDRAWAL       = 'Withdrawal'; | ||||
|     #[Deprecated]
 | ||||
|     /** @deprecated */ | ||||
|     public const string WITHDRAWAL = 'Withdrawal'; | ||||
| 
 | ||||
|     protected $casts | ||||
|                                          = [ | ||||
|                         = [ | ||||
|             'created_at' => 'datetime', | ||||
|             'updated_at' => 'datetime', | ||||
|             'deleted_at' => 'datetime', | ||||
|         ]; | ||||
|     protected $fillable                  = ['type']; | ||||
|     protected $fillable = ['type']; | ||||
| 
 | ||||
|     /** | ||||
|      * Route binder. Converts the key in the URL to the specified object (or throw 404). | ||||
|   | ||||
| @@ -47,19 +47,19 @@ class UserGroup extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $userGroupId = (int) $value; | ||||
|             $userGroupId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user        = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|UserGroup $userGroup */ | ||||
|             $userGroup   = self::find($userGroupId); | ||||
|             $userGroup = self::find($userGroupId); | ||||
|             if (null === $userGroup) { | ||||
|                 throw new NotFoundHttpException(); | ||||
|             } | ||||
|             // need at least ready only to be aware of the user group's existence,
 | ||||
|             // but owner/full role (in the group) or global owner role may overrule this.
 | ||||
|             $access      = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); | ||||
|             $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); | ||||
|             if ($access) { | ||||
|                 return $userGroup; | ||||
|             } | ||||
|   | ||||
| @@ -27,7 +27,6 @@ namespace FireflyIII\Models; | ||||
| use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum; | ||||
| use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum; | ||||
| use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum; | ||||
| use FireflyIII\Handlers\Observer\AccountObserver; | ||||
| use FireflyIII\Handlers\Observer\WebhookObserver; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerIdTrait; | ||||
| use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; | ||||
| @@ -138,10 +137,10 @@ class Webhook extends Model | ||||
|             $webhookId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user      = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|Webhook $webhook */ | ||||
|             $webhook   = $user->webhooks()->find($webhookId); | ||||
|             $webhook = $user->webhooks()->find($webhookId); | ||||
|             if (null !== $webhook) { | ||||
|                 return $webhook; | ||||
|             } | ||||
| @@ -155,16 +154,16 @@ class Webhook extends Model | ||||
|         return $this->belongsTo(User::class); | ||||
|     } | ||||
| 
 | ||||
|     public function webhookMessages(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(WebhookMessage::class); | ||||
|     } | ||||
| 
 | ||||
|     public function webhookDeliveries(): BelongsToMany | ||||
|     { | ||||
|         return $this->belongsToMany(WebhookDelivery::class); | ||||
|     } | ||||
| 
 | ||||
|     public function webhookMessages(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(WebhookMessage::class); | ||||
|     } | ||||
| 
 | ||||
|     public function webhookResponses(): BelongsToMany | ||||
|     { | ||||
|         return $this->belongsToMany(WebhookResponse::class); | ||||
|   | ||||
| @@ -45,13 +45,13 @@ class WebhookAttempt extends Model | ||||
|     public static function routeBinder(string $value): self | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             $attemptId = (int) $value; | ||||
|             $attemptId = (int)$value; | ||||
| 
 | ||||
|             /** @var User $user */ | ||||
|             $user      = auth()->user(); | ||||
|             $user = auth()->user(); | ||||
| 
 | ||||
|             /** @var null|WebhookAttempt $attempt */ | ||||
|             $attempt   = self::find($attemptId); | ||||
|             $attempt = self::find($attemptId); | ||||
|             if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) { | ||||
|                 return $attempt; | ||||
|             } | ||||
| @@ -68,7 +68,7 @@ class WebhookAttempt extends Model | ||||
|     protected function webhookMessageId(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class WebhookDelivery extends Model | ||||
|     protected function key(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -72,6 +72,17 @@ class WebhookMessage extends Model | ||||
|         return $this->hasMany(WebhookAttempt::class); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'sent'    => 'boolean', | ||||
|             'errored' => 'boolean', | ||||
|             'uuid'    => 'string', | ||||
|             'message' => 'json', | ||||
|             'logs'    => 'json', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the amount | ||||
|      */ | ||||
| @@ -88,15 +99,4 @@ class WebhookMessage extends Model | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     protected function casts(): array | ||||
|     { | ||||
|         return [ | ||||
|             'sent'    => 'boolean', | ||||
|             'errored' => 'boolean', | ||||
|             'uuid'    => 'string', | ||||
|             'message' => 'json', | ||||
|             'logs'    => 'json', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class WebhookResponse extends Model | ||||
|     protected function key(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class WebhookTrigger extends Model | ||||
|     protected function key(): Attribute | ||||
|     { | ||||
|         return Attribute::make( | ||||
|             get: static fn ($value) => (int) $value, | ||||
|             get: static fn($value) => (int)$value, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user