mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Add and remove exchange rates
This commit is contained in:
		| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyController.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\ExchangeRate\DestroyRequest; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| class DestroyController extends Controller | ||||
| { | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     public const string RESOURCE_KEY = 'exchange-rates'; | ||||
| 
 | ||||
|     private ExchangeRateRepositoryInterface $repository; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(ExchangeRateRepositoryInterface::class); | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse | ||||
|     { | ||||
|         $date = $request->getDate(); | ||||
|         $rate = $this->repository->getSpecificRateOnDate($from, $to, $date); | ||||
|         if (null === $rate) { | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
|         $this->repository->deleteRate($rate); | ||||
|         return response()->json([], 204); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,82 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyController.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\ExchangeRate\StoreRequest; | ||||
| use FireflyIII\Api\V2\Request\Model\ExchangeRate\UpdateRequest; | ||||
| use FireflyIII\Models\CurrencyExchangeRate; | ||||
| use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Transformers\V2\ExchangeRateTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| class StoreController extends Controller | ||||
| { | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     public const string RESOURCE_KEY = 'exchange-rates'; | ||||
| 
 | ||||
|     private ExchangeRateRepositoryInterface $repository; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(ExchangeRateRepositoryInterface::class); | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public function store(StoreRequest $request): JsonResponse | ||||
|     { | ||||
|         $date = $request->getDate(); | ||||
|         $rate = $request->getRate(); | ||||
|         $from = $request->getFromCurrency(); | ||||
|         $to = $request->getToCurrency(); | ||||
| 
 | ||||
|         // already has rate?
 | ||||
|         $object = $this->repository->getSpecificRateOnDate($from, $to, $date); | ||||
|         if(null !== $object) { | ||||
|             // just update it, no matter.
 | ||||
|             $rate = $this->repository->updateExchangeRate($object, $rate, $date); | ||||
|         } | ||||
|         if(null === $object) { | ||||
|             // store new
 | ||||
|             $rate = $this->repository->storeExchangeRate($from, $to, $rate, $date); | ||||
|         } | ||||
| 
 | ||||
|         $transformer = new ExchangeRateTransformer(); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         return response() | ||||
|             ->api($this->jsonApiObject(self::RESOURCE_KEY, $rate, $transformer)) | ||||
|             ->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyController.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\ExchangeRate; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\ExchangeRate\UpdateRequest; | ||||
| use FireflyIII\Models\CurrencyExchangeRate; | ||||
| use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Transformers\V2\ExchangeRateTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| class UpdateController extends Controller | ||||
| { | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     public const string RESOURCE_KEY = 'exchange-rates'; | ||||
| 
 | ||||
|     private ExchangeRateRepositoryInterface $repository; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->repository = app(ExchangeRateRepositoryInterface::class); | ||||
|                 $this->repository->setUserGroup($this->validateUserGroup($request)); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public function update(UpdateRequest $request, CurrencyExchangeRate $exchangeRate): JsonResponse | ||||
|     { | ||||
|         $date = $request->getDate(); | ||||
|         $rate = $request->getRate(); | ||||
|         $exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date); | ||||
|         $transformer = new ExchangeRateTransformer(); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         return response() | ||||
|             ->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer)) | ||||
|             ->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										51
									
								
								app/Api/V2/Request/Model/ExchangeRate/DestroyRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Api/V2/Request/Model/ExchangeRate/DestroyRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyRequest.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class DestroyRequest extends FormRequest | ||||
| { | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
| 
 | ||||
|     public function getDate(): Carbon | ||||
|     { | ||||
|         return $this->getCarbonDate('date'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'date'  => 'required|date|after:1900-01-01|before:2099-12-31', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										67
									
								
								app/Api/V2/Request/Model/ExchangeRate/StoreRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/Api/V2/Request/Model/ExchangeRate/StoreRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyRequest.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class StoreRequest extends FormRequest | ||||
| { | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
| 
 | ||||
|     public function getDate(): ?Carbon | ||||
|     { | ||||
|         return $this->getCarbonDate('date'); | ||||
|     } | ||||
| 
 | ||||
|     public function getRate(): string | ||||
|     { | ||||
|         return (string) $this->get('rate'); | ||||
|     } | ||||
| 
 | ||||
|     public function getFromCurrency(): TransactionCurrency { | ||||
|         return TransactionCurrency::where('code', $this->get('from'))->first(); | ||||
|     } | ||||
|     public function getToCurrency(): TransactionCurrency { | ||||
|         return TransactionCurrency::where('code', $this->get('to'))->first(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'date' => 'required|date|after:1900-01-01|before:2099-12-31', | ||||
|             'rate' => 'required|numeric|gt:0', | ||||
|             'from' => 'required|exists:transaction_currencies,code', | ||||
|             'to'   => 'required|exists:transaction_currencies,code', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										56
									
								
								app/Api/V2/Request/Model/ExchangeRate/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								app/Api/V2/Request/Model/ExchangeRate/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| <?php | ||||
| /* | ||||
|  * DestroyRequest.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Model\ExchangeRate; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| 
 | ||||
| class UpdateRequest extends FormRequest | ||||
| { | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
| 
 | ||||
|     public function getDate(): ?Carbon | ||||
|     { | ||||
|         return $this->getCarbonDate('date'); | ||||
|     } | ||||
| 
 | ||||
|     public function getRate(): string { | ||||
|         return (string) $this->get('rate'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'date'  => 'date|after:1900-01-01|before:2099-12-31', | ||||
|             'rate'  => 'required|numeric|gt:0', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -79,7 +79,7 @@ class UpdateGroupInformation extends Command | ||||
|     { | ||||
|         $group = $user->userGroup; | ||||
|         if (null === $group) { | ||||
|             $this->friendlyWarning(sprintf('User "%s" has no group.', $user->email)); | ||||
|             $this->friendlyWarning(sprintf('User "%s" has no group. Please run "php artisan firefly-iii:create-group-memberships"', $user->email)); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -47,20 +47,13 @@ class PiggyBankEventFactory | ||||
|         $piggyRepos = app(PiggyBankRepositoryInterface::class); | ||||
|         $piggyRepos->setUser($journal->user); | ||||
| 
 | ||||
|         $repetition = $piggyRepos->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             app('log')->error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         app('log')->debug('Found repetition'); | ||||
|         $amount     = $piggyRepos->getExactAmount($piggyBank, $repetition, $journal); | ||||
|         $amount     = $piggyRepos->getExactAmount($piggyBank, $journal); | ||||
|         if (0 === bccomp($amount, '0')) { | ||||
|             app('log')->debug('Amount is zero, will not create event.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         // amount can be negative here
 | ||||
|         $piggyRepos->addAmountToRepetition($repetition, $amount, $journal); | ||||
|         $piggyRepos->addAmountToPiggyBank($piggyBank, $amount, $journal); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,7 +30,7 @@ use FireflyIII\Factory\PiggyBankFactory; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Note; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Models\PiggyBankRepetition; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; | ||||
| use FireflyIII\Support\Http\Api\ExchangeRateConverter; | ||||
| @@ -43,17 +43,20 @@ trait ModifiesPiggyBanks | ||||
| { | ||||
|     use CreatesObjectGroups; | ||||
| 
 | ||||
|     public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void | ||||
|     public function addAmountToPiggyBank(PiggyBank $piggyBank, string $amount, TransactionJournal $journal): void | ||||
|     { | ||||
|         throw new FireflyException('[a] Piggy bank repetitions are EOL.'); | ||||
|         Log::debug(sprintf('addAmountToRepetition: %s', $amount)); | ||||
|         Log::debug(sprintf('addAmountToPiggyBank: %s', $amount)); | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             /** @var Transaction $source */ | ||||
|             $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); | ||||
|             Log::debug('Remove amount.'); | ||||
|             $this->removeAmount($repetition->piggyBank, bcmul($amount, '-1'), $journal); | ||||
|             $this->removeAmount($piggyBank, $source->account, bcmul($amount, '-1'), $journal); | ||||
|         } | ||||
|         if (1 === bccomp($amount, '0')) { | ||||
|             /** @var Transaction $destination */ | ||||
|             $destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first(); | ||||
|             Log::debug('Add amount.'); | ||||
|             $this->addAmount($repetition->piggyBank, $amount, $journal); | ||||
|             $this->addAmount($piggyBank, $destination->account, $amount, $journal); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -65,9 +68,9 @@ trait ModifiesPiggyBanks | ||||
|         $pivot->native_current_amount = null; | ||||
| 
 | ||||
|         // also update native_current_amount.
 | ||||
|         $userCurrency                 = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); | ||||
|         $userCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); | ||||
|         if ($userCurrency->id !== $piggyBank->transaction_currency_id) { | ||||
|             $converter                    = new ExchangeRateConverter(); | ||||
|             $converter = new ExchangeRateConverter(); | ||||
|             $converter->setIgnoreSettings(true); | ||||
|             $pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $pivot->current_amount); | ||||
|         } | ||||
| @@ -88,9 +91,9 @@ trait ModifiesPiggyBanks | ||||
|         $pivot->native_current_amount = null; | ||||
| 
 | ||||
|         // also update native_current_amount.
 | ||||
|         $userCurrency                 = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); | ||||
|         $userCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); | ||||
|         if ($userCurrency->id !== $piggyBank->transaction_currency_id) { | ||||
|             $converter                    = new ExchangeRateConverter(); | ||||
|             $converter = new ExchangeRateConverter(); | ||||
|             $converter->setIgnoreSettings(true); | ||||
|             $pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $pivot->current_amount); | ||||
|         } | ||||
| @@ -122,8 +125,8 @@ trait ModifiesPiggyBanks | ||||
|             Log::debug(sprintf('Maximum amount: %s', $maxAmount)); | ||||
|         } | ||||
| 
 | ||||
|         $compare       = bccomp($amount, $maxAmount); | ||||
|         $result        = $compare <= 0; | ||||
|         $compare = bccomp($amount, $maxAmount); | ||||
|         $result  = $compare <= 0; | ||||
| 
 | ||||
|         Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true))); | ||||
| 
 | ||||
