mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 18:54:58 +00:00 
			
		
		
		
	Clean up API code.
This commit is contained in:
		| @@ -42,6 +42,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class AccountController |  * Class AccountController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class AccountController extends Controller | class AccountController extends Controller | ||||||
| { | { | ||||||
| @@ -204,45 +205,16 @@ class AccountController extends Controller | |||||||
|     private function mapTypes(string $type): array |     private function mapTypes(string $type): array | ||||||
|     { |     { | ||||||
|         $types = [ |         $types = [ | ||||||
|             'all'                        => [ |             'all'                        => [AccountType::DEFAULT, AccountType::CASH, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE, | ||||||
|                 AccountType::DEFAULT, |                                              AccountType::INITIAL_BALANCE, AccountType::BENEFICIARY, AccountType::IMPORT, AccountType::RECONCILIATION, | ||||||
|                 AccountType::CASH, |                                              AccountType::LOAN,], | ||||||
|                 AccountType::ASSET, |             'asset'                      => [AccountType::DEFAULT, AccountType::ASSET,], | ||||||
|                 AccountType::EXPENSE, |             'cash'                       => [AccountType::CASH,], | ||||||
|                 AccountType::REVENUE, |             'expense'                    => [AccountType::EXPENSE, AccountType::BENEFICIARY,], | ||||||
|                 AccountType::INITIAL_BALANCE, |             'revenue'                    => [AccountType::REVENUE,], | ||||||
|                 AccountType::BENEFICIARY, |             'special'                    => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION, | ||||||
|                 AccountType::IMPORT, |                                              AccountType::LOAN,], | ||||||
|                 AccountType::RECONCILIATION, |             'hidden'                     => [AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION, AccountType::LOAN,], | ||||||
|                 AccountType::LOAN, |  | ||||||
|             ], |  | ||||||
|             'asset'                      => [ |  | ||||||
|                 AccountType::DEFAULT, |  | ||||||
|                 AccountType::ASSET, |  | ||||||
|             ], |  | ||||||
|             'cash'                       => [ |  | ||||||
|                 AccountType::CASH, |  | ||||||
|             ], |  | ||||||
|             'expense'                    => [ |  | ||||||
|                 AccountType::EXPENSE, |  | ||||||
|                 AccountType::BENEFICIARY, |  | ||||||
|             ], |  | ||||||
|             'revenue'                    => [ |  | ||||||
|                 AccountType::REVENUE, |  | ||||||
|             ], |  | ||||||
|             'special'                    => [ |  | ||||||
|                 AccountType::CASH, |  | ||||||
|                 AccountType::INITIAL_BALANCE, |  | ||||||
|                 AccountType::IMPORT, |  | ||||||
|                 AccountType::RECONCILIATION, |  | ||||||
|                 AccountType::LOAN, |  | ||||||
|             ], |  | ||||||
|             'hidden'                     => [ |  | ||||||
|                 AccountType::INITIAL_BALANCE, |  | ||||||
|                 AccountType::IMPORT, |  | ||||||
|                 AccountType::RECONCILIATION, |  | ||||||
|                 AccountType::LOAN, |  | ||||||
|             ], |  | ||||||
|             AccountType::DEFAULT         => [AccountType::DEFAULT], |             AccountType::DEFAULT         => [AccountType::DEFAULT], | ||||||
|             AccountType::CASH            => [AccountType::CASH], |             AccountType::CASH            => [AccountType::CASH], | ||||||
|             AccountType::ASSET           => [AccountType::ASSET], |             AccountType::ASSET           => [AccountType::ASSET], | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class AttachmentController |  * Class AttachmentController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class AttachmentController extends Controller | class AttachmentController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class AvailableBudgetController |  * Class AvailableBudgetController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class AvailableBudgetController extends Controller | class AvailableBudgetController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class BudgetController |  * Class BudgetController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class BudgetController extends Controller | class BudgetController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -34,21 +34,18 @@ use Illuminate\Http\JsonResponse; | |||||||
| use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||||
| use Illuminate\Pagination\LengthAwarePaginator; | use Illuminate\Pagination\LengthAwarePaginator; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use InvalidArgumentException; |  | ||||||
| use League\Fractal\Manager; | use League\Fractal\Manager; | ||||||
| use League\Fractal\Pagination\IlluminatePaginatorAdapter; | use League\Fractal\Pagination\IlluminatePaginatorAdapter; | ||||||
| use League\Fractal\Resource\Collection as FractalCollection; | use League\Fractal\Resource\Collection as FractalCollection; | ||||||
| use League\Fractal\Resource\Item; | use League\Fractal\Resource\Item; | ||||||
| use League\Fractal\Serializer\JsonApiSerializer; | use League\Fractal\Serializer\JsonApiSerializer; | ||||||
| use Log; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class BudgetLimitController |  * Class BudgetLimitController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class BudgetLimitController extends Controller | class BudgetLimitController extends Controller | ||||||
| { | { | ||||||
|     ///** @var CurrencyRepositoryInterface */ |  | ||||||
|     //private $currencyRepository; |  | ||||||
|     /** @var BudgetRepositoryInterface */ |     /** @var BudgetRepositoryInterface */ | ||||||
|     private $repository; |     private $repository; | ||||||
|  |  | ||||||
| @@ -63,7 +60,6 @@ class BudgetLimitController extends Controller | |||||||
|                 /** @var User $user */ |                 /** @var User $user */ | ||||||
|                 $user             = auth()->user(); |                 $user             = auth()->user(); | ||||||
|                 $this->repository = app(BudgetRepositoryInterface::class); |                 $this->repository = app(BudgetRepositoryInterface::class); | ||||||
|                 //$this->currencyRepository = app(CurrencyRepositoryInterface::class); |  | ||||||
|                 $this->repository->setUser($user); |                 $this->repository->setUser($user); | ||||||
|  |  | ||||||
|                 return $next($request); |                 return $next($request); | ||||||
| @@ -94,40 +90,21 @@ class BudgetLimitController extends Controller | |||||||
|      */ |      */ | ||||||
|     public function index(Request $request): JsonResponse |     public function index(Request $request): JsonResponse | ||||||
|     { |     { | ||||||
|         // create some objects: |  | ||||||
|         $manager = new Manager; |         $manager = new Manager; | ||||||
|         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; |         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; | ||||||
|  |  | ||||||
|         // read budget from request |  | ||||||
|         $budgetId = (int)($request->get('budget_id') ?? 0); |         $budgetId = (int)($request->get('budget_id') ?? 0); | ||||||
|         $budget   = null; |         $budget   = $this->repository->findNull($budgetId); | ||||||
|         if ($budgetId > 0) { |  | ||||||
|             $budget = $this->repository->findNull($budgetId); |  | ||||||
|         } |  | ||||||
|         // read start date from request |  | ||||||
|         $start = null; |  | ||||||
|         try { |  | ||||||
|             $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); |  | ||||||
|             $this->parameters->set('start', $start->format('Y-m-d')); |  | ||||||
|         } catch (InvalidArgumentException $e) { |  | ||||||
|             Log::debug(sprintf('Could not parse start date "%s": %s', $request->get('start'), $e->getMessage())); |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // read end date from request |  | ||||||
|         $end = null; |  | ||||||
|         try { |  | ||||||
|             $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); |  | ||||||
|             $this->parameters->set('end', $end->format('Y-m-d')); |  | ||||||
|         } catch (InvalidArgumentException $e) { |  | ||||||
|             Log::debug(sprintf('Could not parse end date "%s": %s', $request->get('end'), $e->getMessage())); |  | ||||||
|         } |  | ||||||
|         $this->parameters->set('budget_id', $budgetId); |         $this->parameters->set('budget_id', $budgetId); | ||||||
|  |  | ||||||
|         // types to get, page size: |         $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); | ||||||
|  |         $this->parameters->set('start', $start->format('Y-m-d')); | ||||||
|  |  | ||||||
|  |         $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); | ||||||
|  |         $this->parameters->set('end', $end->format('Y-m-d')); | ||||||
|  |  | ||||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; |         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||||
|  |  | ||||||
|         // get list of budget limits. Count it and split it. |  | ||||||
|         $collection = new Collection; |         $collection = new Collection; | ||||||
|         if (null === $budget) { |         if (null === $budget) { | ||||||
|             $collection = $this->repository->getAllBudgetLimits($start, $end); |             $collection = $this->repository->getAllBudgetLimits($start, $end); | ||||||
| @@ -138,12 +115,9 @@ class BudgetLimitController extends Controller | |||||||
|  |  | ||||||
|         $count        = $collection->count(); |         $count        = $collection->count(); | ||||||
|         $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); |         $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||||
|  |  | ||||||
|         // make paginator: |  | ||||||
|         $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); |         $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); | ||||||
|         $paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams()); |         $paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams()); | ||||||
|  |  | ||||||
|         // present to user. |  | ||||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); |         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||||
|         $resource = new FractalCollection($budgetLimits, new BudgetLimitTransformer($this->parameters), 'budget_limits'); |         $resource = new FractalCollection($budgetLimits, new BudgetLimitTransformer($this->parameters), 'budget_limits'); | ||||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); |         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class CategoryController |  * Class CategoryController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class CategoryController extends Controller | class CategoryController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -49,6 +49,12 @@ class ConfigurationController extends Controller | |||||||
|         $this->middleware( |         $this->middleware( | ||||||
|             function ($request, $next) { |             function ($request, $next) { | ||||||
|                 $this->repository = app(UserRepositoryInterface::class); |                 $this->repository = app(UserRepositoryInterface::class); | ||||||
|  |                 /** @var User $admin */ | ||||||
|  |                 $admin = auth()->user(); | ||||||
|  |  | ||||||
|  |                 if (!$this->repository->hasRole($admin, 'owner')) { | ||||||
|  |                     throw new FireflyException('No access to method.'); // @codeCoverageIgnore | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 return $next($request); |                 return $next($request); | ||||||
|             } |             } | ||||||
| @@ -60,11 +66,6 @@ class ConfigurationController extends Controller | |||||||
|      */ |      */ | ||||||
|     public function index() |     public function index() | ||||||
|     { |     { | ||||||
|         /** @var User $admin */ |  | ||||||
|         $admin = auth()->user(); |  | ||||||
|         if (!$this->repository->hasRole($admin, 'owner')) { |  | ||||||
|             throw new FireflyException('No access to method.'); // @codeCoverageIgnore |  | ||||||
|         } |  | ||||||
|         $configData = $this->getConfigData(); |         $configData = $this->getConfigData(); | ||||||
|  |  | ||||||
|         return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json'); |         return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json'); | ||||||
| @@ -75,14 +76,10 @@ class ConfigurationController extends Controller | |||||||
|      * |      * | ||||||
|      * @return JsonResponse |      * @return JsonResponse | ||||||
|      * @throws FireflyException |      * @throws FireflyException | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     public function update(Request $request): JsonResponse |     public function update(Request $request): JsonResponse | ||||||
|     { |     { | ||||||
|         /** @var User $admin */ |  | ||||||
|         $admin = auth()->user(); |  | ||||||
|         if (!$this->repository->hasRole($admin, 'owner')) { |  | ||||||
|             throw new FireflyException('No access to method.'); // @codeCoverageIgnore |  | ||||||
|         } |  | ||||||
|         $name  = $request->get('name'); |         $name  = $request->get('name'); | ||||||
|         $value = $request->get('value'); |         $value = $request->get('value'); | ||||||
|         $valid = ['is_demo_site', 'permission_update_check', 'single_user_mode']; |         $valid = ['is_demo_site', 'permission_update_check', 'single_user_mode']; | ||||||
| @@ -107,6 +104,7 @@ class ConfigurationController extends Controller | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return array |      * @return array | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     private function getConfigData(): array |     private function getConfigData(): array | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ use Symfony\Component\HttpFoundation\ParameterBag; | |||||||
|  * Class Controller. |  * Class Controller. | ||||||
|  * |  * | ||||||
|  * @codeCoverageIgnore |  * @codeCoverageIgnore | ||||||
|  |  * @SuppressWarnings(PHPMD.NumberOfChildren) | ||||||
|  */ |  */ | ||||||
| class Controller extends BaseController | class Controller extends BaseController | ||||||
| { | { | ||||||
| @@ -57,6 +58,8 @@ class Controller extends BaseController | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return string |      * @return string | ||||||
|  |      * | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     protected function buildParams(): string |     protected function buildParams(): string | ||||||
|     { |     { | ||||||
| @@ -68,21 +71,18 @@ class Controller extends BaseController | |||||||
|             } |             } | ||||||
|             if ($value instanceof Carbon) { |             if ($value instanceof Carbon) { | ||||||
|                 $params[$key] = $value->format('Y-m-d'); |                 $params[$key] = $value->format('Y-m-d'); | ||||||
|  |                 continue; | ||||||
|             } |             } | ||||||
|             if (!$value instanceof Carbon) { |             $params[$key] = $value; | ||||||
|                 $params[$key] = $value; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         $return .= http_build_query($params); |         $return .= http_build_query($params); | ||||||
|         if (\strlen($return) === 1) { |  | ||||||
|             return ''; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $return; |         return $return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return ParameterBag |      * @return ParameterBag | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     private function getParameters(): ParameterBag |     private function getParameters(): ParameterBag | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class CurrencyController |  * Class CurrencyController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class CurrencyController extends Controller | class CurrencyController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -31,11 +31,9 @@ use FireflyIII\Transformers\CurrencyExchangeRateTransformer; | |||||||
| use FireflyIII\User; | use FireflyIII\User; | ||||||
| use Illuminate\Http\JsonResponse; | use Illuminate\Http\JsonResponse; | ||||||
| use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||||
| use InvalidArgumentException; |  | ||||||
| use League\Fractal\Manager; | use League\Fractal\Manager; | ||||||
| use League\Fractal\Resource\Item; | use League\Fractal\Resource\Item; | ||||||
| use League\Fractal\Serializer\JsonApiSerializer; | use League\Fractal\Serializer\JsonApiSerializer; | ||||||
| use Log; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * |  * | ||||||
| @@ -79,7 +77,6 @@ class CurrencyExchangeRateController extends Controller | |||||||
|         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; |         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; | ||||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); |         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||||
|  |  | ||||||
|         // currencies |  | ||||||
|         $fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR'); |         $fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR'); | ||||||
|         $toCurrency   = $this->repository->findByCodeNull($request->get('to') ?? 'USD'); |         $toCurrency   = $this->repository->findByCodeNull($request->get('to') ?? 'USD'); | ||||||
|  |  | ||||||
| @@ -90,19 +87,11 @@ class CurrencyExchangeRateController extends Controller | |||||||
|             throw new FireflyException('Unknown destination currency.'); |             throw new FireflyException('Unknown destination currency.'); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $dateObj = new Carbon; |         $dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d')); | ||||||
|         try { |  | ||||||
|             $dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d')); |  | ||||||
|         } catch (InvalidArgumentException $e) { |  | ||||||
|             Log::debug($e->getMessage()); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         $this->parameters->set('from', $fromCurrency->code); |         $this->parameters->set('from', $fromCurrency->code); | ||||||
|         $this->parameters->set('to', $toCurrency->code); |         $this->parameters->set('to', $toCurrency->code); | ||||||
|         $this->parameters->set('date', $dateObj->format('Y-m-d')); |         $this->parameters->set('date', $dateObj->format('Y-m-d')); | ||||||
|  |  | ||||||
|         // get the exchange rate. |  | ||||||
|         $rate = $this->repository->getExchangeRate($fromCurrency, $toCurrency, $dateObj); |         $rate = $this->repository->getExchangeRate($fromCurrency, $toCurrency, $dateObj); | ||||||
|         if (null === $rate) { |         if (null === $rate) { | ||||||
|             /** @var User $admin */ |             /** @var User $admin */ | ||||||
| @@ -111,8 +100,6 @@ class CurrencyExchangeRateController extends Controller | |||||||
|             /** @var ExchangeRateInterface $service */ |             /** @var ExchangeRateInterface $service */ | ||||||
|             $service = app(ExchangeRateInterface::class); |             $service = app(ExchangeRateInterface::class); | ||||||
|             $service->setUser($admin); |             $service->setUser($admin); | ||||||
|  |  | ||||||
|             // get rate: |  | ||||||
|             $rate = $service->getRate($fromCurrency, $toCurrency, $dateObj); |             $rate = $service->getRate($fromCurrency, $toCurrency, $dateObj); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,6 +39,11 @@ use League\Fractal\Resource\Collection as FractalCollection; | |||||||
| use League\Fractal\Resource\Item; | use League\Fractal\Resource\Item; | ||||||
| use League\Fractal\Serializer\JsonApiSerializer; | use League\Fractal\Serializer\JsonApiSerializer; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * Class JournalLinkController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  |  */ | ||||||
| class JournalLinkController extends Controller | class JournalLinkController extends Controller | ||||||
| { | { | ||||||
|     /** @var JournalRepositoryInterface */ |     /** @var JournalRepositoryInterface */ | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
| /** | /** | ||||||
|  * |  * | ||||||
|  * Class LinkTypeController |  * Class LinkTypeController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class LinkTypeController extends Controller | class LinkTypeController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
| /** | /** | ||||||
|  * TODO order up and down. |  * TODO order up and down. | ||||||
|  * Class PiggyBankController |  * Class PiggyBankController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class PiggyBankController extends Controller | class PiggyBankController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -106,6 +106,7 @@ class PreferenceController extends Controller | |||||||
|      * @param Preference        $preference |      * @param Preference        $preference | ||||||
|      * |      * | ||||||
|      * @return JsonResponse |      * @return JsonResponse | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     public function update(PreferenceRequest $request, Preference $preference): JsonResponse |     public function update(PreferenceRequest $request, Preference $preference): JsonResponse | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -163,9 +163,6 @@ class RecurrenceController extends Controller | |||||||
|     public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse |     public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse | ||||||
|     { |     { | ||||||
|         $data = $request->getAll(); |         $data = $request->getAll(); | ||||||
|  |  | ||||||
|         // |  | ||||||
|  |  | ||||||
|         $category = $this->repository->update($recurrence, $data); |         $category = $this->repository->update($recurrence, $data); | ||||||
|         $manager  = new Manager(); |         $manager  = new Manager(); | ||||||
|         $baseUrl  = $request->getSchemeAndHttpHost() . '/api/v1'; |         $baseUrl  = $request->getSchemeAndHttpHost() . '/api/v1'; | ||||||
|   | |||||||
| @@ -42,10 +42,10 @@ use League\Fractal\Manager; | |||||||
| use League\Fractal\Pagination\IlluminatePaginatorAdapter; | use League\Fractal\Pagination\IlluminatePaginatorAdapter; | ||||||
| use League\Fractal\Resource\Collection as FractalCollection; | use League\Fractal\Resource\Collection as FractalCollection; | ||||||
| use League\Fractal\Serializer\JsonApiSerializer; | use League\Fractal\Serializer\JsonApiSerializer; | ||||||
| use Log; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class TransactionController |  * Class TransactionController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class TransactionController extends Controller | class TransactionController extends Controller | ||||||
| { | { | ||||||
| @@ -96,19 +96,14 @@ class TransactionController extends Controller | |||||||
|     public function index(Request $request): JsonResponse |     public function index(Request $request): JsonResponse | ||||||
|     { |     { | ||||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; |         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||||
|  |         $type     = $request->get('type') ?? 'default'; | ||||||
|         // read type from URI |  | ||||||
|         $type = $request->get('type') ?? 'default'; |  | ||||||
|         $this->parameters->set('type', $type); |         $this->parameters->set('type', $type); | ||||||
|  |  | ||||||
|         // types to get, page size: |         $types   = $this->mapTypes($this->parameters->get('type')); | ||||||
|         $types = $this->mapTypes($this->parameters->get('type')); |  | ||||||
|  |  | ||||||
|         $manager = new Manager(); |         $manager = new Manager(); | ||||||
|         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; |         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; | ||||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); |         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||||
|  |  | ||||||
|         // collect transactions using the journal collector |  | ||||||
|         /** @var User $admin */ |         /** @var User $admin */ | ||||||
|         $admin = auth()->user(); |         $admin = auth()->user(); | ||||||
|         /** @var JournalCollectorInterface $collector */ |         /** @var JournalCollectorInterface $collector */ | ||||||
| @@ -117,7 +112,6 @@ class TransactionController extends Controller | |||||||
|         $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); |         $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); | ||||||
|         $collector->setAllAssetAccounts(); |         $collector->setAllAssetAccounts(); | ||||||
|  |  | ||||||
|         // remove internal transfer filter: |  | ||||||
|         if (\in_array(TransactionType::TRANSFER, $types, true)) { |         if (\in_array(TransactionType::TRANSFER, $types, true)) { | ||||||
|             $collector->removeFilter(InternalTransferFilter::class); |             $collector->removeFilter(InternalTransferFilter::class); | ||||||
|         } |         } | ||||||
| @@ -131,7 +125,6 @@ class TransactionController extends Controller | |||||||
|         $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); |         $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); | ||||||
|         $transactions = $paginator->getCollection(); |         $transactions = $paginator->getCollection(); | ||||||
|  |  | ||||||
|  |  | ||||||
|         $resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions'); |         $resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions'); | ||||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); |         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||||
|  |  | ||||||
| @@ -235,13 +228,9 @@ class TransactionController extends Controller | |||||||
|     { |     { | ||||||
|         $data         = $request->getAll(); |         $data         = $request->getAll(); | ||||||
|         $data['user'] = auth()->user()->id; |         $data['user'] = auth()->user()->id; | ||||||
|  |         $journal      = $repository->update($transaction->transactionJournal, $data); | ||||||
|         Log::debug('Inside transaction update'); |         $manager      = new Manager(); | ||||||
|  |         $baseUrl      = $request->getSchemeAndHttpHost() . '/api/v1'; | ||||||
|         $journal = $repository->update($transaction->transactionJournal, $data); |  | ||||||
|  |  | ||||||
|         $manager = new Manager(); |  | ||||||
|         $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; |  | ||||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); |         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||||
|  |  | ||||||
|         // add include parameter: |         // add include parameter: | ||||||
| @@ -280,59 +269,22 @@ class TransactionController extends Controller | |||||||
|     private function mapTypes(string $type): array |     private function mapTypes(string $type): array | ||||||
|     { |     { | ||||||
|         $types = [ |         $types = [ | ||||||
|             'all'             => [ |             'all'             => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE, | ||||||
|                 TransactionType::WITHDRAWAL, |                                   TransactionType::RECONCILIATION,], | ||||||
|                 TransactionType::DEPOSIT, |             'withdrawal'      => [TransactionType::WITHDRAWAL,], | ||||||
|                 TransactionType::TRANSFER, |             'withdrawals'     => [TransactionType::WITHDRAWAL,], | ||||||
|                 TransactionType::OPENING_BALANCE, |             'expense'         => [TransactionType::WITHDRAWAL,], | ||||||
|                 TransactionType::RECONCILIATION, |             'income'          => [TransactionType::DEPOSIT,], | ||||||
|             ], |             'deposit'         => [TransactionType::DEPOSIT,], | ||||||
|             'withdrawal'      => [ |             'deposits'        => [TransactionType::DEPOSIT,], | ||||||
|                 TransactionType::WITHDRAWAL, |             'transfer'        => [TransactionType::TRANSFER,], | ||||||
|             ], |             'transfers'       => [TransactionType::TRANSFER,], | ||||||
|             'withdrawals'     => [ |             'opening_balance' => [TransactionType::OPENING_BALANCE,], | ||||||
|                 TransactionType::WITHDRAWAL, |             'reconciliation'  => [TransactionType::RECONCILIATION,], | ||||||
|             ], |             'reconciliations' => [TransactionType::RECONCILIATION,], | ||||||
|             'expense'         => [ |             'special'         => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,], | ||||||
|                 TransactionType::WITHDRAWAL, |             'specials'        => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,], | ||||||
|             ], |             'default'         => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER,], | ||||||
|             'income'          => [ |  | ||||||
|                 TransactionType::DEPOSIT, |  | ||||||
|             ], |  | ||||||
|             'deposit'         => [ |  | ||||||
|                 TransactionType::DEPOSIT, |  | ||||||
|             ], |  | ||||||
|             'deposits'        => [ |  | ||||||
|                 TransactionType::DEPOSIT, |  | ||||||
|             ], |  | ||||||
|             'transfer'        => [ |  | ||||||
|                 TransactionType::TRANSFER, |  | ||||||
|             ], |  | ||||||
|             'transfers'       => [ |  | ||||||
|                 TransactionType::TRANSFER, |  | ||||||
|             ], |  | ||||||
|             'opening_balance' => [ |  | ||||||
|                 TransactionType::OPENING_BALANCE, |  | ||||||
|             ], |  | ||||||
|             'reconciliation'  => [ |  | ||||||
|                 TransactionType::RECONCILIATION, |  | ||||||
|             ], |  | ||||||
|             'reconciliations' => [ |  | ||||||
|                 TransactionType::RECONCILIATION, |  | ||||||
|             ], |  | ||||||
|             'special'         => [ |  | ||||||
|                 TransactionType::OPENING_BALANCE, |  | ||||||
|                 TransactionType::RECONCILIATION, |  | ||||||
|             ], |  | ||||||
|             'specials'        => [ |  | ||||||
|                 TransactionType::OPENING_BALANCE, |  | ||||||
|                 TransactionType::RECONCILIATION, |  | ||||||
|             ], |  | ||||||
|             'default'         => [ |  | ||||||
|                 TransactionType::WITHDRAWAL, |  | ||||||
|                 TransactionType::DEPOSIT, |  | ||||||
|                 TransactionType::TRANSFER, |  | ||||||
|             ], |  | ||||||
|         ]; |         ]; | ||||||
|         if (isset($types[$type])) { |         if (isset($types[$type])) { | ||||||
|             return $types[$type]; |             return $types[$type]; | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ use League\Fractal\Serializer\JsonApiSerializer; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class UserController |  * Class UserController | ||||||
|  |  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||||
|  */ |  */ | ||||||
| class UserController extends Controller | class UserController extends Controller | ||||||
| { | { | ||||||
|   | |||||||
| @@ -24,20 +24,18 @@ declare(strict_types=1); | |||||||
| namespace FireflyIII\Api\V1\Requests; | namespace FireflyIII\Api\V1\Requests; | ||||||
|  |  | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
| use FireflyIII\Models\Account; |  | ||||||
| use FireflyIII\Models\AccountType; |  | ||||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; |  | ||||||
| use FireflyIII\Rules\BelongsUser; | use FireflyIII\Rules\BelongsUser; | ||||||
| use FireflyIII\User; | use FireflyIII\Validation\RecurrenceValidation; | ||||||
|  | use FireflyIII\Validation\TransactionValidation; | ||||||
| use Illuminate\Validation\Validator; | use Illuminate\Validation\Validator; | ||||||
| use InvalidArgumentException; |  | ||||||
| use Log; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class RecurrenceRequest |  * Class RecurrenceRequest | ||||||
|  */ |  */ | ||||||
| class RecurrenceRequest extends Request | class RecurrenceRequest extends Request | ||||||
| { | { | ||||||
|  |     use RecurrenceValidation, TransactionValidation; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return bool |      * @return bool | ||||||
|      */ |      */ | ||||||
| @@ -68,51 +66,10 @@ class RecurrenceRequest extends Request | |||||||
|                 'piggy_bank_name' => $this->string('piggy_bank_name'), |                 'piggy_bank_name' => $this->string('piggy_bank_name'), | ||||||
|                 'tags'            => explode(',', $this->string('tags')), |                 'tags'            => explode(',', $this->string('tags')), | ||||||
|             ], |             ], | ||||||
|             'transactions' => [], |             'transactions' => $this->getTransactionData(), | ||||||
|             'repetitions'  => [], |             'repetitions'  => $this->getRepetitionData(), | ||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         // repetition data: |  | ||||||
|         /** @var array $repetitions */ |  | ||||||
|         $repetitions = $this->get('repetitions'); |  | ||||||
|         /** @var array $repetition */ |  | ||||||
|         foreach ($repetitions as $repetition) { |  | ||||||
|             $return['repetitions'][] = [ |  | ||||||
|                 'type'    => $repetition['type'], |  | ||||||
|                 'moment'  => $repetition['moment'], |  | ||||||
|                 'skip'    => (int)$repetition['skip'], |  | ||||||
|                 'weekend' => (int)$repetition['weekend'], |  | ||||||
|             ]; |  | ||||||
|         } |  | ||||||
|         // transaction data: |  | ||||||
|         /** @var array $transactions */ |  | ||||||
|         $transactions = $this->get('transactions'); |  | ||||||
|         /** @var array $transaction */ |  | ||||||
|         foreach ($transactions as $transaction) { |  | ||||||
|             $return['transactions'][] = [ |  | ||||||
|                 'amount' => $transaction['amount'], |  | ||||||
|  |  | ||||||
|                 'currency_id'   => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, |  | ||||||
|                 'currency_code' => $transaction['currency_code'] ?? null, |  | ||||||
|  |  | ||||||
|                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, |  | ||||||
|                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, |  | ||||||
|                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, |  | ||||||
|  |  | ||||||
|                 'budget_id'     => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, |  | ||||||
|                 'budget_name'   => $transaction['budget_name'] ?? null, |  | ||||||
|                 'category_id'   => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, |  | ||||||
|                 'category_name' => $transaction['category_name'] ?? null, |  | ||||||
|  |  | ||||||
|                 'source_id'        => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, |  | ||||||
|                 'source_name'      => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, |  | ||||||
|                 'destination_id'   => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, |  | ||||||
|                 'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, |  | ||||||
|  |  | ||||||
|                 'description' => $transaction['description'], |  | ||||||
|             ]; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $return; |         return $return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -133,18 +90,12 @@ class RecurrenceRequest extends Request | |||||||
|             'nr_of_repetitions'                    => 'numeric|between:1,31', |             'nr_of_repetitions'                    => 'numeric|between:1,31', | ||||||
|             'apply_rules'                          => 'required|boolean', |             'apply_rules'                          => 'required|boolean', | ||||||
|             'active'                               => 'required|boolean', |             'active'                               => 'required|boolean', | ||||||
|  |  | ||||||
|             // rules for meta values: |  | ||||||
|             'tags'                                 => 'between:1,64000', |             'tags'                                 => 'between:1,64000', | ||||||
|             'piggy_bank_id'                        => 'numeric', |             'piggy_bank_id'                        => 'numeric', | ||||||
|  |  | ||||||
|             // rules for repetitions. |  | ||||||
|             'repetitions.*.type'                   => 'required|in:daily,weekly,ndom,monthly,yearly', |             'repetitions.*.type'                   => 'required|in:daily,weekly,ndom,monthly,yearly', | ||||||
|             'repetitions.*.moment'                 => 'between:0,10', |             'repetitions.*.moment'                 => 'between:0,10', | ||||||
|             'repetitions.*.skip'                   => 'required|numeric|between:0,31', |             'repetitions.*.skip'                   => 'required|numeric|between:0,31', | ||||||
|             'repetitions.*.weekend'                => 'required|numeric|min:1|max:4', |             'repetitions.*.weekend'                => 'required|numeric|min:1|max:4', | ||||||
|  |  | ||||||
|             // rules for transactions. |  | ||||||
|             'transactions.*.currency_id'           => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code', |             'transactions.*.currency_id'           => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code', | ||||||
|             'transactions.*.currency_code'         => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id', |             'transactions.*.currency_code'         => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id', | ||||||
|             'transactions.*.foreign_amount'        => 'numeric|more:0', |             'transactions.*.foreign_amount'        => 'numeric|more:0', | ||||||
| @@ -172,316 +123,76 @@ class RecurrenceRequest extends Request | |||||||
|     { |     { | ||||||
|         $validator->after( |         $validator->after( | ||||||
|             function (Validator $validator) { |             function (Validator $validator) { | ||||||
|                 $this->atLeastOneTransaction($validator); |                 $this->validateOneTransaction($validator); | ||||||
|                 $this->atLeastOneRepetition($validator); |                 $this->validateOneRepetition($validator); | ||||||
|                 $this->validRepeatsUntil($validator); |                 $this->validateRecurrenceRepetition($validator); | ||||||
|                 $this->validRepetitionMoment($validator); |                 $this->validateRepetitionMoment($validator); | ||||||
|                 $this->foreignCurrencyInformation($validator); |                 $this->validateForeignCurrencyInformation($validator); | ||||||
|                 $this->validateAccountInformation($validator); |                 $this->validateAccountInformation($validator); | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Throws an error when this asset account is invalid. |      * Returns the repetition data as it is found in the submitted data. | ||||||
|      * |      * | ||||||
|      * @noinspection MoreThanThreeArgumentsInspection |      * @return array | ||||||
|      * |  | ||||||
|      * @param Validator   $validator |  | ||||||
|      * @param int|null    $accountId |  | ||||||
|      * @param null|string $accountName |  | ||||||
|      * @param string      $idField |  | ||||||
|      * @param string      $nameField |  | ||||||
|      * |  | ||||||
|      * @return null|Account |  | ||||||
|      */ |      */ | ||||||
|     protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account |     private function getRepetitionData(): array | ||||||
|     { |     { | ||||||
|         /** @var User $admin */ |         $return = []; | ||||||
|         $admin       = auth()->user(); |         // repetition data: | ||||||
|         $accountId   = (int)$accountId; |         /** @var array $repetitions */ | ||||||
|         $accountName = (string)$accountName; |         $repetitions = $this->get('repetitions'); | ||||||
|         // both empty? hard exit. |         /** @var array $repetition */ | ||||||
|         if ($accountId < 1 && '' === $accountName) { |         foreach ($repetitions as $repetition) { | ||||||
|             $validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); |             $return[] = [ | ||||||
|  |                 'type'    => $repetition['type'], | ||||||
|             return null; |                 'moment'  => $repetition['moment'], | ||||||
|         } |                 'skip'    => (int)$repetition['skip'], | ||||||
|         // ID belongs to user and is asset account: |                 'weekend' => (int)$repetition['weekend'], | ||||||
|         /** @var AccountRepositoryInterface $repository */ |             ]; | ||||||
|         $repository = app(AccountRepositoryInterface::class); |  | ||||||
|         $repository->setUser($admin); |  | ||||||
|         $set = $repository->getAccountsById([$accountId]); |  | ||||||
|         Log::debug(sprintf('Count of accounts found by ID %d is: %d', $accountId, $set->count())); |  | ||||||
|         if ($set->count() === 1) { |  | ||||||
|             /** @var Account $first */ |  | ||||||
|             $first = $set->first(); |  | ||||||
|             if ($first->accountType->type !== AccountType::ASSET) { |  | ||||||
|                 $validator->errors()->add($idField, trans('validation.belongs_user')); |  | ||||||
|  |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // we ignore the account name at this point. |  | ||||||
|             return $first; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $account = $repository->findByName($accountName, [AccountType::ASSET]); |         return $return; | ||||||
|         if (null === $account) { |  | ||||||
|             $validator->errors()->add($nameField, trans('validation.belongs_user')); |  | ||||||
|  |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $account; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Adds an error to the validator when there are no repetitions in the array of data. |      * Returns the transaction data as it is found in the submitted data. It's a complex method according to code | ||||||
|  |      * standards but it just has a lot of ??-statements because of the fields that may or may not exist. | ||||||
|      * |      * | ||||||
|      * @param Validator $validator |      * @return array | ||||||
|  |      * @SuppressWarnings(PHPMD.NPathComplexity) | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      */ |      */ | ||||||
|     protected function atLeastOneRepetition(Validator $validator): void |     private function getTransactionData(): array | ||||||
|     { |     { | ||||||
|         $data        = $validator->getData(); |         $return = []; | ||||||
|         $repetitions = $data['repetitions'] ?? []; |         // transaction data: | ||||||
|         // need at least one transaction |         /** @var array $transactions */ | ||||||
|         if (\count($repetitions) === 0) { |         $transactions = $this->get('transactions'); | ||||||
|             $validator->errors()->add('description', trans('validation.at_least_one_repetition')); |         /** @var array $transaction */ | ||||||
|         } |         foreach ($transactions as $transaction) { | ||||||
|     } |             $return[] = [ | ||||||
|  |                 'amount'                => $transaction['amount'], | ||||||
|     /** |                 'currency_id'           => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, | ||||||
|      * Adds an error to the validator when there are no transactions in the array of data. |                 'currency_code'         => $transaction['currency_code'] ?? null, | ||||||
|      * |                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, | ||||||
|      * @param Validator $validator |                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, | ||||||
|      */ |                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, | ||||||
|     protected function atLeastOneTransaction(Validator $validator): void |                 'budget_id'             => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, | ||||||
|     { |                 'budget_name'           => $transaction['budget_name'] ?? null, | ||||||
|         $data         = $validator->getData(); |                 'category_id'           => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, | ||||||
|         $transactions = $data['transactions'] ?? []; |                 'category_name'         => $transaction['category_name'] ?? null, | ||||||
|         // need at least one transaction |                 'source_id'             => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, | ||||||
|         if (\count($transactions) === 0) { |                 'source_name'           => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, | ||||||
|             $validator->errors()->add('description', trans('validation.at_least_one_transaction')); |                 'destination_id'        => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, | ||||||
|         } |                 'destination_name'      => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, | ||||||
|     } |                 'description'           => $transaction['description'], | ||||||
|  |             ]; | ||||||
|     /** |  | ||||||
|      * TODO can be made a rule? |  | ||||||
|      * If the transactions contain foreign amounts, there must also be foreign currency information. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function foreignCurrencyInformation(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data         = $validator->getData(); |  | ||||||
|         $transactions = $data['transactions'] ?? []; |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             // must have currency info. |  | ||||||
|             if (isset($transaction['foreign_amount']) |  | ||||||
|                 && !(isset($transaction['foreign_currency_id']) |  | ||||||
|                      || isset($transaction['foreign_currency_code']))) { |  | ||||||
|                 $validator->errors()->add( |  | ||||||
|                     'transactions.' . $index . '.foreign_amount', |  | ||||||
|                     trans('validation.require_currency_info') |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Throws an error when the given opposing account (of type $type) is invalid. |  | ||||||
|      * Empty data is allowed, system will default to cash. |  | ||||||
|      * |  | ||||||
|      * @noinspection MoreThanThreeArgumentsInspection |  | ||||||
|      * |  | ||||||
|      * @param Validator   $validator |  | ||||||
|      * @param string      $type |  | ||||||
|      * @param int|null    $accountId |  | ||||||
|      * @param null|string $accountName |  | ||||||
|      * @param string      $idField |  | ||||||
|      * |  | ||||||
|      * @return null|Account |  | ||||||
|      */ |  | ||||||
|     protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account |  | ||||||
|     { |  | ||||||
|         /** @var User $admin */ |  | ||||||
|         $admin       = auth()->user(); |  | ||||||
|         $accountId   = (int)$accountId; |  | ||||||
|         $accountName = (string)$accountName; |  | ||||||
|         // both empty? done! |  | ||||||
|         if ($accountId < 1 && '' === $accountName) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|         if ($accountId !== 0) { |  | ||||||
|             // ID belongs to user and is $type account: |  | ||||||
|             /** @var AccountRepositoryInterface $repository */ |  | ||||||
|             $repository = app(AccountRepositoryInterface::class); |  | ||||||
|             $repository->setUser($admin); |  | ||||||
|             $set = $repository->getAccountsById([$accountId]); |  | ||||||
|             if ($set->count() === 1) { |  | ||||||
|                 /** @var Account $first */ |  | ||||||
|                 $first = $set->first(); |  | ||||||
|                 if ($first->accountType->type !== $type) { |  | ||||||
|                     $validator->errors()->add($idField, trans('validation.belongs_user')); |  | ||||||
|  |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // we ignore the account name at this point. |  | ||||||
|                 return $first; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // not having an opposing account by this name is NOT a problem. |         return $return; | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * TODO can be a rule? |  | ||||||
|      * |  | ||||||
|      * Validates the given account information. Switches on given transaction type. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function validateAccountInformation(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data            = $validator->getData(); |  | ||||||
|         $transactions    = $data['transactions'] ?? []; |  | ||||||
|         $idField         = 'description'; |  | ||||||
|         $transactionType = $data['type'] ?? 'false'; |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             $sourceId           = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null; |  | ||||||
|             $sourceName         = $transaction['source_name'] ?? null; |  | ||||||
|             $destinationId      = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null; |  | ||||||
|             $destinationName    = $transaction['destination_name'] ?? null; |  | ||||||
|             $sourceAccount      = null; |  | ||||||
|             $destinationAccount = null; |  | ||||||
|             switch ($transactionType) { |  | ||||||
|                 case 'withdrawal': |  | ||||||
|                     $idField            = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.source_name'; |  | ||||||
|                     $sourceAccount      = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField); |  | ||||||
|                     break; |  | ||||||
|                 case 'deposit': |  | ||||||
|                     $idField       = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField); |  | ||||||
|  |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.destination_name'; |  | ||||||
|                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); |  | ||||||
|                     break; |  | ||||||
|                 case 'transfer': |  | ||||||
|                     $idField       = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $nameField     = 'transactions.' . $index . '.source_name'; |  | ||||||
|                     $sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); |  | ||||||
|  |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.destination_name'; |  | ||||||
|                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     $validator->errors()->add($idField, trans('validation.invalid_account_info')); |  | ||||||
|  |  | ||||||
|                     return; |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|             // add some errors in case of same account submitted: |  | ||||||
|             if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) { |  | ||||||
|                 $validator->errors()->add($idField, trans('validation.source_equals_destination')); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     private function validRepeatsUntil(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data        = $validator->getData(); |  | ||||||
|         $repetitions = $data['nr_of_repetitions'] ?? null; |  | ||||||
|         $repeatUntil = $data['repeat_until'] ?? null; |  | ||||||
|         if (null !== $repetitions && null !== $repeatUntil) { |  | ||||||
|             // expect a date OR count: |  | ||||||
|             $validator->errors()->add('repeat_until', trans('validation.require_repeat_until')); |  | ||||||
|             $validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until')); |  | ||||||
|  |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * TODO merge this in a rule somehow. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     private function validRepetitionMoment(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data        = $validator->getData(); |  | ||||||
|         $repetitions = $data['repetitions'] ?? []; |  | ||||||
|         /** |  | ||||||
|          * @var int   $index |  | ||||||
|          * @var array $repetition |  | ||||||
|          */ |  | ||||||
|         foreach ($repetitions as $index => $repetition) { |  | ||||||
|             switch ($repetition['type']) { |  | ||||||
|                 default: |  | ||||||
|                     $validator->errors()->add(sprintf('repetitions.%d.type', $index), trans('validation.valid_recurrence_rep_type')); |  | ||||||
|  |  | ||||||
|                     return; |  | ||||||
|                 case 'daily': |  | ||||||
|                     if ('' !== (string)$repetition['moment']) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     return; |  | ||||||
|                 case 'monthly': |  | ||||||
|                     $dayOfMonth = (int)$repetition['moment']; |  | ||||||
|                     if ($dayOfMonth < 1 || $dayOfMonth > 31) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     return; |  | ||||||
|                 case 'ndom': |  | ||||||
|                     $parameters = explode(',', $repetition['moment']); |  | ||||||
|                     if (\count($parameters) !== 2) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|  |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     $nthDay    = (int)($parameters[0] ?? 0.0); |  | ||||||
|                     $dayOfWeek = (int)($parameters[1] ?? 0.0); |  | ||||||
|                     if ($nthDay < 1 || $nthDay > 5) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|  |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     if ($dayOfWeek < 1 || $dayOfWeek > 7) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|  |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     return; |  | ||||||
|                 case 'weekly': |  | ||||||
|                     $dayOfWeek = (int)$repetition['moment']; |  | ||||||
|                     if ($dayOfWeek < 1 || $dayOfWeek > 7) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|  |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 case 'yearly': |  | ||||||
|                     try { |  | ||||||
|                         Carbon::createFromFormat('Y-m-d', $repetition['moment']); |  | ||||||
|                     } catch (InvalidArgumentException $e) { |  | ||||||
|                         $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); |  | ||||||
|  |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -28,6 +28,10 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class Request. |  * Class Request. | ||||||
|  |  * | ||||||
|  |  * Technically speaking this class does not have to be extended like this but who knows what the future brings. | ||||||
|  |  * | ||||||
|  |  * @SuppressWarnings(PHPMD.NumberOfChildren) | ||||||
|  */ |  */ | ||||||
| class Request extends FireflyIIIRequest | class Request extends FireflyIIIRequest | ||||||
| { | { | ||||||
|   | |||||||
| @@ -24,13 +24,8 @@ declare(strict_types=1); | |||||||
|  |  | ||||||
| namespace FireflyIII\Api\V1\Requests; | namespace FireflyIII\Api\V1\Requests; | ||||||
|  |  | ||||||
| use FireflyIII\Exceptions\FireflyException; |  | ||||||
| use FireflyIII\Models\Account; |  | ||||||
| use FireflyIII\Models\AccountType; |  | ||||||
| use FireflyIII\Models\Transaction; |  | ||||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; |  | ||||||
| use FireflyIII\Rules\BelongsUser; | use FireflyIII\Rules\BelongsUser; | ||||||
| use FireflyIII\User; | use FireflyIII\Validation\TransactionValidation; | ||||||
| use Illuminate\Validation\Validator; | use Illuminate\Validation\Validator; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -39,6 +34,8 @@ use Illuminate\Validation\Validator; | |||||||
|  */ |  */ | ||||||
| class TransactionRequest extends Request | class TransactionRequest extends Request | ||||||
| { | { | ||||||
|  |     use TransactionValidation; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return bool |      * @return bool | ||||||
|      */ |      */ | ||||||
| @@ -49,12 +46,15 @@ class TransactionRequest extends Request | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|  |      * Get all data. Is pretty complex because of all the ??-statements. | ||||||
|  |      * | ||||||
|  |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|  |      * @SuppressWarnings(PHPMD.NPathComplexity) | ||||||
|      * @return array |      * @return array | ||||||
|      */ |      */ | ||||||
|     public function getAll(): array |     public function getAll(): array | ||||||
|     { |     { | ||||||
|         $data = [ |         $data = [ | ||||||
|             // basic fields for journal: |  | ||||||
|             'type'               => $this->string('type'), |             'type'               => $this->string('type'), | ||||||
|             'date'               => $this->date('date'), |             'date'               => $this->date('date'), | ||||||
|             'description'        => $this->string('description'), |             'description'        => $this->string('description'), | ||||||
| @@ -63,8 +63,6 @@ class TransactionRequest extends Request | |||||||
|             'bill_id'            => $this->integer('bill_id'), |             'bill_id'            => $this->integer('bill_id'), | ||||||
|             'bill_name'          => $this->string('bill_name'), |             'bill_name'          => $this->string('bill_name'), | ||||||
|             'tags'               => explode(',', $this->string('tags')), |             'tags'               => explode(',', $this->string('tags')), | ||||||
|  |  | ||||||
|             // then, custom fields for journal |  | ||||||
|             'interest_date'      => $this->date('interest_date'), |             'interest_date'      => $this->date('interest_date'), | ||||||
|             'book_date'          => $this->date('book_date'), |             'book_date'          => $this->date('book_date'), | ||||||
|             'process_date'       => $this->date('process_date'), |             'process_date'       => $this->date('process_date'), | ||||||
| @@ -73,39 +71,15 @@ class TransactionRequest extends Request | |||||||
|             'invoice_date'       => $this->date('invoice_date'), |             'invoice_date'       => $this->date('invoice_date'), | ||||||
|             'internal_reference' => $this->string('internal_reference'), |             'internal_reference' => $this->string('internal_reference'), | ||||||
|             'notes'              => $this->string('notes'), |             'notes'              => $this->string('notes'), | ||||||
|  |             'transactions'       => $this->getTransactionData(), | ||||||
|             // then, transactions (see below). |  | ||||||
|             'transactions'       => [], |  | ||||||
|  |  | ||||||
|         ]; |         ]; | ||||||
|         foreach ($this->get('transactions') as $index => $transaction) { |  | ||||||
|             $array                  = [ |  | ||||||
|                 'description'           => $transaction['description'] ?? null, |  | ||||||
|                 'amount'                => $transaction['amount'], |  | ||||||
|                 'currency_id'           => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, |  | ||||||
|                 'currency_code'         => $transaction['currency_code'] ?? null, |  | ||||||
|                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, |  | ||||||
|                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, |  | ||||||
|                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, |  | ||||||
|                 'budget_id'             => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, |  | ||||||
|                 'budget_name'           => $transaction['budget_name'] ?? null, |  | ||||||
|                 'category_id'           => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, |  | ||||||
|                 'category_name'         => $transaction['category_name'] ?? null, |  | ||||||
|                 'source_id'             => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, |  | ||||||
|                 'source_name'           => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, |  | ||||||
|                 'destination_id'        => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, |  | ||||||
|                 'destination_name'      => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, |  | ||||||
|                 'reconciled'            => $transaction['reconciled'] ?? false, |  | ||||||
|                 'identifier'            => $index, |  | ||||||
|             ]; |  | ||||||
|             $data['transactions'][] = $array; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $data; |         return $data; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return array |      * @return array | ||||||
|  |      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) | ||||||
|      */ |      */ | ||||||
|     public function rules(): array |     public function rules(): array | ||||||
|     { |     { | ||||||
| @@ -150,13 +124,8 @@ class TransactionRequest extends Request | |||||||
|             'transactions.*.destination_name'      => 'between:1,255|nullable', |             'transactions.*.destination_name'      => 'between:1,255|nullable', | ||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         switch ($this->method()) { |         if ($this->method() === 'PUT') { | ||||||
|             default: |             unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']); | ||||||
|                 break; |  | ||||||
|             case 'PUT': |  | ||||||
|             case 'PATCH': |  | ||||||
|                 unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']); |  | ||||||
|                 break; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return $rules; |         return $rules; | ||||||
| @@ -175,11 +144,11 @@ class TransactionRequest extends Request | |||||||
|     { |     { | ||||||
|         $validator->after( |         $validator->after( | ||||||
|             function (Validator $validator) { |             function (Validator $validator) { | ||||||
|                 $this->atLeastOneTransaction($validator); |                 $this->validateOneTransaction($validator); | ||||||
|                 $this->checkValidDescriptions($validator); |                 $this->validateDescriptions($validator); | ||||||
|                 $this->equalToJournalDescription($validator); |                 $this->validateJournalDescription($validator); | ||||||
|                 $this->emptySplitDescriptions($validator); |                 $this->validateSplitDescriptions($validator); | ||||||
|                 $this->foreignCurrencyInformation($validator); |                 $this->validateForeignCurrencyInformation($validator); | ||||||
|                 $this->validateAccountInformation($validator); |                 $this->validateAccountInformation($validator); | ||||||
|                 $this->validateSplitAccounts($validator); |                 $this->validateSplitAccounts($validator); | ||||||
|             } |             } | ||||||
| @@ -187,338 +156,35 @@ class TransactionRequest extends Request | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Throws an error when this asset account is invalid. |      * @SuppressWarnings(PHPMD.CyclomaticComplexity) | ||||||
|      * |      * @SuppressWarnings(PHPMD.NPathComplexity) | ||||||
|      * @noinspection MoreThanThreeArgumentsInspection |      * @return array | ||||||
|      * |  | ||||||
|      * @param Validator   $validator |  | ||||||
|      * @param int|null    $accountId |  | ||||||
|      * @param null|string $accountName |  | ||||||
|      * @param string      $idField |  | ||||||
|      * @param string      $nameField |  | ||||||
|      * |  | ||||||
|      * @return null|Account |  | ||||||
|      */ |      */ | ||||||
|     protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account |     private function getTransactionData(): array | ||||||
|     { |     { | ||||||
|         /** @var User $admin */ |         $return = []; | ||||||
|         $admin       = auth()->user(); |         foreach ($this->get('transactions') as $index => $transaction) { | ||||||
|         $accountId   = (int)$accountId; |             $return[] = [ | ||||||
|         $accountName = (string)$accountName; |                 'description'           => $transaction['description'] ?? null, | ||||||
|         // both empty? hard exit. |                 'amount'                => $transaction['amount'], | ||||||
|         if ($accountId < 1 && '' === $accountName) { |                 'currency_id'           => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, | ||||||
|             $validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); |                 'currency_code'         => $transaction['currency_code'] ?? null, | ||||||
|  |                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, | ||||||
|             return null; |                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, | ||||||
|         } |                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, | ||||||
|         // ID belongs to user and is asset account: |                 'budget_id'             => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, | ||||||
|         /** @var AccountRepositoryInterface $repository */ |                 'budget_name'           => $transaction['budget_name'] ?? null, | ||||||
|         $repository = app(AccountRepositoryInterface::class); |                 'category_id'           => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, | ||||||
|         $repository->setUser($admin); |                 'category_name'         => $transaction['category_name'] ?? null, | ||||||
|         $set = $repository->getAccountsById([$accountId]); |                 'source_id'             => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, | ||||||
|         if ($set->count() === 1) { |                 'source_name'           => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, | ||||||
|             /** @var Account $first */ |                 'destination_id'        => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, | ||||||
|             $first = $set->first(); |                 'destination_name'      => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, | ||||||
|             if ($first->accountType->type !== AccountType::ASSET) { |                 'reconciled'            => $transaction['reconciled'] ?? false, | ||||||
|                 $validator->errors()->add($idField, trans('validation.belongs_user')); |                 'identifier'            => $index, | ||||||
|  |             ]; | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // we ignore the account name at this point. |  | ||||||
|             return $first; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $account = $repository->findByName($accountName, [AccountType::ASSET]); |         return $return; | ||||||
|         if (null === $account) { |  | ||||||
|             $validator->errors()->add($nameField, trans('validation.belongs_user')); |  | ||||||
|  |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $account; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Adds an error to the validator when there are no transactions in the array of data. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function atLeastOneTransaction(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data         = $validator->getData(); |  | ||||||
|         $transactions = $data['transactions'] ?? []; |  | ||||||
|         // need at least one transaction |  | ||||||
|         if (\count($transactions) === 0) { |  | ||||||
|             $validator->errors()->add('description', trans('validation.at_least_one_transaction')); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Adds an error to the "description" field when the user has submitted no descriptions and no |  | ||||||
|      * journal description. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function checkValidDescriptions(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data               = $validator->getData(); |  | ||||||
|         $transactions       = $data['transactions'] ?? []; |  | ||||||
|         $journalDescription = (string)($data['description'] ?? ''); |  | ||||||
|         $validDescriptions  = 0; |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             if (\strlen((string)($transaction['description'] ?? '')) > 0) { |  | ||||||
|                 $validDescriptions++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // no valid descriptions and empty journal description? error. |  | ||||||
|         if ($validDescriptions === 0 && '' === $journalDescription) { |  | ||||||
|             $validator->errors()->add('description', trans('validation.filled', ['attribute' => trans('validation.attributes.description')])); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Adds an error to the validator when the user submits a split transaction (more than 1 transactions) |  | ||||||
|      * but does not give them a description. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function emptySplitDescriptions(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data         = $validator->getData(); |  | ||||||
|         $transactions = $data['transactions'] ?? []; |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             $description = (string)($transaction['description'] ?? ''); |  | ||||||
|             // filled description is mandatory for split transactions. |  | ||||||
|             if ('' === $description && \count($transactions) > 1) { |  | ||||||
|                 $validator->errors()->add( |  | ||||||
|                     'transactions.' . $index . '.description', |  | ||||||
|                     trans('validation.filled', ['attribute' => trans('validation.attributes.transaction_description')]) |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Adds an error to the validator when any transaction descriptions are equal to the journal description. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function equalToJournalDescription(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data               = $validator->getData(); |  | ||||||
|         $transactions       = $data['transactions'] ?? []; |  | ||||||
|         $journalDescription = (string)($data['description'] ?? ''); |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             $description = (string)($transaction['description'] ?? ''); |  | ||||||
|             // description cannot be equal to journal description. |  | ||||||
|             if ($description === $journalDescription) { |  | ||||||
|                 $validator->errors()->add('transactions.' . $index . '.description', trans('validation.equal_description')); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * TODO can be made a rule? |  | ||||||
|      * |  | ||||||
|      * If the transactions contain foreign amounts, there must also be foreign currency information. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      */ |  | ||||||
|     protected function foreignCurrencyInformation(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data         = $validator->getData(); |  | ||||||
|         $transactions = $data['transactions'] ?? []; |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             // must have currency info. |  | ||||||
|             if (isset($transaction['foreign_amount']) |  | ||||||
|                 && !(isset($transaction['foreign_currency_id']) |  | ||||||
|                      || isset($transaction['foreign_currency_code']))) { |  | ||||||
|                 $validator->errors()->add( |  | ||||||
|                     'transactions.' . $index . '.foreign_amount', |  | ||||||
|                     trans('validation.require_currency_info') |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Throws an error when the given opposing account (of type $type) is invalid. |  | ||||||
|      * Empty data is allowed, system will default to cash. |  | ||||||
|      * |  | ||||||
|      * @noinspection MoreThanThreeArgumentsInspection |  | ||||||
|      * |  | ||||||
|      * @param Validator   $validator |  | ||||||
|      * @param string      $type |  | ||||||
|      * @param int|null    $accountId |  | ||||||
|      * @param null|string $accountName |  | ||||||
|      * @param string      $idField |  | ||||||
|      * |  | ||||||
|      * @return null|Account |  | ||||||
|      */ |  | ||||||
|     protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account |  | ||||||
|     { |  | ||||||
|         /** @var User $admin */ |  | ||||||
|         $admin       = auth()->user(); |  | ||||||
|         $accountId   = (int)$accountId; |  | ||||||
|         $accountName = (string)$accountName; |  | ||||||
|         // both empty? done! |  | ||||||
|         if ($accountId < 1 && '' === $accountName) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|         if ($accountId !== 0) { |  | ||||||
|             // ID belongs to user and is $type account: |  | ||||||
|             /** @var AccountRepositoryInterface $repository */ |  | ||||||
|             $repository = app(AccountRepositoryInterface::class); |  | ||||||
|             $repository->setUser($admin); |  | ||||||
|             $set = $repository->getAccountsById([$accountId]); |  | ||||||
|             if ($set->count() === 1) { |  | ||||||
|                 /** @var Account $first */ |  | ||||||
|                 $first = $set->first(); |  | ||||||
|                 if ($first->accountType->type !== $type) { |  | ||||||
|                     $validator->errors()->add($idField, trans('validation.belongs_user')); |  | ||||||
|  |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // we ignore the account name at this point. |  | ||||||
|                 return $first; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // not having an opposing account by this name is NOT a problem. |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Validates the given account information. Switches on given transaction type. |  | ||||||
|      * |  | ||||||
|      * @param Validator $validator |  | ||||||
|      * |  | ||||||
|      * @throws FireflyException |  | ||||||
|      */ |  | ||||||
|     protected function validateAccountInformation(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data         = $validator->getData(); |  | ||||||
|         $transactions = $data['transactions'] ?? []; |  | ||||||
|         if (!isset($data['type'])) { |  | ||||||
|             // the journal may exist in the request: |  | ||||||
|             /** @var Transaction $transaction */ |  | ||||||
|             $transaction = $this->route()->parameter('transaction'); |  | ||||||
|             if (null === $transaction) { |  | ||||||
|                 return; // @codeCoverageIgnore |  | ||||||
|             } |  | ||||||
|             $data['type'] = strtolower($transaction->transactionJournal->transactionType->type); |  | ||||||
|         } |  | ||||||
|         foreach ($transactions as $index => $transaction) { |  | ||||||
|             $sourceId           = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null; |  | ||||||
|             $sourceName         = $transaction['source_name'] ?? null; |  | ||||||
|             $destinationId      = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null; |  | ||||||
|             $destinationName    = $transaction['destination_name'] ?? null; |  | ||||||
|             $sourceAccount      = null; |  | ||||||
|             $destinationAccount = null; |  | ||||||
|             switch ($data['type']) { |  | ||||||
|                 case 'withdrawal': |  | ||||||
|                     $idField            = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.source_name'; |  | ||||||
|                     $sourceAccount      = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField); |  | ||||||
|                     break; |  | ||||||
|                 case 'deposit': |  | ||||||
|                     $idField       = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField); |  | ||||||
|  |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.destination_name'; |  | ||||||
|                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); |  | ||||||
|                     break; |  | ||||||
|                 case 'transfer': |  | ||||||
|                     $idField       = 'transactions.' . $index . '.source_id'; |  | ||||||
|                     $nameField     = 'transactions.' . $index . '.source_name'; |  | ||||||
|                     $sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); |  | ||||||
|  |  | ||||||
|                     $idField            = 'transactions.' . $index . '.destination_id'; |  | ||||||
|                     $nameField          = 'transactions.' . $index . '.destination_name'; |  | ||||||
|                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     // @codeCoverageIgnoreStart |  | ||||||
|                     throw new FireflyException( |  | ||||||
|                         sprintf('The validator cannot handle transaction type "%s" in validateAccountInformation().', $data['type']) |  | ||||||
|                     ); |  | ||||||
|                 // @codeCoverageIgnoreEnd |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|             // add some errors in case of same account submitted: |  | ||||||
|             if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) { |  | ||||||
|                 $validator->errors()->add($idField, trans('validation.source_equals_destination')); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @param Validator $validator |  | ||||||
|      * |  | ||||||
|      * @throws FireflyException |  | ||||||
|      */ |  | ||||||
|     protected function validateSplitAccounts(Validator $validator): void |  | ||||||
|     { |  | ||||||
|         $data  = $validator->getData(); |  | ||||||
|         $count = isset($data['transactions']) ? \count($data['transactions']) : 0; |  | ||||||
|         if ($count < 2) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         // this is pretty much impossible: |  | ||||||
|         // @codeCoverageIgnoreStart |  | ||||||
|         if (!isset($data['type'])) { |  | ||||||
|             // the journal may exist in the request: |  | ||||||
|             /** @var Transaction $transaction */ |  | ||||||
|             $transaction = $this->route()->parameter('transaction'); |  | ||||||
|             if (null === $transaction) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             $data['type'] = strtolower($transaction->transactionJournal->transactionType->type); |  | ||||||
|         } |  | ||||||
|         // @codeCoverageIgnoreEnd |  | ||||||
|  |  | ||||||
|         // collect all source ID's and destination ID's, if present: |  | ||||||
|         $sources      = []; |  | ||||||
|         $destinations = []; |  | ||||||
|  |  | ||||||
|         foreach ($data['transactions'] as $transaction) { |  | ||||||
|             $sources[]      = isset($transaction['source_id']) ? (int)$transaction['source_id'] : 0; |  | ||||||
|             $destinations[] = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : 0; |  | ||||||
|         } |  | ||||||
|         $destinations = array_unique($destinations); |  | ||||||
|         $sources      = array_unique($sources); |  | ||||||
|         // switch on type: |  | ||||||
|         switch ($data['type']) { |  | ||||||
|             case 'withdrawal': |  | ||||||
|                 if (\count($sources) > 1) { |  | ||||||
|                     $validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal')); |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case 'deposit': |  | ||||||
|                 if (\count($destinations) > 1) { |  | ||||||
|                     $validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal')); |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case 'transfer': |  | ||||||
|                 if (\count($sources) > 1 || \count($destinations) > 1) { |  | ||||||
|                     $validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal')); |  | ||||||
|                     $validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal')); |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 // @codeCoverageIgnoreStart |  | ||||||
|                 throw new FireflyException( |  | ||||||
|                     sprintf('The validator cannot handle transaction type "%s" in validateSplitAccounts().', $data['type']) |  | ||||||
|                 ); |  | ||||||
|             // @codeCoverageIgnoreEnd |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -51,9 +51,6 @@ class DecryptAttachment extends Command | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Execute the console command. |      * Execute the console command. | ||||||
|      * |  | ||||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five its fine. |  | ||||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |  | ||||||
|      */ |      */ | ||||||
|     public function handle() |     public function handle() | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -55,9 +55,6 @@ use Schema; | |||||||
|  * Class UpgradeDatabase. |  * Class UpgradeDatabase. | ||||||
|  * |  * | ||||||
|  * Upgrade user database. |  * Upgrade user database. | ||||||
|  * |  | ||||||
|  * |  | ||||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things. |  | ||||||
|  */ |  */ | ||||||
| class UpgradeDatabase extends Command | class UpgradeDatabase extends Command | ||||||
| { | { | ||||||
| @@ -259,9 +256,6 @@ class UpgradeDatabase extends Command | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account. |      * Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account. | ||||||
|      * |  | ||||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's seven but it can't really be helped. |  | ||||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |  | ||||||
|      */ |      */ | ||||||
|     public function updateAccountCurrencies(): void |     public function updateAccountCurrencies(): void | ||||||
|     { |     { | ||||||
| @@ -316,8 +310,6 @@ class UpgradeDatabase extends Command | |||||||
|      * |      * | ||||||
|      * Both source and destination must match the respective currency preference of the related asset account. |      * Both source and destination must match the respective currency preference of the related asset account. | ||||||
|      * So FF3 must verify all transactions. |      * So FF3 must verify all transactions. | ||||||
|      * |  | ||||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |  | ||||||
|      */ |      */ | ||||||
|     public function updateOtherCurrencies(): void |     public function updateOtherCurrencies(): void | ||||||
|     { |     { | ||||||
| @@ -551,10 +543,6 @@ class UpgradeDatabase extends Command | |||||||
|      * |      * | ||||||
|      * Method is long and complex bit I'm taking it for granted. |      * Method is long and complex bit I'm taking it for granted. | ||||||
|      * |      * | ||||||
|      * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |  | ||||||
|      * @SuppressWarnings(PHPMD.NPathComplexity) |  | ||||||
|      * @SuppressWarnings(PHPMD.CyclomaticComplexity) |  | ||||||
|      * |  | ||||||
|      * @param Transaction $transaction |      * @param Transaction $transaction | ||||||
|      */ |      */ | ||||||
|     private function updateTransactionCurrency(Transaction $transaction): void |     private function updateTransactionCurrency(Transaction $transaction): void | ||||||
|   | |||||||
| @@ -46,8 +46,6 @@ use stdClass; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Class VerifyDatabase. |  * Class VerifyDatabase. | ||||||
|  * |  | ||||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |  | ||||||
|  */ |  */ | ||||||
| class VerifyDatabase extends Command | class VerifyDatabase extends Command | ||||||
| { | { | ||||||
|   | |||||||
| @@ -269,12 +269,16 @@ class BudgetRepository implements BudgetRepositoryInterface | |||||||
|     /** |     /** | ||||||
|      * Find a budget or return NULL |      * Find a budget or return NULL | ||||||
|      * |      * | ||||||
|      * @param int $budgetId |      * @param int $budgetId |null | ||||||
|      * |      * | ||||||
|      * @return Budget|null |      * @return Budget|null | ||||||
|      */ |      */ | ||||||
|     public function findNull(int $budgetId): ?Budget |     public function findNull(int $budgetId = null): ?Budget | ||||||
|     { |     { | ||||||
|  |         if (null === $budgetId) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return $this->user->budgets()->find($budgetId); |         return $this->user->budgets()->find($budgetId); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -114,13 +114,11 @@ interface BudgetRepositoryInterface | |||||||
|     public function findByName(string $name): ?Budget; |     public function findByName(string $name): ?Budget; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Find a budget or return NULL |      * @param int|null $budgetId | ||||||
|      * |  | ||||||
|      * @param int $budgetId |  | ||||||
|      * |      * | ||||||
|      * @return Budget|null |      * @return Budget|null | ||||||
|      */ |      */ | ||||||
|     public function findNull(int $budgetId): ?Budget; |     public function findNull(int $budgetId = null): ?Budget; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * This method returns the oldest journal or transaction date known to this budget. |      * This method returns the oldest journal or transaction date known to this budget. | ||||||
|   | |||||||
| @@ -432,6 +432,19 @@ class FireflyValidator extends Validator | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * TODO fill me. | ||||||
|  |      * | ||||||
|  |      * @param $attribute | ||||||
|  |      * @param $value | ||||||
|  |      * @param $parameters | ||||||
|  |      * | ||||||
|  |      * @return bool | ||||||
|  |      */ | ||||||
|  |     public function validateRepetitionMoment($attribute, $value, $parameters): bool { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @SuppressWarnings(PHPMD.UnusedFormalParameter) |      * @SuppressWarnings(PHPMD.UnusedFormalParameter) | ||||||
|      * |      * | ||||||
|   | |||||||
							
								
								
									
										194
									
								
								app/Validation/RecurrenceValidation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								app/Validation/RecurrenceValidation.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * ApiValidation.php | ||||||
|  |  * Copyright (c) 2018 thegrumpydictator@gmail.com | ||||||
|  |  * | ||||||
|  |  * This file is part of Firefly III. | ||||||
|  |  * | ||||||
|  |  * Firefly III is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * Firefly III 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 General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | declare(strict_types=1); | ||||||
|  |  | ||||||
|  | namespace FireflyIII\Validation; | ||||||
|  |  | ||||||
|  | use Carbon\Carbon; | ||||||
|  | use Exception; | ||||||
|  | use Illuminate\Validation\Validator; | ||||||
|  | use InvalidArgumentException; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Trait RecurrenceValidation | ||||||
|  |  * | ||||||
|  |  * Contains advanced validation rules used in validation of new and existing recurrences. | ||||||
|  |  * | ||||||
|  |  * @package FireflyIII\Validation | ||||||
|  |  */ | ||||||
|  | trait RecurrenceValidation | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateRepetitionMoment(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data        = $validator->getData(); | ||||||
|  |         $repetitions = $data['repetitions'] ?? []; | ||||||
|  |         /** | ||||||
|  |          * @var int   $index | ||||||
|  |          * @var array $repetition | ||||||
|  |          */ | ||||||
|  |         foreach ($repetitions as $index => $repetition) { | ||||||
|  |             switch ($repetition['type']) { | ||||||
|  |                 default: | ||||||
|  |                     $validator->errors()->add(sprintf('repetitions.%d.type', $index), trans('validation.valid_recurrence_rep_type')); | ||||||
|  |  | ||||||
|  |                     return; | ||||||
|  |                 case 'daily': | ||||||
|  |                     $this->validateDaily($validator, $index, (string)$repetition['moment']); | ||||||
|  |                     break; | ||||||
|  |                 case 'monthly': | ||||||
|  |                     $this->validateMonthly($validator, $index, (int)$repetition['moment']); | ||||||
|  |                     break; | ||||||
|  |                 case 'ndom': | ||||||
|  |                     $this->validateNdom($validator, $index, (string)$repetition['moment']); | ||||||
|  |                     break; | ||||||
|  |                 case 'weekly': | ||||||
|  |                     $this->validateWeekly($validator, $index, (int)$repetition['moment']); | ||||||
|  |                     break; | ||||||
|  |                 case 'yearly': | ||||||
|  |                     $this->validateYearly($validator, $index, (string)$repetition['moment']); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds an error to the validator when there are no repetitions in the array of data. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateOneRepetition(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data        = $validator->getData(); | ||||||
|  |         $repetitions = $data['repetitions'] ?? []; | ||||||
|  |         // need at least one transaction | ||||||
|  |         if (\count($repetitions) === 0) { | ||||||
|  |             $validator->errors()->add('description', trans('validation.at_least_one_repetition')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Validates that the recurrence has valid repetition information. It either doesn't stop, | ||||||
|  |      * or stops after X times or at X date. Not both of them., | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateRecurrenceRepetition(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data        = $validator->getData(); | ||||||
|  |         $repetitions = $data['nr_of_repetitions'] ?? null; | ||||||
|  |         $repeatUntil = $data['repeat_until'] ?? null; | ||||||
|  |         if (null !== $repetitions && null !== $repeatUntil) { | ||||||
|  |             // expect a date OR count: | ||||||
|  |             $validator->errors()->add('repeat_until', trans('validation.require_repeat_until')); | ||||||
|  |             $validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the repetition type is daily, the moment should be empty. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      * @param int       $index | ||||||
|  |      * @param string    $moment | ||||||
|  |      */ | ||||||
|  |     protected function validateDaily(Validator $validator, int $index, string $moment): void | ||||||
|  |     { | ||||||
|  |         if ('' !== $moment) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the repetition type is monthly, the moment should be a day between 1-31 (inclusive). | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      * @param int       $index | ||||||
|  |      * @param int       $dayOfMonth | ||||||
|  |      */ | ||||||
|  |     protected function validateMonthly(Validator $validator, int $index, int $dayOfMonth): void | ||||||
|  |     { | ||||||
|  |         if ($dayOfMonth < 1 || $dayOfMonth > 31) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the repetition type is "ndom", the first part must be between 1-5 (inclusive), for the week in the month, | ||||||
|  |      * and the second one must be between 1-7 (inclusive) for the day of the week. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      * @param int       $index | ||||||
|  |      * @param string    $moment | ||||||
|  |      */ | ||||||
|  |     protected function validateNdom(Validator $validator, int $index, string $moment): void | ||||||
|  |     { | ||||||
|  |         $parameters = explode(',', $moment); | ||||||
|  |         if (\count($parameters) !== 2) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |  | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         $nthDay    = (int)($parameters[0] ?? 0.0); | ||||||
|  |         $dayOfWeek = (int)($parameters[1] ?? 0.0); | ||||||
|  |         if ($nthDay < 1 || $nthDay > 5) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |  | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if ($dayOfWeek < 1 || $dayOfWeek > 7) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the repetition type is weekly, the moment should be a day between 1-7 (inclusive). | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      * @param int       $index | ||||||
|  |      * @param int       $dayOfWeek | ||||||
|  |      */ | ||||||
|  |     protected function validateWeekly(Validator $validator, int $index, int $dayOfWeek): void | ||||||
|  |     { | ||||||
|  |         if ($dayOfWeek < 1 || $dayOfWeek > 7) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the repetition type is yearly, the moment should be a valid date. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      * @param int       $index | ||||||
|  |      * @param string    $moment | ||||||
|  |      */ | ||||||
|  |     protected function validateYearly(Validator $validator, int $index, string $moment): void | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             Carbon::createFromFormat('Y-m-d', $moment); | ||||||
|  |         } catch (InvalidArgumentException|Exception $e) { | ||||||
|  |             $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										366
									
								
								app/Validation/TransactionValidation.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								app/Validation/TransactionValidation.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * TransactionValidation.php | ||||||
|  |  * Copyright (c) 2018 thegrumpydictator@gmail.com | ||||||
|  |  * | ||||||
|  |  * This file is part of Firefly III. | ||||||
|  |  * | ||||||
|  |  * Firefly III is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * Firefly III 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 General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | declare(strict_types=1); | ||||||
|  |  | ||||||
|  | namespace FireflyIII\Validation; | ||||||
|  |  | ||||||
|  | use FireflyIII\Models\Account; | ||||||
|  | use FireflyIII\Models\AccountType; | ||||||
|  | use FireflyIII\Models\Transaction; | ||||||
|  | use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||||
|  | use FireflyIII\User; | ||||||
|  | use Illuminate\Validation\Validator; | ||||||
|  | use Log; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Trait TransactionValidation | ||||||
|  |  * | ||||||
|  |  * @package FireflyIII\Validation | ||||||
|  |  */ | ||||||
|  | trait TransactionValidation | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Validates the given account information. Switches on given transaction type. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateAccountInformation(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data            = $validator->getData(); | ||||||
|  |         $transactions    = $data['transactions'] ?? []; | ||||||
|  |         $idField         = 'description'; | ||||||
|  |         $transactionType = $data['type'] ?? 'invalid'; | ||||||
|  |         // get transaction type: | ||||||
|  |         if (!isset($data['type'])) { | ||||||
|  |             // the journal may exist in the request: | ||||||
|  |             /** @var Transaction $transaction */ | ||||||
|  |             $transaction = $this->route()->parameter('transaction'); | ||||||
|  |             if (null !== $transaction) { | ||||||
|  |                 $transactionType = strtolower($transaction->transactionJournal->transactionType->type); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($transactions as $index => $transaction) { | ||||||
|  |             $sourceId           = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null; | ||||||
|  |             $sourceName         = $transaction['source_name'] ?? null; | ||||||
|  |             $destinationId      = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null; | ||||||
|  |             $destinationName    = $transaction['destination_name'] ?? null; | ||||||
|  |             $sourceAccount      = null; | ||||||
|  |             $destinationAccount = null; | ||||||
|  |             switch ($transactionType) { | ||||||
|  |                 case 'withdrawal': | ||||||
|  |                     $idField            = 'transactions.' . $index . '.source_id'; | ||||||
|  |                     $nameField          = 'transactions.' . $index . '.source_name'; | ||||||
|  |                     $sourceAccount      = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); | ||||||
|  |                     $idField            = 'transactions.' . $index . '.destination_id'; | ||||||
|  |                     $destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField); | ||||||
|  |                     break; | ||||||
|  |                 case 'deposit': | ||||||
|  |                     $idField       = 'transactions.' . $index . '.source_id'; | ||||||
|  |                     $sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField); | ||||||
|  |  | ||||||
|  |                     $idField            = 'transactions.' . $index . '.destination_id'; | ||||||
|  |                     $nameField          = 'transactions.' . $index . '.destination_name'; | ||||||
|  |                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); | ||||||
|  |                     break; | ||||||
|  |                 case 'transfer': | ||||||
|  |                     $idField       = 'transactions.' . $index . '.source_id'; | ||||||
|  |                     $nameField     = 'transactions.' . $index . '.source_name'; | ||||||
|  |                     $sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); | ||||||
|  |  | ||||||
|  |                     $idField            = 'transactions.' . $index . '.destination_id'; | ||||||
|  |                     $nameField          = 'transactions.' . $index . '.destination_name'; | ||||||
|  |                     $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     $validator->errors()->add($idField, trans('validation.invalid_account_info')); | ||||||
|  |  | ||||||
|  |                     return; | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             // add some errors in case of same account submitted: | ||||||
|  |             if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) { | ||||||
|  |                 $validator->errors()->add($idField, trans('validation.source_equals_destination')); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds an error to the "description" field when the user has submitted no descriptions and no | ||||||
|  |      * journal description. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateDescriptions(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data               = $validator->getData(); | ||||||
|  |         $transactions       = $data['transactions'] ?? []; | ||||||
|  |         $journalDescription = (string)($data['description'] ?? ''); | ||||||
|  |         $validDescriptions  = 0; | ||||||
|  |         foreach ($transactions as $index => $transaction) { | ||||||
|  |             if (\strlen((string)($transaction['description'] ?? '')) > 0) { | ||||||
|  |                 $validDescriptions++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // no valid descriptions and empty journal description? error. | ||||||
|  |         if ($validDescriptions === 0 && '' === $journalDescription) { | ||||||
|  |             $validator->errors()->add('description', trans('validation.filled', ['attribute' => trans('validation.attributes.description')])); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the transactions contain foreign amounts, there must also be foreign currency information. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateForeignCurrencyInformation(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data         = $validator->getData(); | ||||||
|  |         $transactions = $data['transactions'] ?? []; | ||||||
|  |         foreach ($transactions as $index => $transaction) { | ||||||
|  |             // must have currency info. | ||||||
|  |             if (isset($transaction['foreign_amount']) | ||||||
|  |                 && !(isset($transaction['foreign_currency_id']) | ||||||
|  |                      || isset($transaction['foreign_currency_code']))) { | ||||||
|  |                 $validator->errors()->add( | ||||||
|  |                     'transactions.' . $index . '.foreign_amount', | ||||||
|  |                     trans('validation.require_currency_info') | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds an error to the validator when any transaction descriptions are equal to the journal description. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateJournalDescription(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data               = $validator->getData(); | ||||||
|  |         $transactions       = $data['transactions'] ?? []; | ||||||
|  |         $journalDescription = (string)($data['description'] ?? ''); | ||||||
|  |         foreach ($transactions as $index => $transaction) { | ||||||
|  |             $description = (string)($transaction['description'] ?? ''); | ||||||
|  |             // description cannot be equal to journal description. | ||||||
|  |             if ($description === $journalDescription) { | ||||||
|  |                 $validator->errors()->add('transactions.' . $index . '.description', trans('validation.equal_description')); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds an error to the validator when there are no transactions in the array of data. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateOneTransaction(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data         = $validator->getData(); | ||||||
|  |         $transactions = $data['transactions'] ?? []; | ||||||
|  |         // need at least one transaction | ||||||
|  |         if (\count($transactions) === 0) { | ||||||
|  |             $validator->errors()->add('description', trans('validation.at_least_one_transaction')); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Make sure that all the splits accounts are valid in combination with each other. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateSplitAccounts(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data  = $validator->getData(); | ||||||
|  |         $count = isset($data['transactions']) ? \count($data['transactions']) : 0; | ||||||
|  |         if ($count < 2) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // this is pretty much impossible: | ||||||
|  |         // @codeCoverageIgnoreStart | ||||||
|  |         if (!isset($data['type'])) { | ||||||
|  |             // the journal may exist in the request: | ||||||
|  |             /** @var Transaction $transaction */ | ||||||
|  |             $transaction = $this->route()->parameter('transaction'); | ||||||
|  |             if (null === $transaction) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             $data['type'] = strtolower($transaction->transactionJournal->transactionType->type); | ||||||
|  |         } | ||||||
|  |         // @codeCoverageIgnoreEnd | ||||||
|  |  | ||||||
|  |         // collect all source ID's and destination ID's, if present: | ||||||
|  |         $sources      = []; | ||||||
|  |         $destinations = []; | ||||||
|  |  | ||||||
|  |         foreach ($data['transactions'] as $transaction) { | ||||||
|  |             $sources[]      = isset($transaction['source_id']) ? (int)$transaction['source_id'] : 0; | ||||||
|  |             $destinations[] = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : 0; | ||||||
|  |         } | ||||||
|  |         $destinations = array_unique($destinations); | ||||||
|  |         $sources      = array_unique($sources); | ||||||
|  |         // switch on type: | ||||||
|  |         switch ($data['type']) { | ||||||
|  |             case 'withdrawal': | ||||||
|  |                 if (\count($sources) > 1) { | ||||||
|  |                     $validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal')); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case 'deposit': | ||||||
|  |                 if (\count($destinations) > 1) { | ||||||
|  |                     $validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal')); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case 'transfer': | ||||||
|  |                 if (\count($sources) > 1 || \count($destinations) > 1) { | ||||||
|  |                     $validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal')); | ||||||
|  |                     $validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal')); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds an error to the validator when the user submits a split transaction (more than 1 transactions) | ||||||
|  |      * but does not give them a description. | ||||||
|  |      * | ||||||
|  |      * @param Validator $validator | ||||||
|  |      */ | ||||||
|  |     public function validateSplitDescriptions(Validator $validator): void | ||||||
|  |     { | ||||||
|  |         $data         = $validator->getData(); | ||||||
|  |         $transactions = $data['transactions'] ?? []; | ||||||
|  |         foreach ($transactions as $index => $transaction) { | ||||||
|  |             $description = (string)($transaction['description'] ?? ''); | ||||||
|  |             // filled description is mandatory for split transactions. | ||||||
|  |             if ('' === $description && \count($transactions) > 1) { | ||||||
|  |                 $validator->errors()->add( | ||||||
|  |                     'transactions.' . $index . '.description', | ||||||
|  |                     trans('validation.filled', ['attribute' => trans('validation.attributes.transaction_description')]) | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Throws an error when this asset account is invalid. | ||||||
|  |      * | ||||||
|  |      * @noinspection MoreThanThreeArgumentsInspection | ||||||
|  |      * | ||||||
|  |      * @param Validator   $validator | ||||||
|  |      * @param int|null    $accountId | ||||||
|  |      * @param null|string $accountName | ||||||
|  |      * @param string      $idField | ||||||
|  |      * @param string      $nameField | ||||||
|  |      * | ||||||
|  |      * @return null|Account | ||||||
|  |      */ | ||||||
|  |     protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account | ||||||
|  |     { | ||||||
|  |         /** @var User $admin */ | ||||||
|  |         $admin       = auth()->user(); | ||||||
|  |         $accountId   = (int)$accountId; | ||||||
|  |         $accountName = (string)$accountName; | ||||||
|  |         // both empty? hard exit. | ||||||
|  |         if ($accountId < 1 && '' === $accountName) { | ||||||
|  |             $validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); | ||||||
|  |  | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         // ID belongs to user and is asset account: | ||||||
|  |         /** @var AccountRepositoryInterface $repository */ | ||||||
|  |         $repository = app(AccountRepositoryInterface::class); | ||||||
|  |         $repository->setUser($admin); | ||||||
|  |         $set = $repository->getAccountsById([$accountId]); | ||||||
|  |         Log::debug(sprintf('Count of accounts found by ID %d is: %d', $accountId, $set->count())); | ||||||
|  |         if ($set->count() === 1) { | ||||||
|  |             /** @var Account $first */ | ||||||
|  |             $first = $set->first(); | ||||||
|  |             if ($first->accountType->type !== AccountType::ASSET) { | ||||||
|  |                 $validator->errors()->add($idField, trans('validation.belongs_user')); | ||||||
|  |  | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // we ignore the account name at this point. | ||||||
|  |             return $first; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $account = $repository->findByName($accountName, [AccountType::ASSET]); | ||||||
|  |         if (null === $account) { | ||||||
|  |             $validator->errors()->add($nameField, trans('validation.belongs_user')); | ||||||
|  |  | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $account; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Throws an error when the given opposing account (of type $type) is invalid. | ||||||
|  |      * Empty data is allowed, system will default to cash. | ||||||
|  |      * | ||||||
|  |      * @noinspection MoreThanThreeArgumentsInspection | ||||||
|  |      * | ||||||
|  |      * @param Validator   $validator | ||||||
|  |      * @param string      $type | ||||||
|  |      * @param int|null    $accountId | ||||||
|  |      * @param null|string $accountName | ||||||
|  |      * @param string      $idField | ||||||
|  |      * | ||||||
|  |      * @return null|Account | ||||||
|  |      */ | ||||||
|  |     protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account | ||||||
|  |     { | ||||||
|  |         /** @var User $admin */ | ||||||
|  |         $admin       = auth()->user(); | ||||||
|  |         $accountId   = (int)$accountId; | ||||||
|  |         $accountName = (string)$accountName; | ||||||
|  |         // both empty? done! | ||||||
|  |         if ($accountId < 1 && '' === $accountName) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         if ($accountId !== 0) { | ||||||
|  |             // ID belongs to user and is $type account: | ||||||
|  |             /** @var AccountRepositoryInterface $repository */ | ||||||
|  |             $repository = app(AccountRepositoryInterface::class); | ||||||
|  |             $repository->setUser($admin); | ||||||
|  |             $set = $repository->getAccountsById([$accountId]); | ||||||
|  |             if ($set->count() === 1) { | ||||||
|  |                 /** @var Account $first */ | ||||||
|  |                 $first = $set->first(); | ||||||
|  |                 if ($first->accountType->type !== $type) { | ||||||
|  |                     $validator->errors()->add($idField, trans('validation.belongs_user')); | ||||||
|  |  | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // we ignore the account name at this point. | ||||||
|  |                 return $first; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // not having an opposing account by this name is NOT a problem. | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user