Merge branch 'release/6.0.9'

This commit is contained in:
James Cole 2023-04-28 09:27:42 +02:00
commit 926665a8f5
150 changed files with 2750 additions and 1299 deletions

View File

@ -85,12 +85,11 @@ abstract class Controller extends BaseController
{
$bag = new ParameterBag();
$page = (int)request()->get('page');
if ($page < 1) {
$page = 1;
}
if ($page > (2 ^ 16)) {
$page = (2 ^ 16);
if ($page > pow(2, 16)) {
$page = pow(2, 16);
}
$bag->set('page', $page);

View File

@ -57,7 +57,7 @@ class DestroyRequest extends FormRequest
',not_assets_liabilities';
return [
'objects' => sprintf('required|min:1|string|in:%s', $valid),
'objects' => sprintf('required|max:255|min:1|string|in:%s', $valid),
'unused' => 'in:true,false',
];
}

View File

@ -73,7 +73,7 @@ class ExportRequest extends FormRequest
{
return [
'type' => 'in:csv',
'accounts' => 'min:1',
'accounts' => 'min:1|max:65536',
'start' => 'date|before:end',
'end' => 'date|after:start',
];

View File

@ -103,8 +103,8 @@ class StoreRequest extends FormRequest
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$type = $this->convertString('type');
$rules = [
'name' => 'required|min:1|uniqueAccountForUser',
'type' => 'required|min:1|'.sprintf('in:%s', $types),
'name' => 'required|max:1024|min:1|uniqueAccountForUser',
'type' => 'required|max:1024|min:1|'.sprintf('in:%s', $types),
'iban' => ['iban', 'nullable', new UniqueIban(null, $type)],
'bic' => 'bic|nullable',
'account_number' => ['between:1,255', 'nullable', new UniqueAccountNumber(null, $type)],
@ -120,7 +120,7 @@ class StoreRequest extends FormRequest
'credit_card_type' => sprintf('nullable|in:%s|required_if:account_role,ccAsset', $ccPaymentTypes),
'monthly_payment_date' => 'nullable|date|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
'liability_type' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:loan,debt,mortgage',
'liability_amount' => 'required_with:liability_start_date|min:0|numeric',
'liability_amount' => 'required_with:liability_start_date|min:0|numeric|max:1000000000',
'liability_start_date' => 'required_with:liability_amount|date',
'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit',
'interest' => 'between:0,100|numeric',

View File

@ -94,7 +94,7 @@ class UpdateRequest extends FormRequest
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'name' => sprintf('min:1|uniqueAccountForUser:%d', $account->id),
'name' => sprintf('min:1|max:1024|uniqueAccountForUser:%d', $account->id),
'type' => sprintf('in:%s', $types),
'iban' => ['iban', 'nullable', new UniqueIban($account, $this->convertString('type'))],
'bic' => 'bic|nullable',
@ -104,7 +104,7 @@ class UpdateRequest extends FormRequest
'virtual_balance' => 'numeric|nullable',
'order' => 'numeric|nullable',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'active' => [new IsBoolean()],
'include_net_worth' => [new IsBoolean()],
'account_role' => sprintf('in:%s|nullable|required_if:type,asset', $accountRoles),

View File

@ -67,7 +67,7 @@ class Request extends FormRequest
{
return [
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'amount' => 'numeric|gt:0',
'start' => 'date',
'end' => 'date',

View File

@ -82,7 +82,7 @@ class StoreRequest extends FormRequest
'amount_min' => 'numeric|gt:0|required',
'amount_max' => 'numeric|gt:0|required',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date|required',
'end_date' => 'date|after:date',
'extension_date' => 'date|after:date',

View File

@ -84,7 +84,7 @@ class UpdateRequest extends FormRequest
'amount_min' => 'numeric|gt:0',
'amount_max' => 'numeric|gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'date' => 'date',
'end_date' => 'date|after:date',
'extension_date' => 'date|after:date',

View File

@ -65,7 +65,7 @@ class StoreRequest extends FormRequest
'end' => 'required|after:start|date',
'amount' => 'required|gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
];
}
}

View File

@ -69,7 +69,7 @@ class UpdateRequest extends FormRequest
'end' => 'date',
'amount' => 'gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
];
}

View File

@ -63,7 +63,7 @@ class UpdateRequest extends FormRequest
$objectGroup = $this->route()->parameter('objectGroup');
return [
'title' => sprintf('min:1|uniqueObjectGroup:%d', $objectGroup->id),
'title' => sprintf('max:1024|min:1|uniqueObjectGroup:%d', $objectGroup->id),
'order' => 'numeric',
];
}

View File

@ -158,9 +158,9 @@ class StoreRequest extends FormRequest
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'nullable|numeric|gt:0',
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
'transactions.*.currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],

View File

@ -172,9 +172,9 @@ class UpdateRequest extends FormRequest
'transactions.*.amount' => 'numeric|gt:0',
'transactions.*.foreign_amount' => 'nullable|numeric|gt:0',
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
'transactions.*.currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],

View File

@ -130,7 +130,7 @@ class StoreRequest extends FormRequest
'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title',
'trigger' => 'required|in:store-journal,update-journal',
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue',
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
'triggers.*.stop_processing' => [new IsBoolean()],
'triggers.*.active' => [new IsBoolean()],
'actions.*.type' => 'required|in:'.implode(',', $validActions),

View File

@ -147,7 +147,7 @@ class UpdateRequest extends FormRequest
'rule_group_title' => 'nullable|between:1,255|belongsToUser:rule_groups,title',
'trigger' => 'in:store-journal,update-journal',
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue',
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
'triggers.*.stop_processing' => [new IsBoolean()],
'triggers.*.active' => [new IsBoolean()],
'actions.*.type' => 'required|in:'.implode(',', $validActions),

View File

@ -65,8 +65,8 @@ class StoreRequest extends FormRequest
public function rules(): array
{
$rules = [
'tag' => 'required|min:1|uniqueObjectForUser:tags,tag',
'description' => 'min:1|nullable',
'tag' => 'required|min:1|uniqueObjectForUser:tags,tag|max:1024',
'description' => 'min:1|nullable|max:65536',
'date' => 'date|nullable',
];

View File

@ -71,8 +71,8 @@ class UpdateRequest extends FormRequest
$tag = $this->route()->parameter('tagOrId');
// TODO check if uniqueObjectForUser is obsolete
$rules = [
'tag' => 'min:1|uniqueObjectForUser:tags,tag,'.$tag->id,
'description' => 'min:1|nullable',
'tag' => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id,
'description' => 'min:1|nullable|max:65536',
'date' => 'date|nullable',
];

View File

@ -188,9 +188,9 @@ class StoreRequest extends FormRequest
// currency info
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|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:3|exists:transaction_currencies,code|nullable',
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
// amount
'transactions.*.amount' => 'required|numeric|gt:0',
@ -225,25 +225,25 @@ class StoreRequest extends FormRequest
// other interesting fields
'transactions.*.reconciled' => [new IsBoolean()],
'transactions.*.notes' => 'min:1,max:50000|nullable',
'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',
'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',
'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',

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
@ -61,6 +62,7 @@ class UpdateRequest extends FormRequest
*/
public function getAll(): array
{
Log::debug(sprintf('Now in %s', __METHOD__));
$this->integerFields = [
'order',
@ -163,6 +165,9 @@ class UpdateRequest extends FormRequest
/** @var array $transaction */
foreach ($this->get('transactions') as $transaction) {
if(!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
// default response is to update nothing in the transaction:
$current = [];
$current = $this->getIntegerData($current, $transaction);
@ -330,9 +335,9 @@ class UpdateRequest extends FormRequest
// currency info
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
// amount
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
@ -359,25 +364,25 @@ class UpdateRequest extends FormRequest
// other interesting fields
'transactions.*.reconciled' => [new IsBoolean()],
'transactions.*.notes' => 'min:1,max:50000|nullable',
'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',
'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',
'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',
@ -398,6 +403,7 @@ class UpdateRequest extends FormRequest
*/
public function withValidator(Validator $validator): void
{
Log::debug('Now in withValidator');
/** @var TransactionGroup $transactionGroup */
$transactionGroup = $this->route()->parameter('transactionGroup');
$validator->after(

View File

@ -75,7 +75,7 @@ class StoreRequest extends FormRequest
'name' => 'required|between:1,255|unique:transaction_currencies,name',
'code' => 'required|between:3,51|unique:transaction_currencies,code',
'symbol' => 'required|between:1,51|unique:transaction_currencies,symbol',
'decimal_places' => 'between:0,20|numeric|min:0|max:20',
'decimal_places' => 'between:0,20|numeric|min:0|max:12',
'enabled' => [new IsBoolean()],
'default' => [new IsBoolean()],

View File

@ -74,7 +74,7 @@ class UpdateRequest extends FormRequest
'name' => sprintf('between:1,255|unique:transaction_currencies,name,%d', $currency->id),
'code' => sprintf('between:3,51|unique:transaction_currencies,code,%d', $currency->id),
'symbol' => sprintf('between:1,51|unique:transaction_currencies,symbol,%d', $currency->id),
'decimal_places' => 'between:0,20|numeric|min:0|max:20',
'decimal_places' => 'between:0,20|numeric|min:0|max:12',
'enabled' => [new IsBoolean()],
'default' => [new IsBoolean()],
];

View File

@ -59,9 +59,9 @@ class StoreRequest extends FormRequest
public function rules(): array
{
return [
'name' => 'required|unique:link_types,name|min:1',
'outward' => 'required|unique:link_types,outward|min:1|different:inward',
'inward' => 'required|unique:link_types,inward|min:1|different:outward',
'name' => 'required|unique:link_types,name|min:1|max:1024',
'outward' => 'required|unique:link_types,outward|min:1|different:inward|max:1024',
'inward' => 'required|unique:link_types,inward|min:1|different:outward|max:1024',
];
}
}

View File

@ -64,9 +64,9 @@ class UpdateRequest extends FormRequest
$linkType = $this->route()->parameter('linkType');
return [
'name' => [Rule::unique('link_types', 'name')->ignore($linkType->id), 'min:1'],
'outward' => ['different:inward', Rule::unique('link_types', 'outward')->ignore($linkType->id), 'min:1'],
'inward' => ['different:outward', Rule::unique('link_types', 'inward')->ignore($linkType->id), 'min:1'],
'name' => [Rule::unique('link_types', 'name')->ignore($linkType->id), 'min:1','max:1024'],
'outward' => ['different:inward', Rule::unique('link_types', 'outward')->ignore($linkType->id), 'min:1','max:1024'],
'inward' => ['different:outward', Rule::unique('link_types', 'inward')->ignore($linkType->id), 'min:1','max:1024'],
];
}
}

View File

@ -0,0 +1,263 @@
<?php
/*
* CorrectAmounts.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RuleTrigger;
use Illuminate\Console\Command;
/**
* Class ReportSkeleton
*/
class CorrectAmounts extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-amount-pos-neg';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
// auto budgets must be positive
$this->fixAutoBudgets();
// available budgets must be positive
$this->fixAvailableBudgets();
// bills must be positive (both amounts)
$this->fixBills();
// budget limits must be positive
$this->fixBudgetLimits();
// currency_exchange_rates must be positive
$this->fixExchangeRates();
// piggy_bank_repetitions must be positive
$this->fixRepetitions();
// piggy_banks must be positive
$this->fixPiggyBanks();
// recurrences_transactions amount must be positive
$this->fixRecurrences();
// rule_triggers must be positive or zero (amount_less, amount_more, amount_is)
$this->fixRuleTriggers();
return 0;
}
/**
* @return void
*/
private function fixAutoBudgets(): void
{
$set = AutoBudget::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All auto budget amounts are positive.');
return;
}
/** @var AutoBudget $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive((string)$item->amount);
$item->save();
}
$this->line(sprintf('Corrected %d auto budget amount(s).', $count));
}
/**
* @return void
*/
private function fixAvailableBudgets(): void
{
$set = AvailableBudget::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All available budget amounts are positive.');
return;
}
/** @var AvailableBudget $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive((string)$item->amount);
$item->save();
}
$this->line(sprintf('Corrected %d available budget amount(s).', $count));
}
/**
* @return void
*/
private function fixBills(): void
{
$set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All bill amounts are positive.');
return;
}
/** @var Bill $item */
foreach ($set as $item) {
$item->amount_min = app('steam')->positive((string)$item->amount_min);
$item->amount_max = app('steam')->positive((string)$item->amount_max);
$item->save();
}
}
/**
* @return void
*/
private function fixBudgetLimits(): void
{
$set = BudgetLimit::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All budget limit amounts are positive.');
return;
}
/** @var BudgetLimit $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive((string)$item->amount);
$item->save();
}
$this->line(sprintf('Corrected %d budget limit amount(s).', $count));
}
/**
* @return void
*/
private function fixExchangeRates(): void
{
$set = CurrencyExchangeRate::where('rate', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All currency exchange rates are positive.');
return;
}
/** @var BudgetLimit $item */
foreach ($set as $item) {
$item->rate = app('steam')->positive((string)$item->rate);
$item->save();
}
$this->line(sprintf('Corrected %d currency exchange rate(s).', $count));
}
/**
* @return void
*/
private function fixRepetitions(): void
{
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All piggy bank repetition amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->currentamount = app('steam')->positive((string)$item->currentamount);
$item->save();
}
$this->line(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
}
/**
* @return void
*/
private function fixPiggyBanks(): void
{
$set = PiggyBank::where('targetamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All piggy bank amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->targetamount = app('steam')->positive((string)$item->targetamount);
$item->save();
}
$this->line(sprintf('Corrected %d piggy bank amount(s).', $count));
}
/**
* @return void
*/
private function fixRecurrences(): void
{
$set = RecurrenceTransaction::where('amount', '<', 0)
->orWhere('foreign_amount', '<', 0)
->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All recurring transaction amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive((string)$item->amount);
$item->foreign_amount = app('steam')->positive((string)$item->foreign_amount);
$item->save();
}
$this->line(sprintf('Corrected %d recurring transaction amount(s).', $count));
}
/**
* @return void
*/
private function fixRuleTriggers(): void
{
$set = RuleTrigger::whereIn('trigger_type', ['amount_less', 'amount_more', 'amount_is'])->get();
$fixed = 0;
/** @var RuleTrigger $item */
foreach ($set as $item) {
// basic check:
if (-1 === bccomp((string)$item->trigger_value, '0')) {
$fixed++;
$item->trigger_value = app('steam')->positive((string)$item->trigger_value);
$item->save();
}
}
if (0 === $fixed) {
$this->info('Correct: All rule trigger amounts are positive.');
return;
}
$this->line(sprintf('Corrected %d rule trigger amount(s).', $fixed));
}
}

