diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index 5ae356250a..fbdfdfce4b 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -7,14 +7,14 @@ parameters: ignoreErrors: # TODO: slowly remove these exceptions and fix the issues found. - '#Dynamic call to static method#' # all the Laravel ORM things depend on this. - - '#Control structures using switch should not be used.#' # switch is fine insome cases. + - '#Control structures using switch should not be used.#' # switch is fine in some cases. - '#with no value type specified in iterable type array#' # remove this rule when all other issues are solved. - '#has no value type specified in iterable type array#' # remove this rule when all other issues are solved. - '#is not allowed to extend#' - '#switch is forbidden to use#' - '#is neither abstract nor final#' - - '#has a nullable return type declaration#' - - '#with a nullable type declaration#' + - '#has a nullable return type declaration#' # perhaps throw errors instead? + - '#with a nullable type declaration#' # decide what action should be if param is null. - '#with null as default value#' - message: '#Constructor in [a-zA-Z0-9\\_]+ has parameter \$[a-zA-Z0-9\\_]+ with default value#' @@ -56,5 +56,5 @@ parameters: # The level 8 is the highest level. original was 5 # TODO: slowly up the level and fix the issues found. - level: 6 + level: 7 diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index f724df374e..b23746ab9b 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -201,7 +201,7 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface { $query = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->groupBy('transaction_journals.id'); - $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); // @phpstan-ignore-line + $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); /** @phpstan-ignore-line */ $journalIds = []; /** @var stdClass $row */ foreach ($result as $row) { diff --git a/app/Repositories/UserGroups/Account/AccountRepository.php b/app/Repositories/UserGroups/Account/AccountRepository.php index 770d8a0481..287b4a977f 100644 --- a/app/Repositories/UserGroups/Account/AccountRepository.php +++ b/app/Repositories/UserGroups/Account/AccountRepository.php @@ -30,6 +30,7 @@ use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Support\Collection; /** @@ -65,6 +66,31 @@ class AccountRepository implements AccountRepositoryInterface return $account; } + /** + * @inheritDoc + */ + public function findByAccountNumber(string $number, array $types): ?Account + { + $dbQuery = $this->userGroup + ->accounts() + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('accounts.active', true) + ->where( + static function (EloquentBuilder $q1) use ($number) { /** @phpstan-ignore-line */ + $json = json_encode($number); + $q1->where('account_meta.name', '=', 'account_number'); + $q1->where('account_meta.data', '=', $json); + } + ); + + if (0 !== count($types)) { + $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $dbQuery->whereIn('account_types.type', $types); + } + /** @var Account|null */ + return $dbQuery->first(['accounts.*']); + } + /** * @param Account $account * @@ -219,4 +245,22 @@ class AccountRepository implements AccountRepositoryInterface return $dbQuery->take($limit)->get(['accounts.*']); } + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account + { + $query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban'); + + if (0 !== count($types)) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + /** @var Account|null */ + return $query->where('iban', $iban)->first(['accounts.*']); + } } diff --git a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php index 8bacdc32f4..858f82acd7 100644 --- a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php +++ b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php @@ -43,6 +43,22 @@ interface AccountRepositoryInterface */ public function setUserGroup(UserGroup $userGroup): void; + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account; + + /** + * @param string $number + * @param array $types + * + * @return Account|null + */ + public function findByAccountNumber(string $number, array $types): ?Account; + /** * @param User $user * diff --git a/app/Support/Steam.php b/app/Support/Steam.php index eeef4b2a3f..1299bb05af 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -604,7 +604,7 @@ class Steam ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) ->groupBy('transactions.transaction_currency_id'); - $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line + $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); /** @phpstan-ignore-line */ $return = []; /** @var stdClass $entry */ foreach ($balances as $entry) { diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 69219785c9..09f09cec7e 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -28,6 +28,7 @@ use DB; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionType; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -278,6 +279,7 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaDate', static function (int $journalId, string $metaField) { + /** @var TransactionJournalMeta|null $entry */ $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) @@ -300,6 +302,7 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaField', static function (int $journalId, string $metaField) { + /** @var TransactionJournalMeta|null $entry */ $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 7fc6b8c7f6..6d8fcc961c 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -56,7 +56,9 @@ class AddTag implements ActionInterface // journal has this tag maybe? /** @var TagFactory $factory */ $factory = app(TagFactory::class); - $factory->setUser(User::find($journal['user_id'])); + /** @var User $user */ + $user = User::find($journal['user_id']); + $factory->setUser($user); $tag = $factory->findOrCreate($this->action->action_value); if (null === $tag) { @@ -74,6 +76,7 @@ class AddTag implements ActionInterface // add to journal: DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); app('log')->debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + /** @var TransactionJournal $object */ $object = TransactionJournal::find($journal['transaction_journal_id']); // event for audit log entry diff --git a/app/TransactionRules/Actions/ClearNotes.php b/app/TransactionRules/Actions/ClearNotes.php index 129821ae37..0be3b9cae9 100644 --- a/app/TransactionRules/Actions/ClearNotes.php +++ b/app/TransactionRules/Actions/ClearNotes.php @@ -26,7 +26,9 @@ namespace FireflyIII\TransactionRules\Actions; use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; +use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; /** @@ -51,7 +53,9 @@ class ClearNotes implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); + /** @var Note|null $notes */ $notes = $object->notes()->first(); if (null === $notes) { app('log')->debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id'])); diff --git a/app/TransactionRules/Actions/DeleteTransaction.php b/app/TransactionRules/Actions/DeleteTransaction.php index 33756d19e2..fffa7c6118 100644 --- a/app/TransactionRules/Actions/DeleteTransaction.php +++ b/app/TransactionRules/Actions/DeleteTransaction.php @@ -63,6 +63,7 @@ class DeleteTransaction implements ActionInterface $journal['description'] ) ); + /** @var TransactionGroup $group */ $group = TransactionGroup::find($journal['transaction_group_id']); $service = app(TransactionGroupDestroyService::class); $service->destroy($group); @@ -76,7 +77,8 @@ class DeleteTransaction implements ActionInterface ); // trigger delete factory: - $object = TransactionJournal::find($journal['transaction_group_id']); + /** @var TransactionJournal|null $object */ + $object = TransactionJournal::find($journal['transaction_journal_id']); if (null !== $object) { /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 7a0ea8e805..3cb703a5bc 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -55,6 +55,7 @@ class LinkToBill implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); @@ -85,6 +86,7 @@ class LinkToBill implements ActionInterface sprintf('RuleAction LinkToBill set the bill of journal #%d to bill #%d ("%s").', $journal['transaction_journal_id'], $bill->id, $bill->name) ); + /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); event(new TriggeredAuditLog($this->action->rule, $object, 'set_bill', null, $bill->name)); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index 026485b72e..0d1fc2a872 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -54,6 +54,7 @@ class RemoveTag implements ActionInterface { // if tag does not exist, no need to continue: $name = $this->action->action_value; + /** @var User $user */ $user = User::find($journal['user_id']); $tag = $user->tags()->where('tag', $name)->first(); diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 15a078bccd..66804fed0a 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -53,6 +53,7 @@ class SetBudget implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); $search = $this->action->action_value; diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index bb56998c99..ba7106ac63 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -53,6 +53,7 @@ class SetCategory implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User|null $user */ $user = User::find($journal['user_id']); $search = $this->action->action_value; if (null === $user) { diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 6941019b66..82f3a25c8a 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -57,8 +57,8 @@ class SetDestinationAccount implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); - $type = $journal['transaction_type_type']; /** @var TransactionJournal|null $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); diff --git a/app/TransactionRules/Actions/SetSourceAccount.php b/app/TransactionRules/Actions/SetSourceAccount.php index aa95d66c7a..87af4fef4b 100644 --- a/app/TransactionRules/Actions/SetSourceAccount.php +++ b/app/TransactionRules/Actions/SetSourceAccount.php @@ -57,8 +57,8 @@ class SetSourceAccount implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); - $type = $journal['transaction_type_type']; /** @var TransactionJournal|null $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index 8f5902af65..862d433338 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -59,6 +59,7 @@ class UpdatePiggybank implements ActionInterface app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); // refresh the transaction type. + /** @var User $user */ $user = User::find($journal['user_id']); /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index 5cbb762bc5..c9adcab529 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -55,7 +55,7 @@ class ActionFactory $class = self::getActionClass($action->action_type); app('log')->debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); - return new $class($action); + return new $class($action); // @phpstan-ignore-line } /** diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index e0830e3600..0c377df964 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -212,7 +212,11 @@ class AccountTransformer extends AbstractTransformer if (null !== $monthlyPaymentDate) { // try classic date: if (10 === strlen($monthlyPaymentDate)) { - $monthlyPaymentDate = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone'))->toAtomString(); + $object = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone')); + if (false === $object) { + $object = today(config('app.timezone')); + } + $monthlyPaymentDate = $object->toAtomString(); } if (10 !== strlen($monthlyPaymentDate)) { $monthlyPaymentDate = Carbon::parse($monthlyPaymentDate, config('app.timezone'))->toAtomString(); @@ -240,7 +244,12 @@ class AccountTransformer extends AbstractTransformer $openingBalanceDate = $this->repository->getOpeningBalanceDate($account); } if (null !== $openingBalanceDate) { - $openingBalanceDate = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone'))->toAtomString(); + $object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone')); + if (false === $object) { + $object = today(config('app.timezone')); + } + $openingBalanceDate = $object->toAtomString(); + } return [$openingBalance, $openingBalanceDate]; diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index d688db79b7..fabae93f2e 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -62,7 +62,6 @@ class BillTransformer extends AbstractTransformer { $paidData = $this->paidData($bill); $lastPaidDate = $this->getLastPaidDate($paidData); - //$payDates = $this->payDates($bill, $lastPaidDate); $payDates = $this->calculator->getPayDates($this->parameters->get('start'), $this->parameters->get('end'), $bill->date, $bill->repeat_freq, $bill->skip, $lastPaidDate); $currency = $bill->transactionCurrency; $notes = $this->repository->getNoteText($bill); @@ -83,12 +82,20 @@ class BillTransformer extends AbstractTransformer $paidDataFormatted = []; $payDatesFormatted = []; foreach ($paidData as $object) { - $object['date'] = Carbon::createFromFormat('!Y-m-d', $object['date'], config('app.timezone'))->toAtomString(); + $date = Carbon::createFromFormat('!Y-m-d', $object['date'], config('app.timezone')); + if (false === $date) { + $date = today(config('app.timezone')); + } + $object['date'] = $date->toAtomString(); $paidDataFormatted[] = $object; } foreach ($payDates as $string) { - $payDatesFormatted[] = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'))->toAtomString(); + $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); + if (false === $date) { + $date = today(config('app.timezone')); + } + $payDatesFormatted[] = $date->toAtomString(); } // next expected match $nem = null; @@ -98,6 +105,9 @@ class BillTransformer extends AbstractTransformer if (null !== $firstPayDate) { $nemDate = Carbon::createFromFormat('!Y-m-d', $firstPayDate, config('app.timezone')); + if(false === $nemDate) { + $nemDate = today(config('app.timezone')); + } $nem = $nemDate->toAtomString(); // nullify again when it's outside the current view range. @@ -117,6 +127,9 @@ class BillTransformer extends AbstractTransformer $current = $payDatesFormatted[0] ?? null; if (null !== $current && !$nemDate->isToday()) { $temp2 = Carbon::createFromFormat('Y-m-d\TH:i:sP', $current); + if(false === $temp2) { + $temp2 = today(config('app.timezone')); + } $nemDiff = trans('firefly.bill_expected_date', ['date' => $temp2->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW)]); } unset($temp2); @@ -136,7 +149,7 @@ class BillTransformer extends AbstractTransformer 'end_date' => $bill->end_date?->toAtomString(), 'extension_date' => $bill->extension_date?->toAtomString(), 'repeat_freq' => $bill->repeat_freq, - 'skip' => (int)$bill->skip, + 'skip' => $bill->skip, 'active' => $bill->active, 'order' => $bill->order, 'notes' => $notes, @@ -265,124 +278,4 @@ class BillTransformer extends AbstractTransformer return $return; } - /** - * @param Bill $bill - * @param Carbon $lastPaidDate - * - * @return array - */ - protected function payDates(Bill $bill, ?Carbon $lastPaidDate): array - { - app('log')->debug(sprintf('Now in payDates(#%d, "%s")', $bill->id, $lastPaidDate?->format('Y-m-d'))); - if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - app('log')->debug('No start or end date, give empty array.'); - - return []; - } - app('log')->debug(sprintf('Start: %s, end: %s', $this->parameters->get('start')->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d'))); - $set = new Collection(); - $currentStart = clone $this->parameters->get('start'); - // 2023-06-23 subDay to fix 7655 - $currentStart->subDay(); - $loop = 0; - app('log')->debug('start of loop'); - /* - * De eerste dag van de bill telt sowieso. Vanaf daarna gaan we door tellen. - * Weekly die start op 01-10 - * 01-10: dit is hem dus. - * alle - */ - - - /* - * In de eerste week blijft aantal steps hangen op 0. - * Dus dan krijg je: - * 1 okt: 0 - * 2 okt: 0 - * 3 okt 0 - * en daarna pas begint-ie te lopen. - * maar je moet sowieso een periode verder kijken. - * - * dus stel je begint op 1 oktober monthly. - * dan is de eerste hit (want subday) vanaf 30 sept gerekend. - */ - while ($currentStart <= $this->parameters->get('end')) { - app('log')->debug(sprintf('Current start is %s', $currentStart->format('Y-m-d'))); - $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - - - // If nextExpectedMatch is after end, we continue: - if ($nextExpectedMatch > $this->parameters->get('end')) { - app('log')->debug('Next expected match is after END, so stop looking'); - //break; - if ($set->count() > 0) { - app('log')->debug(sprintf('Already have %d date(s), so break.', $set->count())); - break; - } - app('log')->debug('Add date to set anyway.'); - $set->push(clone $nextExpectedMatch); - continue; - } - app('log')->debug(sprintf('Next expected match is %s', $nextExpectedMatch->format('Y-m-d'))); - // add to set, if the date is ON or after the start parameter - // AND date is after last paid date - if ($nextExpectedMatch->gte($this->parameters->get('start')) - && (null === $lastPaidDate || $nextExpectedMatch->gt($lastPaidDate)) - ) { - app('log')->debug('Add date to set.'); - $set->push(clone $nextExpectedMatch); - } - - // 2023-10 - // for the next loop, go to end of period, THEN add day. - //$nextExpectedMatch = app('navigation')->endOfPeriod($nextExpectedMatch, $bill->repeat_freq); - $nextExpectedMatch->addDay(); - $currentStart = clone $nextExpectedMatch; - - - $loop++; - if ($loop > 12) { - break; - } - } - app('log')->debug('end of loop'); - $simple = $set->map( - static function (Carbon $date) { - return $date->format('Y-m-d'); - } - ); - app('log')->debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); - - return $simple->toArray(); - } - - /** - * Given a bill and a date, this method will tell you at which moment this bill expects its next - * transaction. That date must be AFTER $date as a sanity check. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon - */ - protected function nextDateMatch(Bill $bill, Carbon $date): Carbon - { - app('log')->debug(sprintf('Now in nextDateMatch(#%d, %s)', $bill->id, $date->format('Y-m-d'))); - $start = clone $bill->date; - app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); - if ($start->gt($date)) { - app('log')->debug('Start is after bill start, just return bill start date.'); - return clone $start; - } - - $steps = app('navigation')->diffInPeriods($bill->repeat_freq, $bill->skip, $start, $date); - $result = clone $start; - if ($steps > 0) { - $steps -= 1; - app('log')->debug(sprintf('Steps is %d, because addPeriod already adds 1.', $steps)); - $result = app('navigation')->addPeriod($start, $bill->repeat_freq, $steps); - } - app('log')->debug(sprintf('Number of steps is %d, added to %s, result is %s', $steps, $start->format('Y-m-d'), $result->format('Y-m-d'))); - return $result; - } } diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index e7e9a1aa84..5aaf7b8164 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -130,8 +130,8 @@ class RecurrenceTransformer extends AbstractTransformer 'updated_at' => $repetition->updated_at->toAtomString(), 'type' => $repetition->repetition_type, 'moment' => $repetition->repetition_moment, - 'skip' => (int)$repetition->repetition_skip, - 'weekend' => (int)$repetition->weekend, + 'skip' => $repetition->repetition_skip, + 'weekend' => $repetition->weekend, 'description' => $this->repository->repetitionDescription($repetition), 'occurrences' => [], ]; diff --git a/app/Transformers/V2/BillTransformer.php b/app/Transformers/V2/BillTransformer.php index 887bdbd2fd..57d0d5ac4c 100644 --- a/app/Transformers/V2/BillTransformer.php +++ b/app/Transformers/V2/BillTransformer.php @@ -217,7 +217,7 @@ class BillTransformer extends AbstractTransformer 'end_date' => $bill->end_date?->toAtomString(), 'extension_date' => $bill->extension_date?->toAtomString(), 'repeat_freq' => $bill->repeat_freq, - 'skip' => (int)$bill->skip, + 'skip' => $bill->skip, 'active' => $bill->active, 'order' => $bill->order, 'notes' => $this->notes[$bill->id] ?? null, diff --git a/app/Transformers/V2/TransactionGroupTransformer.php b/app/Transformers/V2/TransactionGroupTransformer.php index 1b297cdca5..80fd6d80c1 100644 --- a/app/Transformers/V2/TransactionGroupTransformer.php +++ b/app/Transformers/V2/TransactionGroupTransformer.php @@ -301,16 +301,28 @@ class TransactionGroupTransformer extends AbstractTransformer } // app('log')->debug(sprintf('Now in date("%s")', $string)); if (10 === strlen($string)) { - return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); + if(false === $res) { + return null; + } + return $res; } if (25 === strlen($string)) { return Carbon::parse($string, config('app.timezone')); } if (19 === strlen($string) && str_contains($string, 'T')) { - return Carbon::createFromFormat('Y-m-d\TH:i:s', substr($string, 0, 19), config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d\TH:i:s', substr($string, 0, 19), config('app.timezone')); + if(false === $res) { + return null; + } + return $res; } // 2022-01-01 01:01:01 - return Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone')); + if(false === $res) { + return null; + } + return $res; } } diff --git a/app/User.php b/app/User.php index a616af33c6..727339cf19 100644 --- a/app/User.php +++ b/app/User.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII; +use Carbon\Carbon; use Eloquent; use Exception; use FireflyIII\Enums\UserRoleEnum; @@ -66,7 +67,6 @@ use Illuminate\Notifications\DatabaseNotification; use Illuminate\Notifications\DatabaseNotificationCollection; use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notification; -use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Laravel\Passport\Client; @@ -80,7 +80,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class User. * - * @property int|string $id + * @property int|string $id * @property string $email * @property bool $isAdmin * @property bool $has2FA @@ -177,14 +177,14 @@ class User extends Authenticatable use Notifiable; protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'blocked' => 'boolean', ]; protected $fillable = ['email', 'password', 'blocked', 'blocked_code']; - protected $hidden = ['password', 'remember_token']; - protected $table = 'users'; + protected $hidden = ['password', 'remember_token']; + protected $table = 'users'; /** * @param string $value @@ -401,37 +401,37 @@ class User extends Authenticatable ->where('user_group_id', $userGroup->id)->get(); if (0 === $groupMemberships->count()) { app('log')->error(sprintf( - 'User #%d "%s" does not have roles %s in user group #%d "%s"', - $this->id, - $this->email, - implode(', ', $roles), - $userGroup->id, - $userGroup->title - )); + 'User #%d "%s" does not have roles %s in user group #%d "%s"', + $this->id, + $this->email, + implode(', ', $roles), + $userGroup->id, + $userGroup->title + )); return false; } foreach ($groupMemberships as $membership) { app('log')->debug(sprintf( - 'User #%d "%s" has role "%s" in user group #%d "%s"', - $this->id, - $this->email, - $membership->userRole->title, - $userGroup->id, - $userGroup->title - )); + 'User #%d "%s" has role "%s" in user group #%d "%s"', + $this->id, + $this->email, + $membership->userRole->title, + $userGroup->id, + $userGroup->title + )); if (in_array($membership->userRole->title, $dbRolesTitles, true)) { app('log')->debug(sprintf('Return true, found role "%s"', $membership->userRole->title)); return true; } } app('log')->error(sprintf( - 'User #%d "%s" does not have roles %s in user group #%d "%s"', - $this->id, - $this->email, - implode(', ', $roles), - $userGroup->id, - $userGroup->title - )); + 'User #%d "%s" does not have roles %s in user group #%d "%s"', + $this->id, + $this->email, + implode(', ', $roles), + $userGroup->id, + $userGroup->title + )); return false; // // not necessary, should always return true: // $result = $groupMembership->userRole->title === $role->value; @@ -554,20 +554,28 @@ class User extends Authenticatable public function routeNotificationForSlack(Notification $notification): string { // this check does not validate if the user is owner, Should be done by notification itself. + $res = app('fireflyconfig')->get('slack_webhook_url', '')->data; + if (is_array($res)) { + $res = ''; + } + $res = (string)$res; if ($notification instanceof TestNotification) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof UserInvitation) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof UserRegistration) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof VersionCheckResult) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } - - return app('preferences')->getForUser($this, 'slack_webhook_url', '')->data; + $pref = app('preferences')->getForUser($this, 'slack_webhook_url', '')->data; + if (is_array($pref)) { + return ''; + } + return (string)$pref; } /** @@ -676,7 +684,7 @@ class User extends Authenticatable */ public function userGroup(): BelongsTo { - return $this->belongsTo(UserGroup::class, ); + return $this->belongsTo(UserGroup::class,); } /** diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index c5c52e0afd..c8b246e194 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -73,8 +73,11 @@ class FireflyValidator extends Validator } $secretPreference = app('preferences')->get('temp-mfa-secret'); $secret = $secretPreference?->data ?? ''; + if(is_array($secret)) { + $secret = ''; + } - return Google2FA::verifyKey($secret, $value); + return (bool) Google2FA::verifyKey((string) $secret, $value); } /** @@ -183,7 +186,7 @@ class FireflyValidator extends Validator $value = strtoupper($value); // replace characters outside of ASCI range. - $value = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); + $value = (string) iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); $search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; $replace = [ '', @@ -509,7 +512,8 @@ class FireflyValidator extends Validator if (!array_key_exists('user_id', $this->data)) { return false; } - + + /** @var User $user */ $user = User::find($this->data['user_id']); $type = AccountType::find($this->data['account_type_id'])->first(); $value = $this->data['name']; diff --git a/bootstrap/app.php b/bootstrap/app.php index d340c986dd..f17a7fe025 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -66,7 +66,7 @@ if (!function_exists('stringIsEqual')) { } $app = new Illuminate\Foundation\Application( - realpath(__DIR__ . '/../') + (string)realpath(__DIR__ . '/../') ); /*