mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'release/3.5.4'
This commit is contained in:
commit
53b3f7f821
@ -11,6 +11,8 @@ DB_PASSWORD=secret
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
|
||||
DEFAULT_CURRENCY=EUR
|
||||
|
||||
EMAIL_SMTP=
|
||||
EMAIL_DRIVER=smtp
|
||||
EMAIL_USERNAME=
|
||||
@ -22,3 +24,5 @@ SITE_OWNER=mail@example.com
|
||||
|
||||
SENDGRID_USERNAME=
|
||||
SENDGRID_PASSWORD=
|
||||
|
||||
BLOCKED_DOMAINS=
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
22
README.md
22
README.md
@ -61,19 +61,19 @@ Everything is organised:
|
||||
|
||||
_Please note that everything in these screenshots is fictional and may not be realistic._
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## 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
|
||||
|
||||
|
@ -18,6 +18,13 @@ interface BudgetChartGenerator
|
||||
*/
|
||||
public function budget(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,13 @@ interface CategoryChartGenerator
|
||||
*/
|
||||
public function all(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiYear(Collection $entries);
|
||||
|
||||
/**
|
||||
* @param Collection $entries
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use Auth;
|
||||
use Config;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\User;
|
||||
@ -8,12 +9,12 @@ use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Request as Rq;
|
||||
use Session;
|
||||
use Twig;
|
||||
use Validator;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class AuthController
|
||||
@ -32,9 +33,8 @@ class AuthController extends Controller
|
||||
public function getLogout()
|
||||
{
|
||||
Auth::logout();
|
||||
Log::debug('Logout and redirect to root.');
|
||||
|
||||
return redirect('/login');
|
||||
return redirect('/auth/login');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,8 +88,8 @@ class AuthController extends Controller
|
||||
$foundUser = User::where('email', $credentials['email'])->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.
|
||||
*
|
||||
|
@ -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)]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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');
|
||||
if ($start->diffInMonths($end) > 12) {
|
||||
// per year
|
||||
while ($start < $end) {
|
||||
$startOfYear = clone $start;
|
||||
$startOfYear->startOfYear();
|
||||
$endOfYear = clone $startOfYear;
|
||||
$endOfYear->endOfYear();
|
||||
|
||||
Log::debug('Date ['.$month->format('M Y').']: income = ['.$income.' + '.$currentIncome.'], out = ['.$expense.' + '.$currentExpense.']');
|
||||
// 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);
|
||||
|
||||
$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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
$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);
|
||||
|
||||
|
@ -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')) {
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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!');
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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]));
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
@ -29,6 +30,111 @@ Route::bind(
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
);
|
||||
// account list! Yay!
|
||||
Route::bind(
|
||||
'accountList',
|
||||
function ($value) {
|
||||
if (Auth::check()) {
|
||||
$ids = explode(',', $value);
|
||||
/** @var \Illuminate\Support\Collection $object */
|
||||
$object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
|
||||
->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:
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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!
|
||||
*/
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 = '<span class="text-info">' . $this->formatWithSymbol($symbol, $journal->amount_positive, false) . '</span>';
|
||||
$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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 = '<i class="fa fa-long-arrow-left fa-fw" title="' . trans('firefly.withdrawal') . '"></i>';
|
||||
break;
|
||||
case 'Deposit':
|
||||
case $journal->isDeposit():
|
||||
$txt = '<i class="fa fa-long-arrow-right fa-fw" title="' . trans('firefly.deposit') . '"></i>';
|
||||
break;
|
||||
case 'Transfer':
|
||||
case $journal->isTransfer():
|
||||
$txt = '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
|
||||
break;
|
||||
case 'Opening balance':
|
||||
case $journal->isOpeningBalance():
|
||||
$txt = '<i class="fa-fw fa fa-ban" title="' . trans('firefly.openingBalance') . '"></i>';
|
||||
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 = '<a href="' . route('tags.show', [$tag->id]) . '" class="label label-success" title="' . $amount
|
||||
. '"><i class="fa fa-fw fa-sort-numeric-desc"></i> ' . $tag->tag . '</a>';
|
||||
@ -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 = '<a href="' . route('tags.show', [$tag->id]) . '">' . $amount . '</a>';
|
||||
|
737
composer.lock
generated
737
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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',
|
||||
|
||||
/*
|
||||
|
@ -49,7 +49,7 @@ return [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => __DIR__ . '/../storage/database/testing.db',
|
||||
'database' => storage_path('database/testing.db'),
|
||||
'prefix' => '',
|
||||
],
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
||||
];
|
||||
|
@ -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();
|
||||
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
4
public/js/accounting.min.js
vendored
Normal file
4
public/js/accounting.min.js
vendored
Normal file
@ -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;e<h;e++)c[e]=b.call(d,a[e],e,a);return c}function n(a,b){a=Math.round(Math.abs(a));return isNaN(a)?b:a}function x(a){var b=c.settings.currency.format;"function"===typeof a&&(a=a());return q(a)&&a.match("%v")?{pos:a,neg:a.replace("-","").replace("%v","-%v"),zero:a}:!a||!a.pos||!a.pos.match("%v")?!q(b)?b:c.settings.currency.format={pos:b,neg:b.replace("%v","-%v"),zero:b}:a}var c={version:"0.4.1",settings:{currency:{symbol:"$",format:"%s%v",decimal:".",thousand:",",precision:2,grouping:3},number:{precision:0,grouping:3,thousand:",",decimal:"."}}},w=Array.prototype.map,u=Array.isArray,v=Object.prototype.toString,o=c.unformat=c.parse=function(a,b){if(m(a))return j(a,function(a){return o(a,b)});a=a||0;if("number"===typeof a)return a;var b=b||".",c=RegExp("[^0-9-"+b+"]",["g"]),c=parseFloat((""+a).replace(/\((.*)\)/,"-$1").replace(c,"").replace(b,"."));return!isNaN(c)?c:0},y=c.toFixed=function(a,b){var b=n(b,c.settings.number.precision),d=Math.pow(10,b);return(Math.round(c.unformat(a)*d)/d).toFixed(b)},t=c.formatNumber=c.format=function(a,b,d,i){if(m(a))return j(a,function(a){return t(a,b,d,i)});var a=o(a),e=s(r(b)?b:{precision:b,thousand:d,decimal:i},c.settings.number),h=n(e.precision),f=0>a?"-":"",g=parseInt(y(Math.abs(a||0),h),10)+"",l=3<g.length?g.length%3:0;return f+(l?g.substr(0,l)+e.thousand:"")+g.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+e.thousand)+(h?e.decimal+y(Math.abs(a),h).split(".")[1]:"")},A=c.formatMoney=function(a,b,d,i,e,h){if(m(a))return j(a,function(a){return A(a,b,d,i,e,h)});var a=o(a),f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format);return(0<a?g.pos:0>a?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")<g.pos.indexOf("%v")?!0:!1,k=0,a=j(a,function(a){if(m(a))return c.formatColumn(a,f);a=o(a);a=(0<a?g.pos:0>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<k?l?a.replace(f.symbol,f.symbol+Array(k-a.length+1).join(" ")):Array(k-a.length+1).join(" ")+a:a})};if("undefined"!==typeof exports){if("undefined"!==typeof module&&module.exports)exports=module.exports=c;exports.accounting=c}else"function"===typeof define&&define.amd?define([],function(){return c}):(c.noConflict=function(a){return function(){p.accounting=a;c.noConflict=z;return c}}(p.accounting),p.accounting=c)})(this);
|
@ -24,6 +24,23 @@ var colourSet = [
|
||||
|
||||
];
|
||||
|
||||
// Settings object that controls default parameters for library methods:
|
||||
accounting.settings = {
|
||||
currency: {
|
||||
symbol : currencySymbol, // default currency symbol is '$'
|
||||
format: "%s %v", // controls output: %s = symbol, %v = value/number (can be object: see below)
|
||||
decimal : ",", // decimal point separator
|
||||
thousand: ".", // thousands separator
|
||||
precision : 2 // decimal places
|
||||
},
|
||||
number: {
|
||||
precision : 0, // default precision on numbers is 0
|
||||
thousand: ",",
|
||||
decimal : "."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var fillColors = [];
|
||||
var strokePointHighColors = [];
|
||||
|
||||
@ -45,9 +62,9 @@ var defaultAreaOptions = {
|
||||
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) %>"
|
||||
};
|
||||
|
||||
|
||||
@ -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) %>"
|
||||
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
});
|
64
public/js/reports/default/month.js
Normal file
64
public/js/reports/default/month.js
Normal file
@ -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;
|
||||
}
|
155
public/js/reports/default/multi-year.js
Normal file
155
public/js/reports/default/multi-year.js
Normal file
@ -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('<canvas id="budgets-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
|
||||
|
||||
// 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('<canvas id="categories-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
|
||||
|
||||
// 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);
|
||||
}
|
212
public/js/reports/default/reports.js
Normal file
212
public/js/reports/default/reports.js
Normal file
@ -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);
|
||||
}
|
67
public/js/reports/default/year.js
Normal file
67
public/js/reports/default/year.js
Normal file
@ -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;
|
||||
}
|
122
public/js/reports/index.js
Normal file
122
public/js/reports/index.js
Normal file
@ -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;
|
||||
}
|
||||
|
@ -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' .
|
||||
' <span class="label label-info">expensive</span>, <span class="label label-info">bill</span>' .
|
||||
' or <span class="label label-info">for-party</span>. 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 <span class="label label-success">' .
|
||||
'Christmas dinner with friends</span> 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' .
|
||||
' <span class="label label-info">expensive</span>, <span class="label label-info">bill</span>' .
|
||||
' or <span class="label label-info">for-party</span>. 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 <span class="label label-success">' .
|
||||
'Christmas dinner with friends</span> 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.',
|
||||
|
||||
];
|
||||
|
@ -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.'
|
||||
|
||||
];
|
||||
|
@ -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' => [],
|
||||
|
||||
];
|
||||
|
@ -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. ' .
|
||||
'<span class="label label-info">dure-aanschaf</span>, <span class="label label-info">rekening</span>, ' .
|
||||
'<span class="label label-info">feestje</span>. 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 <span class="label label-success">Kerstdiner</span> 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. ' .
|
||||
'<span class="label label-info">dure-aanschaf</span>, <span class="label label-info">rekening</span>, ' .
|
||||
'<span class="label label-info">feestje</span>. 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 <span class="label label-success">Kerstdiner</span> 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.',
|
||||
];
|
||||
|
@ -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.'
|
||||
|
||||
];
|
||||
|
@ -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' => [],
|
||||
|
||||
];
|
||||
|
@ -17,16 +17,20 @@
|
||||
|
||||
<div class="register-box-body">
|
||||
<p class="login-box-msg">Register a new account</p>
|
||||
{% if host == 'geld.nder.be' %}
|
||||
{% if host == 'geld.nder.be' or host == 'firefly.app' %}
|
||||
<p class="text-info login-box-msg">Please note that an account on this site will only
|
||||
work for one (1) week.</p>
|
||||
work for one (1) month.</p>
|
||||
{% endif %}
|
||||
|
||||
<form role="form" id="register" method="POST" action="{{ URL.to('/auth/register') }}">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="form-group has-feedback">
|
||||
<input type="email" name="email" class="form-control" placeholder="Email"/>
|
||||
<input type="email" name="email" class="form-control" placeholder="Email" />
|
||||
{% if host == 'geld.nder.be' or host == 'firefly.app' %}
|
||||
<p class="help-block">You will receive an email from Firefly III. If your email address
|
||||
is incorrect, your account may not work.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<input type="password" class="form-control" placeholder="Password" name="password"/>
|
||||
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-4 col-sm-3">
|
||||
<small>{{ 'spent'|_ }}: <span class="text-danger">{{ (spent*-1)|formatAmountPlain }}</span></small>
|
||||
<small>{{ 'spent'|_ }}: {{ spent|formatAmount }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -144,7 +144,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50%;">{{ 'spent'|_ }}</td>
|
||||
<td><span class="text-danger">{{ (budget.spent*-1)|formatAmountPlain }}</a></td>
|
||||
<td>{{ budget.spent|formatAmount }}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@ -179,7 +179,7 @@
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
// actually spent bar data:
|
||||
var spent = {{ spent * -1 }};
|
||||
var spent = {{ spent * -1 }}; // must be positive for the calculation to work.
|
||||
var currencySymbol = "{{ getCurrencySymbol()|raw }}";
|
||||
|
||||
// budgeted data:
|
||||
|
@ -50,7 +50,7 @@
|
||||
|
||||
{% for limit in limits %}
|
||||
{% for rep in limit.limitRepetitions %}
|
||||
{% set spentInRep = (spentInRepetitionCorrected(rep)*-1) %}
|
||||
{% set spentInRep = spentInRepetition(rep) %}
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><a href="{{ route('budgets.show',[budget.id,rep.id]) }}">{{ rep.startdate.formatLocalized(monthFormat) }}</a>
|
||||
@ -62,15 +62,15 @@
|
||||
{{ 'amount'|_ }}: {{ rep.amount|formatAmount }}
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6 col-sm-6">
|
||||
{{ 'spent'|_ }}: <span class="text-danger">{{ spentInRep|formatAmountPlain }}</span>
|
||||
{{ 'spent'|_ }}: {{ spentInRep|formatAmount }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
{% set overspent = spentInRep > rep.amount %}
|
||||
{% set overspent = rep.amount + spentInRep < 0 %}
|
||||
|
||||
{% if overspent %}
|
||||
{% set pct = (spentInRep != 0 ? (rep.amount / spentInRep)*100 : 0) %}
|
||||
{% set pct = (spentInRep != 0 ? (rep.amount / (spentInRep*-1))*100 : 0) %} <!-- must have -1 here -->
|
||||
<div class="progress progress-striped">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: {{ pct|round }}%;"></div>
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
{% set amount = rep.amount %}
|
||||
{% set pct = (amount != 0 ? ((spentInRep / amount)*100) : 0) %}
|
||||
{% set pct = (amount != 0 ? (((spentInRep*-1) / amount)*100) : 0) %} <!-- must have -1 here -->
|
||||
<div class="progress progress-striped">
|
||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: {{ pct|round }}%;"></div>
|
||||
|
@ -36,6 +36,10 @@
|
||||
Enjoy!
|
||||
</p>
|
||||
|
||||
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:11px;color:#aaa;">
|
||||
The registration has been created from IP {{ ip }}
|
||||
</p>
|
||||
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
|
@ -17,3 +17,5 @@ Password reset:
|
||||
Documentation:
|
||||
https://github.com/JC5/firefly-iii/wiki/First-use
|
||||
https://github.com/JC5/firefly-iii/wiki/full-description
|
||||
|
||||
The registration has been created from IP {{ ip }}
|
@ -6,7 +6,7 @@
|
||||
<div class="popover-navigation">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-default" data-role="prev">« {{ 'prev'|_ }}</button>
|
||||
<button class="btn btn-sm btn-default" data-role="next">{{ 'prev'|_ }} »</button>
|
||||
<button class="btn btn-sm btn-default" data-role="next">{{ 'next'|_ }} »</button>
|
||||
<button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="{{ 'pause'|_ }}"
|
||||
data-resume-text="Resume"> {{ 'pause'|_ }}</button>
|
||||
</div>
|
||||
|
@ -157,7 +157,7 @@
|
||||
<script src="js/moment.min.js" type="text/javascript"></script>
|
||||
<script src="js/daterangepicker.js" type="text/javascript"></script>
|
||||
<script src="dist/js/app.min.js" type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript" src="js/accounting.min.js"></script>
|
||||
<script src="js/bootstrap-tour.min.js" type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -24,10 +24,8 @@
|
||||
|
||||
<td>
|
||||
{% if event.amount < 0 %}
|
||||
|
||||
<span class="text-danger">{{ trans('firefly.removed_amount', {amount: (event.amount*-1)|formatAmountPlain})|raw }}</span>
|
||||
<span class="text-danger">{{ trans('firefly.removed_amount', {amount: (event.amount)|formatAmountPlain})|raw }}</span>
|
||||
{% else %}
|
||||
|
||||
<span class="text-success">{{ trans('firefly.added_amount', {amount: (event.amount)|formatAmountPlain})|raw }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
@ -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 %}
|
||||
@ -26,26 +26,26 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-6 col-sm-6">
|
||||
{% include 'partials/reports/accounts.twig' %}
|
||||
{% include 'partials/reports/income-vs-expenses.twig' %}
|
||||
{% include 'reports/partials/accounts.twig' %}
|
||||
{% include 'reports/partials/income-vs-expenses.twig' %}
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<!-- income -->
|
||||
{% include 'partials/reports/income.twig' %}
|
||||
{% include 'reports/partials/income.twig' %}
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
<!-- expenses -->
|
||||
{% include 'partials/reports/expenses.twig' %}
|
||||
{% include 'reports/partials/expenses.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-md-8 col-sm-12">
|
||||
<!-- budgets -->
|
||||
{% include 'partials/reports/budgets.twig' %}
|
||||
{% include 'reports/partials/budgets.twig' %}
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-4 col-sm-12">
|
||||
<!-- categories -->
|
||||
{% include 'partials/reports/categories.twig' %}
|
||||
{% include 'reports/partials/categories.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -55,12 +55,12 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
{% include 'partials/reports/balance.twig' %}
|
||||
{% include 'reports/partials/balance.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
{% include 'partials/reports/bills.twig' %}
|
||||
{% include 'reports/partials/bills.twig' %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -85,8 +85,13 @@
|
||||
<script type="text/javascript">
|
||||
var year = {{ start.year }};
|
||||
var month = {{ start.month }};
|
||||
var shared = {% if shared %}'/shared'
|
||||
{% else %}''{% endif %};
|
||||
|
||||
// to report another URL:
|
||||
var startDate = '{{ start.format('Ymd') }}';
|
||||
var endDate = '{{ end.format('Ymd') }}';
|
||||
var reportType = '{{ report_type }}';
|
||||
var accountIds = '{{ accountIds }}';
|
||||
|
||||
var incomeTopLength = {{ incomeTopLength }};
|
||||
var expenseTopLength = {{ expenseTopLength }};
|
||||
var incomeRestShow = false; // starts hidden.
|
||||
@ -96,5 +101,5 @@
|
||||
var showTheRestExpense = '{{ trans('firefly.showTheRest',{number:expenseTopLength}) }}';
|
||||
var hideTheRestExpense = '{{ trans('firefly.hideTheRest',{number:expenseTopLength}) }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="js/reports.js"></script>
|
||||
<script type="text/javascript" src="js/reports/default/month.js"></script>
|
||||
{% endblock %}
|
141
resources/twig/reports/default/multi-year.twig
Normal file
141
resources/twig/reports/default/multi-year.twig
Normal file
@ -0,0 +1,141 @@
|
||||
{% extends "./layout/default.twig" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, report_type, accountIds) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-md-8 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="income-expenses-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-4 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="income-expenses-sum-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for account in accounts %}
|
||||
<div class="row" style="display:none;">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'Asset account'|_ }}: {{ account.name }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<canvas id="account-chart-{{ account.id }}" class="account-chart" data-id="{{ account.id }}" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Selected budgets</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p class="well" id="budgets-chart-message" style="display:none;">
|
||||
Select one or more budgets to generate this chart. Insofar possible, your selection will be saved.
|
||||
</p>
|
||||
<canvas id="budgets-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" class="budget-checkbox" name="budgets[]" value="0"> {{ 'noBudget'|_ }}
|
||||
</label>
|
||||
{% for budget in budgets %}
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" class="budget-checkbox" name="budgets[]" value="{{ budget.id }}"> {{ budget.name }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Selected categories</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p class="well" id="categories-chart-message" style="display:none;">
|
||||
Select one or more categories to generate this chart. Insofar possible, your selection will be saved.
|
||||
</p>
|
||||
<canvas id="categories-chart" class="categories-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" class="category-checkbox" name="categories[]" value="0"> {{ 'noCategory'|_ }}
|
||||
</label>
|
||||
{% for category in categories %}
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" name="categories[]" class="category-checkbox" value="{{ category.id }}"> {{ category.name }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="css/bootstrap-sortable.css" type="text/css" media="all"/>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
|
||||
<script type="text/javascript" src="js/bootstrap-sortable.js"></script>
|
||||
|
||||
<!-- load the libraries and scripts necessary for Google Charts: -->
|
||||
{% if Config.get('firefly.chart') == 'google' %}
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
<script type="text/javascript" src="js/gcharts.js"></script>
|
||||
{% endif %}
|
||||
{% if Config.get('firefly.chart') == 'chartjs' %}
|
||||
<script type="text/javascript" src="js/Chart.min.js"></script>
|
||||
<script type="text/javascript" src="js/charts.js"></script>
|
||||
{% endif %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var year = {{ start.year }};
|
||||
var month = {{ start.month }};
|
||||
|
||||
// to report another URL:
|
||||
var startDate = '{{ start.format('Ymd') }}';
|
||||
var endDate = '{{ end.format('Ymd') }}';
|
||||
var reportType = '{{ report_type }}';
|
||||
var accountIds = '{{ accountIds }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="js/reports/default/multi-year.js"></script>
|
||||
{% endblock %}
|
@ -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 @@
|
||||
<h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{% if Config.get('firefly.chart') == 'google' %}
|
||||
<div id="income-expenses-chart"></div>
|
||||
{% endif %}
|
||||
{% if Config.get('firefly.chart') == 'chartjs' %}
|
||||
<canvas id="income-expenses-chart" style="width:100%;height:400px;"></canvas>
|
||||
{% endif %}
|
||||
<canvas id="income-expenses-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -28,12 +23,7 @@
|
||||
<h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{% if Config.get('firefly.chart') == 'google' %}
|
||||
<div id="income-expenses-sum-chart"></div>
|
||||
{% endif %}
|
||||
{% if Config.get('firefly.chart') == 'chartjs' %}
|
||||
<canvas id="income-expenses-sum-chart" style="width:100%;height:400px;"></canvas>
|
||||
{% endif %}
|
||||
<canvas id="income-expenses-sum-chart" style="width:100%;height:400px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,15 +31,15 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-6 col-sm-6">
|
||||
{% include 'partials/reports/accounts.twig' %}
|
||||
{% include 'partials/reports/income-vs-expenses.twig' %}
|
||||
{% include 'reports/partials/accounts.twig' %}
|
||||
{% include 'reports/partials/income-vs-expenses.twig' %}
|
||||
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
{% include 'partials/reports/income.twig' %}
|
||||
{% include 'reports/partials/income.twig' %}
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-3 col-sm-3">
|
||||
{% include 'partials/reports/expenses.twig' %}
|
||||
{% include 'reports/partials/expenses.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -122,8 +112,14 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var year = '{{start.year}}';
|
||||
var shared = {% if shared %}'/shared'
|
||||
{% else %}''{% endif %};
|
||||
|
||||
// to report another URL:
|
||||
var startDate = '{{ start.format('Ymd') }}';
|
||||
var endDate = '{{ end.format('Ymd') }}';
|
||||
var reportType = '{{ report_type }}';
|
||||
var accountIds = '{{ accountIds }}';
|
||||
|
||||
|
||||
var incomeTopLength = {{ incomeTopLength }};
|
||||
var expenseTopLength = {{ expenseTopLength }};
|
||||
var incomeRestShow = false; // starts hidden.
|
||||
@ -134,6 +130,6 @@
|
||||
var hideTheRestExpense = '{{ trans('firefly.hideTheRest',{number:expenseTopLength}) }}';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="js/reports.js"></script>
|
||||
<script type="text/javascript" src="js/reports/default/year.js"></script>
|
||||
|
||||
{% endblock %}
|
@ -6,45 +6,134 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'reportsOwnAccounts'|_ }}</h3>
|
||||
<h3 class="box-title">{{ 'reports'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
{% for year, entries in months %}
|
||||
<h4><a href="{{ route('reports.year',year) }}">{{ year }}</a></h4>
|
||||
<ul class="list-inline">
|
||||
{% for month in entries %}
|
||||
<li><a href="{{ route('reports.month',[month.year, month.month]) }}">{{ month.formatted }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<form class="form-horizontal" id="report-form" action="{{ route('reports.index') }}" method="post">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
|
||||
<div class="form-group">
|
||||
<label for="inputReportType" class="col-sm-3 control-label">{{ 'report_type'|_ }}</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<select name="report_type" class="form-control" id="inputReportType">
|
||||
<option label="{{ 'report_type_default'|_ }}" value="default">{{ 'report_type_default'|_ }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputAccounts" class="col-sm-3 control-label">{{ 'report_included_accounts'|_ }}</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
{% for account in accounts %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" class="account-checkbox" name="accounts[]" value="{{ account.id }}">
|
||||
{{ account.name }}
|
||||
{% if account.getMeta('accountRole') == 'sharedAsset' %}
|
||||
({{ 'shared'|_|lower }})
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<p class="help-block">
|
||||
{{ 'report_include_help'|_ }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDateRange" class="col-sm-3 control-label">{{ 'report_date_range'|_ }}</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="inputDateRange" name="daterange"
|
||||
value="{{ Session.get('start').format('Y-m-d') }} - {{ Session.get('end').format('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="__none__" class="col-sm-3 control-label">{{ 'report_preset_ranges'|_ }}</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
{% for year, data in months %}
|
||||
<a href="#" class="date-select" data-start="{{ data.start }}" data-end="{{ data.end }}">{{ year }}</a>
|
||||
<ul class="list-inline">
|
||||
{% for month in data.months %}
|
||||
<li>
|
||||
<a data-start="{{ month.start }}" data-end="{{ month.end }}" class="date-select" href="#">{{ month.formatted }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<button type="submit" class="btn btn-default">{{ 'submit'|_ }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||
|
||||
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'reportsOwnAccountsAndShared'|_ }}</h3>
|
||||
<h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
{% for year, entries in months %}
|
||||
<h4><a href="{{ route('reports.year',[year, 'shared']) }}">{{ year }}</a></h4>
|
||||
<ul class="list-inline">
|
||||
{% for month in entries %}
|
||||
<li><a href="{{ route('reports.month',[month.year, month.month,'shared']) }}">{{ month.formatted }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<h4>{{ 'quick_link_default_report'|_ }}</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ route('reports.report',
|
||||
['default',
|
||||
Session.get('start').startOfMonth.format('Ymd'),
|
||||
Session.get('start').endOfMonth.format('Ymd'),
|
||||
accountList
|
||||
]) }}">{{ 'report_this_month_quick'|_ }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('reports.report',
|
||||
['default',
|
||||
Session.get('start').startOfYear.format('Ymd'),
|
||||
Session.get('start').endOfYear.format('Ymd'),
|
||||
accountList
|
||||
]) }}">{{ 'report_this_year_quick'|_ }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('reports.report',
|
||||
['default',
|
||||
start.format('Ymd'),
|
||||
Session.get('end').endOfMonth.format('Ymd'),
|
||||
accountList
|
||||
]) }}">{{ 'report_all_time_quick'|_ }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<em>{{ 'reports_can_bookmark'|_ }}</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="js/reports.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var reportURL = "{{ route('reports.report', ['']) }}";
|
||||
var minDate = "{{ start.format('m/d/Y') }}";
|
||||
var picker;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="js/reports/index.js"></script>
|
||||
{% endblock %}
|
||||
|
@ -13,7 +13,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in accounts.getAccounts %}
|
||||
{% for account in accountReport.getAccounts %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('accounts.show',account.id) }}" title="{{ account.name }}">{{ account.name }}</a>
|
||||
@ -27,9 +27,9 @@
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><em>{{ 'sumOfSums'|_ }}</em></td>
|
||||
<td>{{ accounts.getStart|formatAmount }}</td>
|
||||
<td>{{ accounts.getEnd|formatAmount }}</td>
|
||||
<td>{{ accounts.getDifference|formatAmount }}</td>
|
||||
<td>{{ accountReport.getStart|formatAmount }}</td>
|
||||
<td>{{ accountReport.getEnd|formatAmount }}</td>
|
||||
<td>{{ accountReport.getDifference|formatAmount }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -14,7 +14,6 @@
|
||||
<th>
|
||||
{{ 'leftInBudget'|_ }}
|
||||
</th>
|
||||
<th>{{ 'sum'|_ }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -35,7 +34,7 @@
|
||||
{% for balanceEntry in balanceLine.getBalanceEntries %}
|
||||
<td>
|
||||
{% if balanceEntry.getSpent != 0 %}
|
||||
<span class="text-danger">{{ (balanceEntry.getSpent*-1)|formatAmountPlain }}</span>
|
||||
<span class="text-danger">{{ (balanceEntry.getSpent)|formatAmountPlain }}</span>
|
||||
{% endif %}
|
||||
{% if balanceEntry.getLeft != 0 %}
|
||||
<span class="text-success">{{ (balanceEntry.getLeft)|formatAmountPlain }}</span>
|
||||
@ -43,14 +42,7 @@
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td>
|
||||
{% if balanceLine.leftOfRepetition != 0 %}
|
||||
{{ balanceLine.leftOfRepetition|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if balanceLine.sumOfLeft != 0 %}
|
||||
{{ balanceLine.sumOfLeft|formatAmount }}
|
||||
{% endif %}
|
||||
{{ balanceLine.leftOfRepetition|formatAmount }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
@ -39,7 +39,7 @@
|
||||
{% endif %}
|
||||
<td data-value="{{ (line.getMax - line.getAmount) }}">
|
||||
{% if line.isActive %}
|
||||
{{ (line.getMax - line.getAmount)|formatAmount }}
|
||||
{{ (line.getMax + line.getAmount)|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
@ -41,15 +41,17 @@
|
||||
{% if budgetLine.getSpent != 0 %}
|
||||
{{ budgetLine.getSpent|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if budgetLine.getLeft != 0 %}
|
||||
{{ budgetLine.getLeft|formatAmount }}
|
||||
|
||||
{% if budgetLine.getSpent == 0 %}
|
||||
{{ budgetLine.getSpent|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ budgetLine.getLeft|formatAmount }}
|
||||
</td>
|
||||
<td>
|
||||
{% if budgetLine.getOverspent != 0 %}
|
||||
<span class="text-danger">{{ budgetLine.getOverspent|formatAmountPlain }}</span>
|
||||
{{ budgetLine.getOverspent|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -59,7 +61,14 @@
|
||||
<tr>
|
||||
<td colspan="2"><em>{{ 'sum'|_ }}</em></td>
|
||||
<td>{{ budgets.getBudgeted|formatAmount }}</td>
|
||||
<td>{{ budgets.getSpent|formatAmount }}</td>
|
||||
<td>
|
||||
{% if budgets.getSpent != 0 %}
|
||||
<span class="text-danger">{{ budgets.getSpent|formatAmountPlain }}</span>
|
||||
{% endif %}
|
||||
{% if budgets.getSpent == 0 %}
|
||||
{{ budgets.getSpent|formatAmount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ budgets.getLeft|formatAmount }}</td>
|
||||
<td><span class="text-danger">{{ budgets.getOverspent|formatAmountPlain }}</span></td>
|
||||
</tr>
|
@ -16,14 +16,14 @@
|
||||
<td>
|
||||
<a href="{{ route('categories.show',cat.id) }}">{{ cat.name }}</a>
|
||||
</td>
|
||||
<td><span class="text-danger">{{ (cat.spent * -1)|formatAmountPlain }}</span></td>
|
||||
<td>{{ cat.spent|formatAmount }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><em>{{ 'sum'|_ }}</em></td>
|
||||
<td class="text-danger">{{ (categories.getTotal * -1)|formatAmountPlain }}</td>
|
||||
<td>{{ categories.getTotal|formatAmount }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -18,7 +18,7 @@
|
||||
<small>{{ expense.count }} {{ 'transactions'|_|lower }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="text-danger">{{ (expense.amount)|formatAmountPlain }}</span></td>
|
||||
<td>{{ (expense.amount)|formatAmount }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@ -32,7 +32,7 @@
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td><em>{{ 'sum'|_ }}</em></td>
|
||||
<td><span class="text-danger">{{ (expenses.getTotal)|formatAmountPlain }}</span></td>
|
||||
<td>{{ (expenses.getTotal)|formatAmount }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -10,11 +10,11 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'out'|_ }}</td>
|
||||
<td><span class="text-danger">{{ (expenses.getTotal)|formatAmountPlain }}</span></td>
|
||||
<td>{{ (expenses.getTotal)|formatAmount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ 'difference'|_ }}</td>
|
||||
<td>{{ (incomes.getTotal + (expenses.getTotal * -1))|formatAmount }}</td>
|
||||
<td>{{ (incomes.getTotal + expenses.getTotal)|formatAmount }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
@ -43,6 +43,12 @@
|
||||
<td><a href="{{ route('categories.show',category.id) }}">{{ category.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if journal.bill %}
|
||||
<tr>
|
||||
<td>{{ 'bill'|_ }}</td>
|
||||
<td><a href="{{ route('bills.show', journal.bill_id) }}">{{ journal.bill.name }}</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if journal.tags|length > 0 %}
|
||||
<tr>
|
||||
<td>{{ 'tags'|_ }}</td>
|
||||
|
@ -1,9 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use League\FactoryMuffin\Facade as FactoryMuffin;
|
||||
|
||||
/**
|
||||
* Class TestCase
|
||||
*/
|
||||
@ -52,20 +48,4 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
|
||||
{
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
*
|
||||
* @return Mockery\MockInterface
|
||||
*/
|
||||
public function mock($class)
|
||||
{
|
||||
$mock = Mockery::mock($class);
|
||||
|
||||
$this->app->instance($class, $mock);
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class ExampleTest
|
||||
*/
|
||||
class ExampleTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
// tests
|
||||
public function testMe()
|
||||
{
|
||||
}
|
||||
}
|
32
tests/unit/Models/TransactionTypeTest.php
Normal file
32
tests/unit/Models/TransactionTypeTest.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use TestCase;
|
||||
|
||||
class TransactionTypeTest extends TestCase
|
||||
{
|
||||
public function testIsWithdrawal()
|
||||
{
|
||||
$transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user