diff --git a/.env.example b/.env.example index 4fd2e2b837..25a959d3bb 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,8 @@ DB_PASSWORD=secret CACHE_DRIVER=file SESSION_DRIVER=file +DEFAULT_CURRENCY=EUR + EMAIL_SMTP= EMAIL_DRIVER=smtp EMAIL_USERNAME= @@ -21,4 +23,6 @@ RUNCLEANUP=true SITE_OWNER=mail@example.com SENDGRID_USERNAME= -SENDGRID_PASSWORD= \ No newline at end of file +SENDGRID_PASSWORD= + +BLOCKED_DOMAINS= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 411ae437cd..5cb379f496 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ Thumbs.db .idea/ tests/_output/* _ide_helper.php -/build/logs/clover.xml +/build/logs index.html* app/storage/firefly-export* .vagrant diff --git a/.travis.yml b/.travis.yml index da826954ea..820282fa3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,9 @@ install: - composer update - php artisan env - mv -v .env.testing .env + - touch storage/database/testing.db + - php artisan migrate --env=testing + - php artisan migrate --seed --env=testing script: - phpunit diff --git a/README.md b/README.md index 65a0265eac..479dd5f478 100644 --- a/README.md +++ b/README.md @@ -61,19 +61,19 @@ Everything is organised: _Please note that everything in these screenshots is fictional and may not be realistic._ -![Index](https://i.nder.be/c6hz06d3) +![Index](https://i.nder.be/hmp5mhw5) -![Accounts](https://i.nder.be/gzxxyz6n) +![Accounts](https://i.nder.be/hf5k02g9) -![Budgets](https://i.nder.be/hhu3krqk) +![Budgets](https://i.nder.be/gzv635mz) -![Reports 1](https://i.nder.be/cc3yspf6) +![Reports 1](https://i.nder.be/g0w698s3) -![Reports 2](https://i.nder.be/h6fp7xkb) +![Reports 2](https://i.nder.be/cr77nyxq) -![Bills](https://i.nder.be/c30zkcpv) +![Bills](https://i.nder.be/c7sugsz5) -![Piggy banks](https://i.nder.be/g20k0mdq) +![Piggy banks](https://i.nder.be/gy2nk0y4) ## Running and installing @@ -82,7 +82,13 @@ If you're still interested please read [the installation guide](https://github.c and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**. If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/). -This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one week. +This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one month. It's a trial. + +## Security + +You should always run Firefly III on a site with TLS enabled (https://). Please note that although some parts of the +database are encrypted (transaction descriptions, names, etc.) some parts are _not_ (amounts, dates, etc). If you need +more security, you must enable transparent database encryption or a comparable technology. ## Credits diff --git a/app/Generator/Chart/Budget/BudgetChartGenerator.php b/app/Generator/Chart/Budget/BudgetChartGenerator.php index 552a8b2933..c7aa64a863 100644 --- a/app/Generator/Chart/Budget/BudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/BudgetChartGenerator.php @@ -18,6 +18,13 @@ interface BudgetChartGenerator */ public function budget(Collection $entries); + /** + * @param Collection $entries + * + * @return array + */ + public function multiYear(Collection $entries); + /** * @param Collection $entries * diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index d91a293286..9bc1d28877 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -141,4 +141,37 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator return $data; } + + /** + * @param Collection $entries + * + * @return array + */ + public function multiYear(Collection $entries) + { + // dataset: + $data = [ + 'count' => 0, + 'labels' => [], + 'datasets' => [], + ]; + // get labels from one of the budgets (assuming there's at least one): + $first = $entries->first(); + foreach ($first['budgeted'] as $year => $noInterest) { + $data['labels'][] = strval($year); + } + + // then, loop all entries and create datasets: + foreach ($entries as $entry) { + $name = $entry['name']; + $spent = $entry['spent']; + $budgeted = $entry['budgeted']; + $data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)]; + $data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)]; + } + $data['count'] = count($data['datasets']); + + return $data; + + } } diff --git a/app/Generator/Chart/Category/CategoryChartGenerator.php b/app/Generator/Chart/Category/CategoryChartGenerator.php index 60f8c42f01..6b52518a34 100644 --- a/app/Generator/Chart/Category/CategoryChartGenerator.php +++ b/app/Generator/Chart/Category/CategoryChartGenerator.php @@ -19,6 +19,13 @@ interface CategoryChartGenerator */ public function all(Collection $entries); + /** + * @param Collection $entries + * + * @return array + */ + public function multiYear(Collection $entries); + /** * @param Collection $entries * diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index aa83c15103..07eb4e0f31 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -158,4 +158,41 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator return $data; } + + /** + * @param Collection $entries + * + * @return array + */ + public function multiYear(Collection $entries) + { + // dataset: + $data = [ + 'count' => 0, + 'labels' => [], + 'datasets' => [], + ]; + // get labels from one of the categories (assuming there's at least one): + $first = $entries->first(); + foreach ($first['spent'] as $year => $noInterest) { + $data['labels'][] = strval($year); + } + + // then, loop all entries and create datasets: + foreach ($entries as $entry) { + $name = $entry['name']; + $spent = $entry['spent']; + $earned = $entry['earned']; + if (array_sum(array_values($spent)) != 0) { + $data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)]; + } + if (array_sum(array_values($earned)) != 0) { + $data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)]; + } + } + $data['count'] = count($data['datasets']); + + return $data; + + } } diff --git a/app/Generator/Chart/Report/ChartJsReportChartGenerator.php b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php index 0a49d2354a..00d4484eb6 100644 --- a/app/Generator/Chart/Report/ChartJsReportChartGenerator.php +++ b/app/Generator/Chart/Report/ChartJsReportChartGenerator.php @@ -49,6 +49,39 @@ class ChartJsReportChartGenerator implements ReportChartGenerator return $data; } + /** + * Same as above but other translations. + * + * @param Collection $entries + * + * @return array + */ + public function multiYearInOut(Collection $entries) + { + $data = [ + 'count' => 2, + 'labels' => [], + 'datasets' => [ + [ + 'label' => trans('firefly.income'), + 'data' => [] + ], + [ + 'label' => trans('firefly.expenses'), + 'data' => [] + ] + ], + ]; + + foreach ($entries as $entry) { + $data['labels'][] = $entry[0]->formatLocalized('%Y'); + $data['datasets'][0]['data'][] = round($entry[1], 2); + $data['datasets'][1]['data'][] = round($entry[2], 2); + } + + return $data; + } + /** * @param string $income * @param string $expense @@ -80,4 +113,35 @@ class ChartJsReportChartGenerator implements ReportChartGenerator return $data; } + + /** + * @param string $income + * @param string $expense + * @param int $count + * + * @return array + */ + public function multiYearInOutSummarized($income, $expense, $count) + { + $data = [ + 'count' => 2, + 'labels' => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')], + 'datasets' => [ + [ + 'label' => trans('firefly.income'), + 'data' => [] + ], + [ + 'label' => trans('firefly.expenses'), + 'data' => [] + ] + ], + ]; + $data['datasets'][0]['data'][] = round($income, 2); + $data['datasets'][1]['data'][] = round( $expense, 2); + $data['datasets'][0]['data'][] = round(($income / $count), 2); + $data['datasets'][1]['data'][] = round(( $expense / $count), 2); + + return $data; + } } diff --git a/app/Generator/Chart/Report/ReportChartGenerator.php b/app/Generator/Chart/Report/ReportChartGenerator.php index 7e3c3039b0..f77ef80e03 100644 --- a/app/Generator/Chart/Report/ReportChartGenerator.php +++ b/app/Generator/Chart/Report/ReportChartGenerator.php @@ -12,12 +12,19 @@ use Illuminate\Support\Collection; interface ReportChartGenerator { + /** + * @param Collection $entries + * + * @return array + */ + public function yearInOut(Collection $entries); + /** * @param Collection $entries * * @return array */ - public function yearInOut(Collection $entries); + public function multiYearInOut(Collection $entries); /** * @param string $income @@ -28,4 +35,13 @@ interface ReportChartGenerator */ public function yearInOutSummarized($income, $expense, $count); + /** + * @param string $income + * @param string $expense + * @param int $count + * + * @return array + */ + public function multiYearInOutSummarized($income, $expense, $count); + } diff --git a/app/Helpers/Collection/BalanceLine.php b/app/Helpers/Collection/BalanceLine.php index 0c8ca55853..a5e48efae1 100644 --- a/app/Helpers/Collection/BalanceLine.php +++ b/app/Helpers/Collection/BalanceLine.php @@ -150,24 +150,4 @@ class BalanceLine { $this->balanceEntries = $balanceEntries; } - - /** - * If the BalanceEntries for a BalanceLine have a "left" value, the amount - * of money left in the entire BalanceLine is returned here: - * - * @return float - */ - public function sumOfLeft() - { - $sum = '0'; - bcscale(2); - /** @var BalanceEntry $balanceEntry */ - foreach ($this->getBalanceEntries() as $balanceEntry) { - $sum = bcadd($sum, $balanceEntry->getLeft()); - } - - return $sum; - } - - } diff --git a/app/Helpers/Collection/Category.php b/app/Helpers/Collection/Category.php index e365d2c27d..6e243a7631 100644 --- a/app/Helpers/Collection/Category.php +++ b/app/Helpers/Collection/Category.php @@ -37,6 +37,7 @@ class Category // spent is minus zero for an expense report: if ($category->spent < 0) { $this->categories->push($category); + $this->addTotal($category->spent); } } @@ -55,7 +56,7 @@ class Category */ public function getCategories() { - $set = $this->categories->sortByDesc( + $set = $this->categories->sortBy( function (CategoryModel $category) { return $category->spent; } diff --git a/app/Helpers/Collection/Expense.php b/app/Helpers/Collection/Expense.php index e165feb365..a703ee7a15 100644 --- a/app/Helpers/Collection/Expense.php +++ b/app/Helpers/Collection/Expense.php @@ -33,18 +33,24 @@ class Expense */ public function addOrCreateExpense(TransactionJournal $entry) { + bcscale(2); + $accountId = $entry->account_id; + $amount = strval(round($entry->amount, 2)); + if (bccomp('0', $amount) === -1) { + $amount = bcmul($amount, '-1'); + } + if (!$this->expenses->has($accountId)) { $newObject = new stdClass; - $newObject->amount = strval(round($entry->amount_positive, 2)); + $newObject->amount = $amount; $newObject->name = $entry->name; $newObject->count = 1; $newObject->id = $accountId; $this->expenses->put($accountId, $newObject); } else { - bcscale(2); $existing = $this->expenses->get($accountId); - $existing->amount = bcadd($existing->amount, $entry->amount_positive); + $existing->amount = bcadd($existing->amount, $amount); $existing->count++; $this->expenses->put($accountId, $existing); } @@ -55,8 +61,18 @@ class Expense */ public function addToTotal($add) { - $add = strval(round($add, 2)); bcscale(2); + + + $add = strval(round($add, 2)); + if (bccomp('0', $add) === -1) { + $add = bcmul($add, '-1'); + } + + // if amount is positive, the original transaction + // was a transfer. But since this is an expense report, + // that amount must be negative. + $this->total = bcadd($this->total, $add); } @@ -65,7 +81,7 @@ class Expense */ public function getExpenses() { - $set = $this->expenses->sortByDesc( + $set = $this->expenses->sortBy( function (stdClass $object) { return $object->amount; } diff --git a/app/Helpers/Csv/Importer.php b/app/Helpers/Csv/Importer.php index 73286efa38..936cee2956 100644 --- a/app/Helpers/Csv/Importer.php +++ b/app/Helpers/Csv/Importer.php @@ -296,7 +296,7 @@ class Importer // some debug info: $journalId = $journal->id; - $type = $journal->transactionType->type; + $type = $journal->getTransactionType(); /** @var Account $asset */ $asset = $this->importData['asset-account-object']; /** @var Account $opposing */ @@ -314,13 +314,13 @@ class Importer */ protected function getTransactionType() { - $transactionType = TransactionType::where('type', 'Deposit')->first(); + $transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first(); if ($this->importData['amount'] < 0) { - $transactionType = TransactionType::where('type', 'Withdrawal')->first(); + $transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); } if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) { - $transactionType = TransactionType::where('type', 'Transfer')->first(); + $transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first(); } return $transactionType; diff --git a/app/Helpers/Csv/PostProcessing/Currency.php b/app/Helpers/Csv/PostProcessing/Currency.php index a03b9db27c..e5014120be 100644 --- a/app/Helpers/Csv/PostProcessing/Currency.php +++ b/app/Helpers/Csv/PostProcessing/Currency.php @@ -24,7 +24,7 @@ class Currency implements PostProcessorInterface // fix currency if (is_null($this->data['currency'])) { - $currencyPreference = Preferences::get('currencyPreference', 'EUR'); + $currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')); $this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first(); } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 0c89f6d854..d26ee7512b 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -19,6 +19,8 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget as BudgetModel; use FireflyIII\Models\LimitRepetition; +use Illuminate\Support\Collection; +use Steam; /** * Class ReportHelper @@ -43,48 +45,113 @@ class ReportHelper implements ReportHelperInterface } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return CategoryCollection + */ + public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts) + { + $object = new CategoryCollection; + + /** + * GET CATEGORIES: + */ + /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); + $set = $repository->getCategories(); + foreach ($set as $category) { + $spent = $repository->balanceInPeriodForList($category, $start, $end, $accounts); + $category->spent = $spent; + $object->addCategory($category); + } + + return $object; + } + + /** + * @param Carbon $date + * + * @return array + */ + public function listOfMonths(Carbon $date) + { + + $start = clone $date; + $end = Carbon::now(); + $months = []; + while ($start <= $end) { + $year = $start->year; + + if (!isset($months[$year])) { + $months[$year] = [ + 'start' => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'), + 'end' => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'), + 'months' => [], + ]; + } + + $currentEnd = clone $start; + $currentEnd->endOfMonth(); + $months[$year]['months'][] = [ + 'formatted' => $start->formatLocalized('%B %Y'), + 'start' => $start->format('Y-m-d'), + 'end' => $currentEnd->format('Y-m-d'), + 'month' => $start->month, + 'year' => $year, + ]; + $start->addMonth(); + } + + return $months; + } /** * This method generates a full report for the given period on all - * the users asset and cash accounts. + * given accounts * - * @param Carbon $date - * @param Carbon $end - * @param $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return AccountCollection */ - public function getAccountReport(Carbon $date, Carbon $end, $shared) + public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts) { - - - $accounts = $this->query->getAllAccounts($date, $end, $shared); - $start = '0'; - $end = '0'; - $diff = '0'; + $startAmount = '0'; + $endAmount = '0'; + $diff = '0'; bcscale(2); - // remove cash account, if any: - $accounts = $accounts->filter( - function (Account $account) { - if ($account->accountType->type != 'Cash account') { - return $account; - } + $accounts->each( + function (Account $account) use ($start, $end) { + /** + * The balance for today always incorporates transactions + * made on today. So to get todays "start" balance, we sub one + * day. + */ + $yesterday = clone $start; + $yesterday->subDay(); - return null; + /** @noinspection PhpParamsInspection */ + $account->startBalance = Steam::balance($account, $yesterday); + $account->endBalance = Steam::balance($account, $end); } ); + // summarize: foreach ($accounts as $account) { - $start = bcadd($start, $account->startBalance); - $end = bcadd($end, $account->endBalance); - $diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance)); + $startAmount = bcadd($startAmount, $account->startBalance); + $endAmount = bcadd($endAmount, $account->endBalance); + $diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance)); } $object = new AccountCollection; - $object->setStart($start); - $object->setEnd($end); + $object->setStart($startAmount); + $object->setEnd($endAmount); $object->setDifference($diff); $object->setAccounts($accounts); @@ -92,37 +159,136 @@ class ReportHelper implements ReportHelperInterface } /** + * Get a full report on the users incomes during the period for the given accounts. * - * The balance report contains a Balance object which in turn contains: + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * A BalanceHeader object which contains all relevant user asset accounts for the report. + * @return Income + */ + public function getIncomeReport($start, $end, Collection $accounts) + { + $object = new Income; + $set = $this->query->incomeInPeriod($start, $end, $accounts); + foreach ($set as $entry) { + $object->addToTotal($entry->amount_positive); + $object->addOrCreateIncome($entry); + } + + return $object; + } + + /** + * Get a full report on the users expenses during the period for a list of accounts. * - * A number of BalanceLine objects, which hold: - * - A budget - * - A number of BalanceEntry objects. + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * The BalanceEntry object holds: - * - The same budget (again) - * - A user asset account as mentioned in the BalanceHeader - * - The amount of money spent on the budget by the user asset account + * @return Expense + */ + public function getExpenseReport($start, $end, Collection $accounts) + { + $object = new Expense; + $set = $this->query->expenseInPeriod($start, $end, $accounts); + foreach ($set as $entry) { + $object->addToTotal($entry->amount); // can be positive, if it's a transfer + $object->addOrCreateExpense($entry); + } + + return $object; + } + + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @return BudgetCollection + */ + public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts) + { + $object = new BudgetCollection; + /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $set = $repository->getBudgets(); + + bcscale(2); + + foreach ($set as $budget) { + + $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); + + // no repetition(s) for this budget: + if ($repetitions->count() == 0) { + $spent = $repository->balanceInPeriodForList($budget, $start, $end, $accounts); + $budgetLine = new BudgetLine; + $budgetLine->setBudget($budget); + $budgetLine->setOverspent($spent); + $object->addOverspent($spent); + $object->addBudgetLine($budgetLine); + continue; + } + + // one or more repetitions for budget: + /** @var LimitRepetition $repetition */ + foreach ($repetitions as $repetition) { + $budgetLine = new BudgetLine; + $budgetLine->setBudget($budget); + $budgetLine->setRepetition($repetition); + $expenses = $repository->balanceInPeriodForList($budget, $start, $end, $accounts); + + // 200 en -100 is 100, vergeleken met 0 === 1 + // 200 en -200 is 0, vergeleken met 0 === 0 + // 200 en -300 is -100, vergeleken met 0 === -1 + + $left = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : 0; + $spent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0'; + $overspent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount); + + $budgetLine->setLeft($left); + $budgetLine->setSpent($spent); + $budgetLine->setOverspent($overspent); + $budgetLine->setBudgeted($repetition->amount); + + $object->addBudgeted($repetition->amount); + $object->addSpent($spent); + $object->addLeft($left); + $object->addOverspent($overspent); + $object->addBudgetLine($budgetLine); + + } + + } + + // stuff outside of budgets: + $noBudget = $repository->getWithoutBudgetSum($start, $end); + $budgetLine = new BudgetLine; + $budgetLine->setOverspent($noBudget); + $budgetLine->setSpent($noBudget); + $object->addOverspent($noBudget); + $object->addBudgetLine($budgetLine); + + return $object; + } + + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return Balance */ - public function getBalanceReport(Carbon $start, Carbon $end, $shared) + public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) { $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); $balance = new Balance; // build a balance header: - $header = new BalanceHeader; - - $accounts = $this->query->getAllAccounts($start, $end, $shared); - $budgets = $repository->getBudgets(); + $header = new BalanceHeader; + $budgets = $repository->getBudgets(); foreach ($accounts as $account) { $header->addAccount($account); } @@ -134,6 +300,7 @@ class ReportHelper implements ReportHelperInterface // get budget amount for current period: $rep = $repository->getCurrentRepetition($budget, $start, $end); + // could be null? $line->setRepetition($rep); // loop accounts: @@ -142,7 +309,7 @@ class ReportHelper implements ReportHelperInterface $balanceEntry->setAccount($account); // get spent: - $spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant. + $spent = $this->query->spentInBudget($account, $budget, $start, $end); // I think shared is irrelevant. $balanceEntry->setSpent($spent); $line->addBalanceEntry($balanceEntry); @@ -153,6 +320,7 @@ class ReportHelper implements ReportHelperInterface // then a new line for without budget. // and one for the tags: + // and one for "left unbalanced". $empty = new BalanceLine; $tags = new BalanceLine; $diffLine = new BalanceLine; @@ -164,7 +332,7 @@ class ReportHelper implements ReportHelperInterface $spent = $this->query->spentNoBudget($account, $start, $end); $left = $tagRepository->coveredByBalancingActs($account, $start, $end); bcscale(2); - $diff = bcsub($spent, $left); + $diff = bcadd($spent, $left); // budget $budgetEntry = new BalanceEntry; @@ -199,16 +367,19 @@ class ReportHelper implements ReportHelperInterface * This method generates a full report for the given period on all * the users bills and their payments. * - * @param Carbon $start - * @param Carbon $end + * Excludes bills which have not had a payment on the mentioned accounts. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return BillCollection */ - public function getBillReport(Carbon $start, Carbon $end) + public function getBillReport(Carbon $start, Carbon $end, Collection $accounts) { /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); - $bills = $repository->getBills(); + $bills = $repository->getBillsForAccounts($accounts); $collection = new BillCollection; /** @var Bill $bill */ @@ -238,168 +409,5 @@ class ReportHelper implements ReportHelperInterface } return $collection; - - } - - /** - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared - * - * @return BudgetCollection - */ - public function getBudgetReport(Carbon $start, Carbon $end, $shared) - { - $object = new BudgetCollection; - /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); - $set = $repository->getBudgets(); - - bcscale(2); - - foreach ($set as $budget) { - - $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); - - // no repetition(s) for this budget: - if ($repetitions->count() == 0) { - $spent = $repository->balanceInPeriod($budget, $start, $end, $shared); - $budgetLine = new BudgetLine; - $budgetLine->setBudget($budget); - $budgetLine->setOverspent($spent); - $object->addOverspent($spent); - $object->addBudgetLine($budgetLine); - continue; - } - - // one or more repetitions for budget: - /** @var LimitRepetition $repetition */ - foreach ($repetitions as $repetition) { - $budgetLine = new BudgetLine; - $budgetLine->setBudget($budget); - $budgetLine->setRepetition($repetition); - $expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared); - $expenses = $expenses * -1; - $left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0; - $spent = $expenses > $repetition->amount ? 0 : $expenses; - $overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0; - - $budgetLine->setLeft($left); - $budgetLine->setSpent($spent); - $budgetLine->setOverspent($overspent); - $budgetLine->setBudgeted($repetition->amount); - - $object->addBudgeted($repetition->amount); - $object->addSpent($spent); - $object->addLeft($left); - $object->addOverspent($overspent); - $object->addBudgetLine($budgetLine); - - } - - } - - // stuff outside of budgets: - $noBudget = $repository->getWithoutBudgetSum($start, $end); - $budgetLine = new BudgetLine; - $budgetLine->setOverspent($noBudget); - $object->addOverspent($noBudget); - $object->addBudgetLine($budgetLine); - - return $object; - } - - /** - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared - * - * @return CategoryCollection - */ - public function getCategoryReport(Carbon $start, Carbon $end, $shared) - { - $object = new CategoryCollection; - - - /** - * GET CATEGORIES: - */ - /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); - $set = $repository->getCategories(); - foreach ($set as $category) { - $spent = $repository->balanceInPeriod($category, $start, $end, $shared); - $category->spent = $spent; - $object->addCategory($category); - $object->addTotal($spent); - } - - return $object; - } - - /** - * Get a full report on the users expenses during the period. - * - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared - * - * @return Expense - */ - public function getExpenseReport($start, $end, $shared) - { - $object = new Expense; - $set = $this->query->expenseInPeriodCorrected($start, $end, $shared); - foreach ($set as $entry) { - $object->addToTotal($entry->amount_positive); - $object->addOrCreateExpense($entry); - } - - return $object; - } - - /** - * Get a full report on the users incomes during the period. - * - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared - * - * @return Income - */ - public function getIncomeReport($start, $end, $shared) - { - $object = new Income; - $set = $this->query->incomeInPeriodCorrected($start, $end, $shared); - foreach ($set as $entry) { - $object->addToTotal($entry->amount_positive); - $object->addOrCreateIncome($entry); - } - - return $object; - } - - /** - * @param Carbon $date - * - * @return array - */ - public function listOfMonths(Carbon $date) - { - - $start = clone $date; - $end = Carbon::now(); - $months = []; - while ($start <= $end) { - $year = $start->year; - $months[$year][] = [ - 'formatted' => $start->formatLocalized('%B %Y'), - 'month' => $start->month, - 'year' => $year, - ]; - $start->addMonth(); - } - - return $months; } } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 91f9c8d0f5..f52036cac2 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -10,6 +10,7 @@ use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\Category as CategoryCollection; use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; +use Illuminate\Support\Collection; /** * Interface ReportHelperInterface @@ -21,75 +22,78 @@ interface ReportHelperInterface /** * This method generates a full report for the given period on all - * the users asset and cash accounts. + * given accounts * - * @param Carbon $date - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return AccountCollection */ - public function getAccountReport(Carbon $date, Carbon $end, $shared); + public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts); /** * This method generates a full report for the given period on all * the users bills and their payments. * - * @param Carbon $start - * @param Carbon $end + * Excludes bills which have not had a payment on the mentioned accounts. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return BillCollection */ - public function getBillReport(Carbon $start, Carbon $end); + public function getBillReport(Carbon $start, Carbon $end, Collection $accounts); /** - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return Balance */ - public function getBalanceReport(Carbon $start, Carbon $end, $shared); + public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts); /** - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return BudgetCollection */ - public function getBudgetReport(Carbon $start, Carbon $end, $shared); + public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts); /** - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return CategoryCollection */ - public function getCategoryReport(Carbon $start, Carbon $end, $shared); + public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts); /** - * Get a full report on the users expenses during the period. + * Get a full report on the users expenses during the period for a list of accounts. * - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return Expense */ - public function getExpenseReport($start, $end, $shared); + public function getExpenseReport($start, $end, Collection $accounts); /** - * Get a full report on the users incomes during the period. + * Get a full report on the users incomes during the period for the given accounts. * - * @param Carbon $start - * @param Carbon $end - * @param boolean $shared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return Income */ - public function getIncomeReport($start, $end, $shared); + public function getIncomeReport($start, $end, Collection $accounts); /** * @param Carbon $date diff --git a/app/Helpers/Report/ReportQuery.php b/app/Helpers/Report/ReportQuery.php index de7c08158b..878da06a26 100644 --- a/app/Helpers/Report/ReportQuery.php +++ b/app/Helpers/Report/ReportQuery.php @@ -8,10 +8,10 @@ use Crypt; use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; -use Steam; /** * Class ReportQuery @@ -20,178 +20,6 @@ use Steam; */ class ReportQuery implements ReportQueryInterface { - /** - * See ReportQueryInterface::incomeInPeriodCorrected. - * - * This method's length is caused mainly by the query build stuff. Therefor: - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * - * @param Carbon $start - * @param Carbon $end - * @param bool $includeShared - * - * @return Collection - * - */ - public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) - { - $query = $this->queryJournalsWithTransactions($start, $end); - if ($includeShared === false) { - $query->where( - function (Builder $query) { - $query->where( - function (Builder $q) { // only get withdrawals not from a shared account - $q->where('transaction_types.type', 'Withdrawal'); - $q->where('acm_from.data', '!=', '"sharedAsset"'); - } - ); - $query->orWhere( - function (Builder $q) { // and transfers from a shared account. - $q->where('transaction_types.type', 'Transfer'); - $q->where('acm_to.data', '=', '"sharedAsset"'); - $q->where('acm_from.data', '!=', '"sharedAsset"'); - } - ); - } - ); - } else { - $query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine. - } - $query->orderBy('transaction_journals.date'); - $data = $query->get( // get everything - ['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted'] - ); - - $data->each( - function (TransactionJournal $journal) { - if (intval($journal->account_encrypted) == 1) { - $journal->name = Crypt::decrypt($journal->name); - } - } - ); - - return $data; - } - - /** - * Get a users accounts combined with various meta-data related to the start and end date. - * - * @param Carbon $start - * @param Carbon $end - * @param bool $includeShared - * - * @return Collection - */ - public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false) - { - $query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC') - ->accountTypeIn(['Default account', 'Asset account', 'Cash account']); - if ($includeShared === false) { - $query->leftJoin( - 'account_meta', function (JoinClause $join) { - $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); - } - ) - ->where( - function (Builder $query) { - - $query->where('account_meta.data', '!=', '"sharedAsset"'); - $query->orWhereNull('account_meta.data'); - - } - ); - } - $set = $query->get(['accounts.*']); - $set->each( - function (Account $account) use ($start, $end) { - /** - * The balance for today always incorporates transactions - * made on today. So to get todays "start" balance, we sub one - * day. - */ - $yesterday = clone $start; - $yesterday->subDay(); - - /** @noinspection PhpParamsInspection */ - $account->startBalance = Steam::balance($account, $yesterday); - $account->endBalance = Steam::balance($account, $end); - } - ); - - return $set; - } - - - /** - * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results - * will simply list the transaction journals only. This should allow any follow up counting to be accurate with - * regards to tags. - * - * This method returns all "income" journals in a certain period, which are both transfers from a shared account - * and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does - * not group and returns different fields. - * - * @param Carbon $start - * @param Carbon $end - * @param bool $includeShared - * - * @return Collection - */ - public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false) - { - $query = $this->queryJournalsWithTransactions($start, $end); - if ($includeShared === false) { - // only get deposits not to a shared account - // and transfers to a shared account. - $query->where( - function (Builder $query) { - $query->where( - function (Builder $q) { - $q->where('transaction_types.type', 'Deposit'); - $q->where('acm_to.data', '!=', '"sharedAsset"'); - } - ); - $query->orWhere( - function (Builder $q) { - $q->where('transaction_types.type', 'Transfer'); - $q->where('acm_from.data', '=', '"sharedAsset"'); - $q->where('acm_to.data','!=','"sharedAsset"'); - } - ); - } - ); - } else { - // any deposit is fine. - $query->where('transaction_types.type', 'Deposit'); - } - $query->orderBy('transaction_journals.date'); - - // get everything - $data = $query->get( - ['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted'] - ); - - $data->each( - function (TransactionJournal $journal) { - if (intval($journal->account_encrypted) == 1) { - $journal->name = Crypt::decrypt($journal->name); - } - } - ); - $data = $data->filter( - function (TransactionJournal $journal) { - if ($journal->amount != 0) { - return $journal; - } - - return null; - } - ); - - return $data; - } - /** * Covers tags * @@ -202,22 +30,18 @@ class ReportQuery implements ReportQueryInterface * * @return float */ - public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end) + public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end) { - bcscale(2); - - return bcmul( - Auth::user()->transactionjournals() - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->transactionTypes(['Withdrawal']) - ->where('transactions.account_id', $account->id) - ->before($end) - ->after($start) - ->where('budget_transaction_journal.budget_id', $budget->id) - ->get(['transaction_journals.*'])->sum('amount'), -1 - ); + return Auth::user()->transactionjournals() + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->transactionTypes([TransactionType::WITHDRAWAL]) + ->where('transactions.account_id', $account->id) + ->before($end) + ->after($start) + ->where('budget_transaction_journal.budget_id', $budget->id) + ->get(['transaction_journals.*'])->sum('amount'); } /** @@ -233,7 +57,7 @@ class ReportQuery implements ReportQueryInterface Auth::user()->transactionjournals() ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->transactionTypes(['Withdrawal']) + ->transactionTypes([TransactionType::WITHDRAWAL]) ->where('transactions.account_id', $account->id) ->before($end) ->after($start) @@ -276,4 +100,137 @@ class ReportQuery implements ReportQueryInterface return $query; } + + /** + * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results + * will simply list the transaction journals only. This should allow any follow up counting to be accurate with + * regards to tags. It will only get the incomes to the specified accounts. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Collection + */ + public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts) + { + $query = $this->queryJournalsWithTransactions($start, $end); + + $ids = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $ids[] = $account->id; + } + + // OR is a deposit + // OR any transfer TO the accounts in $accounts, not FROM any of the accounts in $accounts. + $query->where( + function (Builder $query) use ($ids) { + $query->where( + function (Builder $q) { + $q->where('transaction_types.type', TransactionType::DEPOSIT); + } + ); + $query->orWhere( + function (Builder $q) use ($ids) { + $q->where('transaction_types.type', TransactionType::TRANSFER); + $q->whereNotIn('ac_from.id',$ids); + $q->whereIn('ac_to.id', $ids); + } + ); + } + ); + + // only include selected accounts. + $query->whereIn('ac_to.id', $ids); + $query->orderBy('transaction_journals.date'); + + // get everything + $data = $query->get( + ['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted'] + ); + + $data->each( + function (TransactionJournal $journal) { + if (intval($journal->account_encrypted) == 1) { + $journal->name = Crypt::decrypt($journal->name); + } + } + ); + $data = $data->filter( + function (TransactionJournal $journal) { + if ($journal->amount != 0) { + return $journal; + } + + return null; + } + ); + + return $data; + } + + /** + * See ReportQueryInterface::incomeInPeriod + * + * This method returns all "expense" journals in a certain period, which are both transfers to a shared account + * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does + * not group and returns different fields. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return Collection + * + */ + public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts) + { + $ids = []; + + /** @var Account $account */ + foreach ($accounts as $account) { + $ids[] = $account->id; + } + + $query = $this->queryJournalsWithTransactions($start, $end); + + // withdrawals from any account are an expense. + // transfers away, from an account in the list, to an account not in the list, are an expense. + + $query->where( + function (Builder $query) use ($ids) { + $query->where( + function (Builder $q) { + $q->where('transaction_types.type', TransactionType::WITHDRAWAL); + } + ); + $query->orWhere( + function (Builder $q) use ($ids) { + $q->where('transaction_types.type', TransactionType::TRANSFER); + $q->whereIn('ac_from.id', $ids); + $q->whereNotIn('ac_to.id', $ids); + } + ); + } + ); + + // expense goes from the selected accounts: + $query->whereIn('ac_from.id', $ids); + + $query->orderBy('transaction_journals.date'); + $data = $query->get( // get everything + ['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted'] + ); + + $data->each( + function (TransactionJournal $journal) { + if (intval($journal->account_encrypted) == 1) { + $journal->name = Crypt::decrypt($journal->name); + } + } + ); + + return $data; + } } diff --git a/app/Helpers/Report/ReportQueryInterface.php b/app/Helpers/Report/ReportQueryInterface.php index c48aeeab59..8579c52926 100644 --- a/app/Helpers/Report/ReportQueryInterface.php +++ b/app/Helpers/Report/ReportQueryInterface.php @@ -16,7 +16,7 @@ interface ReportQueryInterface { /** - * See ReportQueryInterface::incomeInPeriodCorrected + * See ReportQueryInterface::incomeInPeriod * * This method returns all "expense" journals in a certain period, which are both transfers to a shared account * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does @@ -24,36 +24,25 @@ interface ReportQueryInterface * * @param Carbon $start * @param Carbon $end - * @param bool $includeShared + * @param Collection $accounts * * @return Collection * */ - public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); - - /** - * Get a users accounts combined with various meta-data related to the start and end date. - * - * @param Carbon $start - * @param Carbon $end - * @param bool $includeShared - * - * @return Collection - */ - public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false); + public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts); /** * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results * will simply list the transaction journals only. This should allow any follow up counting to be accurate with - * regards to tags. + * regards to tags. It will only get the incomes to the specified accounts. * - * @param Carbon $start - * @param Carbon $end - * @param bool $includeShared + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return Collection */ - public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false); + public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts); /** * Covers tags as well. @@ -65,7 +54,7 @@ interface ReportQueryInterface * * @return float */ - public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end); + public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end); /** * @param Account $account diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 7caeb6b87d..d7620f07b0 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -1,6 +1,7 @@ where('blocked', 1)->first(); if (!is_null($foundUser)) { // if it exists, show message: - $code = $foundUser->blocked_code; - if(strlen($code) == 0) { + $code = $foundUser->blocked_code; + if (strlen($code) == 0) { $code = 'general_blocked'; } $message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]); @@ -160,21 +160,35 @@ class AuthController extends Controller } // @codeCoverageIgnoreEnd + $data = $request->all(); $data['password'] = bcrypt($data['password']); + // is user email domain blocked? + if ($this->isBlockedDomain($data['email'])) { + $validator->getMessageBag()->add('email', trans('validation.invalid_domain')); + $this->throwValidationException( + $request, $validator + ); + } + Auth::login($this->create($data)); // get the email address if (Auth::user() instanceof User) { - $email = Auth::user()->email; - $address = route('index'); + $email = Auth::user()->email; + $address = route('index'); + $ipAddress = $request->ip(); // send email. - Mail::send( - ['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) { - $message->to($email, $email)->subject('Welcome to Firefly III! '); + try { + Mail::send( + ['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) { + $message->to($email, $email)->subject('Welcome to Firefly III! '); + } + ); + } catch (\Swift_TransportException $e) { + Log::error($e->getMessage()); } - ); // set flash message Session::flash('success', 'You have registered successfully!'); @@ -197,6 +211,32 @@ class AuthController extends Controller // @codeCoverageIgnoreEnd } + /** + * @return array + */ + protected function getBlockedDomains() { + $set = Config::get('mail.blocked_domains'); + $domains = []; + foreach($set as $entry) { + $domain = trim($entry); + if(strlen($domain) > 0) { + $domains[] = $domain; + } + } + return $domains; + } + + protected function isBlockedDomain($email) + { + $parts = explode('@', $email); + $blocked = $this->getBlockedDomains(); + + if (isset($parts[1]) && in_array($parts[1], $blocked)) { + return true; + } + return false; + } + /** * Get a validator for an incoming registration request. * diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php index 8f618f136d..7cb3706ecb 100644 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -2,7 +2,12 @@ use FireflyIII\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ResetsPasswords; - +use FireflyIII\User; +use Illuminate\Http\Request; +use Illuminate\Mail\Message; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Password; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class PasswordController * @@ -41,4 +46,35 @@ class PasswordController extends Controller $this->middleware('guest'); } + /** + * Send a reset link to the given user. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function postEmail(Request $request) + { + $this->validate($request, ['email' => 'required|email']); + + $user = User::whereEmail($request->get('email'))->first(); + + if (!is_null($user) && intval($user->blocked) === 1) { + $response = 'passwords.blocked'; + } else { + $response = Password::sendResetLink($request->only('email'), function (Message $message) { + $message->subject($this->getEmailSubject()); + }); + } + + switch ($response) { + case Password::RESET_LINK_SENT: + return redirect()->back()->with('status', trans($response)); + + case Password::INVALID_USER: + case 'passwords.blocked': + return redirect()->back()->withErrors(['email' => trans($response)]); + + } + } + } diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 7269b0ba31..74876933e4 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -6,6 +6,7 @@ use Carbon\Carbon; use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Input; use Navigation; @@ -135,7 +136,7 @@ class BudgetController extends Controller * * @return \Illuminate\View\View */ - public function index(BudgetRepositoryInterface $repository) + public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository) { $budgets = $repository->getActiveBudgets(); $inactive = $repository->getInactiveBudgets(); @@ -147,6 +148,8 @@ class BudgetController extends Controller $key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd'); $budgetIncomeTotal = Preferences::get($key, 1000)->data; $period = Navigation::periodShow($start, $range); + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); + bcscale(2); /** * Do some cleanup: @@ -156,7 +159,7 @@ class BudgetController extends Controller // loop the budgets: /** @var Budget $budget */ foreach ($budgets as $budget) { - $budget->spent = $repository->balanceInPeriod($budget, $start, $end); + $budget->spent = $repository->balanceInPeriodForList($budget, $start, $end, $accounts); $budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end); if ($budget->currentRep) { $budgeted = bcadd($budgeted, $budget->currentRep->amount); @@ -170,7 +173,7 @@ class BudgetController extends Controller $defaultCurrency = Amount::getDefaultCurrency(); return view( - 'budgets.index', compact('budgetMaximum','period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted') + 'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted') ); } @@ -279,7 +282,7 @@ class BudgetController extends Controller { $budgetData = [ 'name' => $request->input('name'), - 'active' => intval($request->input('active')) == 1 + 'active' => intval($request->input('active')) == 1, ]; $repository->update($budget, $budgetData); diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 116e2c00aa..a7df17912f 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -79,6 +79,38 @@ class AccountController extends Controller return Response::json($data); } + /** + * Shows the balances for a given set of dates and accounts. + * + * TODO fix parameters. + * + * @param AccountRepositoryInterface $repository + * + * @param $url + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function report($report_type, Carbon $start, Carbon $end, Collection $accounts) + { + // chart properties for cache: + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('all'); + $cache->addProperty('accounts'); + $cache->addProperty('default'); + $cache->addProperty($accounts); + if ($cache->has()) { + return Response::json($cache->get()); // @codeCoverageIgnore + } + + // make chart: + $data = $this->generator->all($accounts, $start, $end); + $cache->store($data); + + return Response::json($data); + } + /** * Shows the balances for all the user's expense accounts. * diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 0e017fd380..daa0fdf128 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -6,6 +6,7 @@ use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -35,13 +36,86 @@ class BudgetController extends Controller $this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator'); } + /** + * @param BudgetRepositoryInterface $repository + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * @param Collection $budgets + */ + public function multiYear(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) + { + // chart properties for cache: + $cache = new CacheProperties(); + $cache->addProperty($report_type); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($accounts); + $cache->addProperty($budgets); + $cache->addProperty('multiYearBudget'); + + if ($cache->has()) { + return Response::json($cache->get()); // @codeCoverageIgnore + } + + /** + * budget + * year: + * spent: x + * budgeted: x + * year + * spent: x + * budgeted: x + */ + $entries = new Collection; + // go by budget, not by year. + foreach ($budgets as $budget) { + $entry = ['name' => '', 'spent' => [], 'budgeted' => []]; + + $currentStart = clone $start; + while ($currentStart < $end) { + // fix the date: + $currentEnd = clone $currentStart; + $currentEnd->endOfYear(); + + // get data: + if (is_null($budget->id)) { + $name = trans('firefly.noBudget'); + $sum = $repository->getWithoutBudgetSum($currentStart, $currentEnd); + $budgeted = 0; + } else { + $name = $budget->name; + $sum = $repository->balanceInPeriodForList($budget, $currentStart, $currentEnd, $accounts); + $budgeted = $repository->getBudgetLimitRepetitions($budget, $currentStart, $currentEnd)->sum('amount'); + } + + // save to array: + $year = $currentStart->year; + $entry['name'] = $name; + $entry['spent'][$year] = ($sum * -1); + $entry['budgeted'][$year] = $budgeted; + + // jump to next year. + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + $entries->push($entry); + } + // generate chart with data: + $data = $this->generator->multiYear($entries); + + return Response::json($data); + + } + /** * @param BudgetRepositoryInterface $repository * @param Budget $budget * * @return \Symfony\Component\HttpFoundation\Response */ - public function budget(BudgetRepositoryInterface $repository, Budget $budget) + public function budget(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget) { // dates and times @@ -50,7 +124,9 @@ class BudgetController extends Controller $last = Session::get('end', new Carbon); $final = clone $last; $final->addYears(2); - $last = Navigation::endOfX($last, $range, $final); + $last = Navigation::endOfX($last, $range, $final); + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); + // chart properties for cache: $cache = new CacheProperties(); @@ -68,7 +144,7 @@ class BudgetController extends Controller $end->subDay(); $chartDate = clone $end; $chartDate->startOfMonth(); - $spent = $repository->balanceInPeriod($budget, $first, $end) * -1; + $spent = $repository->balanceInPeriodForList($budget, $first, $end, $accounts) * -1; $entries->push([$chartDate, $spent]); $first = Navigation::addPeriod($first, $range, 0); } @@ -113,7 +189,7 @@ class BudgetController extends Controller /* * Sum of expenses on this day: */ - $sum = $repository->expensesOnDayCorrected($budget, $start); + $sum = $repository->expensesOnDay($budget, $start); $amount = bcadd($amount, $sum); $entries->push([clone $start, $amount]); $start->addDay(); @@ -133,12 +209,13 @@ class BudgetController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(BudgetRepositoryInterface $repository) + public function frontpage(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository) { $budgets = $repository->getBudgets(); $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); $allEntries = new Collection; + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); // chart properties for cache: $cache = new CacheProperties(); @@ -156,13 +233,13 @@ class BudgetController extends Controller foreach ($budgets as $budget) { $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); if ($repetitions->count() == 0) { - $expenses = $repository->balanceInPeriod($budget, $start, $end, true) * -1; + $expenses = $repository->balanceInPeriodForList($budget, $start, $end, $accounts) * -1; $allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]); continue; } /** @var LimitRepetition $repetition */ foreach ($repetitions as $repetition) { - $expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true) * -1; + $expenses = $repository->balanceInPeriodForList($budget, $repetition->startdate, $repetition->enddate, $accounts) * -1; // $left can be less than zero. // $overspent can be more than zero ( = overspending) @@ -197,11 +274,8 @@ class BudgetController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function year(BudgetRepositoryInterface $repository, $year, $shared = false) + public function year(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) { - $start = new Carbon($year . '-01-01'); - $end = new Carbon($year . '-12-31'); - $shared = $shared == 'shared' ? true : false; $allBudgets = $repository->getBudgets(); $budgets = new Collection; @@ -218,7 +292,7 @@ class BudgetController extends Controller // filter empty budgets: foreach ($allBudgets as $budget) { - $spent = $repository->balanceInPeriod($budget, $start, $end, $shared); + $spent = $repository->balanceInPeriodForList($budget, $start, $end, $accounts); if ($spent != 0) { $budgets->push($budget); } @@ -234,7 +308,7 @@ class BudgetController extends Controller // each budget, fill the row: foreach ($budgets as $budget) { - $spent = $repository->balanceInPeriod($budget, $start, $month, $shared); + $spent = $repository->balanceInPeriodForList($budget, $start, $month, $accounts); $row[] = $spent * -1; } $entries->push($row); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index bbc0e143fd..ec7dafbef5 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -107,7 +107,7 @@ class CategoryController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - $array = $repository->getCategoriesAndExpensesCorrected($start, $end); + $array = $repository->getCategoriesAndExpenses($start, $end); // sort by callback: uasort( $array, @@ -121,6 +121,84 @@ class CategoryController extends Controller ); $set = new Collection($array); $data = $this->generator->frontpage($set); + $cache->store($data); + + + return Response::json($data); + + } + + /** + * @param CategoryRepositoryInterface $repository + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * @param Collection $categories + */ + public function multiYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $categories) + { + // chart properties for cache: + $cache = new CacheProperties(); + $cache->addProperty($report_type); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($accounts); + $cache->addProperty($categories); + $cache->addProperty('multiYearCategory'); + + if ($cache->has()) { + return Response::json($cache->get()); // @codeCoverageIgnore + } + + /** + * category + * year: + * spent: x + * earned: x + * year + * spent: x + * earned: x + */ + $entries = new Collection; + // go by budget, not by year. + /** @var Category $category */ + foreach ($categories as $category) { + $entry = ['name' => '', 'spent' => [], 'earned' => []]; + + $currentStart = clone $start; + while ($currentStart < $end) { + // fix the date: + $currentEnd = clone $currentStart; + $currentEnd->endOfYear(); + + // get data: + if (is_null($category->id)) { + $name = trans('firefly.noCategory'); + $spent = $repository->spentNoCategoryForAccounts($accounts, $currentStart, $currentEnd); + $earned = $repository->earnedNoCategoryForAccounts($accounts, $currentStart, $currentEnd); + } else { + $name = $category->name; + $spent = $repository->spentInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); + $earned = $repository->earnedInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); + } + + // save to array: + $year = $currentStart->year; + $entry['name'] = $name; + $entry['spent'][$year] = ($spent * -1); + $entry['earned'][$year] = $earned; + + // jump to next year. + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + $entries->push($entry); + } + // generate chart with data: + $data = $this->generator->multiYear($entries); + $cache->store($data); + return Response::json($data); @@ -151,8 +229,8 @@ class CategoryController extends Controller while ($start <= $end) { - $spent = $repository->spentOnDaySumCorrected($category, $start); - $earned = $repository->earnedOnDaySumCorrected($category, $start); + $spent = $repository->spentOnDaySum($category, $start); + $earned = $repository->earnedOnDaySum($category, $start); $date = Navigation::periodShow($start, '1D'); $entries->push([clone $start, $date, $spent, $earned]); $start->addDay(); @@ -194,8 +272,8 @@ class CategoryController extends Controller while ($start <= $end) { - $spent = $repository->spentOnDaySumCorrected($category, $start); - $earned = $repository->earnedOnDaySumCorrected($category, $start); + $spent = $repository->spentOnDaySum($category, $start); + $earned = $repository->earnedOnDaySum($category, $start); $theDate = Navigation::periodShow($start, '1D'); $entries->push([clone $start, $theDate, $spent, $earned]); $start->addDay(); @@ -213,15 +291,15 @@ class CategoryController extends Controller * This chart will only show expenses. * * @param CategoryRepositoryInterface $repository - * @param $year - * @param bool $shared + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false) + public function spentInYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) { - $start = new Carbon($year . '-01-01'); - $end = new Carbon($year . '-12-31'); $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); @@ -232,12 +310,11 @@ class CategoryController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - $shared = $shared == 'shared' ? true : false; $allCategories = $repository->getCategories(); $entries = new Collection; $categories = $allCategories->filter( - function (Category $category) use ($repository, $start, $end, $shared) { - $spent = $repository->balanceInPeriod($category, $start, $end, $shared); + function (Category $category) use ($repository, $start, $end, $accounts) { + $spent = $repository->balanceInPeriodForList($category, $start, $end, $accounts); if ($spent < 0) { return $category; } @@ -252,7 +329,7 @@ class CategoryController extends Controller $row = [clone $start]; // make a row: foreach ($categories as $category) { // each budget, fill the row - $spent = $repository->balanceInPeriod($category, $start, $month, $shared); + $spent = $repository->balanceInPeriodForList($category, $start, $month, $accounts); if ($spent < 0) { $row[] = $spent * -1; } else { @@ -272,16 +349,15 @@ class CategoryController extends Controller * This chart will only show income. * * @param CategoryRepositoryInterface $repository - * @param $year - * @param bool $shared + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false) + public function earnedInYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) { - $start = new Carbon($year . '-01-01'); - $end = new Carbon($year . '-12-31'); - $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); $cache->addProperty($end); @@ -291,12 +367,11 @@ class CategoryController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - $shared = $shared == 'shared' ? true : false; $allCategories = $repository->getCategories(); $allEntries = new Collection; $categories = $allCategories->filter( - function (Category $category) use ($repository, $start, $end, $shared) { - $spent = $repository->balanceInPeriod($category, $start, $end, $shared); + function (Category $category) use ($repository, $start, $end, $accounts) { + $spent = $repository->balanceInPeriodForList($category, $start, $end, $accounts); if ($spent > 0) { return $category; } @@ -311,7 +386,7 @@ class CategoryController extends Controller $row = [clone $start]; // make a row: foreach ($categories as $category) { // each budget, fill the row - $spent = $repository->balanceInPeriod($category, $start, $month, $shared); + $spent = $repository->balanceInPeriodForList($category, $start, $month, $accounts); if ($spent > 0) { $row[] = $spent; } else { diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 6522f8b2a7..9311589093 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -9,7 +9,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Response; -use Log; /** * Class ReportController @@ -37,41 +36,66 @@ class ReportController extends Controller * Summarizes all income and expenses, per month, for a given year. * * @param ReportQueryInterface $query - * @param $year - * @param bool $shared + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function yearInOut(ReportQueryInterface $query, $year, $shared = false) + public function yearInOut(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts) { - // get start and end of year - $start = new Carbon($year . '-01-01'); - $end = new Carbon($year . '-12-31'); - $shared = $shared == 'shared' ? true : false; - // chart properties for cache: $cache = new CacheProperties; $cache->addProperty('yearInOut'); - $cache->addProperty($year); - $cache->addProperty($shared); + $cache->addProperty($start); + $cache->addProperty($accounts); + $cache->addProperty($end); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } - $entries = new Collection; - while ($start < $end) { - $month = clone $start; - $month->endOfMonth(); - // total income and total expenses: - $incomeSum = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive'); - $expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive'); - $entries->push([clone $start, $incomeSum, $expenseSum]); - $start->addMonth(); + // per year? + if ($start->diffInMonths($end) > 12) { + + $entries = new Collection; + while ($start < $end) { + $startOfYear = clone $start; + $startOfYear->startOfYear(); + $endOfYear = clone $startOfYear; + $endOfYear->endOfYear(); + + // total income and total expenses: + $incomeSum = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); + $expenseSum = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); + + $entries->push([clone $start, $incomeSum, $expenseSum]); + $start->addYear(); + } + + $data = $this->generator->multiYearInOut($entries); + $cache->store($data); + } else { + // per month: + + $entries = new Collection; + while ($start < $end) { + $month = clone $start; + $month->endOfMonth(); + // total income and total expenses: + $incomeSum = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive'); + $expenseSum = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive'); + + + $entries->push([clone $start, $incomeSum, $expenseSum]); + $start->addMonth(); + } + + $data = $this->generator->yearInOut($entries); + $cache->store($data); } - $data = $this->generator->yearInOut($entries); - $cache->store($data); return Response::json($data); @@ -81,54 +105,71 @@ class ReportController extends Controller * Summarizes all income and expenses for a given year. Gives a total and an average. * * @param ReportQueryInterface $query - * @param $year - * @param bool $shared + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false) + public function yearInOutSummarized(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty('yearInOutSummarized'); - $cache->addProperty($year); - $cache->addProperty($shared); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($accounts); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } - $start = new Carbon($year . '-01-01'); - $end = new Carbon($year . '-12-31'); - $shared = $shared == 'shared' ? true : false; $income = '0'; $expense = '0'; $count = 0; bcscale(2); - while ($start < $end) { - $month = clone $start; - $month->endOfMonth(); - // total income and total expenses: - $currentIncome = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive'); - $currentExpense = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive'); - - Log::debug('Date ['.$month->format('M Y').']: income = ['.$income.' + '.$currentIncome.'], out = ['.$expense.' + '.$currentExpense.']'); - - $income = bcadd($income, $currentIncome); - $expense = bcadd($expense, $currentExpense); - - - - - - $count++; - $start->addMonth(); + if ($start->diffInMonths($end) > 12) { + // per year + while ($start < $end) { + $startOfYear = clone $start; + $startOfYear->startOfYear(); + $endOfYear = clone $startOfYear; + $endOfYear->endOfYear(); + + // total income and total expenses: + $currentIncome = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); + $currentExpense = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); + $income = bcadd($income, $currentIncome); + $expense = bcadd($expense, $currentExpense); + + $count++; + $start->addYear(); + } + + $data = $this->generator->multiYearInOutSummarized($income, $expense, $count); + $cache->store($data); + } else { + // per month! + while ($start < $end) { + $month = clone $start; + $month->endOfMonth(); + // total income and total expenses: + $currentIncome = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive'); + $currentExpense = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive'); + $income = bcadd($income, $currentIncome); + $expense = bcadd($expense, $currentExpense); + + $count++; + $start->addMonth(); + } + + $data = $this->generator->yearInOutSummarized($income, $expense, $count); + $cache->store($data); } - $data = $this->generator->yearInOutSummarized($income, $expense, $count); - $cache->store($data); return Response::json($data); diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index b808805255..6c066e5e16 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -147,7 +147,7 @@ class CurrencyController extends Controller public function index(CurrencyRepositoryInterface $repository) { $currencies = $repository->get(); - $defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', 'EUR')); + $defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', env('DEFAULT_CURRENCY','EUR'))); if (!Auth::user()->hasRole('owner')) { diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 9dc2be7629..28b331a165 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -178,7 +178,7 @@ class JsonController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function boxIn(ReportQueryInterface $reportQuery) + public function boxIn(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository) { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); @@ -191,8 +191,8 @@ class JsonController extends Controller if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } - - $amount = $reportQuery->incomeInPeriodCorrected($start, $end, true)->sum('amount'); + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); + $amount = $reportQuery->incomeInPeriod($start, $end, $accounts)->sum('amount'); $data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]; $cache->store($data); @@ -205,11 +205,12 @@ class JsonController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function boxOut(ReportQueryInterface $reportQuery) + public function boxOut(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository) { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); + $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); // works for json too! $cache = new CacheProperties; @@ -220,7 +221,7 @@ class JsonController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - $amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount'); + $amount = $reportQuery->expenseInPeriod($start, $end, $accounts)->sum('amount'); $amount = $amount * -1; $data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]; diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 4d1ca65470..9387b159be 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -4,6 +4,7 @@ use Carbon\Carbon; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use Illuminate\Support\Collection; use Session; use View; @@ -45,107 +46,176 @@ class ReportController extends Controller $months = $this->helper->listOfMonths($start); // does the user have shared accounts? - $accounts = $repository->getAccounts(['Default account', 'Asset account']); - $hasShared = false; - + $accounts = $repository->getAccounts(['Default account', 'Asset account']); + // get id's for quick links: + $accountIds = []; /** @var Account $account */ - foreach ($accounts as $account) { - if ($account->getMeta('accountRole') == 'sharedAsset') { - $hasShared = true; - } + foreach($accounts as $account) { + $accountIds [] = $account->id; } + $accountList = join(',',$accountIds); - return view('reports.index', compact('months', 'hasShared')); + return view('reports.index', compact('months', 'accounts', 'start','accountList')); } /** - * @param string $year - * @param string $month + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * - * @param bool $shared - * - * @return \Illuminate\View\View + * @return View */ - public function month($year = '2014', $month = '1', $shared = false) + public function defaultYear($report_type, Carbon $start, Carbon $end, Collection $accounts) { - $start = new Carbon($year . '-' . $month . '-01'); - $subTitle = trans('firefly.reportForMonth', ['month' => $start->formatLocalized($this->monthFormat)]); - $subTitleIcon = 'fa-calendar'; - $end = clone $start; - $incomeTopLength = 8; - $expenseTopLength = 8; - if ($shared == 'shared') { - $shared = true; - $subTitle = trans('firefly.reportForMonthShared', ['month' => $start->formatLocalized($this->monthFormat)]); - } - - $end->endOfMonth(); - - $accounts = $this->helper->getAccountReport($start, $end, $shared); - $incomes = $this->helper->getIncomeReport($start, $end, $shared); - $expenses = $this->helper->getExpenseReport($start, $end, $shared); - $budgets = $this->helper->getBudgetReport($start, $end, $shared); - $categories = $this->helper->getCategoryReport($start, $end, $shared); - $balance = $this->helper->getBalanceReport($start, $end, $shared); - $bills = $this->helper->getBillReport($start, $end); - - Session::flash('gaEventCategory', 'report'); - Session::flash('gaEventAction', 'month'); - Session::flash('gaEventLabel', $start->format('F Y')); - - - return view( - 'reports.month', - compact( - 'start', 'shared', - 'subTitle', 'subTitleIcon', - 'accounts', - 'incomes', 'incomeTopLength', - 'expenses', 'expenseTopLength', - 'budgets', 'balance', - 'categories', - 'bills' - ) - ); - - } - - /** - * @param $year - * - * @param bool $shared - * - * @return $this - */ - public function year($year, $shared = false) - { - $start = new Carbon('01-01-' . $year); - $end = clone $start; - $subTitle = trans('firefly.reportForYear', ['year' => $year]); - $subTitleIcon = 'fa-bar-chart'; $incomeTopLength = 8; $expenseTopLength = 8; - if ($shared == 'shared') { - $shared = true; - $subTitle = trans('firefly.reportForYearShared', ['year' => $year]); - } - $end->endOfYear(); - - $accounts = $this->helper->getAccountReport($start, $end, $shared); - $incomes = $this->helper->getIncomeReport($start, $end, $shared); - $expenses = $this->helper->getExpenseReport($start, $end, $shared); + $accountReport = $this->helper->getAccountReport($start, $end, $accounts); + $incomes = $this->helper->getIncomeReport($start, $end, $accounts); + $expenses = $this->helper->getExpenseReport($start, $end, $accounts); Session::flash('gaEventCategory', 'report'); Session::flash('gaEventAction', 'year'); Session::flash('gaEventLabel', $start->format('Y')); + // and some id's, joined: + $accountIds = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + $accountIds = join(',', $accountIds); + return view( - 'reports.year', - compact('start', 'shared', 'accounts', 'incomes', 'expenses', 'subTitle', 'subTitleIcon', 'incomeTopLength', 'expenseTopLength') + 'reports.default.year', + compact( + 'start', 'accountReport', 'incomes', 'report_type', 'accountIds', 'end', + 'expenses', 'incomeTopLength', 'expenseTopLength' + ) ); } + /** + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return View + */ + public function defaultMonth($report_type, Carbon $start, Carbon $end, Collection $accounts) + { + $incomeTopLength = 8; + $expenseTopLength = 8; + + // get report stuff! + $accountReport = $this->helper->getAccountReport($start, $end, $accounts); + $incomes = $this->helper->getIncomeReport($start, $end, $accounts); + $expenses = $this->helper->getExpenseReport($start, $end, $accounts); + $budgets = $this->helper->getBudgetReport($start, $end, $accounts); + $categories = $this->helper->getCategoryReport($start, $end, $accounts); + $balance = $this->helper->getBalanceReport($start, $end, $accounts); + $bills = $this->helper->getBillReport($start, $end, $accounts); + + // and some id's, joined: + $accountIds = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + $accountIds = join(',', $accountIds); + + // continue! + return view( + 'reports.default.month', + compact( + 'start', 'end', 'report_type', + 'accountReport', + 'incomes', 'incomeTopLength', + 'expenses', 'expenseTopLength', + 'budgets', 'balance', + 'categories', + 'bills', + 'accountIds', 'report_type' + ) + ); + } + + public function defaultMultiYear($report_type, $start, $end, $accounts) + { + + + // list of users stuff: + $budgets = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface')->getActiveBudgets(); + $categories = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface')->getCategories(); + + // and some id's, joined: + $accountIds = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + $accountIds = join(',', $accountIds); + + return view( + 'reports.default.multi-year', compact('budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'report_type') + ); + } + + /** + * @param $report_type + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return View + */ + public function report($report_type, Carbon $start, Carbon $end, Collection $accounts) + { + // throw an error if necessary. + if ($end < $start) { + return view('error')->with('message', 'End date cannot be before start date, silly!'); + } + + // lower threshold + if ($start < Session::get('first')) { + $start = Session::get('first'); + } + + switch ($report_type) { + default: + case 'default': + + View::share( + 'subTitle', trans( + 'firefly.report_default', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat) + ] + ) + ); + View::share('subTitleIcon', 'fa-calendar'); + + // more than one year date difference means year report. + if ($start->diffInMonths($end) > 12) { + // return view('error')->with('message', 'No report yet for this time period.'); + return $this->defaultMultiYear($report_type, $start, $end, $accounts); + } + // more than two months date difference means year report. + if ($start->diffInMonths($end) > 1) { + return $this->defaultYear($report_type, $start, $end, $accounts); + } + + return $this->defaultMonth($report_type, $start, $end, $accounts); + break; + } + + + } + + } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index ef8c33d0dc..8b9e7d92bd 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -12,6 +12,7 @@ use FireflyIII\Http\Requests\JournalFormRequest; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Input; @@ -45,8 +46,9 @@ class TransactionController extends Controller * * @return \Illuminate\View\View */ - public function create(AccountRepositoryInterface $repository, $what = 'deposit') + public function create(AccountRepositoryInterface $repository, $what = TransactionType::DEPOSIT) { + $what = strtolower($what); $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize')); $maxPostSize = Steam::phpBytes(ini_get('post_max_size')); $uploadSize = min($maxFileSize, $maxPostSize); @@ -95,7 +97,7 @@ class TransactionController extends Controller */ public function delete(TransactionJournal $journal) { - $what = strtolower($journal->transactionType->type); + $what = strtolower($journal->getTransactionType()); $subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]); // put previous url in session @@ -137,7 +139,7 @@ class TransactionController extends Controller public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal) { // cannot edit opening balance - if ($journal->transactionType->type == 'Opening balance') { + if ($journal->isOpeningBalance()) { return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!'); } @@ -145,7 +147,7 @@ class TransactionController extends Controller $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize')); $maxPostSize = Steam::phpBytes(ini_get('post_max_size')); $uploadSize = min($maxFileSize, $maxPostSize); - $what = strtolower($journal->transactionType->type); + $what = strtolower($journal->getTransactionType()); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets[0] = trans('form.noBudget'); @@ -181,7 +183,7 @@ class TransactionController extends Controller $preFilled['amount'] = $journal->amount_positive; - if ($journal->transactionType->type == 'Withdrawal') { + if ($journal->isWithdrawal()) { $preFilled['account_id'] = $journal->source_account->id; $preFilled['expense_account'] = $journal->destination_account->name_for_editform; } else { @@ -269,8 +271,8 @@ class TransactionController extends Controller $t->after = bcadd($t->before, $t->amount); } ); - $what = strtolower($journal->transactionType->type); - $subTitle = trans('firefly.' . $journal->transactionType->type) . ' "' . e($journal->description) . '"'; + $what = strtolower($journal->getTransactionType()); + $subTitle = trans('firefly.' . $journal->getTransactionType()) . ' "' . e($journal->description) . '"'; return view('transactions.show', compact('journal', 'subTitle', 'what')); } @@ -287,7 +289,7 @@ class TransactionController extends Controller $journalData = $request->getJournalData(); // if not withdrawal, unset budgetid. - if ($journalData['what'] != 'withdrawal') { + if ($journalData['what'] != strtolower(TransactionType::WITHDRAWAL)) { $journalData['budget_id'] = 0; } @@ -308,7 +310,7 @@ class TransactionController extends Controller // rescan journal, UpdateJournalConnection event(new JournalSaved($journal)); - if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) { + if ($journal->isTransfer() && intval($request->get('piggy_bank_id')) > 0) { event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); } @@ -340,7 +342,7 @@ class TransactionController extends Controller { // cannot edit opening balance - if ($journal->transactionType->type == 'Opening balance') { + if ($journal->isOpeningBalance()) { return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!'); } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 8b196c51c8..a785f908c9 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -64,6 +64,7 @@ class Authenticate Carbon::setLocale($pref->data); setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data)); + setlocale(LC_MONETARY, Config::get('firefly.locales.' . $pref->data)); return $next($request); } diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index ba7dc0dc19..7744c26fdc 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -5,6 +5,7 @@ namespace FireflyIII\Http\Requests; use Auth; use Carbon\Carbon; use Exception; +use FireflyIII\Models\TransactionType; use Input; /** @@ -65,7 +66,7 @@ class JournalFormRequest extends Request ]; switch ($what) { - case 'withdrawal': + case strtolower(TransactionType::WITHDRAWAL): $rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; $rules['expense_account'] = 'between:1,255'; $rules['category'] = 'between:1,255'; @@ -73,12 +74,12 @@ class JournalFormRequest extends Request $rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets'; } break; - case 'deposit': + case strtolower(TransactionType::DEPOSIT): $rules['category'] = 'between:1,255'; $rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; $rules['revenue_account'] = 'between:1,255'; break; - case 'transfer': + case strtolower(TransactionType::TRANSFER): $rules['account_from_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_to_id'; $rules['account_to_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_from_id'; $rules['category'] = 'between:1,255'; diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index 5d360aaf4c..3494c7a1f7 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -351,30 +351,14 @@ Breadcrumbs::register( ); Breadcrumbs::register( - 'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) { + 'reports.report', function (Generator $breadcrumbs, Carbon $start, Carbon $end, $reportType, $accountIds) { $breadcrumbs->parent('reports.index'); - if ($shared) { - $title = trans('breadcrumbs.yearly_report_shared', ['date' => $date->year]); - } else { - $title = trans('breadcrumbs.yearly_report', ['date' => $date->year]); - } - $breadcrumbs->push($title, route('reports.year', [$date->year])); -} -); -Breadcrumbs::register( - 'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) { - $breadcrumbs->parent('reports.year', $date, $shared); - $language = Preferences::get('language', 'en')->data; - $format = Config::get('firefly.month.' . $language); + $pref = Preferences::get('language', 'en')->data; + $monthFormat = Config::get('firefly.monthAndDay.' . $pref); + $title = trans('firefly.report_default', ['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]); - if ($shared) { - $title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->formatLocalized($format)]); - } else { - $title = trans('breadcrumbs.monthly_report', ['date' => $date->formatLocalized($format)]); - } - - $breadcrumbs->push($title, route('reports.month', [$date->year, $date->month])); + $breadcrumbs->push($title, route('reports.report', ['url' => 'abcde'])); } ); @@ -416,7 +400,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) { - $breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type)); + $breadcrumbs->parent('transactions.index', strtolower($journal->getTransactionType())); $breadcrumbs->push($journal->description, route('transactions.show', [$journal->id])); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 8032412afd..c5b96ed926 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,4 +1,5 @@ where('account_types.editable', 1) + ->whereIn('accounts.id', $ids) + ->where('user_id', Auth::user()->id) + ->get(['accounts.*']); + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +); +// budget list +Route::bind( + 'budgetList', + function ($value) { + if (Auth::check()) { + $ids = explode(',', $value); + /** @var \Illuminate\Support\Collection $object */ + $object = Budget::where('active', 1) + ->whereIn('id', $ids) + ->where('user_id', Auth::user()->id) + ->get(); + + // add empty budget if applicable. + if (in_array('0', $ids)) { + $object->push(new Budget); + } + + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +); + +// category list +Route::bind( + 'categoryList', + function ($value) { + if (Auth::check()) { + $ids = explode(',', $value); + /** @var \Illuminate\Support\Collection $object */ + $object = Category::whereIn('id', $ids) + ->where('user_id', Auth::user()->id) + ->get(); + + // add empty budget if applicable. + if (in_array('0', $ids)) { + $object->push(new Category); + } + + if ($object->count() > 0) { + return $object; + } + } + throw new NotFoundHttpException; + } +); + +// Date +Route::bind( + 'start_date', + function ($value) { + if (Auth::check()) { + + try { + $date = new Carbon($value); + } catch (Exception $e) { + Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id); + throw new NotFoundHttpException; + } + + return $date; + } + throw new NotFoundHttpException; + } +); + +// Date +Route::bind( + 'end_date', + function ($value) { + if (Auth::check()) { + + try { + $date = new Carbon($value); + } catch (Exception $e) { + Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id); + throw new NotFoundHttpException; + } + + return $date; + } + throw new NotFoundHttpException; + } +); Route::bind( 'tj', function ($value) { @@ -287,6 +393,7 @@ Route::group( Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where( ['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared'] ); + Route::get('/chart/account/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\AccountController@report']); Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']); @@ -296,18 +403,24 @@ Route::group( // budgets: Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); - Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); + + // this chart is used in reports: + Route::get('/chart/budget/year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@year']); + Route::get('/chart/budget/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']); + Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']); Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']); // categories: Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']); - Route::get('/chart/category/spent-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@spentInYear'])->where( -['year' => '[0-9]{4}', 'shared' => 'shared'] - ); - Route::get('/chart/category/earned-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@earnedInYear'])->where( - ['year' => '[0-9]{4}', 'shared' => 'shared'] + + // these three charts are for reports: + Route::get('/chart/category/spent-in-year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@spentInYear']); + Route::get('/chart/category/earned-in-year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@earnedInYear']); + Route::get( + '/chart/category/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear'] ); + Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']); Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']); Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); @@ -316,8 +429,10 @@ Route::group( Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']); // reports: - Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); - Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where( + Route::get('/chart/report/in-out/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut'])->where( + ['year' => '[0-9]{4}', 'shared' => 'shared'] + ); + Route::get('/chart/report/in-out-sum/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where( ['year' => '[0-9]{4}', 'shared' => 'shared'] ); @@ -385,10 +500,10 @@ Route::group( * Report Controller */ Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); - Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); - Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where( - ['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared'] - ); + // Route::post('/reports/select', ['uses' => 'ReportController@select', 'as' => 'reports.select']); + Route::get('/reports/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']); + // Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); + // Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where(['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']); // pop ups for budget report: diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 52e026805c..aee80cdfc4 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -147,10 +147,9 @@ class TransactionJournal extends Model } bcscale(2); - $type = $this->transactionType->type; $transaction = $this->transactions->sortByDesc('amount')->first(); $amount = $transaction->amount; - if ($type == 'Withdrawal') { + if ($this->isWithdrawal()) { $amount = $amount * -1; } $cache->store($amount); @@ -167,15 +166,15 @@ class TransactionJournal extends Model */ protected function amountByTagAdvancePayment(Tag $tag, $amount) { - if ($this->transactionType->type == 'Withdrawal') { - $others = $tag->transactionJournals()->transactionTypes(['Deposit'])->get(); + if ($this->isWithdrawal()) { + $others = $tag->transactionJournals()->transactionTypes([TransactionType::DEPOSIT])->get(); foreach ($others as $other) { $amount = bcsub($amount, $other->amount_positive); } return $amount; } - if ($this->transactionType->type == 'Deposit') { + if ($this->isDeposit()) { return '0'; } @@ -190,8 +189,8 @@ class TransactionJournal extends Model */ protected function amountByTagBalancingAct($tag, $amount) { - if ($this->transactionType->type == 'Withdrawal') { - $transfer = $tag->transactionJournals()->transactionTypes(['Transfer'])->first(); + if ($this->isWithdrawal()) { + $transfer = $tag->transactionJournals()->transactionTypes([TransactionType::TRANSFER])->first(); if ($transfer) { $amount = bcsub($amount, $transfer->amount_positive); @@ -491,4 +490,43 @@ class TransactionJournal extends Model return $this->belongsTo('FireflyIII\User'); } + /** + * @return string + */ + public function getTransactionType() + { + return $this->transactionType->type; + } + + /** + * @return bool + */ + public function isWithdrawal() + { + return $this->transactionType->isWithdrawal(); + } + + /** + * @return bool + */ + public function isDeposit() + { + return $this->transactionType->isDeposit(); + } + + /** + * @return bool + */ + public function isTransfer() + { + return $this->transactionType->isTransfer(); + } + + /** + * @return bool + */ + public function isOpeningBalance() + { + return $this->transactionType->isOpeningBalance(); + } } diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 3b00323459..1a07dd91d8 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -23,6 +23,11 @@ class TransactionType extends Model { use SoftDeletes; + const WITHDRAWAL = 'Withdrawal'; + const DEPOSIT = 'Deposit'; + const TRANSFER = 'Transfer'; + const OPENING_BALANCE = 'Opening balance'; + /** * @return array */ @@ -38,4 +43,36 @@ class TransactionType extends Model { return $this->hasMany('FireflyIII\Models\TransactionJournal'); } + + /** + * @return bool + */ + public function isWithdrawal() + { + return $this->type === TransactionType::WITHDRAWAL; + } + + /** + * @return bool + */ + public function isDeposit() + { + return $this->type === TransactionType::DEPOSIT; + } + + /** + * @return bool + */ + public function isTransfer() + { + return $this->type === TransactionType::TRANSFER; + } + + /** + * @return bool + */ + public function isOpeningBalance() + { + return $this->type === TransactionType::OPENING_BALANCE; + } } diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index d911cdea8e..516be1fa6e 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -234,7 +234,7 @@ class AccountRepository implements AccountRepositoryInterface $ids = array_unique($ids); if (count($ids) > 0) { - $accounts = Auth::user()->accounts()->whereIn('id', $ids)->get(); + $accounts = Auth::user()->accounts()->whereIn('id', $ids)->where('accounts.active', 1)->get(); } bcscale(2); @@ -273,6 +273,7 @@ class AccountRepository implements AccountRepositoryInterface $accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC') ->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id') ->where('account_meta.name', 'accountRole') + ->where('accounts.active', 1) ->where('account_meta.data', '"savingAsset"') ->get(['accounts.*']); $start = clone Session::get('start', new Carbon); @@ -329,7 +330,7 @@ class AccountRepository implements AccountRepositoryInterface ->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', 'Transfer'); + ->where('transaction_types.type', TransactionType::TRANSFER); } )->get(); @@ -375,7 +376,7 @@ class AccountRepository implements AccountRepositoryInterface return TransactionJournal ::orderBy('transaction_journals.date', 'ASC') ->accountIs($account) - ->transactionTypes(['Opening balance']) + ->transactionTypes([TransactionType::OPENING_BALANCE]) ->orderBy('created_at', 'ASC') ->first(['transaction_journals.*']); } @@ -548,7 +549,7 @@ class AccountRepository implements AccountRepositoryInterface */ protected function storeInitialBalance(Account $account, Account $opposing, array $data) { - $transactionType = TransactionType::whereType('Opening balance')->first(); + $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); $journal = TransactionJournal::create( [ 'user_id' => $data['user'], @@ -642,4 +643,14 @@ class AccountRepository implements AccountRepositoryInterface return $journal; } + + /** + * @param $accountId + * + * @return Account + */ + public function find($accountId) + { + return Auth::user()->accounts()->findOrNew($accountId); + } } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 86dcb2e616..a3bdd05ec7 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -24,6 +24,13 @@ interface AccountRepositoryInterface */ public function countAccounts(array $types); + /** + * @param $accountId + * + * @return Account + */ + public function find($accountId); + /** * @param Account $account * @param Account $moveTo diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index f852266d03..686267e980 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -5,6 +5,7 @@ namespace FireflyIII\Repositories\Bill; use Auth; use Carbon\Carbon; use DB; +use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -108,6 +109,46 @@ class BillRepository implements BillRepositoryInterface return $set; } + /** + * @param Collection $accounts + * + * @return Collection + */ + public function getBillsForAccounts(Collection $accounts) + { + /** @var Collection $set */ + $set = Auth::user()->bills()->orderBy('name', 'ASC')->get(); + + $ids = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $ids[] = $account->id; + } + + $set = $set->filter( + function (Bill $bill) use ($ids) { + // get transaction journals from or to any of the mentioned accounts. + // if zero, return null. + $journals = $bill->transactionjournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereIn('transactions.account_id', $ids)->count(); + + return ($journals > 0); + + } + ); + + $set = $set->sortBy( + function (Bill $bill) { + + $int = $bill->active == 1 ? 0 : 1; + + return $int . strtolower($bill->name); + } + ); + + return $set; + } + /** * @param Bill $bill * @@ -154,7 +195,7 @@ class BillRepository implements BillRepositoryInterface } $journals = new Collection; if (count($ids) > 0) { - $journals = Auth::user()->transactionjournals()->transactionTypes(['Withdrawal'])->whereIn('transaction_journals.id', $ids)->get( + $journals = Auth::user()->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->whereIn('transaction_journals.id', $ids)->get( ['transaction_journals.*'] ); } @@ -276,12 +317,21 @@ class BillRepository implements BillRepositoryInterface */ public function scan(Bill $bill, TransactionJournal $journal) { + + /* + * Can only support withdrawals. + */ + if (false === $journal->isWithdrawal()) { + return false; + } + $matches = explode(',', $bill->match); $description = strtolower($journal->description) . ' ' . strtolower($journal->destination_account->name); $wordMatch = $this->doWordMatch($matches, $description); $amountMatch = $this->doAmountMatch($journal->amount_positive, $bill->amount_min, $bill->amount_max); Log::debug('Journal #' . $journal->id . ' has description "' . $description . '"'); + /* * If both, update! */ diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index fe36b810a5..a886536cd4 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -77,6 +77,15 @@ interface BillRepositoryInterface */ 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); + /** * @param Bill $bill * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 7b9f594a0e..c9c24eef89 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -7,12 +7,14 @@ use Carbon\Carbon; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Shared\ComponentRepository; use FireflyIII\Support\CacheProperties; use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; +use Log; /** * Class BudgetRepository @@ -50,10 +52,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn * * @return float */ - public function expensesOnDayCorrected(Budget $budget, Carbon $date) + public function expensesOnDay(Budget $budget, Carbon $date) { bcscale(2); - $sum = $budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); + $sum = $budget->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); return $sum; } @@ -87,10 +89,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn /** @var Collection $repetitions */ return LimitRepetition:: leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.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('budget_limits.budget_id', $budget->id) - ->get(['limit_repetitions.*']); + ->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('budget_limits.budget_id', $budget->id) + ->get(['limit_repetitions.*']); } /** @@ -139,9 +141,11 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return $cache->get(); // @codeCoverageIgnore } $data = $budget->limitrepetitions() - ->where('limit_repetitions.startdate', $start) - ->where('limit_repetitions.enddate', $end) - ->first(['limit_repetitions.*']); + ->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.*']); + //Log::debug('Looking for limit reps for budget #' . $budget->id . ' start [' . $start . '] and end [' . $end . '].'); + //Log::debug(DB::getQueryLog()) $cache->store($data); return $data; @@ -182,9 +186,9 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn /** * Returns all the transaction journals for a limit, possibly limited by a limit repetition. * - * @param Budget $budget + * @param Budget $budget * @param LimitRepetition $repetition - * @param int $take + * @param int $take * * @return LengthAwarePaginator */ @@ -201,11 +205,11 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn return $cache->get(); // @codeCoverageIgnore } - $offset = intval(Input::get('page')) > 0 ? intval(Input::get('page')) * $take : 0; - $setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset) - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC'); + $offset = intval(Input::get('page')) > 0 ? intval(Input::get('page')) * $take : 0; + $setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); $countQuery = $budget->transactionJournals(); @@ -215,7 +219,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn } - $set = $setQuery->get(['transaction_journals.*']); + $set = $setQuery->get(['transaction_journals.*']); $count = $countQuery->count(); @@ -249,9 +253,9 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn public function getLimitAmountOnDate(Budget $budget, Carbon $date) { $repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') - ->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00')) - ->where('budget_limits.budget_id', $budget->id) - ->first(['limit_repetitions.*']); + ->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00')) + ->where('budget_limits.budget_id', $budget->id) + ->first(['limit_repetitions.*']); if ($repetition) { return $repetition->amount; @@ -269,15 +273,15 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn public function getWithoutBudget(Carbon $start, Carbon $end) { return Auth::user() - ->transactionjournals() - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('budget_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.*']); + ->transactionjournals() + ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->whereNull('budget_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.*']); } /** @@ -289,33 +293,44 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn public function getWithoutBudgetSum(Carbon $start, Carbon $end) { $noBudgetSet = Auth::user() - ->transactionjournals() - ->whereNotIn( - 'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) { - $query - ->select('transaction_journals.id') - ->from('transaction_journals') - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')) - ->whereNotNull('budget_transaction_journal.budget_id'); - } - ) - ->after($start) - ->before($end) - ->transactionTypes(['Withdrawal']) - ->get(['transaction_journals.*'])->sum('amount'); + ->transactionjournals() + ->whereNotIn( + 'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) { + $query + ->select('transaction_journals.id') + ->from('transaction_journals') + ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')) + ->whereNotNull('budget_transaction_journal.budget_id'); + } + ) + ->after($start) + ->before($end) + ->transactionTypes([TransactionType::WITHDRAWAL]) + ->get(['transaction_journals.*'])->sum('amount'); - bcscale(2); - - return bcmul($noBudgetSet, -1); + return $noBudgetSet; } /** * @param Budget $budget * @param Carbon $start * @param Carbon $end - * @param bool $shared + * @param Collection $accounts + * + * @return string + */ + public function balanceInPeriodForList(Budget $budget, Carbon $start, Carbon $end, Collection $accounts) + { + return $this->commonBalanceInPeriodForList($budget, $start, $end, $accounts); + } + + /** + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * @param bool $shared * * @return string */ @@ -334,7 +349,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn $newBudget = new Budget( [ 'user_id' => $data['user'], - 'name' => $data['name'], + 'name' => $data['name'], ] ); $newBudget->save(); @@ -345,14 +360,14 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn /** * @param Budget $budget - * @param array $data + * @param array $data * * @return Budget */ public function update(Budget $budget, array $data) { // update the account: - $budget->name = $data['name']; + $budget->name = $data['name']; $budget->active = $data['active']; $budget->save(); @@ -376,10 +391,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn // if not, create one! $limit = new BudgetLimit; $limit->budget()->associate($budget); - $limit->startdate = $date; - $limit->amount = $amount; + $limit->startdate = $date; + $limit->amount = $amount; $limit->repeat_freq = 'monthly'; - $limit->repeats = 0; + $limit->repeats = 0; $limit->save(); // likewise, there should be a limit repetition to match the end date diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index e614db0f02..9594f2d2bc 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -35,7 +35,7 @@ interface BudgetRepositoryInterface * * @return float */ - public function expensesOnDayCorrected(Budget $budget, Carbon $date); + public function expensesOnDay(Budget $budget, Carbon $date); /** * @return Collection @@ -139,6 +139,19 @@ interface BudgetRepositoryInterface */ public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true); + /** + * + * 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 balanceInPeriodForList(Budget $budget, Carbon $start, Carbon $end, Collection $accounts); + /** * @param array $data * diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 035f1079ed..859ac36106 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -7,6 +7,7 @@ use Carbon\Carbon; use Crypt; use FireflyIII\Models\Category; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Shared\ComponentRepository; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -58,6 +59,71 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito return $set; } + /** + * Returns the amount earned without category by accounts in period. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end) + { + + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + + // is deposit AND account_from is in the list of $accounts + // not from any of the accounts in the list? + + 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) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereIn('transactions.account_id', $accountIds) + ->transactionTypes([TransactionType::DEPOSIT]) + ->get(['transaction_journals.*'])->sum('amount'); + } + + + /** + * Returns the amount spent without category by accounts in period. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end) + { + + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + + // is withdrawal or transfer AND account_from is in the list of $accounts + + + 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) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereIn('transactions.account_id', $accountIds) + ->transactionTypes([TransactionType::WITHDRAWAL]) + ->get(['transaction_journals.*'])->sum('amount'); + } + /** * @@ -66,7 +132,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito * * @return array */ - public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end) + public function getCategoriesAndExpenses(Carbon $start, Carbon $end) { $set = Auth::user()->transactionjournals() ->leftJoin( @@ -76,7 +142,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito ->before($end) ->where('categories.user_id', Auth::user()->id) ->after($start) - ->transactionTypes(['Withdrawal']) + ->transactionTypes([TransactionType::WITHDRAWAL]) ->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']); bcscale(2); @@ -190,6 +256,19 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito return $this->commonBalanceInPeriod($category, $start, $end, $shared); } + /** + * @param Category $category + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + */ + public function balanceInPeriodForList(Category $category, Carbon $start, Carbon $end, Collection $accounts) + { + return $this->commonBalanceInPeriodForList($category, $start, $end, $accounts); + } + /** * Corrected for tags * @@ -198,9 +277,9 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito * * @return string */ - public function spentOnDaySumCorrected(Category $category, Carbon $date) + public function spentOnDaySum(Category $category, Carbon $date) { - return $category->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); + return $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); } /** @@ -285,9 +364,10 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito return $cache->get(); // @codeCoverageIgnore } - $sum = $category->transactionjournals()->transactionTypes(['Withdrawal'])->before($end)->after($start)->get(['transaction_journals.*'])->sum( - 'amount' - ); + $sum = $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->before($end)->after($start)->get(['transaction_journals.*']) + ->sum( + 'amount' + ); $cache->store($sum); @@ -315,9 +395,10 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito return $cache->get(); // @codeCoverageIgnore } - $sum = $category->transactionjournals()->transactionTypes(['Deposit'])->before($end)->after($start)->get(['transaction_journals.*'])->sum( - 'amount' - ); + $sum = $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->before($end)->after($start)->get(['transaction_journals.*']) + ->sum( + 'amount' + ); $cache->store($sum); @@ -365,8 +446,69 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito * * @return float */ - public function earnedOnDaySumCorrected(Category $category, Carbon $date) + public function earnedOnDaySum(Category $category, Carbon $date) { - return $category->transactionjournals()->transactionTypes(['Deposit'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); + return $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->onDate($date)->get(['transaction_journals.*'])->sum('amount'); + } + + /** + * Calculates how much is spent in this period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end) + { + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + + $sum + = $category + ->transactionjournals() + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->after($start) + ->before($end) + ->whereIn('transactions.account_id', $accountIds) + ->transactionTypes([TransactionType::WITHDRAWAL]) + ->get(['transaction_journals.*']) + ->sum('amount'); + return $sum; + + } + + /** + * Calculate how much is earned in this period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end) + { + $accountIds = []; + foreach ($accounts as $account) { + $accountIds[] = $account->id; + } + $sum + = $category + ->transactionjournals() + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->before($end) + ->whereIn('transactions.account_id', $accountIds) + ->transactionTypes([TransactionType::DEPOSIT]) + ->after($start) + ->get(['transaction_journals.*']) + ->sum('amount'); + return $sum; + } } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index e424927fc8..d54cdb5f55 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -39,6 +39,53 @@ interface CategoryRepositoryInterface */ public function getCategories(); + + /** + * Calculates how much is spent in this period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end); + + /** + * Calculate how much is earned in this period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end); + + /** + * Returns the amount spent without category by accounts in period. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end); + + /** + * Returns the amount earned without category by accounts in period. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end); + /** * Corrected for tags. * @@ -47,7 +94,7 @@ interface CategoryRepositoryInterface * * @return array */ - public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end); + public function getCategoriesAndExpenses(Carbon $start, Carbon $end); /** * @param Category $category @@ -77,8 +124,8 @@ interface CategoryRepositoryInterface * limited by a start or end date. * * @param Category $category - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return string */ @@ -112,6 +159,19 @@ interface CategoryRepositoryInterface */ public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false); + + /** + * Corrected for tags. + * + * @param Category $category + * @param \Carbon\Carbon $start + * @param \Carbon\Carbon $end + * @param Collection $accounts + * + * @return string + */ + public function balanceInPeriodForList(Category $category, Carbon $start, Carbon $end, Collection $accounts); + /** * @param Category $category * @param \Carbon\Carbon $start @@ -143,7 +203,7 @@ interface CategoryRepositoryInterface * * @return float */ - public function spentOnDaySumCorrected(Category $category, Carbon $date); + public function spentOnDaySum(Category $category, Carbon $date); /** * @@ -154,7 +214,7 @@ interface CategoryRepositoryInterface * * @return float */ - public function earnedOnDaySumCorrected(Category $category, Carbon $date); + public function earnedOnDaySum(Category $category, Carbon $date); /** * @param array $data diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index eebbbf205a..3fd766b1a5 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -327,15 +327,15 @@ class JournalRepository implements JournalRepositoryInterface $fromAccount = null; $toAccount = null; switch ($type->type) { - case 'Withdrawal': + case TransactionType::WITHDRAWAL: list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data); break; - case 'Deposit': + case TransactionType::DEPOSIT: list($fromAccount, $toAccount) = $this->storeDepositAccounts($data); break; - case 'Transfer': + case TransactionType::TRANSFER: $fromAccount = Account::find($data['account_from_id']); $toAccount = Account::find($data['account_to_id']); break; diff --git a/app/Repositories/Shared/ComponentRepository.php b/app/Repositories/Shared/ComponentRepository.php index 7edbb7bfb3..7f91fb8c13 100644 --- a/app/Repositories/Shared/ComponentRepository.php +++ b/app/Repositories/Shared/ComponentRepository.php @@ -3,8 +3,11 @@ namespace FireflyIII\Repositories\Shared; use Carbon\Carbon; +use FireflyIII\Models\Account; +use FireflyIII\Models\TransactionType; use FireflyIII\Support\CacheProperties; use Illuminate\Database\Query\JoinClause; +use Illuminate\Support\Collection; /** * Class ComponentRepository @@ -39,7 +42,10 @@ class ComponentRepository } if ($shared === true) { // shared is true: always ignore transfers between accounts! - $sum = $object->transactionjournals()->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance'])->before($end)->after($start) + $sum = $object->transactionjournals() + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE]) + ->before($end) + ->after($start) ->get(['transaction_journals.*'])->sum('amount'); } else { // do something else, SEE budgets. @@ -47,7 +53,7 @@ class ComponentRepository $sum = $object->transactionjournals()->before($end)->after($start) ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') - ->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance']) + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE]) ->leftJoin( 'account_meta', function (JoinClause $join) { $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); @@ -59,4 +65,46 @@ class ComponentRepository return $sum; } + + /** + * @param $object + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + */ + protected function commonBalanceInPeriodForList($object, Carbon $start, Carbon $end, Collection $accounts) + { + $cache = new CacheProperties; // we must cache this. + $cache->addProperty($object->id); + $cache->addProperty(get_class($object)); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($accounts); + $cache->addProperty('balanceInPeriodList'); + + $ids = []; + /** @var Account $account */ + foreach ($accounts as $account) { + $ids[] = $account->id; + } + + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + + $sum = $object->transactionjournals() + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE]) + ->before($end) + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->whereIn('accounts.id', $ids) + ->after($start) + ->get(['transaction_journals.*'])->sum('amount'); + + $cache->store($sum); + + return $sum; + } } diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 1d8f848f51..8f395b4218 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -77,7 +77,7 @@ class TagRepository implements TagRepositoryInterface /** @var Tag $tag */ foreach ($tags as $tag) { - $journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes(['Transfer'])->get(['transaction_journals.*']); + $journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes([TransactionType::TRANSFER])->get(['transaction_journals.*']); /** @var TransactionJournal $journal */ foreach ($journals as $journal) { @@ -158,7 +158,7 @@ class TagRepository implements TagRepositoryInterface if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') { foreach ($tag->transactionjournals as $journal) { - if ($journal->transactionType->type == 'Transfer') { + if ($journal->isTransfer()) { return false; } } @@ -169,7 +169,7 @@ class TagRepository implements TagRepositoryInterface */ $count = 0; foreach ($tag->transactionjournals as $journal) { - if ($journal->transactionType->type == 'Withdrawal') { + if ($journal->isWithdrawal()) { $count++; } } @@ -201,7 +201,7 @@ class TagRepository implements TagRepositoryInterface * If any transaction is a deposit, cannot become a balancing act. */ foreach ($tag->transactionjournals as $journal) { - if ($journal->transactionType->type == 'Deposit') { + if ($journal->isDeposit()) { return false; } } @@ -239,10 +239,10 @@ class TagRepository implements TagRepositoryInterface protected function connectBalancingAct(TransactionJournal $journal, Tag $tag) { /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType('Withdrawal')->first(); + $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType('Transfer')->first(); + $transfer = TransactionType::whereType(TransactionType::TRANSFER)->first(); $transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count(); @@ -275,11 +275,11 @@ class TagRepository implements TagRepositoryInterface protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag) { /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType('Transfer')->first(); + $transfer = TransactionType::whereType(TransactionType::TRANSFER)->first(); /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType('Withdrawal')->first(); + $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); /** @var TransactionType $deposit */ - $deposit = TransactionType::whereType('Deposit')->first(); + $deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first(); $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); $deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count(); @@ -326,10 +326,10 @@ class TagRepository implements TagRepositoryInterface foreach ($tag->transactionjournals as $check) { // $checkAccount is the source_account for a withdrawal // $checkAccount is the destination_account for a deposit - if ($check->transactionType->type == 'Withdrawal' && $check->source_account->id != $journal->destination_account->id) { + if ($check->isWithdrawal() && $check->source_account->id != $journal->destination_account->id) { $match = false; } - if ($check->transactionType->type == 'Deposit' && $check->destination_account->id != $journal->destination_account->id) { + if ($check->isDeposit() && $check->destination_account->id != $journal->destination_account->id) { $match = false; } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 0ef9c0ad2c..86cac1185b 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -40,7 +40,7 @@ class Amount if ($cache->has()) { return $cache->get(); } else { - $currencyPreference = Prefs::get('currencyPreference', 'EUR'); + $currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY','EUR')); $currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $cache->store($currency->symbol); @@ -60,7 +60,7 @@ class Amount { $amount = floatval($amount); $amount = round($amount, 2); - $string = number_format($amount, 2, ',', '.'); + $string = money_format('%!.2n', $amount); if ($coloured === true) { if ($amount === 0.0) { @@ -100,13 +100,13 @@ class Amount $symbol = $journal->symbol; } - if ($journal->transactionType->type == 'Transfer' && $coloured) { + if ($journal->isTransfer() && $coloured) { $txt = '' . $this->formatWithSymbol($symbol, $journal->amount_positive, false) . ''; $cache->store($txt); return $txt; } - if ($journal->transactionType->type == 'Transfer' && !$coloured) { + if ($journal->isTransfer() && !$coloured) { $txt = $this->formatWithSymbol($symbol, $journal->amount_positive, false); $cache->store($txt); @@ -152,7 +152,7 @@ class Amount if ($cache->has()) { return $cache->get(); } else { - $currencyPreference = Prefs::get('currencyPreference', 'EUR'); + $currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY','EUR')); $currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); if ($currency) { @@ -161,9 +161,9 @@ class Amount return $currency->code; } - $cache->store('EUR'); + $cache->store(env('DEFAULT_CURRENCY','EUR')); - return 'EUR'; // @codeCoverageIgnore + return env('DEFAULT_CURRENCY','EUR'); // @codeCoverageIgnore } } diff --git a/app/Support/Twig/Budget.php b/app/Support/Twig/Budget.php index cac102a0a2..a67313513c 100644 --- a/app/Support/Twig/Budget.php +++ b/app/Support/Twig/Budget.php @@ -23,10 +23,10 @@ class Budget extends Twig_Extension { $functions = []; $functions[] = new Twig_SimpleFunction( - 'spentInRepetitionCorrected', function (LimitRepetition $repetition) { + 'spentInRepetition', function (LimitRepetition $repetition) { $cache = new CacheProperties; $cache->addProperty($repetition->id); - $cache->addProperty('spentInRepetitionCorrected'); + $cache->addProperty('spentInRepetition'); if ($cache->has()) { return $cache->get(); // @codeCoverageIgnore } diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index 62c07048de..628745d1f8 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -68,19 +68,17 @@ class Journal extends Twig_Extension return $cache->get(); // @codeCoverageIgnore } - $type = $journal->transactionType->type; - - switch ($type) { - case 'Withdrawal': + switch (true) { + case $journal->isWithdrawal(): $txt = ''; break; - case 'Deposit': + case $journal->isDeposit(): $txt = ''; break; - case 'Transfer': + case $journal->isTransfer(): $txt = ''; break; - case 'Opening balance': + case $journal->isOpeningBalance(): $txt = ''; break; default: @@ -189,7 +187,7 @@ class Journal extends Twig_Extension } if ($tag->tagMode == 'advancePayment') { - if ($journal->transactionType->type == 'Deposit') { + if ($journal->isDeposit()) { $amount = app('amount')->formatJournal($journal, false); $string = ' ' . $tag->tag . ''; @@ -201,7 +199,7 @@ class Journal extends Twig_Extension * AdvancePayment with a withdrawal will show the amount with a link to * the tag. The TransactionJournal should properly calculate the amount. */ - if ($journal->transactionType->type == 'Withdrawal') { + if ($journal->isWithdrawal()) { $amount = app('amount')->formatJournal($journal); $string = '' . $amount . ''; diff --git a/composer.lock b/composer.lock index 671e496e1d..3c22531a9a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,33 +4,34 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c33eed9ad14c084a1e6d3448a29c9c4b", + "hash": "79e0f94d7571803d13e30aa48111b1ab", + "content-hash": "968497c3098b5a38109e66c170c8becd", "packages": [ { "name": "classpreloader/classpreloader", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/ClassPreloader/ClassPreloader.git", - "reference": "8c3c14b10309e3b40bce833913a6c0c0b8c8f962" + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/8c3c14b10309e3b40bce833913a6c0c0b8c8f962", - "reference": "8c3c14b10309e3b40bce833913a6c0c0b8c8f962", + "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", + "reference": "9b10b913c2bdf90c3d2e0d726b454fb7f77c552a", "shasum": "" }, "require": { - "nikic/php-parser": "~1.3", + "nikic/php-parser": "^1.0|^2.0", "php": ">=5.5.9" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.8|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -58,7 +59,7 @@ "class", "preload" ], - "time": "2015-06-28 21:39:13" + "time": "2015-11-09 22:51:51" }, { "name": "danielstjules/stringy", @@ -200,16 +201,16 @@ }, { "name": "doctrine/annotations", - "version": "v1.2.6", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "f4a91702ca3cd2e568c3736aa031ed00c3752af4" + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/f4a91702ca3cd2e568c3736aa031ed00c3752af4", - "reference": "f4a91702ca3cd2e568c3736aa031ed00c3752af4", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", "shasum": "" }, "require": { @@ -264,20 +265,20 @@ "docblock", "parser" ], - "time": "2015-06-17 12:21:22" + "time": "2015-08-31 12:32:49" }, { "name": "doctrine/cache", - "version": "v1.4.1", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03" + "reference": "47c7128262da274f590ae6f86eb137a7a64e82af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c9eadeb743ac6199f7eec423cb9426bc518b7b03", - "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03", + "url": "https://api.github.com/repos/doctrine/cache/zipball/47c7128262da274f590ae6f86eb137a7a64e82af", + "reference": "47c7128262da274f590ae6f86eb137a7a64e82af", "shasum": "" }, "require": { @@ -298,8 +299,8 @@ } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Cache\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } }, "notification-url": "https://packagist.org/downloads/", @@ -334,7 +335,7 @@ "cache", "caching" ], - "time": "2015-04-15 00:11:59" + "time": "2015-12-03 10:50:37" }, { "name": "doctrine/collections", @@ -404,16 +405,16 @@ }, { "name": "doctrine/common", - "version": "v2.5.0", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3" + "reference": "311001fd9865a4d0d59efff4eac6d7dcb3f5270c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/cd8daf2501e10c63dced7b8b9b905844316ae9d3", - "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3", + "url": "https://api.github.com/repos/doctrine/common/zipball/311001fd9865a4d0d59efff4eac6d7dcb3f5270c", + "reference": "311001fd9865a4d0d59efff4eac6d7dcb3f5270c", "shasum": "" }, "require": { @@ -430,7 +431,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev" + "dev-master": "2.5.x-dev" } }, "autoload": { @@ -473,20 +474,20 @@ "persistence", "spl" ], - "time": "2015-04-02 19:55:44" + "time": "2015-12-04 12:49:42" }, { "name": "doctrine/dbal", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "628c2256b646ae2417d44e063bce8aec5199d48d" + "reference": "01dbcbc5cd0a913d751418e635434a18a2f2a75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/628c2256b646ae2417d44e063bce8aec5199d48d", - "reference": "628c2256b646ae2417d44e063bce8aec5199d48d", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/01dbcbc5cd0a913d751418e635434a18a2f2a75c", + "reference": "01dbcbc5cd0a913d751418e635434a18a2f2a75c", "shasum": "" }, "require": { @@ -544,20 +545,20 @@ "persistence", "queryobject" ], - "time": "2015-01-12 21:52:47" + "time": "2015-09-16 16:29:33" }, { "name": "doctrine/inflector", - "version": "v1.0.1", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604" + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/0bcb2e79d8571787f18b7eb036ed3d004908e604", - "reference": "0bcb2e79d8571787f18b7eb036ed3d004908e604", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", "shasum": "" }, "require": { @@ -569,7 +570,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -611,7 +612,7 @@ "singularize", "string" ], - "time": "2014-12-20 21:24:13" + "time": "2015-11-06 14:35:42" }, { "name": "doctrine/lexer", @@ -669,16 +670,16 @@ }, { "name": "grumpydictator/gchart", - "version": "1.0.9", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/JC5/gchart.git", - "reference": "920a0494a0697472bf81c0111b6825c516099e59" + "reference": "0b1de3e045aaf19bb17658d3e81db706d6945e5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JC5/gchart/zipball/920a0494a0697472bf81c0111b6825c516099e59", - "reference": "920a0494a0697472bf81c0111b6825c516099e59", + "url": "https://api.github.com/repos/JC5/gchart/zipball/0b1de3e045aaf19bb17658d3e81db706d6945e5d", + "reference": "0b1de3e045aaf19bb17658d3e81db706d6945e5d", "shasum": "" }, "require": { @@ -701,7 +702,7 @@ } ], "description": "GChart is a small package that allows you to easily generate data for the Google Charts API.", - "time": "2015-04-05 19:17:17" + "time": "2015-12-15 21:07:33" }, { "name": "illuminate/html", @@ -838,30 +839,30 @@ }, { "name": "jeremeamia/SuperClosure", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/jeremeamia/super_closure.git", - "reference": "b712f39c671e5ead60c7ebfe662545456aade833" + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/b712f39c671e5ead60c7ebfe662545456aade833", - "reference": "b712f39c671e5ead60c7ebfe662545456aade833", + "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/29a88be2a4846d27c1613aed0c9071dfad7b5938", + "reference": "29a88be2a4846d27c1613aed0c9071dfad7b5938", "shasum": "" }, "require": { - "nikic/php-parser": "~1.0", - "php": ">=5.4" + "nikic/php-parser": "^1.2|^2.0", + "php": ">=5.4", + "symfony/polyfill-php56": "^1.0" }, "require-dev": { - "codeclimate/php-test-reporter": "~0.1.2", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^4.0|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -878,7 +879,7 @@ "name": "Jeremy Lindblom", "email": "jeremeamia@gmail.com", "homepage": "https://github.com/jeremeamia", - "role": "developer" + "role": "Developer" } ], "description": "Serialize Closure objects, including their context and binding", @@ -892,24 +893,24 @@ "serialize", "tokenizer" ], - "time": "2015-03-11 20:06:43" + "time": "2015-12-05 17:17:57" }, { "name": "laravel/framework", - "version": "v5.1.10", + "version": "v5.1.27", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "d47ccc8de10ccb6f328cc90f901ca5e47e077c93" + "reference": "b16f80878fd3603022d3c84593397cedd9af0bcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d47ccc8de10ccb6f328cc90f901ca5e47e077c93", - "reference": "d47ccc8de10ccb6f328cc90f901ca5e47e077c93", + "url": "https://api.github.com/repos/laravel/framework/zipball/b16f80878fd3603022d3c84593397cedd9af0bcf", + "reference": "b16f80878fd3603022d3c84593397cedd9af0bcf", "shasum": "" }, "require": { - "classpreloader/classpreloader": "~2.0", + "classpreloader/classpreloader": "~2.0|~3.0", "danielstjules/stringy": "~1.8", "doctrine/inflector": "~1.0", "ext-mbstring": "*", @@ -919,8 +920,9 @@ "monolog/monolog": "~1.11", "mtdowling/cron-expression": "~1.0", "nesbot/carbon": "~1.19", + "paragonie/random_compat": "~1.1", "php": ">=5.5.9", - "psy/psysh": "~0.5.1", + "psy/psysh": "0.6.*", "swiftmailer/swiftmailer": "~5.1", "symfony/console": "2.7.*", "symfony/css-selector": "2.7.*", @@ -950,7 +952,6 @@ "illuminate/events": "self.version", "illuminate/exception": "self.version", "illuminate/filesystem": "self.version", - "illuminate/foundation": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", "illuminate/log": "self.version", @@ -969,7 +970,7 @@ "require-dev": { "aws/aws-sdk-php": "~3.0", "iron-io/iron_mq": "~2.0", - "mockery/mockery": "~0.9.1", + "mockery/mockery": "~0.9.2", "pda/pheanstalk": "~3.0", "phpunit/phpunit": "~4.0", "predis/predis": "~1.0" @@ -978,7 +979,7 @@ "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).", "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).", - "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers (~5.3|~6.0).", + "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~5.3|~6.0).", "iron-io/iron_mq": "Required to use the iron queue driver (~2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).", @@ -1020,39 +1021,46 @@ "framework", "laravel" ], - "time": "2015-08-12 18:16:08" + "time": "2015-12-17 20:35:38" }, { "name": "league/commonmark", - "version": "0.7.2", + "version": "0.12.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "7fecb7bdef265e45c80c53e1000e2056a9463401" + "reference": "3eb64850ee688623db494398a5284a7a4cdf7b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/7fecb7bdef265e45c80c53e1000e2056a9463401", - "reference": "7fecb7bdef265e45c80c53e1000e2056a9463401", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3eb64850ee688623db494398a5284a7a4cdf7b47", + "reference": "3eb64850ee688623db494398a5284a7a4cdf7b47", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=5.3.3" + "php": ">=5.4.8" }, "replace": { "colinodell/commonmark-php": "*" }, "require-dev": { "erusev/parsedown": "~1.0", - "jgm/commonmark": "0.18", + "jgm/commonmark": "0.22", + "jgm/smartpunct": "0.22", "michelf/php-markdown": "~1.4", - "phpunit/phpunit": "~4.3" + "mikehaertl/php-shellcommand": "~1.1.0", + "phpunit/phpunit": "~4.3|~5.0", + "scrutinizer/ocular": "^1.1", + "symfony/finder": "~2.3" }, + "bin": [ + "bin/commonmark" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "0.8-dev" + "dev-master": "0.13-dev" } }, "autoload": { @@ -1079,20 +1087,20 @@ "markdown", "parser" ], - "time": "2015-03-08 17:48:53" + "time": "2015-11-04 14:24:41" }, { "name": "league/csv", - "version": "7.1.2", + "version": "7.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "2ee1760c262c41986f6371775907fc9e8603fd26" + "reference": "69bafa6ff924fbf9effe4275d6eb16be81a853ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/2ee1760c262c41986f6371775907fc9e8603fd26", - "reference": "2ee1760c262c41986f6371775907fc9e8603fd26", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/69bafa6ff924fbf9effe4275d6eb16be81a853ef", + "reference": "69bafa6ff924fbf9effe4275d6eb16be81a853ef", "shasum": "" }, "require": { @@ -1100,13 +1108,13 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.0", - "scrutinizer/ocular": "~1.1" + "fabpot/php-cs-fixer": "^1.9", + "phpunit/phpunit": "^4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.2-dev" } }, "autoload": { @@ -1136,25 +1144,28 @@ "read", "write" ], - "time": "2015-06-10 11:12:37" + "time": "2015-11-02 07:36:25" }, { "name": "league/flysystem", - "version": "1.0.11", + "version": "1.0.15", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040" + "reference": "31525caf9e8772683672fefd8a1ca0c0736020f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c16222fdc02467eaa12cb6d6d0e65527741f6040", - "reference": "c16222fdc02467eaa12cb6d6d0e65527741f6040", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/31525caf9e8772683672fefd8a1ca0c0736020f4", + "reference": "31525caf9e8772683672fefd8a1ca0c0736020f4", "shasum": "" }, "require": { "php": ">=5.4.0" }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, "require-dev": { "ext-fileinfo": "*", "mockery/mockery": "~0.9", @@ -1217,20 +1228,20 @@ "sftp", "storage" ], - "time": "2015-07-28 20:41:58" + "time": "2015-09-30 22:26:59" }, { "name": "monolog/monolog", - "version": "1.16.0", + "version": "1.17.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "c0c0b4bee3aabce7182876b0d912ef2595563db7" + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c0c0b4bee3aabce7182876b0d912ef2595563db7", - "reference": "c0c0b4bee3aabce7182876b0d912ef2595563db7", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", "shasum": "" }, "require": { @@ -1244,10 +1255,11 @@ "aws/aws-sdk-php": "^2.4.9", "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "~0.8", + "raven/raven": "^0.13", "ruflin/elastica": ">=0.90 <3.0", "swiftmailer/swiftmailer": "~5.3", "videlalvaro/php-amqplib": "~2.4" @@ -1293,7 +1305,7 @@ "logging", "psr-3" ], - "time": "2015-08-09 17:44:44" + "time": "2015-10-14 12:51:02" }, { "name": "mtdowling/cron-expression", @@ -1341,16 +1353,16 @@ }, { "name": "nesbot/carbon", - "version": "1.20.0", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3" + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", - "reference": "bfd3eaba109c9a2405c92174c8e17f20c2b9caf3", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7", + "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7", "shasum": "" }, "require": { @@ -1358,12 +1370,12 @@ "symfony/translation": "~2.6|~3.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0|~5.0" }, "type": "library", "autoload": { - "psr-0": { - "Carbon": "src" + "psr-4": { + "Carbon\\": "src/Carbon/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1384,36 +1396,42 @@ "datetime", "time" ], - "time": "2015-06-25 04:19:39" + "time": "2015-11-04 20:07:17" }, { "name": "nikic/php-parser", - "version": "v1.4.0", + "version": "v2.0.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "196f177cfefa0f1f7166c0a05d8255889be12418" + "reference": "c542e5d86a9775abd1021618eb2430278bfc1e01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/196f177cfefa0f1f7166c0a05d8255889be12418", - "reference": "196f177cfefa0f1f7166c0a05d8255889be12418", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c542e5d86a9775abd1021618eb2430278bfc1e01", + "reference": "c542e5d86a9775abd1021618eb2430278bfc1e01", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3" + "php": ">=5.4" }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "files": [ - "lib/bootstrap.php" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1429,7 +1447,55 @@ "parser", "php" ], - "time": "2015-07-14 17:31:05" + "time": "2015-12-04 15:28:43" + }, + { + "name": "paragonie/random_compat", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2015-12-10 14:48:13" }, { "name": "psr/log", @@ -1471,29 +1537,29 @@ }, { "name": "psy/psysh", - "version": "v0.5.2", + "version": "v0.6.1", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975" + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/aaf8772ade08b5f0f6830774a5d5c2f800415975", - "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/0f04df0b23663799a8941fae13cd8e6299bde3ed", + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed", "shasum": "" }, "require": { "dnoegel/php-xdg-base-dir": "0.1", "jakub-onderka/php-console-highlighter": "0.3.*", - "nikic/php-parser": "^1.2.1", + "nikic/php-parser": "^1.2.1|~2.0", "php": ">=5.3.9", "symfony/console": "~2.3.10|^2.4.2|~3.0", "symfony/var-dumper": "~2.7|~3.0" }, "require-dev": { "fabpot/php-cs-fixer": "~1.5", - "phpunit/phpunit": "~3.7|~4.0", + "phpunit/phpunit": "~3.7|~4.0|~5.0", "squizlabs/php_codesniffer": "~2.0", "symfony/finder": "~2.1|~3.0" }, @@ -1509,15 +1575,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-develop": "0.6.x-dev" + "dev-develop": "0.7.x-dev" } }, "autoload": { "files": [ "src/Psy/functions.php" ], - "psr-0": { - "Psy\\": "src/" + "psr-4": { + "Psy\\": "src/Psy/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1539,7 +1605,7 @@ "interactive", "shell" ], - "time": "2015-07-16 15:26:57" + "time": "2015-11-12 16:18:56" }, { "name": "rcrowe/twigbridge", @@ -1660,16 +1726,16 @@ }, { "name": "symfony/console", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e" + "url": "https://github.com/symfony/console.git", + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/d6cf02fe73634c96677e428f840704bfbcaec29e", - "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e", + "url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750", + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750", "shasum": "" }, "require": { @@ -1678,7 +1744,6 @@ "require-dev": { "psr/log": "~1.0", "symfony/event-dispatcher": "~2.1", - "symfony/phpunit-bridge": "~2.7", "symfony/process": "~2.1" }, "suggest": { @@ -1695,7 +1760,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1713,28 +1781,25 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-07-28 15:18:12" + "time": "2015-11-18 09:54:26" }, { "name": "symfony/css-selector", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/CssSelector.git", - "reference": "0b5c07b516226b7dd32afbbc82fe547a469c5092" + "url": "https://github.com/symfony/css-selector.git", + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/CssSelector/zipball/0b5c07b516226b7dd32afbbc82fe547a469c5092", - "reference": "0b5c07b516226b7dd32afbbc82fe547a469c5092", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/abb47717fb88aebd9437da2fc8bb01a50a36679f", + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "type": "library", "extra": { "branch-alias": { @@ -1744,7 +1809,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1766,20 +1834,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2015-05-15 13:33:16" + "time": "2015-10-30 20:10:21" }, { "name": "symfony/debug", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/Debug.git", - "reference": "9daa1bf9f7e615fa2fba30357e479a90141222e3" + "url": "https://github.com/symfony/debug.git", + "reference": "0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Debug/zipball/9daa1bf9f7e615fa2fba30357e479a90141222e3", - "reference": "9daa1bf9f7e615fa2fba30357e479a90141222e3", + "url": "https://api.github.com/repos/symfony/debug/zipball/0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa", + "reference": "0dbc119596f4afc82d9b2eb2a7e6a4af1ee763fa", "shasum": "" }, "require": { @@ -1791,13 +1859,7 @@ }, "require-dev": { "symfony/class-loader": "~2.2", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2", - "symfony/phpunit-bridge": "~2.7" - }, - "suggest": { - "symfony/http-foundation": "", - "symfony/http-kernel": "" + "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2" }, "type": "library", "extra": { @@ -1808,7 +1870,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1826,28 +1891,27 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:07:40" + "time": "2015-10-30 20:10:21" }, { "name": "symfony/dom-crawler", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/DomCrawler.git", - "reference": "9dabece63182e95c42b06967a0d929a5df78bc35" + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/9dabece63182e95c42b06967a0d929a5df78bc35", - "reference": "9dabece63182e95c42b06967a0d929a5df78bc35", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b33593cbfe1d81b50d48353f338aca76a08658d8", + "reference": "b33593cbfe1d81b50d48353f338aca76a08658d8", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "symfony/css-selector": "~2.3", - "symfony/phpunit-bridge": "~2.7" + "symfony/css-selector": "~2.3" }, "suggest": { "symfony/css-selector": "" @@ -1861,7 +1925,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1879,20 +1946,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:07:40" + "time": "2015-11-02 20:20:53" }, { "name": "symfony/event-dispatcher", - "version": "v2.7.3", + "version": "v2.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", - "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc", + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc", "shasum": "" }, "require": { @@ -1900,11 +1967,10 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.3" + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1913,13 +1979,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1937,28 +2006,25 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-06-18 19:21:56" + "time": "2015-10-30 20:15:42" }, { "name": "symfony/finder", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4" + "url": "https://github.com/symfony/finder.git", + "reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4", - "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4", + "url": "https://api.github.com/repos/symfony/finder/zipball/a06a0c0ff7db3736a50d530c908cca547bf13da9", + "reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "type": "library", "extra": { "branch-alias": { @@ -1968,7 +2034,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1986,28 +2055,27 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:07:40" + "time": "2015-10-30 20:10:21" }, { "name": "symfony/http-foundation", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "863af6898081b34c65d42100c370b9f3c51b70ca" + "url": "https://github.com/symfony/http-foundation.git", + "reference": "e83a3d105ddaf5a113e803c904fdec552d1f1c35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/863af6898081b34c65d42100c370b9f3c51b70ca", - "reference": "863af6898081b34c65d42100c370b9f3c51b70ca", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e83a3d105ddaf5a113e803c904fdec552d1f1c35", + "reference": "e83a3d105ddaf5a113e803c904fdec552d1f1c35", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "symfony/expression-language": "~2.4", - "symfony/phpunit-bridge": "~2.7" + "symfony/expression-language": "~2.4" }, "type": "library", "extra": { @@ -2021,6 +2089,9 @@ }, "classmap": [ "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2039,20 +2110,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2015-07-22 10:11:00" + "time": "2015-11-20 17:41:18" }, { "name": "symfony/http-kernel", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/HttpKernel.git", - "reference": "405d3e7a59ff7a28ec469441326a0ac79065ea98" + "url": "https://github.com/symfony/http-kernel.git", + "reference": "5570de31e8fbc03777a8c61eb24f9b626e5e5941" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/405d3e7a59ff7a28ec469441326a0ac79065ea98", - "reference": "405d3e7a59ff7a28ec469441326a0ac79065ea98", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5570de31e8fbc03777a8c61eb24f9b626e5e5941", + "reference": "5570de31e8fbc03777a8c61eb24f9b626e5e5941", "shasum": "" }, "require": { @@ -2075,7 +2146,6 @@ "symfony/dom-crawler": "~2.0,>=2.0.5", "symfony/expression-language": "~2.4", "symfony/finder": "~2.0,>=2.0.5", - "symfony/phpunit-bridge": "~2.7", "symfony/process": "~2.0,>=2.0.5", "symfony/routing": "~2.2", "symfony/stopwatch": "~2.3", @@ -2101,7 +2171,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2119,28 +2192,133 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2015-07-31 13:24:45" + "time": "2015-11-23 11:57:49" }, { - "name": "symfony/process", - "version": "v2.7.3", + "name": "symfony/polyfill-php56", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "48aeb0e48600321c272955132d7606ab0a49adb3" + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "a6bd4770a6967517e6610529e14afaa3111094a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3", - "reference": "48aeb0e48600321c272955132d7606ab0a49adb3", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/a6bd4770a6967517e6610529e14afaa3111094a3", + "reference": "a6bd4770a6967517e6610529e14afaa3111094a3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4271c55cbc0a77b2641f861b978123e46b3da969", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, + { + "name": "symfony/process", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f6290983c8725d0afa29bdc3e5295879de3e58f5", + "reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "type": "library", "extra": { "branch-alias": { @@ -2150,7 +2328,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2168,20 +2349,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-07-01 11:25:50" + "time": "2015-11-19 16:11:24" }, { "name": "symfony/routing", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/Routing.git", - "reference": "ea9134f277162b02e5f80ac058b75a77637b0d26" + "url": "https://github.com/symfony/routing.git", + "reference": "7450f6196711b124fb8b04a12286d01a0401ddfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Routing/zipball/ea9134f277162b02e5f80ac058b75a77637b0d26", - "reference": "ea9134f277162b02e5f80ac058b75a77637b0d26", + "url": "https://api.github.com/repos/symfony/routing/zipball/7450f6196711b124fb8b04a12286d01a0401ddfe", + "reference": "7450f6196711b124fb8b04a12286d01a0401ddfe", "shasum": "" }, "require": { @@ -2197,7 +2378,6 @@ "symfony/config": "~2.7", "symfony/expression-language": "~2.4", "symfony/http-foundation": "~2.3", - "symfony/phpunit-bridge": "~2.7", "symfony/yaml": "~2.0,>=2.0.5" }, "suggest": { @@ -2215,7 +2395,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Routing\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2239,20 +2422,20 @@ "uri", "url" ], - "time": "2015-07-09 16:07:40" + "time": "2015-11-18 13:41:01" }, { "name": "symfony/translation", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", - "url": "https://github.com/symfony/Translation.git", - "reference": "c8dc34cc936152c609cdd722af317e4239d10dd6" + "url": "https://github.com/symfony/translation.git", + "reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Translation/zipball/c8dc34cc936152c609cdd722af317e4239d10dd6", - "reference": "c8dc34cc936152c609cdd722af317e4239d10dd6", + "url": "https://api.github.com/repos/symfony/translation/zipball/e4ecb9c3ba1304eaf24de15c2d7a428101c1982f", + "reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f", "shasum": "" }, "require": { @@ -2264,8 +2447,7 @@ "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.7", - "symfony/intl": "~2.3", - "symfony/phpunit-bridge": "~2.7", + "symfony/intl": "~2.4", "symfony/yaml": "~2.2" }, "suggest": { @@ -2282,7 +2464,10 @@ "autoload": { "psr-4": { "Symfony\\Component\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2300,28 +2485,25 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2015-07-09 16:07:40" + "time": "2015-11-18 13:41:01" }, { "name": "symfony/var-dumper", - "version": "v2.7.3", + "version": "v2.7.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e8903ebba5eb019f5886ffce739ea9e3b7519579" + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e8903ebba5eb019f5886ffce739ea9e3b7519579", - "reference": "e8903ebba5eb019f5886ffce739ea9e3b7519579", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8", + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, "suggest": { "ext-symfony_debug": "" }, @@ -2337,7 +2519,10 @@ ], "psr-4": { "Symfony\\Component\\VarDumper\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2359,29 +2544,33 @@ "debug", "dump" ], - "time": "2015-07-28 15:18:12" + "time": "2015-11-18 13:41:01" }, { "name": "twig/twig", - "version": "v1.20.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "1ea4e5f81c6d005fe84d0b38e1c4f1955eb86844" + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/1ea4e5f81c6d005fe84d0b38e1c4f1955eb86844", - "reference": "1ea4e5f81c6d005fe84d0b38e1c4f1955eb86844", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", "shasum": "" }, "require": { "php": ">=5.2.7" }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.20-dev" + "dev-master": "1.23-dev" } }, "autoload": { @@ -2416,7 +2605,7 @@ "keywords": [ "templating" ], - "time": "2015-08-12 15:56:39" + "time": "2015-11-05 12:49:06" }, { "name": "vlucas/phpdotenv", @@ -2466,16 +2655,16 @@ }, { "name": "watson/validating", - "version": "1.0.3", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/dwightwatson/validating.git", - "reference": "c32912f760b456c043657951683ed6c468ab83e7" + "reference": "0b225ddc3187745ff9efcbcd0d9b4b712df190ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dwightwatson/validating/zipball/c32912f760b456c043657951683ed6c468ab83e7", - "reference": "c32912f760b456c043657951683ed6c468ab83e7", + "url": "https://api.github.com/repos/dwightwatson/validating/zipball/0b225ddc3187745ff9efcbcd0d9b4b712df190ba", + "reference": "0b225ddc3187745ff9efcbcd0d9b4b712df190ba", "shasum": "" }, "require": { @@ -2517,7 +2706,7 @@ "laravel", "validation" ], - "time": "2015-06-03 02:25:38" + "time": "2015-12-16 23:15:51" }, { "name": "zizaco/entrust", @@ -2525,12 +2714,12 @@ "source": { "type": "git", "url": "https://github.com/Zizaco/entrust.git", - "reference": "3a768eb52300ec4efe9d6a6685b9bf0db6a50ffd" + "reference": "be28aa634c44551ae5187894c903094a8ce8baee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Zizaco/entrust/zipball/3a768eb52300ec4efe9d6a6685b9bf0db6a50ffd", - "reference": "3a768eb52300ec4efe9d6a6685b9bf0db6a50ffd", + "url": "https://api.github.com/repos/Zizaco/entrust/zipball/be28aa634c44551ae5187894c903094a8ce8baee", + "reference": "be28aa634c44551ae5187894c903094a8ce8baee", "shasum": "" }, "require": { @@ -2584,22 +2773,22 @@ "permission", "roles" ], - "time": "2015-07-09 16:32:53" + "time": "2015-11-12 17:38:37" } ], "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v2.0.5", + "version": "v2.0.6", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "753106cd1f9c724d05bb48728bc652231174e279" + "reference": "23672cbbe78278ff1fdb258caa0b1de7b5d0bc0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/753106cd1f9c724d05bb48728bc652231174e279", - "reference": "753106cd1f9c724d05bb48728bc652231174e279", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/23672cbbe78278ff1fdb258caa0b1de7b5d0bc0c", + "reference": "23672cbbe78278ff1fdb258caa0b1de7b5d0bc0c", "shasum": "" }, "require": { @@ -2611,7 +2800,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -2640,7 +2829,7 @@ "profiler", "webprofiler" ], - "time": "2015-07-09 14:51:59" + "time": "2015-09-09 11:39:27" }, { "name": "barryvdh/laravel-ide-helper", @@ -2707,16 +2896,16 @@ }, { "name": "maximebf/debugbar", - "version": "v1.10.4", + "version": "v1.10.5", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "7b2006e6e095126b5a061ec33fca3d90ea8a8219" + "reference": "30e53e8a28284b69dd223c9f5ee8957befd72636" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/7b2006e6e095126b5a061ec33fca3d90ea8a8219", - "reference": "7b2006e6e095126b5a061ec33fca3d90ea8a8219", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e53e8a28284b69dd223c9f5ee8957befd72636", + "reference": "30e53e8a28284b69dd223c9f5ee8957befd72636", "shasum": "" }, "require": { @@ -2759,7 +2948,7 @@ "keywords": [ "debug" ], - "time": "2015-02-05 07:51:20" + "time": "2015-10-19 20:35:12" }, { "name": "phpdocumentor/reflection-docblock", @@ -2812,35 +3001,37 @@ }, { "name": "symfony/class-loader", - "version": "v2.7.3", + "version": "v2.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/ClassLoader.git", - "reference": "2fccbc544997340808801a7410cdcb96dd12edc4" + "url": "https://github.com/symfony/class-loader.git", + "reference": "51f83451bf0ddfc696e47e4642d6cd10fcfce160" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/2fccbc544997340808801a7410cdcb96dd12edc4", - "reference": "2fccbc544997340808801a7410cdcb96dd12edc4", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/51f83451bf0ddfc696e47e4642d6cd10fcfce160", + "reference": "51f83451bf0ddfc696e47e4642d6cd10fcfce160", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "symfony/finder": "~2.0,>=2.0.5", - "symfony/phpunit-bridge": "~2.7" + "symfony/finder": "~2.0,>=2.0.5|~3.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\ClassLoader\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2858,7 +3049,7 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2015-06-25 12:52:11" + "time": "2015-11-26 07:00:59" } ], "aliases": [], diff --git a/config/app.php b/config/app.php index 1ab4e1f26d..93cef6cb6f 100644 --- a/config/app.php +++ b/config/app.php @@ -139,8 +139,8 @@ return [ 'TwigBridge\ServiceProvider', 'DaveJamesMiller\Breadcrumbs\ServiceProvider', - //'Barryvdh\Debugbar\ServiceProvider', - //'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', +// 'Barryvdh\Debugbar\ServiceProvider', +// 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider', 'Zizaco\Entrust\EntrustServiceProvider', /* diff --git a/config/database.php b/config/database.php index 6d6387980a..f1a98b129b 100644 --- a/config/database.php +++ b/config/database.php @@ -49,7 +49,7 @@ return [ 'sqlite' => [ 'driver' => 'sqlite', - 'database' => __DIR__ . '/../storage/database/testing.db', + 'database' => storage_path('database/testing.db'), 'prefix' => '', ], diff --git a/config/firefly.php b/config/firefly.php index 0259fdf80b..0bab3ab71a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -2,7 +2,7 @@ return [ 'chart' => 'chartjs', - 'version' => '3.5.3', + 'version' => '3.5.4', 'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'], 'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], 'csv_import_enabled' => true, diff --git a/config/mail.php b/config/mail.php index 31c5eaa8a2..a89c2eaad6 100644 --- a/config/mail.php +++ b/config/mail.php @@ -15,7 +15,8 @@ return [ | */ - 'driver' => env('EMAIL_DRIVER', 'smtp'), + 'blocked_domains' => explode(',', env('BLOCKED_DOMAINS')), + 'driver' => env('EMAIL_DRIVER', 'smtp'), /* |-------------------------------------------------------------------------- @@ -28,7 +29,7 @@ return [ | */ - 'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'), + 'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'), /* |-------------------------------------------------------------------------- @@ -41,7 +42,7 @@ return [ | */ - 'port' => 587, + 'port' => 587, /* |-------------------------------------------------------------------------- @@ -54,7 +55,7 @@ return [ | */ - 'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'], + 'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'], /* |-------------------------------------------------------------------------- @@ -80,7 +81,7 @@ return [ | */ - 'username' => env('EMAIL_USERNAME', null), + 'username' => env('EMAIL_USERNAME', null), /* |-------------------------------------------------------------------------- @@ -93,7 +94,7 @@ return [ | */ - 'password' => env('EMAIL_PASSWORD', null), + 'password' => env('EMAIL_PASSWORD', null), /* |-------------------------------------------------------------------------- @@ -106,7 +107,7 @@ return [ | */ - 'sendmail' => '/usr/sbin/sendmail -bs', + 'sendmail' => '/usr/sbin/sendmail -bs', /* |-------------------------------------------------------------------------- @@ -119,6 +120,6 @@ return [ | */ - 'pretend' => env('EMAIL_PRETEND', false), + 'pretend' => env('EMAIL_PRETEND', false), ]; diff --git a/database/seeds/TestDataSeeder.php b/database/seeds/TestDataSeeder.php index 1e583acc44..9d6bf19497 100644 --- a/database/seeds/TestDataSeeder.php +++ b/database/seeds/TestDataSeeder.php @@ -49,7 +49,7 @@ class TestDataSeeder extends Seeder $this->createPiggybanks(); // dates: - $start = Carbon::now()->subyear()->startOfMonth(); + $start = Carbon::now()->subYears(5)->startOfMonth(); $end = Carbon::now()->endOfDay(); diff --git a/database/seeds/TransactionTypeSeeder.php b/database/seeds/TransactionTypeSeeder.php index ecdff20b05..4f7a6e13d6 100644 --- a/database/seeds/TransactionTypeSeeder.php +++ b/database/seeds/TransactionTypeSeeder.php @@ -12,10 +12,10 @@ class TransactionTypeSeeder extends Seeder DB::table('transaction_types')->delete(); - TransactionType::create(['type' => 'Withdrawal']); - TransactionType::create(['type' => 'Deposit']); - TransactionType::create(['type' => 'Transfer']); - TransactionType::create(['type' => 'Opening balance']); + TransactionType::create(['type' => TransactionType::WITHDRAWAL]); + TransactionType::create(['type' => TransactionType::DEPOSIT]); + TransactionType::create(['type' => TransactionType::TRANSFER]); + TransactionType::create(['type' => TransactionType::OPENING_BALANCE]); } } diff --git a/public/js/accounting.min.js b/public/js/accounting.min.js new file mode 100644 index 0000000000..8e09b86846 --- /dev/null +++ b/public/js/accounting.min.js @@ -0,0 +1,4 @@ +/*! + * accounting.js v0.4.2, copyright 2014 Open Exchange Rates, MIT license, http://openexchangerates.github.io/accounting.js + */ +(function(p,z){function q(a){return!!(""===a||a&&a.charCodeAt&&a.substr)}function m(a){return u?u(a):"[object Array]"===v.call(a)}function r(a){return"[object Object]"===v.call(a)}function s(a,b){var d,a=a||{},b=b||{};for(d in b)b.hasOwnProperty(d)&&null==a[d]&&(a[d]=b[d]);return a}function j(a,b,d){var c=[],e,h;if(!a)return c;if(w&&a.map===w)return a.map(b,d);for(e=0,h=a.length;ea?"-":"",g=parseInt(y(Math.abs(a||0),h),10)+"",l=3a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal))};c.formatColumn=function(a,b,d,i,e,h){if(!a)return[];var f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format),l=g.pos.indexOf("%s")a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal));if(a.length>k)k=a.length;return a});return j(a,function(a){return q(a)&&a.length", + scaleLabel: " <%= accounting.formatMoney(value) %>", tooltipFillColor: "rgba(0,0,0,0.5)", - multiTooltipTemplate: "<%=datasetLabel%>: <%= '" + currencySymbol + " ' + Number(value).toFixed(2).replace('.', ',') %>" + multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>" }; @@ -61,7 +78,7 @@ var defaultPieOptions = { scaleFontSize: 10, responsive: false, tooltipFillColor: "rgba(0,0,0,0.5)", - tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>", + tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>", }; @@ -90,10 +107,10 @@ var defaultColumnOptions = { scaleFontSize: 10, responsive: false, animation: false, - scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>", + scaleLabel: "<%= accounting.formatMoney(value) %>", tooltipFillColor: "rgba(0,0,0,0.5)", - tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>", - multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>" + tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>", + multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>" }; var defaultStackedColumnOptions = { @@ -105,9 +122,9 @@ var defaultStackedColumnOptions = { animation: false, scaleFontSize: 10, responsive: false, - scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>", + scaleLabel: "<%= accounting.formatMoney(value) %>", tooltipFillColor: "rgba(0,0,0,0.5)", - multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>" + multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>" }; diff --git a/public/js/reports.js b/public/js/reports.js deleted file mode 100644 index b123497481..0000000000 --- a/public/js/reports.js +++ /dev/null @@ -1,93 +0,0 @@ -/* globals google, expenseRestShow:true, incomeRestShow:true, year, shared, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ - -$(function () { - "use strict"; - drawChart(); -}); - - -function drawChart() { - "use strict"; - if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') { - columnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart'); - columnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart'); - } - if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') { - stackedColumnChart('chart/budget/year/' + year + shared, 'budgets'); - stackedColumnChart('chart/category/spent-in-year/' + year + shared, 'categories-spent-in-year'); - stackedColumnChart('chart/category/earned-in-year/' + year + shared, 'categories-earned-in-year'); - } - if (typeof lineChart !== 'undefined' && typeof month !== 'undefined') { - lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart'); - } -} - - -function openModal(e) { - "use strict"; - var target = $(e.target).parent(); - var URL = target.attr('href'); - - $.get(URL).success(function (data) { - $('#defaultModal').empty().html(data).modal('show'); - - }).fail(function () { - alert('Could not load data.'); - }); - - return false; -} - - -function showIncomes() { - "use strict"; - if (incomeRestShow) { - // hide everything, make button say "show" - $('#showIncomes').text(showTheRest); - $('.incomesCollapsed').removeClass('in').addClass('out'); - - // toggle: - incomeRestShow = false; - } else { - // show everything, make button say "hide". - $('#showIncomes').text(hideTheRest); - $('.incomesCollapsed').removeClass('out').addClass('in'); - - // toggle: - incomeRestShow = true; - } - - return false; -} - -function showExpenses() { - "use strict"; - if (expenseRestShow) { - // hide everything, make button say "show" - $('#showExpenses').text(showTheRestExpense); - $('.expenseCollapsed').removeClass('in').addClass('out'); - - // toggle: - expenseRestShow = false; - } else { - // show everything, make button say "hide". - $('#showExpenses').text(hideTheRestExpense); - $('.expenseCollapsed').removeClass('out').addClass('in'); - - // toggle: - expenseRestShow = true; - } - - return false; -} - -$(function () { - "use strict"; - $('.openModal').on('click', openModal); - - - // click open the top X income list: - $('#showIncomes').click(showIncomes); - // click open the top X expense list: - $('#showExpenses').click(showExpenses); -}); \ No newline at end of file diff --git a/public/js/reports/default/month.js b/public/js/reports/default/month.js new file mode 100644 index 0000000000..be66324dff --- /dev/null +++ b/public/js/reports/default/month.js @@ -0,0 +1,64 @@ +/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ + + +$(function () { + "use strict"; + drawChart(); + + // click open the top X income list: + $('#showIncomes').click(showIncomes); + // click open the top X expense list: + $('#showExpenses').click(showExpenses); +}); + + +function drawChart() { + "use strict"; + + // month view: + // draw account chart + lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart'); +} + + +function showIncomes() { + "use strict"; + if (incomeRestShow) { + // hide everything, make button say "show" + $('#showIncomes').text(showTheRest); + $('.incomesCollapsed').removeClass('in').addClass('out'); + + // toggle: + incomeRestShow = false; + } else { + // show everything, make button say "hide". + $('#showIncomes').text(hideTheRest); + $('.incomesCollapsed').removeClass('out').addClass('in'); + + // toggle: + incomeRestShow = true; + } + + return false; +} + +function showExpenses() { + "use strict"; + if (expenseRestShow) { + // hide everything, make button say "show" + $('#showExpenses').text(showTheRestExpense); + $('.expenseCollapsed').removeClass('in').addClass('out'); + + // toggle: + expenseRestShow = false; + } else { + // show everything, make button say "hide". + $('#showExpenses').text(hideTheRestExpense); + $('.expenseCollapsed').removeClass('out').addClass('in'); + + // toggle: + expenseRestShow = true; + } + + return false; +} \ No newline at end of file diff --git a/public/js/reports/default/multi-year.js b/public/js/reports/default/multi-year.js new file mode 100644 index 0000000000..d43475f6c1 --- /dev/null +++ b/public/js/reports/default/multi-year.js @@ -0,0 +1,155 @@ +/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ + + +$(function () { + "use strict"; + drawChart(); + +}); + + +function drawChart() { + "use strict"; + + // income and expense over multi year: + columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); + columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); + + + $.each($('.account-chart'), function (i, v) { + var holder = $(v); + console.log('Will draw chart for account #' + holder.data('id')); + }); + + // draw budget chart based on selected budgets: + $('.budget-checkbox').on('change', updateBudgetChart); + selectBudgetsByCookie(); + updateBudgetChart(); + + // draw category chart based on selected budgets: + $('.category-checkbox').on('change', updateCategoryChart); + selectCategoriesByCookie(); + updateCategoryChart(); +} + +function selectBudgetsByCookie() { + "use strict"; + var cookie = readCookie('multi-year-budgets'); + if (cookie !== null) { + var cookieArray = cookie.split(','); + for (var x in cookieArray) { + var budgetId = cookieArray[x]; + $('.budget-checkbox[value="' + budgetId + '"').prop('checked', true); + } + } +} + +function selectCategoriesByCookie() { + "use strict"; + var cookie = readCookie('multi-year-categories'); + if (cookie !== null) { + var cookieArray = cookie.split(','); + for (var x in cookieArray) { + var categoryId = cookieArray[x]; + $('.category-checkbox[value="' + categoryId + '"').prop('checked', true); + } + } +} + +function updateBudgetChart() { + "use strict"; + console.log('will update budget chart.'); + // get all budget ids: + var budgets = []; + $.each($('.budget-checkbox'), function (i, v) { + var current = $(v); + if (current.prop('checked')) { + budgets.push(current.val()); + } + }); + + if (budgets.length > 0) { + + var budgetIds = budgets.join(','); + + // remove old chart: + $('#budgets-chart').replaceWith(''); + + // hide message: + $('#budgets-chart-message').hide(); + + // draw chart. Redraw when exists? Not sure if we support that. + columnChart('chart/budget/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + budgetIds, 'budgets-chart'); + createCookie('multi-year-budgets', budgets, 365); + } else { + // hide canvas, show message: + $('#budgets-chart-message').show(); + $('#budgets-chart').hide(); + + } + +} + +function updateCategoryChart() { + "use strict"; + console.log('will update category chart.'); + // get all category ids: + var categories = []; + $.each($('.category-checkbox'), function (i, v) { + var current = $(v); + if (current.prop('checked')) { + categories.push(current.val()); + } + }); + + if (categories.length > 0) { + + var categoryIds = categories.join(','); + + // remove old chart: + $('#categories-chart').replaceWith(''); + + // hide message: + $('#categories-chart-message').hide(); + + // draw chart. Redraw when exists? Not sure if we support that. + columnChart('chart/category/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + categoryIds, 'categories-chart'); + createCookie('multi-year-categories', categories, 365); + } else { + // hide canvas, show message: + $('#categories-chart-message').show(); + $('#categories-chart').hide(); + + } +} + + +function createCookie(name, value, days) { + "use strict"; + var expires; + + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; +} + +function readCookie(name) { + "use strict"; + var nameEQ = encodeURIComponent(name) + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + return null; +} + +function eraseCookie(name) { + createCookie(name, "", -1); +} \ No newline at end of file diff --git a/public/js/reports/default/reports.js b/public/js/reports/default/reports.js new file mode 100644 index 0000000000..10997093bc --- /dev/null +++ b/public/js/reports/default/reports.js @@ -0,0 +1,212 @@ +/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ + + +$(function () { + "use strict"; + drawChart(); + + if ($('#inputDateRange').length > 0) { + + picker = $('#inputDateRange').daterangepicker( + { + locale: { + format: 'YYYY-MM-DD', + firstDay: 1, + }, + minDate: minDate, + drops: 'up', + } + ); + + + // set values from cookies, if any: + if (readCookie('report-type') !== null) { + $('select[name="report_type"]').val(readCookie('report-type')); + } + + if ((readCookie('report-accounts') !== null)) { + var arr = readCookie('report-accounts').split(','); + arr.forEach(function (val) { + $('input[type="checkbox"][value="' + val + '"]').prop('checked', true); + }); + } + + // set date: + var startStr = readCookie('report-start'); + var endStr = readCookie('report-end'); + if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) { + var startDate = moment(startStr, "YYYYMMDD"); + var endDate = moment(endStr, "YYYYMMDD"); + var datePicker = $('#inputDateRange').data('daterangepicker'); + datePicker.setStartDate(startDate); + datePicker.setEndDate(endDate); + } + } + + $('.openModal').on('click', openModal); + + $('.date-select').on('click', preSelectDate); + + $('#report-form').on('submit', catchSubmit); + + + // click open the top X income list: + $('#showIncomes').click(showIncomes); + // click open the top X expense list: + $('#showExpenses').click(showExpenses); +}); + +function catchSubmit() { + "use strict"; + // default;20141201;20141231;4;5 + // report name: + var url = '' + $('select[name="report_type"]').val() + '/'; + + // date, processed: + var picker = $('#inputDateRange').data('daterangepicker'); + url += moment(picker.startDate).format("YYYYMMDD") + '/'; + url += moment(picker.endDate).format("YYYYMMDD") + '/'; + + // all account ids: + var count = 0; + var accounts = []; + $.each($('.account-checkbox'), function (i, v) { + var c = $(v); + if (c.prop('checked')) { + url += c.val() + ';'; + accounts.push(c.val()); + count++; + } + }); + if (count > 0) { + // set cookie to remember choices. + createCookie('report-type', $('select[name="report_type"]').val(), 365); + createCookie('report-accounts', accounts, 365); + createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365); + createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365); + + window.location.href = reportURL + "/" + url; + } + //console.log(url); + + return false; +} + +function preSelectDate(e) { + "use strict"; + var link = $(e.target); + var picker = $('#inputDateRange').data('daterangepicker'); + picker.setStartDate(link.data('start')); + picker.setEndDate(link.data('end')); + return false; + +} + +function drawChart() { + "use strict"; + if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') { + + columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); + columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); + } + if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') { + stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets'); + stackedColumnChart('chart/category/spent-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-year'); + stackedColumnChart('chart/category/earned-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-year'); + } + + //if (typeof lineChart !== 'undefined' && typeof month !== 'undefined' && typeof reportURL === 'undefined') { + // lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart'); + //} + + if (typeof lineChart !== 'undefined' && typeof accountIds !== 'undefined') { + lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart'); + } +} + + +function openModal(e) { + "use strict"; + var target = $(e.target).parent(); + var URL = target.attr('href'); + + $.get(URL).success(function (data) { + $('#defaultModal').empty().html(data).modal('show'); + + }).fail(function () { + alert('Could not load data.'); + }); + + return false; +} + + +function showIncomes() { + "use strict"; + if (incomeRestShow) { + // hide everything, make button say "show" + $('#showIncomes').text(showTheRest); + $('.incomesCollapsed').removeClass('in').addClass('out'); + + // toggle: + incomeRestShow = false; + } else { + // show everything, make button say "hide". + $('#showIncomes').text(hideTheRest); + $('.incomesCollapsed').removeClass('out').addClass('in'); + + // toggle: + incomeRestShow = true; + } + + return false; +} + +function showExpenses() { + "use strict"; + if (expenseRestShow) { + // hide everything, make button say "show" + $('#showExpenses').text(showTheRestExpense); + $('.expenseCollapsed').removeClass('in').addClass('out'); + + // toggle: + expenseRestShow = false; + } else { + // show everything, make button say "hide". + $('#showExpenses').text(hideTheRestExpense); + $('.expenseCollapsed').removeClass('out').addClass('in'); + + // toggle: + expenseRestShow = true; + } + + return false; +} + +function createCookie(name, value, days) { + var expires; + + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; +} + +function readCookie(name) { + var nameEQ = encodeURIComponent(name) + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + return null; +} + +function eraseCookie(name) { + createCookie(name, "", -1); +} \ No newline at end of file diff --git a/public/js/reports/default/year.js b/public/js/reports/default/year.js new file mode 100644 index 0000000000..026b30ba63 --- /dev/null +++ b/public/js/reports/default/year.js @@ -0,0 +1,67 @@ +/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ + + +$(function () { + "use strict"; + drawChart(); + + // click open the top X income list: + $('#showIncomes').click(showIncomes); + // click open the top X expense list: + $('#showExpenses').click(showExpenses); +}); + + +function drawChart() { + "use strict"; + + columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); + columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); + stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets'); + stackedColumnChart('chart/category/spent-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-year'); + stackedColumnChart('chart/category/earned-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-year'); + +} + + +function showIncomes() { + "use strict"; + if (incomeRestShow) { + // hide everything, make button say "show" + $('#showIncomes').text(showTheRest); + $('.incomesCollapsed').removeClass('in').addClass('out'); + + // toggle: + incomeRestShow = false; + } else { + // show everything, make button say "hide". + $('#showIncomes').text(hideTheRest); + $('.incomesCollapsed').removeClass('out').addClass('in'); + + // toggle: + incomeRestShow = true; + } + + return false; +} + +function showExpenses() { + "use strict"; + if (expenseRestShow) { + // hide everything, make button say "show" + $('#showExpenses').text(showTheRestExpense); + $('.expenseCollapsed').removeClass('in').addClass('out'); + + // toggle: + expenseRestShow = false; + } else { + // show everything, make button say "hide". + $('#showExpenses').text(hideTheRestExpense); + $('.expenseCollapsed').removeClass('out').addClass('in'); + + // toggle: + expenseRestShow = true; + } + + return false; +} \ No newline at end of file diff --git a/public/js/reports/index.js b/public/js/reports/index.js new file mode 100644 index 0000000000..c3c3a2b150 --- /dev/null +++ b/public/js/reports/index.js @@ -0,0 +1,122 @@ +/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */ + + +$(function () { + "use strict"; + + if ($('#inputDateRange').length > 0) { + + picker = $('#inputDateRange').daterangepicker( + { + locale: { + format: 'YYYY-MM-DD', + firstDay: 1, + }, + minDate: minDate, + drops: 'up', + } + ); + + + // set values from cookies, if any: + if (readCookie('report-type') !== null) { + $('select[name="report_type"]').val(readCookie('report-type')); + } + + if ((readCookie('report-accounts') !== null)) { + var arr = readCookie('report-accounts').split(','); + arr.forEach(function (val) { + $('input[type="checkbox"][value="' + val + '"]').prop('checked', true); + }); + } + + // set date: + var startStr = readCookie('report-start'); + var endStr = readCookie('report-end'); + if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) { + var startDate = moment(startStr, "YYYYMMDD"); + var endDate = moment(endStr, "YYYYMMDD"); + var datePicker = $('#inputDateRange').data('daterangepicker'); + datePicker.setStartDate(startDate); + datePicker.setEndDate(endDate); + } + } + + $('.date-select').on('click', preSelectDate); + $('#report-form').on('submit', catchSubmit); + +}); + +function catchSubmit() { + "use strict"; + // default;20141201;20141231;4;5 + // report name: + var url = '' + $('select[name="report_type"]').val() + '/'; + + // date, processed: + var picker = $('#inputDateRange').data('daterangepicker'); + url += moment(picker.startDate).format("YYYYMMDD") + '/'; + url += moment(picker.endDate).format("YYYYMMDD") + '/'; + + // all account ids: + var count = 0; + var accounts = []; + $.each($('.account-checkbox'), function (i, v) { + var c = $(v); + if (c.prop('checked')) { + url += c.val() + ','; + accounts.push(c.val()); + count++; + } + }); + if (count > 0) { + // set cookie to remember choices. + createCookie('report-type', $('select[name="report_type"]').val(), 365); + createCookie('report-accounts', accounts, 365); + createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365); + createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365); + + window.location.href = reportURL + "/" + url; + } + //console.log(url); + + return false; +} + +function preSelectDate(e) { + "use strict"; + var link = $(e.target); + var picker = $('#inputDateRange').data('daterangepicker'); + picker.setStartDate(link.data('start')); + picker.setEndDate(link.data('end')); + return false; + +} + + +function createCookie(name, value, days) { + "use strict"; + var expires; + + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; +} + +function readCookie(name) { + "use strict"; + var nameEQ = encodeURIComponent(name) + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + return null; +} + diff --git a/resources/lang/en/firefly.php b/resources/lang/en/firefly.php index 2c543fce80..dfb7fdbfe3 100644 --- a/resources/lang/en/firefly.php +++ b/resources/lang/en/firefly.php @@ -250,35 +250,27 @@ return [ 'details_for_expense' => 'Details for expense account ":name"', 'details_for_revenue' => 'Details for revenue account ":name"', 'details_for_cash' => 'Details for cash account ":name"', - 'store_new_asset_account' => 'Store new asset account', 'store_new_expense_account' => 'Store new expense account', 'store_new_revenue_account' => 'Store new revenue account', - 'edit_asset_account' => 'Edit asset account ":name"', 'edit_expense_account' => 'Edit expense account ":name"', 'edit_revenue_account' => 'Edit revenue account ":name"', - 'delete_asset_account' => 'Delete asset account ":name"', 'delete_expense_account' => 'Delete expense account ":name"', 'delete_revenue_account' => 'Delete revenue account ":name"', - 'asset_deleted' => 'Successfully deleted asset account ":name"', 'expense_deleted' => 'Successfully deleted expense account ":name"', 'revenue_deleted' => 'Successfully deleted revenue account ":name"', - 'update_asset_account' => 'Update asset account', 'update_expense_account' => 'Update expense account', 'update_revenue_account' => 'Update revenue account', - 'make_new_asset_account' => 'Create a new asset account', 'make_new_expense_account' => 'Create a new expense account', 'make_new_revenue_account' => 'Create a new revenue account', - 'asset_accounts' => 'Asset accounts', 'expense_accounts' => 'Expense accounts', 'revenue_accounts' => 'Revenue accounts', - 'accountExtraHelp_asset' => '', 'accountExtraHelp_expense' => '', 'accountExtraHelp_revenue' => '', @@ -341,6 +333,7 @@ return [ 'Default account' => 'Asset account', 'Expense account' => 'Expense account', 'Revenue account' => 'Revenue account', + 'Initial balance account' => 'Initial balance account', 'budgets' => 'Budgets', 'tags' => 'Tags', 'reports' => 'Reports', @@ -376,120 +369,133 @@ return [ 'profile' => 'Profile', // reports: - 'reportForYear' => 'Yearly report for :year', - 'reportForYearShared' => 'Yearly report for :year (including shared accounts)', - 'reportForMonth' => 'Montly report for :month', - 'reportForMonthShared' => 'Montly report for :month (including shared accounts)', - 'incomeVsExpenses' => 'Income vs. expenses', - 'accountBalances' => 'Account balances', - 'balanceStartOfYear' => 'Balance at start of year', - 'balanceEndOfYear' => 'Balance at end of year', - 'balanceStartOfMonth' => 'Balance at start of month', - 'balanceEndOfMonth' => 'Balance at end of month', - 'balanceStart' => 'Balance at start of period', - 'balanceEnd' => 'Balance at end of period', - 'reportsOwnAccounts' => 'Reports for your own accounts', - 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', - 'splitByAccount' => 'Split by account', - 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', - 'coveredWithTags' => 'Covered with tags', - 'leftUnbalanced' => 'Left unbalanced', - 'expectedBalance' => 'Expected balance', - 'outsideOfBudgets' => 'Outside of budgets', - 'leftInBudget' => 'Left in budget', - 'sumOfSums' => 'Sum of sums', - 'notCharged' => 'Not charged (yet)', - 'inactive' => 'Inactive', - 'difference' => 'Difference', - 'in' => 'In', - 'out' => 'Out', - 'topX' => 'top :number', - 'showTheRest' => 'Show everything', - 'hideTheRest' => 'Show only the top :number', - 'sum_of_year' => 'Sum of year', - 'average_of_year' => 'Average of year', - 'categories_earned_in_year' => 'Categories (by earnings)', - 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_default' => 'Default financial report for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Income vs. expenses', + 'accountBalances' => 'Account balances', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Expected balance', + 'outsideOfBudgets' => 'Outside of budgets', + 'leftInBudget' => 'Left in budget', + 'sumOfSums' => 'Sum of sums', + 'noCategory' => '(no category)', + 'notCharged' => 'Not charged (yet)', + 'inactive' => 'Inactive', + 'difference' => 'Difference', + 'in' => 'In', + 'out' => 'Out', + 'topX' => 'top :number', + 'showTheRest' => 'Show everything', + 'hideTheRest' => 'Show only the top :number', + 'sum_of_year' => 'Sum of year', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Average of year', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_included_accounts' => 'Included accounts', + 'report_date_range' => 'Date range', + 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', // charts: - 'dayOfMonth' => 'Day of the month', - 'month' => 'Month', - 'budget' => 'Budget', - 'spent' => 'Spent', - 'earned' => 'Earned', - 'overspent' => 'Overspent', - 'left' => 'Left', - 'noBudget' => '(no budget)', - 'maxAmount' => 'Maximum amount', - 'minAmount' => 'Minumum amount', - 'billEntry' => 'Current bill entry', - 'name' => 'Name', - 'date' => 'Date', - 'paid' => 'Paid', - 'unpaid' => 'Unpaid', - 'day' => 'Day', - 'budgeted' => 'Budgeted', - 'period' => 'Period', - 'balance' => 'Balance', - 'summary' => 'Summary', - 'sum' => 'Sum', - 'average' => 'Average', - 'balanceFor' => 'Balance for :name', + 'dayOfMonth' => 'Day of the month', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'noBudget' => '(no budget)', + 'maxAmount' => 'Maximum amount', + 'minAmount' => 'Minumum amount', + 'billEntry' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'summary' => 'Summary', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', // piggy banks: - 'piggy_bank' => 'Piggy bank', - 'new_piggy_bank' => 'Create new piggy bank', - 'store_piggy_bank' => 'Store new piggy bank', - 'account_status' => 'Account status', - 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', - 'saved_so_far' => 'Saved so far', - 'left_to_save' => 'Left to save', - 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', - 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', - 'add' => 'Add', - 'remove' => 'Remove', - 'max_amount_add' => 'The maximum amount you can add is', - 'max_amount_remove' => 'The maximum amount you can remove is', - 'update_piggy_button' => 'Update piggy bank', - 'update_piggy_title' => 'Update piggy bank ":name"', - 'details' => 'Details', - 'events' => 'Events', - 'target_amount' => 'Target amount', - 'start_date' => 'Start date', - 'target_date' => 'Target date', - 'no_target_date' => 'No target date', - 'todo' => 'to do', - 'table' => 'Table', - 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', - 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', - 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', - 'delete_piggy_bank' => 'Delete piggy bank ":name"', + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'Create new piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', + 'remove' => 'Remove', + 'max_amount_add' => 'The maximum amount you can add is', + 'max_amount_remove' => 'The maximum amount you can remove is', + 'update_piggy_button' => 'Update piggy bank', + 'update_piggy_title' => 'Update piggy bank ":name"', + 'details' => 'Details', + 'events' => 'Events', + 'target_amount' => 'Target amount', + 'start_date' => 'Start date', + 'target_date' => 'Target date', + 'no_target_date' => 'No target date', + 'todo' => 'to do', + 'table' => 'Table', + 'piggy_bank_not_exists' => 'Piggy bank no longer exists.', + 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.', + 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', + 'delete_piggy_bank' => 'Delete piggy bank ":name"', // tags - 'regular_tag' => 'Just a regular tag.', - 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', - 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', + 'regular_tag' => 'Just a regular tag.', + 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.', + 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.', - 'delete_tag' => 'Delete tag ":tag"', - 'new_tag' => 'Make new tag', - 'edit_tag' => 'Edit tag ":tag"', - 'no_year' => 'No year set', - 'no_month' => 'No month set', - 'tag_title_nothing' => 'Default tags', - 'tag_title_balancingAct' => 'Balancing act tags', - 'tag_title_advancePayment' => 'Advance payment tags', - 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like' . - ' expensive, bill' . - ' or for-party. In Firefly III, tags can have more properties' . - ' such as a date, description and location. This allows you to join transactions together in a more' . - ' meaningful way. For example, you could make a tag called ' . - 'Christmas dinner with friends and add information about the restaurant. Such tags are "singular",' . - ' you would only use them for a single occasion, perhaps with multiple transactions.', - 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money' . - ' for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where ' . - 'expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you.' . - ' Using tags the old-fashioned way is of course always possible. ', - 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', + 'delete_tag' => 'Delete tag ":tag"', + 'new_tag' => 'Make new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'no_year' => 'No year set', + 'no_month' => 'No month set', + 'tag_title_nothing' => 'Default tags', + 'tag_title_balancingAct' => 'Balancing act tags', + 'tag_title_advancePayment' => 'Advance payment tags', + 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like' . + ' expensive, bill' . + ' or for-party. In Firefly III, tags can have more properties' . + ' such as a date, description and location. This allows you to join transactions together in a more' . + ' meaningful way. For example, you could make a tag called ' . + 'Christmas dinner with friends and add information about the restaurant. Such tags are "singular",' . + ' you would only use them for a single occasion, perhaps with multiple transactions.', + 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money' . + ' for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where ' . + 'expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you.' . + ' Using tags the old-fashioned way is of course always possible. ', + 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', ]; diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php index bdbc1cd1d7..f248128198 100644 --- a/resources/lang/en/passwords.php +++ b/resources/lang/en/passwords.php @@ -17,5 +17,6 @@ return [ "token" => "This password reset token is invalid.", "sent" => "We have e-mailed your password reset link!", "reset" => "Your password has been reset!", + 'blocked' => 'Nice try though.' ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 9aede79f7d..5003a85297 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -13,6 +13,7 @@ return [ | */ + 'invalid_domain' => 'Due to security constraints, you cannot register from this domain.', 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', 'file_attached' => 'Succesfully uploaded file ":name".', 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', @@ -28,12 +29,10 @@ return [ "before" => "The :attribute must be a date before :date.", 'unique_object_for_user' => 'This name is already in use', 'unique_account_for_user' => 'This account name is already in use', - "between" => [ - "numeric" => "The :attribute must be between :min and :max.", - "file" => "The :attribute must be between :min and :max kilobytes.", - "string" => "The :attribute must be between :min and :max characters.", - "array" => "The :attribute must have between :min and :max items.", - ], + "between.numeric" => "The :attribute must be between :min and :max.", + "between.file" => "The :attribute must be between :min and :max kilobytes.", + "between.string" => "The :attribute must be between :min and :max characters.", + "between.array" => "The :attribute must have between :min and :max items.", "boolean" => "The :attribute field must be true or false.", "confirmed" => "The :attribute confirmation does not match.", "date" => "The :attribute is not a valid date.", @@ -48,19 +47,15 @@ return [ "in" => "The selected :attribute is invalid.", "integer" => "The :attribute must be an integer.", "ip" => "The :attribute must be a valid IP address.", - "max" => [ - "numeric" => "The :attribute may not be greater than :max.", - "file" => "The :attribute may not be greater than :max kilobytes.", - "string" => "The :attribute may not be greater than :max characters.", - "array" => "The :attribute may not have more than :max items.", - ], + "max.numeric" => "The :attribute may not be greater than :max.", + "max.file" => "The :attribute may not be greater than :max kilobytes.", + "max.string" => "The :attribute may not be greater than :max characters.", + "max.array" => "The :attribute may not have more than :max items.", "mimes" => "The :attribute must be a file of type: :values.", - "min" => [ - "numeric" => "The :attribute must be at least :min.", - "file" => "The :attribute must be at least :min kilobytes.", - "string" => "The :attribute must be at least :min characters.", - "array" => "The :attribute must have at least :min items.", - ], + "min.numeric" => "The :attribute must be at least :min.", + "min.file" => "The :attribute must be at least :min kilobytes.", + "min.string" => "The :attribute must be at least :min characters.", + "min.array" => "The :attribute must have at least :min items.", "not_in" => "The selected :attribute is invalid.", "numeric" => "The :attribute must be a number.", "regex" => "The :attribute format is invalid.", @@ -71,44 +66,13 @@ return [ "required_without" => "The :attribute field is required when :values is not present.", "required_without_all" => "The :attribute field is required when none of :values are present.", "same" => "The :attribute and :other must match.", - "size" => [ - "numeric" => "The :attribute must be :size.", - "file" => "The :attribute must be :size kilobytes.", - "string" => "The :attribute must be :size characters.", - "array" => "The :attribute must contain :size items.", - ], + "size.numeric" => "The :attribute must be :size.", + "size.file" => "The :attribute must be :size kilobytes.", + "size.string" => "The :attribute must be :size characters.", + "size.array" => "The :attribute must contain :size items.", "unique" => "The :attribute has already been taken.", "url" => "The :attribute format is invalid.", "timezone" => "The :attribute must be a valid zone.", - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ - - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap attribute place-holders - | with something more reader friendly such as E-Mail Address instead - | of "email". This simply helps us make messages a little cleaner. - | - */ - - 'attributes' => [], + 'attributes' => [], ]; diff --git a/resources/lang/nl/firefly.php b/resources/lang/nl/firefly.php index f8dbfa977e..c7e9b77418 100644 --- a/resources/lang/nl/firefly.php +++ b/resources/lang/nl/firefly.php @@ -253,249 +253,264 @@ return [ 'details_for_revenue' => 'Overzicht voor debiteur ":name"', 'details_for_cash' => 'Overzicht voor contant geldrekening ":name"', - 'store_new_asset_account' => 'Sla nieuwe betaalrekening op', - 'store_new_expense_account' => 'Sla nieuwe crediteur op', - 'store_new_revenue_account' => 'Sla nieuwe debiteur op', + 'store_new_asset_account' => 'Sla nieuwe betaalrekening op', + 'store_new_expense_account' => 'Sla nieuwe crediteur op', + 'store_new_revenue_account' => 'Sla nieuwe debiteur op', - 'edit_asset_account' => 'Wijzig betaalrekening ":name"', - 'edit_expense_account' => 'Wijzig crediteur ":name"', - 'edit_revenue_account' => 'Wijzig debiteur ":name"', + 'edit_asset_account' => 'Wijzig betaalrekening ":name"', + 'edit_expense_account' => 'Wijzig crediteur ":name"', + 'edit_revenue_account' => 'Wijzig debiteur ":name"', - 'delete_asset_account' => 'Verwijder betaalrekening ":name"', - 'delete_expense_account' => 'Verwijder crediteur ":name"', - 'delete_revenue_account' => 'Verwijder debiteur ":name"', + 'delete_asset_account' => 'Verwijder betaalrekening ":name"', + 'delete_expense_account' => 'Verwijder crediteur ":name"', + 'delete_revenue_account' => 'Verwijder debiteur ":name"', - 'asset_deleted' => 'Betaalrekening ":name" is verwijderd.', - 'expense_deleted' => 'Crediteur ":name" is verwijderd.', - 'revenue_deleted' => 'Debiteur ":name" is verwijderd.', + 'asset_deleted' => 'Betaalrekening ":name" is verwijderd.', + 'expense_deleted' => 'Crediteur ":name" is verwijderd.', + 'revenue_deleted' => 'Debiteur ":name" is verwijderd.', - 'update_asset_account' => 'Wijzig betaalrekening', - 'update_expense_account' => 'Wijzig crediteur', - 'update_revenue_account' => 'Wijzig debiteur', + 'update_asset_account' => 'Wijzig betaalrekening', + 'update_expense_account' => 'Wijzig crediteur', + 'update_revenue_account' => 'Wijzig debiteur', - 'make_new_asset_account' => 'Nieuwe betaalrekening', - 'make_new_expense_account' => 'Nieuwe crediteur', - 'make_new_revenue_account' => 'Nieuwe debiteur', + 'make_new_asset_account' => 'Nieuwe betaalrekening', + 'make_new_expense_account' => 'Nieuwe crediteur', + 'make_new_revenue_account' => 'Nieuwe debiteur', - 'asset_accounts' => 'Betaalrekeningen', - 'expense_accounts' => 'Crediteuren', - 'revenue_accounts' => 'Debiteuren', - 'account_type' => 'Account type', + 'asset_accounts' => 'Betaalrekeningen', + 'expense_accounts' => 'Crediteuren', + 'revenue_accounts' => 'Debiteuren', + 'account_type' => 'Account type', // some extra help: - 'accountExtraHelp_asset' => '', - 'accountExtraHelp_expense' => + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => 'Een crediteur is een persoon of een bedrijf waar je geld aan moet betalen. Je staat bij ze in het krijt. Een verwarrende' . ' term misschien, maar zo werkt het nou eenmaal. De supermarkt, je huurbaas of de bank zijn crediteuren. Jouw ' . 'geld (krediet) gaat naar hen toe. De term komt uit de wereld van de boekhouding. De uitgaves die je hier ziet zijn ' . 'positief, want je kijkt uit hun perspectief. Zodra jij afrekent in een winkel, komt het geld er bij hen bij (positief).', - 'accountExtraHelp_revenue' => 'Als je geld krijgt van een bedrijf of een persoon is dat een debiteur. ' . - 'Dat kan salaris zijn, of een andere betaling. ' . - ' Ze hebben een schuld (debet) aan jou. De term komt uit de wereld van de boekhouding.' . - ' De inkomsten die je hier ziet zijn negatief, want je kijkt uit hun perspectief. Zodra een debiteur geld naar jou ' . - 'overmaakt gaat het er bij hen af (negatief).', - 'save_transactions_by_moving' => 'Bewaar deze transacties door ze aan een andere rekening te koppelen:', + 'accountExtraHelp_revenue' => 'Als je geld krijgt van een bedrijf of een persoon is dat een debiteur. ' . + 'Dat kan salaris zijn, of een andere betaling. ' . + ' Ze hebben een schuld (debet) aan jou. De term komt uit de wereld van de boekhouding.' . + ' De inkomsten die je hier ziet zijn negatief, want je kijkt uit hun perspectief. Zodra een debiteur geld naar jou ' . + 'overmaakt gaat het er bij hen af (negatief).', + 'save_transactions_by_moving' => 'Bewaar deze transacties door ze aan een andere rekening te koppelen:', // categories: - 'new_category' => 'Nieuwe categorie', - 'create_new_category' => 'Nieuwe categorie', - 'without_category' => 'Zonder categorie', - 'update_category' => 'Wijzig categorie', - 'edit_category' => 'Wijzig categorie ":name"', - 'categories' => 'Categorieën', - 'no_category' => '(geen categorie)', - 'category' => 'Categorie', - 'delete_category' => 'Verwijder categorie ":name"', - 'store_category' => 'Sla nieuwe categorie op', + 'new_category' => 'Nieuwe categorie', + 'create_new_category' => 'Nieuwe categorie', + 'without_category' => 'Zonder categorie', + 'update_category' => 'Wijzig categorie', + 'edit_category' => 'Wijzig categorie ":name"', + 'categories' => 'Categorieën', + 'no_category' => '(geen categorie)', + 'category' => 'Categorie', + 'delete_category' => 'Verwijder categorie ":name"', + 'store_category' => 'Sla nieuwe categorie op', // transactions: - 'update_withdrawal' => 'Wijzig uitgave', - 'update_deposit' => 'Wijzig inkomsten', - 'update_transfer' => 'Wijzig overschrijving', - 'delete_withdrawal' => 'Verwijder uitgave ":description"', - 'delete_deposit' => 'Verwijder inkomsten ":description"', - 'delete_transfer' => 'Verwijder overschrijving ":description"', + 'update_withdrawal' => 'Wijzig uitgave', + 'update_deposit' => 'Wijzig inkomsten', + 'update_transfer' => 'Wijzig overschrijving', + 'delete_withdrawal' => 'Verwijder uitgave ":description"', + 'delete_deposit' => 'Verwijder inkomsten ":description"', + 'delete_transfer' => 'Verwijder overschrijving ":description"', // new user: - 'welcome' => 'Welkom bij Firefly!', - 'createNewAsset' => 'Maak om te beginnen een nieuwe betaalrekening. Dit is je start van je financiële beheer.', - 'createNewAssetButton' => 'Maak een nieuwe betaalrekening', + 'welcome' => 'Welkom bij Firefly!', + 'createNewAsset' => 'Maak om te beginnen een nieuwe betaalrekening. Dit is je start van je financiële beheer.', + 'createNewAssetButton' => 'Maak een nieuwe betaalrekening', // home page: - 'yourAccounts' => 'Je betaalrekeningen', - 'budgetsAndSpending' => 'Budgetten en uitgaven', - 'savings' => 'Sparen', - 'markAsSavingsToContinue' => 'Om hier wat te zien stel je je betaalrekeningen in als "spaarrekening".', - 'createPiggyToContinue' => 'Maak spaarpotjes om hier iets te zien.', - 'newWithdrawal' => 'Nieuwe uitgave', - 'newDeposit' => 'Nieuwe inkomsten', - 'newTransfer' => 'Nieuwe overschrijving', - 'moneyIn' => 'Inkomsten', - 'moneyOut' => 'Uitgaven', - 'billsToPay' => 'Openstaande contracten', - 'billsPaid' => 'Betaalde contracten', - 'viewDetails' => 'Meer info', - 'divided' => 'verdeeld', - 'toDivide' => 'te verdelen', + 'yourAccounts' => 'Je betaalrekeningen', + 'budgetsAndSpending' => 'Budgetten en uitgaven', + 'savings' => 'Sparen', + 'markAsSavingsToContinue' => 'Om hier wat te zien stel je je betaalrekeningen in als "spaarrekening".', + 'createPiggyToContinue' => 'Maak spaarpotjes om hier iets te zien.', + 'newWithdrawal' => 'Nieuwe uitgave', + 'newDeposit' => 'Nieuwe inkomsten', + 'newTransfer' => 'Nieuwe overschrijving', + 'moneyIn' => 'Inkomsten', + 'moneyOut' => 'Uitgaven', + 'billsToPay' => 'Openstaande contracten', + 'billsPaid' => 'Betaalde contracten', + 'viewDetails' => 'Meer info', + 'divided' => 'verdeeld', + 'toDivide' => 'te verdelen', // menu and titles, should be recycled as often as possible: - 'toggleNavigation' => 'Navigatie aan of uit', - 'currency' => 'Valuta', - 'preferences' => 'Voorkeuren', - 'logout' => 'Uitloggen', - 'searchPlaceholder' => 'Zoeken...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Valuta', - 'accounts' => 'Rekeningen', - 'Asset account' => 'Betaalrekening', - 'Default account' => 'Betaalrekening', - 'Expense account' => 'Crediteur', - 'Revenue account' => 'Debiteur', - 'budgets' => 'Budgetten', - 'tags' => 'Tags', - 'reports' => 'Overzichten', - 'transactions' => 'Transacties', - 'expenses' => 'Uitgaven', - 'income' => 'Inkomsten', - 'transfers' => 'Overschrijvingen', - 'moneyManagement' => 'Geldbeheer', - 'piggyBanks' => 'Spaarpotjes', - 'bills' => 'Contracten', - 'createNew' => 'Nieuw', - 'withdrawal' => 'Uitgave', - 'deposit' => 'Inkomsten', - 'account' => 'Rekening', - 'transfer' => 'Overschrijving', - 'Withdrawal' => 'Uitgave', - 'Deposit' => 'Inkomsten', - 'Transfer' => 'Overschrijving', - 'profile' => 'Profiel', - 'bill' => 'Contract', - 'yes' => 'Ja', - 'no' => 'Nee', - 'amount' => 'Bedrag', - 'newBalance' => 'Nieuw saldo', - 'overview' => 'Overzicht', - 'saveOnAccount' => 'Sparen op rekening', - 'unknown' => 'Onbekend', - 'daily' => 'Dagelijks', - 'weekly' => 'Wekelijks', - 'monthly' => 'Maandelijks', - 'quarterly' => 'Elk kwartaal', - 'half-year' => 'Elk half jaar', - 'yearly' => 'Jaarlijks', - 'sum_of_year' => 'Som van jaar', - 'average_of_year' => 'Gemiddelde in jaar', + 'toggleNavigation' => 'Navigatie aan of uit', + 'currency' => 'Valuta', + 'preferences' => 'Voorkeuren', + 'logout' => 'Uitloggen', + 'searchPlaceholder' => 'Zoeken...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Valuta', + 'accounts' => 'Rekeningen', + 'Asset account' => 'Betaalrekening', + 'Default account' => 'Betaalrekening', + 'Expense account' => 'Crediteur', + 'Revenue account' => 'Debiteur', + 'Initial balance account' => 'Startbalansrekening', + 'budgets' => 'Budgetten', + 'tags' => 'Tags', + 'reports' => 'Overzichten', + 'transactions' => 'Transacties', + 'expenses' => 'Uitgaven', + 'income' => 'Inkomsten', + 'transfers' => 'Overschrijvingen', + 'moneyManagement' => 'Geldbeheer', + 'piggyBanks' => 'Spaarpotjes', + 'bills' => 'Contracten', + 'createNew' => 'Nieuw', + 'withdrawal' => 'Uitgave', + 'deposit' => 'Inkomsten', + 'account' => 'Rekening', + 'transfer' => 'Overschrijving', + 'Withdrawal' => 'Uitgave', + 'Deposit' => 'Inkomsten', + 'Transfer' => 'Overschrijving', + 'profile' => 'Profiel', + 'bill' => 'Contract', + 'yes' => 'Ja', + 'no' => 'Nee', + 'amount' => 'Bedrag', + 'newBalance' => 'Nieuw saldo', + 'overview' => 'Overzicht', + 'saveOnAccount' => 'Sparen op rekening', + 'unknown' => 'Onbekend', + 'daily' => 'Dagelijks', + 'weekly' => 'Wekelijks', + 'monthly' => 'Maandelijks', + 'quarterly' => 'Elk kwartaal', + 'half-year' => 'Elk half jaar', + 'yearly' => 'Jaarlijks', + 'sum_of_year' => 'Som van jaar', + 'average_of_year' => 'Gemiddelde in jaar', + 'sum_of_years' => 'Som van jaren', + 'average_of_years' => 'Gemiddelde in jaren', // reports: - 'reportForYear' => 'Jaaroverzicht :year', - 'reportForYearShared' => 'Jaaroverzicht :year (inclusief gedeelde rekeningen)', - 'reportForMonth' => 'Maandoverzicht voor :month', - 'reportForMonthShared' => 'Maandoverzicht voor :month (inclusief gedeelde rekeningen)', - 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', - 'accountBalances' => 'Rekeningsaldi', - 'balanceStartOfYear' => 'Saldo aan het begin van het jaar', - 'balanceEndOfYear' => 'Saldo aan het einde van het jaar', - 'balanceStartOfMonth' => 'Saldo aan het begin van de maand', - 'balanceEndOfMonth' => 'Saldo aan het einde van de maand', - 'balanceStart' => 'Saldo aan het begin van de periode', - 'balanceEnd' => 'Saldo aan het einde van de periode', - 'reportsOwnAccounts' => 'Overzichten voor je eigen betaalrekeningen', - 'reportsOwnAccountsAndShared' => 'Overzichten voor je eigen betaalrekeningen en gedeelde rekeningen', - 'splitByAccount' => 'Per betaalrekening', - 'balancedByTransfersAndTags' => 'Gecorrigeerd met overschrijvingen en tags', - 'coveredWithTags' => 'Gecorrigeerd met tags', - 'leftUnbalanced' => 'Ongecorrigeerd', - 'expectedBalance' => 'Verwacht saldo', - 'outsideOfBudgets' => 'Buiten budgetten', - 'leftInBudget' => 'Over van budget', - 'sumOfSums' => 'Alles bij elkaar', - 'notCharged' => '(Nog) niet betaald', - 'inactive' => 'Niet actief', - 'difference' => 'Verschil', - 'in' => 'In', - 'out' => 'Uit', - 'topX' => 'top :number', - 'showTheRest' => 'Laat alles zien', - 'hideTheRest' => 'Laat alleen de top :number zien', - 'categories_earned_in_year' => 'Categorieën (inkomsten)', - 'categories_spent_in_year' => 'Categorieën (uitgaven)', + 'report_default' => 'Standaard financieel rapport (:start tot :end)', + 'quick_link_reports' => 'Snelle links', + 'quick_link_default_report' => 'Standaard financieel rapport', + 'report_this_month_quick' => 'Deze maand, alle rekeningen', + 'report_this_year_quick' => 'Dit jaar, alle rekeningen', + 'report_all_time_quick' => 'Gehele periode, alle rekeningen', + 'reports_can_bookmark' => 'Je kan rapporten aan je favorieten toevoegen.', + 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', + 'accountBalances' => 'Rekeningsaldi', + 'balanceStartOfYear' => 'Saldo aan het begin van het jaar', + 'balanceEndOfYear' => 'Saldo aan het einde van het jaar', + 'balanceStartOfMonth' => 'Saldo aan het begin van de maand', + 'balanceEndOfMonth' => 'Saldo aan het einde van de maand', + 'balanceStart' => 'Saldo aan het begin van de periode', + 'balanceEnd' => 'Saldo aan het einde van de periode', + 'reportsOwnAccounts' => 'Overzichten voor je eigen betaalrekeningen', + 'reportsOwnAccountsAndShared' => 'Overzichten voor je eigen betaalrekeningen en gedeelde rekeningen', + 'splitByAccount' => 'Per betaalrekening', + 'balancedByTransfersAndTags' => 'Gecorrigeerd met overschrijvingen en tags', + 'coveredWithTags' => 'Gecorrigeerd met tags', + 'leftUnbalanced' => 'Ongecorrigeerd', + 'expectedBalance' => 'Verwacht saldo', + 'outsideOfBudgets' => 'Buiten budgetten', + 'leftInBudget' => 'Over van budget', + 'sumOfSums' => 'Alles bij elkaar', + 'noCategory' => '(zonder categorie)', + 'notCharged' => '(Nog) niet betaald', + 'inactive' => 'Niet actief', + 'difference' => 'Verschil', + 'in' => 'In', + 'out' => 'Uit', + 'topX' => 'top :number', + 'showTheRest' => 'Laat alles zien', + 'hideTheRest' => 'Laat alleen de top :number zien', + 'categories_earned_in_year' => 'Categorieën (inkomsten)', + 'categories_spent_in_year' => 'Categorieën (uitgaven)', + 'report_type' => 'Rapporttype', + 'report_type_default' => 'Standard financieel rapport', + 'report_included_accounts' => 'Accounts in rapport', + 'report_date_range' => 'Datumbereik', + 'report_include_help' => 'Overboekingen naar gedeelde rekeningen tellen als uitgave. Overboekingen van gedeelde rekeningen tellen als inkomsten.', + 'report_preset_ranges' => 'Standaardbereik', + 'shared' => 'Gedeeld', + // charts: - 'dayOfMonth' => 'Dag vd maand', - 'month' => 'Maand', - 'budget' => 'Budget', - 'spent' => 'Uitgegeven', - 'earned' => 'Verdiend', - 'overspent' => 'Teveel uitgegeven', - 'left' => 'Over', - 'noBudget' => '(geen budget)', - 'maxAmount' => 'Maximaal bedrag', - 'minAmount' => 'Minimaal bedrag', - 'billEntry' => 'Bedrag voor dit contract', - 'name' => 'Naam', - 'date' => 'Datum', - 'paid' => 'Betaald', - 'unpaid' => 'Niet betaald', - 'day' => 'Dag', - 'budgeted' => 'Gebudgetteerd', - 'period' => 'Periode', - 'balance' => 'Saldo', - 'summary' => 'Samenvatting', - 'sum' => 'Som', - 'average' => 'Gemiddeld', - 'balanceFor' => 'Saldo op :name', + 'dayOfMonth' => 'Dag vd maand', + 'month' => 'Maand', + 'budget' => 'Budget', + 'spent' => 'Uitgegeven', + 'earned' => 'Verdiend', + 'overspent' => 'Teveel uitgegeven', + 'left' => 'Over', + 'noBudget' => '(geen budget)', + 'maxAmount' => 'Maximaal bedrag', + 'minAmount' => 'Minimaal bedrag', + 'billEntry' => 'Bedrag voor dit contract', + 'name' => 'Naam', + 'date' => 'Datum', + 'paid' => 'Betaald', + 'unpaid' => 'Niet betaald', + 'day' => 'Dag', + 'budgeted' => 'Gebudgetteerd', + 'period' => 'Periode', + 'balance' => 'Saldo', + 'summary' => 'Samenvatting', + 'sum' => 'Som', + 'average' => 'Gemiddeld', + 'balanceFor' => 'Saldo op :name', // piggy banks: - 'piggy_bank' => 'Spaarpotje', - 'new_piggy_bank' => 'Nieuw spaarpotje', - 'store_piggy_bank' => 'Sla spaarpotje op', - 'account_status' => 'Rekeningoverzicht', - 'left_for_piggy_banks' => 'Over voor spaarpotjes', - 'sum_of_piggy_banks' => 'Som van spaarpotjes', - 'saved_so_far' => 'Gespaard', - 'left_to_save' => 'Te sparen', - 'add_money_to_piggy_title' => 'Stop geld in spaarpotje ":name"', - 'remove_money_from_piggy_title' => 'Haal geld uit spaarpotje ":name"', - 'add' => 'Toevoegen', - 'remove' => 'Verwijderen', - 'max_amount_add' => 'Hooguit toe te voegen', - 'max_amount_remove' => 'Hooguit te verwijderen', - 'update_piggy_button' => 'Wijzig spaarpotje', - 'update_piggy_title' => 'Wijzig spaarpotje ":name"', - 'details' => 'Details', - 'events' => 'Gebeurtenissen', - 'target_amount' => 'Doelbedrag', - 'start_date' => 'Startdatum', - 'target_date' => 'Doeldatum', - 'no_target_date' => 'Geen doeldatum', - 'todo' => 'te doen', - 'table' => 'Tabel', - 'piggy_bank_not_exists' => 'Dit spaarpotje bestaat niet meer.', - 'add_any_amount_to_piggy' => 'Stop geld in dit spaarpotje om het doel van :amount te halen.', - 'add_set_amount_to_piggy' => 'Stop voor :date :amount in dit spaarpotje om hem op tijd te vullen.', - 'delete_piggy_bank' => 'Verwijder spaarpotje ":name"', + 'piggy_bank' => 'Spaarpotje', + 'new_piggy_bank' => 'Nieuw spaarpotje', + 'store_piggy_bank' => 'Sla spaarpotje op', + 'account_status' => 'Rekeningoverzicht', + 'left_for_piggy_banks' => 'Over voor spaarpotjes', + 'sum_of_piggy_banks' => 'Som van spaarpotjes', + 'saved_so_far' => 'Gespaard', + 'left_to_save' => 'Te sparen', + 'add_money_to_piggy_title' => 'Stop geld in spaarpotje ":name"', + 'remove_money_from_piggy_title' => 'Haal geld uit spaarpotje ":name"', + 'add' => 'Toevoegen', + 'remove' => 'Verwijderen', + 'max_amount_add' => 'Hooguit toe te voegen', + 'max_amount_remove' => 'Hooguit te verwijderen', + 'update_piggy_button' => 'Wijzig spaarpotje', + 'update_piggy_title' => 'Wijzig spaarpotje ":name"', + 'details' => 'Details', + 'events' => 'Gebeurtenissen', + 'target_amount' => 'Doelbedrag', + 'start_date' => 'Startdatum', + 'target_date' => 'Doeldatum', + 'no_target_date' => 'Geen doeldatum', + 'todo' => 'te doen', + 'table' => 'Tabel', + 'piggy_bank_not_exists' => 'Dit spaarpotje bestaat niet meer.', + 'add_any_amount_to_piggy' => 'Stop geld in dit spaarpotje om het doel van :amount te halen.', + 'add_set_amount_to_piggy' => 'Stop voor :date :amount in dit spaarpotje om hem op tijd te vullen.', + 'delete_piggy_bank' => 'Verwijder spaarpotje ":name"', // tags - 'regular_tag' => 'Een gewone tag.', - 'balancing_act' => 'Er kunnen maar twee transacties worden getagged; een uitgaven en inkomsten. Ze balanceren elkaar.', - 'advance_payment' => 'Je kan een uitgave taggen en zoveel inkomsten om de uitgave (helemaal) te compenseren.', - 'delete_tag' => 'Verwijder tag ":tag"', - 'new_tag' => 'Maak nieuwe tag', - 'edit_tag' => 'Wijzig tag ":tag"', - 'no_year' => 'Zonder jaar', - 'no_month' => 'Zonder maand', - 'tag_title_nothing' => 'Standaard tags', - 'tag_title_balancingAct' => 'Balancerende tags', - 'tag_title_advancePayment' => 'Vooruitbetaalde tags', - 'tags_introduction' => 'Normaal gesproken zijn tags enkele woorden, gebruikt om gerelateerde zaken snel aan elkaar te plakken. ' . - 'dure-aanschaf, rekening, ' . - 'feestje. In Firefly III hebben tags meer betekenis en kan je er een datum' . - ', beschrijving en locatie aan geven. Daarmee kan je je transacties op een wat zinvollere manier aan elkaar ' . - 'koppelen. Je kan bijvoorbeeld een tag Kerstdiner maken en informatie over' . - ' het restaurant meenemen. Zulke tags zijn enkelvoudig; je gebruikt ze maar bij één gelegenheid.', - 'tags_group' => 'Omdat tags transacties groeperen kan je er teruggaves, vergoedingen en andere geldzaken mee aanduiden, zolang' . - ' de transacties elkaar "opheffen". Hoe je dit aanpakt is aan jou. De gewone manier kan natuurlijk ook.', - 'tags_start' => 'Maak hieronder een tag, of voer nieuwe tags in als je nieuwe transacties maakt.', + 'regular_tag' => 'Een gewone tag.', + 'balancing_act' => 'Er kunnen maar twee transacties worden getagged; een uitgaven en inkomsten. Ze balanceren elkaar.', + 'advance_payment' => 'Je kan een uitgave taggen en zoveel inkomsten om de uitgave (helemaal) te compenseren.', + 'delete_tag' => 'Verwijder tag ":tag"', + 'new_tag' => 'Maak nieuwe tag', + 'edit_tag' => 'Wijzig tag ":tag"', + 'no_year' => 'Zonder jaar', + 'no_month' => 'Zonder maand', + 'tag_title_nothing' => 'Standaard tags', + 'tag_title_balancingAct' => 'Balancerende tags', + 'tag_title_advancePayment' => 'Vooruitbetaalde tags', + 'tags_introduction' => 'Normaal gesproken zijn tags enkele woorden, gebruikt om gerelateerde zaken snel aan elkaar te plakken. ' . + 'dure-aanschaf, rekening, ' . + 'feestje. In Firefly III hebben tags meer betekenis en kan je er een datum' . + ', beschrijving en locatie aan geven. Daarmee kan je je transacties op een wat zinvollere manier aan elkaar ' . + 'koppelen. Je kan bijvoorbeeld een tag Kerstdiner maken en informatie over' . + ' het restaurant meenemen. Zulke tags zijn enkelvoudig; je gebruikt ze maar bij één gelegenheid.', + 'tags_group' => 'Omdat tags transacties groeperen kan je er teruggaves, vergoedingen en andere geldzaken mee aanduiden, zolang' . + ' de transacties elkaar "opheffen". Hoe je dit aanpakt is aan jou. De gewone manier kan natuurlijk ook.', + 'tags_start' => 'Maak hieronder een tag, of voer nieuwe tags in als je nieuwe transacties maakt.', ]; diff --git a/resources/lang/nl/passwords.php b/resources/lang/nl/passwords.php index b3a0582dbf..6b189b754b 100644 --- a/resources/lang/nl/passwords.php +++ b/resources/lang/nl/passwords.php @@ -17,5 +17,6 @@ return [ "token" => "Ongeldig token! Sorry", "sent" => "Je krijgt een mailtje met een linkje om je wachtwoord te herstellen!", "reset" => "Je wachtwoord is hersteld!", + 'blocked' => 'Leuk geprobeerd wel.' ]; diff --git a/resources/lang/nl/validation.php b/resources/lang/nl/validation.php index 8c8a7cfa3c..1539bca460 100644 --- a/resources/lang/nl/validation.php +++ b/resources/lang/nl/validation.php @@ -13,6 +13,7 @@ return [ | */ + 'invalid_domain' => 'Kan niet registereren vanaf dit domein.', 'file_already_attached' => 'Het geuploade bestand ":name" is al gelinkt aan deze transactie.', 'file_attached' => 'Bestand met naam ":name" is met succes geuploaded.', 'file_invalid_mime' => 'Bestand ":name" is van het type ":mime", en die kan je niet uploaden.', @@ -28,12 +29,10 @@ return [ "before" => "The :attribute must be a date before :date.", 'unique_object_for_user' => 'Deze naam is al in gebruik', 'unique_account_for_user' => 'This rekeningnaam is already in use', - "between" => [ - "numeric" => "The :attribute must be between :min and :max.", - "file" => "The :attribute must be between :min and :max kilobytes.", - "string" => "The :attribute must be between :min and :max characters.", - "array" => "The :attribute must have between :min and :max items.", - ], + "between.numeric" => "The :attribute must be between :min and :max.", + "between.file" => "The :attribute must be between :min and :max kilobytes.", + "between.string" => "The :attribute must be between :min and :max characters.", + "between.array" => "The :attribute must have between :min and :max items.", "boolean" => "The :attribute field must be true or false.", "confirmed" => "The :attribute confirmation does not match.", "date" => "The :attribute is not a valid date.", @@ -48,19 +47,15 @@ return [ "in" => "The selected :attribute is invalid.", "integer" => "The :attribute must be an integer.", "ip" => "The :attribute must be a valid IP address.", - "max" => [ - "numeric" => "The :attribute may not be greater than :max.", - "file" => "The :attribute may not be greater than :max kilobytes.", - "string" => "The :attribute may not be greater than :max characters.", - "array" => "The :attribute may not have more than :max items.", - ], + "max.numeric" => "The :attribute may not be greater than :max.", + "max.file" => "The :attribute may not be greater than :max kilobytes.", + "max.string" => "The :attribute may not be greater than :max characters.", + "max.array" => "The :attribute may not have more than :max items.", "mimes" => "The :attribute must be a file of type: :values.", - "min" => [ - "numeric" => "The :attribute must be at least :min.", - "file" => "The :attribute must be at least :min kilobytes.", - "string" => "The :attribute must be at least :min characters.", - "array" => "The :attribute must have at least :min items.", - ], + "min.numeric" => "The :attribute must be at least :min.", + "min.file" => "The :attribute must be at least :min kilobytes.", + "min.string" => "The :attribute must be at least :min characters.", + "min.array" => "The :attribute must have at least :min items.", "not_in" => "The selected :attribute is invalid.", "numeric" => "The :attribute must be a number.", "regex" => "The :attribute format is invalid.", @@ -71,44 +66,13 @@ return [ "required_without" => "The :attribute field is required when :values is not present.", "required_without_all" => "The :attribute field is required when none of :values are present.", "same" => "The :attribute and :other must match.", - "size" => [ - "numeric" => "The :attribute must be :size.", - "file" => "The :attribute must be :size kilobytes.", - "string" => "The :attribute must be :size characters.", - "array" => "The :attribute must contain :size items.", - ], + "size.numeric" => "The :attribute must be :size.", + "size.file" => "The :attribute must be :size kilobytes.", + "size.string" => "The :attribute must be :size characters.", + "size.array" => "The :attribute must contain :size items.", "unique" => "The :attribute has already been taken.", "url" => "The :attribute format is invalid.", "timezone" => "The :attribute must be a valid zone.", - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ - - 'custom' => [ - 'attribute-name' => [ - 'rule-name' => 'custom-message', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap attribute place-holders - | with something more reader friendly such as E-Mail Address instead - | of "email". This simply helps us make messages a little cleaner. - | - */ - 'attributes' => [], ]; diff --git a/resources/twig/auth/register.twig b/resources/twig/auth/register.twig index 1855e5f248..ea209c35d5 100644 --- a/resources/twig/auth/register.twig +++ b/resources/twig/auth/register.twig @@ -17,16 +17,20 @@
- {% if host == 'geld.nder.be' %} + {% if host == 'geld.nder.be' or host == 'firefly.app' %} + work for one (1) month.

{% endif %}
- + + {% if host == 'geld.nder.be' or host == 'firefly.app' %} +

You will receive an email from Firefly III. If your email address + is incorrect, your account may not work.

+ {% endif %}
diff --git a/resources/twig/budgets/index.twig b/resources/twig/budgets/index.twig index 1cfc78fcf4..e98a252672 100644 --- a/resources/twig/budgets/index.twig +++ b/resources/twig/budgets/index.twig @@ -43,7 +43,7 @@
- {{ 'spent'|_ }}: {{ (spent*-1)|formatAmountPlain }} + {{ 'spent'|_ }}: {{ spent|formatAmount }}
@@ -144,7 +144,7 @@ {{ 'spent'|_ }} - {{ (budget.spent*-1)|formatAmountPlain }} + {{ budget.spent|formatAmount }}
@@ -179,7 +179,7 @@ {% block scripts %} - + - + {% endblock %} diff --git a/resources/twig/reports/default/multi-year.twig b/resources/twig/reports/default/multi-year.twig new file mode 100644 index 0000000000..abd50a687d --- /dev/null +++ b/resources/twig/reports/default/multi-year.twig @@ -0,0 +1,141 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, report_type, accountIds) }} +{% endblock %} + +{% block content %} + +
+
+
+
+

