Merge branch 'release/3.4.0.7'

This commit is contained in:
James Cole
2015-05-18 18:23:07 +02:00
194 changed files with 5878 additions and 4194 deletions

View File

@@ -1,5 +1,5 @@
# Firefly III # Firefly III
#### v3.4.0.6 #### v3.4.0.7
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii) [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) [![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
@@ -34,20 +34,11 @@ and the philosophy behind it.
It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since. It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since.
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current features ## Current features
- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); - [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management; - You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management;
- You can manage different types of accounts - You can manage different types of accounts;
- [Asset](https://en.wikipedia.org/wiki/Asset) accounts - [Asset](https://en.wikipedia.org/wiki/Asset) accounts
- Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household)) - Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household))
- Saving accounts - Saving accounts
@@ -65,8 +56,8 @@ Everything is organised:
- Clear views that should show you how you're doing; - Clear views that should show you how you're doing;
- Easy navigation through your records; - Easy navigation through your records;
- Browse back and forth to see previous months or even years; - Browse back and forth to see previous months or even years;
- Lots of charts because we all love them. - Lots of charts because we all love them;
- Financial reporting showing you how well you are doing; - Financial reporting showing you how well you are doing.
## Screenshots ## Screenshots
@@ -86,6 +77,16 @@ _Please note that everything in these screenshots is fictional and may not be re
![Piggy banks](https://i.nder.be/hkud0h53) ![Piggy banks](https://i.nder.be/hkud0h53)
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current state ## Current state
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list

View File

@@ -26,6 +26,8 @@ class Kernel extends ConsoleKernel
* *
* @param \Illuminate\Console\Scheduling\Schedule $schedule * @param \Illuminate\Console\Scheduling\Schedule $schedule
* *
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return void * @return void
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)

View File

@@ -27,6 +27,7 @@ class Handler extends ExceptionHandler
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param \Exception $e * @param \Exception $e
* @SuppressWarnings(PHPMD.ShortVariable)
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
@@ -43,6 +44,7 @@ class Handler extends ExceptionHandler
* Report or log an exception. * Report or log an exception.
* *
* This is a great spot to send exceptions to Sentry, Bugsnag, etc. * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
* @SuppressWarnings(PHPMD.ShortVariable)
* *
* @param \Exception $e * @param \Exception $e
* *

View File

@@ -6,7 +6,6 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Log;
/** /**
* Class ConnectJournalToPiggyBank * Class ConnectJournalToPiggyBank
@@ -28,6 +27,8 @@ class ConnectJournalToPiggyBank
/** /**
* Handle the event when journal is saved. * Handle the event when journal is saved.
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param JournalCreated $event * @param JournalCreated $event
* *
* @return boolean * @return boolean
@@ -37,45 +38,25 @@ class ConnectJournalToPiggyBank
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $event->journal; $journal = $event->journal;
$piggyBankId = $event->piggyBankId; $piggyBankId = $event->piggyBankId;
if (intval($piggyBankId) < 1) {
return false;
}
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); $piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank) || $journal->transactionType->type != 'Transfer') { if (is_null($piggyBank)) {
return false;
}
Log::debug('Found a piggy bank');
$amount = $journal->amount;
Log::debug('Amount: ' . $amount);
if ($amount == 0) {
return false; return false;
} }
// update piggy bank rep for date of transaction journal. // update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) { if (is_null($repetition)) {
Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d'));
return false; return false;
} }
Log::debug('Found rep! ' . $repetition->id); $amount = $journal->amount;
/*
* Add amount when
*/
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) { foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) { if ($transaction->account_id == $piggyBank->account_id) {
if ($transaction->amount < 0) { if ($transaction->amount < 0) {
$amount = $amount * -1; $amount = $transaction->amount * -1;
Log::debug('Transaction is away from piggy, so amount becomes ' . $amount);
} else {
Log::debug('Transaction is to from piggy, so amount stays ' . $amount);
} }
} }
} }
@@ -83,14 +64,8 @@ class ConnectJournalToPiggyBank
$repetition->currentamount += $amount; $repetition->currentamount += $amount;
$repetition->save(); $repetition->save();
PiggyBankEvent::create( 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
]
);
return true; return true;
} }

View File

@@ -1,36 +0,0 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted;
/**
* Class JournalDeletedHandler
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class JournalDeletedHandler
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalDeleted $event
*
* @return void
*/
public function handle(JournalDeleted $event)
{
//
}
}

View File