View File

@ -52,8 +52,10 @@ class CorrectDatabase extends Command
*/
public function handle(): int
{
$this->line('Handle Firefly III database correction commands.');
// if table does not exist, return false
if (!Schema::hasTable('users')) {
$this->error('No "users"-table, will not continue.');
return 1;
}
$commands = [
@ -61,7 +63,7 @@ class CorrectDatabase extends Command
'firefly-iii:create-link-types',
'firefly-iii:create-access-tokens',
'firefly-iii:remove-bills',
'firefly-iii:fix-negative-limits',
'firefly-iii:fix-amount-pos-neg',
'firefly-iii:enable-currencies',
'firefly-iii:fix-transfer-budgets',
'firefly-iii:fix-uneven-amount',
@ -76,16 +78,16 @@ class CorrectDatabase extends Command
'firefly-iii:fix-ob-currencies',
'firefly-iii:fix-long-descriptions',
'firefly-iii:fix-recurring-transactions',
'firefly-iii:restore-oauth-keys',
'firefly-iii:upgrade-group-information',
'firefly-iii:fix-transaction-types',
'firefly-iii:fix-frontpage-accounts',
// new!
'firefly-iii:unify-group-accounts',
'firefly-iii:trigger-credit-recalculation'
];
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));
Artisan::call($command);
$result = Artisan::output();
echo $result;
$this->line(sprintf('Now executing command "%s"', $command));
$this->call($command);
}
return 0;

View File

@ -1,67 +0,0 @@
<?php
/*
* FixBudgetLimits.php
* Copyright (c) 2022 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use DB;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Console\Command;
/**
* Class CorrectionSkeleton
*/
class FixBudgetLimits extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fixes negative budget limits';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:fix-negative-limits';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
$set = BudgetLimit::where('amount', '<', '0')->get();
if (0 === $set->count()) {
$this->info('All budget limits are OK.');
return 0;
}
$count = BudgetLimit::where('amount', '<', '0')->update(['amount' => DB::raw('amount * -1')]);
$this->info(sprintf('Fixed %d budget limit(s)', $count));
return 0;
}
}

View File

@ -25,7 +25,9 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
/**
* Class FixIbans
@ -53,6 +55,52 @@ class FixIbans extends Command
public function handle(): int
{
$accounts = Account::whereNotNull('iban')->get();
$this->filterIbans($accounts);
$this->countAndCorrectIbans($accounts);
return 0;
}
/**
* @param Collection $accounts
* @return void
*/
private function countAndCorrectIbans(Collection $accounts): void
{
$set = [];
/** @var Account $account */
foreach($accounts as $account) {
$userId = (int)$account->user_id;
$set[$userId] = $set[$userId] ?? [];
$iban = (string)$account->iban;
$type = $account->accountType->type;
if(in_array($type, [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], true)) {
$type = 'liabilities';
}
if(array_key_exists($iban, $set[$userId])) {
// iban already in use! two exceptions exist:
if(
!(AccountType::EXPENSE === $set[$userId][$iban] && AccountType::REVENUE === $type) && // allowed combination
!(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination.
) {
$this->line(sprintf('IBAN "%s" is used more than once and will be removed from %s #%d ("%s")', $iban, $account->accountType->type, $account->id, $account->name));
$account->iban = null;
$account->save();
}
}
if(!array_key_exists($iban, $set[$userId])) {
$set[$userId][$iban] = $type;
}
}
}
/**
* @param Collection $accounts
* @return void
*/
private function filterIbans(Collection $accounts): void
{
/** @var Account $account */
foreach ($accounts as $account) {
$iban = $account->iban;
@ -65,7 +113,5 @@ class FixIbans extends Command
}
}
}
return 0;
}
}

View File

@ -57,14 +57,15 @@ class ReportIntegrity extends Command
return 1;
}
$commands = [
'firefly-iii:create-group-memberships',
'firefly-iii:report-empty-objects',
'firefly-iii:report-sum',
'firefly-iii:restore-oauth-keys',
'firefly-iii:upgrade-group-information'
];
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));
Artisan::call($command);
$result = Artisan::output();
echo $result;
$this->call($command);
}
return 0;

View File

