diff --git a/app/controllers/ChartController.php b/app/controllers/ChartController.php
index 469cb51c9c..5060f4b531 100644
--- a/app/controllers/ChartController.php
+++ b/app/controllers/ChartController.php
@@ -186,16 +186,26 @@ class ChartController extends BaseController
}
/**
+ * This method gets all transactions within a budget within the period set by the current session
+ * start and end date. It also includes any envelopes which might exist within this period.
+ *
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetSession(\Budget $budget)
{
- $expense = [];
- $repetitionSeries = [];
- $current = clone Session::get('start');
- $end = clone Session::get('end');
+ $series = [];
+ $end = clone Session::get('end');
+ $start = clone Session::get('start');
+
+
+
+ /*
+ * Expenses per day in the session's period. That's easy.
+ */
+ $expense = [];
+ $current = clone Session::get('start');
while ($current <= $end) {
$spent = $this->_chart->spentOnDay($budget, $current);
$spent = floatval($spent) == 0 ? null : floatval($spent);
@@ -203,89 +213,66 @@ class ChartController extends BaseController
$current->addDay();
}
- // find all limit repetitions (for this budget) between start and end.
- $start = clone Session::get('start');
- $repetitionSeries[] = [
+ $series[] = [
'type' => 'column',
'name' => 'Expenses per day',
'data' => $expense
];
+ unset($expense, $spent, $current);
+ /*
+ * Find all limit repetitions (for this budget) between start and end. This is
+ * quite a complex query.
+ */
+ $reps = $this->_chart->limitsInRange($budget, $start, $end);
- /** @var \Limit $limit */
- foreach ($budget->limits as $limit) {
- $reps = $limit->limitrepetitions()->where(
- function ($q) use ($start, $end) {
- // startdate is between range
- $q->where(
- function ($q) use ($start, $end) {
- $q->where('startdate', '>=', $start->format('Y-m-d'));
- $q->where('startdate', '<=', $end->format('Y-m-d'));
- }
+ /*
+ * For each limitrepetition we create a serie that contains the amount left in
+ * the limitrepetition for its entire date-range. Entries are only actually included when they
+ * fall into the charts date range.
+ *
+ * So example: we have a session date from Jan 15 to Jan 30. The limitrepetition starts at 1 Jan until 1 Feb.
+ *
+ * We loop from 1 Jan to 1 Feb but only include Jan 15 / Jan 30. But we do keep count of the amount outside
+ * of these dates because otherwise the line might be wrong.
+ */
+ /** @var \LimitRepetition $repetition */
+ foreach ($reps as $repetition) {
+ $limitAmount = $repetition->limit->amount;
+
+ // create a serie for the repetition.
+ $currentSerie = [
+ 'type' => 'spline',
+ 'id' => 'rep-' . $repetition->id,
+ 'yAxis' => 1,
+ 'name' => 'Envelope #'.$repetition->id.' in ' . $repetition->periodShow(),
+ 'data' => []
+ ];
+ $current = clone $repetition->startdate;
+ while ($current <= $repetition->enddate) {
+ if ($current >= $start && $current <= $end) {
+ // spent on limit:
+ $spentSoFar = $this->_chart->spentOnLimitRepetitionBetweenDates(
+ $repetition, $repetition->startdate, $current
);
+ $leftInLimit = floatval($limitAmount) - floatval($spentSoFar);
- // or enddate is between range.
- $q->orWhere(
- function ($q) use ($start, $end) {
- $q->where('enddate', '>=', $start->format('Y-m-d'));
- $q->where('enddate', '<=', $end->format('Y-m-d'));
- }
- );
+ $currentSerie['data'][] = [$current->timestamp * 1000, $leftInLimit];
}
- )->get();
- $currentLeftInLimit = floatval($limit->amount);
- /** @var \LimitRepetition $repetition */
- foreach ($reps as $repetition) {
- // create a serie for the repetition.
- $currentSerie = [
- 'type' => 'spline',
- 'id' => 'rep-' . $repetition->id,
- 'yAxis' => 1,
- 'name' => 'Envelope in ' . $repetition->periodShow(),
- 'data' => []
- ];
- $current = clone $repetition->startdate;
- while ($current <= $repetition->enddate) {
- if ($current >= Session::get('start') && $current <= Session::get('end')) {
- // spent on limit:
-
- $spentSoFar = \Transaction::
- leftJoin(
- 'transaction_journals', 'transaction_journals.id', '=',
- 'transactions.transaction_journal_id'
- )
- ->leftJoin(
- 'component_transaction_journal', 'component_transaction_journal.transaction_journal_id',
- '=',
- 'transaction_journals.id'
- )->where('component_transaction_journal.component_id', '=', $budget->id)->where(
- 'transaction_journals.date', '>=', $repetition->startdate->format('Y-m-d')
- )->where('transaction_journals.date', '<=', $current->format('Y-m-d'))->where(
- 'amount', '>', 0
- )->sum('amount');
- $spent = floatval($spent) == 0 ? null : floatval($spent);
- $currentLeftInLimit = floatval($limit->amount) - floatval($spentSoFar);
-
- $currentSerie['data'][] = [$current->timestamp * 1000, $currentLeftInLimit];
- }
- $current->addDay();
- }
-
- // do something here.
- $repetitionSeries[] = $currentSerie;
-
+ $current->addDay();
}
+ // do something here.
+ $series[] = $currentSerie;
}
-
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format(
'M jS, Y'
),
- 'series' => $repetitionSeries
+ 'series' => $series
];
return Response::json($return);
diff --git a/app/lib/Firefly/Helper/Controllers/Chart.php b/app/lib/Firefly/Helper/Controllers/Chart.php
index b0ae152f5a..2866e5a841 100644
--- a/app/lib/Firefly/Helper/Controllers/Chart.php
+++ b/app/lib/Firefly/Helper/Controllers/Chart.php
@@ -4,6 +4,7 @@ namespace Firefly\Helper\Controllers;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
+use Illuminate\Support\Collection;
/**
* Class Chart
@@ -117,6 +118,7 @@ class Chart implements ChartInterface
$limitInPeriod = '';
$spentInPeriod = '';
+ /** @var \Budget $budget */
foreach ($budgets as $budget) {
$budget->count = 0;
foreach ($budget->limits as $limit) {
@@ -144,6 +146,23 @@ class Chart implements ChartInterface
}
$budget->count += count($limit->limitrepetitions);
}
+ if ($budget->count == 0) {
+ // get expenses in period until today, starting at $start.
+ $end = \Session::get('end');
+ $expenses = $budget->transactionjournals()->after($start)->before($end)
+ ->transactionTypes(
+ ['Withdrawal']
+ )->get();
+ $budget->spentInPeriod = 0;
+ /** @var \TransactionJournal $expense */
+ foreach ($expenses as $expense) {
+ $transaction = $expense->transactions[1];
+ if (!is_null($transaction)) {
+ $budget->spentInPeriod += floatval($transaction->amount);
+ }
+ }
+
+ }
}
@@ -162,15 +181,24 @@ class Chart implements ChartInterface
foreach ($budgets as $budget) {
if ($budget->count > 0) {
$data['labels'][] = wordwrap($budget->name, 12, "
");
- }
- foreach ($budget->limits as $limit) {
- foreach ($limit->limitrepetitions as $rep) {
- //0: envelope for period:
- $amount = floatval($rep->amount);
- $spent = $rep->spent;
- $color = $spent > $amount ? '#FF0000' : null;
- $data['series'][0]['data'][] = ['y' => $amount, 'id' => 'amount-' . $rep->id];
- $data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color, 'id' => 'spent-' . $rep->id];
+ foreach ($budget->limits as $limit) {
+ foreach ($limit->limitrepetitions as $rep) {
+ //0: envelope for period:
+ $amount = floatval($rep->amount);
+ $spent = $rep->spent;
+ $color = $spent > $amount ? '#FF0000' : null;
+ $data['series'][0]['data'][] = ['y' => $amount, 'id' => 'amount-' . $rep->id];
+ $data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color,
+ 'id' => 'spent-' . $rep->id];
+ }
+ }
+ } else {
+ // add for "empty" budget:
+ if ($budget->spentInPeriod > 0) {
+ $data['labels'][] = wordwrap($budget->name, 12, "
");
+ $data['series'][0]['data'][] = ['y' => null, 'id' => 'amount-norep-' . $budget->id];
+ $data['series'][1]['data'][] = ['y' => $budget->spentInPeriod,
+ 'id' => 'spent-norep-' . $budget->id];
}
}
@@ -482,5 +510,69 @@ class Chart implements ChartInterface
return $transactions;
}
+ /**
+ * Get all limit (LimitRepetitions) for a budget falling in a certain date range.
+ *
+ * @param \Budget $budget
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return Collection
+ */
+ public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end)
+ {
+ $reps = new Collection;
+ /** @var \Limit $limit */
+ foreach ($budget->limits as $limit) {
+ $set = $limit->limitrepetitions()->where(
+ function ($q) use ($start, $end) {
+ // startdate is between range
+ $q->where(
+ function ($q) use ($start, $end) {
+ $q->where('startdate', '>=', $start->format('Y-m-d'));
+ $q->where('startdate', '<=', $end->format('Y-m-d'));
+ }
+ );
+
+ // or enddate is between range.
+ $q->orWhere(
+ function ($q) use ($start, $end) {
+ $q->where('enddate', '>=', $start->format('Y-m-d'));
+ $q->where('enddate', '<=', $end->format('Y-m-d'));
+ }
+ );
+ }
+ )->get();
+
+ $reps = $reps->merge($set);
+ }
+ return $reps;
+ }
+
+ /**
+ * We check how much money has been spend on the limitrepetition (aka: the current envelope) in the period denoted.
+ * Aka, we have a certain amount of money in an envelope and we wish to know how much we've spent between the dates
+ * entered. This can be a partial match with the date range of the envelope or no match at all.
+ *
+ * @param \LimitRepetition $repetition
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return mixed
+ */
+ public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end) {
+ return floatval(
+ \Transaction::
+ leftJoin('transaction_journals', 'transaction_journals.id', '=','transactions.transaction_journal_id')
+ ->leftJoin('component_transaction_journal', 'component_transaction_journal.transaction_journal_id','=',
+ 'transaction_journals.id'
+ )->where('component_transaction_journal.component_id', '=', $repetition->limit->budget->id)->where(
+ 'transaction_journals.date', '>=', $start->format('Y-m-d')
+ )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->where(
+ 'amount', '>', 0
+ )->sum('amount')) ;
+ }
+
+
}
\ No newline at end of file
diff --git a/app/lib/Firefly/Helper/Controllers/ChartInterface.php b/app/lib/Firefly/Helper/Controllers/ChartInterface.php
index c75dbfab53..ebc42b16ad 100644
--- a/app/lib/Firefly/Helper/Controllers/ChartInterface.php
+++ b/app/lib/Firefly/Helper/Controllers/ChartInterface.php
@@ -85,7 +85,30 @@ interface ChartInterface
*/
public function transactionsByJournals(array $set);
+ /**
+ * Get all limit (LimitRepetitions) for a budget falling in a certain date range.
+ *
+ * @param \Budget $budget
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return Collection
+ */
+ public function limitsInRange(\Budget $budget, Carbon $start, Carbon $end);
+ /**
+ * We check how much money has been spend on the limitrepetition (aka: the current envelope) in the period denoted.
+ * Aka, we have a certain amount of money in an envelope and we wish to know how much we've spent between the dates
+ * entered. This can be a partial match with the date range of the envelope or no match at all.
+ *
+ * @param \LimitRepetition $repetition
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return mixed
+ */
+ public function spentOnLimitRepetitionBetweenDates(\LimitRepetition $repetition, Carbon $start, Carbon $end);
+
}
\ No newline at end of file