@@ -2,6 +2,7 @@
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
/** /**
* Class UpdateJournalConnection * Class UpdateJournalConnection
@@ -39,7 +40,11 @@ class UpdateJournalConnection
return; return;
} }
$piggyBank = $event->piggyBank()->first(); $piggyBank = $event->piggyBank()->first();
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = null;
if ($piggyBank) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) { if (is_null($repetition)) {
return; return;

View File

@@ -0,0 +1,90 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
* Class Account
*
* @package FireflyIII\Helpers\Collection
*/
class Account
{
/** @var Collection */
protected $accounts;
/** @var float */
protected $difference;
/** @var float */
protected $end;
/** @var float */
protected $start;
/**
* @return \Illuminate\Support\Collection
*/
public function getAccounts()
{
return $this->accounts;
}
/**
* @param \Illuminate\Support\Collection $accounts
*/
public function setAccounts($accounts)
{
$this->accounts = $accounts;
}
/**
* @return float
*/
public function getDifference()
{
return $this->difference;
}
/**
* @param float $difference
*/
public function setDifference($difference)
{
$this->difference = $difference;
}
/**
* @return float
*/
public function getEnd()
{
return $this->end;
}
/**
* @param float $end
*/
public function setEnd($end)
{
$this->end = $end;
}
/**
* @return float
*/
public function getStart()
{
return $this->start;
}
/**
* @param float $start
*/
public function setStart($start)
{
$this->start = $start;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Balance
*
* @package FireflyIII\Helpers\Collection
*/
class Balance
{
/** @var BalanceHeader */
protected $balanceHeader;
/** @var Collection */
protected $balanceLines;
/**
*
*/
public function __construct()
{
$this->balanceLines = new Collection;
}
/**
* @param BalanceLine $line
*/
public function addBalanceLine(BalanceLine $line)
{
$this->balanceLines->push($line);
}
/**
* @return BalanceHeader
*/
public function getBalanceHeader()
{
return $this->balanceHeader;
}
/**
* @param BalanceHeader $balanceHeader
*/
public function setBalanceHeader($balanceHeader)
{
$this->balanceHeader = $balanceHeader;
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBalanceLines()
{
return $this->balanceLines;
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
/**
* @codeCoverageIgnore
*
* Class BalanceEntry
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceEntry
{
/** @var AccountModel */
protected $account;
/** @var float */
protected $spent = 0.0;
/** @var float */
protected $left = 0.0;
/**
* @return AccountModel
*/
public function getAccount()
{
return $this->account;
}
/**
* @param AccountModel $account
*/
public function setAccount($account)
{
$this->account = $account;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceHeader
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceHeader
{
/** @var Collection */
protected $accounts;
/**
*
*/
public function __construct()
{
$this->accounts = new Collection;
}
/**
* @param AccountModel $account
*/
public function addAccount(AccountModel $account)
{
$this->accounts->push($account);
}
/**
* @return Collection
*/
public function getAccounts()
{
return $this->accounts;
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceLine
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceLine
{
const ROLE_DEFAULTROLE = 1;
const ROLE_TAGROLE = 2;
const ROLE_DIFFROLE = 3;
/** @var Collection */
protected $balanceEntries;
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
protected $role = self::ROLE_DEFAULTROLE;
/**
*
*/
public function __construct()
{
$this->balanceEntries = new Collection;
}
/**
* @param BalanceEntry $balanceEntry
*/
public function addBalanceEntry(BalanceEntry $balanceEntry)
{
$this->balanceEntries->push($balanceEntry);
}
/**
* @return string
*/
public function getTitle()
{
if ($this->getBudget() instanceof BudgetModel) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.noBudget');
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
}
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return int
*/
public function getRole()
{
return $this->role;
}
/**
* @param int $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine
* should have a "spent" value, which is the amount of money that has been spent
* on the given budget/repetition. If you subtract all those amounts from the budget/repetition's
* total amount, this is returned:
*
* @return float
*/
public function leftOfRepetition()
{
$start = $this->getRepetition() ? $this->getRepetition()->amount : 0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$start += $balanceEntry->getSpent();
}
return $start;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Collection
*/
public function getBalanceEntries()
{
return $this->balanceEntries;
}
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
* of money left in the entire BalanceLine is returned here:
*
* @return float
*/
public function sumOfLeft()
{
$sum = 0.0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum += $balanceEntry->getLeft();
}
return $sum;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
use Illuminate\Support\Collection;
/**
* Class Bill
*
* @package FireflyIII\Helpers\Collection
*/
class Bill
{
/**
* @var Collection
*/
protected $bills;
/**
*
*/
public function __construct()
{
$this->bills = new Collection;
}
/**
* @param BillLine $bill
*/
public function addBill(BillLine $bill)
{
$this->bills->push($bill);
}
/**
* @return Collection
*/
public function getBills()
{
$this->bills->sortBy(
function (BillLine $bill) {
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
$name = $bill->getBill()->name;
return $active.$name;
}
);
return $this->bills;
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
/**
* Class BillLine
*
* @package FireflyIII\Helpers\Collection
*/
class BillLine
{
/** @var bool */
protected $active;
/** @var float */
protected $amount;
/** @var BillModel */
protected $bill;
/** @var bool */
protected $hit;
/** @var float */
protected $max;
/** @var float */
protected $min;
/**
* @return float
*/
public function getAmount()
{
return $this->amount;
}
/**
* @param float $amount
*/
public function setAmount($amount)
{
$this->amount = $amount;
}
/**
* @return BillModel
*/
public function getBill()
{
return $this->bill;
}
/**
* @param BillModel $bill
*/
public function setBill($bill)
{
$this->bill = $bill;
}
/**
* @return float
*/
public function getMax()
{
return $this->max;
}
/**
* @param float $max
*/
public function setMax($max)
{
$this->max = $max;
}
/**
* @return float
*/
public function getMin()
{
return $this->min;
}
/**
* @param float $min
*/
public function setMin($min)
{
$this->min = $min;
}
/**
* @return boolean
*/
public function isActive()
{
return $this->active;
}
/**
* @param boolean $active
*/
public function setActive($active)
{
$this->active = $active;
}
/**
* @return boolean
*/
public function isHit()
{
return $this->hit;
}
/**
* @param boolean $hit
*/
public function setHit($hit)
{
$this->hit = $hit;
}
}

View File

@@ -0,0 +1,149 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Budget
*
* @package FireflyIII\Helpers\Collection
*/
class Budget
{
/** @var Collection */
protected $budgetLines;
/** @var float */
protected $budgeted = 0;
/** @var float */
protected $left = 0;
/** @var float */
protected $overspent = 0;
/** @var float */
protected $spent = 0;
/**
*
*/
public function __construct()
{
$this->budgetLines = new Collection;
}
/**
* @param BudgetLine $budgetLine
*/
public function addBudgetLine(BudgetLine $budgetLine)
{
$this->budgetLines->push($budgetLine);
}
/**
* @param float $add
*/
public function addBudgeted($add)
{
$this->budgeted += floatval($add);
}
/**
* @param float $add
*/
public function addLeft($add)
{
$this->left += floatval($add);
}
/**
* @param float $add
*/
public function addOverspent($add)
{
$this->overspent += floatval($add);
}
/**
* @param float $add
*/
public function addSpent($add)
{
$this->spent += floatval($add);
}
/**
* @return float
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param float $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param float $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = $overspent;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBudgetLines()
{
return $this->budgetLines;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
/**
* @codeCoverageIgnore
*
* Class BudgetLine
*
* @package FireflyIII\Helpers\Collection
*/
class BudgetLine
{
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
/** @var float */
protected $budgeted = 0;
/** @var float */
protected $left = 0;
/** @var float */
protected $overspent = 0;
/** @var float */
protected $spent = 0;
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return float
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param float $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param float $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = $overspent;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 13:09
*/
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Category as CategoryModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Category
*
* @package FireflyIII\Helpers\Collection
*/
class Category
{
/** @var Collection */
protected $categories;
/** @var float */
protected $total = 0;
/**
*
*/
public function __construct()
{
$this->categories = new Collection;
}
/**
* @param CategoryModel $category
*/
public function addCategory(CategoryModel $category)
{
if ($category->spent > 0) {
$this->categories->push($category);
}
}
/**
* @param float $add
*/
public function addTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getCategories()
{
$this->categories->sortByDesc(
function (CategoryModel $category) {
return $category->spent;
}
);
return $this->categories;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Expense
*
* @package FireflyIII\Helpers\Collection
*/
class Expense
{
/** @var Collection */
protected $expenses;
/** @var float */
protected $total;
/**
*
*/
public function __construct()
{
$this->expenses = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateExpense(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->expenses->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = floatval($entry->queryAmount);
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->expenses->put($accountId, $newObject);
} else {
$existing = $this->expenses->get($accountId);
$existing->amount += floatval($entry->queryAmount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getExpenses()
{
$this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}
);
return $this->expenses;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Income
*
* @package FireflyIII\Helpers\Collection
*/
class Income
{
/** @var Collection */
protected $incomes;
/** @var float */
protected $total;
/**
*
*/
public function __construct()
{
$this->incomes = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateIncome(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->incomes->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = floatval($entry->queryAmount);
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->incomes->put($accountId, $newObject);
} else {
$existing = $this->incomes->get($accountId);
$existing->amount += floatval($entry->queryAmount);
$existing->count++;
$this->incomes->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getIncomes()
{
$this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->incomes;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -18,6 +18,7 @@ class Help implements HelpInterface
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $key * @param $key
* *
* @return string * @return string
@@ -29,6 +30,7 @@ class Help implements HelpInterface
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return array * @return array
@@ -57,6 +59,7 @@ class Help implements HelpInterface
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return bool * @return bool
@@ -68,6 +71,7 @@ class Help implements HelpInterface
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $route * @param $route
* @param array $content * @param array $content
* *
@@ -81,6 +85,7 @@ class Help implements HelpInterface
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return bool * @return bool

View File

@@ -63,6 +63,30 @@ class ReminderHelper implements ReminderHelperInterface
} }
} }
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date)
{
$ranges = $this->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($date < $range['end'] && $date > $range['start']) {
// create a reminder here!
$this->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
}
/** /**
* This routine will return an array consisting of two dates which indicate the start * This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has * and end date for each reminder that this piggy bank will have, if the piggy bank has

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Reminders;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
/** /**
@@ -49,4 +50,15 @@ interface ReminderHelperInterface
* @return Reminder * @return Reminder
*/ */
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end); public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date);
} }

View File

@@ -3,12 +3,23 @@
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use App; use App;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause; use FireflyIII\Models\Bill;
use Illuminate\Support\Collection; use FireflyIII\Models\Budget as BudgetModel;
use Steam; use FireflyIII\Models\LimitRepetition;
/** /**
* Class ReportHelper * Class ReportHelper
@@ -18,47 +29,337 @@ use Steam;
class ReportHelper implements ReportHelperInterface class ReportHelper implements ReportHelperInterface
{ {
/** @var ReportQueryInterface */
protected $query;
/** /**
* This method gets some kind of list for a monthly overview. * @param ReportQueryInterface $query
*
*/
public function __construct(ReportQueryInterface $query)
{
$this->query = $query;
}
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
* *
* @param Carbon $date * @param Carbon $date
* @param bool $showSharedReports * @param Carbon $end
* @param $shared
* *
* @return Collection * @return Account
*/ */
public function getBudgetsForMonth(Carbon $date, $showSharedReports = false) public function getAccountReport(Carbon $date, Carbon $end, $shared)
{ {
/** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */
$query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface');
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as queryAmount']);
$budgets = Steam::makeArray($set); $accounts = $this->query->getAllAccounts($date, $end, $shared);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports); $start = 0;
$amounts = Steam::makeArray($amountSet); $end = 0;
$budgets = Steam::mergeArrays($budgets, $amounts); $diff = 0;
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['queryAmount'] = isset($budgets[0]['queryAmount']) ? $budgets[0]['queryAmount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared asset accounts, which are without a budget by default: // summarize:
// which is only relevant when shared asset accounts are hidden. foreach ($accounts as $account) {
if ($showSharedReports === false) { $start += $account->startBalance;
$transfers = $query->sharedExpenses($start, $end)->sum('queryAmount'); $end += $account->endBalance;
$budgets[0]['spent'] += floatval($transfers) * -1; $diff += ($account->endBalance - $account->startBalance);
} }
return $budgets; $object = new AccountCollection;
$object->setStart($start);
$object->setEnd($end);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
/**
*
* The balance report contains a Balance object which in turn contains:
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
*
* The BalanceEntry object holds:
* - The same budget (again)
* - A user asset account as mentioned in the BalanceHeader
* - The amount of money spent on the budget by the user asset account
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
{
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$balance = new Balance;
// build a balance header:
$header = new BalanceHeader;
$accounts = $this->query->getAllAccounts($start, $end, $shared);
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
/** @var BudgetModel $budget */
foreach ($budgets as $budget) {
$line = new BalanceLine;
$line->setBudget($budget);
// get budget amount for current period:
$rep = $repository->getCurrentRepetition($budget, $start);
$line->setRepetition($rep);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
// get spent:
$spent = $this->query->spentInBudget($account, $budget, $start, $end); // I think shared is irrelevant.
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
}
// add line to balance:
$balance->addBalanceLine($line);
}
// then a new line for without budget.
// and one for the tags:
$empty = new BalanceLine;
$tags = new BalanceLine;
$diffLine = new BalanceLine;
$tags->setRole(BalanceLine::ROLE_TAGROLE);
$diffLine->setRole(BalanceLine::ROLE_DIFFROLE);
foreach ($accounts as $account) {
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
$diff = $spent + $left;
// budget
$budgetEntry = new BalanceEntry;
$budgetEntry->setAccount($account);
$budgetEntry->setSpent($spent);
$empty->addBalanceEntry($budgetEntry);
// balanced by tags
$tagEntry = new BalanceEntry;
$tagEntry->setAccount($account);
$tagEntry->setLeft($left);
$tags->addBalanceEntry($tagEntry);
// difference:
$diffEntry = new BalanceEntry;
$diffEntry->setAccount($account);
$diffEntry->setSpent($diff);
$diffLine->addBalanceEntry($diffEntry);
}
$balance->addBalanceLine($empty);
$balance->addBalanceLine($tags);
$balance->addBalanceLine($diffLine);
$balance->setBalanceHeader($header);
return $balance;
}
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end, $shared)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$collection = new BillCollection;
/** @var Bill $bill */
foreach ($bills as $bill) {
$billLine = new BillLine;
$billLine->setBill($bill);
$billLine->setActive(intval($bill->active) == 1);
$billLine->setMin(floatval($bill->amount_min));
$billLine->setMax(floatval($bill->amount_max));
// is hit in period?
$set = $repository->getJournalsInRange($bill, $start, $end);
if ($set->count() == 0) {
$billLine->setHit(false);
} else {
$billLine->setHit(true);
$amount = 0;
foreach ($set as $entry) {
$amount += $entry->amount;
}
$billLine->setAmount($amount);
}
$collection->addBill($billLine);
}
return $collection;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared)
{
$object = new BudgetCollection;
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
foreach ($set as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->spentInPeriod($budget, $start, $end, $shared);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->spentInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriod($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);
}
return $object;
}
/**
* Get a full report on the users expenses during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared)
{
$object = new Expense;
$set = $this->query->expenseInPeriod($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->queryAmount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared)
{
$object = new Income;
$set = $this->query->incomeInPeriod($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->queryAmount);
$object->addOrCreateIncome($entry);
}
return $object;
} }
/** /**
@@ -68,13 +369,14 @@ class ReportHelper implements ReportHelperInterface
*/ */
public function listOfMonths(Carbon $date) public function listOfMonths(Carbon $date)
{ {
$start = clone $date; $start = clone $date;
$end = Carbon::now(); $end = Carbon::now();
$months = []; $months = [];
while ($start <= $end) { while ($start <= $end) {
$year = $start->year; $year = $start->year;
$months[$year][] = [ $months[$year][] = [
'formatted' => $start->format('F Y'), 'formatted' => $start->formatLocalized('%B %Y'),
'month' => $start->month, 'month' => $start->month,
'year' => $year, 'year' => $year,
]; ];
@@ -83,77 +385,4 @@ class ReportHelper implements ReportHelperInterface
return $months; return $months;
} }
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$years = [];
while ($start <= $end) {
$years[] = $start->year;
$start->addYear();
}
$years[] = Carbon::now()->year;
// force the current year.
$years = array_unique($years);
return $years;
}
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
if ($showSharedReports === false) {
$sharedCollection = Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedAsset'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
}
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*'])
->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$startBalance = Steam::balance($account, $start);
$endBalance = Steam::balance($account, $end);
$report[] = [
'start' => $startBalance,
'end' => $endBalance,
'hide' => ($startBalance == 0 && $endBalance == 0),
'account' => $account,
'shared' => $account->accountRole == 'sharedAsset'
];
}
return $report;
}
} }

View File

@@ -3,7 +3,12 @@
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Collection; use FireflyIII\Helpers\Collection\Account;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
/** /**
* Interface ReportHelperInterface * Interface ReportHelperInterface
@@ -14,13 +19,77 @@ interface ReportHelperInterface
{ {
/** /**
* This method gets some kind of list for a monthly overview. * This method generates a full report for the given period on all
* the users asset and cash accounts.
* *
* @param Carbon $date * @param Carbon $date
* @param Carbon $end
* @param boolean $shared
* *
* @return Collection * @return Account
*/ */
public function getBudgetsForMonth(Carbon $date); public function getAccountReport(Carbon $date, Carbon $end, $shared);
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Account
*/
public function getBillReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared);
/**
* Get a full report on the users expenses during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared);
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared);
/** /**
* @param Carbon $date * @param Carbon $date
@@ -29,18 +98,4 @@ interface ReportHelperInterface
*/ */
public function listOfMonths(Carbon $date); public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false);
} }

View File

@@ -7,6 +7,7 @@ use Carbon\Carbon;
use Crypt; use Crypt;
use DB; use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -22,100 +23,57 @@ use Steam;
class ReportQuery implements ReportQueryInterface class ReportQuery implements ReportQueryInterface
{ {
/** /**
* This query retrieves a list of accounts that are active and not shared. * This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
* *
* @param bool $showSharedReports * @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* *
* @return Collection * @return Collection
*
*/ */
public function accountList($showSharedReports = false) public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = Auth::user()->accounts(); $query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) { if ($includeShared === false) {
$query->where(
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)->where(
function (Builder $query) { function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"'); $query->where(
$query->orWhereNull('account_meta.data'); function (Builder $q) { // only get withdrawals not from a shared account
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) { // and transfers from a shared account.
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
} }
); );
} else {
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
} }
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') $query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date');
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->orderBy('accounts.name', 'ASC');
return $query->get(['accounts.*']); // get everything, decrypt and return
} $data = $query->get(['transaction_journals.id', 'transaction_journals.description', 'transaction_journals.encrypted', 'transaction_types.type',
DB::Raw('SUM(`t_from`.`amount`) as `queryAmount`'),
'transaction_journals.date', 't_to.account_id as account_id', 'ac_to.name as name', 'ac_to.encrypted as account_encrypted']);
/** $data->each(
* This method will get a list of all expenses in a certain time period that have no budget function (Model $object) {
* and are balanced by a transfer to make up for it. $object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
* }
* @param Account $account );
* @param Carbon $start $data->sortByDesc('queryAmount');
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal:: return $data;
leftJoin('transaction_group_transaction_journal', 'transaction_group_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transaction_group_transaction_journal as otherFromGroup', function (JoinClause $join) {
$join->on('otherFromGroup.transaction_group_id', '=', 'transaction_group_transaction_journal.transaction_group_id')
->on('otherFromGroup.transaction_journal_id', '!=', 'transaction_journals.id');
}
)
->leftJoin('transaction_journals as otherJournals', 'otherJournals.id', '=', 'otherFromGroup.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'otherJournals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get(
[
'transaction_journals.*',
'transactions.amount as queryAmount'
]
);
return $set;
}
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end)
{
return floatval($this->balancedTransactionsList($account, $start, $end)->sum('queryAmount'));
} }
/** /**
@@ -123,15 +81,15 @@ class ReportQuery implements ReportQueryInterface
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false) public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC') $query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account']); ->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
if ($showSharedReports === false) { if ($includeShared === false) {
$query->leftJoin( $query->leftJoin(
'account_meta', function (JoinClause $join) { 'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
@@ -167,39 +125,6 @@ class ReportQuery implements ReportQueryInterface
return $set; return $set;
} }
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `queryAmount`')]);
}
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.name', 'transactions.amount as queryAmount', 'transaction_journals.*']);
}
/** /**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account * This method returns all "income" journals in a certain period, which are both transfers from a shared account
@@ -208,14 +133,14 @@ class ReportQuery implements ReportQueryInterface
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false) public function incomeInPeriod(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = $this->queryJournalsWithTransactions($start, $end); $query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) { if ($includeShared === false) {
// only get deposits not to a shared account // only get deposits not to a shared account
// and transfers to a shared account. // and transfers to a shared account.
$query->where( $query->where(
@@ -259,318 +184,60 @@ class ReportQuery implements ReportQueryInterface
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name; $object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
} }
); );
$data->sortByDesc('queryAmount');
return $data; return $data;
} }
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/** /**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('queryAmount');
$data = $query->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `queryAmount`')]);
// decrypt data:
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts
// and all transfers to a shared account
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('queryAmount', 'DESC');
$data = $query->get(['t_to.account_id as id', 'ac_to.name as name', 'ac_to.encrypted', DB::Raw('SUM(t_to.amount) as `queryAmount`')]);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account
// or where its a transfer and its from a shared account (both count as incomes)
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any deposit goes:
$query->where('transaction_types.type', 'Deposit');
}
$query->groupBy('t_from.account_id')->orderBy('queryAmount');
$data = $query->get(
['t_from.account_id as account_id', 'ac_from.name as name', 'ac_from.encrypted as encrypted', DB::Raw('SUM(t_from.amount) as `queryAmount`')]
);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount as queryAmount']
);
}
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('categories.name')
->get(
[
'categories.id',
'categories.name as name',
DB::Raw('SUM(`transactions`.`amount`) * -1 AS `queryAmount`')
]
);
}
/**
*
* This query will get all transaction journals and budget information for a specified account
* in a certain date range, where the transaction journal does not have a budget.
* There is no get() specified, this is up to the method itself.
*
* @param Account $account * @param Account $account
* @param Budget $budget
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Builder * @return float
*/ */
protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end) public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end)
{ {
return TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') return floatval(
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id') Auth::user()->transactionjournals()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin( ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
'transactions', function (JoinClause $join) { ->transactionTypes(['Withdrawal'])
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); ->where('transactions.amount', '<', 0)
} ->where('transactions.account_id', $account->id)
) ->before($end)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') ->after($start)
->before($end) ->where('budget_transaction_journal.budget_id', $budget->id)
->after($start) ->sum('transactions.amount')
->where('accounts.id', $account->id) );
->where('transaction_journals.user_id', Auth::user()->id) }
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id') /**
->orderBy('budgets.name', 'ASC'); * @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return float
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false)
{
return floatval(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.amount', '<', 0)
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->whereNull('budget_transaction_journal.budget_id')
->sum('transactions.amount')
);
} }
/** /**
@@ -609,5 +276,4 @@ class ReportQuery implements ReportQueryInterface
return $query; return $query;
} }
} }

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -14,72 +15,17 @@ use Illuminate\Support\Collection;
interface ReportQueryInterface interface ReportQueryInterface
{ {
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @param bool $showSharedReports
*
* @return Collection
*/
public function accountList($showSharedReports = false);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end);
/** /**
* Get a users accounts combined with various meta-data related to the start and end date. * Get a users accounts combined with various meta-data related to the start and end date.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false); public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end);
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end);
/** /**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account * This method returns all "income" journals in a certain period, which are both transfers from a shared account
@@ -88,79 +34,47 @@ interface ReportQueryInterface
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*
*/ */
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false); public function incomeInPeriod(Carbon $start, Carbon $end, $includeShared = false);
/** /**
* Gets a list of expenses grouped by the budget they were filed under. * This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*
*/ */
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false); public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = false);
/** /**
* Gets a list of categories and the expenses therein, grouped by the relevant category. * @param Account $account
* This result excludes transfers to shared accounts which are expenses, technically. * @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* *
* @param Carbon $start * @return float
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/ */
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false); public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end);
/** /**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account. * @param Account $account
* This result excludes transfers to shared accounts which are expenses, technically. * @param Carbon $start
* @param Carbon $end
* @param bool $shared
* *
* So now it will include them! * @return float
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/ */
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false); public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end);
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
} }

View File

@@ -28,7 +28,7 @@ class AccountController extends Controller
{ {
parent::__construct(); parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card'); View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts'); View::share('title', trans('firefly.accounts'));
} }
/** /**
@@ -137,7 +137,7 @@ class AccountController extends Controller
*/ */
public function index(AccountRepositoryInterface $repository, $what) public function index(AccountRepositoryInterface $repository, $what)
{ {
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what); $subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what); $types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = $repository->getAccounts($types); $accounts = $repository->getAccounts($types);

View File

@@ -38,6 +38,7 @@ class AuthController extends Controller
* *
* @param \Illuminate\Contracts\Auth\Guard $auth * @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\Registrar $registrar * @param \Illuminate\Contracts\Auth\Registrar $registrar
*
* @codeCoverageIgnore * @codeCoverageIgnore
* *
*/ */

View File

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
/** /**
* Class PasswordController * Class PasswordController
*
* @codeCoverageIgnore * @codeCoverageIgnore
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth
*/ */

View File

