Merge branch 'release/4.2.1'

This commit is contained in:
James Cole 2016-12-08 21:23:19 +01:00
commit 529bab1112
259 changed files with 3848 additions and 5568 deletions

View File

@ -2,6 +2,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [4.2.1] - 2015-05-25
### Added
- BIC support (see #430)
- New category report section and chart (see the general financial report)
### Changed
- Date range picker now also available on mobile devices (see #435)
- Extended range of amounts for issue #439
- Rewrote all routes. Old bookmarks may break.
## [4.2.0] - 2016-11-27 ## [4.2.0] - 2016-11-27
### Added ### Added
- Lots of (empty) tests - Lots of (empty) tests

View File

@ -72,8 +72,7 @@ class UpgradeDatabase extends Command
} }
$subQuery = TransactionJournal $subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id']) ->groupBy(['transaction_journals.id'])
@ -98,8 +97,7 @@ class UpgradeDatabase extends Command
try { try {
/** @var Transaction $opposing */ /** @var Transaction $opposing */
$opposing = Transaction $opposing = Transaction::where('transaction_journal_id', $journalId)
::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0) ->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed) ->whereNotIn('id', $processed)
->first(); ->first();

View File

@ -102,8 +102,7 @@ class VerifyDatabase extends Command
*/ */
private function reportAccounts() private function reportAccounts()
{ {
$set = Account $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id') ->leftJoin('users', 'accounts.user_id', '=', 'users.id')
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
->whereNull('transactions.account_id') ->whereNull('transactions.account_id')
@ -125,8 +124,7 @@ class VerifyDatabase extends Command
*/ */
private function reportBudgetLimits() private function reportBudgetLimits()
{ {
$set = Budget $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id') ->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']) ->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id') ->whereNull('budget_limits.id')
@ -148,8 +146,7 @@ class VerifyDatabase extends Command
*/ */
private function reportDeletedAccounts() private function reportDeletedAccounts()
{ {
$set = Account $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNotNull('accounts.deleted_at') ->whereNotNull('accounts.deleted_at')
->whereNotNull('transactions.id') ->whereNotNull('transactions.id')
@ -187,8 +184,7 @@ class VerifyDatabase extends Command
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE], TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
]; ];
foreach ($configuration as $transactionType => $accountTypes) { foreach ($configuration as $transactionType => $accountTypes) {
$set = TransactionJournal $set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
@ -218,8 +214,7 @@ class VerifyDatabase extends Command
*/ */
private function reportJournals() private function reportJournals()
{ {
$set = TransactionJournal $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transaction_journals.deleted_at')// USE THIS ->whereNotNull('transaction_journals.deleted_at')// USE THIS
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->whereNotNull('transactions.id') ->whereNotNull('transactions.id')
@ -245,8 +240,7 @@ class VerifyDatabase extends Command
*/ */
private function reportNoTransactions() private function reportNoTransactions()
{ {
$set = TransactionJournal $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id') ->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id') ->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']); ->get(['transaction_journals.id']);
@ -335,8 +329,7 @@ class VerifyDatabase extends Command
*/ */
private function reportTransfersBudgets() private function reportTransfersBudgets()
{ {
$set = TransactionJournal $set = TransactionJournal::distinct()
::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->where('transaction_types.type', TransactionType::TRANSFER) ->where('transaction_types.type', TransactionType::TRANSFER)

View File

@ -292,8 +292,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
private function getWorkSet() private function getWorkSet()
{ {
$accountIds = $this->accounts->pluck('id')->toArray(); $accountIds = $this->accounts->pluck('id')->toArray();
$this->workSet = Transaction $this->workSet = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin( ->leftJoin(
'transactions AS opposing', function (JoinClause $join) { 'transactions AS opposing', function (JoinClause $join) {
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id') $join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')

View File

@ -44,4 +44,11 @@ interface BudgetChartGeneratorInterface
* @return array * @return array
*/ */
public function period(array $entries): array; public function period(array $entries): array;
/**
* @param array $entries
*
* @return array
*/
public function periodNoBudget(array $entries): array;
} }

View File

@ -104,7 +104,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function period(array $entries) : array public function period(array $entries): array
{ {
$data = [ $data = [
@ -133,4 +133,32 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
return $data; return $data;
} }
/**
* @param array $entries
*
* @return array
*/
public function periodNoBudget(array $entries): array
{
$data = [
'labels' => array_keys($entries),
'datasets' => [
0 => [
'label' => trans('firefly.spent'),
'data' => [],
],
],
'count' => 1,
];
foreach ($entries as $label => $entry) {
// data set 0 is budgeted
// data set 1 is spent:
$data['datasets'][0]['data'][] = round(($entry['spent'] * -1), 2);
}
return $data;
}
} }

View File

@ -22,7 +22,6 @@ use Illuminate\Support\Collection;
*/ */
interface CategoryChartGeneratorInterface interface CategoryChartGeneratorInterface
{ {
/** /**
* @param Collection $entries * @param Collection $entries
* *
@ -58,4 +57,11 @@ interface CategoryChartGeneratorInterface
*/ */
public function pieChart(array $data): array; public function pieChart(array $data): array;
/**
* @param array $entries
*
* @return array
*/
public function reportPeriod(array $entries): array;
} }

View File

@ -142,6 +142,41 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
} }
/**
* @param array $entries
*
* @return array
*/
public function reportPeriod(array $entries): array
{
$data = [
'labels' => array_keys($entries),
'datasets' => [
0 => [
'label' => trans('firefly.earned'),
'data' => [],
],
1 => [
'label' => trans('firefly.spent'),
'data' => [],
],
],
'count' => 2,
];
foreach ($entries as $label => $entry) {
// data set 0 is budgeted
// data set 1 is spent:
$data['datasets'][0]['data'][] = round($entry['earned'], 2);
$data['datasets'][1]['data'][] = round(bcmul($entry['spent'], '-1'), 2);
}
return $data;
}
/** /**
* @param array $entries * @param array $entries
* *

View File

@ -29,7 +29,7 @@ class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function multiYearInOut(Collection $entries): array public function multiYearOperations(Collection $entries): array
{ {
$data = [ $data = [
'count' => 2, 'count' => 2,
@ -62,7 +62,7 @@ class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function multiYearInOutSummarized(string $income, string $expense, int $count): array public function multiYearSum(string $income, string $expense, int $count): array
{ {
$data = [ $data = [
'count' => 2, 'count' => 2,
@ -117,7 +117,7 @@ class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function yearInOut(Collection $entries): array public function yearOperations(Collection $entries): array
{ {
// language: // language:
$format = (string)trans('config.month'); $format = (string)trans('config.month');
@ -153,7 +153,7 @@ class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function yearInOutSummarized(string $income, string $expense, int $count): array public function yearSum(string $income, string $expense, int $count): array
{ {
$data = [ $data = [

View File

@ -28,7 +28,7 @@ interface ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function multiYearInOut(Collection $entries): array; public function multiYearOperations(Collection $entries): array;
/** /**
* @param string $income * @param string $income
@ -37,7 +37,7 @@ interface ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function multiYearInOutSummarized(string $income, string $expense, int $count): array; public function multiYearSum(string $income, string $expense, int $count): array;
/** /**
* @param Collection $entries * @param Collection $entries
@ -51,7 +51,7 @@ interface ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function yearInOut(Collection $entries): array; public function yearOperations(Collection $entries): array;
/** /**
* @param string $income * @param string $income
@ -60,6 +60,6 @@ interface ReportChartGeneratorInterface
* *
* @return array * @return array
*/ */
public function yearInOutSummarized(string $income, string $expense, int $count): array; public function yearSum(string $income, string $expense, int $count): array;
} }

View File

@ -78,9 +78,9 @@ class MonthReportGenerator implements ReportGeneratorInterface
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance; $auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
} }
$reportType = 'audit'; $defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
$accountIds = join(',', $this->accounts->pluck('id')->toArray()); $reportType = 'audit';
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', $hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
'interest_date', 'book_date', 'process_date', 'interest_date', 'book_date', 'process_date',
// three new optional fields. // three new optional fields.
@ -88,10 +88,9 @@ class MonthReportGenerator implements ReportGeneratorInterface
'from', 'to', 'budget', 'category', 'bill', 'from', 'to', 'budget', 'category', 'bill',
// more new optional fields // more new optional fields
'internal_reference', 'notes', 'internal_reference', 'notes',
'create_date', 'update_date', 'create_date', 'update_date',
]; ];
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow'))
->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts)

View File

@ -148,8 +148,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
// is not set? // is not set?
if (!isset($result[$opposingId])) { if (!isset($result[$opposingId])) {
$name = $transaction->opposing_account_name; $name = $transaction->opposing_account_name;
$encrypted = intval($transaction->opposing_account_encrypted);
$name = $encrypted === 1 ? Crypt::decrypt($name) : $name;
$result[$opposingId] = [ $result[$opposingId] = [
'name' => $name, 'name' => $name,
'count' => 1, 'count' => 1,
@ -267,14 +265,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
{ {
$transactions = $this->getExpenses()->sortBy('transaction_amount'); $transactions = $this->getExpenses()->sortBy('transaction_amount');
$transactions = $transactions->each(
function (Transaction $transaction) {
if (intval($transaction->opposing_account_encrypted) === 1) {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
}
}
);
return $transactions; return $transactions;
} }
@ -285,14 +275,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
{ {
$transactions = $this->getIncome()->sortByDesc('transaction_amount'); $transactions = $this->getIncome()->sortByDesc('transaction_amount');
$transactions = $transactions->each(
function (Transaction $transaction) {
if (intval($transaction->opposing_account_encrypted) === 1) {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
}
}
);
return $transactions; return $transactions;
} }

View File

@ -34,25 +34,25 @@ class BudgetEventHandler
/** /**
* This method creates a new budget limit repetition when a new budget limit has been created. * This method creates a new budget limit repetition when a new budget limit has been created.
* *
* @param StoredBudgetLimit $event * @param StoredBudgetLimit $budgetLimitEvent
* *
* @return bool * @return bool
*/ */
public function storeRepetition(StoredBudgetLimit $event):bool public function storeRepetition(StoredBudgetLimit $budgetLimitEvent):bool
{ {
return $this->processRepetitionChange($event->budgetLimit, $event->end); return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
} }
/** /**
* Updates, if present the budget limit repetition part of a budget limit. * Updates, if present the budget limit repetition part of a budget limit.
* *
* @param UpdatedBudgetLimit $event * @param UpdatedBudgetLimit $budgetLimitEvent
* *
* @return bool * @return bool
*/ */
public function updateRepetition(UpdatedBudgetLimit $event): bool public function updateRepetition(UpdatedBudgetLimit $budgetLimitEvent): bool
{ {
return $this->processRepetitionChange($event->budgetLimit, $event->end); return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
} }
/** /**

View File

@ -33,15 +33,15 @@ class StoredJournalEventHandler
/** /**
* This method connects a new transfer to a piggy bank. * This method connects a new transfer to a piggy bank.
* *
* @param StoredTransactionJournal $event * @param StoredTransactionJournal $storedJournalEvent
* *
* @return bool * @return bool
*/ */
public function connectToPiggyBank(StoredTransactionJournal $event): bool public function connectToPiggyBank(StoredTransactionJournal $storedJournalEvent): bool
{ {
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $event->journal; $journal = $storedJournalEvent->journal;
$piggyBankId = $event->piggyBankId; $piggyBankId = $storedJournalEvent->piggyBankId;
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId)); Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
@ -101,11 +101,11 @@ class StoredJournalEventHandler
$repetition->currentamount = bcadd($repetition->currentamount, $amount); $repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save(); $repetition->save();
/** @var PiggyBankEvent $event */ /** @var PiggyBankEvent $storedJournalEvent */
$event = PiggyBankEvent::create( $storedJournalEvent = PiggyBankEvent::create(
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount] ['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
); );
Log::debug(sprintf('Created piggy bank event #%d', $event->id)); Log::debug(sprintf('Created piggy bank event #%d', $storedJournalEvent->id));
return true; return true;
} }
@ -113,14 +113,14 @@ class StoredJournalEventHandler
/** /**
* This method grabs all the users rules and processes them. * This method grabs all the users rules and processes them.
* *
* @param StoredTransactionJournal $event * @param StoredTransactionJournal $storedJournalEvent
* *
* @return bool * @return bool
*/ */
public function processRules(StoredTransactionJournal $event): bool public function processRules(StoredTransactionJournal $storedJournalEvent): bool
{ {
// get all the user's rule groups, with the rules, order by 'order'. // get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal; $journal = $storedJournalEvent->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); $groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
// //
/** @var RuleGroup $group */ /** @var RuleGroup $group */
@ -150,13 +150,13 @@ class StoredJournalEventHandler
/** /**
* This method calls a special bill scanner that will check if the stored journal is part of a bill. * This method calls a special bill scanner that will check if the stored journal is part of a bill.
* *
* @param StoredTransactionJournal $event * @param StoredTransactionJournal $storedJournalEvent
* *
* @return bool * @return bool
*/ */
public function scanBills(StoredTransactionJournal $event): bool public function scanBills(StoredTransactionJournal $storedJournalEvent): bool
{ {
$journal = $event->journal; $journal = $storedJournalEvent->journal;
BillScanner::scan($journal); BillScanner::scan($journal);
return true; return true;

View File

@ -31,14 +31,14 @@ class UpdatedJournalEventHandler
/** /**
* This method will check all the rules when a journal is updated. * This method will check all the rules when a journal is updated.
* *
* @param UpdatedTransactionJournal $event * @param UpdatedTransactionJournal $updatedJournalEvent
* *
* @return bool * @return bool
*/ */
public function processRules(UpdatedTransactionJournal $event):bool public function processRules(UpdatedTransactionJournal $updatedJournalEvent):bool
{ {
// get all the user's rule groups, with the rules, order by 'order'. // get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal; $journal = $updatedJournalEvent->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(); $groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
// //
/** @var RuleGroup $group */ /** @var RuleGroup $group */
@ -67,13 +67,13 @@ class UpdatedJournalEventHandler
/** /**
* This method calls a special bill scanner that will check if the updated journal is part of a bill. * This method calls a special bill scanner that will check if the updated journal is part of a bill.
* *
* @param UpdatedTransactionJournal $event * @param UpdatedTransactionJournal $updatedJournalEvent
* *
* @return bool * @return bool
*/ */
public function scanBills(UpdatedTransactionJournal $event): bool public function scanBills(UpdatedTransactionJournal $updatedJournalEvent): bool
{ {
$journal = $event->journal; $journal = $updatedJournalEvent->journal;
BillScanner::scan($journal); BillScanner::scan($journal);
return true; return true;

View File

@ -1,85 +0,0 @@
<?php
/**
* Expense.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
use stdClass;
/**
*
* Class Expense
*
* @package FireflyIII\Helpers\Collection
*/
class Expense
{
/** @var Collection */
protected $expenses;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->expenses = new Collection;
}
/**
* @param stdClass $entry
*/
public function addOrCreateExpense(stdClass $entry)
{
$this->expenses->put($entry->id, $entry);
}
/**
* @param string $add
*/
public function addToTotal(string $add)
{
$add = strval(round($add, 2));
if (bccomp('0', $add) === -1) {
$add = bcmul($add, '-1');
}
// if amount is positive, the original transaction
// was a transfer. But since this is an expense report,
// that amount must be negative.
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getExpenses(): Collection
{
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}
);
return $set;
}
/**
* @return string
*/
public function getTotal(): string
{
return strval(round($this->total, 2));
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* Income.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
use stdClass;
/**
*
* Class Income
*
* @package FireflyIII\Helpers\Collection
*/
class Income
{
/** @var Collection */
protected $incomes;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->incomes = new Collection;
}
/**
* @param stdClass $entry
*/
public function addOrCreateIncome(stdClass $entry)
{
$this->incomes->put($entry->id, $entry);
}
/**
* @param string $add
*/
public function addToTotal(string $add)
{
$add = strval(round($add, 2));
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getIncomes(): Collection
{
$set = $this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $set;
}
/**
* @return string
*/
public function getTotal(): string
{
return strval(round($this->total, 2));
}
}

View File

@ -1,4 +1,14 @@
<?php <?php
/**
* JournalCollector.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1); declare(strict_types = 1);
namespace FireflyIII\Helpers\Collector; namespace FireflyIII\Helpers\Collector;
@ -16,6 +26,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
@ -60,6 +71,8 @@ class JournalCollector implements JournalCollectorInterface
'account_types.type as account_type', 'account_types.type as account_type',
]; ];
/** @var bool */ /** @var bool */
private $filterInternalTransfers;
/** @var bool */
private $filterTransfers = false; private $filterTransfers = false;
/** @var bool */ /** @var bool */
private $joinedBudget = false; private $joinedBudget = false;
@ -127,6 +140,26 @@ class JournalCollector implements JournalCollectorInterface
return $this; return $this;
} }
/**
* @return JournalCollectorInterface
*/
public function disableInternalFilter(): JournalCollectorInterface
{
$this->filterInternalTransfers = false;
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function enableInternalFilter(): JournalCollectorInterface
{
$this->filterInternalTransfers = true;
return $this;
}
/** /**
* @return Collection * @return Collection
*/ */
@ -138,12 +171,25 @@ class JournalCollector implements JournalCollectorInterface
$set = $this->filterTransfers($set); $set = $this->filterTransfers($set);
Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count())); Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count()));
// possibly filter "internal" transfers:
$set = $this->filterInternalTransfers($set);
Log::debug(sprintf('Count of set after filterInternalTransfers() is %d', $set->count()));
// loop for decryption. // loop for decryption.
$set->each( $set->each(
function (Transaction $transaction) { function (Transaction $transaction) {
$transaction->date = new Carbon($transaction->date); $transaction->date = new Carbon($transaction->date);
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
// optionally decrypted:
try {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
} catch (DecryptException $e) {
// if this fails its already decrypted.
}
} }
); );
@ -501,6 +547,47 @@ class JournalCollector implements JournalCollectorInterface
return $this; return $this;
} }
/**
* @param Collection $set
*
* @return Collection
*/
private function filterInternalTransfers(Collection $set): Collection
{
if ($this->filterInternalTransfers === false) {
Log::debug('Did NO filtering for internal transfers on given set.');
return $set;
}
if ($this->joinedOpposing === false) {
Log::error('Cannot filter internal transfers because no opposing information is present.');
return $set;
}
$accountIds = $this->accountIds;
$set = $set->filter(
function (Transaction $transaction) use ($accountIds) {
// both id's in $accountids?
if (in_array($transaction->account_id, $accountIds) && in_array($transaction->opposing_account_id, $accountIds)) {
Log::debug(
sprintf(
'Transaction #%d has #%d and #%d in set, so removed',
$transaction->id, $transaction->account_id, $transaction->opposing_account_id
), $accountIds
);
return false;
}
return $transaction;
}
);
return $set;
}
/** /**
* If the set of accounts used by the collector includes more than one asset * If the set of accounts used by the collector includes more than one asset
* account, chances are the set include double entries: transfers get selected * account, chances are the set include double entries: transfers get selected
@ -583,6 +670,7 @@ class JournalCollector implements JournalCollectorInterface
private function joinOpposingTables() private function joinOpposingTables()
{ {
if (!$this->joinedOpposing) { if (!$this->joinedOpposing) {
Log::debug('joinedOpposing is false');
// join opposing transaction (hard): // join opposing transaction (hard):
$this->query->leftJoin( $this->query->leftJoin(
'transactions as opposing', function (JoinClause $join) { 'transactions as opposing', function (JoinClause $join) {
@ -595,11 +683,12 @@ class JournalCollector implements JournalCollectorInterface
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id'); $this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
$this->query->whereNull('opposing.deleted_at'); $this->query->whereNull('opposing.deleted_at');
$this->fields[] = 'opposing.account_id as opposing_account_id'; $this->fields[] = 'opposing.account_id as opposing_account_id';
$this->fields[] = 'opposing_accounts.name as opposing_account_name'; $this->fields[] = 'opposing_accounts.name as opposing_account_name';
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted'; $this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
$this->fields[] = 'opposing_account_types.type as opposing_account_type'; $this->fields[] = 'opposing_account_types.type as opposing_account_type';
$this->joinedOpposing = true;
Log::debug('joinedOpposing is now true!');
} }
} }
@ -621,19 +710,18 @@ class JournalCollector implements JournalCollectorInterface
private function startQuery(): EloquentBuilder private function startQuery(): EloquentBuilder
{ {
$query = Transaction $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') ->whereNull('transactions.deleted_at')
->whereNull('transactions.deleted_at') ->whereNull('transaction_journals.deleted_at')
->whereNull('transaction_journals.deleted_at') ->where('transaction_journals.user_id', $this->user->id)
->where('transaction_journals.user_id', $this->user->id) ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC');
->orderBy('transaction_journals.id', 'DESC');
return $query; return $query;

View File

@ -38,6 +38,16 @@ interface JournalCollectorInterface
*/ */
public function disableFilter(): JournalCollectorInterface; public function disableFilter(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function disableInternalFilter(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function enableInternalFilter(): JournalCollectorInterface;
/** /**
* @return Collection * @return Collection
*/ */
@ -46,7 +56,7 @@ interface JournalCollectorInterface
/** /**
* @return LengthAwarePaginator * @return LengthAwarePaginator
*/ */
public function getPaginatedJournals():LengthAwarePaginator; public function getPaginatedJournals(): LengthAwarePaginator;
/** /**
* @param Collection $accounts * @param Collection $accounts

View File

@ -120,7 +120,6 @@ class Help implements HelpInterface
* @param string $language * @param string $language
* @param string $content * @param string $content
* *
* @internal param $title
*/ */
public function putInCache(string $route, string $language, string $content) public function putInCache(string $route, string $language, string $content)
{ {

View File

@ -51,13 +51,13 @@ class BalanceReportHelper implements BalanceReportHelperInterface
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return Balance * @return Balance
*/ */
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
{ {
$balance = new Balance; $balance = new Balance;
$header = new BalanceHeader; $header = new BalanceHeader;

View File

@ -26,11 +26,11 @@ use Illuminate\Support\Collection;
interface BalanceReportHelperInterface interface BalanceReportHelperInterface
{ {
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return Balance * @return Balance
*/ */
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance; public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance;
} }

View File

@ -42,22 +42,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
$this->repository = $repository; $this->repository = $repository;
} }
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$budgets = $this->repository->getBudgets();
$report = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data = $this->filterBudgetPeriodReport($report);
return $data;
}
/** /**
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@ -162,31 +146,4 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $array; return $array;
} }
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
} }

View File

@ -26,15 +26,6 @@ use Illuminate\Support\Collection;
interface BudgetReportHelperInterface interface BudgetReportHelperInterface
{ {
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array;
/** /**
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end

View File

@ -17,19 +17,15 @@ use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Bill as BillCollection; use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine; use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection; use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use stdClass;
/** /**
* Class ReportHelper * Class ReportHelper
@ -118,7 +114,7 @@ class ReportHelper implements ReportHelperInterface
* *
* @return CategoryCollection * @return CategoryCollection
*/ */
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection
{ {
$object = new CategoryCollection; $object = new CategoryCollection;
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */
@ -136,57 +132,6 @@ class ReportHelper implements ReportHelperInterface
return $object; return $object;
} }
/**
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense
{
$object = new Expense;
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$collection = $tasker->expenseReport($accounts, $accounts, $start, $end);
/** @var stdClass $entry */
foreach ($collection as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income
{
$object = new Income;
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$collection = $tasker->incomeReport($accounts, $accounts, $start, $end);
/** @var stdClass $entry */
foreach ($collection as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry);
}
return $object;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -49,29 +49,7 @@ interface ReportHelperInterface
* *
* @return CategoryCollection * @return CategoryCollection
*/ */
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection; public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection;
/**
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense;
/**
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income;
/** /**
* @param Carbon $date * @param Carbon $date

View File

@ -171,6 +171,7 @@ class AccountController extends Controller
'accountRole' => $account->getMeta('accountRole'), 'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'), 'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'), 'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'BIC' => $account->getMeta('BIC'),
'openingBalanceDate' => $openingBalanceDate, 'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount, 'openingBalance' => $openingBalanceAmount,
'virtualBalance' => $account->virtual_balance, 'virtualBalance' => $account->virtual_balance,
@ -281,7 +282,7 @@ class AccountController extends Controller
* *
* @return View * @return View
*/ */
public function showWithDate(Account $account, string $date) public function showByDate(Account $account, string $date)
{ {
$carbon = new Carbon($date); $carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
@ -297,7 +298,7 @@ class AccountController extends Controller
$journals = $collector->getPaginatedJournals(); $journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/' . $date); $journals->setPath('accounts/show/' . $account->id . '/' . $date);
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end')); return view('accounts.show-by-date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end'));
} }
/** /**

View File

@ -71,7 +71,7 @@ class ConfigurationController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function store(ConfigurationRequest $request) public function postIndex(ConfigurationRequest $request)
{ {
// get config values: // get config values:
$data = $request->getConfigurationData(); $data = $request->getConfigurationData();

View File

@ -38,10 +38,16 @@ class TwoFactorController extends Controller
$user = auth()->user(); $user = auth()->user();
// to make sure the validator in the next step gets the secret, we push it in session // to make sure the validator in the next step gets the secret, we push it in session
$secret = Preferences::get('twoFactorAuthSecret', '')->data; $secret = Preferences::get('twoFactorAuthSecret', null)->data;
$title = strval(trans('firefly.two_factor_title')); $title = strval(trans('firefly.two_factor_title'));
if (strlen($secret) === 0) { // make sure the user has two factor configured:
$has2FA = Preferences::get('twoFactorAuthEnabled', null)->data;
if (is_null($has2FA) || $has2FA === false) {
return redirect(route('index'));
}
if (strlen(strval($secret)) === 0) {
throw new FireflyException('Your two factor authentication secret is empty, which it cannot be at this point. Please check the log files.'); throw new FireflyException('Your two factor authentication secret is empty, which it cannot be at this point. Please check the log files.');
} }
Session::flash('two-factor-secret', $secret); Session::flash('two-factor-secret', $secret);

View File

@ -332,7 +332,7 @@ class BudgetController extends Controller
* @return View * @return View
* @throws FireflyException * @throws FireflyException
*/ */
public function showWithRepetition(Budget $budget, LimitRepetition $repetition) public function showByRepetition(Budget $budget, LimitRepetition $repetition)
{ {
if ($repetition->budgetLimit->budget->id != $budget->id) { if ($repetition->budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.'); throw new FireflyException('This budget limit is not part of this budget.');

View File

@ -245,7 +245,7 @@ class CategoryController extends Controller
* *
* @return View * @return View
*/ */
public function showWithDate(Category $category, string $date) public function showByDate(Category $category, string $date)
{ {
$carbon = new Carbon($date); $carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
@ -263,7 +263,7 @@ class CategoryController extends Controller
$journals->setPath('categories/show/' . $category->id . '/' . $date); $journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon')); return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
} }
/** /**

View File

@ -112,7 +112,7 @@ class AccountController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function expenseByBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) public function expenseBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{ {
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($account->id); $cache->addProperty($account->id);
@ -153,7 +153,7 @@ class AccountController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function expenseByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) public function expenseCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{ {
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($account->id); $cache->addProperty($account->id);
@ -199,7 +199,7 @@ class AccountController extends Controller
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray()); $frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
$accounts = $repository->getAccountsById($frontPage->data); $accounts = $repository->getAccountsById($frontPage->data);
return Response::json($this->accountBalanceChart($start, $end, $accounts)); return Response::json($this->accountBalanceChart($accounts, $start, $end));
} }
/** /**
@ -210,7 +210,7 @@ class AccountController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function incomeByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) public function incomeCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{ {
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($account->id); $cache->addProperty($account->id);
@ -251,9 +251,9 @@ class AccountController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function report(Carbon $start, Carbon $end, Collection $accounts) public function report(Collection $accounts, Carbon $start, Carbon $end)
{ {
return Response::json($this->accountBalanceChart($start, $end, $accounts)); return Response::json($this->accountBalanceChart($accounts, $start, $end));
} }
/** /**
@ -406,13 +406,13 @@ class AccountController extends Controller
} }
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return array * @return array
*/ */
private function accountBalanceChart(Carbon $start, Carbon $end, Collection $accounts): array private function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();

View File

@ -193,7 +193,7 @@ class BudgetController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function period(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end, Collection $accounts) public function period(BudgetRepositoryInterface $repository, Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
@ -209,7 +209,7 @@ class BudgetController extends Controller
// the expenses: // the expenses:
$periods = Navigation::listOfPeriods($start, $end); $periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); $entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end, false);
$budgeted = []; $budgeted = [];
$key = Navigation::preferredCarbonFormat($start, $end); $key = Navigation::preferredCarbonFormat($start, $end);
$range = Navigation::preferredRangeFormat($start, $end); $range = Navigation::preferredRangeFormat($start, $end);
@ -252,6 +252,47 @@ class BudgetController extends Controller
return Response::json($data); return Response::json($data);
} }
/**
* @param BudgetRepositoryInterface $repository
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function periodNoBudget(BudgetRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty('no-budget');
$cache->addProperty('period');
if ($cache->has()) {
return Response::json($cache->get());
}
// the expenses:
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
// join them:
$result = [];
foreach (array_keys($periods) as $period) {
$nice = $periods[$period];
$result[$nice] = [
'spent' => isset($entries['entries'][$period]) ? $entries['entries'][$period] : '0',
];
}
$data = $this->generator->periodNoBudget($result);
$cache->store($data);
return Response::json($data);
}
/** /**
* @param Collection $repetitions * @param Collection $repetitions
* @param Budget $budget * @param Budget $budget

View File

@ -152,6 +152,84 @@ class CategoryController extends Controller
} }
/**
* @param CRI $repository
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function reportPeriod(CRI $repository, Category $category, Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-chart');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($category);
if ($cache->has()) {
return $cache->get();
}
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end);
// join them:
$result = [];
foreach (array_keys($periods) as $period) {
$nice = $periods[$period];
$result[$nice] = [
'earned' => $income[$category->id]['entries'][$period] ?? '0',
'spent' => $expenses[$category->id]['entries'][$period] ?? '0',
];
}
$data = $this->generator->reportPeriod($result);
return Response::json($data);
}
/**
* @param CRI $repository
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function reportPeriodNoCategory(CRI $repository, Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-category-period-chart');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
$income = $repository->periodIncomeNoCategory($accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end);
// join them:
$result = [];
foreach (array_keys($periods) as $period) {
$nice = $periods[$period];
$result[$nice] = [
'earned' => $income['entries'][$period] ?? '0',
'spent' => $expenses['entries'][$period] ?? '0',
];
}
$data = $this->generator->reportPeriod($result);
return Response::json($data);
}
/** /**
* @param CRI $repository * @param CRI $repository
* @param Category $category * @param Category $category

View File

@ -49,13 +49,12 @@ class ReportController extends Controller
* This chart, by default, is shown on the multi-year and year report pages, * This chart, by default, is shown on the multi-year and year report pages,
* which means that giving it a 2 week "period" should be enough granularity. * which means that giving it a 2 week "period" should be enough granularity.
* *
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts * @param Collection $accounts
* * @param Carbon $start
* @param Carbon $end
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function netWorth(Carbon $start, Carbon $end, Collection $accounts) public function netWorth(Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties; $cache = new CacheProperties;
@ -90,13 +89,16 @@ class ReportController extends Controller
/** /**
* Shows income and expense, debet/credit: operations
*
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts *
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function yearInOut(Carbon $start, Carbon $end, Collection $accounts) public function operations(Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties; $cache = new CacheProperties;
@ -112,14 +114,14 @@ class ReportController extends Controller
if ($start->diffInMonths($end) > 12) { if ($start->diffInMonths($end) > 12) {
// data = method X // data = method X
$data = $this->multiYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end); $data = $this->multiYearOperations($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data); $cache->store($data);
return Response::json($data); return Response::json($data);
} }
// data = method Y // data = method Y
$data = $this->singleYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end); $data = $this->singleYearOperations($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data); $cache->store($data);
return Response::json($data); return Response::json($data);
@ -128,14 +130,14 @@ class ReportController extends Controller
} }
/** /**
* Shows sum income and expense, debet/credit: operations
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts * @param Collection $accounts
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* @internal param AccountRepositoryInterface $repository
*/ */
public function yearInOutSummarized(Carbon $start, Carbon $end, Collection $accounts) public function sum(Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
@ -151,13 +153,13 @@ class ReportController extends Controller
if ($start->diffInMonths($end) > 12) { if ($start->diffInMonths($end) > 12) {
// per year // per year
$data = $this->multiYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end); $data = $this->multiYearSum($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data); $cache->store($data);
return Response::json($data); return Response::json($data);
} }
// per month! // per month!
$data = $this->singleYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end); $data = $this->singleYearSum($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data); $cache->store($data);
return Response::json($data); return Response::json($data);
@ -172,7 +174,7 @@ class ReportController extends Controller
* *
* @return array * @return array
*/ */
protected function multiYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) protected function multiYearOperations(array $earned, array $spent, Carbon $start, Carbon $end)
{ {
$entries = new Collection; $entries = new Collection;
while ($start < $end) { while ($start < $end) {
@ -184,7 +186,7 @@ class ReportController extends Controller
$start->addYear(); $start->addYear();
} }
$data = $this->generator->multiYearInOut($entries); $data = $this->generator->multiYearOperations($entries);
return $data; return $data;
} }
@ -197,7 +199,7 @@ class ReportController extends Controller
* *
* @return array * @return array
*/ */
protected function multiYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) protected function multiYearSum(array $earned, array $spent, Carbon $start, Carbon $end)
{ {
$income = '0'; $income = '0';
$expense = '0'; $expense = '0';
@ -213,7 +215,7 @@ class ReportController extends Controller
$start->addYear(); $start->addYear();
} }
$data = $this->generator->multiYearInOutSummarized($income, $expense, $count); $data = $this->generator->multiYearSum($income, $expense, $count);
return $data; return $data;
} }
@ -245,7 +247,7 @@ class ReportController extends Controller
* *
* @return array * @return array
*/ */
protected function singleYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) protected function singleYearOperations(array $earned, array $spent, Carbon $start, Carbon $end)
{ {
// per month? simply use each month. // per month? simply use each month.
@ -260,7 +262,7 @@ class ReportController extends Controller
$start->addMonth(); $start->addMonth();
} }
$data = $this->generator->yearInOut($entries); $data = $this->generator->yearOperations($entries);
return $data; return $data;
} }
@ -273,7 +275,7 @@ class ReportController extends Controller
* *
* @return array * @return array
*/ */
protected function singleYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) protected function singleYearSum(array $earned, array $spent, Carbon $start, Carbon $end)
{ {
$income = '0'; $income = '0';
$expense = '0'; $expense = '0';
@ -289,7 +291,7 @@ class ReportController extends Controller
$start->addMonth(); $start->addMonth();
} }
$data = $this->generator->yearInOutSummarized($income, $expense, $count); $data = $this->generator->yearSum($income, $expense, $count);
return $data; return $data;
} }