@ -1,8 +1,8 @@
<?php
/**
/*
* CreateDatabase.php
* Copyright (c) 2020 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use Illuminate\Console\Command;
use PDO;

View File

@ -2,7 +2,7 @@
/*
* CreateFirstUser.php
* Copyright (c) 2021 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command;

View File

@ -1,9 +1,29 @@
<?php
/*
* ForceDecimalSize.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

View File

@ -1,9 +1,29 @@
<?php
/*
* ForceMigration.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;

View File

@ -1,7 +1,7 @@
<?php
/**
/*
* ScanAttachments.php
* Copyright (c) 2020 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -21,7 +21,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use Crypt;
use FireflyIII\Models\Attachment;
@ -42,7 +42,7 @@ class ScanAttachments extends Command
*
* @var string
*/
protected $description = 'Rescan all attachments and re-set the MD5 hash and mime.';
protected $description = 'Rescan all attachments and re-set the correct MD5 hash and mime.';
/**
* The name and signature of the console command.

View File

@ -1,8 +1,8 @@
<?php
/**
/*
* SetLatestVersion.php
* Copyright (c) 2020 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use Illuminate\Console\Command;

View File

@ -1,7 +1,7 @@
<?php
/**
/*
* UpgradeFireflyInstructions.php
* Copyright (c) 2020 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -21,11 +21,13 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Support\System\GeneratesInstallationId;
use Illuminate\Console\Command;
use function FireflyIII\Console\Commands\str_starts_with;
/**
* Class UpgradeFireflyInstructions.
*
@ -75,14 +77,15 @@ class UpgradeFireflyInstructions extends Command
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
if (str_starts_with($version, $compare)) {
if (\str_starts_with($version, $compare)) {
$text = $config[$compare];
}
}
$this->showLine();
$this->boxed('');
if (null === $text) {
if (null === $text || '' === $text) {
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
$this->boxedInfo('There are no extra upgrade instructions.');
$this->boxed('Firefly III should be ready for use.');
@ -146,13 +149,13 @@ class UpgradeFireflyInstructions extends Command
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
if (str_starts_with($version, $compare)) {
if (\str_starts_with($version, $compare)) {
$text = $config[$compare];
}
}
$this->showLine();
$this->boxed('');
if (null === $text) {
if (null === $text || '' === $text) {
$this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version));
$this->boxedInfo('There are no extra installation instructions.');
$this->boxed('Firefly III should be ready for use.');

View File

@ -2,7 +2,7 @@
/*
* VerifySecurityAlerts.php
* Copyright (c) 2021 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,12 +22,12 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\System;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
use League\Flysystem\FilesystemException;
use Illuminate\Support\Facades\Log;
use League\Flysystem\FilesystemException;
use Storage;
/**

View File

@ -1,8 +1,8 @@
<?php
/**
/*
* DecryptDatabase.php
* Copyright (c) 2020 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
namespace FireflyIII\Console\Commands\Upgrade;
use Crypt;
use DB;
@ -30,8 +30,8 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use JsonException;
use Illuminate\Support\Facades\Log;
use JsonException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use stdClass;

View File

@ -2,7 +2,7 @@
/*
* FixPostgresSequences.php
* Copyright (c) 2021 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
namespace FireflyIII\Console\Commands\Upgrade;
use DB;
use Illuminate\Console\Command;

View File

@ -57,7 +57,6 @@ class UpgradeDatabase extends Command
{
$this->callInitialCommands();
$commands = [
// there are 14 upgrade commands.
'firefly-iii:transaction-identifiers',
'firefly-iii:migrate-to-groups',
'firefly-iii:account-currencies',
@ -75,41 +74,7 @@ class UpgradeDatabase extends Command
'firefly-iii:migrate-recurrence-type',
'firefly-iii:upgrade-liabilities',
'firefly-iii:liabilities-600',
// there are 16 verify commands.
'firefly-iii:fix-piggies',
'firefly-iii:create-link-types',
'firefly-iii:create-access-tokens',
'firefly-iii:remove-bills',
'firefly-iii:fix-negative-limits',
'firefly-iii:enable-currencies',
'firefly-iii:fix-transfer-budgets',
'firefly-iii:fix-uneven-amount',
'firefly-iii:delete-zero-amount',
'firefly-iii:delete-orphaned-transactions',
'firefly-iii:delete-empty-journals',
'firefly-iii:delete-empty-groups',
'firefly-iii:fix-account-types',
'firefly-iii:fix-account-order',
'firefly-iii:rename-meta-fields',
'firefly-iii:fix-ob-currencies',
'firefly-iii:fix-long-descriptions',
'firefly-iii:fix-recurring-transactions',
'firefly-iii:unify-group-accounts',
'firefly-iii:fix-transaction-types',
'firefly-iii:fix-frontpage-accounts',
'firefly-iii:fix-ibans',
'firefly-iii:create-group-memberships',
'firefly-iii:upgrade-group-information',
// two report commands
'firefly-iii:report-empty-objects',
'firefly-iii:report-sum',
'firefly-iii:restore-oauth-keys',
// instructions
'firefly:instructions update',
'firefly-iii:verify-security-alerts',
'firefly-iii:budget-limit-periods',
];
$args = [];
if ($this->option('force')) {
@ -117,9 +82,7 @@ class UpgradeDatabase extends Command
}
foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command));
Artisan::call($command, $args);
$result = Artisan::output();
echo $result;
$this->call($command, $args);
}
// set new DB version.
app('fireflyconfig')->set('db_version', (int)config('firefly.db_version'));
@ -129,22 +92,19 @@ class UpgradeDatabase extends Command
return 0;
}
/**
* @return void
*/
private function callInitialCommands(): void
{
$this->line('Now seeding the database...');
Artisan::call('migrate', ['--seed' => true, '--force' => true]);
$result = Artisan::output();
echo $result;
$this->call('migrate', ['--seed' => true, '--force' => true,'--no-interaction' => true]);
$this->line('Fix PostgreSQL sequences.');
Artisan::call('firefly-iii:fix-pgsql-sequences');
$result = Artisan::output();
echo $result;
$this->call('firefly-iii:fix-pgsql-sequences');
$this->line('Now decrypting the database (if necessary)...');
Artisan::call('firefly-iii:decrypt-all');
$result = Artisan::output();
echo $result;
$this->call('firefly-iii:decrypt-all');
$this->line('Done!');
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* Created.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\BudgetLimit;
use FireflyIII\Events\Event;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class Created
*/
class Created extends Event
{
use SerializesModels;
public BudgetLimit $budgetLimit;
/**
* @param BudgetLimit $budgetLimit
*/
public function __construct(BudgetLimit $budgetLimit)
{
$this->budgetLimit = $budgetLimit;
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* Created.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\BudgetLimit;
use FireflyIII\Events\Event;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class Deleted
*/
class Deleted extends Event
{
use SerializesModels;
public BudgetLimit $budgetLimit;
/**
* @param BudgetLimit $budgetLimit
*/
public function __construct(BudgetLimit $budgetLimit)
{
$this->budgetLimit = $budgetLimit;
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* Created.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Events\Model\BudgetLimit;
use FireflyIII\Events\Event;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class Updated
*/
class Updated extends Event
{
use SerializesModels;
public BudgetLimit $budgetLimit;
/**
* @param BudgetLimit $budgetLimit
*/
public function __construct(BudgetLimit $budgetLimit)
{
$this->budgetLimit = $budgetLimit;
}
}

View File

@ -122,7 +122,7 @@ class ChartJsGenerator implements GeneratorInterface
foreach ($data as $set) {
$currentSet = [
'label' => $set['label'],
'label' => $set['label'] ?? '(no label)',
'type' => $set['type'] ?? 'line',
'data' => array_values($set['entries']),
];

View File

@ -0,0 +1,218 @@
<?php
declare(strict_types=1);
/*
* BudgetLimitHandler.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Handlers\Events\Model;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use Illuminate\Support\Facades\Log;
use Spatie\Period\Boundaries;
use Spatie\Period\Period;
use Spatie\Period\Precision;
/**
* Class BudgetLimitHandler
*/
class BudgetLimitHandler
{
/**
* @param Created $event
* @return void
*/
public function created(Created $event): void
{
Log::debug(sprintf('BudgetLimitHandler::created(%s)', $event->budgetLimit->id));
$this->updateAvailableBudget($event->budgetLimit);
}
/**
* @param Updated $event
* @return void
*/
public function updated(Updated $event): void
{
Log::debug(sprintf('BudgetLimitHandler::updated(%s)', $event->budgetLimit->id));
$this->updateAvailableBudget($event->budgetLimit);
}
/**
* @param Deleted $event
* @return void
*/
public function deleted(Deleted $event): void
{
Log::debug(sprintf('BudgetLimitHandler::deleted(%s)', $event->budgetLimit->id));
$this->updateAvailableBudget($event->budgetLimit);
}
/**
* @param AvailableBudget $availableBudget
* @return void
*/
private function calculateAmount(AvailableBudget $availableBudget): void
{
$repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($availableBudget->user);
$newAmount = '0';
$abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY());
Log::debug(
sprintf(
'Now at AB #%d, ("%s" to "%s")',
$availableBudget->id,
$availableBudget->start_date->format('Y-m-d'),
$availableBudget->end_date->format('Y-m-d')
)
);
// have to recalc everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) {
Log::debug(
sprintf(
'Found interesting budget limit #%d ("%s" to "%s")',
$budgetLimit->id,
$budgetLimit->start_date->format('Y-m-d'),
$budgetLimit->end_date->format('Y-m-d')
)
);
// overlap in days:
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision: Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
// if both equal eachother, amount from this BL must be added to the AB
if ($limitPeriod->equals($abPeriod)) {
$newAmount = bcadd($newAmount, $budgetLimit->amount);
}
// if budget limit period inside AB period, can be added in full.
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
$newAmount = bcadd($newAmount, $budgetLimit->amount);
}
if (!$limitPeriod->equals($abPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
$overlap = $abPeriod->overlap($limitPeriod);
if (null !== $overlap) {
$length = $overlap->length();
$daily = bcmul($this->getDailyAmount($budgetLimit), (string)$length);
$newAmount = bcadd($newAmount, $daily);
}
}
}
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
$availableBudget->amount = $newAmount;
$availableBudget->save();
}
/**
* @param BudgetLimit $budgetLimit
* @return string
*/
private function getDailyAmount(BudgetLimit $budgetLimit): string
{
$limitPeriod = Period::make(
$budgetLimit->start_date,
$budgetLimit->end_date,
precision: Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
$days = $limitPeriod->length();
$amount = bcdiv((string)$budgetLimit->amount, (string)$days, 12);
Log::debug(
sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount)
);
return $amount;
}
/**
* @param BudgetLimit $budgetLimit
* @return void
* @throws \FireflyIII\Exceptions\FireflyException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
{
Log::debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id));
// based on the view range of the user (month week quarter etc) the budget limit could
// either overlap multiple available budget periods or be contained in a single one.
// all have to be created or updated.
$viewRange = app('preferences')->get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange);
$end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange);
$end = app('navigation')->endOfPeriod($end, $viewRange);
$user = $budgetLimit->budget->user;
// limit period in total is:
$limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
// from the start until the end of the budget limit, need to loop!
$current = clone $start;
while ($current <= $end) {
$currentEnd = app('navigation')->endOfPeriod($current, $viewRange);
// create or find AB for this particular period, and set the amount accordingly.
/** @var AvailableBudget $availableBudget */
$availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where(
'end_date',
$currentEnd->format('Y-m-d')
)->where('transaction_currency_id', $budgetLimit->transaction_currency_id)->first();
if (null !== $availableBudget) {
Log::debug('Found 1 AB, will update.');
$this->calculateAmount($availableBudget);
}
if (null === $availableBudget) {
// if not exists:
$currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
$daily = $this->getDailyAmount($budgetLimit);
$amount = bcmul($daily, (string)$currentPeriod->length(), 12);
// no need to calculate if period is equal.
if ($currentPeriod->equals($limitPeriod)) {
$amount = $budgetLimit->amount;
}
Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d')));
$availableBudget = new AvailableBudget(
[
'user_id' => $budgetLimit->budget->user->id,
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
'start_date' => $current,
'end_date' => $currentEnd,
'amount' => $amount,
]
);
$availableBudget->save();
}
// prep for next loop
$current = app('navigation')->addPeriod($current, $viewRange, 0);
}
}
}

View File

@ -89,12 +89,13 @@ class BudgetLimitController extends Controller
$collection = $this->currencyRepos->get();
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
// remove already budgeted currencies:
// remove already budgeted currencies with the same date range
$currencies = $collection->filter(
static function (TransactionCurrency $currency) use ($budgetLimits) {
/** @var AvailableBudget $budget */
foreach ($budgetLimits as $budget) {
if ($budget->transaction_currency_id === $currency->id) {
static function (TransactionCurrency $currency) use ($budgetLimits, $start, $end) {
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
if ($limit->transaction_currency_id === $currency->id && $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)
) {
return false;
}
}

View File

@ -40,9 +40,9 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use JsonException;
use Illuminate\Support\Facades\Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
@ -101,12 +101,23 @@ class IndexController extends Controller
*/
public function index(Request $request, Carbon $start = null, Carbon $end = null)
{
Log::debug('Start of IndexController::index()');
Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d')));
// collect some basic vars:
$range = app('navigation')->getViewRange(true);
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
$range = app('navigation')->getViewRange(true);
$isCustomRange = session('is_custom_range', false);
if (false === $isCustomRange) {
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
}
// overrule start and end if necessary:
if (true === $isCustomRange) {
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
$end = $end ?? session('end', today(config('app.timezone'))->endOfMonth());
}
$defaultCurrency = app('amount')->getDefaultCurrency();
$currencies = $this->currencyRepository->get();
$budgeted = '0';

View File

@ -82,8 +82,10 @@ class BudgetController extends Controller
$percentage = '0';
if (null !== $availableBudget) {
$available = $availableBudget->amount;
$percentage = bcmul(bcdiv($budgeted, $available), '100');
$available = $availableBudget->amount;
if (0 !== bccomp($available, '0')) {
$percentage = bcmul(bcdiv($budgeted, $available), '100');
}
}
// if available, get the AB for this period + currency, so the bar can be redrawn.

View File

@ -33,9 +33,9 @@ use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Log;
use phpseclib3\Crypt\RSA;
/**
@ -60,61 +60,15 @@ class InstallController extends Controller
{
// empty on purpose.
$this->upgradeCommands = [
// there are 3 initial commands
'migrate' => ['--seed' => true, '--force' => true],
'firefly-iii:fix-pgsql-sequences' => [],
'firefly-iii:decrypt-all' => [],
'firefly-iii:restore-oauth-keys' => [],
'generate-keys' => [], // an exception :(
// upgrade commands
'firefly-iii:transaction-identifiers' => [],
'firefly-iii:migrate-to-groups' => [],
'firefly-iii:account-currencies' => [],
'firefly-iii:transfer-currencies' => [],
'firefly-iii:other-currencies' => [],
'firefly-iii:migrate-notes' => [],
'firefly-iii:migrate-attachments' => [],
'firefly-iii:bills-to-rules' => [],
'firefly-iii:bl-currency' => [],
'firefly-iii:cc-liabilities' => [],
'firefly-iii:back-to-journals' => [],
'firefly-iii:rename-account-meta' => [],
'firefly-iii:migrate-recurrence-meta' => [],
'firefly-iii:migrate-tag-locations' => [],
'firefly-iii:migrate-recurrence-type' => [],
'firefly-iii:upgrade-liabilities' => [],
'firefly-iii:liabilities-600' => [],
// verify commands
'firefly-iii:fix-piggies' => [],
'firefly-iii:create-link-types' => [],
'firefly-iii:create-access-tokens' => [],
'firefly-iii:remove-bills' => [],
'firefly-iii:fix-negative-limits' => [],
'firefly-iii:enable-currencies' => [],
'firefly-iii:fix-transfer-budgets' => [],
'firefly-iii:fix-uneven-amount' => [],
'firefly-iii:delete-zero-amount' => [],
'firefly-iii:delete-orphaned-transactions' => [],
'firefly-iii:delete-empty-journals' => [],
'firefly-iii:delete-empty-groups' => [],
'firefly-iii:fix-account-types' => [],
'firefly-iii:fix-account-order' => [],
'firefly-iii:rename-meta-fields' => [],
'firefly-iii:fix-ob-currencies' => [],
'firefly-iii:fix-long-descriptions' => [],
'firefly-iii:fix-recurring-transactions' => [],
'firefly-iii:unify-group-accounts' => [],
'firefly-iii:fix-transaction-types' => [],
'firefly-iii:fix-frontpage-accounts' => [],
'firefly-iii:fix-ibans' => [],
'firefly-iii:create-group-memberships' => [],
'firefly-iii:upgrade-group-information' => [],
// final command to set the latest version in DB
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:verify-security-alerts' => [],
// there are 5 initial commands
// Check 4 places: InstallController, Docker image, UpgradeDatabase, composer.json
'migrate' => ['--seed' => true, '--force' => true],
'generate-keys' => [], // an exception :(
'firefly-iii:upgrade-database' => [],
'firefly-iii:correct-database' => [],
'firefly-iii:report-integrity' => [],
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:verify-security-alerts' => [],
];
$this->lastError = '';
@ -155,8 +109,8 @@ class InstallController extends Controller
Log::debug(sprintf('Will now run commands. Request index is %d', $requestIndex));
$indexes = array_values(array_keys($this->upgradeCommands));
if(array_key_exists($requestIndex, $indexes)) {
$command = $indexes[$requestIndex];
if (array_key_exists($requestIndex, $indexes)) {
$command = $indexes[$requestIndex];
$parameters = $this->upgradeCommands[$command];
Log::debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters);
try {

View File

@ -87,13 +87,10 @@ class Authenticate
*/
protected function authenticate($request, array $guards)
{
Log::debug(sprintf('Now in %s', __METHOD__));
if (0 === count($guards)) {
Log::debug('No guards present.');
// go for default guard:
/** @noinspection PhpUndefinedMethodInspection */
if ($this->auth->check()) {
Log::debug('Default guard says user is authenticated.');
// do an extra check on user object.
/** @noinspection PhpUndefinedMethodInspection */
/** @var User $user */
@ -104,18 +101,13 @@ class Authenticate
/** @noinspection PhpUndefinedMethodInspection */
return $this->auth->authenticate();
}
Log::debug('Guard array is not empty.');
foreach ($guards as $guard) {
Log::debug(sprintf('Now in guard loop, guard is "%s"', $guard));
if ('api' !== $guard) {
Log::debug('Guard is "api", call authenticate()');
$this->auth->guard($guard)->authenticate();
}
$result = $this->auth->guard($guard)->check();
Log::debug(sprintf('Result is %s', var_export($result, true)));
if ($result) {
Log::debug('Guard says user is authenticated.');
$user = $this->auth->guard($guard)->user();
$this->validateBlockedUser($user, $guards);
// According to PHPstan the method returns void, but we'll see.
@ -134,7 +126,6 @@ class Authenticate
*/
private function validateBlockedUser(?User $user, array $guards): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
if (null === $user) {
Log::warning('User is null, throw exception?');
}

View File

@ -110,7 +110,7 @@ class AccountFormRequest extends FormRequest
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'administration_id' => 'min:1|max:16777216|numeric',
'name' => 'required|min:1|uniqueAccountForUser',
'name' => 'required|max:1024|min:1|uniqueAccountForUser',
'opening_balance' => 'numeric|nullable|max:1000000000',
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'iban' => ['iban', 'nullable', new UniqueIban(null, $this->convertString('objectType'))],
@ -133,7 +133,7 @@ class AccountFormRequest extends FormRequest
if (null !== $account) {
// add rules:
$rules['id'] = 'belongsToUser:accounts';
$rules['name'] = 'required|min:1|uniqueAccountForUser:'.$account->id;
$rules['name'] = 'required|max:1024|min:1|uniqueAccountForUser:'.$account->id;
$rules['iban'] = ['iban', 'nullable', new UniqueIban($account, $account->accountType->type)];
}

View File

@ -81,6 +81,7 @@ class BillUpdateRequest extends FormRequest
'repeat_freq' => sprintf('required|in:%s', join(',', config('firefly.bill_periods'))),
'skip' => 'required|integer|gte:0|lte:31',
'active' => 'boolean',
'notes' => 'between:1,65536|nullable',
];
}
}

View File

@ -43,7 +43,7 @@ class LinkTypeFormRequest extends FormRequest
public function rules(): array
{
// fixed
$nameRule = 'required|min:1|unique:link_types,name';
$nameRule = 'required|max:255|min:1|unique:link_types,name';
$idRule = '';
// get parameter link:
@ -51,14 +51,14 @@ class LinkTypeFormRequest extends FormRequest
if (null !== $link) {
$idRule = 'exists:link_types,id';
$nameRule = 'required|min:1';
$nameRule = 'required|max:255|min:1';
}
return [
'id' => $idRule,
'name' => $nameRule,
'inward' => 'required|min:1|different:outward',
'outward' => 'required|min:1|different:inward',
'inward' => 'required|max:255|min:1|different:outward',
'outward' => 'required|max:255|min:1|different:inward',
];
}
}

View File

@ -45,7 +45,7 @@ class MassEditJournalRequest extends FormRequest
// fixed
return [
'description.*' => 'required|min:1,max:255',
'description.*' => 'required|min:1|max:255',
'source_id.*' => 'numeric|belongsToUser:accounts,id',
'destination_id.*' => 'numeric|belongsToUser:accounts,id',
'journals.*' => 'numeric|belongsToUser:transaction_journals,id',

View File

@ -70,7 +70,7 @@ class PiggyBankUpdateRequest extends FormRequest
'targetamount' => 'nullable|numeric|max:1000000000',
'startdate' => 'date',
'targetdate' => 'date|nullable',
'order' => 'integer|min:1',
'order' => 'integer|max:65536|min:1',
'object_group' => 'min:0|max:255',
];
}

View File

@ -157,9 +157,9 @@ class RuleFormRequest extends FormRequest
'rule_group_id' => 'required|belongsToUser:rule_groups',
'trigger' => 'required|in:store-journal,update-journal',
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|min:1|ruleTriggerValue', $contextTriggers),
'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers),
'actions.*.type' => 'required|in:'.implode(',', $validActions),
'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:255|ruleActionValue', $contextActions),
'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionValue', $contextActions),
'strict' => 'in:0,1',
];

View File

@ -66,17 +66,18 @@ class TagFormRequest extends FormRequest
/** @var Tag $tag */
$tag = $this->route()->parameter('tag');
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag';
$tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag';
if (null !== $tag) {
$idRule = 'belongsToUser:tags';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,'.$tag->id;
$tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag,'.$tag->id;
}
$rules = [
'tag' => $tagRule,
'id' => $idRule,
'description' => 'min:1|nullable',
'description' => 'max:65536|min:1|nullable',
'date' => 'date|nullable',
];
return Location::requestRules($rules);

View File

@ -49,8 +49,8 @@ class TestRuleFormRequest extends FormRequest
$validTriggers = $this->getTriggers();
return [
'rule-trigger.*' => 'required|min:1|in:'.implode(',', $validTriggers),
'rule-trigger-value.*' => 'required|min:1|ruleTriggerValue',
'rule-trigger.*' => 'required|max:1024|min:1|in:'.implode(',', $validTriggers),
'rule-trigger-value.*' => 'required|max:1024|min:1|ruleTriggerValue',
];
}
}

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Eloquent;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
@ -81,6 +84,12 @@ class BudgetLimit extends Model
/** @var array Fields that can be filled */
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount', 'transaction_currency_id'];
protected $dispatchesEvents = [
'created' => Created::class,
'updated' => Updated::class,
'deleted' => Deleted::class,
];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).
*