@@ -29,54 +29,10 @@ class BillController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Bills'); View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o'); View::share('mainTitleIcon', 'fa-calendar-o');
} }
/**
* @param AccountRepositoryInterface $repository
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(AccountRepositoryInterface $repository, Bill $bill)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$accounts = $repository->getAccounts(Config::get('firefly.accountTypesByIdentifier.expense'));
foreach ($matches as $match) {
$match = strtolower($match);
// find expense account for each word if not found already:
if (is_null($expense)) {
/** @var Account $account */
foreach ($accounts as $account) {
$name = strtolower($account->name);
if (!(strpos($name, $match) === false)) {
$expense = $account;
break;
}
}
}
if (is_null($expense)) {
$description[] = $match;
}
}
$parameters = [
'description' => ucfirst(join(' ', $description)),
'expense_account' => is_null($expense) ? '' : $expense->name,
'amount' => round(($bill->amount_min + $bill->amount_max), 2),
];
Session::put('preFilled', $parameters);
return Redirect::to(route('transactions.create', 'withdrawal'));
}
/** /**
* @return $this * @return $this
*/ */

View File

@@ -29,7 +29,7 @@ class BudgetController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Budgets'); View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks'); View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true); View::share('hideBudgets', true);
} }
@@ -137,7 +137,8 @@ class BudgetController extends Controller
$budgets->each( $budgets->each(
function (Budget $budget) use ($repository) { function (Budget $budget) use ($repository) {
$date = Session::get('start', Carbon::now()->startOfMonth()); $date = Session::get('start', Carbon::now()->startOfMonth());
$budget->spent = $repository->spentInMonth($budget, $date); $end = Session::get('end', Carbon::now()->endOfMonth());
$budget->spent = $repository->spentInPeriod($budget, $date, $end);
$budget->currentRep = $repository->getCurrentRepetition($budget, $date); $budget->currentRep = $repository->getCurrentRepetition($budget, $date);
} }
); );

View File

@@ -26,7 +26,7 @@ class CategoryController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Categories'); View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart'); View::share('mainTitleIcon', 'fa-bar-chart');
} }

View File

@@ -0,0 +1,150 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class AccountController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class AccountController extends Controller
{
/**
* Shows the balances for all the user's accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, AccountRepositoryInterface $repository, $year, $month, $shared = false)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
// remove the shared accounts from the collection:
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
}
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows the balances for all the user's frontpage accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows an account's balance for a single month.
*
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, Account $account)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
use Session;
use Steam;
/**
* Class BillController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BillController extends Controller
{
/**
* Shows the overview for a bill. The min/max amount and matched journals.
*
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.maxAmount'), 'number');
$chart->addColumn(trans('firefly.minAmount'), 'number');
$chart->addColumn(trans('firefly.billEntry'), 'number');
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows all bills and whether or not theyve been paid this month (pie chart).
*
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn(trans('firefly.name'), 'string');
$chart->addColumn(trans('firefly.amount'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
}
}
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
use Session;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BudgetController extends Controller
{
/**
* Shows the amount left in a specific budget limit.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimit(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn(trans('firefly.day'), 'date');
$chart->addColumn(trans('firefly.left'), 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDay($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows a budget list with spent/left/overspent.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.budget'), 'string');
$chart->addColumn(trans('firefly.left'), 'number');
$chart->addColumn(trans('firefly.spent'), 'number');
$chart->addColumn(trans('firefly.overspent'), 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->spentInPeriod($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriod($budget, $repetition->startdate, $repetition->enddate, true);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->formatLocalized($this->monthAndDayFormat) . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show a yearly overview for a budget.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, BudgetRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$budgets = $repository->getBudgets();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->spentInPeriod($budget, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Navigation;
use Preferences;
use Response;
use Session;
/**
* Class CategoryController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class CategoryController extends Controller
{
/**
* Show an overview for a category for all time, per month/week/year.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
// jump to start of week / month / year / etc
$start = Navigation::startOfPeriod($start, $range);
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show this month's category overview.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.category'), 'string');
$chart->addColumn(trans('firefly.spent'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpenses($start, $end);
foreach ($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? trans('firefly.noCategory') : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function month(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
while ($start <= $end) {
$spent = $repository->spentOnDaySum($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* This chart will only show expenses.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($categories as $category) {
$chart->addColumn($category->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($categories as $category) {
$spent = $repository->spentInPeriod($category, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 09:36
*/
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
/**
* Class PiggyBankController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class PiggyBankController extends Controller
{
/**
* Shows the piggy bank history.
*
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function history(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.balance'), 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Controllers\Controller;
use Grumpydictator\Gchart\GChart;
use Response;
/**
* Class ReportController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class ReportController extends Controller
{
/**
* Summarizes all income and expenses, per month, for a given year.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOut(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
// get start and end of year
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$chart->addColumn(trans('firefly.month'), 'date');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = floatval($query->incomeInPeriod($start, $month, $shared)->sum('queryAmount'));
$expenseSum = floatval($query->expenseInPeriod($start, $month, $shared)->sum('queryAmount')) * -1;
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOutSummarized(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$income = 0;
$expense = 0;
$count = 0;
$chart->addColumn(trans('firefly.summary'), 'string');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$income += floatval($query->incomeInPeriod($start, $month, $shared)->sum('queryAmount'));
$expense += floatval($query->expenseInPeriod($start, $month, $shared)->sum('queryAmount')) * -1;
$count++;
$start->addMonth();
}
// add total + average:
$chart->addRow(trans('firefly.sum'), $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -1,8 +1,11 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use Illuminate\Foundation\Bus\DispatchesCommands; use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
use Preferences;
use View; use View;
/** /**
@@ -15,6 +18,11 @@ abstract class Controller extends BaseController
use DispatchesCommands, ValidatesRequests; use DispatchesCommands, ValidatesRequests;
/** @var string */
protected $monthAndDayFormat;
/** @var string */
protected $monthFormat;
/** /**
* *
*/ */
@@ -24,5 +32,16 @@ abstract class Controller extends BaseController
View::share('hideCategories', false); View::share('hideCategories', false);
View::share('hideBills', false); View::share('hideBills', false);
View::share('hideTags', false); View::share('hideTags', false);
if (Auth::check()) {
$pref = Preferences::get('language', 'en');
$lang = $pref->data;
$this->monthFormat = Config::get('firefly.month.' . $lang);
$this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang);
View::share('monthFormat', $this->monthFormat);
View::share('monthAndDayFormat', $this->monthAndDayFormat);
View::share('language', $lang);
}
} }
} }

View File

@@ -28,7 +28,7 @@ class CurrencyController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Currencies'); View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd'); View::share('mainTitleIcon', 'fa-usd');
} }

View File

@@ -1,590 +0,0 @@
<?php namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class GoogleChartController
*
* @package FireflyIII\Http\Controllers
*/
class GoogleChartController extends Controller
{
/**
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function accountBalanceChart(GChart $chart, Account $account)
{
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allAccountsBalanceChart(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn('Day of the month', 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, $year)
{
$budgets = $repository->getBudgets();
$chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $repository->spentInMonth($budget, $start);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn('Budget', 'string');
$chart->addColumn('Left', 'number');
$chart->addColumn('Spent', 'number');
$chart->addColumn('Overspent', 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $start, $end);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $repetition->startdate, $repetition->enddate);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->format('j M Y') . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push(['(no budget)', 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allCategoriesHomeChart(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpenses($start, $end);
foreach ($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billOverview(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Recorded bill entry', 'number');
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billsOverview(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
}
}
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow('Unpaid: ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow('Paid: ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimitSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDay($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param int $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, $year = 0)
{
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
if ($year == 0) {
$start = $repository->getFirstBudgetLimitDate($budget);
$end = $repository->getLastBudgetLimitDate($budget);
} else {
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
}
while ($start <= $end) {
$spent = $repository->spentInMonth($budget, $start);
$budgeted = $repository->getLimitAmountOnDate($budget, $start);
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryOverviewChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
/** @var Preference $range */
$range = Preferences::get('viewRange', '1M');
// jump to start of week / month / year / etc (TODO).
$start = Navigation::startOfPeriod($start, $range->data);
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range->data);
$spent = $repository->spentInPeriodSum($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range->data, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryPeriodChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = Session::get('end', Carbon::now()->endOfMonth());
while ($start <= $end) {
$spent = $repository->spentOnDaySum($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function piggyBankHistory(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExp(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
// get report query interface.
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income && total expenses:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExpSum(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$income = 0;
$expense = 0;
$count = 0;
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
// total expenses:
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$income += $incomeSum;
$expense += $expenseSum;
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -53,7 +53,7 @@ class HomeController extends Controller
$types = Config::get('firefly.accountTypesByIdentifier.asset'); $types = Config::get('firefly.accountTypesByIdentifier.asset');
$count = $repository->countAccounts($types); $count = $repository->countAccounts($types);
$title = 'Firefly'; $title = 'Firefly';
$subTitle = 'What\'s playing?'; $subTitle = trans('firefly.welcomeBack');
$mainTitleIcon = 'fa-fire'; $mainTitleIcon = 'fa-fire';
$transactions = []; $transactions = [];
$frontPage = Preferences::get('frontPageAccounts', []); $frontPage = Preferences::get('frontPageAccounts', []);
@@ -63,13 +63,11 @@ class HomeController extends Controller
$savings = $repository->getSavingsAccounts(); $savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts(); $piggyBankAccounts = $repository->getPiggyBankAccounts();
$savingsTotal = 0; $savingsTotal = 0;
foreach ($savings as $savingAccount) { foreach ($savings as $savingAccount) {
$savingsTotal += Steam::balance($savingAccount, $end); $savingsTotal += Steam::balance($savingAccount, $end);
} }
// check if all books are correct.
$sum = $repository->sumOfEverything(); $sum = $repository->sumOfEverything();
if ($sum != 0) { if ($sum != 0) {
Session::flash( Session::flash(

View File

@@ -12,7 +12,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences;
use Response; use Response;
use Session; use Session;
use Steam; use Steam;
@@ -39,19 +38,14 @@ class JsonController extends Controller
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = 0; $amount = 0;
// these two functions are the same as the chart TODO // these two functions are the same as the chart
$bills = $repository->getActiveBills(); $bills = $repository->getActiveBills();
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end); $amount += $repository->billPaymentsInRange($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$amount += $repository->getJournalsInRange($bill, $range['start'], $range['end'])->sum('amount');
}
} }
unset($ranges, $bill, $range, $bills); unset($bill, $bills);
/** /**
* Find credit card accounts and possibly unpaid credit card bills. * Find credit card accounts and possibly unpaid credit card bills.
@@ -60,7 +54,7 @@ class JsonController extends Controller
// if the balance is not zero, the monthly payment is still underway. // if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */ /** @var Account $creditCard */
foreach ($creditCards as $creditCard) { foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true); $balance = Steam::balance($creditCard, $end, true);
if ($balance == 0) { if ($balance == 0) {
// find a transfer TO the credit card which should account for // find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used. // anything paid. If not, the CC is not yet used.
@@ -100,7 +94,7 @@ class JsonController extends Controller
$creditCards = $accountRepository->getCreditCards(); $creditCards = $accountRepository->getCreditCards();
foreach ($creditCards as $creditCard) { foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true); $balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate')); $date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) { if ($balance < 0) {
// unpaid! create a fake bill that matches the amount. // unpaid! create a fake bill that matches the amount.
@@ -128,7 +122,7 @@ class JsonController extends Controller
{ {
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->incomeByPeriod($start, $end, true)->sum('queryAmount'); $amount = $reportQuery->incomeInPeriod($start, $end, true)->sum('queryAmount');
return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
@@ -142,7 +136,7 @@ class JsonController extends Controller
{ {
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->journalsByExpenseAccount($start, $end, true)->sum('queryAmount'); $amount = $reportQuery->expenseInPeriod($start, $end, true)->sum('queryAmount') * -1;
return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
@@ -202,30 +196,6 @@ class JsonController extends Controller
} }
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function setSharedReports()
{
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$new = !$pref->data;
Preferences::set('showSharedReports', $new);
return Response::json(['value' => $new]);
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/** /**
* Returns a JSON list of all beneficiaries. * Returns a JSON list of all beneficiaries.
* *

View File

@@ -31,7 +31,7 @@ class PiggyBankController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Piggy banks'); View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc'); View::share('mainTitleIcon', 'fa-sort-amount-asc');
} }
@@ -45,7 +45,8 @@ class PiggyBankController extends Controller
*/ */
public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank) public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
$leftOnAccount = $repository->leftOnAccount($piggyBank->account); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave); $maxAmount = min($leftOnAccount, $leftToSave);
@@ -157,6 +158,7 @@ class PiggyBankController extends Controller
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = $piggyRepository->getPiggyBanks(); $piggyBanks = $piggyRepository->getPiggyBanks();
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
@@ -172,8 +174,8 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) { if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [ $accounts[$account->id] = [
'name' => $account->name, 'name' => $account->name,
'balance' => Steam::balance($account, null, true), 'balance' => Steam::balance($account, $end, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account), 'leftForPiggyBanks' => $repository->leftOnAccount($account, $end),
'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount), 'sumOfTargets' => floatval($piggyBank->targetamount),
'leftToSave' => $piggyBank->leftToSave 'leftToSave' => $piggyBank->leftToSave
@@ -215,7 +217,8 @@ class PiggyBankController extends Controller
public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank) public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank)
{ {
$amount = round(floatval(Input::get('amount')), 2); $amount = round(floatval(Input::get('amount')), 2);
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2); $maxAmount = round(min($leftOnAccount, $leftToSave), 2);

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Config;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
@@ -21,7 +22,7 @@ class PreferencesController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Preferences'); View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear'); View::share('mainTitleIcon', 'fa-gear');
} }
@@ -37,9 +38,11 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data; $viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []); $frontPageAccounts = Preferences::get('frontPageAccounts', []);
$budgetMax = Preferences::get('budgetMaximum', 1000); $budgetMax = Preferences::get('budgetMaximum', 1000);
$languagePref = Preferences::get('language', 'en');
$language = $languagePref->data;
$budgetMaximum = $budgetMax->data; $budgetMaximum = $budgetMax->data;
return view('preferences.index', compact('budgetMaximum', 'accounts', 'frontPageAccounts', 'viewRange')); return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange'));
} }
/** /**
@@ -49,10 +52,12 @@ class PreferencesController extends Controller
{ {
// front page accounts // front page accounts
$frontPageAccounts = []; $frontPageAccounts = [];
foreach (Input::get('frontPageAccounts') as $id) { if (is_array(Input::get('frontPageAccounts'))) {
$frontPageAccounts[] = intval($id); foreach (Input::get('frontPageAccounts') as $id) {
$frontPageAccounts[] = intval($id);
}
Preferences::set('frontPageAccounts', $frontPageAccounts);
} }
Preferences::set('frontPageAccounts', $frontPageAccounts);
// view range: // view range:
Preferences::set('viewRange', Input::get('viewRange')); Preferences::set('viewRange', Input::get('viewRange'));
@@ -65,6 +70,12 @@ class PreferencesController extends Controller
$budgetMaximum = intval(Input::get('budgetMaximum')); $budgetMaximum = intval(Input::get('budgetMaximum'));
Preferences::set('budgetMaximum', $budgetMaximum); Preferences::set('budgetMaximum', $budgetMaximum);
// language:
$lang = Input::get('language');
if (in_array($lang, array_keys(Config::get('firefly.lang')))) {
Preferences::set('language', $lang);
}
Session::flash('success', 'Preferences saved!'); Session::flash('success', 'Preferences saved!');

View File

@@ -2,13 +2,13 @@
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Preferences; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Session; use Session;
use Steam;
use View; use View;
/** /**
@@ -21,311 +21,132 @@ class ReportController extends Controller
/** @var ReportHelperInterface */ /** @var ReportHelperInterface */
protected $helper; protected $helper;
/** @var ReportQueryInterface */
protected $query;
/** /**
* @param ReportHelperInterface $helper * @param ReportHelperInterface $helper
* @param ReportQueryInterface $query
*/ */
public function __construct(ReportHelperInterface $helper, ReportQueryInterface $query) public function __construct(ReportHelperInterface $helper)
{ {
$this->query = $query; parent::__construct();
$this->helper = $helper; $this->helper = $helper;
View::share('title', 'Reports'); View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
} }
/** /**
* @param string $year * @param AccountRepositoryInterface $repository
* @param string $month
* *
* @return \Illuminate\View\View
*/
public function budget($year = '2014', $month = '1')
{
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// should show shared reports?
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$accountAmounts = []; // array with sums of spent amounts on each account.
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports); // all accounts and some data.
foreach ($accounts as $account) {
$budgets = $this->query->getBudgetSummary($account, $start, $end);// get budget summary for this account:
$balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$accountAmounts[$account->id] = $balancedAmount;
// balance out the transactions (see transaction groups & tags) ^^
// array with budget information for each account:
$array = [];
// should always hide account
$hide = true;
// loop all budgets
/** @var \FireflyIII\Models\Budget $budget */
foreach ($budgets as $budget) {
$id = intval($budget->id);
$data = $budget->toArray();
$array[$id] = $data;
// no longer hide account if any budget has money in it.
if (floatval($data['queryAmount']) != 0) {
$hide = false;
}
$accountAmounts[$account->id] += $data['queryAmount'];
}
$account->hide = $hide;
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
}
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
return view('reports.budget', compact('subTitle', 'accountAmounts', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets'));
}
/**
* @return View * @return View
* @internal param ReportHelperInterface $helper * @internal param ReportHelperInterface $helper
*
*/ */
public function index() public function index(AccountRepositoryInterface $repository)
{ {
$start = Session::get('first'); $start = Session::get('first');
$months = $this->helper->listOfMonths($start); $months = $this->helper->listOfMonths($start);
$years = $this->helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon')); // does the user have shared accounts?
} $accounts = $repository->getAccounts(['Default account', 'Asset account']);
$hasShared = false;
/** /** @var Account $account */
* @param Account $account foreach ($accounts as $account) {
* @param string $year if ($account->getMeta('accountRole') == 'sharedAsset') {
* @param string $month $hasShared = true;
*
* @return \Illuminate\View\View
*/
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return View
* @internal param ReportQueryInterface $query
*
*/
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter(
function (TransactionJournal $journal) {
$count = $journal->transactiongroups()->where('relation', 'balance')->count();
if ($count == 0) {
return $journal;
}
return null;
} }
); }
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalNoBudget(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
return view('reports.index', compact('months', 'hasShared'));
} }
/** /**
* @param string $year * @param string $year
* @param string $month * @param string $month
* *
* @param bool $shared
*
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function month($year = '2014', $month = '1') public function month($year = '2014', $month = '1', $shared = false)
{ {
$date = new Carbon($year . '-' . $month . '-01'); $start = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y'); $subTitle = trans('firefly.reportForMonth', ['date' => $start->formatLocalized($this->monthFormat)]);
$subTitleIcon = 'fa-calendar'; $subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report. $end = clone $start;
/** @var Preference $pref */ $incomeTopLength = 8;
$pref = Preferences::get('showSharedReports', false); $expenseTopLength = 8;
$showSharedReports = $pref->data; if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForMonthShared', ['date' => $start->formatLocalized($this->monthFormat)]);
}
/**
*
* get income for month (date)
*
*/
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth(); $end->endOfMonth();
/** $accounts = $this->helper->getAccountReport($start, $end, $shared);
* Start getIncomeForMonth DONE $incomes = $this->helper->getIncomeReport($start, $end, $shared);
*/ $expenses = $this->helper->getExpenseReport($start, $end, $shared);
$income = $this->query->incomeByPeriod($start, $end, $showSharedReports); $budgets = $this->helper->getBudgetReport($start, $end, $shared);
/** $categories = $this->helper->getCategoryReport($start, $end, $shared);
* End getIncomeForMonth DONE $balance = $this->helper->getBalanceReport($start, $end, $shared);
*/ $bills = $this->helper->getBillReport($start, $end, $shared);
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
/**
* End getExpenseGroupedForMonth DONE
*/
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
/**
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
if ($showSharedReports === false) {
$result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
} else {
$merged = $categories;
}
// sort.
$sorted = Steam::sortNegativeArray($merged);
// limit to $limit:
$categories = Steam::limitArray($sorted, 10);
/**
* End getCategoriesForMonth DONE
*/
/**
* Start getAccountsForMonth
*/
$list = $this->query->accountList($showSharedReports);
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
$id = intval($account->id);
/** @noinspection PhpParamsInspection */
$accounts[$id] = [
'name' => $account->name,
'startBalance' => Steam::balance($account, $start),
'endBalance' => Steam::balance($account, $end)
];
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
}
/**
* End getAccountsForMonth
*/
return view( return view(
'reports.month', 'reports.month',
compact( compact(
'income', 'expenses', 'budgets', 'accounts', 'categories', 'start', 'shared',
'date', 'subTitle', 'displaySum', 'subTitleIcon' 'subTitle', 'subTitleIcon',
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills'
) )
); );
} }
/** /**
* @param $year * @param $year
*
* @param bool $shared
* *
* @return $this * @return $this
*/ */
public function year($year) public function year($year, $shared = false)
{ {
/** @var Preference $pref */ $start = new Carbon('01-01-' . $year);
$pref = Preferences::get('showSharedReports', false); $end = clone $start;
$showSharedReports = $pref->data; $subTitle = trans('firefly.reportForYear', ['year' => $year]);
$date = new Carbon('01-01-' . $year); $subTitleIcon = 'fa-bar-chart';
$end = clone $date; $incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForYearShared', ['year' => $year]);
}
$end->endOfYear(); $end->endOfYear();
$title = 'Reports';
$subTitle = $year; $accounts = $this->helper->getAccountReport($start, $end, $shared);
$subTitleIcon = 'fa-bar-chart'; $incomes = $this->helper->getIncomeReport($start, $end, $shared);
$mainTitleIcon = 'fa-line-chart'; $expenses = $this->helper->getExpenseReport($start, $end, $shared);
$balances = $this->helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports);
return view( return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon') 'reports.year',
compact(
'start', // the date for this report.
'shared', // is a shared report?
'accounts', // all accounts
'incomes', 'expenses', // expenses and incomes.
'subTitle', 'subTitleIcon', // subtitle and subtitle icon.
'incomeTopLength', // length of income top X
'expenseTopLength' // length of expense top X.
)
); );
} }