{{ 'incomeVsExpenses'|_ }}

+
+
+ +
+
+
+
+
+
+

{{ 'incomeVsExpenses'|_ }}

+
+
+ +
+
+
+
+ + {% for account in accounts %} + + {% endfor %} + +
+
+
+
+

Selected budgets

+
+
+
+
+ + +
+
+
+
+ + {% for budget in budgets %} + + {% endfor %} +
+
+
+
+
+
+ +
+
+
+
+

Selected categories

+
+
+
+
+ + +
+
+
+
+ + {% for category in categories %} + + {% endfor %} +
+
+
+
+
+
+ + +{% endblock %} +{% block styles %} + +{% endblock %} +{% block scripts %} + + + + + {% if Config.get('firefly.chart') == 'google' %} + + + {% endif %} + {% if Config.get('firefly.chart') == 'chartjs' %} + + + {% endif %} + + + +{% endblock %} diff --git a/resources/twig/reports/year.twig b/resources/twig/reports/default/year.twig similarity index 80% rename from resources/twig/reports/year.twig rename to resources/twig/reports/default/year.twig index 0d543bc54a..a8ed93ff81 100644 --- a/resources/twig/reports/year.twig +++ b/resources/twig/reports/default/year.twig @@ -1,7 +1,7 @@ {% extends "./layout/default.twig" %} {% block breadcrumbs %} - {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, shared) }} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, report_type, accountIds) }} {% endblock %} {% block content %} @@ -13,12 +13,7 @@