| @@ -157,11 +160,11 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|     public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank | ||||
|     { | ||||
|         $repetition                 = $this->getRepetition($piggyBank); | ||||
|         $repetition = $this->getRepetition($piggyBank); | ||||
|         if (null === $repetition) { | ||||
|             return $piggyBank; | ||||
|         } | ||||
|         $max                        = $piggyBank->target_amount; | ||||
|         $max = $piggyBank->target_amount; | ||||
|         if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) { | ||||
|             $amount = $max; | ||||
|         } | ||||
| @@ -204,14 +207,14 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|     public function update(PiggyBank $piggyBank, array $data): PiggyBank | ||||
|     { | ||||
|         $piggyBank     = $this->updateProperties($piggyBank, $data); | ||||
|         $piggyBank = $this->updateProperties($piggyBank, $data); | ||||
|         if (array_key_exists('notes', $data)) { | ||||
|             $this->updateNote($piggyBank, (string) $data['notes']); | ||||
|         } | ||||
| 
 | ||||
|         // update the order of the piggy bank:
 | ||||
|         $oldOrder      = $piggyBank->order; | ||||
|         $newOrder      = (int) ($data['order'] ?? $oldOrder); | ||||
|         $oldOrder = $piggyBank->order; | ||||
|         $newOrder = (int) ($data['order'] ?? $oldOrder); | ||||
|         if ($oldOrder !== $newOrder) { | ||||
|             $this->setOrder($piggyBank, $newOrder); | ||||
|         } | ||||
| @@ -303,7 +306,7 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $dbNote       = $piggyBank->notes()->first(); | ||||
|         $dbNote = $piggyBank->notes()->first(); | ||||
|         if (null === $dbNote) { | ||||
|             $dbNote = new Note(); | ||||
|             $dbNote->noteable()->associate($piggyBank); | ||||
| @@ -314,16 +317,15 @@ trait ModifiesPiggyBanks | ||||
| 
 | ||||
|     public function setOrder(PiggyBank $piggyBank, int $newOrder): bool | ||||
|     { | ||||
|         $oldOrder         = $piggyBank->order; | ||||
|         $oldOrder = $piggyBank->order; | ||||
|         // Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder));
 | ||||
|         if ($newOrder > $oldOrder) { | ||||
|             PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|                 ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                 ->where('accounts.user_id', $this->user->id) | ||||
|                 ->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) | ||||
|                 ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|                 ->distinct()->decrement('piggy_banks.order') | ||||
|             ; | ||||
|                      ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                      ->where('accounts.user_id', $this->user->id) | ||||
|                      ->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) | ||||
|                      ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|                      ->distinct()->decrement('piggy_banks.order'); | ||||
| 
 | ||||
|             $piggyBank->order = $newOrder; | ||||
|             Log::debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); | ||||
| @@ -332,12 +334,11 @@ trait ModifiesPiggyBanks | ||||
|             return true; | ||||
|         } | ||||
|         PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') | ||||
|             ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|             ->where('accounts.user_id', $this->user->id) | ||||
|             ->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) | ||||
|             ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|             ->distinct()->increment('piggy_banks.order') | ||||
|         ; | ||||
|                  ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') | ||||
|                  ->where('accounts.user_id', $this->user->id) | ||||
|                  ->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) | ||||
|                  ->where('piggy_banks.id', '!=', $piggyBank->id) | ||||
|                  ->distinct()->increment('piggy_banks.order'); | ||||
| 
 | ||||