View File

@@ -289,9 +289,9 @@ class TagController extends Controller
public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag) public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag)
{ {
if (Input::get('setTag') == 'true') { if (Input::get('setTag') == 'true') {
$latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null; $latitude = $request->get('latitude');
$longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null; $longitude = $request->get('longitude');
$zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null; $zoomLevel = $request->get('zoomLevel');
} else { } else {
$latitude = null; $latitude = null;
$longitude = null; $longitude = null;

View File

@@ -30,7 +30,7 @@ class TransactionController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Transactions'); View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat'); View::share('mainTitleIcon', 'fa-repeat');
} }
@@ -44,17 +44,15 @@ class TransactionController extends Controller
{ {
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)'; $budgets[0] = trans('form.noBudget');
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get()); $piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)'; $piggies[0] = trans('form.noPiggybank');
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : []; $preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id']; $respondTo = ['account_id', 'account_from_id'];
$subTitle = 'Add a new ' . e($what); $subTitle = trans('form.add_new_' . $what);
foreach ($respondTo as $r) { foreach ($respondTo as $r) {
if (!is_null(Input::get($r))) { $preFilled[$r] = Input::get($r);
$preFilled[$r] = Input::get($r);
}
} }
Session::put('preFilled', $preFilled); Session::put('preFilled', $preFilled);
@@ -119,10 +117,11 @@ class TransactionController extends Controller
$what = strtolower($journal->transactiontype->type); $what = strtolower($journal->transactiontype->type);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)'; $budgets[0] = trans('form.noBudget');
$transactions = $journal->transactions()->orderBy('amount', 'DESC')->get(); $transactions = $journal->transactions()->orderBy('amount', 'DESC')->get();
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get()); $piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)'; $piggies[0] = trans('form.noPiggybank');
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$preFilled = [ $preFilled = [
'date' => $journal->date->format('Y-m-d'), 'date' => $journal->date->format('Y-m-d'),
'category' => '', 'category' => '',
@@ -182,19 +181,19 @@ class TransactionController extends Controller
case 'expenses': case 'expenses':
case 'withdrawal': case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left'; $subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses'; $subTitle = trans('firefly.expenses');
$types = ['Withdrawal']; $types = ['Withdrawal'];
break; break;
case 'revenue': case 'revenue':
case 'deposit': case 'deposit':
$subTitleIcon = 'fa-long-arrow-right'; $subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits'; $subTitle = trans('firefly.income');
$types = ['Deposit']; $types = ['Deposit'];
break; break;
case 'transfer': case 'transfer':
case 'transfers': case 'transfers':
$subTitleIcon = 'fa-exchange'; $subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers'; $subTitle = trans('firefly.transfers');
$types = ['Transfer']; $types = ['Transfer'];
break; break;
} }
@@ -249,7 +248,7 @@ class TransactionController extends Controller
$t->after = $t->before + $t->amount; $t->after = $t->before + $t->amount;
} }
); );
$subTitle = e($journal->transactiontype->type) . ' "' . e($journal->description) . '"'; $subTitle = trans('firefly.' . $journal->transactiontype->type) . ' "' . e($journal->description) . '"';
return view('transactions.show', compact('journal', 'subTitle')); return view('transactions.show', compact('journal', 'subTitle'));
} }
@@ -269,7 +268,9 @@ class TransactionController extends Controller
// rescan journal, UpdateJournalConnection // rescan journal, UpdateJournalConnection
event(new JournalSaved($journal)); event(new JournalSaved($journal));
// ConnectJournalToPiggyBank // ConnectJournalToPiggyBank
event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
}
$repository->deactivateReminder($request->get('reminder_id')); $repository->deactivateReminder($request->get('reminder_id'));

View File