View File

@ -53,6 +53,7 @@ use Illuminate\Support\Carbon;
* @property-read int|null $accounts_count
* @property-read Collection<int, \FireflyIII\Models\Account> $accounts
* @property-read Collection<int, \FireflyIII\Models\Account> $accounts
* @property-read Collection<int, \FireflyIII\Models\Account> $accounts
* @mixin Eloquent
*/
class UserGroup extends Model

View File

@ -29,6 +29,9 @@ use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Events\ChangedPiggyBankAmount;
use FireflyIII\Events\DestroyedTransactionGroup;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Events\NewVersionAvailable;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
@ -42,6 +45,7 @@ use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Events\WarnUserAboutBill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
@ -160,6 +164,17 @@ class EventServiceProvider extends ServiceProvider
ChangedPiggyBankAmount::class => [
'FireflyIII\Handlers\Events\PiggyBankEventHandler@changePiggyAmount',
],
// budget related events: CRUD budget limit
Created::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
],
Updated::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
],
Deleted::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
],
];
/**
@ -169,7 +184,6 @@ class EventServiceProvider extends ServiceProvider
{
parent::boot();
$this->registerCreateEvents();
$this->registerBudgetEvents();
}
/**
@ -188,57 +202,4 @@ class EventServiceProvider extends ServiceProvider
}
);
}
/**
* TODO needs a dedicated method.
*/
protected function registerBudgetEvents(): void
{
$func = static function (BudgetLimit $limit) {
Log::debug('Trigger budget limit event.');
// find available budget with same period and same currency or create it.
// then set it or add money:
$user = $limit->budget->user;
$availableBudget = $user
->availableBudgets()
->where('start_date', $limit->start_date->format('Y-m-d'))
->where('end_date', $limit->end_date->format('Y-m-d'))
->where('transaction_currency_id', $limit->transaction_currency_id)
->first();
// update!
if (null !== $availableBudget) {
$repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($user);
$set = $repository->getAllBudgetLimitsByCurrency($limit->transactionCurrency, $limit->start_date, $limit->end_date);
$sum = (string)$set->sum('amount');
Log::debug(
sprintf(
'Because budget limit #%d had its amount changed to %s, available budget limit #%d will be updated.',
$limit->id,
$limit->amount,
$availableBudget->id
)
);
$availableBudget->amount = $sum;
$availableBudget->save();
return;
}
Log::debug('Does not exist, create it.');
// create it.
$data = [
'amount' => $limit->amount,
'start' => $limit->start_date,
'end' => $limit->end_date,
'currency_id' => $limit->transaction_currency_id,
];
$repository = app(AvailableBudgetRepositoryInterface::class);
$repository->setUser($user);
$repository->store($data);
};
BudgetLimit::created($func);
BudgetLimit::updated($func);
}
}

View File