View File

@ -60,14 +60,14 @@ class CurrencyController extends Controller
$subTitle = trans('firefly.create_currency'); $subTitle = trans('firefly.create_currency');
// put previous url in session if not redirect from store (not "create another"). // put previous url in session if not redirect from store (not "create another").
if (session('currency.create.fromStore') !== true) { if (session('currencies.create.fromStore') !== true) {
Session::put('currency.create.url', URL::previous()); Session::put('currencies.create.url', URL::previous());
} }
Session::forget('currency.create.fromStore'); Session::forget('currencies.create.fromStore');
Session::flash('gaEventCategory', 'currency'); Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'create'); Session::flash('gaEventAction', 'create');
return view('currency.create', compact('subTitleIcon', 'subTitle')); return view('currencies.create', compact('subTitleIcon', 'subTitle'));
} }
/** /**
@ -85,7 +85,7 @@ class CurrencyController extends Controller
Cache::forget('FFCURRENCYSYMBOL'); Cache::forget('FFCURRENCYSYMBOL');
Cache::forget('FFCURRENCYCODE'); Cache::forget('FFCURRENCYCODE');
return redirect(route('currency.index')); return redirect(route('currencies.index'));
} }
@ -99,18 +99,18 @@ class CurrencyController extends Controller
if (!$this->canDeleteCurrency($currency)) { if (!$this->canDeleteCurrency($currency)) {
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name])); Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currency.index')); return redirect(route('currencies.index'));
} }
// put previous url in session // put previous url in session
Session::put('currency.delete.url', URL::previous()); Session::put('currencies.delete.url', URL::previous());
Session::flash('gaEventCategory', 'currency'); Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'delete'); Session::flash('gaEventAction', 'delete');
$subTitle = trans('form.delete_currency', ['name' => $currency->name]); $subTitle = trans('form.delete_currency', ['name' => $currency->name]);
return view('currency.delete', compact('currency', 'subTitle')); return view('currencies.delete', compact('currency', 'subTitle'));
} }
/** /**
@ -124,7 +124,7 @@ class CurrencyController extends Controller
if (!$this->canDeleteCurrency($currency)) { if (!$this->canDeleteCurrency($currency)) {
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name])); Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currency.index')); return redirect(route('currencies.index'));
} }
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name])); Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
@ -132,7 +132,7 @@ class CurrencyController extends Controller
$currency->forceDelete(); $currency->forceDelete();
} }
return redirect(session('currency.delete.url')); return redirect(session('currencies.delete.url'));
} }
/** /**
@ -147,14 +147,14 @@ class CurrencyController extends Controller
$currency->symbol = htmlentities($currency->symbol); $currency->symbol = htmlentities($currency->symbol);
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
if (session('currency.edit.fromUpdate') !== true) { if (session('currencies.edit.fromUpdate') !== true) {
Session::put('currency.edit.url', URL::previous()); Session::put('currencies.edit.url', URL::previous());
} }
Session::forget('currency.edit.fromUpdate'); Session::forget('currencies.edit.fromUpdate');
Session::flash('gaEventCategory', 'currency'); Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'edit'); Session::flash('gaEventAction', 'edit');
return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon')); return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon'));
} }
@ -174,7 +174,7 @@ class CurrencyController extends Controller
} }
return view('currency.index', compact('currencies', 'defaultCurrency')); return view('currencies.index', compact('currencies', 'defaultCurrency'));
} }
/** /**
@ -189,7 +189,7 @@ class CurrencyController extends Controller
if (!auth()->user()->hasRole('owner')) { if (!auth()->user()->hasRole('owner')) {
Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.');
return redirect(session('currency.create.url')); return redirect(session('currencies.create.url'));
} }
$data = $request->getCurrencyData(); $data = $request->getCurrencyData();
@ -197,13 +197,13 @@ class CurrencyController extends Controller
Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name])); Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
Session::put('currency.create.fromStore', true); Session::put('currencies.create.fromStore', true);
return redirect(route('currency.create'))->withInput(); return redirect(route('currencies.create'))->withInput();
} }
// redirect to previous URL. // redirect to previous URL.
return redirect(session('currency.create.url')); return redirect(session('currencies.create.url'));
} }
@ -226,13 +226,13 @@ class CurrencyController extends Controller
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
Session::put('currency.edit.fromUpdate', true); Session::put('currencies.edit.fromUpdate', true);
return redirect(route('currency.edit', [$currency->id])); return redirect(route('currencies.edit', [$currency->id]));
} }
// redirect to previous URL. // redirect to previous URL.
return redirect(session('currency.edit.url')); return redirect(session('currencies.edit.url'));
} }

View File

@ -102,7 +102,6 @@ class JsonController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* *
* @internal param ARI $accountRepository
*/ */
public function boxIn(AccountTaskerInterface $accountTasker, AccountRepositoryInterface $repository) public function boxIn(AccountTaskerInterface $accountTasker, AccountRepositoryInterface $repository)
{ {

View File

@ -46,7 +46,7 @@ class ReportController extends Controller
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* @throws FireflyException * @throws FireflyException
*/ */
public function info(Request $request) public function general(Request $request)
{ {
$attributes = $request->get('attributes') ?? []; $attributes = $request->get('attributes') ?? [];
$attributes = $this->parseAttributes($attributes); $attributes = $this->parseAttributes($attributes);

View File

@ -74,7 +74,7 @@ class PreferencesController extends Controller
Session::flash('success', strval(trans('firefly.pref_two_factor_auth_disabled'))); Session::flash('success', strval(trans('firefly.pref_two_factor_auth_disabled')));
Session::flash('info', strval(trans('firefly.pref_two_factor_auth_remove_it'))); Session::flash('info', strval(trans('firefly.pref_two_factor_auth_remove_it')));
return redirect(route('preferences')); return redirect(route('preferences.index'));
} }
/** /**
@ -122,7 +122,7 @@ class PreferencesController extends Controller
Session::flash('success', strval(trans('firefly.saved_preferences'))); Session::flash('success', strval(trans('firefly.saved_preferences')));
Preferences::mark(); Preferences::mark();
return redirect(route('preferences')); return redirect(route('preferences.index'));
} }
/** /**
@ -206,7 +206,7 @@ class PreferencesController extends Controller
return redirect(route('preferences.code')); return redirect(route('preferences.code'));
} }
return redirect(route('preferences')); return redirect(route('preferences.index'));
} }
/** /**

View File

@ -108,7 +108,7 @@ class ProfileController extends Controller
Session::flash('success', strval(trans('firefly.password_changed'))); Session::flash('success', strval(trans('firefly.password_changed')));
return redirect(route('profile')); return redirect(route('profile.index'));
} }
/** /**

View File

@ -29,13 +29,13 @@ class AccountController extends Controller
{ {
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return mixed|string
*/ */
public function accountReport(Carbon $start, Carbon $end, Collection $accounts) public function general(Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties; $cache = new CacheProperties;
@ -47,9 +47,9 @@ class AccountController extends Controller
return $cache->get(); return $cache->get();
} }
/** @var AccountTaskerInterface $accountTasker */
$accountTasker = app(AccountTaskerInterface::class); $accountTasker = app(AccountTaskerInterface::class);
$accountReport = $accountTasker->getAccountReport($start, $end, $accounts); $accountReport = $accountTasker->getAccountReport($accounts, $start, $end);
$result = view('reports.partials.accounts', compact('accountReport'))->render(); $result = view('reports.partials.accounts', compact('accountReport'))->render();
$cache->store($result); $cache->store($result);

View File

@ -30,13 +30,13 @@ class BalanceController extends Controller
/** /**
* @param BalanceReportHelperInterface $helper * @param BalanceReportHelperInterface $helper
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return string * @return mixed|string
*/ */
public function balanceReport(BalanceReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) public function general(BalanceReportHelperInterface $helper,Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -50,7 +50,7 @@ class BalanceController extends Controller
return $cache->get(); return $cache->get();
} }
$balance = $helper->getBalanceReport($start, $end, $accounts); $balance = $helper->getBalanceReport($accounts, $start, $end);
$result = view('reports.partials.balance', compact('balance'))->render(); $result = view('reports.partials.balance', compact('balance'))->render();
$cache->store($result); $cache->store($result);