@@ -1,8 +1,12 @@
<?php namespace FireflyIII\Http\Middleware; <?php namespace FireflyIII\Http\Middleware;
use App;
use Closure; use Closure;
use Config;
use FireflyIII\Models\Preference;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Preferences;
/** /**
* Class Authenticate * Class Authenticate
@@ -48,6 +52,11 @@ class Authenticate
return redirect()->guest('auth/login'); return redirect()->guest('auth/login');
} }
} }
// if logged in, set user language:
$pref = Preferences::get('language', 'en');
App::setLocale($pref->data);
setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data));
return $next($request); return $next($request);
} }

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Http\Middleware; namespace FireflyIII\Http\Middleware;
use App;
use Closure; use Closure;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
@@ -53,16 +52,15 @@ class PiggyBanks
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id') ->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->whereNull('piggy_bank_repetitions.id') ->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']); ->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
/** @var PiggyBank $partialPiggy */ /** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) { foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition; $repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy); $repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate; $repetition->startdate = $partialPiggy->startdate;
$repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate; $repetition->targetdate = $partialPiggy->targetdate;
$repetition->currentamount = 0; $repetition->currentamount = 0;
$repetition->save(); $repetition->save();
}
} }
unset($partialPiggy, $set, $repetition); unset($partialPiggy, $set, $repetition);
} }

View File

@@ -43,6 +43,7 @@ class Range
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param \Closure $theNext * @param \Closure $theNext
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return mixed * @return mixed
*/ */
@@ -63,9 +64,6 @@ class Range
Session::put('end', $end); Session::put('end', $end);
} }
if (!Session::has('first')) { if (!Session::has('first')) {
/**
* Get helper thing.
*/
/** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */ /** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface'); $repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface');
$journal = $repository->first(); $journal = $repository->first();
@@ -75,16 +73,12 @@ class Range
Session::put('first', Carbon::now()->startOfYear()); Session::put('first', Carbon::now()->startOfYear());
} }
} }
// set current / next / prev month.
$current = Carbon::now()->format('F Y'); $current = Carbon::now()->format('F Y');
$next = Carbon::now()->endOfMonth()->addDay()->format('F Y'); $next = Carbon::now()->endOfMonth()->addDay()->format('F Y');
$prev = Carbon::now()->startOfMonth()->subDay()->format('F Y'); $prev = Carbon::now()->startOfMonth()->subDay()->format('F Y');
View::share('currentMonthName', $current); View::share('currentMonthName', $current);
View::share('previousMonthName', $prev); View::share('previousMonthName', $prev);
View::share('nextMonthName', $next); View::share('nextMonthName', $next);
} }
return $theNext($request); return $theNext($request);

View File

@@ -49,33 +49,22 @@ class Reminders
if ($this->auth->check() && !$request->isXmlHttpRequest()) { if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// do reminders stuff. // do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get(); $piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;
/** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */ /** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */
$helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface'); $helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface');
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) { foreach ($piggyBanks as $piggyBank) {
$ranges = $helper->getReminderRanges($piggyBank); $helper->createReminders($piggyBank, new Carbon);
foreach ($ranges as $range) {
if ($today < $range['end'] && $today > $range['start']) {
// create a reminder here!
$helper->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
} }
// delete invalid reminders // delete invalid reminders
$set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get( // this is a construction SQLITE cannot handle :(
['reminders.id'] if (env('DB_CONNECTION') != 'sqlite') {
); Reminder::whereUserId($this->auth->user()->id)
foreach ($set as $reminder) { ->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')
$reminder->delete(); ->whereNull('piggy_banks.id')
->delete();
} }
// get and list active reminders: // get and list active reminders:
$reminders = $this->auth->user()->reminders()->today()->get(); $reminders = $this->auth->user()->reminders()->today()->get();
$reminders->each( $reminders->each(

View File

@@ -50,12 +50,11 @@ class JournalFormRequest extends Request
/** /**
* @return array * @return array
* @throws Exception * @throws Exception
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function rules() public function rules()
{ {
// can we switch on the "what"? $what = Input::get('what');
$what = Input::get('what');
$rules = [ $rules = [
'description' => 'required|min:1,max:255', 'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer', 'what' => 'required|in:withdrawal,deposit,transfer',
@@ -74,8 +73,6 @@ class JournalFormRequest extends Request
if (intval(Input::get('budget_id')) != 0) { if (intval(Input::get('budget_id')) != 0) {
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets'; $rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
} }
break; break;
case 'deposit': case 'deposit':
$rules['category'] = 'between:1,255'; $rules['category'] = 'between:1,255';
@@ -93,7 +90,5 @@ class JournalFormRequest extends Request
} }
return $rules; return $rules;
} }
} }

View File

@@ -48,6 +48,7 @@ class TagFormRequest extends Request
'date' => 'date', 'date' => 'date',
'latitude' => 'numeric|min:-90|max:90', 'latitude' => 'numeric|min:-90|max:90',
'longitude' => 'numeric|min:-90|max:90', 'longitude' => 'numeric|min:-90|max:90',
'zoomLevel' => 'numeric|min:0|max:80',
'tagMode' => 'required|in:nothing,balancingAct,advancePayment' 'tagMode' => 'required|in:nothing,balancingAct,advancePayment'
]; ];
} }

View File

@@ -20,7 +20,7 @@ Breadcrumbs::register(
'home', 'home',
function (Generator $breadcrumbs) { function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index')); $breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
} }
); );
@@ -28,17 +28,26 @@ Breadcrumbs::register(
'index', 'index',
function (Generator $breadcrumbs) { function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index')); $breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
} }
); );
//trans('breadcrumbs.')
// accounts // accounts
Breadcrumbs::register( Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) { 'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst(e($what)) . ' accounts', route('accounts.index', $what)); $breadcrumbs->push(trans('breadcrumbs.' . strtolower(e($what)) . '_accounts'), route('accounts.index', $what));
} }
); );
Breadcrumbs::register(
'accounts.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(trans('breadcrumbs.new_' . strtolower(e($what)) . '_account'), route('accounts.create', $what));
}
);
Breadcrumbs::register( Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, Account $account) { 'accounts.show', function (Generator $breadcrumbs, Account $account) {
switch ($account->accountType->type) { switch ($account->accountType->type) {
@@ -67,14 +76,15 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, Account $account) { 'accounts.delete', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Delete ' . e($account->name), route('accounts.delete', $account->id)); $breadcrumbs->push(trans('breadcrumbs.delete_account', ['name' => e($account->name)]), route('accounts.delete', $account->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, Account $account) { 'accounts.edit', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Edit ' . e($account->name), route('accounts.edit', $account->id)); $breadcrumbs->push(trans('breadcrumbs.edit_account', ['name' => e($account->name)]), route('accounts.edit', $account->id));
} }
); );
@@ -82,26 +92,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) { 'budgets.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Budgets', route('budgets.index')); $breadcrumbs->push(trans('breadcrumbs.budgets'), route('budgets.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) { 'budgets.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('budgets.index'); $breadcrumbs->parent('budgets.index');
$breadcrumbs->push('Create new budget', route('budgets.create')); $breadcrumbs->push(trans('breadcrumbs.newBudget'), route('budgets.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) { 'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget); $breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Edit ' . e($budget->name), route('budgets.edit', $budget->id)); $breadcrumbs->push(trans('breadcrumbs.edit_budget', ['name' => e($budget->name)]), route('budgets.edit', $budget->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) { 'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget); $breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Delete ' . e($budget->name), route('budgets.delete', $budget->id)); $breadcrumbs->push(trans('breadcrumbs.delete_budget', ['name' => e($budget->name)]), route('budgets.delete', $budget->id));
} }
); );
@@ -128,26 +138,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) { 'categories.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Categories', route('categories.index')); $breadcrumbs->push(trans('breadcrumbs.categories'), route('categories.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) { 'categories.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('categories.index'); $breadcrumbs->parent('categories.index');
$breadcrumbs->push('Create new category', route('categories.create')); $breadcrumbs->push(trans('breadcrumbs.newCategory'), route('categories.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) { 'categories.edit', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Edit ' . e($category->name), route('categories.edit', $category->id)); $breadcrumbs->push(trans('breadcrumbs.edit_category', ['name' => e($category->name)]), route('categories.edit', $category->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) { 'categories.delete', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Delete ' . e($category->name), route('categories.delete', $category->id)); $breadcrumbs->push(trans('breadcrumbs.delete_category', ['name' => e($category->name)]), route('categories.delete', $category->id));
} }
); );
@@ -170,20 +180,20 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'currency.index', function (Generator $breadcrumbs) { 'currency.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Currencies', route('currency.index')); $breadcrumbs->push(trans('breadcrumbs.currencies'), route('currency.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) { 'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currency.index');
$breadcrumbs->push('Edit ' . $currency->name, route('currency.edit', $currency->id)); $breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currency.edit', $currency->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) { 'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currency.index');
$breadcrumbs->push('Delete ' . $currency->name, route('currency.delete', $currency->id)); $breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currency.delete', $currency->id));
} }
); );
@@ -192,26 +202,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.index', function (Generator $breadcrumbs) { 'piggy-banks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggy-banks.index')); $breadcrumbs->push(trans('breadcrumbs.piggyBanks'), route('piggy-banks.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.create', function (Generator $breadcrumbs) { 'piggy-banks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggy-banks.index'); $breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push('Create new piggy bank', route('piggy-banks.create')); $breadcrumbs->push(trans('breadcrumbs.newPiggyBank'), route('piggy-banks.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) { 'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank); $breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('piggy-banks.edit', $piggyBank->id)); $breadcrumbs->push(trans('breadcrumbs.edit_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.edit', $piggyBank->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) { 'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank); $breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('piggy-banks.delete', $piggyBank->id)); $breadcrumbs->push(trans('breadcrumbs.delete_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.delete', $piggyBank->id));
} }
); );
@@ -227,7 +237,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) { 'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences')); $breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences'));
} }
); );
@@ -236,14 +246,14 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) { 'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile')); $breadcrumbs->push(trans('breadcrumbs.profile'), route('profile'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) { 'change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile'); $breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password')); $breadcrumbs->push(trans('breadcrumbs.changePassword'), route('change-password'));
} }
); );
@@ -252,26 +262,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'bills.index', function (Generator $breadcrumbs) { 'bills.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Bills', route('bills.index')); $breadcrumbs->push(trans('breadcrumbs.bills'), route('bills.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.create', function (Generator $breadcrumbs) { 'bills.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('bills.index'); $breadcrumbs->parent('bills.index');
$breadcrumbs->push('Create new bill', route('bills.create')); $breadcrumbs->push(trans('breadcrumbs.newBill'), route('bills.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.edit', function (Generator $breadcrumbs, Bill $bill) { 'bills.edit', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill); $breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Edit ' . e($bill->name), route('bills.edit', $bill->id)); $breadcrumbs->push(trans('breadcrumbs.edit_bill', ['name' => e($bill->name)]), route('bills.edit', $bill->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.delete', function (Generator $breadcrumbs, Bill $bill) { 'bills.delete', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill); $breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Delete ' . e($bill->name), route('bills.delete', $bill->id)); $breadcrumbs->push(trans('breadcrumbs.delete_bill', ['name' => e($bill->name)]), route('bills.delete', $bill->id));
} }
); );
@@ -287,7 +297,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reminders.index', function (Generator $breadcrumbs) { 'reminders.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Reminders', route('reminders.index')); $breadcrumbs->push(trans('breadcrumbs.reminders'), route('reminders.index'));
} }
); );
@@ -296,7 +306,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) { 'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('reminders.index'); $breadcrumbs->parent('reminders.index');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id)); $breadcrumbs->push(trans('breadcrumbs.reminder', ['id' => e($reminder->id)]), route('reminders.show', $reminder->id));
} }
); );
@@ -306,28 +316,33 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) { 'reports.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Reports', route('reports.index')); $breadcrumbs->push(trans('breadcrumbs.reports'), route('reports.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date) { 'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.index'); $breadcrumbs->parent('reports.index');
$breadcrumbs->push($date->year, route('reports.year', $date->year)); if ($shared) {
$title = trans('breadcrumbs.yearly_report_shared', ['date' => $date->year]);
} else {
$title = trans('breadcrumbs.yearly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.year', $date->year));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'reports.month', function (Generator $breadcrumbs, Carbon $date) { 'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.year', $date); $breadcrumbs->parent('reports.year', $date, $shared);
$breadcrumbs->push('Monthly report for ' . $date->format('F Y'), route('reports.month', [$date->year, $date->month]));
}
);
Breadcrumbs::register( if ($shared) {
'reports.budget', function (Generator $breadcrumbs, Carbon $date) { $title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->year]);
$breadcrumbs->parent('reports.index'); } else {
$breadcrumbs->push('Budget report for ' . $date->format('F Y'), route('reports.budget', [$date->year, $date->month])); $title = trans('breadcrumbs.monthly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.month', [$date->year, $date->month]));
} }
); );
@@ -335,7 +350,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) { 'search', function (Generator $breadcrumbs, $query) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search')); $breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search'));
} }
); );
@@ -343,47 +358,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) { 'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', $what));
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitle = 'Expenses';
break;
case 'revenue':
case 'deposit':
$subTitle = 'Revenue, income and deposits';
break;
case 'transfer':
case 'transfers':
$subTitle = 'Transfers';
break;
case 'opening balance':
$subTitle = 'Opening balances';
break;
default:
throw new FireflyException('Cannot handle $what "' . e($what) . '" in bread crumbs');
}
$breadcrumbs->push($subTitle, route('transactions.index', $what));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) { 'transactions.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push('Create new ' . e($what), route('transactions.create', $what)); $breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', $what));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal); $breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Edit ' . e($journal->description), route('transactions.edit', $journal->id)); $breadcrumbs->push(trans('breadcrumbs.edit_journal', ['description' => $journal->description]), route('transactions.edit', $journal->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal); $breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Delete ' . e($journal->description), route('transactions.delete', $journal->id)); $breadcrumbs->push(trans('breadcrumbs.delete_journal', ['description' => e($journal->description)]), route('transactions.delete', $journal->id));
} }
); );
@@ -391,7 +385,7 @@ Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type)); $breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push(e($journal->description), route('transactions.show', $journal->id)); $breadcrumbs->push($journal->description, route('transactions.show', $journal->id));
} }
); );
@@ -400,16 +394,32 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'tags.index', function (Generator $breadcrumbs) { 'tags.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Tags', route('tags.index')); $breadcrumbs->push(trans('breadcrumbs.tags'), route('tags.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'tags.create', function (Generator $breadcrumbs) { 'tags.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('tags.index'); $breadcrumbs->parent('tags.index');
$breadcrumbs->push('Create tag', route('tags.create')); $breadcrumbs->push(trans('breadcrumbs.createTag'), route('tags.create'));
} }
); );
Breadcrumbs::register(
'tags.edit', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.edit_tag', ['tag' => e($tag->tag)]), route('tags.edit', $tag->id));
}
);
Breadcrumbs::register(
'tags.delete', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]), route('tags.delete', $tag->id));
}
);
Breadcrumbs::register( Breadcrumbs::register(
'tags.show', function (Generator $breadcrumbs, Tag $tag) { 'tags.show', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.index'); $breadcrumbs->parent('tags.index');

View File

@@ -188,7 +188,7 @@ Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'r
Route::controllers( Route::controllers(
[ [
'auth' => 'Auth\AuthController', 'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController', 'password' => 'Auth\PasswordController',
] ]
); );
@@ -223,7 +223,6 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching. Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']); Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']); Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/add/{bill}', ['uses' => 'BillController@add', 'as' => 'bills.add']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']); Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']); Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']); Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']);
@@ -273,22 +272,38 @@ Route::group(
/** /**
* Google Chart Controller * ALL CHART Controllers
*/ */
Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']); // accounts:
Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']); Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']); Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']);
Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']); Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending'])->where(['year' => '[0-9]+']); // bills:
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']); Route::get('/chart/bill/frontpage', ['uses' => 'Chart\BillController@frontpage']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']); Route::get('/chart/bill/{bill}', ['uses' => 'Chart\BillController@single']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']); // budgets:
Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']); Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']); Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']); Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']);
// categories:
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
Route::get('/chart/category/year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
// piggy banks:
Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
// reports:
Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
/** /**
* Help Controller * Help Controller
@@ -306,10 +321,7 @@ Route::group(
Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']); Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']);
Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']); Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']);
Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']); Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']);
Route::get('/json/show-shared-reports', 'JsonController@showSharedReports');
Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals'); Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals');
Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports');
/** /**
* Piggy Bank Controller * Piggy Bank Controller
@@ -355,19 +367,13 @@ Route::group(
* Report Controller * Report Controller
*/ */
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']); //Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}']);
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']); Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']); Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
// pop ups for budget report: // pop ups for budget report:
Route::get('/reports/modal/{account}/{year}/{month}/no-budget', ['uses' => 'ReportController@modalNoBudget', 'as' => 'reports.no-budget']);
Route::get(
'/reports/modal/{account}/{year}/{month}/balanced-transfers',
['uses' => 'ReportController@modalBalancedTransfers', 'as' => 'reports.balanced-transfers']
);
Route::get(
'/reports/modal/{account}/{year}/{month}/left-unbalanced', ['uses' => 'ReportController@modalLeftUnbalanced', 'as' => 'reports.left-unbalanced']
);
/** /**
* Search Controller * Search Controller

View File

@@ -28,7 +28,7 @@ class Account extends Model
/** /**
* @param array $fields * @param array $fields
* * @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return Account|null * @return Account|null
*/ */
@@ -133,6 +133,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -160,6 +161,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param array $types * @param array $types
*/ */
@@ -174,6 +176,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param string $name * @param string $name
* @param string $value * @param string $value
@@ -191,6 +194,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)

View File

@@ -37,6 +37,7 @@ class Category extends Model
/** /**
* @param array $fields * @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return Account|null * @return Account|null
*/ */
@@ -79,6 +80,7 @@ class Category extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)
@@ -89,6 +91,7 @@ class Category extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string

View File

@@ -64,6 +64,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return int * @return int
@@ -93,6 +94,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)
@@ -103,6 +105,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string

View File

@@ -18,6 +18,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return int * @return int
@@ -38,6 +39,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return mixed * @return mixed
@@ -53,6 +55,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return bool * @return bool
@@ -73,6 +76,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@@ -86,6 +90,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* *
* @return $this * @return $this
@@ -100,6 +105,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setMetadataAttribute($value) public function setMetadataAttribute($value)

View File

@@ -29,6 +29,8 @@ class Tag extends Model
/** /**
* @param array $fields * @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* *
* @return Tag|null * @return Tag|null
*/ */
@@ -77,6 +79,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -88,6 +91,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -99,6 +103,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setDescriptionAttribute($value) public function setDescriptionAttribute($value)
@@ -108,6 +113,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setTagAttribute($value) public function setTagAttribute($value)

View File