|         $piggyBank->order = $newOrder; | ||||
|         Log::debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); | ||||
|   | ||||
| @@ -129,10 +129,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string | ||||
|     public function getExactAmount(PiggyBank $piggyBank, TransactionJournal $journal): string | ||||
|     { | ||||
|         throw new FireflyException('[c] Piggy bank repetitions are EOL.'); | ||||
|         app('log')->debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id)); | ||||
|         app('log')->debug(sprintf('Now in getExactAmount(%d, %d)', $piggyBank->id, $journal->id)); | ||||
| 
 | ||||
|         $operator          = null; | ||||
|         $currency          = null; | ||||
| @@ -146,9 +145,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface | ||||
|         $accountRepos->setUser($this->user); | ||||
| 
 | ||||
|         $defaultCurrency   = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); | ||||
|         $piggyBankCurrency = $accountRepos->getAccountCurrency($piggyBank->account) ?? $defaultCurrency; | ||||
| 
 | ||||
|         app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code)); | ||||
|         app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBank->transactionCurrency->code)); | ||||
| 
 | ||||
|         /** @var Transaction $source */ | ||||
|         $source            = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); | ||||
| @@ -192,8 +190,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface | ||||
|         } | ||||
| 
 | ||||
|         app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); | ||||
|         $room              = bcsub($piggyBank->target_amount, $repetition->current_amount); | ||||
|         $compare           = bcmul($repetition->current_amount, '-1'); | ||||
|         $currentAmount = $this->getCurrentAmount($piggyBank); | ||||
|         $room              = bcsub($piggyBank->target_amount, $currentAmount); | ||||
|         $compare           = bcmul($currentAmount, '-1'); | ||||
| 
 | ||||
