diff --git a/app/Http/Controllers/GoogleChartController.php b/app/Http/Controllers/GoogleChartController.php index e3117a6616..37566986b9 100644 --- a/app/Http/Controllers/GoogleChartController.php +++ b/app/Http/Controllers/GoogleChartController.php @@ -1,6 +1,5 @@ addColumn('Month', 'date'); $chart->addColumn('Budgeted', 'number'); $chart->addColumn('Spent', 'number'); - if ($year == 0) { - // grab the first budgetlimit ever: - $firstLimit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first(); - if ($firstLimit) { - $start = new Carbon($firstLimit->startdate); - } else { - $start = Carbon::now()->startOfYear(); - } - // grab the last budget limit ever: - $lastLimit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first(); - if ($lastLimit) { - $end = new Carbon($lastLimit->startdate); - } else { - $end = Carbon::now()->endOfYear(); - } + if ($year == 0) { + $start = $repository->getFirstBudgetLimitDate($budget); + $end = $repository->getLastBudgetLimitDate($budget); } else { $start = Carbon::createFromDate(intval($year), 1, 1); $end = clone $start; @@ -386,19 +371,8 @@ class GoogleChartController extends Controller } while ($start <= $end) { - $spent = $repository->spentInMonth($budget, $start); - $repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') - ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00')) - ->where('budget_limits.budget_id', $budget->id) - ->first(['limit_repetitions.*']); - - if ($repetition) { - $budgeted = floatval($repetition->amount); - \Log::debug('Found a repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name . '!'); - } else { - \Log::debug('No repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name); - $budgeted = null; - } + $spent = $repository->spentInMonth($budget, $start); + $budgeted = $repository->getLimitAmountOnDate($budget, $start); $chart->addRow(clone $start, $budgeted, $spent); $start->addMonth(); } @@ -416,12 +390,11 @@ class GoogleChartController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function categoryOverviewChart(Category $category, GChart $chart) + public function categoryOverviewChart(Category $category, GChart $chart, CategoryRepositoryInterface $repository) { // oldest transaction in category: - /** @var TransactionJournal $first */ - $first = $category->transactionjournals()->orderBy('date', 'ASC')->first(); - $start = $first->date; + $start = $repository->getFirstActivityDate($category); + /** @var Preference $range */ $range = Preferences::get('viewRange', '1M'); // jump to start of week / month / year / etc (TODO). @@ -434,13 +407,12 @@ class GoogleChartController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range->data); - $spent = floatval($category->transactionjournals()->before($currentEnd)->after($start)->lessThan(0)->sum('amount')) * -1; + $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()); @@ -454,17 +426,15 @@ class GoogleChartController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function categoryPeriodChart(Category $category, GChart $chart) + public function categoryPeriodChart(Category $category, GChart $chart, CategoryRepositoryInterface $repository) { - // oldest transaction in category: - /** @var TransactionJournal $first */ - $start = clone Session::get('start'); + $start = clone Session::get('start', Carbon::now()->startOfMonth()); $chart->addColumn('Period', 'date'); $chart->addColumn('Spent', 'number'); - $end = Session::get('end'); + $end = Session::get('end', Carbon::now()->endOfMonth()); while ($start <= $end) { - $spent = floatval($category->transactionjournals()->onDate($start)->lessThan(0)->sum('amount')) * -1; + $spent = $repository->spentOnDaySum($category, $start); $chart->addRow(clone $start, $spent); $start->addDay(); } @@ -482,13 +452,13 @@ class GoogleChartController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function piggyBankHistory(PiggyBank $piggyBank, GChart $chart) + public function piggyBankHistory(PiggyBank $piggyBank, GChart $chart, PiggyBankRepositoryInterface $repository) { $chart->addColumn('Date', 'date'); $chart->addColumn('Balance', 'number'); /** @var Collection $set */ - $set = DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); + $set = $repository->getEventSummarySet($piggyBank); $sum = 0; foreach ($set as $entry) { diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 21838900e4..a7b80cbf24 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -7,9 +7,9 @@ use Carbon\Carbon; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\LimitRepetition; +use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use Illuminate\Database\Query\Builder as QueryBuilder; /** * Class BudgetRepository @@ -56,6 +56,17 @@ class BudgetRepository implements BudgetRepositoryInterface return true; } + /** + * @param Budget $budget + * @param Carbon $date + * + * @return float + */ + public function expensesOnDay(Budget $budget, Carbon $date) + { + return floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($date)->sum('amount')); + } + /** * @return Collection */ @@ -117,6 +128,21 @@ class BudgetRepository implements BudgetRepositoryInterface return $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']); } + /** + * @param Budget $budget + * + * @return Carbon + */ + public function getFirstBudgetLimitDate(Budget $budget) + { + $limit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first(); + if ($limit) { + return $limit->startdate; + } + + return Carbon::now()->startOfYear(); + } + /** * @return Collection */ @@ -158,6 +184,41 @@ class BudgetRepository implements BudgetRepositoryInterface return new LengthAwarePaginator($set, $count, $take, $offset); } + /** + * @param Budget $budget + * + * @return Carbon + */ + public function getLastBudgetLimitDate(Budget $budget) + { + $limit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first(); + if ($limit) { + return $limit->startdate; + } + + return Carbon::now()->startOfYear(); + } + + /** + * @param Budget $budget + * @param Carbon $date + * + * @return float|null + */ + public function getLimitAmountOnDate(Budget $budget, Carbon $date) + { + $repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') + ->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00')) + ->where('budget_limits.budget_id', $budget->id) + ->first(['limit_repetitions.*']); + + if ($repetition) { + return floatval($repetition->amount); + } + + return null; + } + /** * @param Carbon $start * @param Carbon $end @@ -204,6 +265,7 @@ class BudgetRepository implements BudgetRepositoryInterface ->lessThan(0) ->transactionTypes(['Withdrawal']) ->get(); + return floatval($noBudgetSet->sum('amount')) * -1; } @@ -308,15 +370,4 @@ class BudgetRepository implements BudgetRepositoryInterface } - - /** - * @param Budget $budget - * @param Carbon $date - * - * @return float - */ - public function expensesOnDay(Budget $budget, Carbon $date) - { - return floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($date)->sum('amount')); - } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index eb61d7370d..d2c0cd0bed 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -26,6 +26,14 @@ interface BudgetRepositoryInterface */ public function destroy(Budget $budget); + /** + * @param Budget $budget + * @param Carbon $date + * + * @return float + */ + public function expensesOnDay(Budget $budget, Carbon $date); + /** * @return Collection */ @@ -60,6 +68,13 @@ interface BudgetRepositoryInterface */ public function getCurrentRepetition(Budget $budget, Carbon $date); + /** + * @param Budget $budget + * + * @return Carbon + */ + public function getFirstBudgetLimitDate(Budget $budget); + /** * @return Collection */ @@ -76,6 +91,21 @@ interface BudgetRepositoryInterface */ public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50); + /** + * @param Budget $budget + * + * @return Carbon + */ + public function getLastBudgetLimitDate(Budget $budget); + + /** + * @param Budget $budget + * @param Carbon $date + * + * @return float + */ + public function getLimitAmountOnDate(Budget $budget, Carbon $date); + /** * @param Carbon $start * @param Carbon $end @@ -107,14 +137,6 @@ interface BudgetRepositoryInterface */ public function store(array $data); - /** - * @param Budget $budget - * @param Carbon $date - * - * @return float - */ - public function expensesOnDay(Budget $budget, Carbon $date); - /** * @param Budget $budget * @param Carbon $start diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 75f04b5ad7..1266d71b50 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -79,6 +79,23 @@ class CategoryRepository implements CategoryRepositoryInterface ->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]); } + /** + * @param Category $category + * + * @return Carbon + */ + public function getFirstActivityDate(Category $category) + { + /** @var TransactionJournal $first */ + $first = $category->transactionjournals()->orderBy('date', 'ASC')->first(); + if ($first) { + return $first->date; + } + + return new Carbon; + + } + /** * @param Category $category * @param int $page @@ -138,6 +155,18 @@ class CategoryRepository implements CategoryRepositoryInterface ->get(['transaction_journals.*']); } + /** + * @param Category $category + * @param Carbon $start + * @param Carbon $end + * + * @return float + */ + public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end) + { + return floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; + } + /** * @param array $data * @@ -170,4 +199,15 @@ class CategoryRepository implements CategoryRepositoryInterface return $category; } + + /** + * @param Category $category + * @param Carbon $date + * + * @return float + */ + public function spentOnDaySum(Category $category, Carbon $date) + { + return floatval($category->transactionjournals()->onDate($date)->lessThan(0)->sum('amount')) * -1; + } } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 7faa595b60..5b29845daf 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -40,6 +40,13 @@ interface CategoryRepositoryInterface */ public function getCategoriesAndExpenses($start, $end); + /** + * @param Category $category + * + * @return Carbon + */ + public function getFirstActivityDate(Category $category); + /** * @param Category $category * @param int $page @@ -63,6 +70,23 @@ interface CategoryRepositoryInterface */ public function getWithoutCategory(Carbon $start, Carbon $end); + /** + * @param Category $category + * @param Carbon $start + * @param Carbon $end + * + * @return float + */ + public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end); + + /** + * @param Category $category + * @param Carbon $date + * + * @return float + */ + public function spentOnDaySum(Category $category, Carbon $date); + /** * @param array $data * diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index fb381ba1c1..0c95d0ab2f 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -86,6 +86,16 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $part; } + /** + * @param PiggyBank $piggyBank + * + * @return Collection + */ + public function getEventSummarySet(PiggyBank $piggyBank) + { + return DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); + } + /** * Set all piggy banks to order 0. * diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index 13afce327d..4bfe3d8cab 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -35,6 +35,13 @@ interface PiggyBankRepositoryInterface */ public function createPiggyBankPart(array $data); + /** + * @param PiggyBank $piggyBank + * + * @return Collection + */ + public function getEventSummarySet(PiggyBank $piggyBank); + /** * Set all piggy banks to order 0. * diff --git a/tests/controllers/GoogleChartControllerTest.php b/tests/controllers/GoogleChartControllerTest.php index 5637734821..612b2d71f5 100644 --- a/tests/controllers/GoogleChartControllerTest.php +++ b/tests/controllers/GoogleChartControllerTest.php @@ -190,8 +190,10 @@ class GoogleChartControllerTest extends TestCase public function testBudgetLimitSpending() { - $repetition = FactoryMuffin::create('FireflyIII\Models\LimitRepetition'); - $budget = $repetition->budgetlimit->budget; + $repetition = FactoryMuffin::create('FireflyIII\Models\LimitRepetition'); + $repetition->startdate = Carbon::now()->startOfMonth(); + $repetition->enddate = Carbon::now()->endOfMonth(); + $budget = $repetition->budgetlimit->budget; $this->be($budget->user); ///chart/budget/{budget}/{limitrepetition} @@ -206,22 +208,86 @@ class GoogleChartControllerTest extends TestCase public function testBudgetsAndSpending() { - $this->markTestIncomplete(); + $budget = FactoryMuffin::create('FireflyIII\Models\Budget'); + $this->be($budget->user); + + $repository = $this->mock('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $repository->shouldReceive('spentInMonth')->andReturn(100); + $repository->shouldReceive('getLimitAmountOnDate')->andReturn(100); + $repository->shouldReceive('getFirstBudgetLimitDate')->andReturn(Carbon::now()->startOfMonth()); + $repository->shouldReceive('getLastBudgetLimitDate')->andReturn(Carbon::now()->endOfYear()); + + // /chart/budget/{budget}/spending/{year?} + $this->call('GET', '/chart/budget/' . $budget->id . '/spending/0'); + $this->assertResponseOk(); + } + + public function testBudgetsAndSpendingWithYear() + { + $budget = FactoryMuffin::create('FireflyIII\Models\Budget'); + $this->be($budget->user); + + $repository = $this->mock('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $repository->shouldReceive('spentInMonth')->andReturn(100); + $repository->shouldReceive('getLimitAmountOnDate')->andReturn(100); + + // /chart/budget/{budget}/spending/{year?} + $this->call('GET', '/chart/budget/' . $budget->id . '/spending/2015'); + $this->assertResponseOk(); } public function testCategoryOverviewChart() { - $this->markTestIncomplete(); + $category = FactoryMuffin::create('FireflyIII\Models\Category'); + $pref = FactoryMuffin::create('FireflyIII\Models\Preference'); + $this->be($category->user); + $start = new Carbon(); + $start->subDay(); + $end = new Carbon; + $end->addWeek(); + + // mock! + $repository = $this->mock('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); + $repository->shouldReceive('getFirstActivityDate')->andReturn($start); + $repository->shouldReceive('spentInPeriodSum')->andReturn(rand(1, 100)); + Preferences::shouldReceive('get')->andReturn($pref); + + Navigation::shouldReceive('startOfPeriod')->andReturn($start); + Navigation::shouldReceive('endOfPeriod')->andReturn($start); + Navigation::shouldReceive('addPeriod')->andReturn($end); + + $this->call('GET', '/chart/category/' . $category->id . '/overview'); + $this->assertResponseOk(); } public function testCategoryPeriodChart() { - $this->markTestIncomplete(); + $category = FactoryMuffin::create('FireflyIII\Models\Category'); + $this->be($category->user); + + // mock! + $repository = $this->mock('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); + $repository->shouldReceive('spentOnDaySum')->andReturn(rand(1, 100)); + + $this->call('GET', '/chart/category/' . $category->id . '/period'); + $this->assertResponseOk(); } public function testPiggyBankHistory() { - $this->markTestIncomplete(); + $piggyBank = FactoryMuffin::create('FireflyIII\Models\PiggyBank'); + $this->be($piggyBank->account->user); + + $obj = new stdClass; + $obj->sum = 12; + $obj->date = new Carbon; + $collection = new Collection([$obj]); + + $repository = $this->mock('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface'); + $repository->shouldReceive('getEventSummarySet')->andReturn($collection); + + $this->call('GET', '/chart/piggy-history/' . $piggyBank->id); + $this->assertResponseOk(); } public function testYearInExp() diff --git a/tests/factories/all.php b/tests/factories/all.php index d551718cdb..16d2b9ee74 100644 --- a/tests/factories/all.php +++ b/tests/factories/all.php @@ -160,6 +160,20 @@ FactoryMuffin::define( ] ); +FactoryMuffin::define( + 'FireflyIII\Models\PiggyBank', + [ + 'account_id' => 'factory|FireflyIII\Models\Account', + 'name' => 'sentence', + 'targetamount' => 'integer', + 'startdate' => 'date', + 'targetdate' => 'date', + 'reminder_skip' => 0, + 'remind_me' => 0, + 'order' => 0, + ] +); + FactoryMuffin::define( 'FireflyIII\Models\TransactionType', [