@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
/** /**
@@ -61,20 +62,66 @@ class TransactionJournal extends Model
/** /**
* @return float * @return float
*/ */
public function getAmountAttribute() public function getActualAmountAttribute()
{ {
$amount = 0;
/** @var Transaction $t */ /** @var Transaction $t */
foreach ($this->transactions as $t) { foreach ($this->transactions as $t) {
if ($t->amount > 0) { if ($t->amount > 0) {
return floatval($t->amount); $amount = floatval($t->amount);
} }
} }
return 0; return $amount;
} }
/** /**
* @return Account|mixed * @return float
*/
public function getAmountAttribute()
{
$amount = 0;
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0) {
$amount = floatval($t->amount);
}
}
/*
* If the journal has tags, it gets complicated.
*/
if ($this->tags->count() == 0) {
return $amount;
}
// if journal is part of advancePayment AND journal is a withdrawal,
// then journal is being repaid by other journals, so the actual amount will lower:
/** @var Tag $tag */
$tag = $this->tags()->where('tagMode', 'advancePayment')->first();
if ($tag && $this->transactionType->type == 'Withdrawal') {
// loop other deposits, remove from our amount.
$others = $tag->transactionJournals()->transactionTypes(['Deposit'])->get();
foreach ($others as $other) {
$amount -= $other->amount;
}
return $amount;
}
return $amount;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/**
* @return Account
*/ */
public function getAssetAccountAttribute() public function getAssetAccountAttribute()
{ {
@@ -93,7 +140,7 @@ class TransactionJournal extends Model
} }
return $this->transactions()->first(); return $this->transactions()->first()->account;
} }
/** /**
@@ -105,6 +152,28 @@ class TransactionJournal extends Model
return $this->hasMany('FireflyIII\Models\Transaction'); return $this->hasMany('FireflyIII\Models\Transaction');
} }
/**
* @return float
*/
public function getCorrectedActualAmountAttribute()
{
$amount = 0;
$type = $this->transactionType->type;
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0 && $type != 'Withdrawal') {
$amount = floatval($t->amount);
break;
}
if ($t->amount < 0 && $type == 'Withdrawal') {
$amount = floatval($t->amount);
break;
}
}
return $amount;
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return array * @return array
@@ -116,6 +185,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -140,6 +210,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Account $account * @param Account $account
*/ */
@@ -154,6 +225,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -166,6 +238,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -178,6 +251,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param $amount * @param $amount
*/ */
@@ -195,6 +269,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -206,7 +281,26 @@ class TransactionJournal extends Model
} }
/** /**
* Returns the account to which the money was moved.
*
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Account $account
*/
public function scopeToAccountIs(EloquentBuilder $query, Account $account)
{
$query->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
}
);
$query->where('transactions.account_id', $account->id);
}
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param array $types * @param array $types
*/ */
@@ -239,6 +333,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setDescriptionAttribute($value) public function setDescriptionAttribute($value)
@@ -247,15 +342,6 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true; $this->attributes['encrypted'] = true;
} }
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@@ -17,6 +17,8 @@ class ConfigServiceProvider extends ServiceProvider
* to overwrite any "vendor" or package configuration that you may want to * to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command. * modify before the application handles the incoming request / command.
* *
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return void * @return void
*/ */
public function register() public function register()
@@ -163,6 +165,7 @@ class ConfigServiceProvider extends ServiceProvider
'Session', 'Session',
'Route', 'Route',
'Auth', 'Auth',
'Lang',
'URL', 'URL',
'Config', 'Config',
'ExpandedForm' => [ 'ExpandedForm' => [

View File

@@ -49,7 +49,47 @@ class EventServiceProvider extends ServiceProvider
public function boot(DispatcherContract $events) public function boot(DispatcherContract $events)
{ {
parent::boot($events); parent::boot($events);
$this->registerDeleteEvents();
$this->registerCreateEvents();
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
}
/**
*
*/
protected function registerDeleteEvents()
{
TransactionJournal::deleted( TransactionJournal::deleted(
function (TransactionJournal $journal) { function (TransactionJournal $journal) {
@@ -59,8 +99,6 @@ class EventServiceProvider extends ServiceProvider
} }
} }
); );
PiggyBank::deleting( PiggyBank::deleting(
function (PiggyBank $piggyBank) { function (PiggyBank $piggyBank) {
$reminders = $piggyBank->reminders()->get(); $reminders = $piggyBank->reminders()->get();
@@ -82,6 +120,14 @@ class EventServiceProvider extends ServiceProvider
} }
); );
}
/**
*
*/
protected function registerCreateEvents()
{
// move this routine to a filter // move this routine to a filter
// in case of repeated piggy banks and/or other problems. // in case of repeated piggy banks and/or other problems.
PiggyBank::created( PiggyBank::created(
@@ -94,47 +140,6 @@ class EventServiceProvider extends ServiceProvider
$repetition->save(); $repetition->save();
} }
); );
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
/*
* Create new LimitRepetition:
*/
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
/*
* Update existing one.
*/
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
} }
} }

View File

@@ -10,9 +10,9 @@ use FireflyIII\Support\Preferences;
use FireflyIII\Support\Steam; use FireflyIII\Support\Steam;
use FireflyIII\Support\Twig\Budget; use FireflyIII\Support\Twig\Budget;
use FireflyIII\Support\Twig\General; use FireflyIII\Support\Twig\General;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Support\Twig\Journal; use FireflyIII\Support\Twig\Journal;
use FireflyIII\Support\Twig\PiggyBank; use FireflyIII\Support\Twig\PiggyBank;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Validation\FireflyValidator; use FireflyIII\Validation\FireflyValidator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Twig; use Twig;
@@ -46,6 +46,9 @@ class FireflyServiceProvider extends ServiceProvider
Twig::addExtension(new Translation); Twig::addExtension(new Translation);
} }
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function register() public function register()
{ {

View File

@@ -162,8 +162,6 @@ class AccountRepository implements AccountRepositoryInterface
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC'); ->orderBy('transaction_journals.id', 'DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
$count = $query->count(); $count = $query->count();
$set = $query->take(50)->offset($offset)->get(['transaction_journals.*']); $set = $query->take(50)->offset($offset)->get(['transaction_journals.*']);
$paginator = new LengthAwarePaginator($set, $count, 50, $page); $paginator = new LengthAwarePaginator($set, $count, 50, $page);
@@ -308,12 +306,14 @@ class AccountRepository implements AccountRepositoryInterface
/** /**
* @param Account $account * @param Account $account
* @param Carbon $date
* *
* @return float * @return float
*/ */
public function leftOnAccount(Account $account) public function leftOnAccount(Account $account, Carbon $date)
{ {
$balance = Steam::balance($account, null, true);
$balance = Steam::balance($account, $date, true);
/** @var PiggyBank $p */ /** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) { foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount; $balance -= $p->currentRelevantRep()->currentamount;

View File

@@ -115,10 +115,11 @@ interface AccountRepositoryInterface
/** /**
* @param Account $account * @param Account $account
* @param Carbon $date
* *
* @return float * @return float
*/ */
public function leftOnAccount(Account $account); public function leftOnAccount(Account $account, Carbon $date);
/** /**
* @param Account $account * @param Account $account

View File

@@ -22,6 +22,27 @@ use Navigation;
*/ */
class BillRepository implements BillRepositoryInterface class BillRepository implements BillRepositoryInterface
{ {
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end)
{
$amount = 0;
$journals = $bill->transactionjournals()->before($end)->after($start)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$amount += $journal->amount;
}
return $amount;
}
/** /**
* Create a fake bill to help the chart controller. * Create a fake bill to help the chart controller.
* *
@@ -251,13 +272,10 @@ class BillRepository implements BillRepositoryInterface
*/ */
public function scan(Bill $bill, TransactionJournal $journal) public function scan(Bill $bill, TransactionJournal $journal)
{ {
/* $amountMatch = false;
* Match words.
*/
$wordMatch = false; $wordMatch = false;
$matches = explode(',', $bill->match); $matches = explode(',', $bill->match);
$description = strtolower($journal->description); $description = strtolower($journal->description);
Log::debug('Now scanning ' . $description);
/* /*
* Attach expense account to description for more narrow matching. * Attach expense account to description for more narrow matching.
@@ -295,7 +313,6 @@ class BillRepository implements BillRepositoryInterface
* Match amount. * Match amount.
*/ */
$amountMatch = false;
if (count($transactions) > 1) { if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount)); $amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));

View File

@@ -15,6 +15,17 @@ use Illuminate\Support\Collection;
interface BillRepositoryInterface interface BillRepositoryInterface
{ {
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
/** /**
* Create a fake bill to help the chart controller. * Create a fake bill to help the chart controller.
* *

View File

@@ -8,6 +8,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Input; use Input;
@@ -98,8 +99,6 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getBudgets() public function getBudgets()
{ {
$budgets = Auth::user()->budgets()->get(); $budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
return $budgets; return $budgets;
} }
@@ -250,22 +249,40 @@ class BudgetRepository implements BudgetRepositoryInterface
->before($end) ->before($end)
->lessThan(0) ->lessThan(0)
->transactionTypes(['Withdrawal']) ->transactionTypes(['Withdrawal'])
->sum('transactions.amount'); ->sum('transactions.amount');
return floatval($noBudgetSet) * -1; return floatval($noBudgetSet) * -1;
} }
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $start
* @param Carbon $end
* @param bool $shared
* *
* @return float * @return float
*/ */
public function spentInMonth(Budget $budget, Carbon $date) public function spentInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true)
{ {
$end = clone $date; if ($shared === true) {
$date->startOfMonth(); // get everything:
$end->endOfMonth(); $sum = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; } else {
// get all journals in this month where the asset account is NOT shared.
$sum = $budget->transactionjournals()
->before($end)
->after($start)
->lessThan(0)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '!=', '"sharedAsset"')
->sum('amount');
$sum = floatval($sum) * -1;
}
return $sum; return $sum;
} }

View File

@@ -124,11 +124,13 @@ interface BudgetRepositoryInterface
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* *
* @return float * @return float
*/ */
public function spentInMonth(Budget $budget, Carbon $date); public function spentInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true);
/** /**
* @param array $data * @param array $data

View File

@@ -168,11 +168,41 @@ class CategoryRepository implements CategoryRepositoryInterface
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @param bool $shared
*
* @return float * @return float
*/ */
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end) public function spentInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false)
{ {
return floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; if ($shared === true) {
// shared is true.
// always ignore transfers between accounts!
$sum = floatval(
$category->transactionjournals()
->transactionTypes(['Withdrawal'])
->before($end)->after($start)->lessThan(0)->sum('amount')
) * -1;
} else {
// do something else, SEE budgets.
// get all journals in this month where the asset account is NOT shared.
$sum = $category->transactionjournals()
->before($end)
->after($start)
->transactionTypes(['Withdrawal'])
->lessThan(0)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '!=', '"sharedAsset"')
->sum('amount');
$sum = floatval($sum) * -1;
}
return $sum;
} }
/** /**

View File

@@ -75,9 +75,11 @@ interface CategoryRepositoryInterface
* @param \Carbon\Carbon $start * @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end * @param \Carbon\Carbon $end
* *
* @param bool $shared
*
* @return float * @return float
*/ */
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end); public function spentInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false);
/** /**
* @param Category $category * @param Category $category

View File

@@ -338,61 +338,86 @@ class JournalRepository implements JournalRepositoryInterface
*/ */
protected function storeAccounts(TransactionType $type, array $data) protected function storeAccounts(TransactionType $type, array $data)
{ {
$from = null; $fromAccount = null;
$to = null; $toAccount = null;
switch ($type->type) { switch ($type->type) {
case 'Withdrawal': case 'Withdrawal':
list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data);
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break; break;
case 'Deposit': case 'Deposit':
$to = Account::find($data['account_id']); list($fromAccount, $toAccount) = $this->storeDepositAccounts($data);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break; break;
case 'Transfer': case 'Transfer':
$from = Account::find($data['account_from_id']); $fromAccount = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']); $toAccount = Account::find($data['account_to_id']);
break; break;
} }
if (is_null($to) || (!is_null($to) && is_null($to->id))) {
if (is_null($toAccount)) {
Log::error('"to"-account is null, so we cannot continue!'); Log::error('"to"-account is null, so we cannot continue!');
App::abort(500, '"to"-account is null, so we cannot continue!'); App::abort(500, '"to"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
if (is_null($from) || (!is_null($from) && is_null($from->id))) { if (is_null($fromAccount)) {
Log::error('"from"-account is null, so we cannot continue!'); Log::error('"from"-account is null, so we cannot continue!');
App::abort(500, '"from"-account is null, so we cannot continue!'); App::abort(500, '"from"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
return [$from, $to]; return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeWithdrawalAccounts(array $data)
{
$fromAccount = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeDepositAccounts(array $data)
{
$toAccount = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
} }
} }

View File

@@ -19,57 +19,6 @@ use Navigation;
class PiggyBankRepository implements PiggyBankRepositoryInterface class PiggyBankRepository implements PiggyBankRepositoryInterface
{ {
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $repetition->piggyBank()->first();
$bars = new Collection;
$currentStart = clone $repetition->startdate;
if (is_null($piggyBank->reminder)) {
$entry = ['repetition' => $repetition, 'amountPerBar' => floatval($piggyBank->targetamount),
'currentAmount' => floatval($repetition->currentamount), 'cumulativeAmount' => floatval($piggyBank->targetamount),
'startDate' => clone $repetition->startdate, 'targetDate' => clone $repetition->targetdate];
$bars->push($this->createPiggyBankPart($entry));
return $bars;
}
while ($currentStart < $repetition->targetdate) {
$currentTarget = Navigation::endOfX($currentStart, $piggyBank->reminder, $repetition->targetdate);
$entry = ['repetition' => $repetition, 'amountPerBar' => null, 'currentAmount' => floatval($repetition->currentamount),
'cumulativeAmount' => null, 'startDate' => $currentStart, 'targetDate' => $currentTarget];
$bars->push($this->createPiggyBankPart($entry));
$currentStart = clone $currentTarget;
$currentStart->addDay();
}
$amountPerBar = floatval($piggyBank->targetamount) / $bars->count();
$cumulative = $amountPerBar;
/** @var PiggyBankPart $bar */
foreach ($bars as $index => $bar) {
$bar->setAmountPerBar($amountPerBar);
$bar->setCumulativeAmount($cumulative);
if ($bars->count() - 1 == $index) {
$bar->setCumulativeAmount($piggyBank->targetamount);
}
$cumulative += $amountPerBar;
}
return $bars;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param $amount * @param $amount
@@ -83,24 +32,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return true; return true;
} }
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data)
{
$part = new PiggyBankPart;
$part->setRepetition($data['repetition']);
$part->setAmountPerBar($data['amountPerBar']);
$part->setCurrentamount($data['currentAmount']);
$part->setCumulativeAmount($data['cumulativeAmount']);
$part->setStartdate($data['startDate']);
$part->setTargetdate($data['targetDate']);
return $part;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
@@ -151,8 +82,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
{ {
// split query to make it work in sqlite: // split query to make it work in sqlite:
$set = PiggyBank:: $set = PiggyBank::
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id') leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id')
->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']); ->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']);
foreach ($set as $e) { foreach ($set as $e) {
$e->order = 0; $e->order = 0;
$e->save(); $e->save();
@@ -198,7 +129,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
* @param array $data * @param array $data
* *
* @return PiggyBank * @return PiggyBank
* @internal param PiggyBank $account
*/ */
public function update(PiggyBank $piggyBank, array $data) public function update(PiggyBank $piggyBank, array $data)
{ {

View File

@@ -14,18 +14,6 @@ use Illuminate\Support\Collection;
interface PiggyBankRepositoryInterface interface PiggyBankRepositoryInterface
{ {
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition);
/** /**
* @return Collection * @return Collection
*/ */
@@ -38,13 +26,6 @@ interface PiggyBankRepositoryInterface
*/ */
public function getEvents(PiggyBank $piggyBank); public function getEvents(PiggyBank $piggyBank);
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data);
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param $amount * @param $amount

View File

@@ -4,6 +4,8 @@ namespace FireflyIII\Repositories\Tag;
use Auth; use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
@@ -17,7 +19,10 @@ use Illuminate\Support\Collection;
class TagRepository implements TagRepositoryInterface class TagRepository implements TagRepositoryInterface
{ {
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
*
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param Tag $tag * @param Tag $tag
* *
@@ -33,88 +38,54 @@ class TagRepository implements TagRepositoryInterface
return false; return false;
} }
if ($tag->tagMode == 'nothing') { switch ($tag->tagMode) {
// save it, no problem: case 'nothing':
$journal->tags()->save($tag);
return true;
}
/*
* get some withdrawal types:
*/
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
if ($tag->tagMode == 'balancingAct') {
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag); $journal->tags()->save($tag);
return true; return true;
} break;
// and only if this is the only transfer case 'balancingAct':
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) { return $this->connectBalancingAct($journal, $tag);
$journal->tags()->save($tag); break;
case 'advancePayment':
return true; return $this->connectAdvancePayment($journal, $tag);
} break;
// ignore expense
return false;
} }
if ($tag->tagMode == 'advancePayment') {
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
}
}
if ($match) {
$journal->tags()->save($tag);
return true;
}
}
return false;
}
// @codeCoverageIgnoreStart
return false; return false;
} }
// @codeCoverageIgnoreEnd
/**
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end)
{
// the quickest way to do this is by scanning all balancingAct tags
// because there will be less of them any way.
$tags = Auth::user()->tags()->where('tagMode', 'balancingAct')->get();
$amount = 0;
/** @var Tag $tag */
foreach ($tags as $tag) {
$transfer = $tag->transactionjournals()->after($start)->before($end)->toAccountIs($account)->transactionTypes(['Transfer'])->first();
if ($transfer) {
$amount += $transfer->amount;
}
}
return $amount;
}
/** /**
* @param Tag $tag * @param Tag $tag
@@ -127,6 +98,7 @@ class TagRepository implements TagRepositoryInterface
return true; return true;
} }
// @codeCoverageIgnoreEnd
/** /**
* @return Collection * @return Collection
@@ -186,4 +158,107 @@ class TagRepository implements TagRepositoryInterface
return $tag; return $tag;
} }
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectBalancingAct(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
return $this->matchAll($journal, $tag);
}
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return bool
*/
protected function matchAll(TransactionJournal $journal, Tag $tag)
{
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
}
}
if ($match) {
$journal->tags()->save($tag);
return true;
}
return false;
}
} }