@ -23,18 +23,20 @@ declare(strict_types=1);
namespace FireflyIII\Rules;
use Closure;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Log;
/**
* Class UniqueIban
*/
class UniqueIban implements Rule
class UniqueIban implements ValidationRule
{
private ?Account $account;
private ?string $expectedType;
private array $expectedTypes;
/**
* Create a new rule instance.
@ -45,17 +47,24 @@ class UniqueIban implements Rule
*/
public function __construct(?Account $account, ?string $expectedType)
{
$this->account = $account;
$this->expectedType = $expectedType;
$this->account = $account;
$this->expectedTypes = [];
if (null === $expectedType) {
return;
}
$this->expectedTypes = [$expectedType];
// a very basic fix to make sure we get the correct account type:
if ('expense' === $expectedType) {
$this->expectedType = AccountType::EXPENSE;
$this->expectedTypes = [AccountType::EXPENSE];
}
if ('revenue' === $expectedType) {
$this->expectedType = AccountType::REVENUE;
$this->expectedTypes = [AccountType::REVENUE];
}
if ('asset' === $expectedType) {
$this->expectedType = AccountType::ASSET;
$this->expectedTypes = [AccountType::ASSET];
}
if ('liabilities' === $expectedType) {
$this->expectedTypes = [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
}
}
@ -84,7 +93,7 @@ class UniqueIban implements Rule
if (!auth()->check()) {
return true;
}
if (null === $this->expectedType) {
if (0 === count($this->expectedTypes)) {
return true;
}
$maxCounts = $this->getMaxOccurrences();
@ -95,11 +104,11 @@ class UniqueIban implements Rule
if ($count > $max) {
Log::debug(
sprintf(
'IBAN "%s" is in use with %d account(s) of type "%s", which is too much for expected type "%s"',
'IBAN "%s" is in use with %d account(s) of type "%s", which is too much for expected types "%s"',
$value,
$count,
$type,
$this->expectedType
join(', ', $this->expectedTypes)
)
);
@ -120,14 +129,15 @@ class UniqueIban implements Rule
AccountType::ASSET => 0,
AccountType::EXPENSE => 0,
AccountType::REVENUE => 0,
'liabilities' => 0,
];
if ('expense' === $this->expectedType || AccountType::EXPENSE === $this->expectedType) {
if (in_array('expense', $this->expectedTypes, true) || in_array(AccountType::EXPENSE, $this->expectedTypes, true)) {
// IBAN should be unique amongst expense and asset accounts.
// may appear once in revenue accounts
$maxCounts[AccountType::REVENUE] = 1;
}
if ('revenue' === $this->expectedType || AccountType::REVENUE === $this->expectedType) {
if (in_array('revenue', $this->expectedTypes, true) || in_array(AccountType::REVENUE, $this->expectedTypes, true)) {
// IBAN should be unique amongst revenue and asset accounts.
// may appear once in expense accounts
$maxCounts[AccountType::EXPENSE] = 1;
@ -144,12 +154,16 @@ class UniqueIban implements Rule
*/
private function countHits(string $type, string $iban): int
{
$typesArray = [$type];
if ('liabilities' === $type) {
$typesArray = [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
}
$query
= auth()->user()
->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('accounts.iban', $iban)
->where('account_types.type', $type);
->whereIn('account_types.type', $typesArray);
if (null !== $this->account) {
$query->where('accounts.id', '!=', $this->account->id);
@ -157,4 +171,14 @@ class UniqueIban implements Rule
return $query->count();
}
/**
* @inheritDoc
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!$this->passes($attribute, $value)) {
$fail((string)trans('validation.unique_iban_for_user'));
}
}
}

View File

@ -856,12 +856,12 @@ class OperatorQuerySearch implements SearchInterface
break;
case '-tag_is_not':
case 'tag_is':
$result = $this->tagRepository->searchTag($value);
if ($result->count() > 0) {
$this->collector->setTags($result);
$result = $this->tagRepository->findByTag($value);
if (null !== $result) {
$this->collector->setTags(new Collection([$result]));
}
// no tags found means search must result in nothing.
if (0 === $result->count()) {
if (null === $result) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing();
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Validation;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Validation\Validator;
use Illuminate\Support\Facades\Log;
@ -53,6 +54,9 @@ trait GroupValidation
];
/** @var array $transaction */
foreach ($transactions as $index => $transaction) {
if(!is_array($transaction)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
$hasAccountInfo = false;
$hasJournalId = array_key_exists('transaction_journal_id', $transaction);
foreach ($keys as $key) {

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Validation;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
@ -89,6 +90,7 @@ trait TransactionValidation
Log::error(sprintf('Transactions array is not countable, because its a %s', gettype($transactions)));
return [];
}
Log::debug('Returning transactions.', $transactions);
return $transactions;
}
@ -357,6 +359,9 @@ trait TransactionValidation
* @var array $transaction
*/
foreach ($transactions as $index => $transaction) {
if(!is_int($index)) {
throw new FireflyException('Invalid data submitted: transaction is not array.');
}
$this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup);
}
}

View File

@ -2,6 +2,22 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.0.9 - 2023-04-29
### Added
- Better length validation for text fields.
### Changed
- Better calculation of available budget
### Fixed
- [Issue 7377](https://github.com/firefly-iii/firefly-iii/issues/7377) Tag search was broken
- [Issue 7389](https://github.com/firefly-iii/firefly-iii/issues/7389) Bug in charts
- [Issue 7394](https://github.com/firefly-iii/firefly-iii/issues/7394) unique iban check was broken
- [Issue 7427](https://github.com/firefly-iii/firefly-iii/issues/7427) API would not accept page 18 and up.
- [Issue 7410](https://github.com/firefly-iii/firefly-iii/issues/7410) Various dark mode color fixes
- Old documentation links fixed by @mindlessroman and @noxonad!
## 6.0.8 - 2023-04-16
### Added

View File

@ -82,7 +82,7 @@
"ext-xml": "*",
"ext-xmlwriter": "*",
"bacon/bacon-qr-code": "2.*",
"diglactic/laravel-breadcrumbs": "^8.0",
"diglactic/laravel-breadcrumbs": "^8.1",
"doctrine/dbal": "3.*",
"gdbots/query-parser": "^3.0",
"guzzlehttp/guzzle": "^7.5",
@ -104,6 +104,7 @@
"ramsey/uuid": "^4.7",
"rcrowe/twigbridge": "^0.14",
"spatie/laravel-ignition": "^2",
"spatie/period": "^2.4",
"symfony/http-client": "^6.0",
"symfony/mailgun-mailer": "^6.0",
"therobfonz/laravel-mandrill-driver": "^5.0"
@ -114,7 +115,7 @@
"fakerphp/faker": "1.*",
"filp/whoops": "2.*",
"mockery/mockery": "1.*",
"nunomaduro/larastan": "^2.5",
"nunomaduro/larastan": "^2.6",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-deprecation-rules": "^1.1",
"phpstan/phpstan-strict-rules": "^1.4",
@ -155,57 +156,17 @@
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump"
],
"post-update-cmd": [
"@php artisan config:clear",
"@php artisan route:clear",
"@php artisan twig:clean",
"@php artisan view:clear",
"@php artisan clear-compiled",
"@php artisan cache:clear",
"@php artisan firefly-iii:fix-pgsql-sequences",
"@php artisan firefly-iii:decrypt-all",
"@php artisan firefly-iii:transaction-identifiers",
"@php artisan firefly-iii:migrate-to-groups",
"@php artisan firefly-iii:account-currencies",
"@php artisan firefly-iii:transfer-currencies",
"@php artisan firefly-iii:other-currencies",
"@php artisan firefly-iii:migrate-notes",
"@php artisan firefly-iii:migrate-attachments",
"@php artisan firefly-iii:bills-to-rules",
"@php artisan firefly-iii:bl-currency",
"@php artisan firefly-iii:cc-liabilities",
"@php artisan firefly-iii:back-to-journals",
"@php artisan firefly-iii:rename-account-meta",
"@php artisan firefly-iii:migrate-recurrence-meta",
"@php artisan firefly-iii:migrate-tag-locations",
"@php artisan firefly-iii:migrate-recurrence-type",
"@php artisan firefly-iii:upgrade-liabilities",
"@php artisan firefly-iii:liabilities-600",
"@php artisan firefly-iii:fix-piggies",
"@php artisan firefly-iii:create-link-types",
"@php artisan firefly-iii:create-access-tokens",
"@php artisan firefly-iii:remove-bills",
"@php artisan firefly-iii:fix-negative-limits",
"@php artisan firefly-iii:enable-currencies",
"@php artisan firefly-iii:fix-transfer-budgets",
"@php artisan firefly-iii:fix-uneven-amount",
"@php artisan firefly-iii:delete-zero-amount",
"@php artisan firefly-iii:delete-orphaned-transactions",
"@php artisan firefly-iii:delete-empty-journals",
"@php artisan firefly-iii:delete-empty-groups",
"@php artisan firefly-iii:fix-account-types",
"@php artisan firefly-iii:fix-account-order",
"@php artisan firefly-iii:rename-meta-fields",
"@php artisan firefly-iii:fix-ob-currencies",
"@php artisan firefly-iii:fix-long-descriptions",
"@php artisan firefly-iii:fix-recurring-transactions",
"@php artisan firefly-iii:unify-group-accounts",
"@php artisan firefly-iii:fix-transaction-types",
"@php artisan firefly-iii:fix-frontpage-accounts",
"@php artisan firefly-iii:fix-ibans",
"@php artisan firefly-iii:create-group-memberships",
"@php artisan firefly-iii:report-empty-objects",
"@php artisan firefly-iii:report-sum",
"@php artisan firefly-iii:restore-oauth-keys",
"@php artisan firefly-iii:upgrade-group-information",
"@php artisan firefly-iii:set-latest-version --james-is-cool",
"@php artisan firefly:instructions update",
"@php artisan firefly-iii:verify-security-alerts",
"@php artisan passport:install"
"@php artisan firefly-iii:upgrade-database",
"@php artisan firefly-iii:correct-database",
"@php artisan firefly-iii:report-integrity",
"@php artisan passport:install",
"@php artisan firefly:instructions update"
],
"post-install-cmd": [
"@php artisan firefly:instructions install",

287
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ac90c0382ca1d0cb597ee1418a066866",
"content-hash": "3d09d838fdf529c07df3563a3d96de5c",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -62,26 +62,25 @@
},
{
"name": "brick/math",
"version": "0.10.2",
"version": "0.11.0",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "459f2781e1a08d52ee56b0b1444086e038561e3f"
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/459f2781e1a08d52ee56b0b1444086e038561e3f",
"reference": "459f2781e1a08d52ee56b0b1444086e038561e3f",
"url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
"reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.4 || ^8.0"
"php": "^8.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^9.0",
"vimeo/psalm": "4.25.0"
"vimeo/psalm": "5.0.0"
},
"type": "library",
"autoload": {
@ -106,7 +105,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.10.2"
"source": "https://github.com/brick/math/tree/0.11.0"
},
"funding": [
{
@ -114,7 +113,7 @@
"type": "github"
}
],
"time": "2022-08-10T22:54:19+00:00"
"time": "2023-01-15T23:15:59+00:00"
},
{
"name": "dasprid/enum",
@ -309,16 +308,16 @@
},
{
"name": "diglactic/laravel-breadcrumbs",
"version": "v8.1.0",
"version": "v8.1.1",
"source": {
"type": "git",
"url": "https://github.com/diglactic/laravel-breadcrumbs.git",
"reference": "ce3dfb760743c63a287dab4b8090d7bf68b321ee"
"reference": "f72a78eb3e26aea507d7888a65f15e5790864e21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/ce3dfb760743c63a287dab4b8090d7bf68b321ee",
"reference": "ce3dfb760743c63a287dab4b8090d7bf68b321ee",
"url": "https://api.github.com/repos/diglactic/laravel-breadcrumbs/zipball/f72a78eb3e26aea507d7888a65f15e5790864e21",
"reference": "f72a78eb3e26aea507d7888a65f15e5790864e21",
"shasum": ""
},
"require": {
@ -374,9 +373,9 @@
],
"support": {
"issues": "https://github.com/diglactic/laravel-breadcrumbs/issues",
"source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v8.1.0"
"source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v8.1.1"
},
"time": "2023-02-06T22:46:35+00:00"
"time": "2023-04-17T23:24:15+00:00"
},
{
"name": "doctrine/cache",
@ -1374,22 +1373,22 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.5.0",
"version": "7.5.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
"reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9",
"reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.9 || ^2.4",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@ -1482,7 +1481,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.5.0"
"source": "https://github.com/guzzle/guzzle/tree/7.5.1"
},
"funding": [
{
@ -1498,7 +1497,7 @@
"type": "tidelift"
}
],
"time": "2022-08-28T15:39:27+00:00"
"time": "2023-04-17T16:30:08+00:00"
},
{
"name": "guzzlehttp/promises",
@ -1941,16 +1940,16 @@
},
{
"name": "laravel/framework",
"version": "v10.7.1",
"version": "v10.9.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36"
"reference": "35078125f61ef0b125edf524de934f108d4b47fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/ddbbb2b50388721fe63312bb4469cae13163fd36",
"reference": "ddbbb2b50388721fe63312bb4469cae13163fd36",
"url": "https://api.github.com/repos/laravel/framework/zipball/35078125f61ef0b125edf524de934f108d4b47fd",
"reference": "35078125f61ef0b125edf524de934f108d4b47fd",
"shasum": ""
},
"require": {
@ -2137,20 +2136,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2023-04-11T14:11:49+00:00"
"time": "2023-04-25T13:47:18+00:00"
},
{
"name": "laravel/passport",
"version": "v11.8.5",
"version": "v11.8.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/passport.git",
"reference": "5417fe870a1a76628c13c79ce4c9b6fbea429bc0"
"reference": "a75f0a92136a6cf91e5c6755374f77bf4b30c411"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/passport/zipball/5417fe870a1a76628c13c79ce4c9b6fbea429bc0",
"reference": "5417fe870a1a76628c13c79ce4c9b6fbea429bc0",
"url": "https://api.github.com/repos/laravel/passport/zipball/a75f0a92136a6cf91e5c6755374f77bf4b30c411",
"reference": "a75f0a92136a6cf91e5c6755374f77bf4b30c411",
"shasum": ""
},
"require": {
@ -2215,20 +2214,20 @@
"issues": "https://github.com/laravel/passport/issues",
"source": "https://github.com/laravel/passport"
},
"time": "2023-04-04T14:06:53+00:00"
"time": "2023-04-24T20:05:58+00:00"
},
{
"name": "laravel/sanctum",
"version": "v3.2.1",
"version": "v3.2.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/sanctum.git",
"reference": "d09d69bac55708fcd4a3b305d760e673d888baf9"
"reference": "c8269dcff3cda261ce113ab9548a0253d709aa91"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/d09d69bac55708fcd4a3b305d760e673d888baf9",
"reference": "d09d69bac55708fcd4a3b305d760e673d888baf9",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/c8269dcff3cda261ce113ab9548a0253d709aa91",
"reference": "c8269dcff3cda261ce113ab9548a0253d709aa91",
"shasum": ""
},
"require": {
@ -2242,6 +2241,7 @@
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.0|^8.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.3"
},
"type": "library",
@ -2280,7 +2280,7 @@
"issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum"
},
"time": "2023-01-13T15:41:49+00:00"
"time": "2023-04-26T18:55:47+00:00"
},
{
"name": "laravel/serializable-closure",
@ -2467,16 +2467,16 @@
},
{
"name": "laravelcollective/html",
"version": "v6.4.0",
"version": "v6.4.1",
"source": {
"type": "git",
"url": "https://github.com/LaravelCollective/html.git",
"reference": "ac74f580459a5120079b8def0404e5d312a09504"
"reference": "64ddfdcaeeb8d332bd98bef442bef81e39c3910b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/ac74f580459a5120079b8def0404e5d312a09504",
"reference": "ac74f580459a5120079b8def0404e5d312a09504",
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/64ddfdcaeeb8d332bd98bef442bef81e39c3910b",
"reference": "64ddfdcaeeb8d332bd98bef442bef81e39c3910b",
"shasum": ""
},
"require": {
@ -2535,7 +2535,8 @@
"issues": "https://github.com/LaravelCollective/html/issues",
"source": "https://github.com/LaravelCollective/html"
},
"time": "2023-02-13T18:15:35+00:00"
"abandoned": "spatie/laravel-html",
"time": "2023-04-25T02:46:11+00:00"
},
{
"name": "lcobucci/clock",
@ -3886,16 +3887,16 @@
},
{
"name": "nunomaduro/collision",
"version": "v7.5.0",
"version": "v7.5.2",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
"reference": "bbbc6fb9c1ee88f8aa38e47abd15c465f946f85e"
"reference": "76b3cabda0aabda455fc3b9db6c3615f5a87c7ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/bbbc6fb9c1ee88f8aa38e47abd15c465f946f85e",
"reference": "bbbc6fb9c1ee88f8aa38e47abd15c465f946f85e",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/76b3cabda0aabda455fc3b9db6c3615f5a87c7ff",
"reference": "76b3cabda0aabda455fc3b9db6c3615f5a87c7ff",
"shasum": ""
},
"require": {
@ -3905,19 +3906,19 @@
"symfony/console": "^6.2.8"
},
"conflict": {
"phpunit/phpunit": "<10.1.0"
"phpunit/phpunit": "<10.1.2"
},
"require-dev": {
"brianium/paratest": "^7.1.3",
"laravel/framework": "^10.7.1",
"laravel/pint": "^1.8.0",
"laravel/framework": "^10.8.0",
"laravel/pint": "^1.9.0",
"laravel/sail": "^1.21.4",
"laravel/sanctum": "^3.2.1",
"laravel/tinker": "^2.8.1",
"nunomaduro/larastan": "^2.5.1",
"orchestra/testbench-core": "^8.4.2",
"pestphp/pest": "^2.5.0",
"phpunit/phpunit": "^10.1.0",
"nunomaduro/larastan": "^2.6.0",
"orchestra/testbench-core": "^8.5.0",
"pestphp/pest": "^2.5.2",
"phpunit/phpunit": "^10.1.1",
"sebastian/environment": "^6.0.1",
"spatie/laravel-ignition": "^2.1.0"
},
@ -3978,7 +3979,7 @@
"type": "patreon"
}
],
"time": "2023-04-14T10:39:16+00:00"
"time": "2023-04-22T22:12:40+00:00"
},
{
"name": "nunomaduro/termwind",
@ -5335,20 +5336,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.7.3",
"version": "4.7.4",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "433b2014e3979047db08a17a205f410ba3869cf2"
"reference": "60a4c63ab724854332900504274f6150ff26d286"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/433b2014e3979047db08a17a205f410ba3869cf2",
"reference": "433b2014e3979047db08a17a205f410ba3869cf2",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286",
"reference": "60a4c63ab724854332900504274f6150ff26d286",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10",
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
@ -5411,7 +5412,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.3"
"source": "https://github.com/ramsey/uuid/tree/4.7.4"
},
"funding": [
{
@ -5423,7 +5424,7 @@
"type": "tidelift"
}
],
"time": "2023-01-12T18:13:24+00:00"
"time": "2023-04-15T23:01:58+00:00"
},
{
"name": "rcrowe/twigbridge",
@ -5634,16 +5635,16 @@
},
{
"name": "spatie/ignition",
"version": "1.5.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/ignition.git",
"reference": "4db9c9626e4d7745efbe0b512157326190b41b65"
"reference": "fbcfcabc44e506e40c4d72fd4ddf465e272a600e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/ignition/zipball/4db9c9626e4d7745efbe0b512157326190b41b65",
"reference": "4db9c9626e4d7745efbe0b512157326190b41b65",
"url": "https://api.github.com/repos/spatie/ignition/zipball/fbcfcabc44e506e40c4d72fd4ddf465e272a600e",
"reference": "fbcfcabc44e506e40c4d72fd4ddf465e272a600e",
"shasum": ""
},
"require": {
@ -5674,7 +5675,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.4.x-dev"
"dev-main": "1.5.x-dev"
}
},
"autoload": {
@ -5713,7 +5714,7 @@
"type": "github"
}
],
"time": "2023-04-12T09:07:50+00:00"
"time": "2023-04-27T08:40:07+00:00"
},
{
"name": "spatie/laravel-ignition",
@ -5807,6 +5808,60 @@
],
"time": "2023-04-12T09:26:00+00:00"
},
{
"name": "spatie/period",
"version": "2.4.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/period.git",
"reference": "85fbbea7b24fdff0c924aeed5b109be93c025850"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/period/zipball/85fbbea7b24fdff0c924aeed5b109be93c025850",
"reference": "85fbbea7b24fdff0c924aeed5b109be93c025850",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"larapack/dd": "^1.1",
"nesbot/carbon": "^2.63",
"pestphp/pest": "^1.22",
"phpunit/phpunit": "^9.5",
"spatie/ray": "^1.31"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Period\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brent Roose",
"email": "brent@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Complex period comparisons",
"homepage": "https://github.com/spatie/period",
"keywords": [
"period",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/period/issues",
"source": "https://github.com/spatie/period/tree/2.4.0"
},
"time": "2023-02-20T14:31:09+00:00"
},
{
"name": "symfony/console",
"version": "v6.2.8",
@ -7638,32 +7693,32 @@
},
{
"name": "symfony/psr-http-message-bridge",
"version": "v2.1.4",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/psr-http-message-bridge.git",
"reference": "a125b93ef378c492e274f217874906fb9babdebb"
"reference": "28a732c05bbad801304ad5a5c674cf2970508993"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/a125b93ef378c492e274f217874906fb9babdebb",
"reference": "a125b93ef378c492e274f217874906fb9babdebb",
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/28a732c05bbad801304ad5a5c674cf2970508993",
"reference": "28a732c05bbad801304ad5a5c674cf2970508993",
"shasum": ""
},
"require": {
"php": ">=7.1",
"psr/http-message": "^1.0",
"symfony/http-foundation": "^4.4 || ^5.0 || ^6.0"
"php": ">=7.2.5",
"psr/http-message": "^1.0 || ^2.0",
"symfony/http-foundation": "^5.4 || ^6.0"
},
"require-dev": {
"nyholm/psr7": "^1.1",
"psr/log": "^1.1 || ^2 || ^3",
"symfony/browser-kit": "^4.4 || ^5.0 || ^6.0",
"symfony/config": "^4.4 || ^5.0 || ^6.0",
"symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0",
"symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0",
"symfony/http-kernel": "^4.4 || ^5.0 || ^6.0",
"symfony/phpunit-bridge": "^5.4@dev || ^6.0"
"symfony/browser-kit": "^5.4 || ^6.0",
"symfony/config": "^5.4 || ^6.0",
"symfony/event-dispatcher": "^5.4 || ^6.0",
"symfony/framework-bundle": "^5.4 || ^6.0",
"symfony/http-kernel": "^5.4 || ^6.0",
"symfony/phpunit-bridge": "^6.2"
},
"suggest": {
"nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
@ -7671,7 +7726,7 @@
"type": "symfony-bridge",
"extra": {
"branch-alias": {
"dev-main": "2.1-dev"
"dev-main": "2.2-dev"
}
},
"autoload": {
@ -7706,7 +7761,7 @@
],
"support": {
"issues": "https://github.com/symfony/psr-http-message-bridge/issues",
"source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.4"
"source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.2.0"
},
"funding": [
{
@ -7722,7 +7777,7 @@
"type": "tidelift"
}
],
"time": "2022-11-28T22:46:34+00:00"
"time": "2023-04-21T08:40:19+00:00"
},
{
"name": "symfony/routing",
@ -9409,16 +9464,16 @@
},
{
"name": "nunomaduro/larastan",
"version": "2.5.1",
"version": "v2.6.0",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/larastan.git",
"reference": "072e2c9566ae000bf66c92384fc933b81885244b"
"reference": "ccac5b25949576807862cf32ba1fce1769c06c42"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/larastan/zipball/072e2c9566ae000bf66c92384fc933b81885244b",
"reference": "072e2c9566ae000bf66c92384fc933b81885244b",
"url": "https://api.github.com/repos/nunomaduro/larastan/zipball/ccac5b25949576807862cf32ba1fce1769c06c42",
"reference": "ccac5b25949576807862cf32ba1fce1769c06c42",
"shasum": ""
},
"require": {
@ -9432,7 +9487,7 @@
"illuminate/support": "^9.47.0 || ^10.0.0",
"php": "^8.0.2",
"phpmyadmin/sql-parser": "^5.6.0",
"phpstan/phpstan": "~1.10.3"
"phpstan/phpstan": "~1.10.6"
},
"require-dev": {
"nikic/php-parser": "^4.15.2",
@ -9481,7 +9536,7 @@
],
"support": {
"issues": "https://github.com/nunomaduro/larastan/issues",
"source": "https://github.com/nunomaduro/larastan/tree/2.5.1"
"source": "https://github.com/nunomaduro/larastan/tree/v2.6.0"
},
"funding": [
{
@ -9501,7 +9556,7 @@
"type": "patreon"
}
],
"time": "2023-03-04T23:46:40+00:00"
"time": "2023-04-20T12:40:01+00:00"
},
{
"name": "phar-io/manifest",
@ -9814,16 +9869,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.18.1",
"version": "1.20.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
"reference": "6c04009f6cae6eda2f040745b6b846080ef069c2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2",
"reference": "6c04009f6cae6eda2f040745b6b846080ef069c2",
"shasum": ""
},
"require": {
@ -9853,22 +9908,22 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3"
},
"time": "2023-04-07T11:51:11+00:00"
"time": "2023-04-25T09:01:03+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.10.13",
"version": "1.10.14",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "f07bf8c6980b81bf9e49d44bd0caf2e737614a70"
"reference": "d232901b09e67538e5c86a724be841bea5768a7c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f07bf8c6980b81bf9e49d44bd0caf2e737614a70",
"reference": "f07bf8c6980b81bf9e49d44bd0caf2e737614a70",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c",
"reference": "d232901b09e67538e5c86a724be841bea5768a7c",
"shasum": ""
},
"require": {
@ -9917,7 +9972,7 @@
"type": "tidelift"
}
],
"time": "2023-04-12T19:29:52+00:00"
"time": "2023-04-19T13:47:27+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@ -10018,16 +10073,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.0",
"version": "10.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "fc4f5ee614fa82d50ecf9014b51af0a9561f3df8"
"reference": "884a0da7f9f46f28b2cb69134217fd810b793974"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fc4f5ee614fa82d50ecf9014b51af0a9561f3df8",
"reference": "fc4f5ee614fa82d50ecf9014b51af0a9561f3df8",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/884a0da7f9f46f28b2cb69134217fd810b793974",
"reference": "884a0da7f9f46f28b2cb69134217fd810b793974",
"shasum": ""
},
"require": {
@ -10046,7 +10101,7 @@
"theseer/tokenizer": "^1.2.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0"
"phpunit/phpunit": "^10.1"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@ -10084,7 +10139,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.0"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.1"
},
"funding": [
{
@ -10092,7 +10147,7 @@
"type": "github"
}
],
"time": "2023-04-13T07:08:27+00:00"
"time": "2023-04-17T12:15:40+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -10337,16 +10392,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.1.0",
"version": "10.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "5a477aea03e61329132935689ae2d73f418f5e25"
"reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5a477aea03e61329132935689ae2d73f418f5e25",
"reference": "5a477aea03e61329132935689ae2d73f418f5e25",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6f0cd95be71add539f8fd2be25b2a4a29789000b",
"reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b",
"shasum": ""
},
"require": {
@ -10360,7 +10415,7 @@
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=8.1",
"phpunit/php-code-coverage": "^10.1",
"phpunit/php-code-coverage": "^10.1.1",
"phpunit/php-file-iterator": "^4.0",
"phpunit/php-invoker": "^4.0",
"phpunit/php-text-template": "^3.0",
@ -10418,7 +10473,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.0"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.2"
},
"funding": [
{
@ -10434,7 +10489,7 @@
"type": "tidelift"
}
],
"time": "2023-04-14T05:15:09+00:00"
"time": "2023-04-22T07:38:19+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -107,7 +107,7 @@ return [
'webhooks' => true,
'handle_debts' => true,
],
'version' => '6.0.8',
'version' => '6.0.9',
'api_version' => '2.0.1',
'db_version' => 19,

