diff --git a/app/Api/V2/Controllers/Autocomplete/AccountController.php b/app/Api/V2/Controllers/Autocomplete/AccountController.php index 62fbfc914b..42b267c4a7 100644 --- a/app/Api/V2/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V2/Controllers/Autocomplete/AccountController.php @@ -30,7 +30,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use Illuminate\Http\JsonResponse; @@ -79,6 +79,7 @@ class AccountController extends Controller */ public function accounts(AutocompleteRequest $request): JsonResponse { + die('uses old administration ID check, needs to be updated. 1'); $data = $request->getData(); $types = $data['types']; $query = $data['query']; diff --git a/app/Api/V2/Controllers/Chart/AccountController.php b/app/Api/V2/Controllers/Chart/AccountController.php index 92d728a9b7..2d7a298c67 100644 --- a/app/Api/V2/Controllers/Chart/AccountController.php +++ b/app/Api/V2/Controllers/Chart/AccountController.php @@ -31,7 +31,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use Illuminate\Http\JsonResponse; use Psr\Container\ContainerExceptionInterface; @@ -55,6 +55,7 @@ class AccountController extends Controller $this->middleware( function ($request, $next) { $this->repository = app(AccountRepositoryInterface::class); + die('uses old administration ID check, needs to be updated.2'); $this->repository->setAdministrationId(auth()->user()->user_group_id); return $next($request); } diff --git a/app/Api/V2/Controllers/Chart/BalanceController.php b/app/Api/V2/Controllers/Chart/BalanceController.php index 8161eb3f7b..6c56e97313 100644 --- a/app/Api/V2/Controllers/Chart/BalanceController.php +++ b/app/Api/V2/Controllers/Chart/BalanceController.php @@ -32,7 +32,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Http\JsonResponse; diff --git a/app/Api/V2/Controllers/Chart/BudgetController.php b/app/Api/V2/Controllers/Chart/BudgetController.php index ea8f9609fe..e5e468f3f4 100644 --- a/app/Api/V2/Controllers/Chart/BudgetController.php +++ b/app/Api/V2/Controllers/Chart/BudgetController.php @@ -32,8 +32,8 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Administration\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\OperationsRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; @@ -78,6 +78,7 @@ class BudgetController extends Controller */ public function dashboard(DateRequest $request): JsonResponse { + die('uses old administration ID check, needs to be updated.3'); // get user. /** @var User $user */ $user = auth()->user(); diff --git a/app/Api/V2/Controllers/Chart/CategoryController.php b/app/Api/V2/Controllers/Chart/CategoryController.php index bece4e2bd1..8b82845247 100644 --- a/app/Api/V2/Controllers/Chart/CategoryController.php +++ b/app/Api/V2/Controllers/Chart/CategoryController.php @@ -32,7 +32,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; @@ -53,6 +53,7 @@ class CategoryController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + die('uses old administration ID check, needs to be updated.4'); $this->accountRepos = app(AccountRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->accountRepos->setAdministrationId(auth()->user()->user_group_id); diff --git a/app/Api/V2/Controllers/Controller.php b/app/Api/V2/Controllers/Controller.php index 1843ea2d9a..2dfd00b8aa 100644 --- a/app/Api/V2/Controllers/Controller.php +++ b/app/Api/V2/Controllers/Controller.php @@ -171,7 +171,7 @@ class Controller extends BaseController * * @return array */ - final protected function jsonApiObject(string $key, Model $object, AbstractTransformer $transformer): array + final protected function jsonApiObject(string $key, array | Model $object, AbstractTransformer $transformer): array { // create some objects: $manager = new Manager(); diff --git a/app/Api/V2/Controllers/Model/Bill/ShowController.php b/app/Api/V2/Controllers/Model/Bill/ShowController.php index fc9829acbc..f06c47a524 100644 --- a/app/Api/V2/Controllers/Model/Bill/ShowController.php +++ b/app/Api/V2/Controllers/Model/Bill/ShowController.php @@ -27,7 +27,7 @@ namespace FireflyIII\Api\V2\Controllers\Model\Bill; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Models\Bill; -use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; use FireflyIII\Transformers\V2\AccountTransformer; use FireflyIII\Transformers\V2\BillTransformer; use Illuminate\Http\JsonResponse; @@ -46,6 +46,7 @@ class ShowController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + die('uses old administration ID check, needs to be updated.5'); $this->repository = app(BillRepositoryInterface::class); $this->repository->setAdministrationId(auth()->user()->user_group_id); return $next($request); diff --git a/app/Api/V2/Controllers/Model/Bill/SumController.php b/app/Api/V2/Controllers/Model/Bill/SumController.php index 1f37b0b5bf..402d768eb6 100644 --- a/app/Api/V2/Controllers/Model/Bill/SumController.php +++ b/app/Api/V2/Controllers/Model/Bill/SumController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Api\V2\Controllers\Model\Bill; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Request\Generic\DateRequest; -use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; use Illuminate\Http\JsonResponse; /** @@ -63,6 +63,7 @@ class SumController extends Controller */ public function paid(DateRequest $request): JsonResponse { + die('uses old administration ID check, needs to be updated.6'); $this->repository->setAdministrationId(auth()->user()->user_group_id); $result = $this->repository->sumPaidInRange($this->parameters->get('start'), $this->parameters->get('end')); @@ -82,6 +83,7 @@ class SumController extends Controller */ public function unpaid(DateRequest $request): JsonResponse { + die('uses old administration ID check, needs to be updated.7'); $this->repository->setAdministrationId(auth()->user()->user_group_id); $result = $this->repository->sumUnpaidInRange($this->parameters->get('start'), $this->parameters->get('end')); diff --git a/app/Api/V2/Controllers/Model/PiggyBank/ShowController.php b/app/Api/V2/Controllers/Model/PiggyBank/ShowController.php index ed6b0846e9..aa32aeaa90 100644 --- a/app/Api/V2/Controllers/Model/PiggyBank/ShowController.php +++ b/app/Api/V2/Controllers/Model/PiggyBank/ShowController.php @@ -26,7 +26,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\PiggyBank; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Repositories\Administration\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Repositories\UserGroups\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Transformers\V2\PiggyBankTransformer; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -44,6 +44,7 @@ class ShowController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { + die('uses old administration ID check, needs to be updated.8'); $this->repository = app(PiggyBankRepositoryInterface::class); $this->repository->setAdministrationId(auth()->user()->user_group_id); return $next($request); diff --git a/app/Api/V2/Controllers/Model/Transaction/StoreController.php b/app/Api/V2/Controllers/Model/Transaction/StoreController.php index 20e76ed300..01aebcb56a 100644 --- a/app/Api/V2/Controllers/Model/Transaction/StoreController.php +++ b/app/Api/V2/Controllers/Model/Transaction/StoreController.php @@ -26,25 +26,105 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\Transaction; use FireflyIII\Api\V2\Controllers\Controller; +use FireflyIII\Api\V2\Request\Model\Transaction\StoreRequest; +use FireflyIII\Events\StoredTransactionGroup; +use FireflyIII\Exceptions\DuplicateTransactionException; +use FireflyIII\Exceptions\FireflyException; +use Illuminate\Validation\ValidationException; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; +use FireflyIII\Rules\IsDuplicateTransaction; +use FireflyIII\Transformers\V2\TransactionGroupTransformer; +use FireflyIII\User; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Validator; /** * Class StoreController */ class StoreController extends Controller { + private TransactionGroupRepositoryInterface $groupRepository; + + /** + * TransactionController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->groupRepository = app(TransactionGroupRepositoryInterface::class); + return $next($request); + } + ); + } + /** * TODO this method is practically the same as the V1 method and borrows as much code as possible. * TODO still it duplicates a lot. * TODO the v1 endpoints will never support separate administrations, this is an important distinction. * * @return JsonResponse + * @throws FireflyException + * @throws ValidationException */ - public function post(): JsonResponse + public function post(StoreRequest $request): JsonResponse { - return response()->json([]); + app('log')->debug('Now in API v2 StoreController::store()'); + $data = $request->getAll(); + $data['user'] = auth()->user()->id; + $userGroup = $request->getUserGroup(); + $data['user_group'] = $userGroup; + // overrule user group and see where we end up. + // what happens when we refer to a budget that is not in this user group? + + app('log')->channel('audit')->info('Store new transaction over API.', $data); + + try { + $transactionGroup = $this->groupRepository->store($data); + } catch (DuplicateTransactionException $e) { + app('log')->warning('Caught a duplicate transaction. Return error message.'); + $validator = Validator::make( + ['transactions' => [['description' => $e->getMessage()]]], + ['transactions.0.description' => new IsDuplicateTransaction()] + ); + throw new ValidationException($validator, 0, $e); + } catch (FireflyException $e) { + app('log')->warning('Caught an exception. Return error message.'); + app('log')->error($e->getMessage()); + $message = sprintf('Internal exception: %s', $e->getMessage()); + $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); + throw new ValidationException($validator, 0, $e); + } + app('preferences')->mark(); + $applyRules = $data['apply_rules'] ?? true; + $fireWebhooks = $data['fire_webhooks'] ?? true; + event(new StoredTransactionGroup($transactionGroup, $applyRules, $fireWebhooks)); + + /** @var User $admin */ + $admin = auth()->user(); + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on transaction group. + ->setTransactionGroup($transactionGroup); + + $selectedGroup = $collector->getGroups()->first(); + if (null === $selectedGroup) { + throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); + } + + $transformer = new TransactionGroupTransformer(); + $transformer->setParameters($this->parameters); + + return response() + ->api($this->jsonApiObject('transactions', $selectedGroup, $transformer)) + ->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V2/Controllers/Summary/BasicController.php b/app/Api/V2/Controllers/Summary/BasicController.php index 25eca36e4b..35c92a743f 100644 --- a/app/Api/V2/Controllers/Summary/BasicController.php +++ b/app/Api/V2/Controllers/Summary/BasicController.php @@ -36,11 +36,11 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Models\UserGroup; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\AvailableBudgetRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\OperationsRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\AvailableBudgetRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\User; @@ -77,6 +77,8 @@ class BasicController extends Controller $this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); + die('uses old administration ID check, needs to be updated.9'); + $this->abRepository->setAdministrationId($user->user_group_id); $this->accountRepository->setAdministrationId($user->user_group_id); $this->billRepository->setAdministrationId($user->user_group_id); diff --git a/app/Api/V2/Request/Model/Transaction/StoreRequest.php b/app/Api/V2/Request/Model/Transaction/StoreRequest.php new file mode 100644 index 0000000000..683a289a97 --- /dev/null +++ b/app/Api/V2/Request/Model/Transaction/StoreRequest.php @@ -0,0 +1,317 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V2\Request\Model\Transaction; + +use FireflyIII\Enums\UserRoleEnum; +use FireflyIII\Models\UserGroup; +use FireflyIII\Rules\BelongsUserGroup; +use FireflyIII\Rules\IsBoolean; +use FireflyIII\Rules\IsDateOrTime; +use FireflyIII\Support\NullArrayObject; +use FireflyIII\Support\Request\AppendsLocationData; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use FireflyIII\User; +use FireflyIII\Validation\CurrencyValidation; +use FireflyIII\Validation\GroupValidation; +use FireflyIII\Validation\TransactionValidation; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; + +/** + * Class StoreRequest + * + * TODO this class is basically the same as the v1 request. However, it does not accept + * TODO models, objects and references that are NOT part of the designated user group (aka administration). + * TODO this distinction is already made in the CheckLogin trait, where there is also a convenient function + * TODO to grab the current UserGroup. This code is slightly different from other v2 apis that use + * TODO the "administration_id", those will have to be updated later on. + */ +class StoreRequest extends FormRequest +{ + protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; + use ChecksLogin; + use ConvertsDataTypes; + + use TransactionValidation; + use GroupValidation; + use CurrencyValidation; + use AppendsLocationData; + + /** + * Get all data. + * + * @return array + */ + public function getAll(): array + { + app('log')->debug('V2: Get all data in TransactionStoreRequest'); + + return [ + 'group_title' => $this->convertString('group_title'), + 'error_if_duplicate_hash' => $this->boolean('error_if_duplicate_hash'), + 'apply_rules' => $this->boolean('apply_rules', true), + 'fire_webhooks' => $this->boolean('fire_webhooks', true), + 'transactions' => $this->getTransactionData(), + ]; + // TODO include location and ability to process it. + } + + /** + * Get transaction data. + * + * @return array + */ + private function getTransactionData(): array + { + $return = []; + /** + * @var array $transaction + */ + foreach ($this->get('transactions') as $transaction) { + $object = new NullArrayObject($transaction); + $return[] = [ + 'type' => $this->clearString($object['type'], false), + 'date' => $this->dateFromValue($object['date']), + 'order' => $this->integerFromValue((string)$object['order']), + + 'currency_id' => $this->integerFromValue((string)$object['currency_id']), + 'currency_code' => $this->clearString((string)$object['currency_code'], false), + + // foreign currency info: + 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), + 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false), + + // amount and foreign amount. Cannot be 0. + 'amount' => $this->clearString((string)$object['amount'], false), + 'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false), + + // description. + 'description' => $this->clearString($object['description'], false), + + // source of transaction. If everything is null, assume cash account. + 'source_id' => $this->integerFromValue((string)$object['source_id']), + 'source_name' => $this->clearString((string)$object['source_name'], false), + 'source_iban' => $this->clearString((string)$object['source_iban'], false), + 'source_number' => $this->clearString((string)$object['source_number'], false), + 'source_bic' => $this->clearString((string)$object['source_bic'], false), + + // destination of transaction. If everything is null, assume cash account. + 'destination_id' => $this->integerFromValue((string)$object['destination_id']), + 'destination_name' => $this->clearString((string)$object['destination_name'], false), + 'destination_iban' => $this->clearString((string)$object['destination_iban'], false), + 'destination_number' => $this->clearString((string)$object['destination_number'], false), + 'destination_bic' => $this->clearString((string)$object['destination_bic'], false), + + // budget info + 'budget_id' => $this->integerFromValue((string)$object['budget_id']), + 'budget_name' => $this->clearString((string)$object['budget_name'], false), + + // category info + 'category_id' => $this->integerFromValue((string)$object['category_id']), + 'category_name' => $this->clearString((string)$object['category_name'], false), + + // journal bill reference. Optional. Will only work for withdrawals + 'bill_id' => $this->integerFromValue((string)$object['bill_id']), + 'bill_name' => $this->clearString((string)$object['bill_name'], false), + + // piggy bank reference. Optional. Will only work for transfers + 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), + 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false), + + // some other interesting properties + 'reconciled' => $this->convertBoolean((string)$object['reconciled']), + 'notes' => $this->clearString((string)$object['notes']), + 'tags' => $this->arrayFromValue($object['tags']), + + // all custom fields: + 'internal_reference' => $this->clearString((string)$object['internal_reference'], false), + 'external_id' => $this->clearString((string)$object['external_id'], false), + 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), + 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), + 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false), + 'external_url' => $this->clearString((string)$object['external_url'], false), + + 'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false), + 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false), + 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false), + 'sepa_db' => $this->clearString((string)$object['sepa_db'], false), + 'sepa_country' => $this->clearString((string)$object['sepa_country'], false), + 'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false), + 'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false), + 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false), + // custom date fields. Must be Carbon objects. Presence is optional. + 'interest_date' => $this->dateFromValue($object['interest_date']), + 'book_date' => $this->dateFromValue($object['book_date']), + 'process_date' => $this->dateFromValue($object['process_date']), + 'due_date' => $this->dateFromValue($object['due_date']), + 'payment_date' => $this->dateFromValue($object['payment_date']), + 'invoice_date' => $this->dateFromValue($object['invoice_date']), + + ]; + } + + return $return; + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + app('log')->debug('V2: Collect rules of TransactionStoreRequest'); + + // at this point the userGroup can't be NULL because the + // authorize() method will complain. Loudly. + /** @var UserGroup $userGroup */ + $userGroup = $this->getUserGroup(); + + return [ + // basic fields for group: + 'group_title' => 'between:1,1000|nullable', + 'error_if_duplicate_hash' => [new IsBoolean()], + 'apply_rules' => [new IsBoolean()], + + // transaction rules (in array for splits): + 'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation', + 'transactions.*.date' => ['required', new IsDateOrTime()], + 'transactions.*.order' => 'numeric|min:0', + + // currency info + 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable', + 'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable', + 'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable', + 'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable', + + // amount + 'transactions.*.amount' => 'required|numeric|gt:0', + 'transactions.*.foreign_amount' => 'numeric', + + // description + 'transactions.*.description' => 'nullable|between:1,1000', + + // source of transaction + 'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], + 'transactions.*.source_name' => 'between:1,255|nullable', + 'transactions.*.source_iban' => 'between:1,255|nullable|iban', + 'transactions.*.source_number' => 'between:1,255|nullable', + 'transactions.*.source_bic' => 'between:1,255|nullable|bic', + + // destination of transaction + 'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], + 'transactions.*.destination_name' => 'between:1,255|nullable', + 'transactions.*.destination_iban' => 'between:1,255|nullable|iban', + 'transactions.*.destination_number' => 'between:1,255|nullable', + 'transactions.*.destination_bic' => 'between:1,255|nullable|bic', + + // budget, category, bill and piggy + 'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)], + 'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], + 'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'], + 'transactions.*.category_name' => 'between:1,255|nullable', + 'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)], + 'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], + 'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)], + 'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], + + // other interesting fields + 'transactions.*.reconciled' => [new IsBoolean()], + 'transactions.*.notes' => 'min:1|max:50000|nullable', + 'transactions.*.tags' => 'between:0,255', + + // meta info fields + 'transactions.*.internal_reference' => 'min:1|max:255|nullable', + 'transactions.*.external_id' => 'min:1|max:255|nullable', + 'transactions.*.recurrence_id' => 'min:1|max:255|nullable', + 'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable', + 'transactions.*.external_url' => 'min:1|max:255|nullable|url', + + // SEPA fields: + 'transactions.*.sepa_cc' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable', + 'transactions.*.sepa_db' => 'min:1|max:255|nullable', + 'transactions.*.sepa_country' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ep' => 'min:1|max:255|nullable', + 'transactions.*.sepa_ci' => 'min:1|max:255|nullable', + 'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable', + + // dates + 'transactions.*.interest_date' => 'date|nullable', + 'transactions.*.book_date' => 'date|nullable', + 'transactions.*.process_date' => 'date|nullable', + 'transactions.*.due_date' => 'date|nullable', + 'transactions.*.payment_date' => 'date|nullable', + 'transactions.*.invoice_date' => 'date|nullable', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + /** @var User $user */ + $user = auth()->user(); + /** @var UserGroup $userGroup */ + $userGroup = $this->getUserGroup(); + $validator->after( + function (Validator $validator) use ($user, $userGroup) { + // must be valid array. + $this->validateTransactionArray($validator); // does not need group validation. + + // must submit at least one transaction. + app('log')->debug('Now going to validateOneTransaction'); + $this->validateOneTransaction($validator); // does not need group validation. + app('log')->debug('Now done with validateOneTransaction'); + + // all journals must have a description + $this->validateDescriptions($validator); // does not need group validation. + + // all transaction types must be equal: + $this->validateTransactionTypes($validator); // does not need group validation. + + // validate foreign currency info + $this->validateForeignCurrencyInformation($validator); // does not need group validation. + + // validate all account info + $this->validateAccountInformation($validator, $user, $userGroup); + + // validate source/destination is equal, depending on the transaction journal type. + $this->validateEqualAccounts($validator); + + // the group must have a description if > 1 journal. + $this->validateGroupDescription($validator); + } + ); + } + + +} diff --git a/app/Factory/TransactionGroupFactory.php b/app/Factory/TransactionGroupFactory.php index 7f677f865f..9304a24650 100644 --- a/app/Factory/TransactionGroupFactory.php +++ b/app/Factory/TransactionGroupFactory.php @@ -81,7 +81,7 @@ class TransactionGroupFactory $group = new TransactionGroup(); $group->user()->associate($this->user); - $group->userGroup()->associate($this->user->userGroup); + $group->userGroup()->associate($data['user_group'] ?? $this->user->userGroup); $group->title = $title; $group->save(); diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index ac0024bf69..d1d117f1a0 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -33,7 +33,7 @@ class TransactionObserver public function deleting(Transaction $transaction): void { app('log')->debug('Observe "deleting" of a transaction.'); - $transaction->transactionJournal->delete(); + $transaction?->transactionJournal?->delete(); } } diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index e98ae422b5..d7401b48d3 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -29,7 +29,7 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Http\Api\ExchangeRateConverter; @@ -245,6 +245,7 @@ class NetWorth implements NetWorthInterface { $this->userGroup = $userGroup; $this->adminAccountRepository = app(AdminAccountRepositoryInterface::class); + die('uses old administration ID check, needs to be updated.A'); $this->adminAccountRepository->setAdministrationId($userGroup->id); } diff --git a/app/Providers/AccountServiceProvider.php b/app/Providers/AccountServiceProvider.php index b88c2befa5..f8b58165e5 100644 --- a/app/Providers/AccountServiceProvider.php +++ b/app/Providers/AccountServiceProvider.php @@ -29,8 +29,8 @@ use FireflyIII\Repositories\Account\AccountTasker; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Account\OperationsRepository; use FireflyIII\Repositories\Account\OperationsRepositoryInterface; -use FireflyIII\Repositories\Administration\Account\AccountRepository as AdminAccountRepository; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepository as AdminAccountRepository; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -42,9 +42,7 @@ class AccountServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -84,7 +82,6 @@ class AccountServiceProvider extends ServiceProvider // phpstan thinks auth does not exist. if ($app->auth->check()) { // @phpstan-ignore-line $repository->setUser(auth()->user()); - $repository->setAdministrationId((int)auth()->user()->user_group_id); } return $repository; diff --git a/app/Providers/BillServiceProvider.php b/app/Providers/BillServiceProvider.php index 57ac73960f..cefec28cf9 100644 --- a/app/Providers/BillServiceProvider.php +++ b/app/Providers/BillServiceProvider.php @@ -25,8 +25,8 @@ namespace FireflyIII\Providers; use FireflyIII\Repositories\Bill\BillRepository; use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use FireflyIII\Repositories\Administration\Bill\BillRepository as AdminBillRepository; -use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface as AdminBillRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Bill\BillRepository as AdminBillRepository; +use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface as AdminBillRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -38,9 +38,7 @@ class BillServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. diff --git a/app/Providers/BudgetServiceProvider.php b/app/Providers/BudgetServiceProvider.php index de223bdd14..7babaddc10 100644 --- a/app/Providers/BudgetServiceProvider.php +++ b/app/Providers/BudgetServiceProvider.php @@ -25,20 +25,20 @@ namespace FireflyIII\Providers; use FireflyIII\Repositories\Budget\AvailableBudgetRepository; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\AvailableBudgetRepository as AdminAbRepository; -use FireflyIII\Repositories\Administration\Budget\AvailableBudgetRepositoryInterface as AdminAbRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\AvailableBudgetRepository as AdminAbRepository; +use FireflyIII\Repositories\UserGroups\Budget\AvailableBudgetRepositoryInterface as AdminAbRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepository; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepository; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\BudgetRepository as AdminBudgetRepository; -use FireflyIII\Repositories\Administration\Budget\BudgetRepositoryInterface as AdminBudgetRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\BudgetRepository as AdminBudgetRepository; +use FireflyIII\Repositories\UserGroups\Budget\BudgetRepositoryInterface as AdminBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\NoBudgetRepository; use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepository; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Repositories\Administration\Budget\OperationsRepository as AdminOperationsRepository; -use FireflyIII\Repositories\Administration\Budget\OperationsRepositoryInterface as AdminOperationsRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Budget\OperationsRepository as AdminOperationsRepository; +use FireflyIII\Repositories\UserGroups\Budget\OperationsRepositoryInterface as AdminOperationsRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -50,9 +50,7 @@ class BudgetServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -80,6 +78,7 @@ class BudgetServiceProvider extends ServiceProvider $repository = app(AdminBudgetRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line $repository->setUser(auth()->user()); + die('uses old administration ID check, needs to be updated.C'); $repository->setAdministrationId(auth()->user()->user_group_id); } @@ -109,6 +108,7 @@ class BudgetServiceProvider extends ServiceProvider $repository = app(AdminAbRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line $repository->setUser(auth()->user()); + die('uses old administration ID check, needs to be updated.D'); $repository->setAdministrationId(auth()->user()->user_group_id); } @@ -164,6 +164,7 @@ class BudgetServiceProvider extends ServiceProvider $repository = app(AdminOperationsRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line $repository->setUser(auth()->user()); + die('uses old administration ID check, needs to be updated.E'); $repository->setAdministrationId(auth()->user()->user_group_id); } diff --git a/app/Providers/PiggyBankServiceProvider.php b/app/Providers/PiggyBankServiceProvider.php index f06b1ab55a..66a244596b 100644 --- a/app/Providers/PiggyBankServiceProvider.php +++ b/app/Providers/PiggyBankServiceProvider.php @@ -26,8 +26,8 @@ namespace FireflyIII\Providers; use FireflyIII\Repositories\PiggyBank\PiggyBankRepository; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\Repositories\Administration\PiggyBank\PiggyBankRepository as AdminPiggyBankRepository; -use FireflyIII\Repositories\Administration\PiggyBank\PiggyBankRepositoryInterface as AdminPiggyBankRepositoryInterface; +use FireflyIII\Repositories\UserGroups\PiggyBank\PiggyBankRepository as AdminPiggyBankRepository; +use FireflyIII\Repositories\UserGroups\PiggyBank\PiggyBankRepositoryInterface as AdminPiggyBankRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -40,9 +40,7 @@ class PiggyBankServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. diff --git a/app/Repositories/Administration/Account/AccountRepository.php b/app/Repositories/UserGroups/Account/AccountRepository.php similarity index 97% rename from app/Repositories/Administration/Account/AccountRepository.php rename to app/Repositories/UserGroups/Account/AccountRepository.php index 220747f663..9b7acaf693 100644 --- a/app/Repositories/Administration/Account/AccountRepository.php +++ b/app/Repositories/UserGroups/Account/AccountRepository.php @@ -23,13 +23,13 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Account; +namespace FireflyIII\Repositories\UserGroups\Account; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; @@ -38,7 +38,7 @@ use Illuminate\Support\Collection; */ class AccountRepository implements AccountRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * @param Account $account diff --git a/app/Repositories/Administration/Account/AccountRepositoryInterface.php b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php similarity index 97% rename from app/Repositories/Administration/Account/AccountRepositoryInterface.php rename to app/Repositories/UserGroups/Account/AccountRepositoryInterface.php index 678f6f4595..2e3bbc6c1c 100644 --- a/app/Repositories/Administration/Account/AccountRepositoryInterface.php +++ b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Account; +namespace FireflyIII\Repositories\UserGroups\Account; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionCurrency; diff --git a/app/Repositories/Administration/Bill/BillRepository.php b/app/Repositories/UserGroups/Bill/BillRepository.php similarity index 98% rename from app/Repositories/Administration/Bill/BillRepository.php rename to app/Repositories/UserGroups/Bill/BillRepository.php index 2ec273612a..94c985eef0 100644 --- a/app/Repositories/Administration/Bill/BillRepository.php +++ b/app/Repositories/UserGroups/Bill/BillRepository.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Bill; +namespace FireflyIII\Repositories\UserGroups\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; @@ -31,7 +31,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; /** @@ -39,7 +39,7 @@ use Illuminate\Support\Collection; */ class BillRepository implements BillRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * Correct order of piggies in case of issues. diff --git a/app/Repositories/Administration/Bill/BillRepositoryInterface.php b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php similarity index 97% rename from app/Repositories/Administration/Bill/BillRepositoryInterface.php rename to app/Repositories/UserGroups/Bill/BillRepositoryInterface.php index 8d26233f37..ba68704938 100644 --- a/app/Repositories/Administration/Bill/BillRepositoryInterface.php +++ b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Bill; +namespace FireflyIII\Repositories\UserGroups\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; diff --git a/app/Repositories/Administration/Budget/AvailableBudgetRepository.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php similarity index 95% rename from app/Repositories/Administration/Budget/AvailableBudgetRepository.php rename to app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php index 5f6de79554..29748532eb 100644 --- a/app/Repositories/Administration/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php @@ -23,19 +23,19 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; use FireflyIII\Models\AvailableBudget; use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; /** * Class AvailableBudgetRepository */ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * @param Carbon $start diff --git a/app/Repositories/Administration/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php similarity index 95% rename from app/Repositories/Administration/Budget/AvailableBudgetRepositoryInterface.php rename to app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php index cefcdd6305..6a24ad24da 100644 --- a/app/Repositories/Administration/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; diff --git a/app/Repositories/Administration/Budget/BudgetRepository.php b/app/Repositories/UserGroups/Budget/BudgetRepository.php similarity index 89% rename from app/Repositories/Administration/Budget/BudgetRepository.php rename to app/Repositories/UserGroups/Budget/BudgetRepository.php index 6955062846..57e054d9dd 100644 --- a/app/Repositories/Administration/Budget/BudgetRepository.php +++ b/app/Repositories/UserGroups/Budget/BudgetRepository.php @@ -23,9 +23,9 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; /** @@ -33,7 +33,7 @@ use Illuminate\Support\Collection; */ class BudgetRepository implements BudgetRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * @inheritDoc diff --git a/app/Repositories/Administration/Budget/BudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php similarity index 94% rename from app/Repositories/Administration/Budget/BudgetRepositoryInterface.php rename to app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php index 1d79f248c3..6d9ef85332 100644 --- a/app/Repositories/Administration/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; use Illuminate\Support\Collection; diff --git a/app/Repositories/Administration/Budget/OperationsRepository.php b/app/Repositories/UserGroups/Budget/OperationsRepository.php similarity index 96% rename from app/Repositories/Administration/Budget/OperationsRepository.php rename to app/Repositories/UserGroups/Budget/OperationsRepository.php index 04b6e6d497..90423858a1 100644 --- a/app/Repositories/Administration/Budget/OperationsRepository.php +++ b/app/Repositories/UserGroups/Budget/OperationsRepository.php @@ -23,13 +23,13 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; /** @@ -37,7 +37,7 @@ use Illuminate\Support\Collection; */ class OperationsRepository implements OperationsRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * @inheritDoc @@ -132,6 +132,7 @@ class OperationsRepository implements OperationsRepositoryInterface { /** @var BudgetRepositoryInterface $repos */ $repos = app(BudgetRepositoryInterface::class); + die('uses old administration ID check, needs to be updated.F'); $repos->setAdministrationId($this->getAdministrationId()); return $repos->getActiveBudgets(); diff --git a/app/Repositories/Administration/Budget/OperationsRepositoryInterface.php b/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php similarity index 96% rename from app/Repositories/Administration/Budget/OperationsRepositoryInterface.php rename to app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php index a4832d3354..d6313824dc 100644 --- a/app/Repositories/Administration/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\Budget; +namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; use Illuminate\Support\Collection; diff --git a/app/Repositories/Administration/PiggyBank/PiggyBankRepository.php b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php similarity index 90% rename from app/Repositories/Administration/PiggyBank/PiggyBankRepository.php rename to app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php index ed52deea58..e6b80198b7 100644 --- a/app/Repositories/Administration/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php @@ -23,9 +23,9 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\PiggyBank; +namespace FireflyIII\Repositories\UserGroups\PiggyBank; -use FireflyIII\Support\Repositories\Administration\AdministrationTrait; +use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; /** @@ -33,7 +33,7 @@ use Illuminate\Support\Collection; */ class PiggyBankRepository implements PiggyBankRepositoryInterface { - use AdministrationTrait; + use UserGroupTrait; /** * @inheritDoc diff --git a/app/Repositories/Administration/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php similarity index 94% rename from app/Repositories/Administration/PiggyBank/PiggyBankRepositoryInterface.php rename to app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php index 10fae5bbcb..8a02a8e84d 100644 --- a/app/Repositories/Administration/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Repositories\Administration\PiggyBank; +namespace FireflyIII\Repositories\UserGroups\PiggyBank; use Illuminate\Support\Collection; diff --git a/app/Rules/BelongsUser.php b/app/Rules/BelongsUser.php index ec3cb365c5..296dc580b8 100644 --- a/app/Rules/BelongsUser.php +++ b/app/Rules/BelongsUser.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -31,54 +32,28 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; -use Illuminate\Contracts\Validation\Rule; +use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Log; /** * Class BelongsUser */ -class BelongsUser implements Rule +class BelongsUser implements ValidationRule { /** - * Create a new rule instance. - * - * @return void + * @inheritDoc */ - public function __construct() - { - // - } - - /** - * Get the validation error message. - * - * @return string - */ - public function message(): string - { - return (string)trans('validation.belongs_user'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - * @throws FireflyException - * - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $attribute = $this->parseAttribute($attribute); if (!auth()->check()) { - return true; + $fail('validation.belongs_user')->translate(); + return; } $attribute = (string)$attribute; Log::debug(sprintf('Going to validate %s', $attribute)); - return match ($attribute) { + $result = match ($attribute) { 'piggy_bank_id' => $this->validatePiggyBankId((int)$value), 'piggy_bank_name' => $this->validatePiggyBankName($value), 'bill_id' => $this->validateBillId((int)$value), @@ -88,8 +63,11 @@ class BelongsUser implements Rule 'category_id' => $this->validateCategoryId((int)$value), 'budget_name' => $this->validateBudgetName($value), 'source_id', 'destination_id' => $this->validateAccountId((int)$value), - default => throw new FireflyException(sprintf('Rule BelongUser cannot handle "%s"', $attribute)), + default => throw new FireflyException(sprintf('Rule BelongsUser cannot handle "%s"', $attribute)), }; + if (false === $result) { + $fail('validation.belongs_user')->translate(); + } } /** diff --git a/app/Rules/BelongsUserGroup.php b/app/Rules/BelongsUserGroup.php new file mode 100644 index 0000000000..7fc37fa978 --- /dev/null +++ b/app/Rules/BelongsUserGroup.php @@ -0,0 +1,262 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Rules; + +use Closure; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\UserGroup; +use Illuminate\Contracts\Validation\ValidationRule; +use Illuminate\Support\Facades\Log; + +/** + * Class BelongsUserGroup + * TODO this method has a lot in common with BelongsUser but will check if the UserGroup + * TODO that is submitted is valid. This method will not validate if the user has a valid ROLE in this + * TODO group. + */ +class BelongsUserGroup implements ValidationRule +{ + private UserGroup $userGroup; + + /** + * Create a new rule instance. + * + * @return void + */ + public function __construct(UserGroup $userGroup) + { + $this->userGroup = $userGroup; + } + + /** + * @inheritDoc + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $attribute = $this->parseAttribute($attribute); + if (!auth()->check()) { + $fail('validation.belongs_user')->translate(); + return; + } + $attribute = (string)$attribute; + Log::debug(sprintf('Going to validate %s', $attribute)); + + $result = match ($attribute) { + 'piggy_bank_id' => $this->validatePiggyBankId((int)$value), + 'piggy_bank_name' => $this->validatePiggyBankName($value), + 'bill_id' => $this->validateBillId((int)$value), + 'transaction_journal_id' => $this->validateJournalId((int)$value), + 'bill_name' => $this->validateBillName($value), + 'budget_id' => $this->validateBudgetId((int)$value), + 'category_id' => $this->validateCategoryId((int)$value), + 'budget_name' => $this->validateBudgetName($value), + 'source_id', 'destination_id' => $this->validateAccountId((int)$value), + default => throw new FireflyException(sprintf('Rule BelongsUser cannot handle "%s"', $attribute)), + }; + if (false === $result) { + $fail('validation.belongs_user_or_user_group')->translate(); + } + } + + /** + * @param string $attribute + * + * @return string + */ + private function parseAttribute(string $attribute): string + { + $parts = explode('.', $attribute); + if (1 === count($parts)) { + return $attribute; + } + if (3 === count($parts)) { + return $parts[2]; + } + + return $attribute; + } + + /** + * @param int $value + * + * @return bool + */ + private function validatePiggyBankId(int $value): bool + { + $count = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('piggy_banks.id', '=', $value) + ->where('accounts.user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } + + /** + * @param string $value + * + * @return bool + */ + private function validatePiggyBankName(string $value): bool + { + $count = $this->countField(PiggyBank::class, 'name', $value); + + return 1 === $count; + } + + /** + * @param string $class + * @param string $field + * @param string $value + * + * @return int + * + */ + protected function countField(string $class, string $field, string $value): int + { + $value = trim($value); + $objects = []; + // get all objects belonging to user: + if (PiggyBank::class === $class) { + $objects = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') + ->where('accounts.user_group_id', '=', $this->userGroup->id)->get(['piggy_banks.*']); + } + if (PiggyBank::class !== $class) { + $objects = $class::where('user_group_id', '=', $this->userGroup->id)->get(); + } + $count = 0; + foreach ($objects as $object) { + $objectValue = trim((string)$object->$field); + app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); + if ($objectValue === $value) { + $count++; + app('log')->debug(sprintf('Hit! Count is now %d', $count)); + } + } + + return $count; + } + + /** + * @param int $value + * + * @return bool + */ + private function validateBillId(int $value): bool + { + if (0 === $value) { + return true; + } + $count = Bill::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } + + /** + * @param int $value + * + * @return bool + */ + private function validateJournalId(int $value): bool + { + if (0 === $value) { + return true; + } + $count = TransactionJournal::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } + + /** + * @param string $value + * + * @return bool + */ + private function validateBillName(string $value): bool + { + $count = $this->countField(Bill::class, 'name', $value); + app('log')->debug(sprintf('Result of countField for bill name "%s" is %d', $value, $count)); + + return 1 === $count; + } + + /** + * @param int $value + * + * @return bool + */ + private function validateBudgetId(int $value): bool + { + if (0 === $value) { + return true; + } + $count = Budget::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } + + /** + * @param int $value + * + * @return bool + */ + private function validateCategoryId(int $value): bool + { + $count = Category::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } + + /** + * @param string $value + * + * @return bool + */ + private function validateBudgetName(string $value): bool + { + $count = $this->countField(Budget::class, 'name', $value); + + return 1 === $count; + } + + /** + * @param int $value + * + * @return bool + */ + private function validateAccountId(int $value): bool + { + if (0 === $value) { + // its ok to submit 0. other checks will fail. + return true; + } + $count = Account::where('id', '=', $value)->where('user_group_id', '=', $this->userGroup->id)->count(); + + return 1 === $count; + } +} diff --git a/app/Support/Repositories/Administration/AdministrationTrait.php b/app/Support/Repositories/Administration/AdministrationTrait.php deleted file mode 100644 index 533beec861..0000000000 --- a/app/Support/Repositories/Administration/AdministrationTrait.php +++ /dev/null @@ -1,95 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Support\Repositories\Administration; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\GroupMembership; -use FireflyIII\Models\UserGroup; -use FireflyIII\User; -use Illuminate\Contracts\Auth\Authenticatable; - -/** - * Trait AdministrationTrait - */ -trait AdministrationTrait -{ - protected ?int $administrationId = null; - protected User $user; - protected ?UserGroup $userGroup = null; - - /** - * @return int - */ - public function getAdministrationId(): int - { - return $this->administrationId; - } - - /** - * @param int $administrationId - * - * @throws FireflyException - */ - public function setAdministrationId(int $administrationId): void - { - $this->administrationId = $administrationId; - $this->refreshAdministration(); - } - - /** - * @return void - * @throws FireflyException - */ - private function refreshAdministration(): void - { - if (null !== $this->administrationId) { - $memberships = GroupMembership::where('user_id', $this->user->id) - ->where('user_group_id', $this->administrationId) - ->count(); - if (0 === $memberships) { - throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $this->administrationId)); - } - $this->userGroup = UserGroup::find($this->administrationId); - if (null === $this->userGroup) { - throw new FireflyException(sprintf('Unfound administration for user #%d', $this->user->id)); - } - return; - } - throw new FireflyException(sprintf('Cannot validate administration for user #%d', $this->user->id)); - } - - /** - * @param Authenticatable|User|null $user - * - * @return void - */ - public function setUser(Authenticatable|User|null $user): void - { - if (null !== $user) { - $this->user = $user; - } - } -} diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php new file mode 100644 index 0000000000..8f54ece7fb --- /dev/null +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -0,0 +1,85 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Repositories\UserGroup; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\GroupMembership; +use FireflyIII\Models\UserGroup; +use FireflyIII\User; +use Illuminate\Contracts\Auth\Authenticatable; + +/** + * Trait UserGroupTrait + */ +trait UserGroupTrait +{ + protected User $user; + protected UserGroup $userGroup; + + /** + * @param Authenticatable|User|null $user + * + * @return void + */ + public function setUser(Authenticatable | User | null $user): void + { + if (null !== $user) { + $this->user = $user; + $this->userGroup = $user->userGroup; + } + } + + /** + * TODO This method does not check if the user has access to this particular user group. + * + * @param UserGroup $userGroup + * + * @return void + */ + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + /** + * @param int $userGroupId + * + * @throws FireflyException + */ + public function setUserGroupById(int $userGroupId): void + { + $memberships = GroupMembership::where('user_id', $this->user->id) + ->where('user_group_id', $userGroupId) + ->count(); + if (0 === $memberships) { + throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId)); + } + $this->userGroup = UserGroup::find($userGroupId); + if (null === $this->userGroup) { + throw new FireflyException(sprintf('Unfound administration for user #%d', $this->user->id)); + } + } +} diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index 2839ceea86..e0d236f993 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -27,7 +27,6 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\UserGroup; use FireflyIII\User; use Illuminate\Support\Facades\Log; -use ValueError; /** * Trait ChecksLogin @@ -51,20 +50,51 @@ trait ChecksLogin app('log')->debug('Request class has no acceptedRoles array'); return true; // check for false already took place. } - /** @var UserGroup $userGroup */ - $userGroup = $this->route()->parameter('userGroup'); - if (null === $userGroup) { - app('log')->debug('Request class has no userGroup parameter.'); - return true; - } /** @var User $user */ - $user = auth()->user(); + $user = auth()->user(); + $userGroup = $this->getUserGroup(); + if (null === $userGroup) { + app('log')->error('User has no valid user group submitted or otherwise.'); + return false; + } + /** @var UserRoleEnum $role */ foreach ($this->acceptedRoles as $role) { - if ($user->hasRoleInGroup($userGroup, $role, true, true)) { + // system owner cannot overrule this, MUST be member of the group. + if ($user->hasRoleInGroup($userGroup, $role, true, false)) { return true; } } return false; } + + /** + * Return the user group or NULL if none is set. + * Will throw exception if invalid. + * + * @return UserGroup|null + */ + public function getUserGroup(): ?UserGroup + { + /** @var User $user */ + $user = auth()->user(); + app('log')->debug('Now in getUserGroup()'); + /** @var UserGroup $userGroup */ + $userGroup = $this->route()->parameter('userGroup'); + if (null === $userGroup) { + app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.'); + $userGroupId = (int)$this->get('user_group_id'); + if (0 === $userGroupId) { + app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id)); + $userGroupId = (int)$user->user_group_id; + } + $userGroup = UserGroup::find($userGroupId); + if (null === $userGroup) { + app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); + return null; + } + app('log')->debug('Request class has valid user_group_id.'); + } + return $userGroup; + } } diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index b549f2e545..a08a223d2f 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -26,7 +26,7 @@ namespace FireflyIII\Support\Request; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; -use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -171,6 +171,7 @@ trait ConvertsDataTypes // set administration ID // group ID $administrationId = auth()->user()->getAdministrationId(); + die('uses old administration ID check, needs to be updated.G'); $repository->setAdministrationId($administrationId); $set = $this->get('accounts'); diff --git a/app/Validation/Account/DepositValidation.php b/app/Validation/Account/DepositValidation.php index f1bfb62185..4e306e18f5 100644 --- a/app/Validation/Account/DepositValidation.php +++ b/app/Validation/Account/DepositValidation.php @@ -139,7 +139,7 @@ trait DepositValidation // if the user submits an ID, but that ID is not of the correct type, // return false. if (null !== $accountId) { - $search = $this->accountRepository->find($accountId); + $search = $this->getRepository()->find($accountId); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); Log::debug(sprintf('Firefly III accepts ID #%d as valid account data.', $accountId)); @@ -153,7 +153,7 @@ trait DepositValidation // if user submits an IBAN: if (null !== $accountIban) { - $search = $this->accountRepository->findByIbanNull($accountIban, $validTypes); + $search = $this->getRepository()->findByIbanNull($accountIban, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { Log::debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type)); $result = false; @@ -167,7 +167,7 @@ trait DepositValidation // if user submits a number: if (null !== $accountNumber && '' !== $accountNumber) { - $search = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes); + $search = $this->getRepository()->findByAccountNumber($accountNumber, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { Log::debug( sprintf('User submitted number ("%s"), which is a "%s", so this is not a valid source.', $accountNumber, $search->accountType->type) diff --git a/app/Validation/Account/OBValidation.php b/app/Validation/Account/OBValidation.php index c52fe68407..c77d65d4da 100644 --- a/app/Validation/Account/OBValidation.php +++ b/app/Validation/Account/OBValidation.php @@ -115,7 +115,7 @@ trait OBValidation // return false. if (null !== $accountId && null === $accountName) { Log::debug('Source ID is not null, but name is null.'); - $search = $this->accountRepository->find($accountId); + $search = $this->getRepository()->find($accountId); // the source resulted in an account, but it's not of a valid type. if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { diff --git a/app/Validation/Account/WithdrawalValidation.php b/app/Validation/Account/WithdrawalValidation.php index e8343e44c0..e7ad03e8e5 100644 --- a/app/Validation/Account/WithdrawalValidation.php +++ b/app/Validation/Account/WithdrawalValidation.php @@ -108,7 +108,7 @@ trait WithdrawalValidation // if there's an ID it must be of the "validTypes". if (null !== $accountId && 0 !== $accountId) { - $found = $this->accountRepository->find($accountId); + $found = $this->getRepository()->find($accountId); if (null !== $found) { $type = $found->accountType->type; if (in_array($type, $validTypes, true)) { diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index 89e65455f3..080665e403 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -26,9 +26,10 @@ namespace FireflyIII\Validation; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionType; +use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as UserGroupAccountRepositoryInterface; use FireflyIII\User; -use FireflyIII\Validation\Account\AccountValidatorProperties; use FireflyIII\Validation\Account\DepositValidation; use FireflyIII\Validation\Account\LiabilityValidation; use FireflyIII\Validation\Account\OBValidation; @@ -42,7 +43,6 @@ use Illuminate\Support\Facades\Log; */ class AccountValidator { - use AccountValidatorProperties; use WithdrawalValidation; use DepositValidation; use TransferValidation; @@ -50,28 +50,32 @@ class AccountValidator use OBValidation; use LiabilityValidation; - public bool $createMode; - public string $destError; - public ?Account $destination; - public ?Account $source; - public string $sourceError; - private AccountRepositoryInterface $accountRepository; - private array $combinations; - private string $transactionType; - private User $user; + public bool $createMode; + public string $destError; + public ?Account $destination; + public ?Account $source; + public string $sourceError; + private AccountRepositoryInterface $accountRepository; + private array $combinations; + private string $transactionType; + private bool $useUserGroupRepository = false; + private User $user; + private UserGroup $userGroup; + private UserGroupAccountRepositoryInterface $userGroupAccountRepository; /** * AccountValidator constructor. */ public function __construct() { - $this->createMode = false; - $this->destError = 'No error yet.'; - $this->sourceError = 'No error yet.'; - $this->combinations = config('firefly.source_dests'); - $this->source = null; - $this->destination = null; - $this->accountRepository = app(AccountRepositoryInterface::class); + $this->createMode = false; + $this->destError = 'No error yet.'; + $this->sourceError = 'No error yet.'; + $this->combinations = config('firefly.source_dests'); + $this->source = null; + $this->destination = null; + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->userGroupAccountRepository = app(UserGroupAccountRepositoryInterface::class); } /** @@ -126,6 +130,19 @@ class AccountValidator { $this->user = $user; $this->accountRepository->setUser($user); + $this->useUserGroupRepository = false; + } + + /** + * @param UserGroup $userGroup + * + * @return void + */ + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + $this->userGroupAccountRepository->setUserGroup($userGroup); + $this->useUserGroupRepository = true; } /** @@ -265,7 +282,7 @@ class AccountValidator // find by ID if (null !== $accountId && $accountId > 0) { - $first = $this->accountRepository->find($accountId); + $first = $this->getRepository()->find($accountId); $accountType = null === $first ? 'invalid' : $first->accountType->type; $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. @@ -277,7 +294,7 @@ class AccountValidator // find by iban if (null !== $accountIban && '' !== (string)$accountIban) { - $first = $this->accountRepository->findByIbanNull($accountIban, $validTypes); + $first = $this->getRepository()->findByIbanNull($accountIban, $validTypes); $accountType = null === $first ? 'invalid' : $first->accountType->type; $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. @@ -289,7 +306,7 @@ class AccountValidator // find by number if (null !== $accountNumber && '' !== (string)$accountNumber) { - $first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes); + $first = $this->getRepository()->findByAccountNumber($accountNumber, $validTypes); $accountType = null === $first ? 'invalid' : $first->accountType->type; $check = in_array($accountType, $validTypes, true); $check = $inverse ? !$check : $check; // reverse the validation check if necessary. @@ -301,7 +318,7 @@ class AccountValidator // find by name: if ('' !== (string)$accountName) { - $first = $this->accountRepository->findByName($accountName, $validTypes); + $first = $this->getRepository()->findByName($accountName, $validTypes); if (null !== $first) { app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban')); return $first; @@ -311,4 +328,16 @@ class AccountValidator return null; } + + /** + * @return AccountRepositoryInterface|UserGroupAccountRepositoryInterface + */ + private function getRepository(): AccountRepositoryInterface | UserGroupAccountRepositoryInterface + { + if ($this->useUserGroupRepository) { + return $this->userGroupAccountRepository; + } + + return $this->accountRepository; + } } diff --git a/app/Validation/Administration/ValidatesAdministrationAccess.php b/app/Validation/Administration/ValidatesAdministrationAccess.php index f75aad5af7..7108bee116 100644 --- a/app/Validation/Administration/ValidatesAdministrationAccess.php +++ b/app/Validation/Administration/ValidatesAdministrationAccess.php @@ -27,7 +27,6 @@ namespace FireflyIII\Validation\Administration; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\UserRole; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Auth\AuthenticationException; diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 02e6b4ef5f..6124ab59d9 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -47,7 +47,6 @@ use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; use ValueError; - use function is_string; /** diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index 54ce6d14b3..82a2cd246a 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -30,7 +30,9 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\User; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; @@ -42,17 +44,20 @@ trait TransactionValidation /** * Validates the given account information. Switches on given transaction type. * - * @param Validator $validator + * Inclusion of user and/or group is optional. + * + * @param Validator $validator + * @param User|null $user + * @param UserGroup|null $userGroup */ - public function validateAccountInformation(Validator $validator): void + public function validateAccountInformation(Validator $validator, User $user = null, UserGroup $userGroup = null): void { if ($validator->errors()->count() > 0) { return; } Log::debug('Now in validateAccountInformation (TransactionValidation) ()'); - $transactions = $this->getTransactionsArray($validator); - $data = $validator->getData(); - + $transactions = $this->getTransactionsArray($validator); + $data = $validator->getData(); $transactionType = $data['type'] ?? 'invalid'; Log::debug(sprintf('Going to loop %d transaction(s)', count($transactions))); @@ -61,6 +66,8 @@ trait TransactionValidation * @var array $transaction */ foreach ($transactions as $index => $transaction) { + $transaction['user'] = $user; + $transaction['user_group'] = $userGroup; if (!is_int($index)) { continue; } @@ -107,6 +114,13 @@ trait TransactionValidation /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); + if (array_key_exists('user', $transaction) && null !== $transaction['user']) { + $accountValidator->setUser($transaction['user']); + } + if (array_key_exists('user_group', $transaction) && null !== $transaction['user_group']) { + $accountValidator->setUserGroup($transaction['user_group']); + } + $transactionType = $transaction['type'] ?? $transactionType; $accountValidator->setTransactionType($transactionType); @@ -204,7 +218,8 @@ trait TransactionValidation array $transaction, string $transactionType, int $index - ): void { + ): void + { Log::debug('Now in sanityCheckForeignCurrency()'); if (0 !== $validator->errors()->count()) { Log::debug('Already have errors, return'); diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 242b83aa0e..595547aaf2 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -50,6 +50,7 @@ return [ 'invalid_transaction_type' => 'Invalid transaction type.', 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'This value is invalid for this field.', + 'belongs_user_or_user_group' => 'This value is invalid for this field.', 'at_least_one_transaction' => 'Need at least one transaction.', 'recurring_transaction_id' => 'Need at least one transaction.', 'need_id_to_match' => 'You need to submit this entry with an ID for the API to be able to match it.',