View File

@@ -2,6 +2,8 @@
namespace FireflyIII\Repositories\Tag; namespace FireflyIII\Repositories\Tag;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -15,6 +17,23 @@ use Illuminate\Support\Collection;
interface TagRepositoryInterface interface TagRepositoryInterface
{ {
/**
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end);
/** /**
* @param array $data * @param array $data
* *

View File

@@ -84,32 +84,20 @@ class Amount
*/ */
public function formatJournal(TransactionJournal $journal, $coloured = true) public function formatJournal(TransactionJournal $journal, $coloured = true)
{ {
$showPositive = true;
if (is_null($journal->symbol)) { if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol; $symbol = $journal->transactionCurrency->symbol;
} else { } else {
$symbol = $journal->symbol; $symbol = $journal->symbol;
} }
$amount = 0; $amount = $journal->amount;
if ($journal->transactionType->type == 'Withdrawal') {
if (is_null($journal->type)) { $amount = $amount * -1;
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
} }
if ($journal->transactionType->type == 'Transfer' && $coloured) {
if ($type == 'Withdrawal') { return '<span class="text-info">' . $this->formatWithSymbol($symbol, $amount, false) . '</span>';
$showPositive = false;
} }
if ($journal->transactionType->type == 'Transfer' && !$coloured) {
foreach ($journal->transactions as $t) { return $this->formatWithSymbol($symbol, $amount, false);
if (floatval($t->amount) > 0 && $showPositive === true) {
$amount = floatval($t->amount);
break;
}
if (floatval($t->amount) < 0 && $showPositive === false) {
$amount = floatval($t->amount);
}
} }
return $this->formatWithSymbol($symbol, $amount, $coloured); return $this->formatWithSymbol($symbol, $amount, $coloured);

View File

@@ -51,29 +51,7 @@ class ExpandedForm
if (isset($options['label'])) { if (isset($options['label'])) {
return $options['label']; return $options['label'];
} }
$labels = [ return trans('form.'.$name);
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'tagMode' => 'Tag mode',
'tagPosition' => 'Tag location',
'virtualBalance' => 'Virtual balance',
'longitude_latitude' => 'Location',
'targetamount' => 'Target amount',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank'];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
} }

View File

@@ -28,21 +28,11 @@ class Navigation
$add = ($skip + 1); $add = ($skip + 1);
$functionMap = [ $functionMap = [
'1D' => 'addDays', '1D' => 'addDays', 'daily' => 'addDays',
'daily' => 'addDays', '1W' => 'addWeeks', 'weekly' => 'addWeeks', 'week' => 'addWeeks',
'1W' => 'addWeeks', '1M' => 'addMonths', 'month' => 'addMonths', 'monthly' => 'addMonths', '3M' => 'addMonths',
'weekly' => 'addWeeks', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'week' => 'addWeeks', 'year' => 'addYears', 'yearly' => 'addYears',
'1M' => 'addMonths',
'month' => 'addMonths',
'monthly' => 'addMonths',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYears',
'yearly' => 'addYears',
]; ];
$modifierMap = [ $modifierMap = [
'quarter' => 3, 'quarter' => 3,
@@ -75,21 +65,11 @@ class Navigation
$currentEnd = clone $theCurrentEnd; $currentEnd = clone $theCurrentEnd;
$functionMap = [ $functionMap = [
'1D' => 'addDay', '1D' => 'addDay', 'daily' => 'addDay',
'daily' => 'addDay', '1W' => 'addWeek', 'week' => 'addWeek', 'weekly' => 'addWeek',
'1W' => 'addWeek', '1M' => 'addMonth', 'month' => 'addMonth', 'monthly' => 'addMonth',
'week' => 'addWeek', '3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'weekly' => 'addWeek', 'year' => 'addYear', 'yearly' => 'addYear',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
]; ];
$modifierMap = [ $modifierMap = [
'quarter' => 3, 'quarter' => 3,

View File

@@ -23,12 +23,9 @@ class Steam
* *
* @return float * @return float
*/ */
public function balance(Account $account, Carbon $date = null, $ignoreVirtualBalance = false) public function balance(Account $account, Carbon $date, $ignoreVirtualBalance = false)
{ {
$date = is_null($date) ? Carbon::now() : $date;
// find the first known transaction on this account: // find the first known transaction on this account:
//
$firstDateObject = $account $firstDateObject = $account
->transactions() ->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
@@ -37,12 +34,6 @@ class Steam
$firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date); $firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date);
$date = $date < $firstDate ? $firstDate : $date; $date = $date < $firstDate ? $firstDate : $date;
/**
*select * from transactions
* left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
* order by date ASC
*/
$balance = floatval( $balance = floatval(
$account->transactions()->leftJoin( $account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'

View File

@@ -3,10 +3,12 @@
namespace FireflyIII\Support\Twig; namespace FireflyIII\Support\Twig;
use App; use App;
use Carbon\Carbon;
use Config; use Config;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use Route; use Route;
use Session;
use Twig_Extension; use Twig_Extension;
use Twig_SimpleFilter; use Twig_SimpleFilter;
use Twig_SimpleFunction; use Twig_SimpleFunction;
@@ -21,6 +23,7 @@ class General extends Twig_Extension
/** /**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @return array * @return array
*/ */
public function getFilters() public function getFilters()
@@ -56,8 +59,9 @@ class General extends Twig_Extension
if (is_null($account)) { if (is_null($account)) {
return 'NULL'; return 'NULL';
} }
$date = Session::get('end', Carbon::now()->endOfMonth());
return App::make('steam')->balance($account); return App::make('steam')->balance($account, $date);
} }
); );

View File

@@ -18,6 +18,7 @@ class Journal extends Twig_Extension
{ {
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return array * @return array
*/ */
public function getFilters() public function getFilters()
@@ -27,20 +28,25 @@ class Journal extends Twig_Extension
$filters[] = new Twig_SimpleFilter( $filters[] = new Twig_SimpleFilter(
'typeIcon', function (TransactionJournal $journal) { 'typeIcon', function (TransactionJournal $journal) {
$type = $journal->transactionType->type; $type = $journal->transactionType->type;
if ($type == 'Withdrawal') {
return '<span class="glyphicon glyphicon-arrow-left" title="Withdrawal"></span>'; switch ($type) {
} case 'Withdrawal':
if ($type == 'Deposit') { return '<span class="glyphicon glyphicon-arrow-left" title="' . trans('firefly.withdrawal') . '"></span>';
return '<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>'; break;
} case 'Deposit':
if ($type == 'Transfer') { return '<span class="glyphicon glyphicon-arrow-right" title="' . trans('firefly.deposit') . '"></span>';
return '<i class="fa fa-fw fa-exchange" title="Transfer"></i>'; break;
} case 'Transfer':
if ($type == 'Opening balance') { return '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
return '<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>'; break;
case 'Opening balance':
return '<span class="glyphicon glyphicon-ban-circle" title="' . trans('firefly.openingBalance') . '"></span>';
break;
default:
return '';
break;
} }
return '';
}, ['is_safe' => ['html']] }, ['is_safe' => ['html']]
); );
@@ -49,6 +55,8 @@ class Journal extends Twig_Extension
} }
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return array * @return array
*/ */
public function getFunctions() public function getFunctions()
@@ -70,13 +78,38 @@ class Journal extends Twig_Extension
if ($journal->tags->count() == 0) { if ($journal->tags->count() == 0) {
return App::make('amount')->formatJournal($journal); return App::make('amount')->formatJournal($journal);
} }
foreach ($journal->tags as $tag) { foreach ($journal->tags as $tag) {
if ($tag->tagMode == 'balancingAct') { if ($tag->tagMode == 'balancingAct') {
// return tag formatted for a "balancing act". // return tag formatted for a "balancing act", even if other
// tags are present.
$amount = App::make('amount')->formatJournal($journal, false); $amount = App::make('amount')->formatJournal($journal, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</span>'; . '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</a>';
}
/*
* AdvancePayment with a deposit will show the tag instead of the amount:
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Deposit') {
$amount = App::make('amount')->formatJournal($journal, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-sort-numeric-desc"></i> ' . $tag->tag . '</a>';
}
/*
* AdvancePayment with a withdrawal will show the amount with a link to
* the tag. The TransactionJournal should properly calculate the amount.
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Withdrawal') {
$amount = App::make('amount')->formatJournal($journal);
return '<a href="' . route('tags.show', $tag->id) . '">' . $amount . '</a>';
}
if ($tag->tagMode == 'nothing') {
// return the amount:
return App::make('amount')->formatJournal($journal);
} }
} }

View File

@@ -24,7 +24,7 @@ class Translation extends Twig_Extension
$filters[] = new Twig_SimpleFilter( $filters[] = new Twig_SimpleFilter(
'_', function ($name) { '_', function ($name) {
return trans('firefly.'.$name); return trans('firefly.' . $name);
}, ['is_safe' => ['html']] }, ['is_safe' => ['html']]
); );

View File

@@ -29,10 +29,11 @@ class FireflyValidator extends Validator
* @param array $rules * @param array $rules
* @param array $messages * @param array $messages
* @param array $customAttributes * @param array $customAttributes
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/ */
public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = []) public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{ {
parent::__construct($translator, $data, $rules, $messages); parent::__construct($translator, $data, $rules, $messages, $customAttributes);
} }
/** /**

View File

@@ -21,7 +21,7 @@ return [
'defaultAsset' => 'Default asset account', 'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account', 'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account', 'savingAsset' => 'Savings account',
'ccAsset' => 'Credit card', 'ccAsset' => 'Credit card',
], ],
'range_to_text' => [ 'range_to_text' => [
@@ -32,7 +32,7 @@ return [
'6M' => 'half year', '6M' => 'half year',
'custom' => '(custom)' 'custom' => '(custom)'
], ],
'ccTypes' => [ 'ccTypes' => [
'monthlyFull' => 'Full payment every month' 'monthlyFull' => 'Full payment every month'
], ],
'range_to_name' => [ 'range_to_name' => [
@@ -77,9 +77,9 @@ return [
], ],
'accountTypeByIdentifier' => 'accountTypeByIdentifier' =>
[ [
'asset' => 'Asset account', 'asset' => 'Asset account',
'expense' => 'Expense account', 'expense' => 'Expense account',
'revenue' => 'Revenue account' 'revenue' => 'Revenue account'
], ],
'shortNamesByFullName' => 'shortNamesByFullName' =>
[ [
@@ -89,6 +89,22 @@ return [
'Beneficiary account' => 'expense', 'Beneficiary account' => 'expense',
'Revenue account' => 'revenue', 'Revenue account' => 'revenue',
'Cash account' => 'cash', 'Cash account' => 'cash',
] ],
'lang' => [
'en' => 'English',
'nl' => 'Nederlands'
],
'locales' => [
'en' => ['en', 'English', 'en_US', 'en_US.utf8'],
'nl' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
],
'month' => [
'en' => '%B %Y',
'nl' => '%B %Y',
],
'monthAndDay' => [
'en' => '%B %e, %Y',
'nl' => '%e %B %Y',
],
]; ];

2
database/.gitignore vendored
View File

@@ -1 +1 @@
*.sqlite

2
pu.sh
View File

@@ -11,7 +11,7 @@ fi
if [ ! -z "$1" ] if [ ! -z "$1" ]
then then
phpunit --verbose tests/repositories/$1.php phpunit --verbose tests/helpers/$1.php
fi fi
# restore .env file # restore .env file

View File

@@ -1,7 +1,7 @@
$(document).ready(function () { $(document).ready(function () {
if (typeof(googleComboChart) === 'function' && typeof(billID) !== 'undefined') { if (typeof(googleComboChart) === 'function' && typeof(billID) !== 'undefined') {
googleComboChart('chart/bills/' + billID, 'bill-overview'); googleComboChart('chart/bill/' + billID, 'bill-overview');
} }
} }
); );

View File

@@ -1,8 +1,8 @@
$(function () { $(function () {
if (typeof categoryID !== 'undefined') { if (typeof categoryID !== 'undefined') {
googleColumnChart('chart/category/' + categoryID + '/overview', 'componentOverview'); googleColumnChart('chart/category/' + categoryID + '/all', 'all');
googleColumnChart('chart/category/' + categoryID + '/period', 'periodOverview'); googleColumnChart('chart/category/' + categoryID + '/month', 'month');
} }

View File

@@ -1,5 +1,5 @@
var google = google || {}; var google = google || {};
google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line', 'sankey', 'table']}); google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line'],'language': language });
function googleChart(chartType, URL, container, options) { function googleChart(chartType, URL, container, options) {
if ($('#' + container).length === 1) { if ($('#' + container).length === 1) {

View File

@@ -2,11 +2,12 @@ google.setOnLoadCallback(drawChart);
function drawChart() { function drawChart() {
googleLineChart('chart/home/account', 'accounts-chart'); googleLineChart('chart/account/frontpage', 'accounts-chart');
//googleColumnChart('chart/home/budgets', 'budgets-chart'); googlePieChart('chart/bill/frontpage', 'bills-chart');
googleStackedColumnChart('chart/home/budgets', 'budgets-chart'); googleStackedColumnChart('chart/budget/frontpage', 'budgets-chart');
googleColumnChart('chart/home/categories', 'categories-chart'); googleColumnChart('chart/category/frontpage', 'categories-chart');
googlePieChart('chart/home/bills', 'bills-chart');
getBoxAmounts(); getBoxAmounts();
} }

View File

@@ -3,7 +3,7 @@ $(function () {
$('.removeMoney').on('click', removeMoney); $('.removeMoney').on('click', removeMoney);
if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') { if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history'); googleLineChart('chart/piggyBank/' + piggyBankID, 'piggy-bank-history');
} }
$('#sortable tbody').sortable( $('#sortable tbody').sortable(

View File

@@ -1,20 +1,26 @@
if (typeof(google) != 'undefined') { if (typeof(google) != 'undefined') {
google.setOnLoadCallback(drawChart); google.setOnLoadCallback(drawChart);
} }
function drawChart() { function drawChart() {
googleColumnChart('chart/reports/income-expenses/' + year, 'income-expenses-chart'); googleColumnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
googleColumnChart('chart/reports/income-expenses-sum/' + year, 'income-expenses-sum-chart') googleColumnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
googleStackedColumnChart('chart/budgets/spending/' + year, 'budgets'); googleStackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
googleStackedColumnChart('chart/category/year/' + year + shared, 'categories');
googleLineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
} }
$(function () { $(function () {
$('.openModal').on('click', openModal); $('.openModal').on('click', openModal);
includeSharedToggle();
$('#includeShared').click(includeSharedSet);
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
}); });
function openModal(e) { function openModal(e) {
@@ -32,31 +38,44 @@ function openModal(e) {
return false; return false;
} }
function includeSharedToggle() {
// get setting from JSON. function showIncomes() {
$.getJSON('json/show-shared-reports').success(function (data) { "use strict";
console.log('GO'); if (incomeRestShow) {
if (data.value == true) { // hide everything, make button say "show"
// show shared data, update button: $('#showIncomes').text(showTheRest);
//<i class="state-icon glyphicon glyphicon-check"></i> $('.incomesCollapsed').removeClass('in').addClass('out');
$('#includeShared').empty().addClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-check')).append(' Include shared asset accounts').show();
console.log('true'); // toggle:
} else { incomeRestShow = false;
$('#includeShared').empty().removeClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-unchecked')).append(' Include shared asset accounts').show(); } else {
console.log('false'); // show everything, make button say "hide".
} $('#showIncomes').text(hideTheRest);
}).fail(function () { $('.incomesCollapsed').removeClass('out').addClass('in');
console.log('fail');
}); // toggle:
incomeRestShow = true;
}
return false;
} }
function includeSharedSet() { function showExpenses() {
// get setting from JSON. if (expenseRestShow) {
$.getJSON('json/show-shared-reports/set').success(function (data) { // hide everything, make button say "show"
console.log('Value is now: ' + data.value); $('#showExpenses').text(showTheRestExpense);
includeSharedToggle(); $('.expenseCollapsed').removeClass('in').addClass('out');
}).fail(function () {
console.log('fail'); // toggle:
}); expenseRestShow = false;
} else {
// show everything, make button say "hide".
$('#showExpenses').text(hideTheRestExpense);
$('.expenseCollapsed').removeClass('out').addClass('in');
// toggle:
expenseRestShow = true;
}
return false; return false;
} }

View File

@@ -0,0 +1,74 @@
<?php
return [
'home' => 'Home',
'asset_accounts' => 'Asset accounts',
'expense_accounts' => 'Expense accounts',
'revenue_accounts' => 'Revenue accounts',
'new_asset_account' => 'New asset accounts',
'new_expense_account' => 'New expense account',
'new_revenue_account' => 'New revenue account',
'delete_account' => 'Delete account ":name"',
'edit_account' => 'Edit account ":name"',
'budgets' => 'Budgets',
'newBudget' => 'Create a new budget',
'delete_budget' => 'Delete budget ":name"',
'edit_budget' => 'Edit budget ":name"',
'categories' => 'Categories',
'newCategory' => 'Create a new categori',
'delete_category' => 'Delete category ":name"',
'edit_category' => 'Edit category ":name"',
'currencies' => 'Currencies',
'edit_currency' => 'Edit currencies ":name"',
'delete_currency' => 'Delete currencies ":name"',
'piggyBanks' => 'Piggy banks',
'newPiggyBank' => 'Create a new piggy bank',
'edit_piggyBank' => 'Edit piggy bank ":name"',
'delete_piggyBank' => 'Delete piggy bank ":name"',
'preferences' => 'Preferences',
'profile' => 'Profile',
'changePassword' => 'Change your password',
'bills' => 'Bills',
'newBill' => 'New bill',
'edit_bill' => 'Edit bill ":name"',
'delete_bill' => 'Delete bill ":name"',
'reminders' => 'Reminders',
'reminder' => 'Reminder #:id',
'reports' => 'Reports',
'monthly_report' => 'Montly report for :date',
'monthly_report_shared' => 'Montly report for :date (including shared accounts)',
'yearly_report' => 'Yearly report for :date',
'yearly_report_shared' => 'Yearly report for :date (including shared accounts)',
'budget_report' => 'Budget report for :date',
'searchResult' => 'Search for ":query"',
'withdrawal_list' => 'Expenses',
'deposit_list' => 'Revenue, income and deposits',
'transfer_list' => 'Transfers',
'transfers_list' => 'Transfers',
'create_withdrawal' => 'Create new withdrawal',
'create_deposit' => 'Create new deposit',
'create_transfer' => 'Create new transfer',
'edit_journal' => 'Edit transaction ":description"',
'delete_journal' => 'Delete transaction ":description"',
'tags' => 'Tags',
'createTag' => 'Create new tag',
'edit_tag' => 'Edit tag ":tag"',
'delete_tag' => 'Delete tag ":tag"',
];

View File

@@ -1,12 +1,161 @@
<?php <?php
return [
'welcome' => 'Welcome to Firefly!',
'mainTitle' => 'What\'s playing?',
'close' => 'Clone',
'pleaseHold' => 'Please hold...',
'mandatoryFields' => 'Mandatory fields',
'optionalFields' => 'Optional fields',
'options' => 'Options',
'something' => 'Something!'
// general fields and things.
return [
'test' => 'You have selected English.',
'close' => 'Close',
'pleaseHold' => 'Please hold...',
'mandatoryFields' => 'Mandatory fields',
'optionalFields' => 'Optional fields',
'options' => 'Options',
'something' => 'Something!',
'actions' => 'Actions',
'edit' => 'Edit',
'delete' => 'Delete',
'welcomeBack' => 'What\'s playing?',
// new user:
'welcome' => 'Welcome to Firefly!',
'createNewAsset' => 'Create a new asset account to get started. This will allow you to create transactions and start your financial management',
'createNewAssetButton' => 'Create new asset account',
// home page:
'yourAccounts' => 'Your accounts',
'budgetsAndSpending' => 'Budgets and spending',
'savings' => 'Savings',
'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel',
'createPiggyToContinue' => 'Create piggy banks to fill this panel.',
'newWithdrawal' => 'New expense',
'newDeposit' => 'New deposit',
'newTransfer' => 'New transfer',
'moneyIn' => 'Money in',
'moneyOut' => 'Money out',
'billsToPay' => 'Bills to pay',
'billsPaid' => 'Bills paid',
'viewDetails' => 'View details',
'divided' => 'divided',
'toDivide' => 'left to divide',
// menu and titles, should be recycled as often as possible:
'toggleNavigation' => 'Toggle navigation',
'seeAllReminders' => 'See all reminders',
'reminders' => 'Reminders',
'currency' => 'Currency',
'preferences' => 'Preferences',
'logout' => 'Logout',
'searchPlaceholder' => 'Search...',
'dashboard' => 'Dashboard',
'currencies' => 'Currencies',
'accounts' => 'Accounts',
'assetAccounts' => 'Asset accounts',
'expenseAccounts' => 'Expense accounts',
'revenueAccounts' => 'Revenue accounts',
'Asset account' => 'Asset account',
'Default account' => 'Asset account',
'Expense account' => 'Expense account',
'Revenue account' => 'Revenue account',
'budgets' => 'Budgets',
'categories' => 'Categories',
'tags' => 'Tags',
'reports' => 'Reports',
'transactions' => 'Transactions',
'expenses' => 'Expenses',
'income' => 'Revenue / income',
'transfers' => 'Transfer',
'moneyManagement' => 'Money management',
'piggyBanks' => 'Piggy banks',
'bills' => 'Bills',
'createNew' => 'Create new',
'withdrawal' => 'Withdrawal',
'deposit' => 'Deposit',
'transfer' => 'Transfer',
'Withdrawal' => 'Withdrawal',
'Deposit' => 'Deposit',
'Transfer' => 'Transfer',
'bill' => 'Rekening',
'yes' => 'Yes',
'no' => 'No',
'amount' => 'Amount',
'newBalance' => 'New balance',
'overview' => 'Overview',
'saveOnAccount' => 'Save on account',
'unknown' => 'Unknown',
'daily' => 'Daily',
'weekly' => 'Weekly',
'monthly' => 'Monthly',
'quarterly' => 'Quarterly',
'half-year' => 'Every six months',
'yearly' => 'Yearly',
'reportForYear' => 'Yearly report for :year',
'reportForYearShared' => 'Yearly report for :year (including shared accounts)',
'reportForMonth' => 'Montly report for :year',
'reportForMonthShared' => 'Montly report for :year (including shared accounts)',
'incomeVsExpenses' => 'Income vs. expenses',
'accountBalances' => 'Account balances',
'balanceStartOfYear' => 'Balance at start of year',
'balanceEndOfYear' => 'Balance at end of year',
'balanceStartOfMonth' => 'Balance at end of month',
'balanceEndOfMonth' => 'Balance at end of month',
'balanceStart' => 'Balance at end of period',
'balanceEnd' => 'Balance at end of period',
'reportsOwnAccounts' => 'Reports for your own accounts',
'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts',
'account' => 'Account',
'splitByAccount' => 'Split by account',
'balancedByTransfersAndTags' => 'Balanced by transfers and tags',
'leftUnbalanced' => 'Left unbalanced',
'expectedBalance' => 'Expected balance',
'outsideOfBudgets' => 'Outside of budgets',
'leftInBudget' => 'Left in budget',
'sumOfSums' => 'Sum of sums',
'notCharged' => 'Not charged (yet)',
'inactive' => 'Inactive',
'difference' => 'Difference',
'in' => 'In',
'out' => 'Out',
'topX' => 'top :number',
'showTheRest' => 'Show everything',
'hideTheRest' => 'Show only the top :number',
// charts:
'dayOfMonth' => 'Day of the month',
'month' => 'Month',
'budget' => 'Budget',
'spent' => 'Spent',
'overspent' => 'Overspent',
'left' => 'Left',
'noCategory' => '(no category)',
'noBudget' => '(no budget)',
'category' => 'Category',
'maxAmount' => 'Maximum amount',
'minAmount' => 'Minumum amount',
'billEntry' => 'Current bill entry',
'name' => 'Name',
'date' => 'Date',
'paid' => 'Paid',
'unpaid' => 'Unpaid',
'day' => 'Day',
'budgeted' => 'Budgeted',
'period' => 'Period',
'balance' => 'Balance',
'summary' => 'Summary',
'sum' => 'Sum',
'average' => 'Average',
'balanceFor' => 'Balance for :name',
'asset_accounts' => 'Asset accounts',
'expense_accounts' => 'Expense accounts',
'revenue_accounts' => 'Revenue accounts',
// some extra help:
'accountExtraHelp_asset' => '',
'accountExtraHelp_expense' => '',
'accountExtraHelp_revenue' => '',
]; ];

View File

@@ -0,0 +1,78 @@
<?php
return [
'name' => 'Name',
'active' => 'Active',
'amount_min' => 'Minimum amount',
'amount_max' => 'Maximum amount',
'match' => 'Matches on',
'repeat_freq' => 'Repeats',
'account_from_id' => 'From account',
'account_to_id' => 'To account',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'tagMode' => 'Tag mode',
'tagPosition' => 'Tag location',
'virtualBalance' => 'Vitual balance',
'longitude_latitude' => 'Location',
'targetamount' => 'Target amount',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank',
'returnHere' => 'Return here',
'returnHereExplanation' => 'After storing, return here to create another one.',
'returnHereUpdateExplanation' => 'After updating, return here.',
'description' => 'Description',
'expense_account' => 'Expense account',
'revenue_account' => 'Revenue account',
'amount' => 'Amount',
'date' => 'Date',
'category' => 'Category',
'tags' => 'Tags',
'deletePermanently' => 'Delete permanently',
'cancel' => 'Cancel',
'targetdate' => 'Target date',
'remind_me' => 'Remind me',
'tag' => 'Tag',
'reminder' => 'Remind me every',
'under' => 'Under',
'store_new_withdrawal' => 'Store new withdrawal',
'store_new_deposit' => 'Store new deposit',
'store_new_transfer' => 'Store new transfer',
'add_new_withdrawal' => 'Add a new withdrawal',
'add_new_deposit' => 'Add a new deposit',
'add_new_transfer' => 'Add a new transfer',
'noPiggybank' => '(no piggy bank)',
'noBudget' => '(no budget)',
'delete_account' => 'Delete account ":name"',
'delete_bill' => 'Delete bill ":name"',
'delete_budget' => 'Delete budget ":name"',
'delete_category' => 'Delete category ":name"',
'delete_currency' => 'Delete currency ":name"',
'delete_piggyBank' => 'Delete piggy banl ":name"',
'delete_journal' => 'Delete transaction with description ":description"',
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?',
'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.' .
'|All :count transactions connected to this account will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.' .
'|All :count piggy bank connected to this account will be deleted as well.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.' .
'|All :count transactions connected to this bill will spared deletion.',
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.' .
'|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' .
'|All :count transactions connected to this category will spared deletion.',
];

View File

@@ -0,0 +1,32 @@
<?php
// all table headers.
return [
'name' => 'Name',
'role' => 'Role',
'currentBalance' => 'Current balance',
'active' => 'Is active?',
'lastActivity' => 'Last activity',
'balanceDiff' => 'Balance difference between :start and :end',
'matchedOn' => 'Matched on',
'matchesOn' => 'Matched on',
'matchingAmount' => 'Amount',
'lastMatch' => 'Last match',
'expectedMatch' => 'Expected match',
'automatch' => 'Automatch?',
'repeat_freq' => 'Repeats',
'description' => 'Description',
'amount' => 'Amount',
'date' => 'Date',
'from' => 'From',
'to' => 'To',
'budget' => 'Budget',
'category' => 'Category',
'bill' => 'Bill',
'withdrawal' => 'Withdrawal',
'deposit' => 'Deposit',
'transfer' => 'Transfer',
'type' => 'Type',
'completed' => 'Completed',
];

View File

@@ -2,18 +2,18 @@
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pagination Language Lines | Pagination Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used by the paginator library to build | The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything | the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application. | you want to customize your views to better match your application.
| |
*/ */
'previous' => '&laquo; Previous', 'previous' => '&laquo; Previous',
'next' => 'Next &raquo;', 'next' => 'Next &raquo;',
]; ];

View File

@@ -2,21 +2,21 @@
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Password Reminder Language Lines | Password Reminder Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are the default lines which match reasons | The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt | that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password. | has failed, such as for an invalid token or invalid new password.
| |
*/ */
"password" => "Passwords must be at least six characters and match the confirmation.", "password" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.", "user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.", "token" => "This password reset token is invalid.",
"sent" => "We have e-mailed your password reset link!", "sent" => "We have e-mailed your password reset link!",
"reset" => "Your password has been reset!", "reset" => "Your password has been reset!",
]; ];

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