View File

@ -11,7 +11,7 @@
},
"dependencies": {
"@popperjs/core": "^2.11.2",
"@quasar/extras": "^1.16.2",
"@quasar/extras": "^1.16.3",
"apexcharts": "^3.32.1",
"axios": "^0.21.1",
"axios-cache-adapter": "^2.7.3",

View File

@ -152,15 +152,15 @@ export default {
"rule_action_remove_tag_choice": "Quitar etiqueta ..",
"rule_action_remove_all_tags_choice": "Eliminar todas las etiquetas",
"rule_action_set_description_choice": "A\u00f1adir descripci\u00f3n ..",
"rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..",
"rule_action_append_description_choice": "Append description with ..",
"rule_action_prepend_description_choice": "Prepend description with ..",
"rule_action_set_source_account_choice": "Set source account to ..",
"rule_action_set_destination_account_choice": "Set destination account to ..",
"rule_action_append_notes_choice": "Append notes with ..",
"rule_action_prepend_notes_choice": "Prepend notes with ..",
"rule_action_update_piggy_choice": "A\u00f1adir \/ quitar el importe de la transacci\u00f3n de la hucha ..",
"rule_action_append_description_choice": "A\u00f1adir descripci\u00f3n con ..",
"rule_action_prepend_description_choice": "Anteponer descripci\u00f3n con ..",
"rule_action_set_source_account_choice": "Establecer cuenta de origen ..",
"rule_action_set_destination_account_choice": "Establecer cuenta de destino ..",
"rule_action_append_notes_choice": "A\u00f1adir notas con ..",
"rule_action_prepend_notes_choice": "Anteponer notas con ..",
"rule_action_clear_notes_choice": "Eliminar cualquier nota",
"rule_action_set_notes_choice": "Set notes to ..",
"rule_action_set_notes_choice": "Establecer nota ..",
"rule_action_link_to_bill_choice": "Enlazar a una factura ..",
"rule_action_convert_deposit_choice": "Convertir transacci\u00f3n en un ingreso",
"rule_action_convert_withdrawal_choice": "Convierta esta transacci\u00f3n en un gasto",

View File

@ -148,10 +148,10 @@ export default {
"rule_action_clear_category_choice": "\u6e05\u7a7a\u4efb\u4f55\u5206\u7c7b",
"rule_action_set_budget_choice": "Set budget to ..",
"rule_action_clear_budget_choice": "\u6e05\u7a7a\u4efb\u4f55\u9884\u7b97",
"rule_action_add_tag_choice": "Add tag ..",
"rule_action_remove_tag_choice": "Remove tag ..",
"rule_action_add_tag_choice": "\u6dfb\u52a0\u6807\u7b7e..",
"rule_action_remove_tag_choice": "\u79fb\u9664\u6807\u7b7e..",
"rule_action_remove_all_tags_choice": "\u79fb\u9664\u6240\u6709\u6807\u7b7e",
"rule_action_set_description_choice": "Set description to ..",
"rule_action_set_description_choice": "\u8bbe\u7f6e\u63cf\u8ff0\u4e3a",
"rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..",
"rule_action_append_description_choice": "Append description with ..",
"rule_action_prepend_description_choice": "Prepend description with ..",
@ -160,7 +160,7 @@ export default {
"rule_action_append_notes_choice": "Append notes with ..",
"rule_action_prepend_notes_choice": "Prepend notes with ..",
"rule_action_clear_notes_choice": "\u79fb\u9664\u6240\u6709\u5907\u6ce8",
"rule_action_set_notes_choice": "Set notes to ..",
"rule_action_set_notes_choice": "\u8bbe\u7f6e\u5907\u6ce8\u4e3a..",
"rule_action_link_to_bill_choice": "\u5173\u8054\u81f3\u8d26\u5355\u2026",
"rule_action_convert_deposit_choice": "\u8f6c\u6362\u4ea4\u6613\u4e3a\u6536\u5165",
"rule_action_convert_withdrawal_choice": "\u8f6c\u6362\u4ea4\u6613\u4e3a\u652f\u51fa",

View File

@ -338,7 +338,7 @@ page container: q-ma-xs (margin all, xs) AND q-mb-md to give the page content so
<q-footer class="bg-grey-8 text-white" bordered>
<q-toolbar>
<div>
<small>Firefly III v v6.0.8 &copy; James Cole, AGPL-3.0-or-later.</small>
<small>Firefly III v v6.0.9 &copy; James Cole, AGPL-3.0-or-later.</small>
</div>
</q-toolbar>
</q-footer>

View File

@ -1437,10 +1437,10 @@
core-js "^3.6.5"
core-js-compat "^3.6.5"
"@quasar/extras@^1.16.2":
version "1.16.2"
resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.16.2.tgz#fcc6374a882253051f9d7302cb9ba828f0010cdf"
integrity sha512-spDc1DrwxGts0MjmOAJ11xpxJANhCI1vEadxaw89wRQJ/QfKd0HZrwN7uN1U15cRozGRkJpdbsnP4cVXpkPysA==
"@quasar/extras@^1.16.3":
version "1.16.3"
resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.16.3.tgz#72216e2d450a2ee70613957da88191339b90de41"
integrity sha512-c9p2j4KWrWqOcCcCD9IMjgPZzAiXKSMEMc3uYbqa5mHlEJ1kl88OMaeJUcmP+INRQ29AFSEQ9tZE20jLmdnLXw==
"@quasar/fastclick@1.1.5":
version "1.1.5"

View File

@ -15,7 +15,7 @@
"devDependencies": {
"@johmun/vue-tags-input": "^2",
"@vue/compiler-sfc": "^3.2.45",
"axios": "^1.2",
"axios": "^1.3",
"bootstrap-sass": "^3",
"cross-env": "^7.0",
"font-awesome": "^4.7.0",

View File

@ -18,24 +18,22 @@
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php"
colors="true" stopOnFailure="true" processIsolation="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache"
backupStaticProperties="false">
<coverage>
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Api">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" stopOnFailure="true" processIsolation="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<coverage/>
<testsuites>
<testsuite name="Api">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
</php>
<source>
<include>
<directory suffix=".php">./app</directory>
</include>
</source>
</phpunit>

393
public/v1/css/daterangepicker-dark.css vendored Normal file
View File

@ -0,0 +1,393 @@
/**
fff = 282d32
eee = 31373e
ddd = 3f4750
ebf4f8 = 4b4f50
*/
.daterangepicker {
position: absolute;
color: inherit;
background-color: #282d32;
border-radius: 4px;
border: 1px solid #3f4750;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: sans-serif, Arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker .calendar-table {
border: 1px solid #282d32;
border-radius: 4px;
background-color: #282d32;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #31373e;
border-color: transparent;
color: inherit;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #282d32;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #4b4f50;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker:before, .daterangepicker:after {
position: absolute;
display: inline-block;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker:before {
top: -7px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-bottom: 7px solid #ccc;
}
.daterangepicker:after {
top: -6px;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
}
.daterangepicker.opensleft:before {
right: 9px;
}
.daterangepicker.opensleft:after {
right: 10px;
}
.daterangepicker.openscenter:before {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.openscenter:after {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.opensright:before {
left: 9px;
}
.daterangepicker.opensright:after {
left: 10px;
}
.daterangepicker.drop-up {
margin-top: -7px;
}
.daterangepicker.drop-up:before {
top: initial;
bottom: -7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.drop-up:after {
top: initial;
bottom: -6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
float: none;
}
.daterangepicker.single .drp-selected {
display: none;
}
.daterangepicker.show-calendar .drp-calendar {
display: block;
}
.daterangepicker.show-calendar .drp-buttons {
display: block;
}
.daterangepicker.auto-apply .drp-buttons {
display: none;
}
.daterangepicker .drp-calendar {
display: none;
max-width: 270px;
}
.daterangepicker .drp-calendar.left {
padding: 8px 0 8px 8px;
}
.daterangepicker .drp-calendar.right {
padding: 8px;
}
.daterangepicker .drp-calendar.single .calendar-table {
border: none;
}
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
color: #fff;
border: solid black;
border-width: 0 2px 2px 0;
border-radius: 0;
display: inline-block;
padding: 3px;
}
.daterangepicker .calendar-table .next span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
white-space: nowrap;
text-align: center;
vertical-align: middle;
min-width: 32px;
width: 32px;
height: 24px;
line-height: 24px;
font-size: 12px;
border-radius: 4px;
border: 1px solid transparent;
cursor: pointer;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
border-spacing: 0;
border-collapse: collapse;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: transparent;
color: #fff;
}
.daterangepicker th.month {
width: auto;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
cursor: not-allowed;
text-decoration: line-through;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin: 0 auto;
background: #eee;
border: 1px solid #eee;
padding: 2px;
outline: 0;
font-size: 12px;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 4px auto 0 auto;
line-height: 30px;
position: relative;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
.daterangepicker .drp-buttons {
clear: both;
text-align: right;
padding: 8px;
border-top: 1px solid #ddd;
display: none;
line-height: 12px;
vertical-align: middle;
}
.daterangepicker .drp-selected {
display: inline-block;
font-size: 12px;
padding-right: 8px;
}
.daterangepicker .drp-buttons .btn {
margin-left: 8px;
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
}
.daterangepicker.show-ranges .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker .ranges {
float: none;
text-align: left;
margin: 0;
}
.daterangepicker.show-calendar .ranges {
margin-top: 8px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
.daterangepicker .ranges li {
font-size: 12px;
padding: 8px 12px;
cursor: pointer;
}
.daterangepicker .ranges li:hover {
background-color: #eee;
}
.daterangepicker .ranges li.active {
background-color: #08c;
color: #fff;
}
/* Larger Screen Styling */
@media (min-width: 564px) {
.daterangepicker {
width: auto; }
.daterangepicker .ranges ul {
width: 140px; }
.daterangepicker.single .ranges ul {
width: 100%; }
.daterangepicker.single .drp-calendar.left {
clear: none; }
.daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .drp-calendar {
float: left; }
.daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .drp-calendar {
float: right; }
.daterangepicker.ltr {
direction: ltr;
text-align: left; }
.daterangepicker.ltr .drp-calendar.left {
clear: left;
margin-right: 0; }
.daterangepicker.ltr .drp-calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.daterangepicker.ltr .drp-calendar.right {
margin-left: 0; }
.daterangepicker.ltr .drp-calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.daterangepicker.ltr .drp-calendar.left .calendar-table {
padding-right: 8px; }
.daterangepicker.ltr .ranges, .daterangepicker.ltr .drp-calendar {
float: left; }
.daterangepicker.rtl {
direction: rtl;
text-align: right; }
.daterangepicker.rtl .drp-calendar.left {
clear: right;
margin-left: 0; }
.daterangepicker.rtl .drp-calendar.left .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.daterangepicker.rtl .drp-calendar.right {
margin-right: 0; }
.daterangepicker.rtl .drp-calendar.right .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.daterangepicker.rtl .drp-calendar.left .calendar-table {
padding-left: 12px; }
.daterangepicker.rtl .ranges, .daterangepicker.rtl .drp-calendar {
text-align: right;
float: right; } }
@media (min-width: 730px) {
.daterangepicker .ranges {
width: auto; }
.daterangepicker.ltr .ranges {
float: left; }
.daterangepicker.rtl .ranges {
float: right; }
.daterangepicker .drp-calendar.left {
clear: none !important; } }

View File

438
public/v1/css/daterangepicker-light.css vendored Normal file
View File

@ -0,0 +1,438 @@
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: sans-serif, Arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker:before, .daterangepicker:after {
position: absolute;
display: inline-block;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker:before {
top: -7px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-bottom: 7px solid #ccc;
}
.daterangepicker:after {
top: -6px;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
}
.daterangepicker.opensleft:before {
right: 9px;
}
.daterangepicker.opensleft:after {
right: 10px;
}
.daterangepicker.openscenter:before {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.openscenter:after {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.opensright:before {
left: 9px;
}
.daterangepicker.opensright:after {
left: 10px;
}
.daterangepicker.drop-up {
margin-top: -7px;
}
.daterangepicker.drop-up:before {
top: initial;
bottom: -7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.drop-up:after {
top: initial;
bottom: -6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
float: none;
}
.daterangepicker.single .drp-selected {
display: none;
}
.daterangepicker.show-calendar .drp-calendar {
display: block;
}
.daterangepicker.show-calendar .drp-buttons {
display: block;
}
.daterangepicker.auto-apply .drp-buttons {
display: none;
}
.daterangepicker .drp-calendar {
display: none;
max-width: 270px;
}
.daterangepicker .drp-calendar.left {
padding: 8px 0 8px 8px;
}
.daterangepicker .drp-calendar.right {
padding: 8px;
}
.daterangepicker .drp-calendar.single .calendar-table {
border: none;
}
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
color: #fff;
border: solid black;
border-width: 0 2px 2px 0;
border-radius: 0;
display: inline-block;
padding: 3px;
}
.daterangepicker .calendar-table .next span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
white-space: nowrap;
text-align: center;
vertical-align: middle;
min-width: 32px;
width: 32px;
height: 24px;
line-height: 24px;
font-size: 12px;
border-radius: 4px;
border: 1px solid transparent;
cursor: pointer;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
border-spacing: 0;
border-collapse: collapse;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: transparent;
color: #fff;
}
.daterangepicker th.month {
width: auto;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
cursor: not-allowed;
text-decoration: line-through;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin: 0 auto;
background: #eee;
border: 1px solid #eee;
padding: 2px;
outline: 0;
font-size: 12px;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 4px auto 0 auto;
line-height: 30px;
position: relative;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
.daterangepicker .drp-buttons {
clear: both;
text-align: right;
padding: 8px;
border-top: 1px solid #ddd;
display: none;
line-height: 12px;
vertical-align: middle;
}
.daterangepicker .drp-selected {
display: inline-block;
font-size: 12px;
padding-right: 8px;
}
.daterangepicker .drp-buttons .btn {
margin-left: 8px;
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
}
.daterangepicker.show-ranges .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker .ranges {
float: none;
text-align: left;
margin: 0;
}
.daterangepicker.show-calendar .ranges {
margin-top: 8px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
.daterangepicker .ranges li {
font-size: 12px;
padding: 8px 12px;
cursor: pointer;
}
.daterangepicker .ranges li:hover {
background-color: #eee;
}
.daterangepicker .ranges li.active {
background-color: #08c;
color: #fff;
}
/* Larger Screen Styling */
@media (min-width: 564px) {
.daterangepicker {
width: auto;
}
.daterangepicker .ranges ul {
width: 140px;
}
.daterangepicker.single .ranges ul {
width: 100%;
}
.daterangepicker.single .drp-calendar.left {
clear: none;
}
.daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .drp-calendar {
float: left;
}
.daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .drp-calendar {
float: right;
}
.daterangepicker.ltr {
direction: ltr;
text-align: left;
}
.daterangepicker.ltr .drp-calendar.left {
clear: left;
margin-right: 0;
}
.daterangepicker.ltr .drp-calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker.ltr .drp-calendar.right {
margin-left: 0;
}
.daterangepicker.ltr .drp-calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker.ltr .drp-calendar.left .calendar-table {
padding-right: 8px;
}
.daterangepicker.ltr .ranges, .daterangepicker.ltr .drp-calendar {
float: left;
}
.daterangepicker.rtl {
direction: rtl;
text-align: right;
}
.daterangepicker.rtl .drp-calendar.left {
clear: right;
margin-left: 0;
}
.daterangepicker.rtl .drp-calendar.left .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker.rtl .drp-calendar.right {
margin-right: 0;
}
.daterangepicker.rtl .drp-calendar.right .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker.rtl .drp-calendar.left .calendar-table {
padding-left: 12px;
}
.daterangepicker.rtl .ranges, .daterangepicker.rtl .drp-calendar {
text-align: right;
float: right;
}
}
@media (min-width: 730px) {
.daterangepicker .ranges {
width: auto;
}
.daterangepicker.ltr .ranges {
float: left;
}
.daterangepicker.rtl .ranges {
float: right;
}
.daterangepicker .drp-calendar.left {
clear: none !important;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -138,9 +138,6 @@
.skin-firefly-iii .form-control {
color: #bec5cb;
}
.skin-firefly-iii .vue-tags-input {
background: #353c42 !important;
}
.skin-firefly-iii .ti-input {
border: 1px solid #353c42 !important;
}

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,9 @@
.skin-firefly-iii .money-transfer {
color: #31708f;
}
.skin-firefly-iii .ti-new-tag-input {
background: #fff;
}
.skin-firefly-iii .main-header .navbar {
background-color: #3c8dbc;
}

View File

@ -1 +1 @@
.skin-firefly-iii .money-neutral{color:#999}.skin-firefly-iii .money-positive{color:#3c763d}.skin-firefly-iii .money-negative{color:#a94442}.skin-firefly-iii .money-transfer{color:#31708f}.skin-firefly-iii .main-header .navbar{background-color:#3c8dbc}.skin-firefly-iii .main-header .navbar .nav>li>a{color:#fff}.skin-firefly-iii .main-header .navbar .nav>li>a:hover,.skin-firefly-iii .main-header .navbar .nav>li>a:active,.skin-firefly-iii .main-header .navbar .nav>li>a:focus,.skin-firefly-iii .main-header .navbar .nav .open>a,.skin-firefly-iii .main-header .navbar .nav .open>a:hover,.skin-firefly-iii .main-header .navbar .nav .open>a:focus,.skin-firefly-iii .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-firefly-iii .main-header .navbar .sidebar-toggle{color:#fff}.skin-firefly-iii .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-firefly-iii .main-header .navbar .sidebar-toggle{color:#fff}.skin-firefly-iii .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-firefly-iii .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-firefly-iii .main-header .navbar .dropdown-menu li a{color:#fff}.skin-firefly-iii .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-firefly-iii .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-firefly-iii .main-header .logo:hover{background-color:#3b8ab8}.skin-firefly-iii .main-header li.user-header{background-color:#3c8dbc}.skin-firefly-iii .content-header{background:transparent}.skin-firefly-iii .wrapper,.skin-firefly-iii .main-sidebar,.skin-firefly-iii .left-side{background-color:#f9fafc}.skin-firefly-iii .main-sidebar{border-right:1px solid #d2d6de}.skin-firefly-iii .user-panel>.info,.skin-firefly-iii .user-panel>.info>a{color:#444}.skin-firefly-iii .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-firefly-iii .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-firefly-iii .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-firefly-iii .sidebar-menu>li:hover>a,.skin-firefly-iii .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-firefly-iii .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-firefly-iii .sidebar-menu>li.active>a{font-weight:600}.skin-firefly-iii .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-firefly-iii .sidebar a{color:#444}.skin-firefly-iii .sidebar a:hover{text-decoration:none}.skin-firefly-iii .sidebar-menu .treeview-menu>li>a{color:#777}.skin-firefly-iii .sidebar-menu .treeview-menu>li.active>a,.skin-firefly-iii .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-firefly-iii .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-firefly-iii .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-firefly-iii .sidebar-form input[type="text"],.skin-firefly-iii .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-firefly-iii .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-firefly-iii .sidebar-form input[type="text"]:focus,.skin-firefly-iii .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-firefly-iii .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-firefly-iii .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-firefly-iii.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-firefly-iii .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
.skin-firefly-iii .money-neutral{color:#999}.skin-firefly-iii .money-positive{color:#3c763d}.skin-firefly-iii .money-negative{color:#a94442}.skin-firefly-iii .money-transfer{color:#31708f}.skin-firefly-iii .ti-new-tag-input{background:#fff}.skin-firefly-iii .main-header .navbar{background-color:#3c8dbc}.skin-firefly-iii .main-header .navbar .nav>li>a{color:#fff}.skin-firefly-iii .main-header .navbar .nav>li>a:hover,.skin-firefly-iii .main-header .navbar .nav>li>a:active,.skin-firefly-iii .main-header .navbar .nav>li>a:focus,.skin-firefly-iii .main-header .navbar .nav .open>a,.skin-firefly-iii .main-header .navbar .nav .open>a:hover,.skin-firefly-iii .main-header .navbar .nav .open>a:focus,.skin-firefly-iii .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-firefly-iii .main-header .navbar .sidebar-toggle{color:#fff}.skin-firefly-iii .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-firefly-iii .main-header .navbar .sidebar-toggle{color:#fff}.skin-firefly-iii .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-firefly-iii .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-firefly-iii .main-header .navbar .dropdown-menu li a{color:#fff}.skin-firefly-iii .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-firefly-iii .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-firefly-iii .main-header .logo:hover{background-color:#3b8ab8}.skin-firefly-iii .main-header li.user-header{background-color:#3c8dbc}.skin-firefly-iii .content-header{background:transparent}.skin-firefly-iii .wrapper,.skin-firefly-iii .main-sidebar,.skin-firefly-iii .left-side{background-color:#f9fafc}.skin-firefly-iii .main-sidebar{border-right:1px solid #d2d6de}.skin-firefly-iii .user-panel>.info,.skin-firefly-iii .user-panel>.info>a{color:#444}.skin-firefly-iii .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-firefly-iii .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-firefly-iii .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-firefly-iii .sidebar-menu>li:hover>a,.skin-firefly-iii .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-firefly-iii .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-firefly-iii .sidebar-menu>li.active>a{font-weight:600}.skin-firefly-iii .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-firefly-iii .sidebar a{color:#444}.skin-firefly-iii .sidebar a:hover{text-decoration:none}.skin-firefly-iii .sidebar-menu .treeview-menu>li>a{color:#777}.skin-firefly-iii .sidebar-menu .treeview-menu>li.active>a,.skin-firefly-iii .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-firefly-iii .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-firefly-iii .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-firefly-iii .sidebar-form input[type="text"],.skin-firefly-iii .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-firefly-iii .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-firefly-iii .sidebar-form input[type="text"]:focus,.skin-firefly-iii .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-firefly-iii .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-firefly-iii .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-firefly-iii.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-firefly-iii .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More