From 4fdd2c851f3a57d12bc4e60f4614dc430cf01722 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 14:23:50 +0100 Subject: [PATCH 01/11] Fix registration. --- tests/acceptance/Controllers/Auth/AuthControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/Controllers/Auth/AuthControllerTest.php b/tests/acceptance/Controllers/Auth/AuthControllerTest.php index 29d957f17d..da8cd983fa 100644 --- a/tests/acceptance/Controllers/Auth/AuthControllerTest.php +++ b/tests/acceptance/Controllers/Auth/AuthControllerTest.php @@ -83,7 +83,6 @@ class AuthControllerTest extends TestCase $this->assertEquals(200, $response->status()); - } /** @@ -95,6 +94,7 @@ class AuthControllerTest extends TestCase 'email' => 'thegrumpydictator+test@gmail.com', 'password' => 'james123', 'password_confirmation' => 'james123', + '_token' => Session::token(), ]; $response = $this->call('POST', '/register', $args); $this->assertEquals(302, $response->status()); From 6938f8ab89c603676332aa16857aa058c0c67a92 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 15:14:31 +0100 Subject: [PATCH 02/11] I just realised this still said "watch movie". --- resources/views/emails/registered-html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/emails/registered-html.twig b/resources/views/emails/registered-html.twig index 11516a4c57..683ca0737a 100644 --- a/resources/views/emails/registered-html.twig +++ b/resources/views/emails/registered-html.twig @@ -49,7 +49,7 @@ "potentialAction": { "@type": "ViewAction", "target": "https://geld.nder.be/", - "name": "Watch movie" + "name": "Visit Firefly III" }, "publisher": { "@type": "Organization", From c84f4e2bc07a7ffdd8eef3f9f2cbc4b1c398951f Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 15:21:27 +0100 Subject: [PATCH 03/11] Sort by alphabet. --- app/Repositories/Bill/BillRepository.php | 317 ++++--- .../Bill/BillRepositoryInterface.php | 82 +- app/Repositories/Budget/BudgetRepository.php | 823 +++++++++--------- .../Budget/BudgetRepositoryInterface.php | 179 ++-- .../Category/CategoryRepository.php | 198 +++-- 5 files changed, 796 insertions(+), 803 deletions(-) diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index daa5a2a698..075065b77f 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -34,6 +34,24 @@ class BillRepository implements BillRepositoryInterface return $bill->delete(); } + /** + * @return Collection + */ + public function getActiveBills() + { + /** @var Collection $set */ + $set = Auth::user()->bills() + ->where('active', 1) + ->get( + [ + 'bills.*', + DB::Raw('(`bills`.`amount_min` + `bills`.`amount_max` / 2) as `expectedAmount`'), + ] + )->sortBy('name'); + + return $set; + } + /** * Returns all journals connected to these bills in the given range. Amount paid * is stored in "journalAmount" as a negative number. @@ -68,7 +86,6 @@ class BillRepository implements BillRepositoryInterface return $set; } - /** * @return Collection */ @@ -125,6 +142,127 @@ class BillRepository implements BillRepositoryInterface return $set; } + /** + * Get the total amount of money paid for the users active bills in the date range given. + * This amount will be negative (they're expenses). + * + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function getBillsPaidInRange(Carbon $start, Carbon $end) + { + $amount = '0'; + $bills = $this->getActiveBills(); + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $ranges = $this->getRanges($bill, $start, $end); + + foreach ($ranges as $range) { + $paid = $bill->transactionjournals() + ->before($range['end']) + ->after($range['start']) + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); + } + ) + ->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); + $amount = bcadd($amount, $paid->sum_amount); + } + } + + return $amount; + } + + /** + * Get the total amount of money due for the users active bills in the date range given. This amount will be positive. + * + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function getBillsUnpaidInRange(Carbon $start, Carbon $end) + { + $amount = '0'; + $bills = $this->getActiveBills(); + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $ranges = $this->getRanges($bill, $start, $end); + $paidBill = '0'; + foreach ($ranges as $range) { + $paid = $bill->transactionjournals() + ->before($range['end']) + ->after($range['start']) + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0); + } + ) + ->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); + $paidBill = bcadd($paid->sum_amount, $paidBill); + } + if ($paidBill == 0) { + $amount = bcadd($amount, $bill->expectedAmount); + } + } + + return $amount; + } + + /** + * This method will tell you if you still have a CC bill to pay. Amount will be positive if the amount + * has been paid, otherwise it will be negative. + * + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function getCreditCardBill(Carbon $start, Carbon $end) + { + + /** @var AccountRepositoryInterface $accountRepository */ + $accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface'); + $amount = '0'; + $creditCards = $accountRepository->getCreditCards($end); // Find credit card accounts and possibly unpaid credit card bills. + /** @var Account $creditCard */ + foreach ($creditCards as $creditCard) { + if ($creditCard->balance == 0) { + // find a transfer TO the credit card which should account for anything paid. If not, the CC is not yet used. + $set = TransactionJournal::whereIn( + 'transaction_journals.id', function (Builder $q) use ($creditCard, $start, $end) { + $q->select('transaction_journals.id') + ->from('transactions') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->where('transactions.account_id', $creditCard->id) + ->where('transactions.amount', '>', 0)// this makes the filter unnecessary. + ->where('transaction_journals.user_id', Auth::user()->id) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->where('transaction_types.type', TransactionType::TRANSFER); + } + )->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0); + } + )->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); + + $amount = bcadd($amount, $set->sum_amount); + } else { + $amount = bcadd($amount, $creditCard->balance); + } + } + + return $amount; + + } + /** * This method also returns the amount of the journal in "journalAmount" * for easy access. @@ -173,8 +311,9 @@ class BillRepository implements BillRepositoryInterface */ public function getPossiblyRelatedJournals(Bill $bill) { - $set = DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get( - ['transaction_journal_id'] + $set = new Collection( + DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max) + ->get(['transaction_journal_id']) ); $ids = $set->pluck('transaction_journal_id')->toArray(); @@ -392,6 +531,22 @@ class BillRepository implements BillRepositoryInterface return $bill; } + /** + * @param float $amount + * @param float $min + * @param float $max + * + * @return bool + */ + protected function doAmountMatch($amount, $min, $max) + { + if ($amount >= $min && $amount <= $max) { + return true; + } + + return false; + } + /** * @param array $matches * @param $description @@ -413,160 +568,4 @@ class BillRepository implements BillRepositoryInterface return $wordMatch; } - - /** - * @param float $amount - * @param float $min - * @param float $max - * - * @return bool - */ - protected function doAmountMatch($amount, $min, $max) - { - if ($amount >= $min && $amount <= $max) { - return true; - } - - return false; - } - - /** - * Get the total amount of money paid for the users active bills in the date range given. - * This amount will be negative (they're expenses). - * - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function getBillsPaidInRange(Carbon $start, Carbon $end) - { - $amount = '0'; - $bills = $this->getActiveBills(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $ranges = $this->getRanges($bill, $start, $end); - - foreach ($ranges as $range) { - $paid = $bill->transactionjournals() - ->before($range['end']) - ->after($range['start']) - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); - } - ) - ->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); - $amount = bcadd($amount, $paid->sum_amount); - } - } - - return $amount; - } - - /** - * @return Collection - */ - public function getActiveBills() - { - /** @var Collection $set */ - $set = Auth::user()->bills() - ->where('active', 1) - ->get( - [ - 'bills.*', - DB::Raw('(`bills`.`amount_min` + `bills`.`amount_max` / 2) as `expectedAmount`'), - ] - )->sortBy('name'); - - return $set; - } - - - /** - * Get the total amount of money due for the users active bills in the date range given. This amount will be positive. - * - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function getBillsUnpaidInRange(Carbon $start, Carbon $end) - { - $amount = '0'; - $bills = $this->getActiveBills(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $ranges = $this->getRanges($bill, $start, $end); - $paidBill = '0'; - foreach ($ranges as $range) { - $paid = $bill->transactionjournals() - ->before($range['end']) - ->after($range['start']) - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0); - } - ) - ->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); - $paidBill = bcadd($paid->sum_amount, $paidBill); - } - if ($paidBill == 0) { - $amount = bcadd($amount, $bill->expectedAmount); - } - } - - return $amount; - } - - /** - * This method will tell you if you still have a CC bill to pay. Amount will be positive if the amount - * has been paid, otherwise it will be negative. - * - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function getCreditCardBill(Carbon $start, Carbon $end) - { - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface'); - $amount = '0'; - $creditCards = $accountRepository->getCreditCards($end); // Find credit card accounts and possibly unpaid credit card bills. - /** @var Account $creditCard */ - foreach ($creditCards as $creditCard) { - if ($creditCard->balance == 0) { - // find a transfer TO the credit card which should account for anything paid. If not, the CC is not yet used. - $set = TransactionJournal::whereIn( - 'transaction_journals.id', function (Builder $q) use ($creditCard, $start, $end) { - $q->select('transaction_journals.id') - ->from('transactions') - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->where('transactions.account_id', $creditCard->id) - ->where('transactions.amount', '>', 0)// this makes the filter unnecessary. - ->where('transaction_journals.user_id', Auth::user()->id) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where('transaction_types.type', TransactionType::TRANSFER); - } - )->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0); - } - )->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]); - - $amount = bcadd($amount, $set->sum_amount); - } else { - $amount = bcadd($amount, $creditCard->balance); - } - } - - return $amount; - - } } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 0cf52754bf..0cefc960c8 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -16,15 +16,42 @@ interface BillRepositoryInterface { /** - * This method will tell you if you still have a CC bill to pay. Amount will be negative if the amount - * has been paid + * @param Bill $bill * - * @param Carbon $start - * @param Carbon $end - * - * @return string + * @return mixed */ - public function getCreditCardBill(Carbon $start, Carbon $end); + public function destroy(Bill $bill); + + /** + * @return Collection + */ + public function getActiveBills(); + + /** + * Returns all journals connected to these bills in the given range. Amount paid + * is stored in "journalAmount" as a negative number. + * + * @param Collection $bills + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end); + + /** + * @return Collection + */ + public function getBills(); + + /** + * Gets the bills which have some kind of relevance to the accounts mentioned. + * + * @param Collection $accounts + * + * @return Collection + */ + public function getBillsForAccounts(Collection $accounts); /** * Get the total amount of money paid for the users active bills in the date range given. @@ -47,44 +74,15 @@ interface BillRepositoryInterface public function getBillsUnpaidInRange(Carbon $start, Carbon $end); /** - * @return Collection - */ - public function getActiveBills(); - - - /** - * @param Bill $bill + * This method will tell you if you still have a CC bill to pay. Amount will be negative if the amount + * has been paid * - * @return mixed - */ - public function destroy(Bill $bill); - - /** - * Returns all journals connected to these bills in the given range. Amount paid - * is stored in "journalAmount" as a negative number. + * @param Carbon $start + * @param Carbon $end * - * @param Collection $bills - * @param Carbon $start - * @param Carbon $end - * - * @return Collection + * @return string */ - public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end); - - - /** - * @return Collection - */ - public function getBills(); - - /** - * Gets the bills which have some kind of relevance to the accounts mentioned. - * - * @param Collection $accounts - * - * @return Collection - */ - public function getBillsForAccounts(Collection $accounts); + public function getCreditCardBill(Carbon $start, Carbon $end); /** * @param Bill $bill diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 715665e372..dbf9d47c31 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -25,6 +25,19 @@ use Input; class BudgetRepository extends ComponentRepository implements BudgetRepositoryInterface { + /** + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + */ + public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts) + { + return $this->commonBalanceInPeriod($budget, $start, $end, $accounts); + } + /** * @return void */ @@ -35,6 +48,299 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn } + /** + * @param Budget $budget + * + * @return boolean + */ + public function destroy(Budget $budget) + { + $budget->delete(); + + return true; + } + + /** + * @param Budget $budget + * + * @return Carbon + */ + public function firstActivity(Budget $budget) + { + $first = $budget->transactionjournals()->orderBy('date', 'ASC')->first(); + if ($first) { + return $first->date; + } + + return new Carbon; + } + + /** + * @return Collection + */ + public function getActiveBudgets() + { + /** @var Collection $set */ + $set = Auth::user()->budgets()->where('active', 1)->get(); + + $set = $set->sortBy( + function (Budget $budget) { + return strtolower($budget->name); + } + ); + + return $set; + } + + /** + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end) + { + /** @var Collection $repetitions */ + return LimitRepetition:: + leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') + ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00')) + ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00')) + ->where('budgets.user_id', Auth::user()->id) + ->get(['limit_repetitions.*', 'budget_limits.budget_id']); + } + + /** + * Get the budgeted amounts for each budgets in each year. + * + * @param Collection $budgets + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end) + { + $budgetIds = $budgets->pluck('id')->toArray(); + + $set = Auth::user()->budgets() + ->leftJoin('budget_limits', 'budgets.id', '=', 'budget_limits.budget_id') + ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') + ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')) + ->where('limit_repetitions.enddate', '<=', $end->format('Y-m-d')) + ->groupBy('budgets.id') + ->groupBy('dateFormatted') + ->whereIn('budgets.id', $budgetIds) + ->get( + [ + 'budgets.*', + DB::Raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'), + DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`'), + ] + ); + + return $set; + } + + /** + * @return Collection + */ + public function getBudgets() + { + /** @var Collection $set */ + $set = Auth::user()->budgets()->get(); + + $set = $set->sortBy( + function (Budget $budget) { + return strtolower($budget->name); + } + ); + + return $set; + } + + /** + * Returns an array with every budget in it and the expenses for each budget + * per month. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + + /** @var Collection $set */ + $set = Auth::user()->budgets() + ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); + } + ) + ->groupBy('budgets.id') + ->groupBy('dateFormatted') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->whereIn('transactions.account_id', $ids) + ->get( + [ + 'budgets.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), + DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), + ] + ); + + $set = $set->sortBy( + function (Budget $budget) { + return strtolower($budget->name); + } + ); + + $return = []; + foreach ($set as $budget) { + $id = $budget->id; + if (!isset($return[$id])) { + $return[$id] = [ + 'budget' => $budget, + 'entries' => [], + ]; + } + // store each entry: + $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; + } + + return $return; + } + + /** + * Returns an array with every budget in it and the expenses for each budget + * per year for. + * + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's a query. + * + * @return array + */ + public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $budgetIds = $budgets->pluck('id')->toArray(); + + /** @var Collection $set */ + $set = Auth::user()->budgets() + ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); + } + ) + ->groupBy('budgets.id') + ->groupBy('dateFormatted') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->whereIn('transactions.account_id', $ids) + ->whereIn('budgets.id', $budgetIds) + ->get( + [ + 'budgets.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'), + DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), + ] + ); + + $set = $set->sortBy( + function (Budget $budget) { + return strtolower($budget->name); + } + ); + + $return = []; + foreach ($set as $budget) { + $id = $budget->id; + if (!isset($return[$id])) { + $return[$id] = [ + 'budget' => $budget, + 'entries' => [], + ]; + } + // store each entry: + $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; + } + + return $return; + } + + /** + * Returns a list of budgets, budget limits and limit repetitions + * (doubling any of them in a left join) + * + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end) + { + /** @var Collection $set */ + $set = Auth::user() + ->budgets() + ->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') + ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') + ->where( + function (Builder $query) use ($start, $end) { + $query->where( + function (Builder $query) use ($start, $end) { + $query->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')); + $query->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d')); + } + ); + $query->orWhere( + function (Builder $query) { + $query->whereNull('limit_repetitions.startdate'); + $query->whereNull('limit_repetitions.enddate'); + } + ); + } + ) + ->get(['budgets.*', 'limit_repetitions.startdate', 'limit_repetitions.enddate', 'limit_repetitions.amount']); + + $set = $set->sortBy( + function (Budget $budget) { + return strtolower($budget->name); + } + ); + + return $set; + + } + + /** + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return LimitRepetition|null + */ + public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end) + { + $data = $budget->limitrepetitions() + ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00')) + ->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00')) + ->first(['limit_repetitions.*']); + + return $data; + } + /** * Returns the expenses for this budget grouped per day, with the date * in "date" (a string, not a Carbon) and the amount in "dailyAmount". @@ -96,179 +402,6 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return $set; } - /** - * @param Budget $budget - * - * @return boolean - */ - public function destroy(Budget $budget) - { - $budget->delete(); - - return true; - } - - /** - * @return Collection - */ - public function getActiveBudgets() - { - /** @var Collection $set */ - $set = Auth::user()->budgets()->where('active', 1)->get(); - - $set = $set->sortBy( - function (Budget $budget) { - return strtolower($budget->name); - } - ); - - return $set; - } - - /** - * Returns a list of budgets, budget limits and limit repetitions - * (doubling any of them in a left join) - * - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end) - { - /** @var Collection $set */ - $set = Auth::user() - ->budgets() - ->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') - ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') - ->where( - function (Builder $query) use ($start, $end) { - $query->where( - function (Builder $query) use ($start, $end) { - $query->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')); - $query->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d')); - } - ); - $query->orWhere( - function (Builder $query) { - $query->whereNull('limit_repetitions.startdate'); - $query->whereNull('limit_repetitions.enddate'); - } - ); - } - ) - ->get(['budgets.*', 'limit_repetitions.startdate', 'limit_repetitions.enddate', 'limit_repetitions.amount']); - - $set = $set->sortBy( - function (Budget $budget) { - return strtolower($budget->name); - } - ); - - return $set; - - } - - /** - * @param Budget $budget - * - * @return Carbon - */ - public function firstActivity(Budget $budget) - { - $first = $budget->transactionjournals()->orderBy('date', 'ASC')->first(); - if ($first) { - return $first->date; - } - - return new Carbon; - } - - /** - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end) - { - /** @var Collection $repetitions */ - return LimitRepetition:: - leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') - ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') - ->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00')) - ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00')) - ->where('budgets.user_id', Auth::user()->id) - ->get(['limit_repetitions.*', 'budget_limits.budget_id']); - } - - /** - * Returns an array with the following key:value pairs: - * - * yyyy-mm-dd: - * - * Where yyyy-mm-dd is the date and is the money spent using DEPOSITS in the $budget - * from all the users accounts. - * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentPerDay(Budget $budget, Carbon $start, Carbon $end) - { - /** @var Collection $query */ - $query = $budget->transactionJournals() - ->transactionTypes([TransactionType::WITHDRAWAL]) - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transactions.amount', '<', 0) - ->before($end) - ->after($start) - ->groupBy('dateFormatted')->get(['transaction_journals.date as dateFormatted', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]); - - $return = []; - foreach ($query->toArray() as $entry) { - $return[$entry['dateFormatted']] = $entry['sum']; - } - - return $return; - } - - /** - * @return Collection - */ - public function getBudgets() - { - /** @var Collection $set */ - $set = Auth::user()->budgets()->get(); - - $set = $set->sortBy( - function (Budget $budget) { - return strtolower($budget->name); - } - ); - - return $set; - } - - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return LimitRepetition|null - */ - public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end) - { - $data = $budget->limitrepetitions() - ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00')) - ->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00')) - ->first(['limit_repetitions.*']); - - return $data; - } - /** * @param Budget $budget * @@ -284,64 +417,6 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return Carbon::now()->startOfYear(); } - /** - * Returns an array with every budget in it and the expenses for each budget - * per month. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end) - { - $ids = $accounts->pluck('id')->toArray(); - - /** @var Collection $set */ - $set = Auth::user()->budgets() - ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); - } - ) - ->groupBy('budgets.id') - ->groupBy('dateFormatted') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->whereIn('transactions.account_id', $ids) - ->get( - [ - 'budgets.*', - DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), - DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), - ] - ); - - $set = $set->sortBy( - function (Budget $budget) { - return strtolower($budget->name); - } - ); - - $return = []; - foreach ($set as $budget) { - $id = $budget->id; - if (!isset($return[$id])) { - $return[$id] = [ - 'budget' => $budget, - 'entries' => [], - ]; - } - // store each entry: - $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; - } - - return $return; - } - /** * @return Collection */ @@ -448,187 +523,6 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return $entry->journalAmount; } - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return string - */ - public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts) - { - return $this->commonBalanceInPeriod($budget, $start, $end, $accounts); - } - - /** - * @param array $data - * - * @return Budget - */ - public function store(array $data) - { - $newBudget = new Budget( - [ - 'user_id' => $data['user'], - 'name' => $data['name'], - ] - ); - $newBudget->save(); - - return $newBudget; - } - - - /** - * @param Budget $budget - * @param array $data - * - * @return Budget - */ - public function update(Budget $budget, array $data) - { - // update the account: - $budget->name = $data['name']; - $budget->active = $data['active']; - $budget->save(); - - return $budget; - } - - /** - * @param Budget $budget - * @param Carbon $date - * @param $amount - * - * @return BudgetLimit - */ - public function updateLimitAmount(Budget $budget, Carbon $date, $amount) - { - // there should be a budget limit for this startdate: - /** @var BudgetLimit $limit */ - $limit = $budget->budgetlimits()->where('budget_limits.startdate', $date)->first(['budget_limits.*']); - - if (!$limit) { - // if not, create one! - $limit = new BudgetLimit; - $limit->budget()->associate($budget); - $limit->startdate = $date; - $limit->amount = $amount; - $limit->repeat_freq = 'monthly'; - $limit->repeats = 0; - $limit->save(); - - // likewise, there should be a limit repetition to match the end date - // (which is always the end of the month) but that is caught by an event. - - } else { - if ($amount > 0) { - $limit->amount = $amount; - $limit->save(); - } else { - $limit->delete(); - } - } - - return $limit; - } - - /** - * Get the budgeted amounts for each budgets in each year. - * - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end) - { - $budgetIds = $budgets->pluck('id')->toArray(); - - $set = Auth::user()->budgets() - ->leftJoin('budget_limits', 'budgets.id', '=', 'budget_limits.budget_id') - ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') - ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')) - ->where('limit_repetitions.enddate', '<=', $end->format('Y-m-d')) - ->groupBy('budgets.id') - ->groupBy('dateFormatted') - ->whereIn('budgets.id', $budgetIds) - ->get( - [ - 'budgets.*', - DB::Raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'), - DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`'), - ] - ); - - return $set; - } - - /** - * Returns an array with every budget in it and the expenses for each budget - * per year for. - * - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's a query. - * - * @return array - */ - public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) - { - $ids = $accounts->pluck('id')->toArray(); - $budgetIds = $budgets->pluck('id')->toArray(); - - /** @var Collection $set */ - $set = Auth::user()->budgets() - ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); - } - ) - ->groupBy('budgets.id') - ->groupBy('dateFormatted') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->whereIn('transactions.account_id', $ids) - ->whereIn('budgets.id', $budgetIds) - ->get( - [ - 'budgets.*', - DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'), - DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`'), - ] - ); - - $set = $set->sortBy( - function (Budget $budget) { - return strtolower($budget->name); - } - ); - - $return = []; - foreach ($set as $budget) { - $id = $budget->id; - if (!isset($return[$id])) { - $return[$id] = [ - 'budget' => $budget, - 'entries' => [], - ]; - } - // store each entry: - $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; - } - - return $return; - } - /** * Returns an array with the following key:value pairs: * @@ -727,4 +621,109 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return $set; } + + /** + * Returns an array with the following key:value pairs: + * + * yyyy-mm-dd: + * + * Where yyyy-mm-dd is the date and is the money spent using DEPOSITS in the $budget + * from all the users accounts. + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentPerDay(Budget $budget, Carbon $start, Carbon $end) + { + /** @var Collection $query */ + $query = $budget->transactionJournals() + ->transactionTypes([TransactionType::WITHDRAWAL]) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transactions.amount', '<', 0) + ->before($end) + ->after($start) + ->groupBy('dateFormatted')->get(['transaction_journals.date as dateFormatted', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]); + + $return = []; + foreach ($query->toArray() as $entry) { + $return[$entry['dateFormatted']] = $entry['sum']; + } + + return $return; + } + + /** + * @param array $data + * + * @return Budget + */ + public function store(array $data) + { + $newBudget = new Budget( + [ + 'user_id' => $data['user'], + 'name' => $data['name'], + ] + ); + $newBudget->save(); + + return $newBudget; + } + + /** + * @param Budget $budget + * @param array $data + * + * @return Budget + */ + public function update(Budget $budget, array $data) + { + // update the account: + $budget->name = $data['name']; + $budget->active = $data['active']; + $budget->save(); + + return $budget; + } + + /** + * @param Budget $budget + * @param Carbon $date + * @param $amount + * + * @return BudgetLimit + */ + public function updateLimitAmount(Budget $budget, Carbon $date, $amount) + { + // there should be a budget limit for this startdate: + /** @var BudgetLimit $limit */ + $limit = $budget->budgetlimits()->where('budget_limits.startdate', $date)->first(['budget_limits.*']); + + if (!$limit) { + // if not, create one! + $limit = new BudgetLimit; + $limit->budget()->associate($budget); + $limit->startdate = $date; + $limit->amount = $amount; + $limit->repeat_freq = 'monthly'; + $limit->repeats = 0; + $limit->save(); + + // likewise, there should be a limit repetition to match the end date + // (which is always the end of the month) but that is caught by an event. + + } else { + if ($amount > 0) { + $limit->amount = $amount; + $limit->save(); + } else { + $limit->delete(); + } + } + + return $limit; + } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index c9c9d8bc40..0aec8c451a 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -17,22 +17,30 @@ interface BudgetRepositoryInterface { + /** + * + * Same as ::spentInPeriod but corrects journals for a set of accounts + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + */ + public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts); + /** * @return void */ public function cleanupBudgets(); /** - * Returns the expenses for this budget grouped per day, with the date - * in "date" (a string, not a Carbon) and the amount in "dailyAmount". - * * @param Budget $budget - * @param Carbon $start - * @param Carbon $end * - * @return Collection + * @return boolean */ - public function getExpensesPerDay(Budget $budget, Carbon $start, Carbon $end); + public function destroy(Budget $budget); /** * @param Budget $budget @@ -42,72 +50,33 @@ interface BudgetRepositoryInterface public function firstActivity(Budget $budget); /** - * Returns the expenses for this budget grouped per month, with the date - * in "date" (a string, not a Carbon) and the amount in "dailyAmount". - * - * @param Budget $budget + * @return Collection + */ + public function getActiveBudgets(); + + /** * @param Carbon $start * @param Carbon $end * * @return Collection */ - public function getExpensesPerMonth(Budget $budget, Carbon $start, Carbon $end); - + public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end); /** - * Returns a list of expenses (in the field "spent", grouped per budget per account. + * Get the budgeted amounts for each budgets in each year. * * @param Collection $budgets - * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @return Collection */ - public function spentPerBudgetPerAccount(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end); + public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end); /** - * Returns an array with the following key:value pairs: - * - * yyyy-mm-dd: - * - * Where yyyy-mm-dd is the date and is the money spent using WITHDRAWALS in the $budget - * from all the users accounts. - * - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array + * @return Collection */ - public function spentPerDay(Budget $budget, Carbon $start, Carbon $end); - - /** - * Returns an array with the following key:value pairs: - * - * yyyy-mm-dd: - * - * That array contains: - * - * budgetid: - * - * Where yyyy-mm-dd is the date and is the money spent using WITHDRAWALS in the $budget - * from the given users accounts.. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentAllPerDayForAccounts(Collection $accounts, Carbon $start, Carbon $end); - - /** - * @param Budget $budget - * - * @return boolean - */ - public function destroy(Budget $budget); + public function getBudgets(); /** * Returns an array with every budget in it and the expenses for each budget @@ -134,35 +103,6 @@ interface BudgetRepositoryInterface */ public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end); - /** - * @return Collection - */ - public function getActiveBudgets(); - - /** - * Get the budgeted amounts for each budgets in each year. - * - * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end); - - /** - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end); - - /** - * @return Collection - */ - public function getBudgets(); - /** * Returns a list of budgets, budget limits and limit repetitions * (doubling any of them in a left join) @@ -183,6 +123,30 @@ interface BudgetRepositoryInterface */ public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end); + /** + * Returns the expenses for this budget grouped per day, with the date + * in "date" (a string, not a Carbon) and the amount in "dailyAmount". + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getExpensesPerDay(Budget $budget, Carbon $start, Carbon $end); + + /** + * Returns the expenses for this budget grouped per month, with the date + * in "date" (a string, not a Carbon) and the amount in "dailyAmount". + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getExpensesPerMonth(Budget $budget, Carbon $start, Carbon $end); + /** * @param Budget $budget * @@ -223,17 +187,52 @@ interface BudgetRepositoryInterface public function getWithoutBudgetSum(Carbon $start, Carbon $end); /** + * Returns an array with the following key:value pairs: * - * Same as ::spentInPeriod but corrects journals for a set of accounts + * yyyy-mm-dd: * - * @param Budget $budget + * That array contains: + * + * budgetid: + * + * Where yyyy-mm-dd is the date and is the money spent using WITHDRAWALS in the $budget + * from the given users accounts.. + * + * @param Collection $accounts * @param Carbon $start * @param Carbon $end - * @param Collection $accounts * - * @return string + * @return array */ - public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts); + public function spentAllPerDayForAccounts(Collection $accounts, Carbon $start, Carbon $end); + + /** + * Returns a list of expenses (in the field "spent", grouped per budget per account. + * + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function spentPerBudgetPerAccount(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end); + + /** + * Returns an array with the following key:value pairs: + * + * yyyy-mm-dd: + * + * Where yyyy-mm-dd is the date and is the money spent using WITHDRAWALS in the $budget + * from all the users accounts. + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentPerDay(Budget $budget, Carbon $start, Carbon $end); /** * @param array $data diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 6d0cc981d2..f821c52a85 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -19,91 +19,6 @@ use Illuminate\Support\Collection; class CategoryRepository implements CategoryRepositoryInterface { - /** - * Returns a list of all the categories belonging to a user. - * - * @return Collection - */ - public function listCategories() - { - /** @var Collection $set */ - $set = Auth::user()->categories()->orderBy('name', 'ASC')->get(); - $set = $set->sortBy( - function (Category $category) { - return strtolower($category->name); - } - ); - - return $set; - } - - /** - * Returns a list of transaction journals in the range (all types, all accounts) that have no category - * associated to them. - * - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function listNoCategory(Carbon $start, Carbon $end) - { - return Auth::user() - ->transactionjournals() - ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('category_transaction_journal.id') - ->before($end) - ->after($start) - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->get(['transaction_journals.*']); - } - - /** - * This method returns a very special collection for each category: - * - * category, year, expense/earned, amount - * - * categories can be duplicated. - * - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end) - { - - $set = Auth::user()->categories() - ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'category_transaction_journal.transaction_journal_id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->whereIn('transaction_types.type', [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL]) - ->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) - ->whereIn('categories.id', $categories->pluck('id')->toArray()) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->groupBy('categories.id') - ->groupBy('transaction_types.type') - ->groupBy('dateFormatted') - ->get( - [ - 'categories.*', - DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y") as `dateFormatted`'), - 'transaction_types.type', - DB::Raw('SUM(`amount`) as `sum`'), - ] - ); - - return $set; - - } - - /** * Returns a collection of Categories appended with the amount of money that has been earned * in these categories, based on the $accounts involved, in period X, grouped per month. @@ -154,6 +69,90 @@ class CategoryRepository implements CategoryRepositoryInterface } + /** + * Returns a list of all the categories belonging to a user. + * + * @return Collection + */ + public function listCategories() + { + /** @var Collection $set */ + $set = Auth::user()->categories()->orderBy('name', 'ASC')->get(); + $set = $set->sortBy( + function (Category $category) { + return strtolower($category->name); + } + ); + + return $set; + } + + /** + * This method returns a very special collection for each category: + * + * category, year, expense/earned, amount + * + * categories can be duplicated. + * + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end) + { + + $set = Auth::user()->categories() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'category_transaction_journal.transaction_journal_id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereIn('transaction_types.type', [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL]) + ->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) + ->whereIn('categories.id', $categories->pluck('id')->toArray()) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->groupBy('categories.id') + ->groupBy('transaction_types.type') + ->groupBy('dateFormatted') + ->get( + [ + 'categories.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y") as `dateFormatted`'), + 'transaction_types.type', + DB::Raw('SUM(`amount`) as `sum`'), + ] + ); + + return $set; + + } + + /** + * Returns a list of transaction journals in the range (all types, all accounts) that have no category + * associated to them. + * + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function listNoCategory(Carbon $start, Carbon $end) + { + return Auth::user() + ->transactionjournals() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->whereNull('category_transaction_journal.id') + ->before($end) + ->after($start) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->get(['transaction_journals.*']); + } + /** * Returns a collection of Categories appended with the amount of money that has been spent * in these categories, based on the $accounts involved, in period X, grouped per month. @@ -206,6 +205,20 @@ class CategoryRepository implements CategoryRepositoryInterface return $collection; } + /** + * Returns the total amount of money related to transactions without any category connected to + * it. Returns either the earned amount. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function sumEarnedNoCategory(Collection $accounts, Carbon $start, Carbon $end) + { + return $this->sumNoCategory($accounts, $start, $end, Query::EARNED); + } /** * Returns the total amount of money related to transactions without any category connected to @@ -222,21 +235,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $this->sumNoCategory($accounts, $start, $end, Query::SPENT); } - /** - * Returns the total amount of money related to transactions without any category connected to - * it. Returns either the earned amount. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function sumEarnedNoCategory(Collection $accounts, Carbon $start, Carbon $end) - { - return $this->sumNoCategory($accounts, $start, $end, Query::EARNED); - } - /** * Returns the total amount of money related to transactions without any category connected to * it. Returns either the earned or the spent amount. From c9e4a09da6575ea6dbe6284bb1fda04eabdc14a4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 15:23:36 +0100 Subject: [PATCH 04/11] Sort by alphabet. --- .../Journal/JournalRepository.php | 48 +-- app/Repositories/Rule/RuleRepository.php | 235 ++++++----- .../Rule/RuleRepositoryInterface.php | 62 +-- app/Repositories/Tag/TagRepository.php | 157 ++++--- app/Rules/Processor.php | 131 +++--- app/Support/Amount.php | 84 ++-- app/Support/CacheProperties.php | 16 +- app/Support/ExpandedForm.php | 232 +++++------ app/Support/Preferences.php | 40 +- app/Support/Steam.php | 43 +- app/Validation/FireflyValidator.php | 385 +++++++++--------- 11 files changed, 713 insertions(+), 720 deletions(-) diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index e0b3dc3dc8..ba3b4200e3 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -364,30 +364,6 @@ class JournalRepository implements JournalRepositoryInterface 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 * @@ -411,4 +387,28 @@ class JournalRepository implements JournalRepositoryInterface 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]; + } } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 5f4be310b5..0af4ad1ff6 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -23,6 +23,99 @@ use FireflyIII\Models\RuleTrigger; class RuleRepository implements RuleRepositoryInterface { + /** + * @param Rule $rule + * + * @return bool + */ + public function destroy(Rule $rule) + { + foreach ($rule->ruleTriggers as $trigger) { + $trigger->delete(); + } + foreach ($rule->ruleActions as $action) { + $action->delete(); + } + $rule->delete(); + + return true; + } + + /** + * @param RuleGroup $ruleGroup + * + * @return int + */ + public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup) + { + return intval($ruleGroup->rules()->max('order')); + } + + /** + * @param Rule $rule + * + * @return bool + */ + public function moveDown(Rule $rule) + { + $order = $rule->order; + + // find the rule with order+1 and give it order-1 + $other = $rule->ruleGroup->rules()->where('order', ($order + 1))->first(); + if ($other) { + $other->order = $other->order - 1; + $other->save(); + } + + + $rule->order = ($rule->order + 1); + $rule->save(); + $this->resetRulesInGroupOrder($rule->ruleGroup); + } + + /** + * @param Rule $rule + * + * @return bool + */ + public function moveUp(Rule $rule) + { + $order = $rule->order; + + // find the rule with order-1 and give it order+1 + $other = $rule->ruleGroup->rules()->where('order', ($order - 1))->first(); + if ($other) { + $other->order = ($other->order + 1); + $other->save(); + } + + $rule->order = ($rule->order - 1); + $rule->save(); + $this->resetRulesInGroupOrder($rule->ruleGroup); + } + + /** + * @param Rule $rule + * @param array $ids + * + * @return bool + */ + public function reorderRuleActions(Rule $rule, array $ids) + { + $order = 1; + foreach ($ids as $actionId) { + /** @var RuleTrigger $trigger */ + $action = $rule->ruleActions()->find($actionId); + if (!is_null($action)) { + $action->order = $order; + $action->save(); + $order++; + } + } + + return true; + } + /** * @param Rule $rule * @param array $ids @@ -70,73 +163,6 @@ class RuleRepository implements RuleRepositoryInterface } - /** - * @param Rule $rule - * @param array $ids - * - * @return bool - */ - public function reorderRuleActions(Rule $rule, array $ids) - { - $order = 1; - foreach ($ids as $actionId) { - /** @var RuleTrigger $trigger */ - $action = $rule->ruleActions()->find($actionId); - if (!is_null($action)) { - $action->order = $order; - $action->save(); - $order++; - } - } - - return true; - } - - - /** - * @param Rule $rule - * - * @return bool - */ - public function moveUp(Rule $rule) - { - $order = $rule->order; - - // find the rule with order-1 and give it order+1 - $other = $rule->ruleGroup->rules()->where('order', ($order - 1))->first(); - if ($other) { - $other->order = ($other->order + 1); - $other->save(); - } - - $rule->order = ($rule->order - 1); - $rule->save(); - $this->resetRulesInGroupOrder($rule->ruleGroup); - } - - /** - * @param Rule $rule - * - * @return bool - */ - public function moveDown(Rule $rule) - { - $order = $rule->order; - - // find the rule with order+1 and give it order-1 - $other = $rule->ruleGroup->rules()->where('order', ($order + 1))->first(); - if ($other) { - $other->order = $other->order - 1; - $other->save(); - } - - - $rule->order = ($rule->order + 1); - $rule->save(); - $this->resetRulesInGroupOrder($rule->ruleGroup); - } - - /** * @param array $data * @@ -185,58 +211,6 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } - /** - * @param Rule $rule - * @param string $action - * @param string $value - * @param bool $stopProcessing - * @param int $order - * - * @return RuleTrigger - */ - public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order) - { - $ruleTrigger = new RuleTrigger; - $ruleTrigger->rule()->associate($rule); - $ruleTrigger->order = $order; - $ruleTrigger->active = 1; - $ruleTrigger->stop_processing = $stopProcessing; - $ruleTrigger->trigger_type = $action; - $ruleTrigger->trigger_value = $value; - $ruleTrigger->save(); - - return $ruleTrigger; - } - - /** - * @param Rule $rule - * - * @return bool - */ - public function destroy(Rule $rule) - { - foreach ($rule->ruleTriggers as $trigger) { - $trigger->delete(); - } - foreach ($rule->ruleActions as $action) { - $action->delete(); - } - $rule->delete(); - - return true; - } - - /** - * @param RuleGroup $ruleGroup - * - * @return int - */ - public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup) - { - return intval($ruleGroup->rules()->max('order')); - } - - /** * @param Rule $rule * @param string $action @@ -261,6 +235,29 @@ class RuleRepository implements RuleRepositoryInterface return $ruleAction; } + /** + * @param Rule $rule + * @param string $action + * @param string $value + * @param bool $stopProcessing + * @param int $order + * + * @return RuleTrigger + */ + public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order) + { + $ruleTrigger = new RuleTrigger; + $ruleTrigger->rule()->associate($rule); + $ruleTrigger->order = $order; + $ruleTrigger->active = 1; + $ruleTrigger->stop_processing = $stopProcessing; + $ruleTrigger->trigger_type = $action; + $ruleTrigger->trigger_value = $value; + $ruleTrigger->save(); + + return $ruleTrigger; + } + /** * @param Rule $rule * @param array $data diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 7843c8c72a..42829751ff 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -36,27 +36,11 @@ interface RuleRepositoryInterface public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup); /** - * @param Rule $rule - * @param array $ids + * @param Rule $rule * * @return bool */ - public function reorderRuleTriggers(Rule $rule, array $ids); - - /** - * @param Rule $rule - * @param array $ids - * - * @return bool - */ - public function reorderRuleActions(Rule $rule, array $ids); - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup); + public function moveDown(Rule $rule); /** * @param Rule $rule @@ -67,18 +51,26 @@ interface RuleRepositoryInterface /** * @param Rule $rule - * @param array $data - * - * @return Rule - */ - public function update(Rule $rule, array $data); - - /** - * @param Rule $rule + * @param array $ids * * @return bool */ - public function moveDown(Rule $rule); + public function reorderRuleActions(Rule $rule, array $ids); + + /** + * @param Rule $rule + * @param array $ids + * + * @return bool + */ + public function reorderRuleTriggers(Rule $rule, array $ids); + + /** + * @param RuleGroup $ruleGroup + * + * @return bool + */ + public function resetRulesInGroupOrder(RuleGroup $ruleGroup); /** * @param array $data @@ -94,9 +86,9 @@ interface RuleRepositoryInterface * @param bool $stopProcessing * @param int $order * - * @return RuleTrigger + * @return RuleAction */ - public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order); + public function storeAction(Rule $rule, $action, $value, $stopProcessing, $order); /** * @param Rule $rule @@ -105,8 +97,16 @@ interface RuleRepositoryInterface * @param bool $stopProcessing * @param int $order * - * @return RuleAction + * @return RuleTrigger */ - public function storeAction(Rule $rule, $action, $value, $stopProcessing, $order); + public function storeTrigger(Rule $rule, $action, $value, $stopProcessing, $order); + + /** + * @param Rule $rule + * @param array $data + * + * @return Rule + */ + public function update(Rule $rule, array $data); } \ No newline at end of file diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index d0368d05f1..952090e750 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -22,6 +22,48 @@ class TagRepository implements TagRepositoryInterface { + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $set = Auth::user()->tags() + ->leftJoin('tag_transaction_journal', 'tag_transaction_journal.tag_id', '=', 'tags.id') + ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id') + ->leftJoin( + 'transactions AS t_from', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_to', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); + } + ) + ->where('tags.tagMode', 'balancingAct') + ->where('transaction_types.type', TransactionType::TRANSFER) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->whereNull('transaction_journals.deleted_at') + ->whereIn('t_from.account_id', $ids) + ->whereIn('t_to.account_id', $ids) + ->groupBy('t_to.account_id') + ->get( + [ + 't_to.account_id', + DB::Raw('SUM(`t_to`.`amount`) as `sum`'), + ] + ); + + return $set; + } + /** * * @param TransactionJournal $journal @@ -95,49 +137,6 @@ class TagRepository implements TagRepositoryInterface return $amount; } - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end) - { - $ids = $accounts->pluck('id')->toArray(); - $set = Auth::user()->tags() - ->leftJoin('tag_transaction_journal', 'tag_transaction_journal.tag_id', '=', 'tags.id') - ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id') - ->leftJoin( - 'transactions AS t_from', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); - } - ) - ->leftJoin( - 'transactions AS t_to', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); - } - ) - ->where('tags.tagMode', 'balancingAct') - ->where('transaction_types.type', TransactionType::TRANSFER) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->whereNull('transaction_journals.deleted_at') - ->whereIn('t_from.account_id', $ids) - ->whereIn('t_to.account_id', $ids) - ->groupBy('t_to.account_id') - ->get( - [ - 't_to.account_id', - DB::Raw('SUM(`t_to`.`amount`) as `sum`'), - ] - ); - - return $set; - } - /** * @param Tag $tag * @@ -278,42 +277,6 @@ class TagRepository implements TagRepositoryInterface return $tag; } - /** - * @param TransactionJournal $journal - * @param Tag $tag - * - * @return boolean - */ - protected function connectBalancingAct(TransactionJournal $journal, Tag $tag) - { - /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); - /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType(TransactionType::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); - $journal->save(); - - return true; - } - // and only if this is the only transfer - if ($journal->transaction_type_id == $transfer->id && $transfers < 1) { - $journal->tags()->save($tag); - $journal->save(); - - return true; - } - - // ignore expense - return false; - - } - /** * @param TransactionJournal $journal * @param Tag $tag @@ -361,6 +324,42 @@ class TagRepository implements TagRepositoryInterface } + /** + * @param TransactionJournal $journal + * @param Tag $tag + * + * @return boolean + */ + protected function connectBalancingAct(TransactionJournal $journal, Tag $tag) + { + /** @var TransactionType $withdrawal */ + $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); + /** @var TransactionType $transfer */ + $transfer = TransactionType::whereType(TransactionType::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); + $journal->save(); + + return true; + } + // and only if this is the only transfer + if ($journal->transaction_type_id == $transfer->id && $transfers < 1) { + $journal->tags()->save($tag); + $journal->save(); + + return true; + } + + // ignore expense + return false; + + } + /** * @param TransactionJournal $journal * @param Tag $tag diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index eb932ca2ec..54125bfcac 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -25,17 +25,14 @@ use Log; */ class Processor { - /** @var Rule */ - protected $rule; - /** @var TransactionJournal */ protected $journal; - - /** @var array */ - private $triggerTypes = []; - + /** @var Rule */ + protected $rule; /** @var array */ private $actionTypes = []; + /** @var array */ + private $triggerTypes = []; /** * Processor constructor. @@ -51,6 +48,38 @@ class Processor $this->actionTypes = Domain::getRuleActions(); } + /** + * @return TransactionJournal + */ + public function getJournal() + { + return $this->journal; + } + + /** + * @param TransactionJournal $journal + */ + public function setJournal($journal) + { + $this->journal = $journal; + } + + /** + * @return Rule + */ + public function getRule() + { + return $this->rule; + } + + /** + * @param Rule $rule + */ + public function setRule($rule) + { + $this->rule = $rule; + } + public function handle() { // get all triggers: @@ -62,6 +91,34 @@ class Processor } + /** + * @return bool + */ + protected function actions() + { + /** + * @var int $index + * @var RuleAction $action + */ + foreach ($this->rule->ruleActions()->orderBy('order', 'ASC')->get() as $action) { + $type = $action->action_type; + $class = $this->actionTypes[$type]; + Log::debug('Action #' . $action->id . ' for rule #' . $action->rule_id . ' (' . $type . ')'); + if (!class_exists($class)) { + abort(500, 'Could not instantiate class for rule action type "' . $type . '" (' . $class . ').'); + } + /** @var ActionInterface $actionClass */ + $actionClass = new $class($action, $this->journal); + $actionClass->act(); + if ($action->stop_processing) { + break; + } + + } + + return true; + } + /** * TODO stop when stop_processing is present. * @@ -101,65 +158,5 @@ class Processor } - /** - * @return bool - */ - protected function actions() - { - /** - * @var int $index - * @var RuleAction $action - */ - foreach ($this->rule->ruleActions()->orderBy('order', 'ASC')->get() as $action) { - $type = $action->action_type; - $class = $this->actionTypes[$type]; - Log::debug('Action #' . $action->id . ' for rule #' . $action->rule_id . ' (' . $type . ')'); - if (!class_exists($class)) { - abort(500, 'Could not instantiate class for rule action type "' . $type . '" (' . $class . ').'); - } - /** @var ActionInterface $actionClass */ - $actionClass = new $class($action, $this->journal); - $actionClass->act(); - if ($action->stop_processing) { - break; - } - - } - - return true; - } - - /** - * @return Rule - */ - public function getRule() - { - return $this->rule; - } - - /** - * @param Rule $rule - */ - public function setRule($rule) - { - $this->rule = $rule; - } - - /** - * @return TransactionJournal - */ - public function getJournal() - { - return $this->journal; - } - - /** - * @param TransactionJournal $journal - */ - public function setJournal($journal) - { - $this->journal = $journal; - } - } \ No newline at end of file diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 72bb329ad7..be465b5872 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -17,6 +17,17 @@ use Preferences as Prefs; class Amount { + /** + * @param $amount + * @param bool $coloured + * + * @return string + */ + public function format($amount, $coloured = true) + { + return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); + } + /** * This method will properly format the given number, in color or "black and white", * as a currency, given two things: the currency required and the current locale. @@ -48,48 +59,6 @@ class Amount return $result; } - /** - * @param $amount - * @param bool $coloured - * - * @return string - */ - public function format($amount, $coloured = true) - { - return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); - } - - /** - * @return string - */ - public function getCurrencySymbol() - { - $cache = new CacheProperties; - $cache->addProperty('getCurrencySymbol'); - if ($cache->has()) { - return $cache->get(); - } else { - $currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')); - $currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); - - $cache->store($currency->symbol); - - return $currency->symbol; - } - } - - /** - * @param string $symbol - * @param float $amount - * @param bool $coloured - * - * @return string - */ - public function formatWithSymbol($symbol, $amount, $coloured = true) - { - return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); - } - /** * * @param TransactionJournal $journal @@ -139,6 +108,18 @@ class Amount return $this->formatAnything($currency, $transaction->amount, $coloured); } + /** + * @param string $symbol + * @param float $amount + * @param bool $coloured + * + * @return string + */ + public function formatWithSymbol($symbol, $amount, $coloured = true) + { + return $this->formatAnything($this->getDefaultCurrency(), $amount, $coloured); + } + /** * @return Collection */ @@ -173,6 +154,25 @@ class Amount } } + /** + * @return string + */ + public function getCurrencySymbol() + { + $cache = new CacheProperties; + $cache->addProperty('getCurrencySymbol'); + if ($cache->has()) { + return $cache->get(); + } else { + $currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')); + $currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); + + $cache->store($currency->symbol); + + return $currency->symbol; + } + } + /** * @return TransactionCurrency */ diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index ba2d654b3d..ed0c1f3302 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -71,6 +71,14 @@ class CacheProperties return Cache::has($this->md5); } + /** + * @param $data + */ + public function store($data) + { + Cache::forever($this->md5, $data); + } + /** * @return void */ @@ -95,12 +103,4 @@ class CacheProperties $this->md5 = md5($this->md5); } - - /** - * @param $data - */ - public function store($data) - { - Cache::forever($this->md5, $data); - } } diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 37e4cc6c53..2fe9edcd3e 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -19,25 +19,6 @@ use Session; class ExpandedForm { - /** - * @param $name - * @param null $value - * @param array $options - * - * @return string - */ - public function staticText($name, $value, array $options = []) - { - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $value = $this->fillFieldValue($name, $value); - $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); - - return $html; - - } - /** * @param $name * @param null $value @@ -63,86 +44,6 @@ class ExpandedForm } - /** - * @param $name - * @param $options - * - * @return mixed - */ - protected function label($name, $options) - { - if (isset($options['label'])) { - return $options['label']; - } - - return trans('form.' . $name); - - } - - /** - * @param $name - * @param $label - * @param array $options - * - * @return array - */ - protected function expandOptionArray($name, $label, array $options) - { - $options['class'] = 'form-control'; - $options['id'] = 'ffInput_' . $name; - $options['autocomplete'] = 'off'; - $options['placeholder'] = ucfirst($label); - - return $options; - } - - /** - * @param $name - * - * @return string - */ - protected function getHolderClasses($name) - { - /* - * Get errors from session: - */ - /** @var MessageBag $errors */ - $errors = Session::get('errors'); - $classes = 'form-group'; - - if (!is_null($errors) && $errors->has($name)) { - $classes = 'form-group has-error has-feedback'; - } - - return $classes; - } - - /** - * @param $name - * @param $value - * - * @return mixed - */ - protected function fillFieldValue($name, $value) - { - if (Session::has('preFilled')) { - $preFilled = Session::get('preFilled'); - $value = isset($preFilled[$name]) && is_null($value) ? $preFilled[$name] : $value; - } - // @codeCoverageIgnoreStart - try { - if (!is_null(Input::old($name))) { - $value = Input::old($name); - } - } catch (RuntimeException $e) { - // don't care about session errors. - } - - // @codeCoverageIgnoreEnd - - return $value; - } - /** * @param $name * @param null $value @@ -208,6 +109,23 @@ class ExpandedForm return $html; } + /** + * @param $name + * @param array $options + * + * @return string + */ + public function file($name, array $options = []) + { + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); + + return $html; + + } + /** * @param $name * @param null $value @@ -366,6 +284,25 @@ class ExpandedForm return $html; } + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + */ + public function staticText($name, $value, array $options = []) + { + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); + $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); + + return $html; + + } + /** * @param $name * @param null $value @@ -385,23 +322,6 @@ class ExpandedForm return $html; } - /** - * @param $name - * @param array $options - * - * @return string - */ - public function file($name, array $options = []) - { - $label = $this->label($name, $options); - $options = $this->expandOptionArray($name, $label, $options); - $classes = $this->getHolderClasses($name); - $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); - - return $html; - - } - /** * @param $name * @param null $value @@ -440,4 +360,84 @@ class ExpandedForm return $html; } + + /** + * @param $name + * @param $label + * @param array $options + * + * @return array + */ + protected function expandOptionArray($name, $label, array $options) + { + $options['class'] = 'form-control'; + $options['id'] = 'ffInput_' . $name; + $options['autocomplete'] = 'off'; + $options['placeholder'] = ucfirst($label); + + return $options; + } + + /** + * @param $name + * @param $value + * + * @return mixed + */ + protected function fillFieldValue($name, $value) + { + if (Session::has('preFilled')) { + $preFilled = Session::get('preFilled'); + $value = isset($preFilled[$name]) && is_null($value) ? $preFilled[$name] : $value; + } + // @codeCoverageIgnoreStart + try { + if (!is_null(Input::old($name))) { + $value = Input::old($name); + } + } catch (RuntimeException $e) { + // don't care about session errors. + } + + // @codeCoverageIgnoreEnd + + return $value; + } + + /** + * @param $name + * + * @return string + */ + protected function getHolderClasses($name) + { + /* + * Get errors from session: + */ + /** @var MessageBag $errors */ + $errors = Session::get('errors'); + $classes = 'form-group'; + + if (!is_null($errors) && $errors->has($name)) { + $classes = 'form-group has-error has-feedback'; + } + + return $classes; + } + + /** + * @param $name + * @param $options + * + * @return mixed + */ + protected function label($name, $options) + { + if (isset($options['label'])) { + return $options['label']; + } + + return trans('form.' . $name); + + } } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index e705f9cb55..531eb3e0ad 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -13,16 +13,6 @@ use FireflyIII\Models\Preference; */ class Preferences { - /** - * @return string - */ - public function lastActivity() - { - $preference = $this->get('lastActivity', microtime())->data; - - return md5($preference); - } - /** * @param string $name * @param string $default @@ -53,6 +43,26 @@ class Preferences } + /** + * @return string + */ + public function lastActivity() + { + $preference = $this->get('lastActivity', microtime())->data; + + return md5($preference); + } + + /** + * @return bool + */ + public function mark() + { + $this->set('lastActivity', microtime()); + + return true; + } + /** * @param $name * @param string $value @@ -80,14 +90,4 @@ class Preferences return $pref; } - - /** - * @return bool - */ - public function mark() - { - $this->set('lastActivity', microtime()); - - return true; - } } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 647b75f138..0358e07abd 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -16,27 +16,6 @@ use FireflyIII\Models\Transaction; class Steam { - /** - * @param array $accounts - * - * @return array - */ - public function getLastActivities(array $accounts) - { - $list = []; - - $set = Auth::user()->transactions() - ->whereIn('account_id', $accounts) - ->groupBy('account_id') - ->get(['transactions.account_id', DB::raw('MAX(`transaction_journals`.`date`) as `max_date`')]); - - foreach ($set as $entry) { - $list[intval($entry->account_id)] = new Carbon($entry->max_date); - } - - return $list; - } - /** * * @param \FireflyIII\Models\Account $account @@ -163,7 +142,29 @@ class Steam return $result; } + /** + * @param array $accounts + * + * @return array + */ + public function getLastActivities(array $accounts) + { + $list = []; + + $set = Auth::user()->transactions() + ->whereIn('account_id', $accounts) + ->groupBy('account_id') + ->get(['transactions.account_id', DB::raw('MAX(`transaction_journals`.`date`) as `max_date`')]); + + foreach ($set as $entry) { + $list[intval($entry->account_id)] = new Carbon($entry->max_date); + } + + return $list; + } + // parse PHP size: + /** * @param $string * diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index d386645292..8fd6befc1f 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -40,83 +40,6 @@ class FireflyValidator extends Validator parent::__construct($translator, $data, $rules, $messages, $customAttributes); } - /** - * @param $attribute - * - * @return bool - */ - public function validateRuleTriggerValue($attribute) - { - // get the index from a string like "rule-trigger-value.2". - $parts = explode('.', $attribute); - $index = $parts[count($parts) - 1]; - - // loop all rule-triggers. - // check if rule-value matches the thing. - if (is_array($this->data['rule-trigger'])) { - $name = $this->getRuleTriggerName($index); - $value = $this->getRuleTriggerValue($index); - switch ($name) { - default: - return true; - case 'amount_less': - return is_numeric($value); - break; - case 'transaction_type': - $count = TransactionType::where('type', $value)->count(); - - return $count === 1; - break; - case 'invalid': - return false; - break; - } - } - } - - /** - * @param $attribute - * - * @return bool - */ - public function validateRuleActionValue($attribute) - { - // get the index from a string like "rule-action-value.2". - $parts = explode('.', $attribute); - $index = $parts[count($parts) - 1]; - // loop all rule-actions. - // check if rule-action-value matches the thing. - - if (is_array($this->data['rule-action'])) { - $name = isset($this->data['rule-action'][$index]) ? $this->data['rule-action'][$index] : 'invalid'; - $value = isset($this->data['rule-action-value'][$index]) ? $this->data['rule-action-value'][$index] : false; - switch ($name) { - default: - Log::debug(' (' . $attribute . ') (index:' . $index . ') Name is "' . $name . '" so no action is taken.'); - - return true; - case 'set_budget': - /** @var BudgetRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); - $budgets = $repository->getBudgets(); - // count budgets, should have at least one - $count = $budgets->filter( - function (Budget $budget) use ($value) { - return $budget->name == $value; - } - )->count(); - - return ($count === 1); - case 'invalid': - return false; - - } - } - - return false; - } - - /** * @param $attribute * @param $value @@ -166,6 +89,82 @@ class FireflyValidator extends Validator return (intval($checksum) === 1); } + /** + * @param $attribute + * + * @return bool + */ + public function validateRuleActionValue($attribute) + { + // get the index from a string like "rule-action-value.2". + $parts = explode('.', $attribute); + $index = $parts[count($parts) - 1]; + // loop all rule-actions. + // check if rule-action-value matches the thing. + + if (is_array($this->data['rule-action'])) { + $name = isset($this->data['rule-action'][$index]) ? $this->data['rule-action'][$index] : 'invalid'; + $value = isset($this->data['rule-action-value'][$index]) ? $this->data['rule-action-value'][$index] : false; + switch ($name) { + default: + Log::debug(' (' . $attribute . ') (index:' . $index . ') Name is "' . $name . '" so no action is taken.'); + + return true; + case 'set_budget': + /** @var BudgetRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $budgets = $repository->getBudgets(); + // count budgets, should have at least one + $count = $budgets->filter( + function (Budget $budget) use ($value) { + return $budget->name == $value; + } + )->count(); + + return ($count === 1); + case 'invalid': + return false; + + } + } + + return false; + } + + /** + * @param $attribute + * + * @return bool + */ + public function validateRuleTriggerValue($attribute) + { + // get the index from a string like "rule-trigger-value.2". + $parts = explode('.', $attribute); + $index = $parts[count($parts) - 1]; + + // loop all rule-triggers. + // check if rule-value matches the thing. + if (is_array($this->data['rule-trigger'])) { + $name = $this->getRuleTriggerName($index); + $value = $this->getRuleTriggerValue($index); + switch ($name) { + default: + return true; + case 'amount_less': + return is_numeric($value); + break; + case 'transaction_type': + $count = TransactionType::where('type', $value)->count(); + + return $count === 1; + break; + case 'invalid': + return false; + break; + } + } + } + /** * @param $attribute * @param $value @@ -197,122 +196,6 @@ class FireflyValidator extends Validator return false; } - /** - * @return bool - */ - protected function validateAccountAnonymously() - { - if (!isset($this->data['user_id'])) { - return false; - } - - $user = User::find($this->data['user_id']); - $type = AccountType::find($this->data['account_type_id'])->first(); - $value = $this->tryDecrypt($this->data['name']); - - - $set = $user->accounts()->where('account_type_id', $type->id)->get(); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->name == $value) { - return false; - } - } - - return true; - } - - /** - * @param $value - * - * @return mixed - */ - protected function tryDecrypt($value) - { - try { - $value = Crypt::decrypt($value); - } catch (DecryptException $e) { - // do not care. - } - - return $value; - } - - /** - * @param $value - * @param $parameters - * - * @return bool - */ - protected function validateByAccountTypeString($value, $parameters) - { - $search = Config::get('firefly.accountTypeByIdentifier.' . $this->data['what']); - $type = AccountType::whereType($search)->first(); - $ignore = isset($parameters[0]) ? intval($parameters[0]) : 0; - - $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->name == $value) { - return false; - } - } - - return true; - } - - /** - * @param $value - * @param $parameters - * - * @return bool - */ - protected function validateByAccountTypeId($value, $parameters) - { - $type = AccountType::find($this->data['account_type_id'])->first(); - $ignore = isset($parameters[0]) ? intval($parameters[0]) : 0; - $value = $this->tryDecrypt($value); - - $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->name == $value) { - return false; - } - } - - return true; - - } - - /** - * @param $value - * - * @return bool - * @internal param $parameters - * - */ - protected function validateByAccountId($value) - { - /** @var Account $existingAccount */ - $existingAccount = Account::find($this->data['id']); - - $type = $existingAccount->accountType; - $ignore = $existingAccount->id; - $value = $this->tryDecrypt($value); - - $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); - /** @var Account $entry */ - foreach ($set as $entry) { - if ($entry->name == $value) { - return false; - } - } - - return true; - - } - /** * @param $attribute * @param $value @@ -407,6 +290,122 @@ class FireflyValidator extends Validator return true; } + /** + * @param $value + * + * @return mixed + */ + protected function tryDecrypt($value) + { + try { + $value = Crypt::decrypt($value); + } catch (DecryptException $e) { + // do not care. + } + + return $value; + } + + /** + * @return bool + */ + protected function validateAccountAnonymously() + { + if (!isset($this->data['user_id'])) { + return false; + } + + $user = User::find($this->data['user_id']); + $type = AccountType::find($this->data['account_type_id'])->first(); + $value = $this->tryDecrypt($this->data['name']); + + + $set = $user->accounts()->where('account_type_id', $type->id)->get(); + /** @var Account $entry */ + foreach ($set as $entry) { + if ($entry->name == $value) { + return false; + } + } + + return true; + } + + /** + * @param $value + * + * @return bool + * @internal param $parameters + * + */ + protected function validateByAccountId($value) + { + /** @var Account $existingAccount */ + $existingAccount = Account::find($this->data['id']); + + $type = $existingAccount->accountType; + $ignore = $existingAccount->id; + $value = $this->tryDecrypt($value); + + $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); + /** @var Account $entry */ + foreach ($set as $entry) { + if ($entry->name == $value) { + return false; + } + } + + return true; + + } + + /** + * @param $value + * @param $parameters + * + * @return bool + */ + protected function validateByAccountTypeId($value, $parameters) + { + $type = AccountType::find($this->data['account_type_id'])->first(); + $ignore = isset($parameters[0]) ? intval($parameters[0]) : 0; + $value = $this->tryDecrypt($value); + + $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); + /** @var Account $entry */ + foreach ($set as $entry) { + if ($entry->name == $value) { + return false; + } + } + + return true; + + } + + /** + * @param $value + * @param $parameters + * + * @return bool + */ + protected function validateByAccountTypeString($value, $parameters) + { + $search = Config::get('firefly.accountTypeByIdentifier.' . $this->data['what']); + $type = AccountType::whereType($search)->first(); + $ignore = isset($parameters[0]) ? intval($parameters[0]) : 0; + + $set = Auth::user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); + /** @var Account $entry */ + foreach ($set as $entry) { + if ($entry->name == $value) { + return false; + } + } + + return true; + } + /** * @param int $index * From 441ba79ec75032dab42e44c4cb34f2162a8c80cc Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 15:27:53 +0100 Subject: [PATCH 05/11] Some tests --- database/seeds/TestDataSeeder.php | 2 +- .../Controllers/BillControllerTest.php | 110 +++++++++++------- 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 6f02bb1755..bdca76d317 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -33,7 +33,7 @@ class TestDataSeeder extends Seeder // create asset accounts for user #1. $this->createAssetAccounts($user); - // create a bills for user #1 + // create bills for user #1 $this->createBills($user); // create some budgets for user #1 diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php index 9062569f90..d407daced8 100644 --- a/tests/acceptance/Controllers/BillControllerTest.php +++ b/tests/acceptance/Controllers/BillControllerTest.php @@ -16,14 +16,12 @@ class BillControllerTest extends TestCase /** * @covers FireflyIII\Http\Controllers\BillController::create - * @todo Implement testCreate(). */ public function testCreate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills/create'); + $this->assertEquals(200, $response->status()); } /** @@ -32,93 +30,117 @@ class BillControllerTest extends TestCase */ public function testDelete() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills/delete/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::destroy - * @todo Implement testDestroy(). */ public function testDestroy() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->session(['bills.delete.url' => 'http://localhost']); + $this->be($this->user()); + $args = [ + '_token' => Session::token(), + ]; + $response = $this->call('POST', '/bills/destroy/2', $args); + $this->assertSessionHas('success'); + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::edit - * @todo Implement testEdit(). */ public function testEdit() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills/edit/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::index - * @todo Implement testIndex(). */ public function testIndex() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::rescan - * @todo Implement testRescan(). */ public function testRescan() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills/rescan/1'); + $this->assertSessionHas('success'); + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::show - * @todo Implement testShow(). */ public function testShow() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/bills/show/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::store - * @todo Implement testStore(). */ public function testStore() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->session(['bills.create.url' => 'http://localhost']); + $args = [ + 'name' => 'Some test', + 'match' => 'words', + '_token' => Session::token(), + 'amount_min' => 10, + 'amount_max' => 100, + 'amount_currency_id_amount_min' => 1, + 'amount_currency_id_amount_max' => 1, + 'date' => '20160101', + 'repeat_freq' => 'monthly', + 'skip' => 0, + ]; + + $this->be($this->user()); + $response = $this->call('POST', '/bills/store', $args); + $this->assertSessionHas('success'); + + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BillController::update - * @todo Implement testUpdate(). */ public function testUpdate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->session(['bills.edit.url' => 'http://localhost']); + $args = [ + 'id' => 1, + 'name' => 'Some test', + 'match' => 'words', + '_token' => Session::token(), + 'amount_min' => 10, + 'amount_max' => 100, + 'amount_currency_id_amount_min' => 1, + 'amount_currency_id_amount_max' => 1, + 'date' => '20160101', + 'repeat_freq' => 'monthly', + 'skip' => 0, + ]; + + $this->be($this->user()); + $response = $this->call('POST', '/bills/update/1', $args); + $this->assertSessionHas('success'); + + $this->assertEquals(302, $response->status()); } } From 81287602d7f353e5a4543b2d0ed8d2198ab61252 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 16:15:23 +0100 Subject: [PATCH 06/11] Updated tests. --- .../Controllers/BudgetControllerTest.php | 128 ++++++++++-------- .../Controllers/CategoryControllerTest.php | 103 +++++++------- .../acceptance/Controllers/ControllerTest.php | 63 --------- 3 files changed, 121 insertions(+), 173 deletions(-) delete mode 100644 tests/acceptance/Controllers/ControllerTest.php diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php index bfceaab89d..68ea18b6f0 100644 --- a/tests/acceptance/Controllers/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/BudgetControllerTest.php @@ -15,145 +15,153 @@ class BudgetControllerTest extends TestCase { /** * @covers FireflyIII\Http\Controllers\BudgetController::amount - * @todo Implement testAmount(). */ public function testAmount() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $args = [ + 'amount' => 1200, + '_token' => Session::token(), + ]; + $this->be($this->user()); + + $response = $this->call('POST', '/budgets/amount/1', $args); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::create - * @todo Implement testCreate(). */ public function testCreate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/create'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::delete - * @todo Implement testDelete(). */ public function testDelete() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/delete/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::destroy - * @todo Implement testDestroy(). */ public function testDestroy() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + + $args = [ + '_token' => Session::token(), + ]; + + $this->session(['budgets.delete.url' => 'http://localhost']); + + $response = $this->call('POST', '/budgets/destroy/2', $args); + $this->assertSessionHas('success'); + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::edit - * @todo Implement testEdit(). */ public function testEdit() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/edit/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::index - * @todo Implement testIndex(). */ public function testIndex() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::noBudget - * @todo Implement testNoBudget(). */ public function testNoBudget() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/list/noBudget'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::postUpdateIncome - * @todo Implement testPostUpdateIncome(). */ public function testPostUpdateIncome() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $args = [ + 'amount' => 1200, + '_token' => Session::token(), + ]; + $this->be($this->user()); + + $response = $this->call('POST', '/budgets/income', $args); + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::show - * @todo Implement testShow(). */ public function testShow() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/show/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\BudgetController::store - * @todo Implement testStore(). */ public function testStore() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->session(['budgets.create.url' => 'http://localhost']); + $args = [ + '_token' => Session::token(), + 'name' => 'Some kind of test budget.', + ]; + + $response = $this->call('POST', '/budgets/store', $args); + $this->assertEquals(302, $response->status()); + $this->assertSessionHas('success'); } /** * @covers FireflyIII\Http\Controllers\BudgetController::update - * @todo Implement testUpdate(). */ public function testUpdate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->session(['budgets.edit.url' => 'http://localhost']); + $args = [ + '_token' => Session::token(), + 'name' => 'Some kind of test budget.', + ]; + + $response = $this->call('POST', '/budgets/update/1', $args); + $this->assertEquals(302, $response->status()); + $this->assertSessionHas('success'); } /** * @covers FireflyIII\Http\Controllers\BudgetController::updateIncome - * @todo Implement testUpdateIncome(). */ public function testUpdateIncome() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/budgets/income'); + $this->assertEquals(200, $response->status()); } } diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php index 9ad44bf07c..a9c571321a 100644 --- a/tests/acceptance/Controllers/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/CategoryControllerTest.php @@ -16,121 +16,124 @@ class CategoryControllerTest extends TestCase /** * @covers FireflyIII\Http\Controllers\CategoryController::create - * @todo Implement testCreate(). */ public function testCreate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/create'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::delete - * @todo Implement testDelete(). */ public function testDelete() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/delete/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::destroy - * @todo Implement testDestroy(). */ public function testDestroy() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + + $args = [ + '_token' => Session::token(), + ]; + + $this->session(['categories.delete.url' => 'http://localhost']); + + $response = $this->call('POST', '/categories/destroy/2', $args); + $this->assertSessionHas('success'); + $this->assertEquals(302, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::edit - * @todo Implement testEdit(). */ public function testEdit() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/edit/1'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::index - * @todo Implement testIndex(). */ public function testIndex() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::noCategory - * @todo Implement testNoCategory(). */ public function testNoCategory() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/list/noCategory'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::show - * @todo Implement testShow(). */ public function testShow() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/show/1'); + $this->assertEquals(200, $response->status()); + } /** * @covers FireflyIII\Http\Controllers\CategoryController::showWithDate - * @todo Implement testShowWithDate(). */ public function testShowWithDate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $response = $this->call('GET', '/categories/show/1/20150101'); + $this->assertEquals(200, $response->status()); } /** * @covers FireflyIII\Http\Controllers\CategoryController::store - * @todo Implement testStore(). */ public function testStore() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->session(['categories.create.url' => 'http://localhost']); + $args = [ + '_token' => Session::token(), + 'name' => 'Some kind of test cat.', + ]; + + $response = $this->call('POST', '/categories/store', $args); + $this->assertEquals(302, $response->status()); + $this->assertSessionHas('success'); } /** * @covers FireflyIII\Http\Controllers\CategoryController::update - * @todo Implement testUpdate(). */ public function testUpdate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->session(['categories.edit.url' => 'http://localhost']); + $args = [ + '_token' => Session::token(), + 'name' => 'Some kind of test category.', + ]; + + $response = $this->call('POST', '/categories/update/1', $args); + $this->assertEquals(302, $response->status()); + $this->assertSessionHas('success'); } } diff --git a/tests/acceptance/Controllers/ControllerTest.php b/tests/acceptance/Controllers/ControllerTest.php deleted file mode 100644 index 374c3050df..0000000000 --- a/tests/acceptance/Controllers/ControllerTest.php +++ /dev/null @@ -1,63 +0,0 @@ -markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Controller::authorizeForUser - * @todo Implement testAuthorizeForUser(). - */ - public function testAuthorizeForUser() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Controller::validate - * @todo Implement testValidate(). - */ - public function testValidate() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Controller::validateWithBag - * @todo Implement testValidateWithBag(). - */ - public function testValidateWithBag() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } -} From ea6c54cad08d108acafcca657bde38966f973e65 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 21:23:39 +0100 Subject: [PATCH 07/11] Expand specifix to have a property that determines whether they fire before anything happens, or after. Could be expanded to allow for even more fine-grained control. See issue #145 --- app/Helpers/Csv/Specifix/Specifix.php | 44 +++++++++++++++++++ .../Csv/Specifix/SpecifixInterface.php | 14 ++++++ 2 files changed, 58 insertions(+) create mode 100644 app/Helpers/Csv/Specifix/Specifix.php diff --git a/app/Helpers/Csv/Specifix/Specifix.php b/app/Helpers/Csv/Specifix/Specifix.php new file mode 100644 index 0000000000..627ba651ca --- /dev/null +++ b/app/Helpers/Csv/Specifix/Specifix.php @@ -0,0 +1,44 @@ +processorType; + } + + /** + * @param $processorType + * + * @return $this + */ + public function setProcessorType($processorType) + { + $this->processorType = $processorType; + + return $this; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Csv/Specifix/SpecifixInterface.php b/app/Helpers/Csv/Specifix/SpecifixInterface.php index 142d224c4d..34e9a3e833 100644 --- a/app/Helpers/Csv/Specifix/SpecifixInterface.php +++ b/app/Helpers/Csv/Specifix/SpecifixInterface.php @@ -8,16 +8,30 @@ namespace FireflyIII\Helpers\Csv\Specifix; */ interface SpecifixInterface { + const PRE_PROCESSOR = 1; + const POST_PROCESSOR = 2; /** * Implement bank and locale related fixes. */ public function fix(); + /** + * @return int + */ + public function getProcessorType(); + /** * @param array $data */ public function setData($data); + /** + * @param int $processorType + * + * @return $this + */ + public function setProcessorType($processorType); + /** * @param array $row */ From 9350557d107678341553c1acd6d197608d0ac668 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 21:24:08 +0100 Subject: [PATCH 08/11] Properly extend the current specifix to indicate they're all post-processors. See issue #145 --- app/Helpers/Csv/Specifix/AbnAmroDescription.php | 10 +++++++++- app/Helpers/Csv/Specifix/Dummy.php | 9 ++++++++- app/Helpers/Csv/Specifix/RabobankDescription.php | 10 +++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php index b49881e81c..1f47db072a 100644 --- a/app/Helpers/Csv/Specifix/AbnAmroDescription.php +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -12,7 +12,7 @@ use Log; * * @package FireflyIII\Helpers\Csv\Specifix */ -class AbnAmroDescription +class AbnAmroDescription extends Specifix implements SpecifixInterface { /** @var array */ protected $data; @@ -20,6 +20,14 @@ class AbnAmroDescription /** @var array */ protected $row; + /** + * AbnAmroDescription constructor. + */ + public function __construct() + { + $this->setProcessorType(self::POST_PROCESSOR); + } + /** * @return array diff --git a/app/Helpers/Csv/Specifix/Dummy.php b/app/Helpers/Csv/Specifix/Dummy.php index ad2a7c0b58..915e87bd37 100644 --- a/app/Helpers/Csv/Specifix/Dummy.php +++ b/app/Helpers/Csv/Specifix/Dummy.php @@ -7,7 +7,7 @@ namespace FireflyIII\Helpers\Csv\Specifix; * * @package FireflyIII\Helpers\Csv\Specifix */ -class Dummy +class Dummy extends Specifix implements SpecifixInterface { /** @var array */ protected $data; @@ -15,6 +15,13 @@ class Dummy /** @var array */ protected $row; + /** + * Dummy constructor. + */ + public function __construct() + { + $this->setProcessorType(self::POST_PROCESSOR); + } /** * @return array diff --git a/app/Helpers/Csv/Specifix/RabobankDescription.php b/app/Helpers/Csv/Specifix/RabobankDescription.php index 426688d758..a8fced085a 100644 --- a/app/Helpers/Csv/Specifix/RabobankDescription.php +++ b/app/Helpers/Csv/Specifix/RabobankDescription.php @@ -9,7 +9,7 @@ use Log; * * @package FireflyIII\Helpers\Csv\Specifix */ -class RabobankDescription +class RabobankDescription extends Specifix implements SpecifixInterface { /** @var array */ protected $data; @@ -17,6 +17,14 @@ class RabobankDescription /** @var array */ protected $row; + /** + * RabobankDescription constructor. + */ + public function __construct() + { + $this->setProcessorType(self::POST_PROCESSOR); + } + /** * @return array From d211555beb450fd059afbe749250d6a8a04e6921 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 21:24:27 +0100 Subject: [PATCH 09/11] Only fire post-processing specifix in the import routine. See issue #145 --- app/Helpers/Csv/Importer.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php index 2aa4274b26..8f139d8bc8 100644 --- a/app/Helpers/Csv/Importer.php +++ b/app/Helpers/Csv/Importer.php @@ -246,6 +246,8 @@ class Importer Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':'); + // here would be the place where preprocessors would fire. + /** @var ConverterInterface $converter */ $converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class); $converter->setData($data); // the complete array so far. @@ -294,10 +296,12 @@ class Importer foreach ($this->getSpecifix() as $className) { /** @var SpecifixInterface $specifix */ $specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className); - $specifix->setData($this->importData); - $specifix->setRow($this->importRow); - Log::debug('Now post-process specifix named ' . $className . ':'); - $this->importData = $specifix->fix(); + if ($specifix->getProcessorType() == SpecifixInterface::POST_PROCESSOR) { + $specifix->setData($this->importData); + $specifix->setRow($this->importRow); + Log::debug('Now post-process specifix named ' . $className . ':'); + $this->importData = $specifix->fix(); + } } From cc9d1c4bfda25c0e11f4b977d68e8c672a2bfdd9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 20 Jan 2016 21:24:48 +0100 Subject: [PATCH 10/11] Some unrelated code cleanup. --- app/Http/Controllers/CsvController.php | 4 ++-- resources/views/csv/index.twig | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php index 6bc6a9b2e3..b54ef7314a 100644 --- a/app/Http/Controllers/CsvController.php +++ b/app/Http/Controllers/CsvController.php @@ -81,7 +81,7 @@ class CsvController extends Controller } $keys = array_keys(Config::get('csv.roles')); foreach ($keys as $name) { - $availableRoles[$name] = trans('firefly.csv_column_' . $name);//$role['name']; + $availableRoles[$name] = trans('firefly.csv_column_' . $name); } ksort($availableRoles); @@ -162,7 +162,7 @@ class CsvController extends Controller Session::forget('csv-roles'); Session::forget('csv-mapped'); Session::forget('csv-specifix'); - SessioN::forget('csv-delimiter'); + Session::forget('csv-delimiter'); // get list of supported specifix $specifix = []; diff --git a/resources/views/csv/index.twig b/resources/views/csv/index.twig index deff9ec3ae..9749ac83a1 100644 --- a/resources/views/csv/index.twig +++ b/resources/views/csv/index.twig @@ -55,7 +55,7 @@ {{ ExpandedForm.checkbox('has_headers',1,null,{helpText: 'csv_header_help'|_}) }} - {{ ExpandedForm.text('date_format','Ymd',{helpText: 'csv_date_help'|_}) }} + {{ ExpandedForm.text('date_format','Ymd',{helpText: trans('firefly.csv_date_help', {dateExample: phpdate('Ymd')}) }) }} {{ ExpandedForm.file('csv',{helpText: 'csv_csv_file_help'|_}) }} @@ -65,7 +65,6 @@ {{ ExpandedForm.select('csv_import_account', accounts, 0, {helpText: 'csv_import_account_help'|_} ) }} - {{ ExpandedForm.multiCheckbox('specifix', specifix) }} From 9ce960b3d7418b27b710cdd20ca9fa08d0d4058c Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 21 Jan 2016 09:52:45 +0100 Subject: [PATCH 11/11] There are no converters that need the role of the current field. --- app/Helpers/Csv/Converter/BasicConverter.php | 18 ------------------ .../Csv/Converter/ConverterInterface.php | 5 ----- app/Helpers/Csv/Importer.php | 1 - 3 files changed, 24 deletions(-) diff --git a/app/Helpers/Csv/Converter/BasicConverter.php b/app/Helpers/Csv/Converter/BasicConverter.php index bdfb3c2612..2c7bff6f2a 100644 --- a/app/Helpers/Csv/Converter/BasicConverter.php +++ b/app/Helpers/Csv/Converter/BasicConverter.php @@ -18,8 +18,6 @@ class BasicConverter /** @var array */ protected $mapped; /** @var string */ - protected $role; - /** @var string */ protected $value; /** @@ -86,22 +84,6 @@ class BasicConverter $this->mapped = $mapped; } - /** - * @return string - */ - public function getRole() - { - return $this->role; - } - - /** - * @param string $role - */ - public function setRole($role) - { - $this->role = $role; - } - /** * @return string */ diff --git a/app/Helpers/Csv/Converter/ConverterInterface.php b/app/Helpers/Csv/Converter/ConverterInterface.php index cb7aebcade..1eb2e37db9 100644 --- a/app/Helpers/Csv/Converter/ConverterInterface.php +++ b/app/Helpers/Csv/Converter/ConverterInterface.php @@ -36,11 +36,6 @@ interface ConverterInterface */ public function setMapped($mapped); - /** - * @param string $role - */ - public function setRole($role); - /** * @param string $value */ diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php index 8f139d8bc8..963b615027 100644 --- a/app/Helpers/Csv/Importer.php +++ b/app/Helpers/Csv/Importer.php @@ -255,7 +255,6 @@ class Importer $converter->setIndex($index); $converter->setMapped($this->mapped); $converter->setValue($value); - $converter->setRole($role); $data[$field] = $converter->convert(); } // move to class vars.