View File

@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface; use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@ -29,43 +30,16 @@ use Navigation;
class BudgetController extends Controller class BudgetController extends Controller
{ {
/**
*
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$periods = Navigation::listOfPeriods($start, $end);
$budgets = $helper->getBudgetPeriodReport($start, $end, $accounts);
$result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render();
$cache->store($result);
return $result;
}
/** /**
* @param BudgetReportHelperInterface $helper * @param BudgetReportHelperInterface $helper
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return string * @return mixed|string
*/ */
public function budgetReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) public function general(BudgetReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
@ -86,4 +60,64 @@ class BudgetController extends Controller
return $result; return $result;
} }
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function period(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
// generate budget report right here.
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budgets = $repository->getBudgets();
$data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget"
$report = $this->filterBudgetPeriodReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
} }

View File

@ -17,8 +17,12 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Navigation;
/** /**
* Class CategoryController * Class CategoryController
@ -27,16 +31,82 @@ use Illuminate\Support\Collection;
*/ */
class CategoryController extends Controller class CategoryController extends Controller
{ {
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function expenses(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-expenses-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodExpenses($categories, $accounts, $start, $end);
$data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function income(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodIncome($categories, $accounts, $start, $end);
$data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/** /**
* @param ReportHelperInterface $helper * @param ReportHelperInterface $helper
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return mixed|string
*/ */
public function categoryReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) public function operations(Collection $accounts, Carbon $start, Carbon $end)
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties; $cache = new CacheProperties;
@ -48,12 +118,56 @@ class CategoryController extends Controller
return $cache->get(); return $cache->get();
} }
$categories = $helper->getCategoryReport($start, $end, $accounts); /** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$report = [];
/** @var Category $category */
foreach ($categories as $category) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
if (bccomp($spent, '0') !== 0) {
$report[$category->id] = ['name' => $category->name, 'spent' => $spent];
}
}
$result = view('reports.partials.categories', compact('categories'))->render(); // sort the result
// Obtain a list of columns
$sum = [];
foreach ($report as $categoryId => $row) {
$sum[$categoryId] = floatval($row['spent']);
}
array_multisort($sum, SORT_ASC, $report);
$result = view('reports.partials.categories', compact('report'))->render();
$cache->store($result); $cache->store($result);
return $result; return $result;
} }
/**
* Filters empty results from category period report
*
* @param array $data
*
* @return array
*/
private function filterReport(array $data): array
{
foreach ($data as $categoryId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$categoryId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$categoryId]);
}
}
return $data;
}
} }

