mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-03-06 16:01:29 +00:00
Compare commits
12 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e85f06792b | ||
|
|
b66f95f1dc | ||
|
|
4d63146524 | ||
|
|
695f990236 | ||
|
|
e882530a69 | ||
|
|
993a2491e9 | ||
|
|
f6ea517b5d | ||
|
|
6f4143bb79 | ||
|
|
4a2c77c6b7 | ||
|
|
0e62f980b0 | ||
|
|
41533fd922 | ||
|
|
d6b5fbe341 |
@@ -14,10 +14,6 @@ parameters:
|
||||
enabled: false
|
||||
noNullableReturnTypeDeclaration:
|
||||
enabled: false
|
||||
privateInFinalClass:
|
||||
enabled: false
|
||||
noIsset:
|
||||
enabled: false
|
||||
finalInAbstractClass:
|
||||
enabled: false
|
||||
noConstructorParameterWithDefaultValue:
|
||||
@@ -38,29 +34,6 @@ parameters:
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
ignoreErrors:
|
||||
# these are actually interesting but not right now:
|
||||
- identifier: staticMethod.dynamicCall
|
||||
- identifier: argument.templateType
|
||||
- identifier: method.childReturnType
|
||||
- identifier: instanceof.alwaysFalse
|
||||
- identifier: deadCode.unreachable
|
||||
- identifier: method.dynamicName
|
||||
- identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
- identifier: property.notFound
|
||||
- identifier: arguments.count
|
||||
- identifier: staticMethod.dynamicName
|
||||
- identifier: catch.neverThrown
|
||||
- identifier: notIdentical.alwaysTrue
|
||||
- identifier: method.notFound
|
||||
- identifier: nullsafe.neverNull
|
||||
- identifier: identical.alwaysFalse
|
||||
- identifier: if.condNotBoolean
|
||||
# - identifier: booleanNot.exprNotBoolean
|
||||
- identifier: method.nonObject
|
||||
- identifier: function.impossibleType
|
||||
- identifier: booleanNot.exprNotBoolean
|
||||
- identifier: ternary.condNotBoolean
|
||||
- identifier: booleanNot.alwaysFalse
|
||||
- identifier: booleanAnd.alwaysFalse
|
||||
- identifier: greater.alwaysTrue
|
||||
- identifier: function.alreadyNarrowedType
|
||||
- identifier: booleanNot.alwaysTrue
|
||||
@@ -70,18 +43,13 @@ parameters:
|
||||
- identifier: larastan.noUnnecessaryCollectionCall
|
||||
- identifier: varTag.differentVariable
|
||||
- identifier: identical.alwaysTrue
|
||||
- identifier: clone.nonObject
|
||||
- identifier: assign.propertyReadOnly
|
||||
- identifier: property.nonObject
|
||||
- identifier: varTag.nativeType
|
||||
- identifier: booleanAnd.leftAlwaysFalse
|
||||
- identifier: property.onlyWritten
|
||||
- identifier: parameter.phpDocType
|
||||
- identifier: property.dynamicName
|
||||
- identifier: property.unusedType
|
||||
- identifier: staticMethod.deprecated
|
||||
- identifier: greater.invalid
|
||||
- identifier: instanceof.alwaysTrue
|
||||
# ignore everything but things that BREAK
|
||||
- identifier: property.deprecated
|
||||
- identifier: method.deprecated
|
||||
@@ -93,7 +61,15 @@ parameters:
|
||||
- identifier: assign.propertyType
|
||||
- identifier: return.unusedType
|
||||
- identifier: return.phpDocType
|
||||
# all errors below I will never fix.
|
||||
# all errors below I will (probably) never fix.
|
||||
- identifier: method.notFound # way too many false positives
|
||||
- identifier: catch.neverThrown # plenty of errors that are thrown undocumented
|
||||
- identifier: staticMethod.dynamicName # dont care
|
||||
- identifier: arguments.count # one false positive
|
||||
- identifier: property.notFound # false positives
|
||||
- identifier: method.dynamicName # i dont care
|
||||
- identifier: staticMethod.dynamicCall # many false positives.
|
||||
- identifier: argument.templateType # no clue how to fix single occurrence.
|
||||
# - '#expects view-string\|null, string given#'
|
||||
# - '#expects view-string, string given#'
|
||||
# - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#"
|
||||
|
||||
@@ -112,19 +112,6 @@ final class BasicController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if date is outside session range.
|
||||
*/
|
||||
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool
|
||||
{ // Validate a preference
|
||||
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// start and end in the past? use $end
|
||||
return $start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date);
|
||||
}
|
||||
|
||||
private function getBalanceInformation(Carbon $start, Carbon $end): array
|
||||
{
|
||||
Log::debug('getBalanceInformation');
|
||||
|
||||
@@ -62,7 +62,7 @@ final class BatchController extends Controller
|
||||
}
|
||||
Log::debug(sprintf('Counted %d journals.', count($journals)));
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
/** @var null|TransactionJournal $first */
|
||||
$first = $journals->first();
|
||||
$group = $first?->transactionGroup;
|
||||
if (null === $group) {
|
||||
|
||||
@@ -38,7 +38,7 @@ class DateRangeRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$start = $this->getCarbonDate('start')?->startOfDay();
|
||||
|
||||
@@ -36,7 +36,7 @@ class DateRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$date = $this->getCarbonDate('date')?->endOfDay();
|
||||
|
||||
@@ -50,7 +50,7 @@ class ObjectTypeApiRequest extends ApiRequest
|
||||
|
||||
$this->objectType = $config['object_type'] ?? null;
|
||||
|
||||
if (!$this->objectType) {
|
||||
if (null === $this->objectType) {
|
||||
throw new RuntimeException('ObjectTypeApiRequest requires a object_type config');
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class ObjectTypeApiRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$type = $this->convertString('types', 'all');
|
||||
|
||||
@@ -42,7 +42,7 @@ class QueryRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$query = $this->convertString('query');
|
||||
|
||||
@@ -40,7 +40,7 @@ class AccountTypeApiRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class AccountTypesApiRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$types = explode(',', $this->convertString('types', 'all'));
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Factory\TransactionCurrencyFactory;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
@@ -85,6 +86,7 @@ class StoreRequest extends FormRequest
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
/** @var Budget $budget */
|
||||
$budget = $this->route()->parameter('budget');
|
||||
$validator->after(static function (Validator $validator) use ($budget): void {
|
||||
if (0 !== count($validator->failed())) {
|
||||
|
||||
@@ -61,7 +61,7 @@ class UpdateRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
/** @var TransactionCurrency $currency */
|
||||
/** @var string|TransactionCurrency $currency */
|
||||
$currency = $this->route()->parameter('currency_code');
|
||||
if (is_string($currency)) {
|
||||
$currency = TransactionCurrency::whereCode($currency)->first();
|
||||
|
||||
@@ -41,7 +41,7 @@ class PaginationRequest extends ApiRequest
|
||||
|
||||
$this->sortClass = $config['sort_class'] ?? null;
|
||||
|
||||
if (!$this->sortClass) {
|
||||
if (null === $this->sortClass) {
|
||||
throw new RuntimeException('PaginationRequest requires a sort_class config');
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ class PaginationRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class PaginationRequest extends ApiRequest
|
||||
$page = $this->convertInteger('page');
|
||||
$page = min(max(1, $page), 2 ** 16);
|
||||
$offset = ($page - 1) * $limit;
|
||||
$sort = $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : $this->get('sort');
|
||||
$sort = null !== $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : $this->get('sort');
|
||||
$this->attributes->set('limit', $limit);
|
||||
$this->attributes->set('sort', $sort);
|
||||
$this->attributes->set('page', $page);
|
||||
|
||||
@@ -45,7 +45,7 @@ class CountRequest extends AggregateFormRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$this->attributes->set('include_deleted', $this->convertBoolean($this->input('include_deleted', 'false')));
|
||||
|
||||
@@ -37,7 +37,7 @@ class SearchQueryRequest extends ApiRequest
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$query = $this->convertString('query');
|
||||
|
||||
@@ -57,24 +57,28 @@ class CorrectsIbans extends Command
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$userId = $account->user_id;
|
||||
$userId = $account->user_id;
|
||||
$set[$userId] ??= [];
|
||||
$iban = (string) $account->iban;
|
||||
$iban = (string) $account->iban;
|
||||
if ('' === $iban) {
|
||||
continue;
|
||||
}
|
||||
$type = $account->accountType->type;
|
||||
$type = $account->accountType->type;
|
||||
if (in_array($type, [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], true)) {
|
||||
$type = 'liabilities';
|
||||
}
|
||||
// iban already in use! two exceptions exist:
|
||||
if (
|
||||
array_key_exists($iban, $set[$userId]) && (
|
||||
!AccountTypeEnum::EXPENSE->value === $set[$userId][$iban]
|
||||
&& AccountTypeEnum::REVENUE->value === $type
|
||||
&& !(AccountTypeEnum::REVENUE->value === $set[$userId][$iban] && AccountTypeEnum::EXPENSE->value === $type)
|
||||
)
|
||||
) {
|
||||
$showWarningAndCorrect = false;
|
||||
if (array_key_exists($iban, $set[$userId])) {
|
||||
// the type is a revenue account, and the existing IBAN is NOT an expense account.
|
||||
if (AccountTypeEnum::REVENUE->value === $type && AccountTypeEnum::EXPENSE->value !== $set[$userId][$iban]) {
|
||||
$showWarningAndCorrect = true;
|
||||
}
|
||||
// the type is an expense account, and the existing IBAN is NOT a revenue account
|
||||
if (AccountTypeEnum::EXPENSE->value === $type && AccountTypeEnum::REVENUE->value !== $set[$userId][$iban]) {
|
||||
$showWarningAndCorrect = true;
|
||||
}
|
||||
}
|
||||
if ($showWarningAndCorrect) {
|
||||
$this->friendlyWarning(sprintf(
|
||||
'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")',
|
||||
$iban,
|
||||
|
||||
@@ -322,7 +322,7 @@ class CorrectsUnevenAmount extends Command
|
||||
foreach ($journals as $entry) {
|
||||
$sum = (string) $entry->the_sum;
|
||||
$sum = Steam::floatalize($sum);
|
||||
if (!is_numeric($sum) || '' === $sum || str_contains($sum, 'e') || str_contains($sum, ',')) {
|
||||
if (!is_numeric($sum) || str_contains($sum, 'e') || str_contains($sum, ',')) {
|
||||
$message = sprintf('Journal #%d has an invalid sum ("%s"). No sure what to do.', $entry->transaction_journal_id, $entry->the_sum);
|
||||
$this->friendlyWarning($message);
|
||||
Log::warning($message);
|
||||
|
||||
@@ -101,8 +101,13 @@ class UpgradesLiabilitiesEight extends Command
|
||||
|
||||
private function hasBadOpening(Account $account): bool
|
||||
{
|
||||
/** @var TransactionType $openingBalanceType */
|
||||
$openingBalanceType = TransactionType::whereType(TransactionTypeEnum::OPENING_BALANCE->value)->first();
|
||||
|
||||
/** @var TransactionType $liabilityType */
|
||||
$liabilityType = TransactionType::whereType(TransactionTypeEnum::LIABILITY_CREDIT->value)->first();
|
||||
|
||||
/** @var null|TransactionJournal $openingJournal */
|
||||
$openingJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $openingBalanceType->id)
|
||||
@@ -111,6 +116,8 @@ class UpgradesLiabilitiesEight extends Command
|
||||
if (null === $openingJournal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var null|TransactionJournal $liabilityJournal */
|
||||
$liabilityJournal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->where('transaction_journals.transaction_type_id', $liabilityType->id)
|
||||
@@ -120,7 +127,7 @@ class UpgradesLiabilitiesEight extends Command
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $openingJournal->date->isSameDay($liabilityJournal->date);
|
||||
return $openingJournal->date->isSameDay($liabilityJournal->date);
|
||||
}
|
||||
|
||||
private function isExecuted(): bool
|
||||
|
||||
@@ -338,7 +338,7 @@ class TransactionJournalFactory
|
||||
'date_tz' => $carbon->format('e'),
|
||||
'order' => $order,
|
||||
'tag_count' => 0,
|
||||
'completed' => !$row['batch_submission'],
|
||||
'completed' => is_bool($row['batch_submission']) && !$row['batch_submission'],
|
||||
]);
|
||||
Log::debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description));
|
||||
|
||||
@@ -574,16 +574,11 @@ class TransactionJournalFactory
|
||||
return [$sourceAccount, $account];
|
||||
}
|
||||
|
||||
if (!$sourceAccount instanceof Account) {
|
||||
Log::debug('Source account is NULL, destination account is not.');
|
||||
$account = $this->accountRepository->getReconciliation($destinationAccount);
|
||||
Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type));
|
||||
Log::debug('Source account is NULL, destination account is not.');
|
||||
$account = $this->accountRepository->getReconciliation($destinationAccount);
|
||||
Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type));
|
||||
|
||||
return [$account, $destinationAccount];
|
||||
}
|
||||
Log::debug('Unused fallback');
|
||||
|
||||
return [$sourceAccount, $destinationAccount];
|
||||
return [$account, $destinationAccount];
|
||||
}
|
||||
|
||||
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void
|
||||
|
||||
@@ -275,7 +275,7 @@ final class LoginController extends Controller
|
||||
$request->session()->regenerate();
|
||||
$this->clearLoginAttempts($request);
|
||||
$response = $this->authenticated($request, $this->guard()->user());
|
||||
if ($response) {
|
||||
if (null !== $response) {
|
||||
return $response;
|
||||
}
|
||||
$path = Steam::getSafeUrl(session()->pull('url.intended', route('index')), route('index'));
|
||||
|
||||
@@ -176,7 +176,7 @@ final class RegisterController extends Controller
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function allowedToRegister(): bool
|
||||
private function allowedToRegister(): bool
|
||||
{
|
||||
// is allowed to register?
|
||||
$allowRegistration = true;
|
||||
|
||||
@@ -304,7 +304,7 @@ final class DebugController extends Controller
|
||||
|
||||
return [
|
||||
'debug' => var_export(config('app.debug'), true),
|
||||
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
|
||||
'audit_log_channel' => implode(', ', config('logging.channels.audit.channels')),
|
||||
'default_language' => (string) config('firefly.default_language'),
|
||||
'default_locale' => (string) config('firefly.default_locale'),
|
||||
'remote_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A',
|
||||
@@ -323,11 +323,11 @@ final class DebugController extends Controller
|
||||
private function getBuildInfo(): array
|
||||
{
|
||||
$return = [
|
||||
'is_docker' => env('IS_DOCKER', false),
|
||||
'is_docker' => config('firefly.is_docker'),
|
||||
'build' => '(unknown)',
|
||||
'build_date' => '(unknown)',
|
||||
'base_build' => '(unknown)',
|
||||
'base_build_date' => '(unknown)',
|
||||
'base_build' => config('firefly.base_image_build'),
|
||||
'base_build_date' => config('firefly.base_image_date'),
|
||||
];
|
||||
|
||||
try {
|
||||
@@ -348,12 +348,6 @@ final class DebugController extends Controller
|
||||
Log::debug('Could not check build date, but thats ok.');
|
||||
Log::warning($e->getMessage());
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_BUILD')) {
|
||||
$return['base_build'] = env('BASE_IMAGE_BUILD');
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_DATE')) {
|
||||
$return['base_build_date'] = env('BASE_IMAGE_DATE');
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ final class PreferencesController extends Controller
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$isDocker = env('IS_DOCKER', false);
|
||||
$isDocker = config('firefly.is_docker');
|
||||
$groupedAccounts = [];
|
||||
|
||||
/** @var Account $account */
|
||||
|
||||
@@ -67,7 +67,7 @@ class SecureHeaders
|
||||
];
|
||||
|
||||
// overrule in development mode
|
||||
if (true === env('IS_LOCAL_DEV')) {
|
||||
if (true === config('firefly.is_local_dev')) {
|
||||
$csp = [
|
||||
"default-src 'none'",
|
||||
"object-src 'none'",
|
||||
|
||||
@@ -213,7 +213,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
/** @var RecurrenceTransaction $transaction */
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$single = [
|
||||
'type' => null === $transaction?->transactionType?->type
|
||||
'type' => null === $transaction->transactionType?->type
|
||||
? strtolower((string) $recurrence->transactionType->type)
|
||||
: strtolower($transaction->transactionType->type),
|
||||
'date' => $date,
|
||||
|
||||
@@ -42,7 +42,7 @@ class ProcessesBudgetLimits implements ShouldQueue
|
||||
public function handle(CreatedBudgetLimit|DestroyedBudgetLimit|UpdatedBudgetLimit $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in ProcessesBudgetLimits::handle for event %s', get_class($event)));
|
||||
if ($event instanceof DestroyedBudgetLimit && null !== $event->user) {
|
||||
if ($event instanceof DestroyedBudgetLimit) {
|
||||
// need to recalculate all available budgets for this user.
|
||||
$calculator = new AvailableBudgetCalculator();
|
||||
$calculator->setUser($event->user);
|
||||
|
||||
@@ -58,10 +58,10 @@ class SendsWebhookMessages implements ShouldQueue
|
||||
$message->save();
|
||||
Log::debug(sprintf('Send message #%d', $message->id));
|
||||
SendWebhookMessage::dispatch($message)->afterResponse();
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false !== $message->sent) {
|
||||
Log::debug(sprintf('Skip message #%d', $message->id));
|
||||
}
|
||||
Log::debug(sprintf('Skip message #%d', $message->id));
|
||||
}
|
||||
|
||||
// clean up sent messages table:
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Handlers\Observer\BillObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
@@ -38,6 +39,11 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* @property Carbon $date
|
||||
* @property null|Carbon $end_date
|
||||
* @property null|Carbon $extension_date
|
||||
*/
|
||||
#[ObservedBy([BillObserver::class])]
|
||||
class Bill extends Model
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
@@ -32,6 +33,9 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* @property Carbon $date
|
||||
*/
|
||||
class CurrencyExchangeRate extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
@@ -11,6 +12,10 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
/**
|
||||
* @property Carbon $start
|
||||
* @property Carbon $end
|
||||
*/
|
||||
class PeriodStatistic extends Model
|
||||
{
|
||||
use ReturnsIntegerUserIdTrait;
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Handlers\Observer\PiggyBankObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
@@ -36,6 +37,10 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* @property null|Carbon $target_date
|
||||
* @property null|Carbon $start_date
|
||||
*/
|
||||
#[ObservedBy([PiggyBankObserver::class])]
|
||||
class PiggyBank extends Model
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Handlers\Observer\PiggyBankEventObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
@@ -31,6 +32,9 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property Carbon $date
|
||||
*/
|
||||
#[ObservedBy([PiggyBankEventObserver::class])]
|
||||
class PiggyBankEvent extends Model
|
||||
{
|
||||
|
||||
@@ -30,6 +30,9 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* @property mixed $data
|
||||
*/
|
||||
class Preference extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -40,8 +40,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* @property Carbon $first_date
|
||||
* @property null|Carbon $first_date
|
||||
* @property null|Carbon $latest_date
|
||||
* @property null|Carbon $repeat_until
|
||||
*/
|
||||
#[ObservedBy([DeletedRecurrenceObserver::class])]
|
||||
class Recurrence extends Model
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Handlers\Observer\DeletedTagObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
@@ -36,6 +37,9 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* @property null|Carbon $date
|
||||
*/
|
||||
#[ObservedBy([DeletedTagObserver::class])]
|
||||
class Tag extends Model
|
||||
{
|
||||
|
||||
@@ -47,7 +47,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @method EloquentBuilder|static after()
|
||||
* @method static EloquentBuilder|static query()
|
||||
*
|
||||
* @property TransactionGroup $transactionGroup
|
||||
* @property null|TransactionGroup $transactionGroup
|
||||
* @property Carbon $date
|
||||
*/
|
||||
#[ObservedBy([DeletedTransactionJournalObserver::class])]
|
||||
class TransactionJournal extends Model
|
||||
|
||||
@@ -687,6 +687,31 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
|
||||
return $dbQuery->take($limit)->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function searchAccountIncludingInactive(string $query, array $types, int $limit): Collection
|
||||
{
|
||||
$dbQuery = $this->user
|
||||
->accounts()
|
||||
->orderBy('accounts.order', 'ASC')
|
||||
->orderBy('accounts.account_type_id', 'ASC')
|
||||
->orderBy('accounts.name', 'ASC')
|
||||
->with(['accountType'])
|
||||
;
|
||||
if ('' !== $query) {
|
||||
// split query on spaces just in case:
|
||||
$parts = explode(' ', $query);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$dbQuery->whereLike('name', $search);
|
||||
}
|
||||
}
|
||||
if (0 !== count($types)) {
|
||||
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$dbQuery->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
return $dbQuery->take($limit)->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection
|
||||
{
|
||||
$dbQuery = $this->user
|
||||
|
||||
@@ -156,6 +156,8 @@ interface AccountRepositoryInterface
|
||||
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||
|
||||
public function searchAccountIncludingInactive(string $query, array $types, int $limit): Collection;
|
||||
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection;
|
||||
|
||||
public function store(array $data): Account;
|
||||
|
||||
@@ -101,8 +101,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::negative((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::negative((string) $journal['pc_amount']) : null,
|
||||
'foreign_amount' => array_key_exists('foreign_amount', $journal) && null !== $journal['foreign_amount']
|
||||
? Steam::negative((string) $journal['foreign_amount'])
|
||||
: null,
|
||||
'pc_amount' => array_key_exists('pc_amount', $journal) && null !== $journal['pc_amount']
|
||||
? Steam::negative((string) $journal['pc_amount'])
|
||||
: null,
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'budget_name' => $journal['budget_name'],
|
||||
@@ -186,8 +190,12 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
||||
'currency_code' => (string) $journal['currency_code'],
|
||||
'currency_decimal_places' => (int) $journal['currency_decimal_places'],
|
||||
'foreign_currency_id' => (int) ($journal['foreign_currency_id'] ?? 0),
|
||||
'foreign_amount' => isset($journal['foreign_amount']) ? Steam::positive((string) $journal['foreign_amount']) : null,
|
||||
'pc_amount' => isset($journal['pc_amount']) ? Steam::positive((string) $journal['pc_amount']) : null,
|
||||
'foreign_amount' => array_key_exists('foreign_amount', $journal) && null !== $journal['foreign_amount']
|
||||
? Steam::positive((string) $journal['foreign_amount'])
|
||||
: null,
|
||||
'pc_amount' => array_key_exists('pc_amount', $journal) && null !== $journal['pc_amount']
|
||||
? Steam::positive((string) $journal['pc_amount'])
|
||||
: null,
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'budget_name' => $journal['budget_name'],
|
||||
|
||||
@@ -86,6 +86,8 @@ class AccountDestroyService
|
||||
foreach ($collection as $row) {
|
||||
if ((int) $row->the_count > 1) {
|
||||
$journalId = $row->transaction_journal_id;
|
||||
|
||||
/** @var null|TransactionJournal $journal */
|
||||
$journal = $user->transactionJournals()->find($journalId);
|
||||
if (null !== $journal) {
|
||||
Log::debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id));
|
||||
|
||||
@@ -144,15 +144,17 @@ class RemoteUserGuard implements Guard
|
||||
return $this->user?->id;
|
||||
}
|
||||
|
||||
public function setUser(Authenticatable|User|null $user): void
|
||||
public function setUser(Authenticatable|User|null $user): Guard
|
||||
{
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
return;
|
||||
return $this;
|
||||
}
|
||||
Log::error(sprintf('Did not set user at %s', __METHOD__));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function user(): ?User
|
||||
|
||||
@@ -43,14 +43,12 @@ class BudgetList implements BinderInterface
|
||||
if ('allBudgets' === $value) {
|
||||
return auth()->user()->budgets()->where('active', true)->orderBy('order', 'ASC')->orderBy('name', 'ASC')->get();
|
||||
}
|
||||
|
||||
$list = array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
|
||||
if (0 === count($list)) {
|
||||
if ('' === $value) {
|
||||
Log::warning('Budget list count is zero, return 404.');
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$list = array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
|
||||
/** @var Collection $collection */
|
||||
$collection = auth()->user()->budgets()->where('active', true)->whereIn('id', $list)->get();
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Binder;
|
||||
use FireflyIII\Models\Category;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
@@ -42,11 +43,12 @@ class CategoryList implements BinderInterface
|
||||
if ('allCategories' === $value) {
|
||||
return auth()->user()->categories()->orderBy('name', 'ASC')->get();
|
||||
}
|
||||
if ('' === $value) {
|
||||
Log::warning('Category list count is zero, return 404.');
|
||||
|
||||
$list = array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
if (0 === count($list)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$list = array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
|
||||
/** @var Collection $collection */
|
||||
$collection = auth()->user()->categories()->whereIn('id', $list)->get();
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Binder;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
@@ -65,11 +66,12 @@ class JournalList implements BinderInterface
|
||||
|
||||
protected static function parseList(string $value): array
|
||||
{
|
||||
$list = array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
if (0 === count($list)) {
|
||||
if ('' === $value) {
|
||||
Log::warning('Category list count is zero, return 404.');
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $list;
|
||||
return array_unique(array_map(\intval(...), explode(',', $value)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,15 +44,15 @@ class TagList implements BinderInterface
|
||||
if ('allTags' === $value) {
|
||||
return auth()->user()->tags()->orderBy('tag', 'ASC')->get();
|
||||
}
|
||||
$list = array_unique(array_map(\strtolower(...), explode(',', $value)));
|
||||
Log::debug('List of tags is', $list);
|
||||
|
||||
if (0 === count($list)) {
|
||||
Log::error('Tag list is empty.');
|
||||
if ('' === $value) {
|
||||
Log::warning('Category list count is zero, return 404.');
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$list = array_unique(array_map(\strtolower(...), explode(',', $value)));
|
||||
Log::debug('List of tags is', $list);
|
||||
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
|
||||
@@ -128,6 +128,12 @@ class BillDateCalculator
|
||||
}
|
||||
}
|
||||
Log::debug('end of loop');
|
||||
|
||||
/** @template T
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return null|T
|
||||
*/
|
||||
$simple = $set->map(static fn (Carbon $date) => $date->format('Y-m-d'));
|
||||
Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray());
|
||||
|
||||
|
||||
@@ -201,25 +201,25 @@ class Navigation
|
||||
Log::debug('endOfPeriod() requests "YTD" + future, set it to "3M" instead.');
|
||||
$repeatFreq = '3M';
|
||||
}
|
||||
|
||||
$new = Carbon::now();
|
||||
$functionMap = [
|
||||
'1D' => 'endOfDay',
|
||||
'daily' => 'endOfDay',
|
||||
'1W' => 'addWeek',
|
||||
'week' => 'addWeek',
|
||||
'weekly' => 'addWeek',
|
||||
'1M' => 'addMonth',
|
||||
'month' => 'addMonth',
|
||||
'monthly' => 'addMonth',
|
||||
'3M' => 'addQuarter',
|
||||
'quarter' => 'addQuarter',
|
||||
'quarterly' => 'addQuarter',
|
||||
'1W' => 'addWeeks',
|
||||
'week' => 'addWeeks',
|
||||
'weekly' => 'addWeeks',
|
||||
'1M' => 'addMonths',
|
||||
'month' => 'addMonths',
|
||||
'monthly' => 'addMonths',
|
||||
'3M' => 'addQuarters',
|
||||
'quarter' => 'addQuarters',
|
||||
'quarterly' => 'addQuarters',
|
||||
'6M' => 'addMonths',
|
||||
'half-year' => 'addMonths',
|
||||
'half_year' => 'addMonths',
|
||||
'year' => 'addYear',
|
||||
'yearly' => 'addYear',
|
||||
'1Y' => 'addYear',
|
||||
'year' => 'addYears',
|
||||
'yearly' => 'addYears',
|
||||
'1Y' => 'addYears',
|
||||
];
|
||||
$modifierMap = ['half-year' => 6, 'half_year' => 6, '6M' => 6];
|
||||
$subDay = ['week', 'weekly', '1W', 'month', 'monthly', '1M', '3M', 'quarter', 'quarterly', '6M', 'half-year', 'half_year', '1Y', 'year', 'yearly'];
|
||||
|
||||
@@ -487,7 +487,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
|
||||
// search direction: for destination accounts
|
||||
if (SearchDirection::DESTINATION === $searchDirection) { // destination
|
||||
// destination can be
|
||||
// the destination account can be
|
||||
$searchTypes = [
|
||||
AccountTypeEnum::ASSET->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
@@ -530,7 +530,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
}
|
||||
|
||||
// get accounts:
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337);
|
||||
$accounts = $this->accountRepository->searchAccountIncludingInactive($value, $searchTypes, 1337);
|
||||
if (0 === $accounts->count() && false === $prohibited) {
|
||||
Log::warning('Found zero accounts, search for non existing account, NO results will be returned.');
|
||||
$this->collector->findNothing();
|
||||
|
||||
@@ -47,7 +47,6 @@ use Illuminate\Http\Middleware\HandleCors;
|
||||
use Illuminate\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
|
||||
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
|
||||
use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware;
|
||||
|
||||
/*
|
||||
@@ -63,19 +62,20 @@ use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware;
|
||||
|
||||
bcscale(12);
|
||||
|
||||
if (!function_exists('envNonEmpty')) {
|
||||
if (!function_exists('envDefaultWhenEmpty')) {
|
||||
/**
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
function envNonEmpty(string $key, string | int | bool | null $default = null)
|
||||
function envDefaultWhenEmpty(mixed $value, string | int | bool | null $default = null): mixed
|
||||
{
|
||||
$result = env($key, $default);
|
||||
if ('' === $result) {
|
||||
if(null === $value) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $result;
|
||||
if('' === $value) {
|
||||
return $default;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
changelog.md
16
changelog.md
@@ -3,11 +3,11 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 6.5.4 - 2026-03-06
|
||||
## v6.5.4 - 2026-03-06
|
||||
|
||||
### Added
|
||||
|
||||
- Add some debug info to find
|
||||
- Add some debug info to find problems with available budget calculations.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -17,13 +17,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Security
|
||||
|
||||
- Published security advisory https://github.com/firefly-iii/firefly-iii/security/advisories/GHSA-5q8v-j673-m5v4 found and reported by @lighthousekeeper1212
|
||||
- It's possible to submit webhook URLs that point to internal IP addresses. This will still be the case in the future, though some reserved ranges are no blocked. Let me know if this impacts you.
|
||||
- It's possible to submit webhook URLs that point to internal IP addresses. This will still be the case in the future, though some reserved ranges are now blocked. Let me know if this impacts you.
|
||||
|
||||
### API
|
||||
|
||||
- Initial release.
|
||||
|
||||
## 6.5.3 - 2026-03-05
|
||||
## v6.5.3 - 2026-03-05
|
||||
|
||||
This release fixes some sloppy coding on my part, but good news everyone! A new linter is in place that should prevent that from happening. Turns out I had disabled it in the past :(.
|
||||
|
||||
@@ -38,7 +34,7 @@ This release fixes some sloppy coding on my part, but good news everyone! A new
|
||||
### Fixed
|
||||
- [Issue 11866](https://github.com/firefly-iii/firefly-iii/issues/11866) (ReflectionException on Transaction\ListRequest) reported by @brot
|
||||
|
||||
## 6.5.2 - 2026-03-04
|
||||
## v6.5.2 - 2026-03-04
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -61,7 +57,7 @@ This release fixes some sloppy coding on my part, but good news everyone! A new
|
||||
- [Issue 11822](https://github.com/firefly-iii/firefly-iii/issues/11822) (API - account transaction type filtering) reported by @mgrove36
|
||||
- [Issue 11842](https://github.com/firefly-iii/firefly-iii/issues/11842) (API: `/api/v1/configuration` always returns unauthenticated for v6.5.1) reported by @dreautall
|
||||
|
||||
## 6.5.1 - 2026-02-28
|
||||
## v6.5.1 - 2026-02-28
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This releases also fixes a security issue, relevant only if you have multiple users using your Firefly III instance. Upgrading is recommended.
|
||||
|
||||
@@ -36,12 +36,12 @@ use Illuminate\Support\Facades\URL;
|
||||
use Spatie\Html\Facades\Html;
|
||||
|
||||
return [
|
||||
'name' => envNonEmpty('APP_NAME', 'Firefly III'),
|
||||
'env' => envNonEmpty('APP_ENV', 'production'),
|
||||
'name' => envDefaultWhenEmpty(env('APP_NAME'), 'Firefly III'),
|
||||
'env' => envDefaultWhenEmpty(env('APP_ENV'), 'production'),
|
||||
'debug' => env('APP_DEBUG', false),
|
||||
'url' => envNonEmpty('APP_URL', 'http://localhost'),
|
||||
'timezone' => envNonEmpty('TZ', 'UTC'),
|
||||
'locale' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),
|
||||
'url' => envDefaultWhenEmpty(env('APP_URL'), 'http://localhost'),
|
||||
'timezone' => envDefaultWhenEmpty(env('TZ'), 'UTC'),
|
||||
'locale' => envDefaultWhenEmpty(env('DEFAULT_LANGUAGE'), 'en_US'),
|
||||
'fallback_locale' => 'en_US',
|
||||
'key' => env('APP_KEY'),
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
@@ -37,11 +37,11 @@ return [
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
|
||||
'guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
|
||||
'passwords' => 'users',
|
||||
],
|
||||
'guard_header' => envNonEmpty('AUTHENTICATION_GUARD_HEADER', 'REMOTE_USER'),
|
||||
'guard_email' => envNonEmpty('AUTHENTICATION_GUARD_EMAIL'),
|
||||
'guard_header' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD_HEADER'), 'REMOTE_USER'),
|
||||
'guard_email' => env('AUTHENTICATION_GUARD_EMAIL'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -36,7 +36,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => envNonEmpty('CACHE_DRIVER', 'file'),
|
||||
'default' => envDefaultWhenEmpty(env('CACHE_DRIVER'), 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -41,15 +41,15 @@ if (false !== $databaseUrl) {
|
||||
}
|
||||
|
||||
// Get SSL parameters from .env file.
|
||||
$mysql_ssl_ca_dir = envNonEmpty('MYSQL_SSL_CAPATH');
|
||||
$mysql_ssl_ca_file = envNonEmpty('MYSQL_SSL_CA');
|
||||
$mysql_ssl_cert = envNonEmpty('MYSQL_SSL_CERT');
|
||||
$mysql_ssl_key = envNonEmpty('MYSQL_SSL_KEY');
|
||||
$mysql_ssl_ciphers = envNonEmpty('MYSQL_SSL_CIPHER');
|
||||
$mysql_ssl_verify = envNonEmpty('MYSQL_SSL_VERIFY_SERVER_CERT');
|
||||
$mysql_ssl_ca_dir = env('MYSQL_SSL_CAPATH');
|
||||
$mysql_ssl_ca_file = env('MYSQL_SSL_CA');
|
||||
$mysql_ssl_cert = env('MYSQL_SSL_CERT');
|
||||
$mysql_ssl_key = env('MYSQL_SSL_KEY');
|
||||
$mysql_ssl_ciphers = env('MYSQL_SSL_CIPHER');
|
||||
$mysql_ssl_verify = env('MYSQL_SSL_VERIFY_SERVER_CERT');
|
||||
|
||||
$mySqlSSLOptions = [];
|
||||
$useSSL = envNonEmpty('MYSQL_USE_SSL', false);
|
||||
$useSSL = envDefaultWhenEmpty(env('MYSQL_USE_SSL'), false);
|
||||
if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) {
|
||||
if (null !== $mysql_ssl_ca_dir) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir;
|
||||
@@ -72,19 +72,19 @@ if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) {
|
||||
}
|
||||
|
||||
return [
|
||||
'default' => envNonEmpty('DB_CONNECTION', 'mysql'),
|
||||
'default' => envDefaultWhenEmpty(env('DB_CONNECTION'), 'mysql'),
|
||||
'connections' => [
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => envNonEmpty('DB_DATABASE', storage_path('database/database.sqlite')),
|
||||
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), storage_path('database/database.sqlite')),
|
||||
'prefix' => '',
|
||||
],
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => envNonEmpty('DB_HOST', $host),
|
||||
'port' => envNonEmpty('DB_PORT', $port),
|
||||
'database' => envNonEmpty('DB_DATABASE', $database),
|
||||
'username' => envNonEmpty('DB_USERNAME', $username),
|
||||
'host' => envDefaultWhenEmpty(env('DB_HOST'), $host),
|
||||
'port' => envDefaultWhenEmpty(env('DB_PORT'), $port),
|
||||
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), $database),
|
||||
'username' => envDefaultWhenEmpty(env('DB_USERNAME'), $username),
|
||||
'password' => env('DB_PASSWORD', $password),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => 'utf8mb4',
|
||||
@@ -96,19 +96,19 @@ return [
|
||||
],
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => envNonEmpty('DB_HOST', $host),
|
||||
'port' => envNonEmpty('DB_PORT', $port),
|
||||
'database' => envNonEmpty('DB_DATABASE', $database),
|
||||
'username' => envNonEmpty('DB_USERNAME', $username),
|
||||
'host' => envDefaultWhenEmpty(env('DB_HOST'), $host),
|
||||
'port' => envDefaultWhenEmpty(env('DB_PORT'), $port),
|
||||
'database' => envDefaultWhenEmpty(env('DB_DATABASE'), $database),
|
||||
'username' => envDefaultWhenEmpty(env('DB_USERNAME'), $username),
|
||||
'password' => env('DB_PASSWORD', $password),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'search_path' => envNonEmpty('PGSQL_SCHEMA', 'public'),
|
||||
'schema' => envNonEmpty('PGSQL_SCHEMA', 'public'),
|
||||
'sslmode' => envNonEmpty('PGSQL_SSL_MODE', 'prefer'),
|
||||
'sslcert' => envNonEmpty('PGSQL_SSL_CERT'),
|
||||
'sslkey' => envNonEmpty('PGSQL_SSL_KEY'),
|
||||
'sslrootcert' => envNonEmpty('PGSQL_SSL_ROOT_CERT'),
|
||||
'search_path' => envDefaultWhenEmpty(env('PGSQL_SCHEMA'), 'public'),
|
||||
'schema' => envDefaultWhenEmpty(env('PGSQL_SCHEMA'), 'public'),
|
||||
'sslmode' => envDefaultWhenEmpty(env('PGSQL_SSL_MODE'), 'prefer'),
|
||||
'sslcert' => env('PGSQL_SSL_CERT'),
|
||||
'sslkey' => env('PGSQL_SSL_KEY'),
|
||||
'sslrootcert' => env('PGSQL_SSL_ROOT_CERT'),
|
||||
],
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
@@ -139,21 +139,21 @@ return [
|
||||
// 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'),
|
||||
],
|
||||
'default' => [
|
||||
'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'),
|
||||
'url' => envNonEmpty('REDIS_URL'),
|
||||
'path' => envNonEmpty('REDIS_PATH'),
|
||||
'host' => envNonEmpty('REDIS_HOST', '127.0.0.1'),
|
||||
'port' => envNonEmpty('REDIS_PORT', 6379),
|
||||
'scheme' => envDefaultWhenEmpty(env('REDIS_SCHEME'), 'tcp'),
|
||||
'url' => env('REDIS_URL'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => envDefaultWhenEmpty(env('REDIS_HOST'), '127.0.0.1'),
|
||||
'port' => envDefaultWhenEmpty(env('REDIS_PORT'), 6379),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
],
|
||||
'cache' => [
|
||||
'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'),
|
||||
'url' => envNonEmpty('REDIS_URL'),
|
||||
'path' => envNonEmpty('REDIS_PATH'),
|
||||
'host' => envNonEmpty('REDIS_HOST', '127.0.0.1'),
|
||||
'port' => envNonEmpty('REDIS_PORT', 6379),
|
||||
'scheme' => envDefaultWhenEmpty(env('REDIS_SCHEME'), 'tcp'),
|
||||
'url' => env('REDIS_URL'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => envDefaultWhenEmpty(env('REDIS_HOST'), '127.0.0.1'),
|
||||
'port' => envDefaultWhenEmpty(env('REDIS_PORT'), 6379),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
|
||||
@@ -75,14 +75,20 @@ return [
|
||||
'webhooks' => true,
|
||||
'handle_debts' => true,
|
||||
'expression_engine' => true,
|
||||
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
|
||||
'running_balance_column' => (bool)envDefaultWhenEmpty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-03-06',
|
||||
'build_time' => 1772780248,
|
||||
'build_time' => 1772807311,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
// Docker build info, if present:
|
||||
'is_docker' => env('IS_DOCKER', false),
|
||||
'base_image_build' => envDefaultWhenEmpty(env('BASE_IMAGE_BUILD'), '(unknown)'),
|
||||
'base_image_date' => envDefaultWhenEmpty(env('BASE_IMAGE_DATE'), '(unknown)'),
|
||||
'is_local_dev' => env('IS_LOCAL_DEV', false),
|
||||
|
||||
// generic settings
|
||||
'maxUploadSize' => 1073741824, // 1 GB
|
||||
'send_error_message' => env('SEND_ERROR_MESSAGE', true),
|
||||
@@ -91,7 +97,7 @@ return [
|
||||
// tokens and keys
|
||||
'fixer_api_key' => env('FIXER_API_KEY', ''),
|
||||
'ipinfo_token' => env('IPINFO_TOKEN', ''),
|
||||
'static_cron_token' => envNonEmpty('STATIC_CRON_TOKEN'),
|
||||
'static_cron_token' => env('STATIC_CRON_TOKEN'),
|
||||
|
||||
// flags
|
||||
'enable_external_map' => env('ENABLE_EXTERNAL_MAP', false), // no longer used, only for default.
|
||||
@@ -106,8 +112,8 @@ return [
|
||||
'tracker_url' => env('TRACKER_URL', ''),
|
||||
|
||||
// authentication settings
|
||||
'authentication_guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
|
||||
'custom_logout_url' => envNonEmpty('CUSTOM_LOGOUT_URL', ''),
|
||||
'authentication_guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
|
||||
'custom_logout_url' => envDefaultWhenEmpty(env('CUSTOM_LOGOUT_URL'), ''),
|
||||
|
||||
// static config (cannot be changed by user)
|
||||
'update_endpoint' => 'https://version.firefly-iii.org/index.json',
|
||||
@@ -188,8 +194,8 @@ return [
|
||||
'convertToPrimary' => false,
|
||||
],
|
||||
'default_currency' => 'EUR',
|
||||
'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),
|
||||
'default_locale' => envNonEmpty('DEFAULT_LOCALE', 'equal'),
|
||||
'default_language' => envDefaultWhenEmpty(env('DEFAULT_LANGUAGE'), 'en_US'),
|
||||
'default_locale' => envDefaultWhenEmpty(env('DEFAULT_LOCALE'), 'equal'),
|
||||
|
||||
// account types that may have or set a currency
|
||||
'valid_currency_account_types' => [
|
||||
@@ -218,7 +224,7 @@ return [
|
||||
'available_dark_modes' => ['light', 'dark', 'browser'],
|
||||
'bill_reminder_periods' => [90, 30, 14, 7, 0],
|
||||
'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y'],
|
||||
'valid_url_protocols' => envNonEmpty('VALID_URL_PROTOCOLS', 'http,https,ftp,ftps,mailto'), // no longer used, only for default.
|
||||
'valid_url_protocols' => envDefaultWhenEmpty(env('VALID_URL_PROTOCOLS'), 'http,https,ftp,ftps,mailto'), // no longer used, only for default.
|
||||
'allowedMimes' => [
|
||||
// plain files
|
||||
'text/plain',
|
||||
|
||||
@@ -34,8 +34,8 @@ $validChannels = ['single', 'papertrail', 'stdout', 'daily', 'syslog', 'err
|
||||
$validAuditChannels = ['audit_papertrail', 'audit_stdout', 'audit_stdout', 'audit_daily', 'audit_syslog', 'audit_errorlog'];
|
||||
|
||||
// which settings did the user set, if any?
|
||||
$defaultLogChannel = (string) envNonEmpty('LOG_CHANNEL', 'stack');
|
||||
$auditLogChannel = (string) envNonEmpty('AUDIT_LOG_CHANNEL', '');
|
||||
$defaultLogChannel = (string) envDefaultWhenEmpty(env('LOG_CHANNEL'), 'stack');
|
||||
$auditLogChannel = (string) env('AUDIT_LOG_CHANNEL');
|
||||
|
||||
if ('stack' === $defaultLogChannel) {
|
||||
$defaultChannels = ['daily', 'stdout'];
|
||||
@@ -60,8 +60,8 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => envNonEmpty('LOG_CHANNEL', 'stack'),
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'default' => envDefaultWhenEmpty(env('LOG_CHANNEL'), 'stack'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
@@ -93,11 +93,11 @@ return [
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
'handler' => SyslogUdpHandler::class,
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_HOST'),
|
||||
@@ -107,21 +107,21 @@ return [
|
||||
'stdout' => [
|
||||
'driver' => 'single',
|
||||
'path' => 'php://stdout',
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/ff3-'.PHP_SAPI.'.log'),
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
'days' => 7,
|
||||
],
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('APP_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
|
||||
/*
|
||||
@@ -130,7 +130,7 @@ return [
|
||||
*/
|
||||
'audit_papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
|
||||
'handler' => SyslogUdpHandler::class,
|
||||
'tap' => [AuditLogger::class],
|
||||
'handler_with' => [
|
||||
@@ -142,24 +142,24 @@ return [
|
||||
'driver' => 'single',
|
||||
'path' => 'php://stdout',
|
||||
'tap' => [AuditLogger::class],
|
||||
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
'audit_daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/ff3-audit.log'),
|
||||
'tap' => [AuditLogger::class],
|
||||
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
|
||||
'days' => 90,
|
||||
],
|
||||
'audit_syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'tap' => [AuditLogger::class],
|
||||
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
'audit_errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'tap' => [AuditLogger::class],
|
||||
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
|
||||
'level' => envDefaultWhenEmpty(env('AUDIT_LOG_LEVEL'), 'info'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -34,16 +34,16 @@ return [
|
||||
| and used as needed; however, this mailer will be used by default.
|
||||
|
|
||||
*/
|
||||
'default' => envNonEmpty('MAIL_MAILER', 'log'),
|
||||
'default' => envDefaultWhenEmpty(env('MAIL_MAILER'), 'log'),
|
||||
|
||||
'mailers' => [
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => envNonEmpty('MAIL_HOST', 'smtp.mailtrap.io'),
|
||||
'host' => envDefaultWhenEmpty(env('MAIL_HOST'), 'smtp.mailtrap.io'),
|
||||
'port' => (int) env('MAIL_PORT', 2525),
|
||||
'encryption' => envNonEmpty('MAIL_ENCRYPTION', 'tls'),
|
||||
'username' => envNonEmpty('MAIL_USERNAME', 'user@example.com'),
|
||||
'password' => envNonEmpty('MAIL_PASSWORD', 'password'),
|
||||
'encryption' => envDefaultWhenEmpty(env('MAIL_ENCRYPTION'), 'tls'),
|
||||
'username' => envDefaultWhenEmpty(env('MAIL_USERNAME'), 'user@example.com'),
|
||||
'password' => envDefaultWhenEmpty(env('MAIL_PASSWORD'), 'password'),
|
||||
'timeout' => null,
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
@@ -73,7 +73,7 @@ return [
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => envNonEmpty('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs'),
|
||||
'path' => envDefaultWhenEmpty(env('MAIL_SENDMAIL_COMMAND'), '/usr/sbin/sendmail -bs'),
|
||||
],
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
@@ -91,7 +91,7 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
'from' => ['address' => envNonEmpty('MAIL_FROM', 'changeme@example.com'), 'name' => 'Firefly III Mailer'],
|
||||
'from' => ['address' => envDefaultWhenEmpty(env('MAIL_FROM'), 'changeme@example.com'), 'name' => 'Firefly III Mailer'],
|
||||
'markdown' => [
|
||||
'theme' => 'default',
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'),
|
||||
'guard' => envDefaultWhenEmpty(env('AUTHENTICATION_GUARD'), 'web'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
protected static function hasForeign(string $table, string $column): bool
|
||||
private static function hasForeign(string $table, string $column): bool
|
||||
{
|
||||
$foreignKeysDefinitions = Schema::getForeignKeys($table);
|
||||
foreach ($foreignKeysDefinitions as $foreignKeyDefinition) {
|
||||
|
||||
Reference in New Issue
Block a user