{{ 'incomeVsExpenses'|_ }}

- {% if Config.get('firefly.chart') == 'google' %} -
- {% endif %} - {% if Config.get('firefly.chart') == 'chartjs' %} - - {% endif %} +
@@ -28,12 +23,7 @@

{{ 'incomeVsExpenses'|_ }}

- {% if Config.get('firefly.chart') == 'google' %} -
- {% endif %} - {% if Config.get('firefly.chart') == 'chartjs' %} - - {% endif %} +
@@ -41,15 +31,15 @@
- {% include 'partials/reports/accounts.twig' %} - {% include 'partials/reports/income-vs-expenses.twig' %} + {% include 'reports/partials/accounts.twig' %} + {% include 'reports/partials/income-vs-expenses.twig' %}
- {% include 'partials/reports/income.twig' %} + {% include 'reports/partials/income.twig' %}
- {% include 'partials/reports/expenses.twig' %} + {% include 'reports/partials/expenses.twig' %}
@@ -122,8 +112,14 @@ - + {% endblock %} diff --git a/resources/twig/reports/index.twig b/resources/twig/reports/index.twig index 064b17ed58..acb99e3a61 100644 --- a/resources/twig/reports/index.twig +++ b/resources/twig/reports/index.twig @@ -6,45 +6,134 @@ {% block content %}
-
+
-