View File

@ -1,119 +0,0 @@
<?php
/**
* InOutController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class InOutController
*
* @package FireflyIII\Http\Controllers\Report
*/
class InOutController extends Controller
{
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $helper->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.expenses', compact('expenses'))->render();
$cache->store($result);
return $result;
}
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('inc-exp-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $helper->getIncomeReport($start, $end, $accounts);
$expenses = $helper->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render();
$cache->store($result);
return $result;
}
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $helper->getIncomeReport($start, $end, $accounts);
$result = view('reports.partials.income', compact('incomes'))->render();
$cache->store($result);
return $result;
}
}

View File

@ -0,0 +1,245 @@
<?php
/**
* OperationsController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class OperationsController
*
* @package FireflyIII\Http\Controllers\Report
*/
class OperationsController extends Controller
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function expenses(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $this->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.expenses', compact('expenses'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function operations(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('inc-exp-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $this->getIncomeReport($start, $end, $accounts);
$expenses = $this->getExpenseReport($start, $end, $accounts);
$incomeSum = array_sum(
array_map(
function ($item) {
return $item['sum'];
}, $incomes
)
);
$expensesSum = array_sum(
array_map(
function ($item) {
return $item['sum'];
}, $expenses
)
);
$result = view('reports.partials.operations', compact('incomeSum', 'expensesSum'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function income(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
//return $cache->get();
}
$income = $this->getIncomeReport($start, $end, $accounts);
$result = view('reports.partials.income', compact('income'))->render();
$cache->store($result);
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all expenses for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
$transactions = $transactions->filter(
function (Transaction $transaction) {
// return negative amounts only.
if (bccomp($transaction->transaction_amount, '0') === -1) {
return $transaction;
}
return false;
}
);
$expenses = $this->groupByOpposing($transactions);
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($expenses as $accountId => $row) {
$sum[$accountId] = floatval($row['sum']);
}
array_multisort($sum, SORT_ASC, $expenses);
return $expenses;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all expenses for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
$transactions = $transactions->filter(
function (Transaction $transaction) {
// return positive amounts only.
if (bccomp($transaction->transaction_amount, '0') === 1) {
return $transaction;
}
return false;
}
);
$income = $this->groupByOpposing($transactions);
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($income as $accountId => $row) {
$sum[$accountId] = floatval($row['sum']);
}
array_multisort($sum, SORT_DESC, $income);
return $income;
}
/**
* @param Collection $transactions
*
* @return array
*/
private function groupByOpposing(Collection $transactions): array
{
$expenses = [];
// join the result together:
foreach ($transactions as $transaction) {
$opposingId = $transaction->opposing_account_id;
$name = $transaction->opposing_account_name;
if (!isset($expenses[$opposingId])) {
$expenses[$opposingId] = [
'id' => $opposingId,
'name' => $name,
'sum' => '0',
'count' => 0,
];
}
$expenses[$opposingId]['sum'] = bcadd($expenses[$opposingId]['sum'], $transaction->transaction_amount);
$expenses[$opposingId]['count']++;
}
return $expenses;
}
}

View File

@ -61,14 +61,13 @@ class ReportController extends Controller
} }
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return string * @return string
* @throws FireflyException
*/ */
public function auditReport(Carbon $start, Carbon $end, Collection $accounts) public function auditReport(Collection $accounts, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date'));
@ -97,15 +96,14 @@ class ReportController extends Controller
} }
/** /**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
*
* @param Collection $categories
* *
* @return string * @return string
*/ */
public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories) public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date'));
@ -134,14 +132,13 @@ class ReportController extends Controller
} }
/** /**
* @param Collection $accounts
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param Collection $accounts
* *
* @return string * @return string
* @throws FireflyException
*/ */
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts) public function defaultReport(Collection $accounts, Carbon $start, Carbon $end)
{ {
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date'));
@ -227,6 +224,12 @@ class ReportController extends Controller
return redirect(route('reports.index')); return redirect(route('reports.index'));
} }
if ($request->getCategoryList()->count() === 0 && $reportType === 'category') {
Session::flash('error', trans('firefly.select_more_than_one_category'));
return redirect(route('reports.index'));
}
if ($end < $start) { if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); return view('error')->with('message', trans('firefly.end_after_start_date'));
} }
@ -240,13 +243,13 @@ class ReportController extends Controller
default: default:
throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType)); throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType));
case 'category': case 'category':
$uri = route('reports.report.category', [$start, $end, $accounts, $categories]); $uri = route('reports.report.category', [$accounts, $categories, $start, $end]);
break; break;
case 'default': case 'default':
$uri = route('reports.report.default', [$start, $end, $accounts]); $uri = route('reports.report.default', [$accounts, $start, $end]);
break; break;
case 'audit': case 'audit':
$uri = route('reports.report.audit', [$start, $end, $accounts]); $uri = route('reports.report.audit', [$accounts, $start, $end]);
break; break;
} }

View File