|         if (0 === bccomp($piggyBank->target_amount, '0')) { | ||||
|             // amount is zero? then the "room" is positive amount of we wish to add or remove.
 | ||||
| @@ -215,7 +214,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface | ||||
| 
 | ||||
|         // amount is negative and $currentAmount is smaller than $amount
 | ||||
|         if (-1 === bccomp($amount, '0') && 1 === bccomp($compare, $amount)) { | ||||
|             app('log')->debug(sprintf('Max amount to remove is %f', $repetition->current_amount)); | ||||
|             app('log')->debug(sprintf('Max amount to remove is %f', $currentAmount)); | ||||
|             app('log')->debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); | ||||
|             app('log')->debug(sprintf('New amount is %f', $compare)); | ||||
| 
 | ||||
|   | ||||
| @@ -40,7 +40,7 @@ interface PiggyBankRepositoryInterface | ||||
| { | ||||
|     public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool; | ||||
| 
 | ||||
|     public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void; | ||||
|     public function addAmountToPiggyBank(PiggyBank $piggyBank, string $amount, TransactionJournal $journal): void; | ||||
| 
 | ||||
|     public function canAddAmount(PiggyBank $piggyBank, Account $account, string $amount): bool; | ||||
| 
 | ||||
| @@ -80,7 +80,7 @@ interface PiggyBankRepositoryInterface | ||||
|     /** | ||||
|      * Used for connecting to a piggy bank. | ||||
|      */ | ||||
|     public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string; | ||||
|     public function getExactAmount(PiggyBank $piggyBank, TransactionJournal $journal): string; | ||||
| 
 | ||||
|     /** | ||||
|      * Return note for piggy bank. | ||||
|   | ||||
| @@ -24,6 +24,8 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Repositories\UserGroups\ExchangeRate; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\CurrencyExchangeRate; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| @@ -39,19 +41,57 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface | ||||
|         // orderBy('date', 'DESC')->toRawSql();
 | ||||
|         return | ||||
|             $this->userGroup->currencyExchangeRates() | ||||
|                 ->where(function (Builder $q1) use ($from, $to): void { | ||||
|                     $q1->where(function (Builder $q) use ($from, $to): void { | ||||
|                         $q->where('from_currency_id', $from->id) | ||||
|                             ->where('to_currency_id', $to->id) | ||||
|                         ; | ||||
|                     })->orWhere(function (Builder $q) use ($from, $to): void { | ||||
|                         $q->where('from_currency_id', $to->id) | ||||
|                             ->where('to_currency_id', $from->id) | ||||
|                         ; | ||||
|                     }); | ||||
|                 }) | ||||
|                 ->orderBy('date', 'DESC')->get(['currency_exchange_rates.*']) | ||||
|         ; | ||||
|                             ->where(function (Builder $q1) use ($from, $to): void { | ||||
|                                 $q1->where(function (Builder $q) use ($from, $to): void { | ||||
|                                     $q->where('from_currency_id', $from->id) | ||||
|                                       ->where('to_currency_id', $to->id); | ||||
|                                 })->orWhere(function (Builder $q) use ($from, $to): void { | ||||
|                                     $q->where('from_currency_id', $to->id) | ||||
|                                       ->where('to_currency_id', $from->id); | ||||
|                                 }); | ||||
|                             }) | ||||
|                             ->orderBy('date', 'DESC') | ||||
|                             ->get(['currency_exchange_rates.*']); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     #[\Override] public function getSpecificRateOnDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): ?CurrencyExchangeRate
 | ||||
|     { | ||||
|         return | ||||
|             $this->userGroup->currencyExchangeRates() | ||||
|                             ->where('from_currency_id', $from->id) | ||||
|                             ->where('to_currency_id', $to->id) | ||||
|                             ->where('date', $date->format('Y-m-d')) | ||||
|                             ->first(); | ||||
|     } | ||||
| 
 | ||||
|     #[\Override] public function deleteRate(CurrencyExchangeRate $rate): void
 | ||||
|     { | ||||
|         $this->userGroup->currencyExchangeRates()->where('id', $rate->id)->delete(); | ||||
|     } | ||||
| 
 | ||||
|     #[\Override] public function updateExchangeRate(CurrencyExchangeRate $object, string $rate, ?Carbon $date = null): CurrencyExchangeRate
 | ||||
|     { | ||||
|         $object->rate = $rate; | ||||
|         if (null !== $date) { | ||||
|             $object->date = $date; | ||||
|         } | ||||
|         $object->save(); | ||||
|         return $object; | ||||
|     } | ||||
| 
 | ||||
|     #[\Override] public function storeExchangeRate(TransactionCurrency $from, TransactionCurrency $to, string $rate, Carbon $date): CurrencyExchangeRate
 | ||||
|     { | ||||
|         $object                   = new CurrencyExchangeRate(); | ||||
|         $object->user_id          = auth()->user()->id; | ||||
|         $object->user_group_id    = $this->userGroup->id; | ||||
|         $object->from_currency_id = $from->id; | ||||
|         $object->to_currency_id   = $to->id; | ||||
|         $object->rate             = $rate; | ||||
|         $object->date             = $date; | ||||
|         $object->date_tz          = $date->format('e'); | ||||
|         $object->save(); | ||||
| 
 | ||||
|         return $object; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,10 +24,21 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Repositories\UserGroups\ExchangeRate; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Models\CurrencyExchangeRate; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use Illuminate\Support\Collection; | ||||
| 
 | ||||
| interface ExchangeRateRepositoryInterface | ||||
| { | ||||
|     public function getRates(TransactionCurrency $from, TransactionCurrency $to): Collection; | ||||
| 
 | ||||
|     public function getSpecificRateOnDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): ?CurrencyExchangeRate; | ||||
| 
 | ||||
|     public function deleteRate(CurrencyExchangeRate $rate): void; | ||||
| 
 | ||||
|     public function updateExchangeRate(CurrencyExchangeRate $object, string $rate, ?Carbon $date = null): CurrencyExchangeRate; | ||||
| 
 | ||||
|     public function storeExchangeRate(TransactionCurrency $from, TransactionCurrency $to, string $rate, Carbon $date): CurrencyExchangeRate; | ||||
| 
 | ||||
| } | ||||
|   | ||||
							
								
								
									
										51
									
								
								app/Support/Binder/UserGroupExchangeRate.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Support/Binder/UserGroupExchangeRate.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * UserGroupTransaction.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Support\Binder; | ||||
| 
 | ||||
| use FireflyIII\Models\CurrencyExchangeRate; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Routing\Route; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserGroupTransaction. | ||||
|  */ | ||||
| class UserGroupExchangeRate implements BinderInterface | ||||
| { | ||||
|     public static function routeBinder(string $value, Route $route): CurrencyExchangeRate | ||||
|     { | ||||
|         if (auth()->check()) { | ||||
|             /** @var User $user */ | ||||
|             $user = auth()->user(); | ||||
|             $rate = CurrencyExchangeRate::where('id', (int) $value) | ||||
|                                         ->where('user_group_id', $user->user_group_id) | ||||
|                                         ->first(); | ||||
|             if (null !== $rate) { | ||||
|                 return $rate; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         throw new NotFoundHttpException(); | ||||
|     } | ||||
| } | ||||
| @@ -30,7 +30,7 @@ class Domain | ||||
| { | ||||
|     public static function getBindables(): array | ||||
|     { | ||||
|         return config('firefly.bindables'); | ||||
|         return config('bindables.bindables'); | ||||
|     } | ||||
| 
 | ||||
|     public static function getRuleActions(): array | ||||
|   | ||||
| @@ -47,16 +47,15 @@ class Preferences | ||||
|         } | ||||
| 
 | ||||
|         return Preference::where('user_id', $user->id) | ||||
|             ->where('name', '!=', 'currencyPreference') | ||||
|             ->where(function (Builder $q) use ($user): void { | ||||
|                 $q->whereNull('user_group_id'); | ||||
|                 $q->orWhere('user_group_id', $user->user_group_id); | ||||
|             }) | ||||
|             ->get() | ||||
|         ; | ||||
|                          ->where('name', '!=', 'currencyPreference') | ||||
|                          ->where(function (Builder $q) use ($user): void { | ||||
|                              $q->whereNull('user_group_id'); | ||||
|                              $q->orWhere('user_group_id', $user->user_group_id); | ||||
|                          }) | ||||
|                          ->get(); | ||||
|     } | ||||
| 
 | ||||
|     public function get(string $name, null|array|bool|int|string $default = null): ?Preference | ||||
|     public function get(string $name, null | array | bool | int | string $default = null): ?Preference | ||||
|     { | ||||
|         /** @var null|User $user */ | ||||
|         $user = auth()->user(); | ||||
| @@ -70,7 +69,7 @@ class Preferences | ||||
|         return $this->getForUser($user, $name, $default); | ||||
|     } | ||||
| 
 | ||||
|     public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference | ||||
|     public function getForUser(User $user, string $name, null | array | bool | int | string $default = null): ?Preference | ||||
|     { | ||||
|         // don't care about user group ID, except for some specific preferences.
 | ||||
|         $userGroupId = $this->getUserGroupId($user, $name); | ||||
| @@ -122,14 +121,16 @@ class Preferences | ||||
|         Cache::put($key, '', 5); | ||||
|     } | ||||
| 
 | ||||
|     public function setForUser(User $user, string $name, null|array|bool|int|string $value): Preference | ||||
|     public function setForUser(User $user, string $name, null | array | bool | int | string $value): Preference | ||||
|     { | ||||
|         $fullName   = sprintf('preference%s%s', $user->id, $name); | ||||
|         $groupId    = $this->getUserGroupId($user, $name); | ||||
|         $fullName = sprintf('preference%s%s', $user->id, $name); | ||||
|         $groupId  = $this->getUserGroupId($user, $name); | ||||
|         $groupId  = 0 === (int)$groupId ? null : (int) $groupId; | ||||
| 
 | ||||
|         Cache::forget($fullName); | ||||
| 
 | ||||
|         /** @var null|Preference $pref */ | ||||
|         $pref       = Preference::where('user_group_id', $groupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); | ||||
|         $pref = Preference::where('user_group_id', $groupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); | ||||
| 
 | ||||
|         if (null !== $pref && null === $value) { | ||||
|             $pref->delete(); | ||||
| @@ -144,6 +145,7 @@ class Preferences | ||||
|             $pref->user_id       = (int) $user->id; | ||||
|             $pref->user_group_id = $groupId; | ||||
|             $pref->name          = $name; | ||||
| 
 | ||||
|         } | ||||
|         $pref->data = $value; | ||||
|         $pref->save(); | ||||
| @@ -171,13 +173,12 @@ class Preferences | ||||
|     { | ||||
|         $result      = []; | ||||
|         $preferences = Preference::where('user_id', $user->id) | ||||
|             ->where(function (Builder $q) use ($user): void { | ||||
|                 $q->whereNull('user_group_id'); | ||||
|                 $q->orWhere('user_group_id', $user->user_group_id); | ||||
|             }) | ||||
|             ->whereIn('name', $list) | ||||
|             ->get(['id', 'name', 'data']) | ||||
|         ; | ||||
|                                  ->where(function (Builder $q) use ($user): void { | ||||
|                                      $q->whereNull('user_group_id'); | ||||
|                                      $q->orWhere('user_group_id', $user->user_group_id); | ||||
|                                  }) | ||||
|                                  ->whereIn('name', $list) | ||||
|                                  ->get(['id', 'name', 'data']); | ||||
| 
 | ||||
|         /** @var Preference $preference */ | ||||
|         foreach ($preferences as $preference) { | ||||
| @@ -215,7 +216,7 @@ class Preferences | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     public function getEncryptedForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference | ||||
|     public function getEncryptedForUser(User $user, string $name, null | array | bool | int | string $default = null): ?Preference | ||||
|     { | ||||
|         $result = $this->getForUser($user, $name, $default); | ||||
|         if ('' === $result->data) { | ||||
| @@ -236,7 +237,7 @@ class Preferences | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     public function getFresh(string $name, null|array|bool|int|string $default = null): ?Preference | ||||
|     public function getFresh(string $name, null | array | bool | int | string $default = null): ?Preference | ||||
|     { | ||||
|         /** @var null|User $user */ | ||||
|         $user = auth()->user(); | ||||
| @@ -287,7 +288,7 @@ class Preferences | ||||
|         return $this->set($name, $encrypted); | ||||
|     } | ||||
| 
 | ||||
|     public function set(string $name, null|array|bool|int|string $value): Preference | ||||
|     public function set(string $name, null | array | bool | int | string $value): Preference | ||||
|     { | ||||
|         /** @var null|User $user */ | ||||
|         $user = auth()->user(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user