{{ 'reportsOwnAccounts'|_ }}

+

{{ 'reports'|_ }}

- {% for year, entries in months %} -

{{ year }}

- - {% endfor %} + + +
+ + +
+ +
+
+
+ + +
+ {% for account in accounts %} +
+ +
+ {% endfor %} + +

+ {{ 'report_include_help'|_ }} +

+
+
+ + +
+ + +
+ +
+
+
+ + +
+ {% for year, data in months %} + {{ year }} + + {% endfor %} +
+
+ +
+
+ +
+
+ +
-
+ +
-

{{ 'reportsOwnAccountsAndShared'|_ }}

+

{{ 'quick_link_reports'|_ }}

- - {% for year, entries in months %} -

{{ year }}

- - {% endfor %} +

{{ 'quick_link_default_report'|_ }}

+ +

+ {{ 'reports_can_bookmark'|_ }} +

+
+ {% endblock %} {% block scripts %} - + + + {% endblock %} diff --git a/resources/twig/partials/reports/accounts.twig b/resources/twig/reports/partials/accounts.twig similarity index 83% rename from resources/twig/partials/reports/accounts.twig rename to resources/twig/reports/partials/accounts.twig index 89c051d859..b0d4782a24 100644 --- a/resources/twig/partials/reports/accounts.twig +++ b/resources/twig/reports/partials/accounts.twig @@ -13,7 +13,7 @@ - {% for account in accounts.getAccounts %} + {% for account in accountReport.getAccounts %} {{ account.name }} @@ -27,9 +27,9 @@ {{ 'sumOfSums'|_ }} - {{ accounts.getStart|formatAmount }} - {{ accounts.getEnd|formatAmount }} - {{ accounts.getDifference|formatAmount }} + {{ accountReport.getStart|formatAmount }} + {{ accountReport.getEnd|formatAmount }} + {{ accountReport.getDifference|formatAmount }} diff --git a/resources/twig/partials/reports/balance.twig b/resources/twig/reports/partials/balance.twig similarity index 80% rename from resources/twig/partials/reports/balance.twig rename to resources/twig/reports/partials/balance.twig index e431e9a992..74cf4804a9 100644 --- a/resources/twig/partials/reports/balance.twig +++ b/resources/twig/reports/partials/balance.twig @@ -14,7 +14,6 @@ {{ 'leftInBudget'|_ }} - {{ 'sum'|_ }} @@ -35,7 +34,7 @@ {% for balanceEntry in balanceLine.getBalanceEntries %} {% if balanceEntry.getSpent != 0 %} - {{ (balanceEntry.getSpent*-1)|formatAmountPlain }} + {{ (balanceEntry.getSpent)|formatAmountPlain }} {% endif %} {% if balanceEntry.getLeft != 0 %} {{ (balanceEntry.getLeft)|formatAmountPlain }} @@ -43,14 +42,7 @@ {% endfor %} - {% if balanceLine.leftOfRepetition != 0 %} - {{ balanceLine.leftOfRepetition|formatAmount }} - {% endif %} - - - {% if balanceLine.sumOfLeft != 0 %} - {{ balanceLine.sumOfLeft|formatAmount }} - {% endif %} + {{ balanceLine.leftOfRepetition|formatAmount }} {% endfor %} diff --git a/resources/twig/partials/reports/bills.twig b/resources/twig/reports/partials/bills.twig similarity index 96% rename from resources/twig/partials/reports/bills.twig rename to resources/twig/reports/partials/bills.twig index 84288f6501..266c9b6f5b 100644 --- a/resources/twig/partials/reports/bills.twig +++ b/resources/twig/reports/partials/bills.twig @@ -39,7 +39,7 @@ {% endif %} {% if line.isActive %} - {{ (line.getMax - line.getAmount)|formatAmount }} + {{ (line.getMax + line.getAmount)|formatAmount }} {% endif %} diff --git a/resources/twig/partials/reports/budgets.twig b/resources/twig/reports/partials/budgets.twig similarity index 80% rename from resources/twig/partials/reports/budgets.twig rename to resources/twig/reports/partials/budgets.twig index 3972b4dffe..aa8c9986e3 100644 --- a/resources/twig/partials/reports/budgets.twig +++ b/resources/twig/reports/partials/budgets.twig @@ -41,15 +41,17 @@ {% if budgetLine.getSpent != 0 %} {{ budgetLine.getSpent|formatAmount }} {% endif %} - - - {% if budgetLine.getLeft != 0 %} - {{ budgetLine.getLeft|formatAmount }} + + {% if budgetLine.getSpent == 0 %} + {{ budgetLine.getSpent|formatAmount }} {% endif %} + + {{ budgetLine.getLeft|formatAmount }} + {% if budgetLine.getOverspent != 0 %} - {{ budgetLine.getOverspent|formatAmountPlain }} + {{ budgetLine.getOverspent|formatAmount }} {% endif %} @@ -59,7 +61,14 @@ {{ 'sum'|_ }} {{ budgets.getBudgeted|formatAmount }} - {{ budgets.getSpent|formatAmount }} + + {% if budgets.getSpent != 0 %} + {{ budgets.getSpent|formatAmountPlain }} + {% endif %} + {% if budgets.getSpent == 0 %} + {{ budgets.getSpent|formatAmount }} + {% endif %} + {{ budgets.getLeft|formatAmount }} {{ budgets.getOverspent|formatAmountPlain }} diff --git a/resources/twig/partials/reports/categories.twig b/resources/twig/reports/partials/categories.twig similarity index 81% rename from resources/twig/partials/reports/categories.twig rename to resources/twig/reports/partials/categories.twig index 88aee1a0b9..ee5000825e 100644 --- a/resources/twig/partials/reports/categories.twig +++ b/resources/twig/reports/partials/categories.twig @@ -16,14 +16,14 @@ {{ cat.name }} - {{ (cat.spent * -1)|formatAmountPlain }} + {{ cat.spent|formatAmount }} {% endfor %} {{ 'sum'|_ }} - {{ (categories.getTotal * -1)|formatAmountPlain }} + {{ categories.getTotal|formatAmount }} diff --git a/resources/twig/partials/reports/expenses.twig b/resources/twig/reports/partials/expenses.twig similarity index 87% rename from resources/twig/partials/reports/expenses.twig rename to resources/twig/reports/partials/expenses.twig index eb3f1d035c..25f632fc8f 100644 --- a/resources/twig/partials/reports/expenses.twig +++ b/resources/twig/reports/partials/expenses.twig @@ -18,7 +18,7 @@ {{ expense.count }} {{ 'transactions'|_|lower }} {% endif %} - {{ (expense.amount)|formatAmountPlain }} + {{ (expense.amount)|formatAmount }} {% endfor %} @@ -32,7 +32,7 @@ {% endif %} {{ 'sum'|_ }} - {{ (expenses.getTotal)|formatAmountPlain }} + {{ (expenses.getTotal)|formatAmount }} diff --git a/resources/twig/partials/reports/income-vs-expenses.twig b/resources/twig/reports/partials/income-vs-expenses.twig similarity index 74% rename from resources/twig/partials/reports/income-vs-expenses.twig rename to resources/twig/reports/partials/income-vs-expenses.twig index c3d6d52b84..5103c1987b 100644 --- a/resources/twig/partials/reports/income-vs-expenses.twig +++ b/resources/twig/reports/partials/income-vs-expenses.twig @@ -10,11 +10,11 @@ {{ 'out'|_ }} - {{ (expenses.getTotal)|formatAmountPlain }} + {{ (expenses.getTotal)|formatAmount }} {{ 'difference'|_ }} - {{ (incomes.getTotal + (expenses.getTotal * -1))|formatAmount }} + {{ (incomes.getTotal + expenses.getTotal)|formatAmount }}
diff --git a/resources/twig/partials/reports/income.twig b/resources/twig/reports/partials/income.twig similarity index 100% rename from resources/twig/partials/reports/income.twig rename to resources/twig/reports/partials/income.twig diff --git a/resources/twig/transactions/show.twig b/resources/twig/transactions/show.twig index c87273dd1b..b6cd21498b 100644 --- a/resources/twig/transactions/show.twig +++ b/resources/twig/transactions/show.twig @@ -43,6 +43,12 @@ {{ category.name }} {% endfor %} + {% if journal.bill %} + + {{ 'bill'|_ }} + {{ journal.bill.name }} + + {% endif %} {% if journal.tags|length > 0 %} {{ 'tags'|_ }} diff --git a/tests/TestCase.php b/tests/TestCase.php index dc5d0dda48..c5a49310ed 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,9 +1,5 @@ app->instance($class, $mock); - - return $mock; - } - - } diff --git a/tests/unit/ExampleTest.php b/tests/unit/ExampleTest.php deleted file mode 100644 index 05b642ccaf..0000000000 --- a/tests/unit/ExampleTest.php +++ /dev/null @@ -1,20 +0,0 @@ -first(); + $this->assertTrue($transactionType->isWithdrawal()); + } + + public function testIsDeposit() + { + $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); + $this->assertTrue($transactionType->isDeposit()); + } + + public function testIsTransfer() + { + $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); + $this->assertTrue($transactionType->isTransfer()); + } + + public function testIsOpeningBalance() + { + $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); + $this->assertTrue($transactionType->isOpeningBalance()); + } +}