@ -104,7 +104,6 @@ class RuleController extends Controller
* @param Rule $rule * @param Rule $rule
* *
* @return View * @return View
* @internal param RuleRepositoryInterface $repository
*/ */
public function delete(Rule $rule) public function delete(Rule $rule)
{ {

View File

@ -15,14 +15,12 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Input; use Input;
use Preferences; use Preferences;
use Response;
use Session; use Session;
use URL; use URL;
use View; use View;
@ -170,33 +168,14 @@ class TagController extends Controller
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions')); return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions'));
} }
/**
* @param $state
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function hideTagHelp(string $state)
{
$newState = $state == 'true' ? true : false;
Preferences::set('hideTagHelp', $newState);
return Response::json([true]);
}
/** /**
* *
*/ */
public function index() public function index()
{ {
/** @var Preference $helpHiddenPref */ $title = 'Tags';
$helpHiddenPref = Preferences::get('hideTagHelp', false); $mainTitleIcon = 'fa-tags';
$title = 'Tags'; $types = ['nothing', 'balancingAct', 'advancePayment'];
$mainTitleIcon = 'fa-tags';
$helpHidden = $helpHiddenPref->data;
// group years.
$types = ['nothing', 'balancingAct', 'advancePayment'];
// loop each types and get the tags, group them by year. // loop each types and get the tags, group them by year.
$collection = []; $collection = [];
@ -223,7 +202,7 @@ class TagController extends Controller
} }
} }
return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'helpHidden', 'collection')); return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'collection'));
} }
/** /**

View File

@ -62,7 +62,7 @@ class ConvertController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/ */
public function convert(TransactionType $destinationType, TransactionJournal $journal) public function index(TransactionType $destinationType, TransactionJournal $journal)
{ {
if ($this->isOpeningBalance($journal)) { if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal); return $this->redirectToAccount($journal);
@ -115,7 +115,7 @@ class ConvertController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal) public function postIndex(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
{ {
if ($this->isOpeningBalance($journal)) { if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal); return $this->redirectToAccount($journal);

View File

@ -179,7 +179,7 @@ class SingleController extends Controller
$count = $journal->transactions()->count(); $count = $journal->transactions()->count();
if ($count > 2) { if ($count > 2) {
return redirect(route('transactions.edit-split', [$journal->id])); return redirect(route('transactions.split.edit', [$journal->id]));
} }
$what = strtolower(TransactionJournal::transactionTypeStr($journal)); $what = strtolower(TransactionJournal::transactionTypeStr($journal));
@ -286,7 +286,7 @@ class SingleController extends Controller
if ($doSplit === true) { if ($doSplit === true) {
// redirect to edit screen: // redirect to edit screen:
return redirect(route('transactions.edit-split', [$journal->id])); return redirect(route('transactions.split.edit', [$journal->id]));
} }

View File

@ -157,7 +157,7 @@ class SplitController extends Controller
// set value so edit routine will not overwrite URL: // set value so edit routine will not overwrite URL:
Session::put('transactions.edit-split.fromUpdate', true); Session::put('transactions.edit-split.fromUpdate', true);
return redirect(route('transactions.edit-split', [$journal->id]))->withInput(['return_to_edit' => 1]); return redirect(route('transactions.split.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
} }
// redirect to previous URL. // redirect to previous URL.
@ -249,8 +249,8 @@ class SplitController extends Controller
$transactions = $this->tasker->getTransactionsOverview($journal); $transactions = $this->tasker->getTransactionsOverview($journal);
$return = []; $return = [];
/** @var array $transaction */ /** @var array $transaction */
foreach ($transactions as $transaction) { foreach ($transactions as $index => $transaction) {
$return[] = [ $set = [
'description' => $transaction['description'], 'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'], 'source_account_id' => $transaction['source_account_id'],
'source_account_name' => $transaction['source_account_name'], 'source_account_name' => $transaction['source_account_name'],
@ -260,6 +260,15 @@ class SplitController extends Controller
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0, 'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'], 'category' => $transaction['category'],
]; ];
// set initial category and/or budget:
if (count($transactions) === 1 && $index === 0) {
$set['budget_id'] = TransactionJournal::budgetId($journal);
$set['category'] = TransactionJournal::categoryAsString($journal);
}
$return[] = $set;
} }
return $return; return $return;

View File

@ -146,7 +146,7 @@ class TransactionController extends Controller
* *
* @return View * @return View
*/ */
public function indexDate(Request $request, string $what, string $date) public function indexByDate(Request $request, string $what, string $date)
{ {
$carbon = new Carbon($date); $carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;

View File

@ -57,7 +57,7 @@ class AuthenticateTwoFactor
$has2faSecret = !is_null(Preferences::get('twoFactorAuthSecret')); $has2faSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
$is2faAuthed = Session::get('twofactor-authenticated'); $is2faAuthed = Session::get('twofactor-authenticated');
if ($is2faEnabled && $has2faSecret && !$is2faAuthed) { if ($is2faEnabled && $has2faSecret && !$is2faAuthed) {
return redirect(route('two-factor')); return redirect(route('two-factor.index'));
} }
return $next($request); return $next($request);

View File

@ -46,7 +46,12 @@ class IsConfirmed
return redirect()->guest('login'); return redirect()->guest('login');
} }
// must the user be confirmed in the first place? // must the user be confirmed in the first place?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; $confirmPreference = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'));
$mustConfirmAccount = false;
if (!is_null($confirmPreference)) {
$mustConfirmAccount = $confirmPreference->data;
}
// user must be logged in, then continue: // user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data; $isConfirmed = Preferences::get('user_confirmed', false)->data;

View File

@ -61,7 +61,6 @@ class Range
* @param string|null $guard * @param string|null $guard
* *
* @return mixed * @return mixed
* @internal param Closure $next
*/ */
public function handle(Request $request, Closure $next, $guard = null) public function handle(Request $request, Closure $next, $guard = null)
{ {

View File

@ -42,10 +42,11 @@ class AccountFormRequest extends Request
'name' => trim($this->input('name')), 'name' => trim($this->input('name')),
'active' => intval($this->input('active')) === 1, 'active' => intval($this->input('active')) === 1,
'accountType' => $this->input('what'), 'accountType' => $this->input('what'),
'currency_id' => intval($this->input('currency_id')), 'currency_id' => intval($this->input('currency_id')),
'virtualBalance' => round($this->input('virtualBalance'), 2), 'virtualBalance' => round($this->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')), 'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
'iban' => trim($this->input('iban')), 'iban' => trim($this->input('iban')),
'BIC' => trim($this->input('BIC')),
'accountNumber' => trim($this->input('accountNumber')), 'accountNumber' => trim($this->input('accountNumber')),
'accountRole' => $this->input('accountRole'), 'accountRole' => $this->input('accountRole'),
'openingBalance' => round($this->input('openingBalance'), 2), 'openingBalance' => round($this->input('openingBalance'), 2),
@ -79,6 +80,7 @@ class AccountFormRequest extends Request
'name' => $nameRule, 'name' => $nameRule,
'openingBalance' => 'numeric', 'openingBalance' => 'numeric',
'iban' => 'iban', 'iban' => 'iban',
'BIC' => 'bic',
'virtualBalance' => 'numeric', 'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date', 'openingBalanceDate' => 'date',
'currency_id' => 'exists:transaction_currencies,id', 'currency_id' => 'exists:transaction_currencies,id',

View File

@ -327,29 +327,29 @@ Breadcrumbs::register(
* CURRENCIES * CURRENCIES
*/ */
Breadcrumbs::register( Breadcrumbs::register(
'currency.index', function (BreadCrumbGenerator $breadcrumbs) { 'currencies.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.currencies'), route('currency.index')); $breadcrumbs->push(trans('firefly.currencies'), route('currencies.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.create', function (BreadCrumbGenerator $breadcrumbs) { 'currencies.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currencies.index');
$breadcrumbs->push(trans('firefly.create_currency'), route('currency.create')); $breadcrumbs->push(trans('firefly.create_currency'), route('currencies.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.edit', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) { 'currencies.edit', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currencies.index');
$breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currency.edit', [$currency->id])); $breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currencies.edit', [$currency->id]));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.delete', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) { 'currencies.delete', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currencies.index');
$breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currency.delete', [$currency->id])); $breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currencies.delete', [$currency->id]));
} }
); );
@ -404,9 +404,9 @@ Breadcrumbs::register(
* PREFERENCES * PREFERENCES
*/ */
Breadcrumbs::register( Breadcrumbs::register(
'preferences', function (BreadCrumbGenerator $breadcrumbs) { 'preferences.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences')); $breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences.index'));
} }
); );
@ -414,7 +414,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'preferences.code', function (BreadCrumbGenerator $breadcrumbs) { 'preferences.code', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences')); $breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences.index'));
} }
); );
@ -423,22 +423,22 @@ Breadcrumbs::register(
* PROFILE * PROFILE
*/ */
Breadcrumbs::register( Breadcrumbs::register(
'profile', function (BreadCrumbGenerator $breadcrumbs) { 'profile.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.profile'), route('profile')); $breadcrumbs->push(trans('breadcrumbs.profile'), route('profile.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'profile.change-password', function (BreadCrumbGenerator $breadcrumbs) { 'profile.change-password', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('profile'); $breadcrumbs->parent('profile.index');
$breadcrumbs->push(trans('breadcrumbs.changePassword'), route('profile.change-password')); $breadcrumbs->push(trans('breadcrumbs.changePassword'), route('profile.change-password'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'profile.delete-account', function (BreadCrumbGenerator $breadcrumbs) { 'profile.delete-account', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('profile'); $breadcrumbs->parent('profile.index');
$breadcrumbs->push(trans('firefly.delete_account'), route('profile.delete-account')); $breadcrumbs->push(trans('firefly.delete_account'), route('profile.delete-account'));
} }
@ -529,9 +529,9 @@ Breadcrumbs::register(
* SEARCH * SEARCH
*/ */
Breadcrumbs::register( Breadcrumbs::register(
'search', function (BreadCrumbGenerator $breadcrumbs, $query) { 'search.index', function (BreadCrumbGenerator $breadcrumbs, $query) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search')); $breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search.index'));
} }
); );

View File

@ -199,8 +199,7 @@ class Account extends Model
*/ */
public function getOpeningBalanceAmount(): string public function getOpeningBalanceAmount(): string
{ {
$journal = TransactionJournal $journal = TransactionJournal::sortCorrectly()
::sortCorrectly()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $this->id) ->where('transactions.account_id', $this->id)
->transactionTypes([TransactionType::OPENING_BALANCE]) ->transactionTypes([TransactionType::OPENING_BALANCE])
@ -230,8 +229,7 @@ class Account extends Model
public function getOpeningBalanceDate(): Carbon public function getOpeningBalanceDate(): Carbon
{ {
$date = new Carbon('1900-01-01'); $date = new Carbon('1900-01-01');
$journal = TransactionJournal $journal = TransactionJournal::sortCorrectly()
::sortCorrectly()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $this->id) ->where('transactions.account_id', $this->id)
->transactionTypes([TransactionType::OPENING_BALANCE]) ->transactionTypes([TransactionType::OPENING_BALANCE])

View File

@ -64,8 +64,7 @@ class TransactionJournal extends TransactionJournalSupport
public static function routeBinder($value) public static function routeBinder($value)
{ {
if (auth()->check()) { if (auth()->check()) {
$object = TransactionJournal $object = TransactionJournal::where('transaction_journals.id', $value)
::where('transaction_journals.id', $value)
->with('transactionType') ->with('transactionType')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('user_id', auth()->user()->id)->first(['transaction_journals.*']); ->where('user_id', auth()->user()->id)->first(['transaction_journals.*']);

View File

@ -41,7 +41,7 @@ class AccountRepository implements AccountRepositoryInterface
/** @var User */ /** @var User */
private $user; private $user;
/** @var array */ /** @var array */
private $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType', 'accountNumber','currency_id']; private $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType', 'accountNumber', 'currency_id', 'BIC'];
/** /**
* AttachmentRepository constructor. * AttachmentRepository constructor.
@ -60,7 +60,7 @@ class AccountRepository implements AccountRepositoryInterface
* *
* @return int * @return int
*/ */
public function count(array $types):int public function count(array $types): int
{ {
$count = $this->user->accounts()->accountTypeIn($types)->count(); $count = $this->user->accounts()->accountTypeIn($types)->count();
@ -367,12 +367,11 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
protected function openingBalanceTransaction(Account $account): TransactionJournal protected function openingBalanceTransaction(Account $account): TransactionJournal
{ {
$journal = TransactionJournal $journal = TransactionJournal::sortCorrectly()
::sortCorrectly() ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->where('transactions.account_id', $account->id)
->where('transactions.account_id', $account->id) ->transactionTypes([TransactionType::OPENING_BALANCE])
->transactionTypes([TransactionType::OPENING_BALANCE]) ->first(['transaction_journals.*']);
->first(['transaction_journals.*']);
if (is_null($journal)) { if (is_null($journal)) {
Log::debug('Could not find a opening balance journal, return empty one.'); Log::debug('Could not find a opening balance journal, return empty one.');
@ -482,7 +481,7 @@ class AccountRepository implements AccountRepositoryInterface
* *
* @return Account * @return Account
*/ */
protected function storeOpposingAccount(float $amount, string $name):Account protected function storeOpposingAccount(float $amount, string $name): Account
{ {
$type = $amount < 0 ? 'expense' : 'revenue'; $type = $amount < 0 ? 'expense' : 'revenue';
$opposingData = [ $opposingData = [

View File

@ -119,6 +119,6 @@ interface AccountRepositoryInterface
* *
* @return Account * @return Account
*/ */
public function store(array $data) : Account; public function store(array $data): Account;
} }

View File

@ -107,41 +107,12 @@ class AccountTasker implements AccountTaskerInterface
/** /**
* @param Collection $accounts * @param Collection $accounts
* @param Collection $excluded
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Collection
* @see self::financialReport
*
*/
public function expenseReport(Collection $accounts, Collection $excluded, Carbon $start, Carbon $end): Collection
{
$idList = [
'accounts' => $accounts->pluck('id')->toArray(),
'exclude' => $excluded->pluck('id')->toArray(),
];
Log::debug(
'Now calling expenseReport.',
['accounts' => $idList['accounts'], 'excluded' => $idList['exclude'],
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d'),
]
);
return $this->financialReport($idList, $start, $end, false);
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection * @return AccountCollection
*/ */
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): AccountCollection
{ {
$startAmount = '0'; $startAmount = '0';
$endAmount = '0'; $endAmount = '0';
@ -190,35 +161,6 @@ class AccountTasker implements AccountTaskerInterface
return $object; return $object;
} }
/**
* @param Collection $accounts
* @param Collection $excluded
* @param Carbon $start
* @param Carbon $end
*
* @see AccountTasker::financialReport()
*
* @return Collection
*
*/
public function incomeReport(Collection $accounts, Collection $excluded, Carbon $start, Carbon $end): Collection
{
$idList = [
'accounts' => $accounts->pluck('id')->toArray(),
'exclude' => $excluded->pluck('id')->toArray(),
];
Log::debug(
'Now calling expenseReport.',
['accounts' => $idList['accounts'], 'excluded' => $idList['exclude'],
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d'),
]
);
return $this->financialReport($idList, $start, $end, true);
}
/** /**
* Will return how much money has been going out (ie. spent) by the given account(s). * Will return how much money has been going out (ie. spent) by the given account(s).
* Alternatively, will return how much money has been coming in (ie. earned) by the given accounts. * Alternatively, will return how much money has been coming in (ie. earned) by the given accounts.
@ -249,21 +191,20 @@ class AccountTasker implements AccountTaskerInterface
$joinModifier = $incoming ? '<' : '>'; $joinModifier = $incoming ? '<' : '>';
$selection = $incoming ? '>' : '<'; $selection = $incoming ? '>' : '<';
$query = Transaction $query = Transaction::distinct()
::distinct() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin(
->leftJoin( 'transactions as other_side', function (JoinClause $join) use ($joinModifier) {
'transactions as other_side', function (JoinClause $join) use ($joinModifier) { $join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0);
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0); }
} )
) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id)
->where('transaction_journals.user_id', $this->user->id) ->whereNull('transactions.deleted_at')
->whereNull('transactions.deleted_at') ->whereNull('transaction_journals.deleted_at')
->whereNull('transaction_journals.deleted_at') ->whereIn('transactions.account_id', $accounts['accounts'])
->whereIn('transactions.account_id', $accounts['accounts']) ->where('transactions.amount', $selection, 0);
->where('transactions.amount', $selection, 0);
if (count($accounts['exclude']) > 0) { if (count($accounts['exclude']) > 0) {
$query->whereNotIn('other_side.account_id', $accounts['exclude']); $query->whereNotIn('other_side.account_id', $accounts['exclude']);
} }
@ -279,91 +220,4 @@ class AccountTasker implements AccountTaskerInterface
return $sum; return $sum;
} }
/**
*
* This method will determin how much has flown (in the given period) from OR to $accounts to/from anywhere else,
* except $excluded. This could be a list of incomes, or a list of expenses. This method shows
* the name, the amount and the number of transactions. It is a summary, and only used in some reports.
*
* $incoming=true a list of incoming money (earnings)
* $incoming=false a list of outgoing money (expenses).
*
* @param array $accounts
* @param Carbon $start
* @param Carbon $end
* @param bool $incoming
*
* Opening balances are ignored.
*
* @return Collection
*/
protected function financialReport(array $accounts, Carbon $start, Carbon $end, bool $incoming): Collection
{
$collection = new Collection;
$joinModifier = $incoming ? '<' : '>';
$selection = $incoming ? '>' : '<';
$query = Transaction
::distinct()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->leftJoin(
'transactions as other_side', function (JoinClause $join) use ($joinModifier) {
$join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0);
}
)
->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id')
->where('transaction_types.type', '!=', TransactionType::OPENING_BALANCE)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_journals.user_id', $this->user->id)
->whereNull('transactions.deleted_at')
->whereNull('other_side.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereIn('transactions.account_id', $accounts['accounts'])
->where('other_side.amount', '=', DB::raw('transactions.amount * -1'))
->where('transactions.amount', $selection, 0)
->orderBy('transactions.amount');
if (count($accounts['exclude']) > 0) {
$query->whereNotIn('other_side.account_id', $accounts['exclude']);
}
$set = $query->get(
[
'transaction_journals.id',
'other_side.account_id',
'other_account.name',
'other_account.encrypted',
'transactions.amount',
]
);
// summarize ourselves:
$temp = [];
foreach ($set as $entry) {
// save into $temp:
$id = intval($entry->account_id);
if (isset($temp[$id])) {
$temp[$id]['count']++;
$temp[$id]['amount'] = bcadd($temp[$id]['amount'], $entry->amount);
}
if (!isset($temp[$id])) {
$temp[$id] = [
'name' => intval($entry->encrypted) === 1 ? Crypt::decrypt($entry->name) : $entry->name,
'amount' => $entry->amount,
'count' => 1,
];
}
}
// loop $temp and create collection:
foreach ($temp as $key => $entry) {
$object = new stdClass();
$object->id = $key;
$object->name = $entry['name'];
$object->count = $entry['count'];
$object->amount = $entry['amount'];
$collection->push($object);
}
return $collection;
}
} }

View File

@ -51,37 +51,11 @@ interface AccountTaskerInterface
/** /**
* @param Collection $accounts * @param Collection $accounts
* @param Collection $excluded
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @see AccountTasker::financialReport()
*
* @return Collection
*
*/
public function expenseReport(Collection $accounts, Collection $excluded, Carbon $start, Carbon $end): Collection;
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection * @return AccountCollection
*/ */
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection; public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): AccountCollection;
/**
* @param Collection $accounts
* @param Collection $excluded
* @param Carbon $start
* @param Carbon $end
*
* @see AccountTasker::financialReport()
*
* @return Collection
*
*/
public function incomeReport(Collection $accounts, Collection $excluded, Carbon $start, Carbon $end): Collection;
} }

View File

@ -66,7 +66,7 @@ class BillRepository implements BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function find(int $billId) : Bill public function find(int $billId): Bill
{ {
$bill = $this->user->bills()->find($billId); $bill = $this->user->bills()->find($billId);
if (is_null($bill)) { if (is_null($bill)) {
@ -83,7 +83,7 @@ class BillRepository implements BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function findByName(string $name) : Bill public function findByName(string $name): Bill
{ {
$bills = $this->user->bills()->get(['bills.*']); $bills = $this->user->bills()->get(['bills.*']);

View File

@ -40,7 +40,7 @@ interface BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function find(int $billId) : Bill; public function find(int $billId): Bill;
/** /**
* Find a bill by name. * Find a bill by name.
@ -49,7 +49,7 @@ interface BillRepositoryInterface
* *
* @return Bill * @return Bill
*/ */
public function findByName(string $name) : Bill; public function findByName(string $name): Bill;
/** /**
* @return Collection * @return Collection

View File

@ -212,10 +212,8 @@ class BudgetRepository implements BudgetRepositoryInterface
} }
/** /**
* This method is being used to generate the budget overview in the year/multi-year report. More specifically, this * This method is being used to generate the budget overview in the year/multi-year report. Its used
* method runs the query and returns the result that is used for this report. * in both the year/multi-year budget overview AND in the accompanying chart.
*
* The query is used in both the year/multi-year budget overview AND in the accompanying chart.
* *
* @param Collection $budgets * @param Collection $budgets
* @param Collection $accounts * @param Collection $accounts
@ -226,54 +224,31 @@ class BudgetRepository implements BudgetRepositoryInterface
*/ */
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array
{ {
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Budget $budget */
foreach ($budgets as $budget) {
$data[$budget->id] = [
'name' => $budget->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end); $collector->setAccounts($accounts)->setRange($start, $end);
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
$transactions = $collector->getJournals(); $transactions = $collector->getJournals();
// this is the date format we need: // loop transactions:
// define period to group on: /** @var Transaction $transaction */
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
// this is the set of transactions for this period
// in these budgets. Now they must be grouped (manually)
// id, period => amount
$data = [];
foreach ($transactions as $transaction) { foreach ($transactions as $transaction) {
$budgetId = max(intval($transaction->transaction_journal_budget_id), intval($transaction->transaction_budget_id)); $budgetId = max(intval($transaction->transaction_journal_budget_id), intval($transaction->transaction_budget_id));
$date = $transaction->date->format($carbonFormat); $date = $transaction->date->format($carbonFormat);
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $transaction->transaction_amount);
if (!isset($data[$budgetId])) {
$data[$budgetId]['name'] = $this->getBudgetName($budgetId, $budgets);
$data[$budgetId]['sum'] = '0';
$data[$budgetId]['entries'] = [];
}
if (!isset($data[$budgetId]['entries'][$date])) {
$data[$budgetId]['entries'][$date] = '0';
}
$data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date], $transaction->transaction_amount);
}
// and now the same for stuff without a budget:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL]);
$collector->withoutBudget();
$transactions = $collector->getJournals();
$data[0]['entries'] = [];
$data[0]['name'] = strval(trans('firefly.no_budget'));
$data[0]['sum'] = '0';
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($data[0]['entries'][$date])) {
$data[0]['entries'][$date] = '0';
}
$data[0]['entries'][$date] = bcadd($data[0]['entries'][$date], $transaction->transaction_amount);
} }
return $data; return $data;
@ -314,6 +289,40 @@ class BudgetRepository implements BudgetRepositoryInterface
return $set; return $set;
} }
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL]);
$collector->withoutBudget();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_budget')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
/** /**
* @param Collection $budgets * @param Collection $budgets
* @param Collection $accounts * @param Collection $accounts
@ -332,20 +341,23 @@ class BudgetRepository implements BudgetRepositoryInterface
Log::debug('spentInPeriod: and these accounts: ', $accountIds); Log::debug('spentInPeriod: and these accounts: ', $accountIds);
Log::debug(sprintf('spentInPeriod: Start date is "%s", end date is "%s"', $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('spentInPeriod: Start date is "%s", end date is "%s"', $start->format('Y-m-d'), $end->format('Y-m-d')));
$fromJournalsQuery = TransactionJournal $fromJournalsQuery = TransactionJournal::leftJoin(
::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') 'budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') )
->leftJoin( ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
'transactions', function (JoinClause $join) { ->leftJoin(
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); 'transactions', function (JoinClause $join) {
} $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
) 'transactions.amount', '<', 0
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) );
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) }
->whereNull('transaction_journals.deleted_at') )
->whereNull('transactions.deleted_at') ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.user_id', $this->user->id) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal'); ->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_journals.user_id', $this->user->id)
->where('transaction_types.type', 'Withdrawal');
// add budgets: // add budgets:
if ($budgets->count() > 0) { if ($budgets->count() > 0) {
@ -368,17 +380,16 @@ class BudgetRepository implements BudgetRepositoryInterface
* and budget_transaction.budget_id in (1,61) * and budget_transaction.budget_id in (1,61)
* and transactions.account_id in (2) * and transactions.account_id in (2)
*/ */
$fromTransactionsQuery = Transaction $fromTransactionsQuery = Transaction::leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id')
::leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->whereNull('transactions.deleted_at')
->whereNull('transactions.deleted_at') ->whereNull('transaction_journals.deleted_at')
->whereNull('transaction_journals.deleted_at') ->where('transactions.amount', '<', 0)
->where('transactions.amount', '<', 0) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id)
->where('transaction_journals.user_id', $this->user->id) ->where('transaction_types.type', 'Withdrawal');
->where('transaction_types.type', 'Withdrawal');
// add budgets: // add budgets:
if ($budgets->count() > 0) { if ($budgets->count() > 0) {
@ -550,25 +561,4 @@ class BudgetRepository implements BudgetRepositoryInterface
return $limit; return $limit;
} }
/**
* @param int $budgetId
* @param Collection $budgets
*
* @return string
*/
private function getBudgetName(int $budgetId, Collection $budgets): string
{
$first = $budgets->filter(
function (Budget $budget) use ($budgetId) {
return $budgetId === $budget->id;
}
);
if (!is_null($first->first())) {
return $first->first()->name;
}
return '(unknown)';
}
} }

View File

@ -111,6 +111,14 @@ interface BudgetRepositoryInterface
*/ */
public function getInactiveBudgets(): Collection; public function getInactiveBudgets(): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @param Collection $budgets * @param Collection $budgets
* @param Collection $accounts * @param Collection $accounts
@ -119,7 +127,7 @@ interface BudgetRepositoryInterface
* *
* @return string * @return string
*/ */
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string; public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string;
/** /**
* @param Collection $accounts * @param Collection $accounts
@ -143,7 +151,7 @@ interface BudgetRepositoryInterface
* *
* @return Budget * @return Budget
*/ */
public function update(Budget $budget, array $data) : Budget; public function update(Budget $budget, array $data): Budget;
/** /**
* @param Budget $budget * @param Budget $budget
@ -154,6 +162,6 @@ interface BudgetRepositoryInterface
* *
* @return BudgetLimit * @return BudgetLimit
*/ */
public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $range, int $amount) : BudgetLimit; public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $range, int $amount): BudgetLimit;
} }

View File

@ -15,12 +15,16 @@ namespace FireflyIII\Repositories\Category;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Navigation;
/** /**
* Class CategoryRepository * Class CategoryRepository
@ -221,6 +225,178 @@ class CategoryRepository implements CategoryRepositoryInterface
return $last; return $last;
} }
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Category $category */
foreach ($categories as $category) {
$data[$category->id] = [
'name' => $category->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
// loop transactions:
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// if positive, skip:
if (bccomp($transaction->transaction_amount, '0') === 1) {
continue;
}
$categoryId = max(intval($transaction->transaction_journal_category_id), intval($transaction->transaction_category_id));
$date = $transaction->date->format($carbonFormat);
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->withOpposingAccount();
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])->enableInternalFilter();
$collector->withoutCategory();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_category')),
'sum' => '0',
];
foreach ($transactions as $transaction) {
// if positive, skip:
if (bccomp($transaction->transaction_amount, '0') === 1) {
continue;
}
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
return $result;
}
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array
{
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
$data = [];
// prep data array:
/** @var Category $category */
foreach ($categories as $category) {
$data[$category->id] = [
'name' => $category->name,
'sum' => '0',
'entries' => [],
];
}
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
// loop transactions:
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// if negative, skip:
if (bccomp($transaction->transaction_amount, '0') === -1) {
continue;
}
$categoryId = max(intval($transaction->transaction_journal_category_id), intval($transaction->transaction_category_id));
$date = $transaction->date->format($carbonFormat);
$data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $transaction->transaction_amount);
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array
{
Log::debug('Now in periodIncomeNoCategory()');
$carbonFormat = Navigation::preferredCarbonFormat($start, $end);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->withOpposingAccount();
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->enableInternalFilter();
$collector->withoutCategory();
$transactions = $collector->getJournals();
$result = [
'entries' => [],
'name' => strval(trans('firefly.no_category')),
'sum' => '0',
];
Log::debug('Looping transactions..');
foreach ($transactions as $transaction) {
// if negative, skip:
if (bccomp($transaction->transaction_amount, '0') === -1) {
continue;
}
$date = $transaction->date->format($carbonFormat);
if (!isset($result['entries'][$date])) {
$result['entries'][$date] = '0';
}
$result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
}
Log::debug('Done looping transactions..');
Log::debug('Finished periodIncomeNoCategory()');
return $result;
}
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts
@ -233,6 +409,7 @@ class CategoryRepository implements CategoryRepositoryInterface
{ {
$sum = $this->sumInPeriod($categories, $accounts, TransactionType::WITHDRAWAL, $start, $end); $sum = $this->sumInPeriod($categories, $accounts, TransactionType::WITHDRAWAL, $start, $end);
$sum = bcmul($sum, '-1'); $sum = bcmul($sum, '-1');
return $sum; return $sum;
} }
@ -404,5 +581,4 @@ class CategoryRepository implements CategoryRepositoryInterface
return $sum; return $sum;
} }
} }

View File

@ -24,7 +24,6 @@ use Illuminate\Support\Collection;
*/ */
interface CategoryRepositoryInterface interface CategoryRepositoryInterface
{ {
/** /**
* @param Category $category * @param Category $category
* *
@ -49,7 +48,7 @@ interface CategoryRepositoryInterface
* *
* @return string * @return string
*/ */
public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) :string; public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string;
/** /**
* Find a category * Find a category
@ -58,7 +57,7 @@ interface CategoryRepositoryInterface
* *
* @return Category * @return Category
*/ */
public function find(int $categoryId) : Category; public function find(int $categoryId): Category;
/** /**
* Find a category * Find a category
@ -67,7 +66,7 @@ interface CategoryRepositoryInterface
* *
* @return Category * @return Category
*/ */
public function findByName(string $name) : Category; public function findByName(string $name): Category;
/** /**
* @param Category $category * @param Category $category
@ -93,6 +92,44 @@ interface CategoryRepositoryInterface
*/ */
public function lastUseDate(Category $category, Collection $accounts): Carbon; public function lastUseDate(Category $category, Collection $accounts): Carbon;
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $categories
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts
@ -110,7 +147,7 @@ interface CategoryRepositoryInterface
* *
* @return string * @return string
*/ */
public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string; public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string;
/** /**
* @param array $data * @param array $data

View File

@ -43,7 +43,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function find(int $currencyId) : TransactionCurrency public function find(int $currencyId): TransactionCurrency
{ {
$currency = TransactionCurrency::find($currencyId); $currency = TransactionCurrency::find($currencyId);
if (is_null($currency)) { if (is_null($currency)) {
@ -61,7 +61,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByCode(string $currencyCode) : TransactionCurrency public function findByCode(string $currencyCode): TransactionCurrency
{ {
$currency = TransactionCurrency::whereCode($currencyCode)->first(); $currency = TransactionCurrency::whereCode($currencyCode)->first();
if (is_null($currency)) { if (is_null($currency)) {
@ -78,7 +78,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByName(string $currencyName) : TransactionCurrency public function findByName(string $currencyName): TransactionCurrency
{ {
$preferred = TransactionCurrency::whereName($currencyName)->first(); $preferred = TransactionCurrency::whereName($currencyName)->first();
if (is_null($preferred)) { if (is_null($preferred)) {
@ -95,7 +95,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findBySymbol(string $currencySymbol) : TransactionCurrency public function findBySymbol(string $currencySymbol): TransactionCurrency
{ {
$currency = TransactionCurrency::whereSymbol($currencySymbol)->first(); $currency = TransactionCurrency::whereSymbol($currencySymbol)->first();
if (is_null($currency)) { if (is_null($currency)) {

View File

@ -39,7 +39,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function find(int $currencyId) : TransactionCurrency; public function find(int $currencyId): TransactionCurrency;
/** /**
* Find by currency code * Find by currency code
@ -48,7 +48,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByCode(string $currencyCode) : TransactionCurrency; public function findByCode(string $currencyCode): TransactionCurrency;
/** /**
* Find by currency name * Find by currency name
@ -57,7 +57,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findByName(string $currencyName) : TransactionCurrency; public function findByName(string $currencyName): TransactionCurrency;
/** /**
* Find by currency symbol * Find by currency symbol
@ -66,7 +66,7 @@ interface CurrencyRepositoryInterface
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
public function findBySymbol(string $currencySymbol) : TransactionCurrency; public function findBySymbol(string $currencySymbol): TransactionCurrency;
/** /**
* @return Collection * @return Collection

View File

@ -105,7 +105,7 @@ class JournalRepository implements JournalRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
public function find(int $journalId) : TransactionJournal public function find(int $journalId): TransactionJournal
{ {
$journal = $this->user->transactionJournals()->where('id', $journalId)->first(); $journal = $this->user->transactionJournals()->where('id', $journalId)->first();
if (is_null($journal)) { if (is_null($journal)) {

View File

@ -52,7 +52,7 @@ interface JournalRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
public function find(int $journalId) : TransactionJournal; public function find(int $journalId): TransactionJournal;
/** /**
* Get users very first transaction journal * Get users very first transaction journal

View File

@ -171,39 +171,37 @@ class JournalTasker implements JournalTaskerInterface
$identifier = intval($transaction->identifier); $identifier = intval($transaction->identifier);
// go! // go!
$sum $sum = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
= Transaction ->where('account_id', $transaction->account_id)
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->whereNull('transactions.deleted_at')
->where('account_id', $transaction->account_id) ->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at') ->where('transactions.id', '!=', $transactionId)
->whereNull('transaction_journals.deleted_at') ->where(
->where('transactions.id', '!=', $transactionId) function (Builder $q1) use ($date, $order, $journalId, $identifier) {
->where( $q1->where('transaction_journals.date', '<', $date); // date
function (Builder $q1) use ($date, $order, $journalId, $identifier) { $q1->orWhere(
$q1->where('transaction_journals.date', '<', $date); // date function (Builder $q2) use ($date, $order) { // function 1
$q1->orWhere( $q2->where('transaction_journals.date', $date);
function (Builder $q2) use ($date, $order) { // function 1 $q2->where('transaction_journals.order', '>', $order);
$q2->where('transaction_journals.date', $date); }
$q2->where('transaction_journals.order', '>', $order); );
} $q1->orWhere(
); function (Builder $q3) use ($date, $order, $journalId) { // function 2
$q1->orWhere( $q3->where('transaction_journals.date', $date);
function (Builder $q3) use ($date, $order, $journalId) { // function 2 $q3->where('transaction_journals.order', $order);
$q3->where('transaction_journals.date', $date); $q3->where('transaction_journals.id', '<', $journalId);
$q3->where('transaction_journals.order', $order); }
$q3->where('transaction_journals.id', '<', $journalId); );
} $q1->orWhere(
); function (Builder $q4) use ($date, $order, $journalId, $identifier) { // function 3
$q1->orWhere( $q4->where('transaction_journals.date', $date);
function (Builder $q4) use ($date, $order, $journalId, $identifier) { // function 3 $q4->where('transaction_journals.order', $order);
$q4->where('transaction_journals.date', $date); $q4->where('transaction_journals.id', $journalId);
$q4->where('transaction_journals.order', $order); $q4->where('transactions.identifier', '>', $identifier);
$q4->where('transaction_journals.id', $journalId); }
$q4->where('transactions.identifier', '>', $identifier); );
} }
); )->sum('transactions.amount');
}
)->sum('transactions.amount');
return strval($sum); return strval($sum);
} }

View File

@ -117,7 +117,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanksWithAmount() : Collection public function getPiggyBanksWithAmount(): Collection
{ {
$set = $this->getPiggyBanks(); $set = $this->getPiggyBanks();
foreach ($set as $piggy) { foreach ($set as $piggy) {

View File

@ -58,7 +58,7 @@ interface PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getEvents(PiggyBank $piggyBank) : Collection; public function getEvents(PiggyBank $piggyBank): Collection;
/** /**
* Highest order of all piggy banks. * Highest order of all piggy banks.
@ -72,14 +72,14 @@ interface PiggyBankRepositoryInterface
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanks() : Collection; public function getPiggyBanks(): Collection;
/** /**
* Also add amount in name. * Also add amount in name.
* *
* @return Collection * @return Collection
*/ */
public function getPiggyBanksWithAmount() : Collection; public function getPiggyBanksWithAmount(): Collection;
/** /**
* Set all piggy banks to order 0. * Set all piggy banks to order 0.

View File

@ -93,7 +93,7 @@ class TagRepository implements TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function find(int $tagId) : Tag public function find(int $tagId): Tag
{ {
$tag = $this->user->tags()->find($tagId); $tag = $this->user->tags()->find($tagId);
if (is_null($tag)) { if (is_null($tag)) {
@ -108,7 +108,7 @@ class TagRepository implements TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function findByTag(string $tag) : Tag public function findByTag(string $tag): Tag
{ {
$tags = $this->user->tags()->get(); $tags = $this->user->tags()->get();
/** @var Tag $tag */ /** @var Tag $tag */

View File

@ -49,14 +49,14 @@ interface TagRepositoryInterface
* *
* @return Tag * @return Tag
*/ */
public function find(int $tagId) : Tag; public function find(int $tagId): Tag;
/** /**
* @param string $tag * @param string $tag
* *
* @return Tag * @return Tag
*/ */
public function findByTag(string $tag) : Tag; public function findByTag(string $tag): Tag;
/** /**
* This method returns all the user's tags. * This method returns all the user's tags.

View File

@ -113,12 +113,11 @@ class UserRepository implements UserRepositoryInterface
$return['bills'] = $user->bills()->count(); $return['bills'] = $user->bills()->count();
$return['categories'] = $user->categories()->count(); $return['categories'] = $user->categories()->count();
$return['budgets'] = $user->budgets()->count(); $return['budgets'] = $user->budgets()->count();
$return['budgets_with_limits'] = BudgetLimit $return['budgets_with_limits'] = BudgetLimit::distinct()
::distinct() ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->where('amount', '>', 0)
->where('amount', '>', 0) ->whereNull('budgets.deleted_at')
->whereNull('budgets.deleted_at') ->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count();
->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count();
$return['export_jobs'] = $user->exportJobs()->count(); $return['export_jobs'] = $user->exportJobs()->count();
$return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count(); $return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count();
$return['import_jobs'] = $user->exportJobs()->count(); $return['import_jobs'] = $user->exportJobs()->count();

View File

@ -40,20 +40,7 @@ class ExpandedForm
*/ */
public function amount(string $name, $value = null, array $options = []): string public function amount(string $name, $value = null, array $options = []): string
{ {
$label = $this->label($name, $options); return $this->currencyField($name, 'amount', $value, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = Amt::getAllCurrencies();
unset($options['currency']);
unset($options['placeholder']);
$html = view('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
} }
/** /**
@ -65,20 +52,7 @@ class ExpandedForm
*/ */
public function amountSmall(string $name, $value = null, array $options = []): string public function amountSmall(string $name, $value = null, array $options = []): string
{ {
$label = $this->label($name, $options); return $this->currencyField($name, 'amount-small', $value, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = Amt::getAllCurrencies();
unset($options['currency']);
unset($options['placeholder']);
$html = view('form.amount-small', compact('defaultCurrency', 'currencies', 'classes', 'name', 'value', 'options'))->render();
return $html;
} }
/** /**
@ -90,18 +64,7 @@ class ExpandedForm
*/ */
public function balance(string $name, $value = null, array $options = []): string public function balance(string $name, $value = null, array $options = []): string
{ {
$label = $this->label($name, $options); return $this->currencyField($name, 'balance', $value, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = round($this->fillFieldValue($name, $value), 2);
$options['step'] = 'any';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = Amt::getAllCurrencies();
unset($options['currency']);
unset($options['placeholder']);
$html = view('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
} }
/** /**
@ -320,7 +283,6 @@ class ExpandedForm
return $html; return $html;
} }
/** /**
* @param $name * @param $name
* @param array $list * @param array $list
@ -500,4 +462,29 @@ class ExpandedForm
return strval(trans('form.' . $name)); return strval(trans('form.' . $name));
} }
/**
* @param string $name
* @param string $view
* @param null $value
* @param array $options
*
* @return string
*/
private function currencyField(string $name, string $view, $value = null, array $options = []): string
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = Amt::getAllCurrencies();
unset($options['currency']);
unset($options['placeholder']);
$html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
} }

View File

@ -246,8 +246,8 @@ class Navigation
* If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is less than a year, * If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is less than a year,
* method returns "Y-m". If the date difference is larger, method returns "Y". * method returns "Y-m". If the date difference is larger, method returns "Y".
* *
* @param Carbon $start * @param \Carbon\Carbon $start
* @param Carbon $end * @param \Carbon\Carbon $end
* *
* @return string * @return string
*/ */
@ -270,8 +270,8 @@ class Navigation
* If the date difference between start and end is less than a month, method returns "1D". If the difference is less than a year, * If the date difference between start and end is less than a month, method returns "1D". If the difference is less than a year,
* method returns "1M". If the date difference is larger, method returns "1Y". * method returns "1M". If the date difference is larger, method returns "1Y".
* *
* @param Carbon $start * @param \Carbon\Carbon $start
* @param Carbon $end * @param \Carbon\Carbon $end
* *
* @return string * @return string
*/ */

View File

@ -109,8 +109,7 @@ class Transaction extends Twig_Extension
'optionalJournalAmount', function (int $journalId, string $transactionAmount, string $code, string $type): string { 'optionalJournalAmount', function (int $journalId, string $transactionAmount, string $code, string $type): string {
$amount = strval( $amount = strval(
TransactionModel TransactionModel::where('transaction_journal_id', $journalId)
::where('transaction_journal_id', $journalId)
->whereNull('deleted_at') ->whereNull('deleted_at')
->where('amount', '<', 0) ->where('amount', '<', 0)
->sum('amount') ->sum('amount')
@ -193,8 +192,7 @@ class Transaction extends Twig_Extension
// name is present in object, use that one: // name is present in object, use that one:
if (bccomp($transaction->transaction_amount, '0') === -1 && !is_null($transaction->opposing_account_id)) { if (bccomp($transaction->transaction_amount, '0') === -1 && !is_null($transaction->opposing_account_id)) {
$name = intval($transaction->opposing_account_encrypted) === 1 ? Crypt::decrypt($transaction->opposing_account_name) $name = $transaction->opposing_account_name;
: $transaction->opposing_account_name;
$id = intval($transaction->opposing_account_id); $id = intval($transaction->opposing_account_id);
$type = intval($transaction->opposing_account_type); $type = intval($transaction->opposing_account_type);
} }
@ -204,8 +202,7 @@ class Transaction extends Twig_Extension
// if the amount is negative, find the opposing account and use that one: // if the amount is negative, find the opposing account and use that one:
$journalId = $transaction->journal_id; $journalId = $transaction->journal_id;
/** @var TransactionModel $other */ /** @var TransactionModel $other */
$other = TransactionModel $other = TransactionModel::where('transaction_journal_id', $journalId)->where('transactions.id', '!=', $transaction->id)
::where('transaction_journal_id', $journalId)->where('transactions.id', '!=', $transaction->id)
->where('amount', '=', bcmul($transaction->transaction_amount, '-1'))->where('identifier', $transaction->identifier) ->where('amount', '=', bcmul($transaction->transaction_amount, '-1'))->where('identifier', $transaction->identifier)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
@ -278,8 +275,7 @@ class Transaction extends Twig_Extension
if (bccomp($transaction->transaction_amount, '0') === 1 && is_null($transaction->opposing_account_id)) { if (bccomp($transaction->transaction_amount, '0') === 1 && is_null($transaction->opposing_account_id)) {
$journalId = $transaction->journal_id; $journalId = $transaction->journal_id;
/** @var TransactionModel $other */ /** @var TransactionModel $other */
$other = TransactionModel $other = TransactionModel::where('transaction_journal_id', $journalId)->where('transactions.id', '!=', $transaction->id)
::where('transaction_journal_id', $journalId)->where('transactions.id', '!=', $transaction->id)
->where('amount', '=', bcmul($transaction->transaction_amount, '-1'))->where('identifier', $transaction->identifier) ->where('amount', '=', bcmul($transaction->transaction_amount, '-1'))->where('identifier', $transaction->identifier)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')

View File

@ -57,8 +57,6 @@ class FireflyValidator extends Validator
* @param $value * @param $value
* *
* @return bool * @return bool
* @internal param $parameters
*
* *
*/ */
public function validate2faCode($attribute, $value): bool public function validate2faCode($attribute, $value): bool
@ -95,6 +93,27 @@ class FireflyValidator extends Validator
} }
/**
* @param $attribute
* @param $value
*
* @return bool
*
*/
public function validateBic($attribute, $value): bool
{
$regex = '/^[a-z]{6}[0-9a-z]{2}([0-9a-z]{3})?\z/i';
$result = preg_match($regex, $value);
if ($result === false) {
return false;
}
if ($result === 0) {
return false;
}
return true;
}
/** /**
* @param $attribute * @param $attribute
* @param $value * @param $value
@ -455,7 +474,6 @@ class FireflyValidator extends Validator
* @param $value * @param $value
* *
* @return bool * @return bool
* @internal param $parameters
* *
*/ */
private function validateByAccountId($value): bool private function validateByAccountId($value): bool

106
composer.lock generated
View File

@ -105,20 +105,20 @@
}, },
{ {
"name": "classpreloader/classpreloader", "name": "classpreloader/classpreloader",
"version": "3.0.0", "version": "3.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ClassPreloader/ClassPreloader.git", "url": "https://github.com/ClassPreloader/ClassPreloader.git",
"reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a" "reference": "bc7206aa892b5a33f4680421b69b191efd32b096"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/bc7206aa892b5a33f4680421b69b191efd32b096",
"reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", "reference": "bc7206aa892b5a33f4680421b69b191efd32b096",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^1.0|^2.0", "nikic/php-parser": "^1.0|^2.0|^3.0",
"php": ">=5.5.9" "php": ">=5.5.9"
}, },
"require-dev": { "require-dev": {
@ -127,7 +127,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.0-dev" "dev-master": "3.1-dev"
} }
}, },
"autoload": { "autoload": {
@ -155,7 +155,7 @@
"class", "class",
"preload" "preload"
], ],
"time": "2015-11-09 22:51:51" "time": "2016-09-16 12:50:15"
}, },
{ {
"name": "davejamesmiller/laravel-breadcrumbs", "name": "davejamesmiller/laravel-breadcrumbs",
@ -445,16 +445,16 @@
}, },
{ {
"name": "doctrine/common", "name": "doctrine/common",
"version": "v2.6.1", "version": "v2.6.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/common.git", "url": "https://github.com/doctrine/common.git",
"reference": "a579557bc689580c19fee4e27487a67fe60defc0" "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", "url": "https://api.github.com/repos/doctrine/common/zipball/7bce00698899aa2c06fe7365c76e4d78ddb15fa3",
"reference": "a579557bc689580c19fee4e27487a67fe60defc0", "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -514,7 +514,7 @@
"persistence", "persistence",
"spl" "spl"
], ],
"time": "2015-12-25 13:18:31" "time": "2016-11-30 16:50:46"
}, },
{ {
"name": "doctrine/dbal", "name": "doctrine/dbal",
@ -797,20 +797,20 @@
}, },
{ {
"name": "jeremeamia/SuperClosure", "name": "jeremeamia/SuperClosure",
"version": "2.2.0", "version": "2.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/jeremeamia/super_closure.git", "url": "https://github.com/jeremeamia/super_closure.git",
"reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938" "reference": "443c3df3207f176a1b41576ee2a66968a507b3db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/29a88be2a4846d27c1613aed0c9071dfad7b5938", "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/443c3df3207f176a1b41576ee2a66968a507b3db",
"reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938", "reference": "443c3df3207f176a1b41576ee2a66968a507b3db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^1.2|^2.0", "nikic/php-parser": "^1.2|^2.0|^3.0",
"php": ">=5.4", "php": ">=5.4",
"symfony/polyfill-php56": "^1.0" "symfony/polyfill-php56": "^1.0"
}, },
@ -820,7 +820,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.2-dev" "dev-master": "2.3-dev"
} }
}, },
"autoload": { "autoload": {
@ -851,7 +851,7 @@
"serialize", "serialize",
"tokenizer" "tokenizer"
], ],
"time": "2015-12-05 17:17:57" "time": "2016-12-07 09:37:55"
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
@ -2060,16 +2060,16 @@
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v3.1.7", "version": "v3.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc" "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/28b0832b2553ffb80cabef6a7a812ff1e670c0bc", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e8f47a327c2f0fd5aa04fa60af2b693006ed7283",
"reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc", "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2089,7 +2089,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.1-dev" "dev-master": "3.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -2116,7 +2116,7 @@
], ],
"description": "Symfony EventDispatcher Component", "description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-10-13 06:28:43" "time": "2016-10-13 06:29:04"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
@ -3578,16 +3578,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "4.0.2", "version": "4.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "6cba06ff75a1a63a71033e1a01b89056f3af1e8d" "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6cba06ff75a1a63a71033e1a01b89056f3af1e8d", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/903fd6318d0a90b4770a009ff73e4a4e9c437929",
"reference": "6cba06ff75a1a63a71033e1a01b89056f3af1e8d", "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3637,7 +3637,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2016-11-01 05:06:24" "time": "2016-11-28 16:00:31"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -3822,16 +3822,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "5.6.5", "version": "5.7.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "875145fabfa261fa9c1aea663dd29ddce92dca8f" "reference": "336aff0ac52e306c98e7455bc3e8d7b0bf777a5e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/875145fabfa261fa9c1aea663dd29ddce92dca8f", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/336aff0ac52e306c98e7455bc3e8d7b0bf777a5e",
"reference": "875145fabfa261fa9c1aea663dd29ddce92dca8f", "reference": "336aff0ac52e306c98e7455bc3e8d7b0bf777a5e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3843,14 +3843,14 @@
"myclabs/deep-copy": "~1.3", "myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0", "php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.3.1", "phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "^4.0.1", "phpunit/php-code-coverage": "^4.0.3",
"phpunit/php-file-iterator": "~1.4", "phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2", "phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6", "phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.2", "phpunit/phpunit-mock-objects": "^3.2",
"sebastian/comparator": "~1.2.2", "sebastian/comparator": "~1.2.2",
"sebastian/diff": "~1.2", "sebastian/diff": "~1.2",
"sebastian/environment": "^1.3 || ^2.0", "sebastian/environment": "^1.3.4 || ^2.0",
"sebastian/exporter": "~2.0", "sebastian/exporter": "~2.0",
"sebastian/global-state": "~1.0", "sebastian/global-state": "~1.0",
"sebastian/object-enumerator": "~2.0", "sebastian/object-enumerator": "~2.0",
@ -3874,7 +3874,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "5.6.x-dev" "dev-master": "5.7.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -3900,7 +3900,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2016-11-21 15:23:34" "time": "2016-12-03 08:33:00"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
@ -4476,16 +4476,16 @@
}, },
{ {
"name": "symfony/class-loader", "name": "symfony/class-loader",
"version": "v3.1.7", "version": "v3.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/class-loader.git", "url": "https://github.com/symfony/class-loader.git",
"reference": "61de6c27f9d4efe988ea8274f19c2d4e331dd4e4" "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/61de6c27f9d4efe988ea8274f19c2d4e331dd4e4", "url": "https://api.github.com/repos/symfony/class-loader/zipball/87cd4e69435d98de01d0162c5f9c0ac017075c63",
"reference": "61de6c27f9d4efe988ea8274f19c2d4e331dd4e4", "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4501,7 +4501,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.1-dev" "dev-master": "3.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4528,7 +4528,7 @@
], ],
"description": "Symfony ClassLoader Component", "description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-11-15 12:07:16" "time": "2016-11-29 08:26:13"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@ -4641,25 +4641,31 @@
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v3.1.7", "version": "v3.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/yaml.git",
"reference": "9da375317228e54f4ea1b013b30fa47417e84943" "reference": "f2300ba8fbb002c028710b92e1906e7457410693"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/9da375317228e54f4ea1b013b30fa47417e84943", "url": "https://api.github.com/repos/symfony/yaml/zipball/f2300ba8fbb002c028710b92e1906e7457410693",
"reference": "9da375317228e54f4ea1b013b30fa47417e84943", "reference": "f2300ba8fbb002c028710b92e1906e7457410693",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.5.9" "php": ">=5.5.9"
}, },
"require-dev": {
"symfony/console": "~2.8|~3.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.1-dev" "dev-master": "3.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -4686,7 +4692,7 @@
], ],
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-11-18 21:05:29" "time": "2016-11-18 21:17:59"
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",

View File

@ -24,7 +24,7 @@ return [
'must_confirm_account' => false, 'must_confirm_account' => false,
], ],
'chart' => 'chartjs', 'chart' => 'chartjs',
'version' => '4.2.0', 'version' => '4.2.1',
'csv_import_enabled' => true, 'csv_import_enabled' => true,
'maxUploadSize' => 5242880, 'maxUploadSize' => 5242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],

View File

@ -87,7 +87,7 @@ class CreateMainTables extends Migration
$table->integer('user_id', false, true); $table->integer('user_id', false, true);
$table->integer('account_type_id', false, true); $table->integer('account_type_id', false, true);
$table->string('name', 1024); $table->string('name', 1024);
$table->decimal('virtual_balance', 10, 4)->nullable(); $table->decimal('virtual_balance', 14, 4)->nullable();
$table->string('iban', 255)->nullable(); $table->string('iban', 255)->nullable();
$table->boolean('active')->default(1); $table->boolean('active')->default(1);
@ -165,8 +165,8 @@ class CreateMainTables extends Migration
$table->integer('user_id', false, true); $table->integer('user_id', false, true);
$table->string('name', 1024); $table->string('name', 1024);
$table->string('match', 1024); $table->string('match', 1024);
$table->decimal('amount_min', 10, 4); $table->decimal('amount_min', 14, 4);
$table->decimal('amount_max', 10, 4); $table->decimal('amount_max', 14, 4);
$table->date('date'); $table->date('date');
$table->string('repeat_freq', 30); $table->string('repeat_freq', 30);
$table->smallInteger('skip', false, true)->default(0); $table->smallInteger('skip', false, true)->default(0);
@ -215,7 +215,7 @@ class CreateMainTables extends Migration
$table->timestamps(); $table->timestamps();
$table->integer('budget_id', false, true); $table->integer('budget_id', false, true);
$table->date('startdate'); $table->date('startdate');
$table->decimal('amount', 10, 4); $table->decimal('amount', 14, 4);
$table->string('repeat_freq', 30); $table->string('repeat_freq', 30);
$table->boolean('repeats')->default(0); $table->boolean('repeats')->default(0);
@ -233,7 +233,7 @@ class CreateMainTables extends Migration
$table->integer('budget_limit_id', false, true); $table->integer('budget_limit_id', false, true);
$table->date('startdate'); $table->date('startdate');
$table->date('enddate'); $table->date('enddate');
$table->decimal('amount', 10, 4); $table->decimal('amount', 14, 4);
// link budget limit id to budget_limitss table // link budget limit id to budget_limitss table
$table->foreign('budget_limit_id')->references('id')->on('budget_limits')->onDelete('cascade'); $table->foreign('budget_limit_id')->references('id')->on('budget_limits')->onDelete('cascade');
@ -313,7 +313,7 @@ class CreateMainTables extends Migration
$table->softDeletes(); $table->softDeletes();
$table->integer('account_id', false, true); $table->integer('account_id', false, true);
$table->string('name', 1024); $table->string('name', 1024);
$table->decimal('targetamount', 10, 4); $table->decimal('targetamount', 14, 4);
$table->date('startdate')->nullable(); $table->date('startdate')->nullable();
$table->date('targetdate')->nullable(); $table->date('targetdate')->nullable();
$table->integer('order', false, true)->default(0); $table->integer('order', false, true)->default(0);
@ -334,7 +334,7 @@ class CreateMainTables extends Migration
$table->integer('piggy_bank_id', false, true); $table->integer('piggy_bank_id', false, true);
$table->date('startdate')->nullable(); $table->date('startdate')->nullable();
$table->date('targetdate')->nullable(); $table->date('targetdate')->nullable();
$table->decimal('currentamount', 10, 4); $table->decimal('currentamount', 14, 4);
$table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade');
@ -606,7 +606,7 @@ class CreateMainTables extends Migration
$table->integer('piggy_bank_id', false, true); $table->integer('piggy_bank_id', false, true);
$table->integer('transaction_journal_id', false, true)->nullable(); $table->integer('transaction_journal_id', false, true)->nullable();
$table->date('date'); $table->date('date');
$table->decimal('amount', 10, 4); $table->decimal('amount', 14, 4);
$table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null'); $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null');
@ -623,7 +623,7 @@ class CreateMainTables extends Migration
$table->integer('account_id', false, true); $table->integer('account_id', false, true);
$table->integer('transaction_journal_id', false, true); $table->integer('transaction_journal_id', false, true);
$table->string('description', 1024)->nullable(); $table->string('description', 1024)->nullable();
$table->decimal('amount', 10, 4); $table->decimal('amount', 14, 4);
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade'); $table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');

View File

@ -1,4 +1,13 @@
<?php <?php
/**
* 2016_10_09_150037_expand_transactions_table.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -1,4 +1,13 @@
<?php <?php
/**
* 2016_10_22_075804_changes_for_v410.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -1,4 +1,13 @@
<?php <?php
/**
* 2016_11_24_210552_changes_for_v420.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;

View File

@ -28,6 +28,7 @@ class TransactionCurrencySeeder extends Seeder
TransactionCurrency::create(['code' => 'HUF', 'name' => 'Hungarian forint', 'symbol' => 'Ft']); TransactionCurrency::create(['code' => 'HUF', 'name' => 'Hungarian forint', 'symbol' => 'Ft']);
TransactionCurrency::create(['code' => 'BRL', 'name' => 'Real', 'symbol' => 'R$']); TransactionCurrency::create(['code' => 'BRL', 'name' => 'Real', 'symbol' => 'R$']);
TransactionCurrency::create(['code' => 'GBP', 'name' => 'British Pound', 'symbol' => '£']); TransactionCurrency::create(['code' => 'GBP', 'name' => 'British Pound', 'symbol' => '£']);
TransactionCurrency::create(['code' => 'IDR', 'name' => 'Indonesian rupiah', 'symbol' => 'Rp']);
} }
} }

View File

@ -6,9 +6,6 @@
* of the MIT license. See the LICENSE file for details. * of the MIT license. See the LICENSE file for details.
*/ */
/* global $, lineChart, dateString, accountID, token, incomeByCategoryUri, expenseByCategoryUri, expenseByBudgetUri */
// Return a helper with preserved width of cells // Return a helper with preserved width of cells
var fixHelper = function (e, tr) { var fixHelper = function (e, tr) {
"use strict"; "use strict";
@ -24,11 +21,11 @@ var fixHelper = function (e, tr) {
$(function () { $(function () {
"use strict"; "use strict";
lineChart('chart/account/' + accountID + '/' + dateString, 'period-specific-account'); lineChart(periodUri, 'period-specific-account');
pieChart(incomeByCategoryUri, 'account-cat-in'); pieChart(incomeCategoryUri, 'account-cat-in');
pieChart(expenseByCategoryUri, 'account-cat-out'); pieChart(expenseCategoryUri, 'account-cat-out');
pieChart(expenseByBudgetUri, 'account-budget-out'); pieChart(expenseBudgetUri, 'account-budget-out');
// sortable! // sortable!

View File

@ -15,10 +15,10 @@ var fixHelper = function (e, tr) {
$(function () { $(function () {
"use strict"; "use strict";
lineChart('chart/account/' + accountID, 'overview-chart'); lineChart(singleUri, 'overview-chart');
pieChart(incomeByCategoryUri, 'account-cat-in'); pieChart(incomeCategoryUri, 'account-cat-in');
pieChart(expenseByCategoryUri, 'account-cat-out'); pieChart(expenseCategoryUri, 'account-cat-out');
pieChart(expenseByBudgetUri, 'account-budget-out'); pieChart(expenseBudgetUri, 'account-budget-out');
// sortable! // sortable!

View File

@ -2,8 +2,6 @@
$(function () { $(function () {
"use strict"; "use strict";
if (typeof(columnChart) === 'function' && typeof(billID) !== 'undefined') { columnChart(billUri, 'bill-overview');
columnChart('chart/bill/' + billID, 'bill-overview');
}
} }
); );

View File

@ -1,117 +1,6 @@
/* globals $, budgeted:true, currencySymbol, budgetIncomeTotal, columnChart, budgetedMuch, budgetedPercentage, token, budgetID, repetitionID, spent, lineChart */
function drawSpentBar() {
"use strict";
if ($('.spentBar').length > 0) {
var overspent = spent > budgeted;
var pct;
if (overspent) {
// draw overspent bar
pct = (budgeted / spent) * 100;
$('.spentBar .progress-bar-warning').css('width', pct + '%');
$('.spentBar .progress-bar-danger').css('width', (100 - pct) + '%');
} else {
// draw normal bar:
pct = (spent / budgeted) * 100;
$('.spentBar .progress-bar-info').css('width', pct + '%');
}
}
}
function drawBudgetedBar() {
"use strict";
if ($('.budgetedBar').length > 0) {
var budgetedMuch = budgeted > budgetIncomeTotal;
// recalculate percentage:
var pct;
if (budgetedMuch) {
// budgeted too much.
pct = (budgetIncomeTotal / budgeted) * 100;
$('.budgetedBar .progress-bar-warning').css('width', pct + '%');
$('.budgetedBar .progress-bar-danger').css('width', (100 - pct) + '%');
$('.budgetedBar .progress-bar-info').css('width', 0);
} else {
pct = (budgeted / budgetIncomeTotal) * 100;
$('.budgetedBar .progress-bar-warning').css('width', 0);
$('.budgetedBar .progress-bar-danger').css('width', 0);
$('.budgetedBar .progress-bar-info').css('width', pct + '%');
}
$('#budgetedAmount').html(currencySymbol + ' ' + budgeted.toFixed(2));
}
}
function updateBudgetedAmounts(e) {
"use strict";
var target = $(e.target);
var id = target.data('id');
var value = target.val();
var original = target.data('original');
var difference = value - original;
if (difference !== 0) {
// add difference to 'budgeted' var
budgeted = budgeted + difference;
// update original:
target.data('original', value);
// run drawBudgetedBar() again:
drawBudgetedBar();
// send a post to Firefly to update the amount:
$.post('budgets/amount/' + id, {amount: value, _token: token}).done(function (data) {
// update the link if relevant:
if (data.repetition > 0) {
$('.budget-link[data-id="' + id + '"]').attr('href', 'budgets/show/' + id + '/' + data.repetition);
} else {
$('.budget-link[data-id="' + id + '"]').attr('href', 'budgets/show/' + id);
}
});
}
console.log('Budget id is ' + id);
console.log('Difference = ' + (value - original ));
}
$(function () { $(function () {
"use strict"; "use strict";
$('.updateIncome').on('click', updateIncome); columnChart(budgetChartUri, 'budgetOverview');
/*
On start, fill the "spent"-bar using the content from the page.
*/
drawSpentBar();
drawBudgetedBar();
/*
When the input changes, update the percentages for the budgeted bar:
*/
$('input[type="number"]').on('input', updateBudgetedAmounts);
/*
Draw the charts, if necessary:
*/
if (typeof budgetID !== 'undefined' && typeof repetitionID === 'undefined') {
columnChart('chart/budget/' + budgetID, 'budgetOverview');
}
if (typeof budgetID !== 'undefined' && typeof repetitionID !== 'undefined') {
lineChart('chart/budget/' + budgetID + '/' + repetitionID, 'budgetOverview');
}
}); });
function updateIncome() {
"use strict";
$('#defaultModal').empty().load('budgets/income', function () {
$('#defaultModal').modal('show');
});
return false;
}

View File

@ -1,19 +1,4 @@
/* globals $, categoryID, columnChart, categoryDate */ /* globals $, categoryID, columnChart, categoryDate */
$(function () { $(function () {
"use strict"; "use strict";
if (typeof categoryID !== 'undefined') {
// more splits:
if ($('#all').length > 0) {
columnChart('chart/category/' + categoryID + '/all', 'all');
}
if ($('#period').length > 0) {
columnChart('chart/category/' + categoryID + '/period', 'period');
}
}
if (typeof categoryID !== 'undefined' && typeof categoryDate !== 'undefined') {
columnChart('chart/category/' + categoryID + '/period/' + categoryDate, 'period-specific-period');
}
}); });

View File

@ -0,0 +1,4 @@
$(function () {
"use strict";
columnChart(specific, 'period-specific-period');
});

View File

@ -1,19 +1,5 @@
/* globals $, categoryID, columnChart, categoryDate */
$(function () { $(function () {
"use strict"; "use strict";
if (typeof categoryID !== 'undefined') { columnChart(all, 'all');
// more splits: columnChart(current, 'period');
if ($('#all').length > 0) {
columnChart('chart/category/' + categoryID + '/all', 'all');
}
if ($('#period').length > 0) {
columnChart('chart/category/' + categoryID + '/period', 'period');
}
}
if (typeof categoryID !== 'undefined' && typeof categoryDate !== 'undefined') {
columnChart('chart/category/' + categoryID + '/period/' + categoryDate, 'period-specific-period');
}
}); });

View File

@ -1,19 +0,0 @@
/* globals $, categoryID, columnChart, categoryDate */
$(function () {
"use strict";
if (typeof categoryID !== 'undefined') {
// more splits:
if ($('#all').length > 0) {
columnChart('chart/category/' + categoryID + '/all', 'all');
}
if ($('#period').length > 0) {
columnChart('chart/category/' + categoryID + '/period', 'period');
}
}
if (typeof categoryID !== 'undefined' && typeof categoryDate !== undefined) {
columnChart('chart/category/' + categoryID + '/period/' + categoryDate, 'period-specific-period');
}
});

View File

@ -32,14 +32,14 @@ function endTheTour() {
function drawChart() { function drawChart() {
"use strict"; "use strict";
lineChart('chart/account/frontpage', 'accounts-chart'); lineChart(accountFrontpageUri, 'accounts-chart');
if (billCount > 0) { if (billCount > 0) {
pieChart('chart/bill/frontpage', 'bills-chart'); pieChart('chart/bill/frontpage', 'bills-chart');
} }
stackedColumnChart('chart/budget/frontpage', 'budgets-chart'); stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
columnChart('chart/category/frontpage', 'categories-chart'); columnChart('chart/category/frontpage', 'categories-chart');
columnChart('chart/account/expense', 'expense-accounts-chart'); columnChart(accountExpenseUri, 'expense-accounts-chart');
columnChart('chart/account/revenue', 'revenue-accounts-chart'); columnChart(accountRevenueUri, 'revenue-accounts-chart');
getBoxAmounts(); getBoxAmounts();

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